30#include <osg/Geometry>
31#include <osg/MatrixTransform>
34#include <simgear/scene/material/EffectGeode.hxx>
35#include <simgear/scene/material/matlib.hxx>
36#include <simgear/scene/material/mat.hxx>
37#include <simgear/scene/util/OsgMath.hxx>
38#include <simgear/timing/sg_time.hxx>
39#include <simgear/math/sg_geodesy.hxx>
66 SG_LOG(SG_ATC, SG_DEBUG,
"ActiveRunway " << icao <<
"/" << r <<
" " << cc );
67 currentlyCleared = cc;
68 distanceToFinal = 6.0 * SG_NM_TO_METER;
72 SG_LOG(SG_ATC, SG_DEBUG,
"Removed from RunwayQueue " << rwy <<
" " <<
id );
73 auto it = std::find_if(runwayQueue.begin(), runwayQueue.end(), [
id](
const SGSharedPtr<FGTrafficRecord> acft) {
74 return acft->getId() == id;
76 if (it == runwayQueue.end()) {
77 SG_LOG(SG_ATC, SG_WARN,
"Erasing non existant aircraft " << rwy <<
" " <<
id );
81 runwayQueue.erase(it);
87 SG_LOG(SG_ATC, SG_WARN,
"updateDepartureQueue " << runwayQueue.size());
88 runwayQueue.erase(runwayQueue.begin());
89 SG_LOG(SG_ATC, SG_WARN,
"updateDepartureQueue " << runwayQueue.size());
100 time_t eta = trafficRecord->getPlannedArrivalTime();
103 if (
get( trafficRecord->getId())==
nullptr) {
105 runwayQueue.push_back(trafficRecord);
110 if (runwayQueue.empty()) {
112 SG_LOG(SG_ATC, SG_DEBUG, icao <<
"/" <<
getRunwayName() <<
" Checked eta slots, using " << eta);
115 SG_LOG(SG_ATC, SG_DEBUG, icao <<
"/" <<
getRunwayName() <<
" Checking eta slots " << eta <<
" : " << runwayQueue.size() <<
" Timediff : " << (eta -
globals->get_time_params()->get_cur_time()));
118 std::vector<SGSharedPtr<FGTrafficRecord>>::iterator
i;
119 for (
i = runwayQueue.begin();
120 i != runwayQueue.end(); ++
i) {
121 SG_LOG(SG_ATC, SG_DEBUG,
"Stored time : " << (*i)->getPlannedArrivalTime());
125 i = runwayQueue.begin();
126 if ((eta +
SEPARATION) < (*i)->getPlannedArrivalTime()) {
128 SG_LOG(SG_ATC, SG_DEBUG,
"Added to start. New ETA : " << newEta );
129 trafficRecord->setRunwaySlot(newEta);
137 while ((
i != runwayQueue.end()) && (!found)) {
138 std::vector<SGSharedPtr<FGTrafficRecord>>::iterator j =
i + 1;
141 if (j == runwayQueue.end()) {
142 if (((*i)->getPlannedArrivalTime() +
SEPARATION) < eta) {
143 SG_LOG(SG_ATC, SG_DEBUG,
"Storing at end");
146 newEta = (*i)->getPlannedArrivalTime() +
SEPARATION;
147 SG_LOG(SG_ATC, SG_DEBUG,
"Storing at end + SEPARATION");
149 SG_LOG(SG_ATC, SG_DEBUG,
"End. New ETA : " << newEta <<
" Timediff : " << (newEta-eta));
150 trafficRecord->setRunwaySlot(newEta);
158 if ((((*j)->getPlannedArrivalTime() - (*i)->getPlannedArrivalTime()) > (
SEPARATION * 2))) {
162 SG_LOG(SG_ATC, SG_DEBUG,
"Found potential slot after " << (*
i));
163 if (eta > (*i)->getPlannedArrivalTime() && (eta < (*j)->getPlannedArrivalTime())) {
165 if (eta < ((*i)->getPlannedArrivalTime() +
SEPARATION)) {
166 newEta = (*i)->getPlannedArrivalTime() +
SEPARATION;
167 SG_LOG(SG_ATC, SG_DEBUG,
"Using original" << (*i)->getPlannedArrivalTime() <<
" + SEPARATION ");
170 SG_LOG(SG_ATC, SG_DEBUG,
"Using original after " << (*i)->getPlannedArrivalTime());
172 }
else if (eta < (*i)->getPlannedArrivalTime()) {
174 newEta = (*i)->getPlannedArrivalTime() +
SEPARATION;
175 SG_LOG(SG_ATC, SG_DEBUG,
"Using delayed slot after " << (*i)->getPlannedArrivalTime());
194 SG_LOG(SG_ATC, SG_DEBUG,
"Done. New ETA : " << newEta <<
" " << rwy <<
" Size : " << runwayQueue.size() <<
" " << trafficRecord->getCallsign() );
195 trafficRecord->setRunwaySlot(newEta);
208 time_t eta = trafficRecord->getPlannedArrivalTime();
209 time_t now =
globals->get_time_params()->get_cur_time();
211 newETA = std::max(newETA, now);
213 SG_LOG(SG_ATC, SG_DEBUG,
"Update First " << eta <<
" " << newETA <<
" " << now <<
" " << rwy <<
" Leg " << trafficRecord->getLeg() <<
" Size : " << runwayQueue.size() <<
" " << trafficRecord->getCallsign() );
217 for (SGSharedPtr<FGTrafficRecord> queueRecord: runwayQueue) {
218 if (trafficRecord->getId() == queueRecord->getId()) {
220 diff = std::max((time_t)0, diff);
221 SG_LOG(SG_ATC, SG_DEBUG, queueRecord->getCallsign() <<
"(" << queueRecord->getId() <<
")" <<
" Diff " << diff );
222 trafficRecord->setPlannedArrivalTime(newETA);
223 queueRecord->setRunwaySlot(queueRecord->getRunwaySlot() + diff);
225 queueRecord->setRunwaySlot(queueRecord->getRunwaySlot() + diff);
226 SG_LOG(SG_ATC, SG_DEBUG, queueRecord->getCallsign() <<
"(" << queueRecord->getId() <<
")" <<
" Diff " << diff );
235 time_t now =
globals->get_time_params()->get_cur_time();
237 SG_LOG(SG_ATC, SG_DEBUG,
"Runway Queue for " << icao <<
"/" << rwy <<
" Size : " << runwayQueue.size());
238 for (
auto acft : runwayQueue) {
239 SG_LOG(SG_ATC, SG_DEBUG,
" " << acft->getCallsign() <<
"(" << acft->getId() <<
") Leg : " << acft->getLeg() <<
" Diff : " << acft->getRunwaySlot() - now <<
" " << acft->getRunwaySlot() <<
" " << acft->getPlannedArrivalTime() <<
" " << acft->getPos().getLatitudeDeg() <<
" " << acft->getPos().getLongitudeDeg() <<
" Speed " << acft->getSpeed() <<
" Elevation " << acft->getPos().getElevationM());
247 auto it = std::find_if(runwayQueue.begin(), runwayQueue.end(), [
id](
const SGSharedPtr<FGTrafficRecord> acft) {
248 return acft->getId() == id;
251 if (it == runwayQueue.end()) {
261 auto it = std::find_if(runwayQueue.begin(), runwayQueue.end(), [stat](
const SGSharedPtr<FGTrafficRecord> acft) {
262 return acft->getTakeOffStatus() == stat;
265 if (it == runwayQueue.end()) {
275 if (runwayQueue.empty()) {
279 return *runwayQueue.begin();
285 assert(!ac->getAircraft());
286 assert(!ac->getAircraft()->getDie());
288 runwayQueue.push_back(std::move(ac));
303 allowTransmission(true),
307 heading(0), speed(0), altitude(0), radius(0)
318 SG_LOG(SG_AI, SG_BULK,
"Traffic record position: " << pos);
320 if (runway==
"" && route) {
335 return aircraft->
getDie();
340 aircraft->clearATCController();
346 if(aircraft.valid()) {
347 return aircraft.ptr();
358 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| checkPositionAndIntentions CurrentPos : " << currentPos <<
" Other : " << other.currentPos <<
" Leg : " << leg <<
" Other Leg : " << other.leg );
359 if (currentPos == other.currentPos &&
getId() != other.
getId() ) {
360 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| Check Position and intentions: " << other.
getCallsign() <<
" we are on the same taxiway; Index = " << currentPos);
361 int headingTowards = SGGeodesy::courseDeg( other.
getPos(),
getPos() );
362 int headingDiff = SGMiscd::normalizePeriodic(-180, 180, headingTowards -
getHeading() );
363 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| " << heading <<
"\t" << headingTowards <<
"\t" << headingDiff);
365 result = abs(headingDiff) < 89;
377 else if (!intentions.empty()) {
378 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| Itentions Size " << intentions.size());
381 while (
i != intentions.end()) {
382 if ((*
i) == other.currentPos) {
387 if (
i != intentions.end()) {
388 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| Check Position and intentions: " << other.
getCallsign()<<
" matches Index = " << (*
i));
389 int headingTowards = SGGeodesy::courseDeg( other.
getPos(),
getPos() );
390 int distanceM = SGGeodesy::distanceM( other.
getPos(),
getPos() );
391 int headingDiff = SGMiscd::normalizePeriodic(-180, 180, headingTowards -
getHeading() );
392 SG_LOG(SG_ATC, SG_BULK,
getCallsign() <<
"| Heading : " << heading <<
"\t Heading Other->Current" << headingTowards <<
"\t Heading Diff :" << headingDiff <<
"\t Distance : " << distanceM);
394 result = abs(headingDiff) < 89 && distanceM < 400;
402 double hdg,
double spd,
405 this->pos = SGGeod::fromDegFt(lon, lat, alt);
406 if (heading != 0 && spd != 0) {
407 headingDiff = SGMiscd::normalizePeriodic(-180, 180, heading - hdg);
426 int currentTargetNode = 0, otherTargetNode = 0;
429 if (other.currentPos > 0)
431 if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
432 return currentTargetNode;
433 if (! intentions.empty()) {
434 for (
i = intentions.begin();
i != intentions.end(); ++
i) {
436 if (currentTargetNode ==
438 SG_LOG(SG_ATC, SG_BULK,
"Current crosses at " << currentTargetNode);
439 return currentTargetNode;
444 if (! other.intentions.empty()) {
445 for (
i = other.intentions.begin();
i != other.intentions.end(); ++
i) {
447 if (otherTargetNode ==
449 SG_LOG(SG_ATC, SG_BULK,
"Other crosses at " << currentTargetNode);
450 return otherTargetNode;
455 if (! intentions.empty() && ! other.intentions.empty()) {
456 for (
i = intentions.begin();
i != intentions.end(); ++
i) {
457 for (j = other.intentions.begin(); j != other.intentions.end(); ++j) {
458 SG_LOG(SG_ATC, SG_BULK,
"finding segment " << *
i <<
" and " << *j);
459 if (((*
i) > 0) && ((*j) > 0)) {
464 if (currentTargetNode == otherTargetNode) {
465 SG_LOG(SG_ATC, SG_BULK,
"Routes will cross at " << currentTargetNode);
466 return currentTargetNode;
478 int node = -1, othernode = -1;
481 if (other.currentPos > 0)
484 if ((node == othernode) && (node != -1))
486 if (! other.intentions.empty()) {
488 i != other.intentions.end(); ++
i) {
491 if ((node == othernode) && (node > -1))
518 SG_LOG(SG_ATC, SG_BULK,
"Current segment " << currentPos);
520 if ((currentPos > 0) && (other.currentPos > 0)) {
523 if (opp->
getIndex() == other.currentPos)
535 SG_LOG(SG_ATC, SG_BULK,
"Found the node " << node);
541 if (! other.intentions.empty()) {
543 j != other.intentions.end(); ++j) {
544 SG_LOG(SG_ATC, SG_BULK,
"Current segment 1 " << (*
i));
551 <<
" are opposites ");
553 getIndex() == node) {
555 SG_LOG(SG_ATC, SG_BULK,
"Found the node " << node);
571 if (aircraft->getDie()) {
575 time_t now =
globals->get_time_params()->get_cur_time();
576 time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
577 return ((now + margin) > deptime);
583 instruction.setChangeSpeed(
true);
584 instruction.setSpeed(spd);
589 instruction.setChangeHeading(
true);
590 instruction.setHeading(heading);
595 return allowPushback;
608 holdPosition =
false;
610 changeHeading =
false;
611 changeAltitude =
false;
612 resolveCircularWait =
false;
622 return (holdPattern || holdPosition || changeSpeed || changeHeading
623 || changeAltitude || resolveCircularWait);
const SGSharedPtr< FGTrafficRecord > get(int id) const
Fetch the first aircraft in the departure queue with a certain status.
void setCleared(int number)
ActiveRunwayQueue(const std::string &apt, const std::string &r, int cc)
void updateDepartureQueue()
const time_t SEPARATION
Separation between aircraft in seconds.
void updateFirst(SGSharedPtr< FGTrafficRecord > eta, time_t newETA)
Update the first and move all records backwards.
void requestTimeSlot(SGSharedPtr< FGTrafficRecord > eta)
Fetch next slot for the active runway.
const std::string & getRunwayName() const
const SGSharedPtr< FGTrafficRecord > getFirstAircraftInDepartureQueue() const
void printRunwayQueue() const
Output the contents of the departure queue vector nicely formatted.
const SGSharedPtr< FGTrafficRecord > getFirstOfStatus(int stat) const
Fetch the first aircraft in the departure queue with a certain status.
void removeFromQueue(int id)
void addToQueue(SGSharedPtr< FGTrafficRecord > ac)
const std::string & getRunway() const
bool hasInstruction() const
FGTaxiSegment * findSegment(const FGTaxiNode *from, const FGTaxiNode *to) const
Find the taxiway segment joining two (ground-net) nodes.
FGTaxiNodeRef getEnd() const
FGTaxiSegment * opposite()
FGTaxiNodeRef getStart() const
void setHeadingAdjustment(double heading)
bool checkPositionAndIntentions(FGTrafficRecord &other)
Check if another aircraft is ahead of the current one, and on the same taxiway.
void setPositionAndHeading(double lat, double lon, double hdg, double spd, double alt, int leg)
void setSpeedAdjustment(double spd)
bool isActive(int margin) const
void setPositionAndIntentions(int pos, FGAIFlightPlan *route)
bool pushBackAllowed() const
bool isOpposing(FGGroundNetwork *, FGTrafficRecord &other, int node)
int crosses(FGGroundNetwork *, FGTrafficRecord &other)
FGAIAircraft * getAircraft() const
double getHeading() const
const std::string & getCallsign() const
virtual ~FGTrafficRecord()
void setAircraft(FGAIAircraft *ref)
void clearATCController() const
bool onRoute(FGGroundNetwork *, FGTrafficRecord &other)
void setRunway(const std::string &rwy)
std::vector< int >::iterator intVecIterator