From 3667e5de3cd7408b7acd242b4ddcd3af84d1fe4b Mon Sep 17 00:00:00 2001 From: portree_kid Date: Thu, 29 Dec 2022 22:21:23 +0100 Subject: [PATCH] AI/ATC * Added Messages for Takeoff * Added Messages for Hold & Holdpatterns * More enums instead of Magic Numbers --- src/AIModel/AIAircraft.cxx | 59 ++++++++++---- src/AIModel/AIAircraft.hxx | 11 ++- src/AIModel/AIFlightPlan.hxx | 3 +- src/AIModel/AIFlightPlanCreate.cxx | 97 +++++++++++------------ src/ATC/ATCController.cxx | 85 ++++++++++++++++---- src/ATC/ATCController.hxx | 61 ++++++++------ src/ATC/ApproachController.cxx | 36 ++++++++- src/ATC/GroundController.cxx | 73 ++++++----------- src/ATC/GroundController.hxx | 4 +- src/ATC/StartupController.cxx | 80 +++++++------------ src/ATC/StartupController.hxx | 5 -- src/ATC/TowerController.cxx | 46 ++++++++--- src/ATC/atc_mgr.cxx | 6 +- src/ATC/trafficcontrol.cxx | 47 ++++++++--- src/ATC/trafficcontrol.hxx | 35 +++++++- test_suite/unit_tests/AI/test_traffic.cxx | 4 +- 16 files changed, 400 insertions(+), 252 deletions(-) diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index d8765bee1..f5dd3c7a8 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -69,10 +69,10 @@ FGAIAircraft::FGAIAircraft(FGAISchedule* ref) : /* HOT must be disabled for AI A groundOffset = 0; } - fp = 0; - controller = 0; - prevController = 0; - towerController = 0; + fp = nullptr; + controller = nullptr; + prevController = nullptr; + towerController = nullptr; dt_count = 0; dt_elev_count = 0; use_perf_vs = true; @@ -604,11 +604,13 @@ bool FGAIAircraft::loadNextLeg(double distance) { } else { double cruiseAlt = trafficRef->getCruiseAlt() * 100; - SG_LOG(SG_AI, SG_BULK, getCallSign() << "|Loading Leg " << leg+1); + int nextLeg = determineNextLeg(leg); + + SG_LOG(SG_AI, SG_BULK, getCallSign() << "|Loading Leg " << nextLeg); bool ok = fp->create (this, dep, arr, - leg+1, + nextLeg, cruiseAlt, trafficRef->getSpeed(), _getLatitude(), @@ -721,10 +723,10 @@ void FGAIAircraft::announcePositionToController() { } break; case AILeg::APPROACH: - if (trafficRef->getArrivalAirport()->getDynamics()) { - controller = trafficRef->getArrivalAirport()->getDynamics()->getApproachController(); - } - break; + if (trafficRef->getArrivalAirport()->getDynamics()) { + controller = trafficRef->getArrivalAirport()->getDynamics()->getApproachController(); + } + break; case AILeg::PARKING_TAXI: // Taxiing for parking if (trafficRef->getArrivalAirport()->getDynamics()->getGroundController()->exists()) controller = trafficRef->getArrivalAirport()->getDynamics()->getGroundController(); @@ -749,7 +751,7 @@ void FGAIAircraft::announcePositionToController() { } } -void FGAIAircraft::scheduleForATCTowerDepartureControl(int state) { +void FGAIAircraft::scheduleForATCTowerDepartureControl() { if (!takeOffStatus) { int leg = fp->getLeg(); if (trafficRef) { @@ -765,7 +767,7 @@ void FGAIAircraft::scheduleForATCTowerDepartureControl(int state) { } } } - takeOffStatus = state; + takeOffStatus = AITakeOffStatus::QUEUED; } // Process ATC instructions and report back @@ -782,7 +784,17 @@ void FGAIAircraft::processATC(const FGATCInstruction& instruction) { // let an offending aircraft take an evasive action // for instance taxi back a little bit. } - if (instruction.getHoldPattern ()) {} + switch (fp->getLeg()) + { + case AILeg::STARTUP_PUSHBACK: + case AILeg::APPROACH: + break; + default: + break; + } + if (instruction.getHoldPattern ()) { + //holdtime = instruction.getHoldTime(); + } // Hold Position if (instruction.getHoldPosition ()) { @@ -1048,11 +1060,14 @@ bool FGAIAircraft::handleAirportEndPoints(FGAIWaypoint* prev, time_t now) { AccelTo(0.0); } if (prev->contains("legend")) { - fp->incrementLeg(); + int nextLeg = determineNextLeg(fp->getLeg()); + while (nextLeg!=fp->getLeg()) { + fp->incrementLeg(); + } } if (prev->contains(string("DepartureHold"))) { //cerr << "Passing point DepartureHold" << endl; - scheduleForATCTowerDepartureControl(1); + scheduleForATCTowerDepartureControl(); } if (prev->contains(string("Accel"))) { takeOffStatus = AITakeOffStatus::CLEARED_FOR_TAKEOFF; @@ -1387,6 +1402,20 @@ const string& FGAIAircraft::atGate() return empty; } +int FGAIAircraft::determineNextLeg(int leg) { + if (leg==AILeg::APPROACH) { + time_t now = globals->get_time_params()->get_cur_time(); + if (controller->getInstruction(getID()).getRunwaySlot() + > now) { + return AILeg::HOLD; + } else { + return AILeg::LANDING; + } + } else { + return leg+1; + } +} + void FGAIAircraft::handleATCRequests(double dt) { if (!this->getTrafficRef()) { diff --git a/src/AIModel/AIAircraft.hxx b/src/AIModel/AIAircraft.hxx index 0393bef2d..c4e95af1f 100644 --- a/src/AIModel/AIAircraft.hxx +++ b/src/AIModel/AIAircraft.hxx @@ -44,9 +44,10 @@ namespace AILeg CLIMB = 4, CRUISE = 5, APPROACH = 6, - LANDING = 7, - PARKING_TAXI = 8, - PARKING = 9 + HOLD = 7, + LANDING = 8, + PARKING_TAXI = 9, + PARKING = 10 }; } @@ -122,7 +123,7 @@ public: int getTakeOffStatus() { return takeOffStatus; }; void setTakeOffSlot(time_t timeSlot) { takeOffTimeSlot = timeSlot;}; time_t getTakeOffSlot(){return takeOffTimeSlot;}; - void scheduleForATCTowerDepartureControl(int state); + void scheduleForATCTowerDepartureControl(); const std::string& GetTransponderCode() { return transponderCode; }; void SetTransponderCode(const std::string& tc) { transponderCode = tc;}; @@ -200,6 +201,8 @@ private: /**Handle special cases for the User AI shadow*/ void updateUserFlightPlan(double dt); + /**Calculate the next leg (hold or not)*/ + int determineNextLeg(int leg); void handleATCRequests(double dt); inline bool isStationary() { diff --git a/src/AIModel/AIFlightPlan.hxx b/src/AIModel/AIFlightPlan.hxx index 94a2b4e02..5af552f69 100644 --- a/src/AIModel/AIFlightPlan.hxx +++ b/src/AIModel/AIFlightPlan.hxx @@ -185,7 +185,7 @@ public: double checkTrackLength(const std::string& wptName) const; time_t getStartTime() const { return start_time; } - time_t getArrivalTime() const { return arrivalTime; } + time_t getArrivalTime() const { return arrivalTime; } bool create(FGAIAircraft *, FGAirport *dep, FGAirport *arr, int leg, double alt, double speed, double lat, double lon, bool firstLeg, double radius, const std::string& fltType, const std::string& aircraftType, const std::string& airline, double distance); @@ -268,6 +268,7 @@ private: bool createClimb(FGAIAircraft *, bool, FGAirport *, FGAirport* arrival, double, double, const std::string&); bool createCruise(FGAIAircraft *, bool, FGAirport*, FGAirport*, const SGGeod& current, double, double, const std::string&); bool createDescent(FGAIAircraft *, FGAirport *, const SGGeod& current, double speed, double alt,const std::string&, double distance); + bool createHold(FGAIAircraft *, FGAirport *, const SGGeod& current, double speed, double alt,const std::string&, double distance); bool createLanding(FGAIAircraft *, FGAirport *, const std::string&); bool createParking(FGAIAircraft *, FGAirport *, double radius); void deleteWaypoints(); diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index d0a933058..86cba393c 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -101,6 +101,10 @@ bool FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, retVal = createDescent(ac, arr, SGGeod::fromDeg(longitude, latitude), speed, alt, fltType, distance); break; + case AILeg::HOLD: + retVal = createHold(ac, arr, SGGeod::fromDeg(longitude, latitude), speed, alt, fltType, + distance); + break; case AILeg::LANDING: retVal = createLanding(ac, arr, fltType); break; @@ -775,7 +779,7 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, heading); if (!apt->hasRunwayWithIdent(activeRunway)) { SG_LOG(SG_AI, SG_WARN, ac->getCallSign() << - "| FGAIFlightPlan::createLanding: No such runway " << activeRunway << " at " << apt->ident()); + "| FGAIFlightPlan::createDescent: No such runway " << activeRunway << " at " << apt->ident()); return false; } @@ -1018,63 +1022,56 @@ bool FGAIFlightPlan::createDescent(FGAIAircraft * ac, createArc(ac, secondaryTarget, ac->_getHeading()+rightAngle, heading+rightAngle, firstTurnIncrement, initialTurnRadius, ac->getAltitude(), altDiff/3, vDescent, "straight_turn_%03d"); } - // To prevent absurdly steep approaches, compute the origin from where the approach should have started - SGGeod origin; - - if (distance < requiredDistance * 0.8) { - reposition = true; - SGGeodesy::direct(waypoints.back()->getPos(), azimuth, - requiredDistance, origin, dummyAz2); - - double addDD = SGGeodesy::courseDeg(refPoint, initialTarget); - distance = SGGeodesy::distanceM(current, initialTarget); - azimuth = SGGeodesy::courseDeg(current, initialTarget); - } else { - // We project a new point out of line so the aircraft has room to turn to new heading. - origin = SGGeodesy::direct(current, ac->getTrueHeadingDeg(), initialTurnRadius); - distance = SGGeodesy::distanceM(origin, initialTarget); - azimuth = SGGeodesy::courseDeg(origin, initialTarget); - } - - // Calculate the ETA at final, based on remaining distance, and approach speed. - // distance should really consist of flying time to terniary target, plus circle - // but the distance to secondary target should work as a reasonable approximation - // aditionally add the amount of distance covered by making a turn of "side" - time_t remaining = - (initialTurnRadius + distance) / ((vDescent * SG_NM_TO_METER) / 3600.0); time_t now = globals->get_time_params()->get_cur_time(); - //if (ac->getTrafficRef()->getCallSign() == fgGetString("/ai/track-callsign")) { - // cerr << " Arrival time estimation: turn angle " << side << ". Turn distance " << turnDistance << ". Linear distance " << distance << ". Time to go " << remaining << endl; - // //exit(1); - //} - - time_t eta = now + remaining; + arrivalTime = now; //choose a distance to the runway such that it will take at least 60 seconds more // time to get there than the previous aircraft. // Don't bother when aircraft need to be repositioned, because that marks the initialization phased... - - time_t newEta; - - if (reposition == false) { - newEta = - apt->getDynamics()->getApproachController()->getRunway(rwy->name())->requestTimeSlot(eta); - } else { - newEta = eta; - } - - arrivalTime = newEta; - time_t additionalTimeNeeded = newEta - eta; - SG_LOG(SG_AI, SG_BULK, "Additional time required " << additionalTimeNeeded << " "); - - //Number of holds - double holdsPatterns = (additionalTimeNeeded-additionalTimeNeeded%240)/240; - - additionalTimeNeeded -= holdsPatterns*240; - return true; } +/******************************************************************* + * CreateHold + * Generate a hold flight path from the permission to land point + * Exactly one hold pattern + ******************************************************************/ +bool FGAIFlightPlan::createHold(FGAIAircraft * ac, + FGAirport * apt, + const SGGeod& current, + double speed, + double alt, + const string & fltType, + double requiredDistance) +{ + //Beginning of Descent + const string& rwyClass = getRunwayClassFromTrafficType(fltType); + double vDescent = ac->getPerformance()->vDescent(); + double vApproach = ac->getPerformance()->vApproach(); + double initialTurnRadius = getTurnRadius(vDescent, true); + double heading = ac->getTrueHeadingDeg(); + apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, + heading); + if (!apt->hasRunwayWithIdent(activeRunway)) { + SG_LOG(SG_AI, SG_WARN, ac->getCallSign() << + "| FGAIFlightPlan::createHold: No such runway " << activeRunway << " at " << apt->ident()); + return false; + } + FGRunwayRef rwy = apt->getRunwayByIdent(activeRunway); + double currentAltitude = waypoints.back()->getAltitude(); + double distanceOut = apt->getDynamics()->getApproachController()->getRunway(rwy->name())->getApproachDistance(); //12 * SG_NM_TO_METER; + double lateralOffset = initialTurnRadius; + + SGGeod secondaryTarget = rwy->pointOffCenterline(-2 * distanceOut, lateralOffset); + SGGeod secondHoldCenter = rwy->pointOffCenterline(-4 * distanceOut, lateralOffset); + + createArc(ac, secondaryTarget, rwy->headingDeg()-90, rwy->headingDeg()+90, 5, initialTurnRadius, + currentAltitude, 0, vDescent, "hold_1_%03d"); + createArc(ac, secondHoldCenter, rwy->headingDeg()+90, rwy->headingDeg()-90, 5, initialTurnRadius, + currentAltitude, 0, vDescent, "hold_2_%03d"); + + return true; +} /** * compute the distance along the centerline, to the ILS glideslope * transmitter. Return -1 if there's no GS for the runway diff --git a/src/ATC/ATCController.cxx b/src/ATC/ATCController.cxx index 03daf01da..030ed4b69 100644 --- a/src/ATC/ATCController.cxx +++ b/src/ATC/ATCController.cxx @@ -102,6 +102,36 @@ bool FGATCController::isUserAircraft(FGAIAircraft* ac) return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; }; +bool FGATCController::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, + AtcMsgDir msgDir) +{ + int state = i->getState(); + if ((state >= minState) && (state <= maxState) && available) { + if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { + SG_LOG(SG_ATC, SG_BULK, "Checking state " << state << " for " << i->getAircraft()->getCallSign()); + SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); + int n = trans_num->getIntValue(); + if (n == 0) { + trans_num->setIntValue(-1); + // PopupCallback(n); + SG_LOG(SG_ATC, SG_DEBUG, "Selected transmission message " << n); + //auto atc = globals->get_subsystem(); + //FGATCDialogNew::instance()->removeEntry(1); + } else { + SG_LOG(SG_ATC, SG_BULK, "Sending message for " << i->getAircraft()->getCallSign()); + transmit(&(*i), parent, msgId, msgDir, false); + return false; + } + } + transmit(&(*i), parent, msgId, msgDir, true); + i->updateState(); + lastTransmission = now; + available = false; + return true; + } + return false; +} + void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible) { @@ -109,7 +139,6 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, int stationFreq = 0; int taxiFreq = 0; int towerFreq = 0; - int freqId = 0; string atisInformation; string text; string taxiFreqStr; @@ -133,14 +162,20 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, instructionText = "taxi"; } - SG_LOG(SG_ATC, SG_DEBUG, "transmitting for: " << sender << "Leg = " << rec->getLeg()); + SG_LOG(SG_ATC, SG_BULK, "transmitting for: " << sender << " at Leg " << rec->getLeg()); + //FIXME move departure and arrival to rec auto depApt = rec->getAircraft()->getTrafficRef()->getDepartureAirport(); + auto arrApt = rec->getAircraft()->getTrafficRef()->getArrivalAirport(); if (!depApt) { SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit"); return; } + if (!arrApt) { + SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty arrival airport, can't transmit"); + return; + } stationFreq = getFrequency(); taxiFreq = depApt->getDynamics()->getGroundFrequency(2); @@ -281,16 +316,12 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, text = receiver + ". Holding short runway " + activeRunway + ". " + sender + "."; - //text = "test1"; - SG_LOG(SG_ATC, SG_DEBUG, "1 Currently at leg " << rec->getLeg()); break; case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT: activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); text = receiver + " Roger. Holding short runway " // + activeRunway + ". " + sender + "."; - //text = "test2"; - SG_LOG(SG_ATC, SG_DEBUG, "2 Currently at leg " << rec->getLeg()); break; case MSG_CLEARED_FOR_TAKEOFF: activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); @@ -300,23 +331,40 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, case MSG_ACKNOWLEDGE_CLEARED_FOR_TAKEOFF: activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); text = receiver + " Roger. Cleared for takeoff runway " + activeRunway + ". " + sender + "."; - //text = "test2"; break; case MSG_SWITCH_TOWER_FREQUENCY: towerFreqStr = formatATCFrequency3_2(towerFreq); text = receiver + " Contact Tower at " + towerFreqStr + ". " + sender + "."; - //text = "test3"; - SG_LOG(SG_ATC, SG_DEBUG, "3 Currently at leg " << rec->getLeg()); break; case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY: towerFreqStr = formatATCFrequency3_2(towerFreq); text = receiver + " Roger, switching to tower at " + towerFreqStr + ". " + sender + "."; - //text = "test4"; - SG_LOG(SG_ATC, SG_DEBUG, "4 Currently at leg " << rec->getLeg()); + break; + case MSG_ARRIVAL: + text = receiver + ". " + sender + " Information delta."; + break; + case MSG_ACKNOWLEDGE_ARRIVAL: + activeRunway = rec->getRunway(); + text = receiver + " expect ILS approach " + activeRunway + ". " + sender; + break; + case MSG_CLEARED_TO_LAND: + activeRunway = rec->getRunway(); + //TODO Weather + text = receiver + " runway " + activeRunway + " cleared to land. " + sender; + break; + case MSG_ACKNOWLEDGE_CLEARED_TO_LAND: + activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); + text = receiver + " runway " + activeRunway + " cleared to land. " + sender; + break; + case MSG_HOLD: + text = receiver + " hold as published . " + sender; + break; + case MSG_ACKNOWLEDGE_HOLD: + text = receiver + " holding as published . " + sender; break; default: //text = "test3"; - text = text + sender + ". Transmitting unknown Message."; + text = text + sender + ". Transmitting unknown Message. MsgId " + std::to_string(msgId); break; } @@ -341,12 +389,10 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, ((onBoardRadioFreqI0 == stationFreq)|| (onBoardRadioFreqI1 == stationFreq))) { if (rec->allowTransmissions()) { - if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) { SG_LOG(SG_ATC, SG_DEBUG, "Using ITM radio propagation"); FGRadioTransmission* radio = new FGRadioTransmission(); SGGeod sender_pos; - double sender_alt_ft, sender_alt; if(ground_to_air) { sender_pos = parent->parent()->geod(); } @@ -368,6 +414,11 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, } } +/** + * Sign off the aircraft with the id from this controller. + * @param id the id of the aircraft +*/ + void FGATCController::signOff(int id) { TrafficVectorIterator i = searchActiveTraffic(id); @@ -377,8 +428,12 @@ void FGATCController::signOff(int id) "AI error: Aircraft without traffic record is signing off from " << getName() << " at " << SG_ORIGIN << " list " << activeTraffic.empty()); return; } + SG_LOG(SG_ATC, SG_DEBUG, i->getCallsign() << " (" << i->getId() << ") signing off from " << getName() << "(" << getFrequency() << ")"); + int oldSize = activeTraffic.size(); activeTraffic.erase(i); - SG_LOG(SG_ATC, SG_DEBUG, i->getCallsign() << " signing off from " << getName() ); + if ((oldSize-activeTraffic.size()) != 1) { + SG_LOG(SG_ATC, SG_WARN, i->getCallsign() << " not removed "); + } } bool FGATCController::hasInstruction(int id) diff --git a/src/ATC/ATCController.hxx b/src/ATC/ATCController.hxx index 036d32043..4526f00af 100644 --- a/src/ATC/ATCController.hxx +++ b/src/ATC/ATCController.hxx @@ -39,28 +39,35 @@ namespace ATCMessageState { enum Type { -// 0 = Normal; no action required -NORMAL = 0, -// 1 = "Acknowledge "Hold position -ACK_HOLD = 1, -// 2 = "Acknowledge "Resume taxi". -ACK_RESUME_TAXI = 2, -// 3 = "Issue TaxiClearance" -TAXI_CLEARED = 3, -// 4 = Acknowledge Taxi Clearance" -ACK_TAXI_CLEARED = 4, -// 5 = Post acknowlegde taxiclearance: Start taxiing -START_TAXI = 5, -// 6 = Report runway -REPORT_RUNWAY = 6, -// 7 = Acknowledge report runway -ACK_REPORT_RUNWAY = 7, -// 8 = Switch tower frequency -SWITCH_TOWER = 8, -// 9 = Acknowledge switch tower frequency -ACK_SWITCH_TOWER = 9, -// 10 = Cleared for takeoff -CLEARED_TAKEOFF = 10, + // 0 = Normal; no action required + NORMAL, + // 1 = "Acknowledge "Hold position + ACK_HOLD, + // 2 = "Acknowledge "Resume taxi". + ACK_RESUME_TAXI, + // 3 = "Issue TaxiClearance" + TAXI_CLEARED = 3, + // 4 = Acknowledge Taxi Clearance" + ACK_TAXI_CLEARED = 4, + // 5 = Post acknowlegde taxiclearance: Start taxiing + START_TAXI = 5, + // 6 = Report runway + REPORT_RUNWAY = 6, + // 7 = Acknowledge report runway + ACK_REPORT_RUNWAY = 7, + // 8 = Switch tower frequency + SWITCH_GROUND_TOWER = 8, + // 9 = Acknowledge switch tower frequency + ACK_SWITCH_GROUND_TOWER = 9, + // 10 = Cleared for takeoff + CLEARED_TAKEOFF, + ACK_CLEARED_TAKEOFF, + ANNOUNCE_ARRIVAL, + ACK_ARRIVAL, + HOLD, + CLEARED_TO_LAND, + ACK_CLEARED_TO_LAND, + LANDING_TAXI }; } @@ -118,7 +125,13 @@ public: MSG_CLEARED_FOR_TAKEOFF, MSG_ACKNOWLEDGE_CLEARED_FOR_TAKEOFF, MSG_SWITCH_TOWER_FREQUENCY, - MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY + MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, + MSG_ARRIVAL, + MSG_ACKNOWLEDGE_ARRIVAL, + MSG_HOLD, + MSG_ACKNOWLEDGE_HOLD, + MSG_CLEARED_TO_LAND, + MSG_ACKNOWLEDGE_CLEARED_TO_LAND } AtcMsgId; typedef enum { @@ -135,6 +148,8 @@ public: FGAIAircraft *aircraft) = 0; virtual void updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt) = 0; + bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, + AtcMsgDir msgDir); virtual void signOff(int id); bool hasInstruction(int id); diff --git a/src/ATC/ApproachController.cxx b/src/ATC/ApproachController.cxx index c6bb720de..a384637b7 100644 --- a/src/ATC/ApproachController.cxx +++ b/src/ATC/ApproachController.cxx @@ -98,9 +98,11 @@ void FGApproachController::announcePosition(int id, rec.setLeg(leg); rec.setCallsign(ref->getCallSign()); rec.setAircraft(ref); + rec.setPlannedArrivalTime(intendedRoute->getArrivalTime()); activeTraffic.push_back(rec); } else { i->setPositionAndHeading(lat, lon, heading, speed, alt); + i->setPlannedArrivalTime(intendedRoute->getArrivalTime()); } } @@ -108,6 +110,7 @@ void FGApproachController::updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt) { + time_t now = globals->get_time_params()->get_cur_time(); // Search activeTraffic for a record matching our id TrafficVectorIterator i = FGATCController::searchActiveTraffic(id); TrafficVectorIterator current; @@ -115,7 +118,7 @@ void FGApproachController::updateAircraftInformation(int id, SGGeod geod, // update position of the current aircraft if (i == activeTraffic.end() || activeTraffic.empty()) { SG_LOG(SG_ATC, SG_ALERT, - "AI error: updating aircraft without traffic record at " << SG_ORIGIN); + "FGApproachController updating aircraft without traffic record at " << SG_ORIGIN); } else { i->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt); current = i; @@ -146,13 +149,42 @@ void FGApproachController::updateAircraftInformation(int id, SGGeod geod, } else { current->clearSpeedAdjustment(); } + if ((now - lastTransmission) > 15) { + available = true; + } + //Start of our status runimplicit "announce arrival" + if (checkTransmissionState(ATCMessageState::NORMAL, ATCMessageState::NORMAL, current, now, MSG_ARRIVAL, ATC_AIR_TO_GROUND)) { + current->setRunwaySlot(getRunway(current->getRunway())->requestTimeSlot(current->getPlannedArrivalTime())); + current->setState(ATCMessageState::ACK_ARRIVAL); + } + if (checkTransmissionState(ATCMessageState::ACK_ARRIVAL, ATCMessageState::ACK_ARRIVAL, current, now, MSG_ACKNOWLEDGE_ARRIVAL, ATC_GROUND_TO_AIR)) { + if (current->getRunwaySlot() > current->getPlannedArrivalTime()) { + current->setState(ATCMessageState::HOLD); + } else { + current->setState(ATCMessageState::CLEARED_TO_LAND); + } + } + if (checkTransmissionState(ATCMessageState::HOLD, ATCMessageState::HOLD, current, now, MSG_ACKNOWLEDGE_HOLD, ATC_AIR_TO_GROUND)) { + current->setState(ATCMessageState::ACK_HOLD); + } + if (checkTransmissionState(ATCMessageState::CLEARED_TO_LAND, ATCMessageState::CLEARED_TO_LAND, current, now, MSG_CLEARED_TO_LAND, ATC_GROUND_TO_AIR)) { + current->setState(ATCMessageState::ACK_CLEARED_TO_LAND); + } + if (checkTransmissionState(ATCMessageState::ACK_CLEARED_TO_LAND, ATCMessageState::ACK_CLEARED_TO_LAND, current, now, MSG_ACKNOWLEDGE_CLEARED_TO_LAND, ATC_AIR_TO_GROUND)) { + current->setState(ATCMessageState::SWITCH_GROUND_TOWER); + } + if (checkTransmissionState(ATCMessageState::SWITCH_GROUND_TOWER, ATCMessageState::SWITCH_GROUND_TOWER, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) { + } + if (checkTransmissionState(ATCMessageState::ACK_SWITCH_GROUND_TOWER, ATCMessageState::ACK_SWITCH_GROUND_TOWER, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) { + current->setState(ATCMessageState::LANDING_TAXI); + } } //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff); } setDt(getDt() + dt); } -/* Periodically check for and remove dead traffic records */ +/** Periodically check for and remove dead traffic records */ void FGApproachController::update(double dt) { FGATCController::eraseDeadTraffic(); diff --git a/src/ATC/GroundController.cxx b/src/ATC/GroundController.cxx index fdb3929fd..c1b0c121c 100644 --- a/src/ATC/GroundController.cxx +++ b/src/ATC/GroundController.cxx @@ -130,7 +130,7 @@ void FGGroundController::announcePosition(int id, } } -/* +/** * The ground network can deal with the following states: * 0 = Normal; no action required * 1 = "Acknowledge "Hold position @@ -143,35 +143,6 @@ void FGGroundController::announcePosition(int id, * 8 = Switch tower frequency * 9 = Acknowledge switch tower frequency */ -bool FGGroundController::checkTransmissionState(int minState, int maxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, - AtcMsgDir msgDir) -{ - int state = i->getState(); - if ((state >= minState) && (state <= maxState) && available) { - if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { - SG_LOG(SG_ATC, SG_DEBUG, "Checking state " << state << " for " << i->getAircraft()->getCallSign()); - SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - int n = trans_num->getIntValue(); - if (n == 0) { - trans_num->setIntValue(-1); - // PopupCallback(n); - SG_LOG(SG_ATC, SG_DEBUG, "Selected transmission message " << n); - //auto atc = globals->get_subsystem(); - //FGATCDialogNew::instance()->removeEntry(1); - } else { - SG_LOG(SG_ATC, SG_DEBUG, "creating message for " << i->getAircraft()->getCallSign()); - transmit(&(*i), parent, msgId, msgDir, false); - return false; - } - } - transmit(&(*i), parent, msgId, msgDir, true); - i->updateState(); - lastTransmission = now; - available = false; - return true; - } - return false; -} void FGGroundController::updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, @@ -221,26 +192,25 @@ void FGGroundController::updateAircraftInformation(int id, SGGeod geod, if ((now - lastTransmission) > 15) { available = true; } - if (checkTransmissionState(0,2, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::NORMAL,ATCMessageState::ACK_RESUME_TAXI, current, now, MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) { current->setState(ATCMessageState::TAXI_CLEARED); } - if (checkTransmissionState(3,3, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) { + if (checkTransmissionState(ATCMessageState::TAXI_CLEARED,ATCMessageState::TAXI_CLEARED, current, now, MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR)) { current->setState(ATCMessageState::ACK_TAXI_CLEARED); } - if (checkTransmissionState(4,4, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::ACK_TAXI_CLEARED,ATCMessageState::ACK_TAXI_CLEARED, current, now, MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND)) { current->setState(ATCMessageState::START_TAXI); } - if ((state == 5) && available) { + if ((state == ATCMessageState::START_TAXI) && available) { current->setState(ATCMessageState::NORMAL); current->getAircraft()->setTaxiClearanceRequest(false); current->setHoldPosition(false); available = false; } - } } -/* +/** * Scan for a speed adjustment change. Find the nearest aircraft that is in front * and adjust speed when we get too close. Only do this when current position and/or * intentions of the current aircraft match current taxiroute position of the proximate @@ -310,7 +280,7 @@ void FGGroundController::checkSpeedAdjustment(int id, double lat, if( current->getId() == i->getId()) { continue; } - SG_LOG(SG_ATC, SG_BULK, "Comparing " << current->getCallsign() << " and " << i->getCallsign()); + SG_LOG(SG_ATC, SG_BULK, current->getCallsign() << "| Comparing with " << i->getCallsign() << " Id: " << i->getId()); SGGeod other = i->getPos(); SGGeodesy::inverse(curr, other, course, az2, dist); bearing = fabs(heading - course); @@ -394,7 +364,7 @@ void FGGroundController::checkSpeedAdjustment(int id, double lat, } } -/* +/** * Check for "Hold position instruction". * The hold position should be issued under the following conditions: * 1) For aircraft entering or crossing a runway with active traffic on it, or landing aircraft near it @@ -427,12 +397,15 @@ void FGGroundController::checkHoldPosition(int id, double lat, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " << SG_ORIGIN); } current = i; - if (current->getAircraft()->getTakeOffStatus() == 1) { + if (current->getAircraft()->getTakeOffStatus() == AITakeOffStatus::QUEUED) { current->setHoldPosition(true); return; } - if (current->getAircraft()->getTakeOffStatus() == 2) { - SG_LOG(SG_ATC, SG_DEBUG, current->getAircraft()->getCallSign() << ". Taxi in position and hold"); + if ((now - lastTransmission) > 15) { + available = true; + } + + if (current->getAircraft()->getTakeOffStatus() == AITakeOffStatus::CLEARED_FOR_TAKEOFF) { current->setHoldPosition(false); current->clearSpeedAdjustment(); return; @@ -485,7 +458,7 @@ void FGGroundController::checkHoldPosition(int id, double lat, if ((now - lastTransmission) > 2) { available = true; } - if (current->getState() == 0) { + if (current->getState() == ATCMessageState::NORMAL) { if ((origStatus != currStatus) && available) { SG_LOG(SG_ATC, SG_DEBUG, "Issuing hold short instruction " << currStatus << " " << available); if (currStatus == true) { // No has a hold short instruction @@ -511,11 +484,11 @@ void FGGroundController::checkHoldPosition(int id, double lat, //9 = Acknowledge switch tower frequency //int state = current->getState(); - if (checkTransmissionState(1,1, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::ACK_HOLD, ATCMessageState::ACK_HOLD, current, now, MSG_ACKNOWLEDGE_HOLD_POSITION, ATC_AIR_TO_GROUND)) { current->setState(ATCMessageState::NORMAL); current->setHoldPosition(true); } - if (checkTransmissionState(2,2, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::ACK_RESUME_TAXI, ATCMessageState::ACK_RESUME_TAXI, current, now, MSG_ACKNOWLEDGE_RESUME_TAXI, ATC_AIR_TO_GROUND)) { current->setState(ATCMessageState::NORMAL); current->setHoldPosition(false); } @@ -523,13 +496,13 @@ void FGGroundController::checkHoldPosition(int id, double lat, SG_LOG(SG_ATC, SG_DEBUG, "Scheduling " << current->getAircraft()->getCallSign() << " for hold short"); current->setState(ATCMessageState::REPORT_RUNWAY); } - if (checkTransmissionState(6,6, current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::REPORT_RUNWAY ,ATCMessageState::REPORT_RUNWAY , current, now, MSG_REPORT_RUNWAY_HOLD_SHORT, ATC_AIR_TO_GROUND)) { } - if (checkTransmissionState(7,7, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) { + if (checkTransmissionState(ATCMessageState::ACK_REPORT_RUNWAY, ATCMessageState::ACK_REPORT_RUNWAY, current, now, MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT, ATC_GROUND_TO_AIR)) { } - if (checkTransmissionState(8,8, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) { + if (checkTransmissionState(ATCMessageState::SWITCH_GROUND_TOWER, ATCMessageState::SWITCH_GROUND_TOWER, current, now, MSG_SWITCH_TOWER_FREQUENCY, ATC_GROUND_TO_AIR)) { } - if (checkTransmissionState(9,9, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) { + if (checkTransmissionState(ATCMessageState::ACK_SWITCH_GROUND_TOWER, ATCMessageState::ACK_SWITCH_GROUND_TOWER, current, now, MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY, ATC_AIR_TO_GROUND)) { } @@ -580,7 +553,7 @@ bool FGGroundController::checkForCircularWaits(int id) int counter = 0; if (id == target) { - SG_LOG(SG_ATC, SG_DEBUG, "aircraft waits for user"); + SG_LOG(SG_ATC, SG_DEBUG, "aircraft is waiting for user"); return false; } @@ -641,7 +614,7 @@ static void WorldCoordinate(osg::Matrix& obj_pos, double lat, 0.0, 1.0, 0.0)); } -/* Draw visible taxi routes */ +/** Draw visible taxi routes */ void FGGroundController::render(bool visible) { SGMaterialLib *matlib = globals->get_matlib(); diff --git a/src/ATC/GroundController.hxx b/src/ATC/GroundController.hxx index 1f47764f0..9e4c093c9 100644 --- a/src/ATC/GroundController.hxx +++ b/src/ATC/GroundController.hxx @@ -76,9 +76,7 @@ public: double radius, int leg, FGAIAircraft *aircraft); virtual void updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt); - bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, - AtcMsgDir msgDir); - bool checkForCircularWaits(int id); + bool checkForCircularWaits(int id); virtual void render(bool); virtual std::string getName(); virtual void update(double dt); diff --git a/src/ATC/StartupController.cxx b/src/ATC/StartupController.cxx index 2871b8538..207173849 100644 --- a/src/ATC/StartupController.cxx +++ b/src/ATC/StartupController.cxx @@ -105,41 +105,7 @@ void FGStartupController::announcePosition(int id, } } -bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, - AtcMsgDir msgDir) -{ - int state = i->getState(); - if ((state == st) && available) { - if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) { - - SG_LOG(SG_ATC, SG_BULK, "Checking state " << st << " for " << i->getAircraft()->getCallSign()); - SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true); - int n = trans_num->getIntValue(); - if (n == 0) { - trans_num->setIntValue(-1); - // PopupCallback(n); - SG_LOG(SG_ATC, SG_BULK, "Selected transmission message " << n); - //FGATCDialogNew::instance()->removeEntry(1); - } else { - SG_LOG(SG_ATC, SG_BULK, "Creating message for " << i->getAircraft()->getCallSign()); - transmit(&(*i), &(*parent), msgId, msgDir, false); - return false; - } - } - if (now > startTime) { - SG_LOG(SG_ATC, SG_BULK, "Transmitting startup msg"); - transmit(&(*i), &(*parent), msgId, msgDir, true); - i->updateState(); - lastTransmission = now; - available = false; - return true; - } - } - return false; -} - -void FGStartupController::updateAircraftInformation(int id, SGGeod geod, - double heading, double speed, double alt, +void FGStartupController::updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt) { // Search activeTraffic for a record matching our id @@ -171,7 +137,8 @@ void FGStartupController::updateAircraftInformation(int id, SGGeod geod, time_t now = globals->get_time_params()->get_cur_time(); - if ((startTime - now) > 0) { + if (((startTime - now) > 60 && (startTime - now)%60 == 0) || + ((startTime - now) < 60 && (startTime - now) > 0)) { SG_LOG(SG_ATC, SG_BULK, i->getAircraft()->getTrafficRef()->getCallSign() << " is scheduled to depart in " << startTime - now << " seconds. Available = " << available << " at parking " << getGateName(i->getAircraft())); } @@ -179,20 +146,31 @@ void FGStartupController::updateAircraftInformation(int id, SGGeod geod, available = true; } - checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); - checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); - checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); - checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); - if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) { - i->nextFrequency(); + if (now >(startTime + 0)) { + checkTransmissionState(ATCMessageState::NORMAL, ATCMessageState::NORMAL, i, now, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); } - checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND); - checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR); - checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND); - - - - if ((state == 8) && available) { + if (now >(startTime + 60)) { + checkTransmissionState(ATCMessageState::ACK_HOLD, ATCMessageState::ACK_HOLD, i, now, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); + } + if (now >(startTime + 80)) { + checkTransmissionState(ATCMessageState::ACK_RESUME_TAXI, ATCMessageState::ACK_RESUME_TAXI, i, now, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); + } + if (now >(startTime + 100)) { + checkTransmissionState(ATCMessageState::TAXI_CLEARED, ATCMessageState::TAXI_CLEARED, i, now, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); + } + if (now >(startTime + 130)) { + checkTransmissionState(ATCMessageState::ACK_TAXI_CLEARED, ATCMessageState::ACK_TAXI_CLEARED, i, now, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND); + } + if (now >(startTime + 140)) { + checkTransmissionState(ATCMessageState::START_TAXI, ATCMessageState::START_TAXI, i, now, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND); + } + if (now >(startTime + 150)) { + checkTransmissionState(ATCMessageState::REPORT_RUNWAY, ATCMessageState::REPORT_RUNWAY, i, now, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR); + } + if (now >(startTime + 180)) { + checkTransmissionState(ATCMessageState::ACK_REPORT_RUNWAY, ATCMessageState::ACK_REPORT_RUNWAY, i, now, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND); + } + if ((state == ATCMessageState::SWITCH_GROUND_TOWER) && available) { if (now > startTime + 200) { if (i->pushBackAllowed()) { i->allowRepeatedTransmissions(); @@ -208,7 +186,7 @@ void FGStartupController::updateAircraftInformation(int id, SGGeod geod, available = false; } } - if ((state == 9) && available) { + if ((state == ATCMessageState::ACK_SWITCH_GROUND_TOWER) && available) { i->setHoldPosition(false); } } @@ -230,7 +208,6 @@ static void WorldCoordinate(osg::Matrix& obj_pos, double lat, void FGStartupController::render(bool visible) { - SG_LOG(SG_ATC, SG_DEBUG, "Rendering startup controller"); SGMaterialLib *matlib = globals->get_matlib(); if (group) { //int nr = ; @@ -245,6 +222,7 @@ void FGStartupController::render(bool visible) group = 0; } if (visible) { + SG_LOG(SG_ATC, SG_BULK, "Rendering startup controller"); group = new osg::Group; FGScenery * local_scenery = globals->get_scenery(); //double elevation_meters = 0.0; diff --git a/src/ATC/StartupController.hxx b/src/ATC/StartupController.hxx index f4dd55e90..5b2be9584 100644 --- a/src/ATC/StartupController.hxx +++ b/src/ATC/StartupController.hxx @@ -61,11 +61,6 @@ public: virtual void render(bool); virtual std::string getName(); virtual void update(double dt); - - // Hpoefully, we can move this function to the base class, but I need to verify what is needed for the other controllers before doing so. - bool checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId, - AtcMsgDir msgDir); - }; #endif \ No newline at end of file diff --git a/src/ATC/TowerController.cxx b/src/ATC/TowerController.cxx index 487957c42..d8f87be21 100644 --- a/src/ATC/TowerController.cxx +++ b/src/ATC/TowerController.cxx @@ -96,7 +96,7 @@ void FGTowerController::announcePosition(int id, rec.setPositionAndHeading(lat, lon, heading, speed, alt); rec.setRunway(intendedRoute->getRunway()); rec.setLeg(leg); - //rec.setCallSign(callsign); + rec.setCallsign(ref->getCallSign()); rec.setRadius(radius); rec.setAircraft(ref); activeTraffic.push_back(rec); @@ -113,13 +113,19 @@ void FGTowerController::announcePosition(int id, if (rwy == activeRunways.end()) { ActiveRunway aRwy(intendedRoute->getRunway(), id); aRwy.addToDepartureQueue(ref); + time_t now = globals->get_time_params()->get_cur_time(); + rec.setPlannedArrivalTime(now); + rec.setRunwaySlot(aRwy.requestTimeSlot(now)); activeRunways.push_back(aRwy); rwy = (activeRunways.end()-1); } else { + time_t now = globals->get_time_params()->get_cur_time(); + rec.setPlannedArrivalTime(now); + rec.setRunwaySlot(rwy->requestTimeSlot(now)); rwy->addToDepartureQueue(ref); } - SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getdepartureQueueSize() << " for takeoff "); + SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getdepartureQueueSize() << " for takeoff from " << rwy->getRunwayName()); } else { i->setPositionAndHeading(lat, lon, heading, speed, alt); } @@ -134,6 +140,7 @@ void FGTowerController::updateAircraftInformation(int id, SGGeod geod, setDt(getDt() + dt); + time_t now = globals->get_time_params()->get_cur_time(); if (i == activeTraffic.end() || (activeTraffic.empty())) { SG_LOG(SG_ATC, SG_ALERT, "AI error: updating aircraft without traffic record at " << @@ -184,30 +191,43 @@ void FGTowerController::updateAircraftInformation(int id, SGGeod geod, if (ac->getTakeOffStatus() == AITakeOffStatus::QUEUED) { // transmit takeoff clearance ac->setTakeOffStatus(AITakeOffStatus::CLEARED_FOR_TAKEOFF); - transmit(&(*i), &(*parent), MSG_CLEARED_FOR_TAKEOFF, ATC_GROUND_TO_AIR, true); - i->setState(ATCMessageState::CLEARED_TAKEOFF); + TrafficVectorIterator first = searchActiveTraffic(ac->getID()); + //FIXME use checkTransmissionState + if (first == activeTraffic.end() || activeTraffic.empty()) { + SG_LOG(SG_ATC, SG_ALERT, + "FGApproachController updating aircraft without traffic record at " << SG_ORIGIN); + } else { + first->setState(ATCMessageState::CLEARED_TAKEOFF); + transmit(&(*first), &(*parent), MSG_CLEARED_FOR_TAKEOFF, ATC_GROUND_TO_AIR, true); + } } } //FIXME Make it an member of traffic record - if (current.getAircraft()->getTakeOffStatus() == AITakeOffStatus::CLEARED_FOR_TAKEOFF) { + if (current.getAircraft()->getTakeOffStatus() == AITakeOffStatus::CLEARED_FOR_TAKEOFF && + current.getRunwaySlot() < now) { current.setHoldPosition(false); + if (checkTransmissionState(ATCMessageState::CLEARED_TAKEOFF, ATCMessageState::CLEARED_TAKEOFF, i, now, MSG_ACKNOWLEDGE_CLEARED_FOR_TAKEOFF, ATC_AIR_TO_GROUND)) { + current.setState(ATCMessageState::ANNOUNCE_ARRIVAL); + } } else { current.setHoldPosition(true); + SG_LOG(SG_ATC, SG_BULK, + current.getCallsign() << "| Waiting for " << (current.getRunwaySlot() - now) << " seconds"); } int clearanceId = rwy->getCleared(); if (clearanceId) { if (id == clearanceId) { - SG_LOG(SG_ATC, SG_BULK, "Unset Hold " << clearanceId << " for " << rwy->getRunwayName()); + SG_LOG(SG_ATC, SG_BULK, current.getCallsign() << "| Unset Hold " << clearanceId << " for rwy " << rwy->getRunwayName()); current.setHoldPosition(false); } else { - SG_LOG(SG_ATC, SG_WARN, "Not cleared " << id << " Currently cleared " << clearanceId); + SG_LOG(SG_ATC, SG_BULK, current.getCallsign() << "| Not cleared " << id << " Currently cleared " << clearanceId); } } else { if (current.getAircraft() == rwy->getFirstAircraftInDepartureQueue()) { SG_LOG(SG_ATC, SG_BULK, - "Cleared " << current.getAircraft()->getCallSign() << " for " << rwy->getRunwayName() << " Id " << id); + current.getCallsign() << "| Cleared for runway " << getName() << " " << rwy->getRunwayName() << " Id " << id); rwy->setCleared(id); - auto ac = rwy->getFirstOfStatus(1); + auto ac = rwy->getFirstOfStatus(AITakeOffStatus::QUEUED); if (ac) { ac->setTakeOffStatus(AITakeOffStatus::QUEUED); // transmit takeoff clearacne? But why twice? @@ -241,9 +261,8 @@ void FGTowerController::signOff(int id) }); if (runwayIt != activeRunways.end()) { - SG_LOG(SG_ATC, SG_BULK, "Cleared " << id << " from " << runwayIt->getRunwayName() ); - runwayIt->setCleared(0); - runwayIt->updateDepartureQueue(); + SG_LOG(SG_ATC, SG_BULK, i->getCallsign() << "|Cleared " << id << " from " << runwayIt->getRunwayName() << " cleared " << runwayIt->getCleared() ); + runwayIt->removeFromDepartureQueue(id); } else { SG_LOG(SG_ATC, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN); @@ -294,9 +313,10 @@ void FGTowerController::render(bool visible) { } string FGTowerController::getName() { - return string(parent->getId() + "-tower"); + return string(parent->parent()->getName() + "-tower"); } + void FGTowerController::update(double dt) { FGATCController::eraseDeadTraffic(); diff --git a/src/ATC/atc_mgr.cxx b/src/ATC/atc_mgr.cxx index 7424edcc5..e4019b5c5 100644 --- a/src/ATC/atc_mgr.cxx +++ b/src/ATC/atc_mgr.cxx @@ -59,9 +59,9 @@ FGATCManager::~FGATCManager() { } /** -Sets up ATC subsystem parts depending on other subsystems -Override of SGSubsystem::postinit() -Will set private boolean flag "initSucceeded" to true upon conclusion +* Sets up ATC subsystem parts depending on other subsystems +* Override of SGSubsystem::postinit() +* Will set private boolean flag "initSucceeded" to true upon conclusion */ void FGATCManager::postinit() { diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 5b8aa03e3..841a2f0f2 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -67,12 +67,23 @@ ActiveRunway::ActiveRunway(const std::string& r, int cc) : distanceToFinal = 6.0 * SG_NM_TO_METER; }; +void ActiveRunway::removeFromDepartureQueue(int id) { + if (id!=currentlyCleared) { + printDepartureQueue(); + SG_LOG(SG_ATC, SG_WARN, "Not cleared id being removed from DepartureQueue " << id << " currently cleared " << currentlyCleared); + } else { + setCleared(0); + updateDepartureQueue(); + printDepartureQueue(); + } +} + void ActiveRunway::updateDepartureQueue() { departureQueue.erase(departureQueue.begin()); } -/* +/** * Fetch next slot for the active runway * @param eta time of slot requested * @return newEta: next slot available; starts at eta paramater @@ -92,12 +103,12 @@ time_t ActiveRunway::requestTimeSlot(time_t eta) // if the aircraft is the first arrival, add to the vector and return eta directly if (estimatedArrivalTimes.empty()) { estimatedArrivalTimes.push_back(eta); - SG_LOG(SG_ATC, SG_DEBUG, getRunwayName() << "Checked eta slots, using " << eta); + SG_LOG(SG_ATC, SG_BULK, getRunwayName() << " Checked eta slots, using " << eta); return eta; } else { // First check the already assigned slots to see where we need to fit the flight in TimeVectorIterator i = estimatedArrivalTimes.begin(); - SG_LOG(SG_ATC, SG_DEBUG, getRunwayName() << " Checking eta slots " << eta << " : " << estimatedArrivalTimes.size() << " Timediff " << (eta - globals->get_time_params()->get_cur_time())); + SG_LOG(SG_ATC, SG_BULK, getRunwayName() << " Checking eta slots " << eta << " : " << estimatedArrivalTimes.size() << " Timediff : " << (eta - globals->get_time_params()->get_cur_time())); // is this needed - just a debug output? for (i = estimatedArrivalTimes.begin(); @@ -110,7 +121,7 @@ time_t ActiveRunway::requestTimeSlot(time_t eta) if ((eta + separation) < (*i)) { newEta = eta; SG_LOG(SG_ATC, SG_BULK, "Storing at beginning"); - SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta); + SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta ); slotHousekeeping(newEta); return newEta; } @@ -128,7 +139,7 @@ time_t ActiveRunway::requestTimeSlot(time_t eta) newEta = (*i) + separation; SG_LOG(SG_ATC, SG_BULK, "Storing at end + separation"); } - SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta); + SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta << " Timediff : " << (newEta-eta)); slotHousekeeping(newEta); return newEta; } else { @@ -197,7 +208,7 @@ void ActiveRunway::slotHousekeeping(time_t newEta) } } -/* Output the contents of the departure queue vector nicely formatted*/ +/** Output the contents of the departure queue vector nicely formatted*/ void ActiveRunway::printDepartureQueue() { SG_LOG(SG_ATC, SG_DEBUG, "Departure queue for " << rwy << ": "); @@ -208,7 +219,7 @@ void ActiveRunway::printDepartureQueue() } -/* Fetch the first aircraft in the departure queue with a certain status */ +/** Fetch the first aircraft in the departure queue with a certain status */ SGSharedPtrActiveRunway::getFirstOfStatus(int stat) const { auto it = std::find_if(departureQueue.begin(), departureQueue.end(), [stat](const SGSharedPtr& acft) { @@ -316,10 +327,14 @@ bool FGTrafficRecord::isDead() const { bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other) { bool result = false; - SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|checkPositionAndIntentions"); + SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| checkPositionAndIntentions CurrentPos : " << currentPos << " Other : " << other.currentPos << " Leg : " << leg << " Other Leg : " << other.leg ); if (currentPos == other.currentPos && getId() != other.getId() ) { - SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|Check Position and intentions: " << other.getCallsign() << " we are on the same taxiway; Index = " << currentPos); - result = true; + SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| Check Position and intentions: " << other.getCallsign() << " we are on the same taxiway; Index = " << currentPos); + int headingTowards = SGGeodesy::courseDeg( other.getPos(), getPos() ); + int headingDiff = SGMiscd::normalizePeriodic(-180, 180, headingTowards - getHeading() ); + SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| " << heading << "\t" << headingTowards << "\t" << headingDiff); + // getHeading() + result = abs(headingDiff) < 89; } // else if (! other.intentions.empty()) // { @@ -331,8 +346,8 @@ bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other) // SG_LOG(SG_ATC, SG_BULK, "Check Position and intentions: current matches other.intentions"); // result = true; // } - else if (! intentions.empty()) { - SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|Itentions " << intentions.size()); + else if (!intentions.empty()) { + SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| Itentions Size " << intentions.size()); intVecIterator i = intentions.begin(); //while (!((i == intentions.end()) || ((*i) == other.currentPos))) while (i != intentions.end()) { @@ -343,7 +358,13 @@ bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other) } if (i != intentions.end()) { SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| Check Position and intentions: " << other.getCallsign()<< " matches Index = " << (*i)); - result = true; + int headingTowards = SGGeodesy::courseDeg( other.getPos(), getPos() ); + int distanceM = SGGeodesy::distanceM( other.getPos(), getPos() ); + int headingDiff = SGMiscd::normalizePeriodic(-180, 180, headingTowards - getHeading() ); + SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| Heading : " << heading << "\t Heading Other->Current" << headingTowards << "\t Heading Diff :" << headingDiff << "\t Distance : " << distanceM); + // difference of heading is small and it's actually near + result = abs(headingDiff) < 89 && distanceM < 400; +// result = true; } } return result; diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 6bf7ac786..f2556793b 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -64,6 +64,7 @@ class FGATCInstruction { private: bool holdPattern; + int requestedArrivalTime; bool holdPosition; bool changeSpeed; bool changeHeading; @@ -80,6 +81,12 @@ public: bool getHoldPattern () const { return holdPattern; }; + void setRunwaySlot (int val) { + requestedArrivalTime = val; + }; + int getRunwaySlot () const { + return requestedArrivalTime; + }; bool getHoldPosition () const { return holdPosition; }; @@ -156,6 +163,7 @@ private: bool allowTransmission; bool allowPushback; int priority; + int plannedArrivalTime; time_t timer; intVec intentions; FGATCInstruction instruction; @@ -186,9 +194,15 @@ public: int getId() const { return id; }; + /** + * Return the current ATC State of type @see ATCMessageState + */ int getState() const { return state; }; + /** + * Set the current ATC State of type @see ATCMessageState + */ void setState(int s) { state = s; } @@ -212,7 +226,23 @@ public: bool getSpeedAdjustment() const { return instruction.getChangeSpeed(); }; - + void setPlannedArrivalTime (int val) { + plannedArrivalTime = val; + }; + /**Arrival time planned by aircraft.*/ + int getPlannedArrivalTime () const { + return plannedArrivalTime; + }; + void setRunwaySlot( int val ) { + if (plannedArrivalTime) { + SG_LOG(SG_ATC, SG_BULK, callsign << "| Runwayslot " << (val-plannedArrivalTime)); + } + instruction.setRunwaySlot(val); + }; + /**Arrival time requested by ATC.*/ + int getRunwaySlot() { + return instruction.getRunwaySlot(); + }; SGGeod getPos() { return pos; } @@ -251,7 +281,6 @@ public: void setHoldPosition (bool inst) { instruction.setHoldPosition(inst); }; - void setWaitsForId(int id) { waitsForId = id; }; @@ -368,6 +397,8 @@ public: SGSharedPtr getFirstOfStatus(int stat) const; + void removeFromDepartureQueue(int id); + void updateDepartureQueue(); void printDepartureQueue(); diff --git a/test_suite/unit_tests/AI/test_traffic.cxx b/test_suite/unit_tests/AI/test_traffic.cxx index a6e83cdd1..feed94992 100644 --- a/test_suite/unit_tests/AI/test_traffic.cxx +++ b/test_suite/unit_tests/AI/test_traffic.cxx @@ -1031,7 +1031,7 @@ FGAIAircraft * TrafficTests::flyAI(SGSharedPtr aiAircraft, std::st int startSpeed = aiAircraft->GetFlightPlan()->getCurrentWaypoint()->getSpeed(); aiAircraft->AccelTo(startSpeed); - for (size_t i = 0; i < 12000000 && !(aiAircraft->getDie()) && aiAircraft->GetFlightPlan()->getLeg() < 10; i++) { + for (size_t i = 0; i < 12000000 && !(aiAircraft->getDie()) && aiAircraft->GetFlightPlan()->getLeg() <= AILeg::PARKING; i++) { CPPUNIT_ASSERT_EQUAL(aiAircraft->GetFlightPlan()->isValidPlan(), true); if (!aiAircraft->getDie()) { // collect position @@ -1072,7 +1072,7 @@ FGAIAircraft * TrafficTests::flyAI(SGSharedPtr aiAircraft, std::st aiAircraft->dumpCSV(csvFile, lineIndex++); // A flight without loops should never reach 400° CPPUNIT_ASSERT_LESSEQUAL(400.0, headingSum); - CPPUNIT_ASSERT_LESSEQUAL(10, aiAircraft->GetFlightPlan()->getLeg()); + CPPUNIT_ASSERT_LESSEQUAL( 10, aiAircraft->GetFlightPlan()->getLeg()); CPPUNIT_ASSERT_MESSAGE( "Aircraft has not completed test in time.", i < 3000000); // Arrived at a parking int beforeNextDepTime = aiAircraft->getTrafficRef()->getDepartureTime() - 30;