32#include <osg/Geometry>
33#include <osg/MatrixTransform>
36#include <simgear/debug/logstream.hxx>
37#include <simgear/scene/material/EffectGeode.hxx>
38#include <simgear/scene/material/matlib.hxx>
39#include <simgear/scene/material/mat.hxx>
40#include <simgear/scene/util/OsgMath.hxx>
41#include <simgear/structure/exception.hxx>
42#include <simgear/timing/timestamp.hxx>
43#include <simgear/timing/sg_time.hxx>
77 networkInitialized =
false;
82 networkInitialized =
true;
94void FGGroundController::signOff(
int id)
101 int currentPosition,
double lat,
102 double lon,
double heading,
103 double speed,
double alt,
104 double radius,
int leg,
108 SG_LOG(SG_ATC, SG_ALERT,
"announcePosition: missing aircraft performance");
118 SG_LOG(SG_ATC, SG_BULK,
"Adding " << aircraft->
getCallSign() <<
"(" <<
id <<
")" );
122 rec->setPositionAndIntentions(currentPosition, intendedRoute);
123 rec->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
124 rec->setRadius(radius);
126 rec->setAircraft(aircraft);
134 SG_LOG(SG_ATC, SG_DEBUG,
135 "Added " << sharedRec->getCallsign() <<
"(" << sharedRec->getId() <<
") " << sharedRec);
140 SG_LOG(SG_ATC, SG_ALERT,
141 "Not moved " << (*i)->getCallsign() <<
"(" << (*i)->getId() <<
")");
144 (*i)->setPositionAndIntentions(currentPosition, intendedRoute);
145 (*i)->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
166 double heading,
double speed,
double alt,
180 SG_LOG(SG_ATC, SG_DEV_WARN,
181 "AI error: updating aircraft without traffic record at " <<
", id=" <<
id);
185 SG_LOG(SG_ATC, SG_BULK,
"Moving " << (*i)->getCallsign() <<
"(" << (*i)->getId() <<
") Speed : " << speed
186 <<
" Speed 2 : " << (*i)->getSpeed());
188 airportGroundRadar->move(SGRect<double>(geod.getLatitudeDeg(), geod.getLongitudeDeg()), *
i);
189 (*i)->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt,
AILeg::UNKNOWN);
200 (*current)->clearResolveCircularWait();
202 checkSpeedAdjustment(
id, geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
203 bool needsTaxiClearance = (*current)->getAircraft()->getTaxiClearanceRequest();
204 time_t now =
globals->get_time_params()->get_cur_time();
208 if (!needsTaxiClearance) {
209 checkHoldPosition(
id, geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
211 (*i)->setResolveCircularWait();
219 (*current)->setHoldPosition(
true);
220 int state = (*current)->getState();
233 (*current)->getAircraft()->setTaxiClearanceRequest(
false);
234 (*current)->setHoldPosition(
false);
254void FGGroundController::checkSpeedAdjustment(
int id,
double lat,
255 double lon,
double heading,
256 double speed,
double alt) {
264 SG_LOG(SG_ATC, SG_ALERT,
265 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment (" <<
id <<
")" );
271 if (blocker!=
nullptr) {
272 int oldWaitsForId = (*i)->getWaitsForId();
273 (*i)->setWaitsForId(blocker->getId());
274 if(oldWaitsForId!=blocker->getId()) {
275 (*i)->setWaitingSince(now);
278 double distM = SGGeodesy::distanceM((*i)->getPos(), blocker->getPos());
279 double sizeA = (*i)->getRadius()*2;
280 double sizeB = blocker->getRadius();
281 double distanceSlowdown = std::min((distM-20-sizeB), sizeA);
282 double speedCorrection = std::min(std::max((distanceSlowdown/sizeA),0.0),1.0);
283 int newSpeed = blocker->getSpeed() * speedCorrection;
284 newSpeed = newSpeed>2?newSpeed:0;
285 int waittime = (now-(*i)->getWaitingSince());
286 const sgDebugPriority level = waittime > 6000?SG_DEV_WARN:SG_DEBUG;
287 if (blocker->getWaitsForId()) {
288 SG_LOG(SG_ATC, level,
289 (*i)->getCallsign() <<
"(" << (*i)->getId() <<
") is blocked by " << blocker->getCallsign() <<
"(" << blocker->getId() <<
") for " << waittime <<
" seconds which is blocked by (" << blocker->getWaitsForId() <<
") new speed " << newSpeed <<
" Dist : " << distM <<
" Other speed : " << blocker->getSpeed());
290 if (blocker->getWaitsForId() == (*i)->getId()) {
291 SG_LOG(SG_ATC, level,
292 (*i)->getCallsign() <<
"(" << (*i)->getId() <<
") circular ");
295 SG_LOG(SG_ATC, level,
296 (*i)->getCallsign() <<
"(" << (*i)->getId() <<
") is blocked by " << blocker->getCallsign() <<
"(" << blocker->getId() <<
") for " << waittime <<
" seconds new speed " << newSpeed <<
" Dist : " << distM <<
" Other speed : " << blocker->getSpeed());
299 (*i)->setSpeedAdjustment(newSpeed);
301 if (oldWaitsForId!=blocker->getId()) {
303 (*i)->setRequestHoldPosition(
true);
308 int oldWaitsForId = (*i)->getWaitsForId();
309 int waitTime = now - (*i)->getWaitingSince();
310 if (oldWaitsForId>0 && waitTime > 5) {
311 SG_LOG(SG_ATC, SG_DEBUG,
312 (*i)->getCallsign() <<
"(" << (*i)->getId() <<
") cleared of blocker (" << oldWaitsForId <<
")");
313 (*i)->setResumeTaxi(
true);
314 (*i)->clearSpeedAdjustment();
315 (*i)->setWaitingSince(0);
316 (*i)->setWaitsForId(0);
330void FGGroundController::checkHoldPosition(
int id,
double lat,
331 double lon,
double heading,
332 double speed,
double alt)
339 if ((*i)->getId() ==
id) {
353 SG_LOG(SG_ATC, SG_ALERT,
354 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " );
358 (*current)->setHoldPosition(
true);
363 (*current)->setHoldPosition(
false);
364 (*current)->clearSpeedAdjustment();
371 if ((*current)->getRequestHoldPosition()) {
373 SG_LOG(SG_ATC, SG_DEBUG,
"Transmitting hold short instruction ");
375 (*current)->setRequestHoldPosition(
false);
376 (*current)->setHoldPosition(
true);
381 SG_LOG(SG_ATC, SG_BULK,
"Current transmit state " << (*current)->getState());
383 if ((*current)->getResumeTaxi()) {
385 SG_LOG(SG_ATC, SG_DEBUG,
"Transmitting resume instruction ");
387 (*current)->setResumeTaxi(
false);
388 (*current)->setHoldPosition(
false);
393 SG_LOG(SG_ATC, SG_BULK,
"Current transmit state " << (*current)->getState());
404 (*current)->setHoldPosition(
true);
408 (*current)->setHoldPosition(
false);
410 if ((*current)->getTakeOffStatus() && ((*current)->getState() == 0)) {
411 SG_LOG(SG_ATC, SG_DEBUG,
"Scheduling " << (*current)->getAircraft()->getCallSign() <<
" for hold short");
441 SG_LOG(SG_ATC, SG_BULK,
"Performing circular check for " <<
id);
448 if ((*i)->getId() ==
id) {
459 SG_LOG(SG_ATC, SG_BULK,
460 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits at " );
464 target = (*current)->getWaitsForId();
469 SG_LOG(SG_ATC, SG_DEBUG,
"aircraft is waiting for user");
473 while ((target > 0) && (target !=
id) && counter++ < trafficSize) {
479 if ((*iter)->getId() == target) {
490 SG_LOG(SG_ATC, SG_BULK,
"AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits Id : " << target);
495 target = (*other)->getWaitsForId();
500 if ((*current)->getId() == (*other)->getId())
505 SG_LOG(SG_ATC, SG_BULK,
"[done] ");
507 SG_LOG(SG_ATC, SG_WARN,
508 "Detected circular wait condition: Id = " <<
id <<
509 "target = " << target);
526 SG_LOG(SG_ATC, SG_DEBUG,
527 "Added " << (aiObject)->getCallsign() <<
"(" << (aiObject)->getId() <<
") " << aiObject);
528 parent->getRunwayQueue(aiObject->getRunway())->removeFromQueue(aiObject->getId());
535 double lon,
double elev,
double hdg,
double slope)
537 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
538 obj_pos = makeZUpFrame(geod);
541 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
543 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
550 SGMaterialLib *matlib =
globals->get_matlib();
555 globals->get_scenery()->get_scene_graph()->removeChild(
group);
566 group =
new osg::Group;
570 time_t now =
globals->get_time_params()->get_cur_time();
578 const int pos = (*i)->getCurrentPosition();
581 SGGeod start = (*i)->getPos();
582 SGGeod end (segment->
getEnd()->geod());
584 double length = SGGeodesy::distanceM(start, end);
588 SGGeodesy::inverse(start, end, heading, az2, length);
589 double coveredDistance = length * 0.5;
591 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
592 SG_LOG(SG_ATC, SG_BULK,
"Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() <<
", " << center.getLongitudeDeg() <<
"). Heading = " << heading);
596 osg::MatrixTransform *obj_trans =
new osg::MatrixTransform;
597 obj_trans->setDataVariance(osg::Object::STATIC);
599 double elevationStart;
601 elevationStart =
fgGetDouble(
"/position/ground-elev-m");
603 elevationStart = ((*i)->getAircraft()->_getAltitude());
605 double elevationEnd = segment->
getEnd()->getElevationM();
606 SG_LOG(SG_ATC, SG_DEBUG,
"Using elevation " << elevationEnd);
608 if ((elevationEnd == 0) || (elevationEnd =
parent->getElevation())) {
609 SGGeod center2 = end;
610 center2.setElevationM(SG_MAX_ELEVATION_M);
616 elevationEnd =
parent->getElevation();
618 segment->
getEnd()->setElevation(elevationEnd);
620 double elevationMean = (elevationStart + elevationEnd) / 2.0;
621 double elevDiff = elevationEnd - elevationStart;
623 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
625 SG_LOG(SG_ATC, SG_DEBUG,
"1. Using mean elevation : " << elevationMean <<
" and " << slope);
627 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
629 obj_trans->setMatrix( obj_pos );
632 float width = length /2.0;
633 osg::Vec3 corner(-width, 0, 0.25f);
634 osg::Vec3 widthVec(2*width + 1, 0, 0);
635 osg::Vec3 heightVec(0, 1, 0);
636 osg::Geometry* geometry;
637 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
638 simgear::EffectGeode* geode =
new simgear::EffectGeode;
639 geode->setName(
"test");
640 geode->addDrawable(geometry);
644 mat = matlib->find(
"UnidirectionalTaperRed", center);
646 mat = matlib->find(
"UnidirectionalTaperGreen", center);
649 geode->setEffect(mat->get_effect());
650 obj_trans->addChild(geode);
653 group->addChild( obj_trans );
656 SG_LOG(SG_ATC, SG_INFO,
"BIG FAT WARNING: current position is here : " << pos);
659 for (
intVecIterator j = (*i)->getIntentions().begin(); j != (*i)->getIntentions().end(); j++) {
663 osg::MatrixTransform *obj_trans =
new osg::MatrixTransform;
664 obj_trans->setDataVariance(osg::Object::STATIC);
667 double elevationStart = segmentK->
getStart()->getElevationM();
668 double elevationEnd = segmentK->
getEnd ()->getElevationM();
669 if ((elevationStart == 0) || (elevationStart ==
parent->getElevation())) {
670 SGGeod center2 = segmentK->
getStart()->geod();
671 center2.setElevationM(SG_MAX_ELEVATION_M);
677 elevationStart =
parent->getElevation();
679 segmentK->
getStart()->setElevation(elevationStart);
681 if ((elevationEnd == 0) || (elevationEnd ==
parent->getElevation())) {
682 SGGeod center2 = segmentK->
getEnd()->geod();
683 center2.setElevationM(SG_MAX_ELEVATION_M);
689 elevationEnd =
parent->getElevation();
691 segmentK->
getEnd()->setElevation(elevationEnd);
694 double elevationMean = (elevationStart + elevationEnd) / 2.0;
695 double elevDiff = elevationEnd - elevationStart;
697 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
699 SG_LOG(SG_ATC, SG_DEBUG,
"2. Using mean elevation : " << elevationMean <<
" and " << slope);
701 SGGeod segCenter = segmentK->
getCenter();
702 WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), segCenter.getLongitudeDeg(),
703 elevationMean+ 0.5, -(segmentK->
getHeading()), slope );
705 obj_trans->setMatrix( obj_pos );
708 float width = segmentK->
getLength() /2.0;
709 osg::Vec3 corner(-width, 0, 0.25f);
710 osg::Vec3 widthVec(2*width + 1, 0, 0);
711 osg::Vec3 heightVec(0, 1, 0);
712 osg::Geometry* geometry;
713 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
714 simgear::EffectGeode* geode =
new simgear::EffectGeode;
715 geode->setName(
"test");
716 geode->addDrawable(geometry);
720 mat = matlib->find(
"UnidirectionalTaperRed", segCenter);
722 mat = matlib->find(
"UnidirectionalTaperGreen", segCenter);
725 geode->setEffect(mat->get_effect());
726 obj_trans->addChild(geode);
729 group->addChild( obj_trans );
734 globals->get_scenery()->get_scene_graph()->addChild(
group);
739 return string(
parent->parent()->getName() +
"-ground");
744 time_t now =
globals->get_time_params()->get_cur_time();
755 for (
i = startupTraffic.begin();
i != startupTraffic.end(); ++
i) {
756 updateStartupTraffic(
i, priority, now);
760 updateActiveTraffic(
i, priority, now);
770 if (!(*i)->getAircraft()) {
771 SG_LOG(SG_ATC, SG_ALERT,
"updateStartupTraffic: missing aircraft");
775 if (!(*i)->getAircraft()->getPerformance()) {
776 SG_LOG(SG_ATC, SG_ALERT,
"updateStartupTraffic: missing aircraft performance");
785 (*i)->allowPushBack();
786 (*i)->setPriority(priority++);
793 if (!(*i)->getAircraft()) {
794 SG_LOG(SG_ATC, SG_ALERT,
"updateActiveTraffic: missing aircraft");
798 if ((*i)->getAircraft()->getDie()) {
803 if (!(*i)->getAircraft()->getPerformance()) {
804 SG_LOG(SG_ATC, SG_ALERT,
"updateActiveTraffic: missing aircraft performance");
808 (*i)->setPriority(priority++);
812int FGGroundController::getFrequency() {
813 int groundFreq =
parent->getGroundFrequency(2);
814 int towerFreq =
parent->getTowerFrequency(2);
815 return groundFreq>0?groundFreq:towerFreq;
static void WorldCoordinate(osg::Matrix &obj_pos, double lat, double lon, double elev, double hdg, double slope)
bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b)
PerformanceData * getPerformance()
const std::string & getCallSign() const
time_t getArrivalTime() const
TrafficVectorIterator searchActiveTraffic(int id) const
Search activeTraffic vector to find matching id.
bool isUserAircraft(FGAIAircraft *)
virtual void handover(SGSharedPtr< FGTrafficRecord > aiObject, int leg)
We share the traffic record much like real life.
virtual void signOff(int id)
Sign off the aircraft with the id from this controller.
FGAirportDynamics * parent
SGSharedPtr< AirportGroundRadar > airportGroundRadar
TrafficVector activeTraffic
void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible)
@ MSG_REQUEST_TAXI_CLEARANCE
@ MSG_ISSUE_TAXI_CLEARANCE
@ MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT
@ MSG_SWITCH_TOWER_FREQUENCY
@ MSG_ACKNOWLEDGE_HOLD_POSITION
@ MSG_ACKNOWLEDGE_RESUME_TAXI
@ MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY
@ MSG_ACKNOWLEDGE_TAXI_CLEARANCE
@ MSG_REPORT_RUNWAY_HOLD_SHORT
bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, AtcMsgDir msgDir)
SGTime * get_time_params() const
FGGroundController(FGAirportDynamics *par)
virtual std::string getName() const
virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
virtual void handover(SGSharedPtr< FGTrafficRecord > aiObject, int leg)
We share the traffic record much like real life.
virtual void updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt)
The ground network can deal with the following states: 0 = Normal; no action required 1 = "Acknowledg...
virtual void render(bool)
Draw visible taxi routes.
bool checkForCircularWaits(int id)
Check whether situations occur where the current aircraft is waiting for itself due to higher order i...
virtual void update(double dt)
void unblockAllSegments(time_t now)
FGTaxiSegment * findSegment(const FGTaxiNode *from, const FGTaxiNode *to) const
Find the taxiway segment joining two (ground-net) nodes.
bool get_elevation_m(const SGGeod &geod, double &alt, const simgear::BVHMaterial **material, const osg::Node *butNotFrom=0)
Compute the elevation of the scenery at geodetic latitude lat, geodetic longitude lon and not higher ...
FGTaxiNodeRef getEnd() const
bool hasBlock(time_t now)
FGTaxiNodeRef getStart() const
double getHeading() const
std::vector< int >::iterator intVecIterator
@ ACK_SWITCH_GROUND_TOWER
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
std::list< SGSharedPtr< FGTrafficRecord > >::const_iterator TrafficVectorIterator
std::list< SGSharedPtr< FGTrafficRecord > > TrafficVector