From 467513cbaf40a7e1bd4077d71b60eef6c881741a Mon Sep 17 00:00:00 2001 From: Durk Talsma Date: Sun, 29 Aug 2010 19:25:34 +0200 Subject: [PATCH] Major update to the AI code: * New features - More realistic descent paths - Separation during descent and approach - ATC approach controller (still silent) - inbound traffic flow will start immediately * Bug fixes - Properly handle vertical speed when on ground - Departing aircraft now wait for taxiclerance before moving - Traffic manager waits for proper weather initialization - Fixed instabilities in the preferential runway usage code - Fine tuning of waypoint following code. --- src/AIModel/AIAircraft.cxx | 150 +- src/AIModel/AIAircraft.hxx | 9 +- src/AIModel/AIFlightPlan.cxx | 31 +- src/AIModel/AIFlightPlan.hxx | 11 +- src/AIModel/AIFlightPlanCreate.cxx | 1212 ++++++++++------ src/AIModel/AIFlightPlanCreateCruise.cxx | 7 +- src/ATC/trafficcontrol.cxx | 1660 +++++++++++++--------- src/ATC/trafficcontrol.hxx | 45 +- src/Airports/dynamics.cxx | 23 +- src/Airports/dynamics.hxx | 20 +- src/Airports/groundnetwork.cxx | 65 +- src/Airports/runwaybase.cxx | 21 + src/Airports/runwaybase.hxx | 1 + src/Airports/runwayprefs.cxx | 623 ++++---- src/Traffic/Schedule.cxx | 1 - src/Traffic/TrafficMgr.cxx | 4 +- 16 files changed, 2396 insertions(+), 1487 deletions(-) diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 92f458427..ac79b97cc 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -1,4 +1,4 @@ -// // // FGAIAircraft - FGAIBase-derived class creates an AI airplane +// FGAIAircraft - FGAIBase-derived class creates an AI airplane // // Written by David Culp, started October 2003. // @@ -78,6 +78,8 @@ FGAIAircraft::FGAIAircraft(FGAISchedule *ref) : FGAIBase(otAircraft) { roll = 0; headingChangeRate = 0.0; headingError = 0; + minBearing = 360; + speedFraction =1.0; holdPos = false; needsTaxiClearance = false; @@ -257,6 +259,16 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) { return; dt_count = 0; + double distanceToDescent; + if(reachedEndOfCruise(distanceToDescent)) { + if (!loadNextLeg(distanceToDescent)) { + setDie(true); + return; + } + prev = fp->getPreviousWaypoint(); + curr = fp->getCurrentWaypoint(); + next = fp->getNextWaypoint(); + } if (! leadPointReached(curr)) { controlHeading(curr); controlSpeed(curr, next); @@ -278,7 +290,7 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) { //TODO let the fp handle this (loading of next leg) fp->IncrementWaypoint( trafficRef != 0 ); - if (!(fp->getNextWaypoint()) && trafficRef != 0) + if ( ((!(fp->getNextWaypoint()))) && (trafficRef != 0) ) if (!loadNextLeg()) { setDie(true); return; @@ -342,7 +354,7 @@ const char * FGAIAircraft::_getTransponderCode() const { } -bool FGAIAircraft::loadNextLeg() { +bool FGAIAircraft::loadNextLeg(double distance) { int leg; if ((leg = fp->getLeg()) == 10) { @@ -374,7 +386,8 @@ bool FGAIAircraft::loadNextLeg() { trafficRef->getRadius(), trafficRef->getFlightType(), acType, - company); + company, + distance); //cerr << "created leg " << leg << " for " << trafficRef->getCallSign() << endl; } return true; @@ -423,6 +436,7 @@ void FGAIAircraft::doGroundAltitude() { altitude_ft = (tgt_altitude_ft + groundOffset); else altitude_ft += 0.1 * ((tgt_altitude_ft+groundOffset) - altitude_ft); + tgt_vs = 0; } @@ -449,6 +463,11 @@ void FGAIAircraft::announcePositionToController() { cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl; } break; + case 7: + if (trafficRef->getDepartureAirport()->getDynamics()) { + controller = trafficRef->getArrivalAirport()->getDynamics()->getApproachController(); + } + break; case 9: // Taxiing for parking if (trafficRef->getArrivalAirport()->getDynamics()->getGroundNetwork()->exists()) controller = trafficRef->getArrivalAirport()->getDynamics()->getGroundNetwork(); @@ -629,13 +648,34 @@ bool FGAIAircraft::leadPointReached(FGAIFlightPlan::waypoint* curr) { // << dist_to_go << ": Lead distance " // << lead_dist << " " << curr->name // << " Ground target speed " << groundTargetSpeed << endl; - // if (trafficRef) { - // if (trafficRef->getCallSign() == "Transavia7584") { - // cerr << trafficRef->getCallSign() << " " << tgt_altitude_ft << " " << _getSpeed() << " " - // << _getAltitude() << " "<< _getLatitude() << " " << _getLongitude() << " " << dist_to_go << " " << lead_dist << curr->name << endl; - // } - // } - return dist_to_go < lead_dist; + double bearing; + if (speed > 50) { // don't do bearing calculations for ground traffic + bearing = getBearing(fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)); + if (bearing < minBearing) { + minBearing = bearing; + if (minBearing < 10) { + minBearing = 10; + } + if ((minBearing < 360.0) && (minBearing > 10.0)) { + speedFraction = cos(minBearing *SG_DEGREES_TO_RADIANS); + } else { + speedFraction = 1.0; + } + } + } + if (trafficRef) { + //cerr << "Tracking callsign : \"" << fgGetString("/ai/track-callsign") << "\"" << endl; +/* if (trafficRef->getCallSign() == fgGetString("/ai/track-callsign")) { + cerr << trafficRef->getCallSign() << " " << tgt_altitude_ft << " " << _getSpeed() << " " + << _getAltitude() << " "<< _getLatitude() << " " << _getLongitude() << " " << dist_to_go << " " << lead_dist << " " << curr->name << " " << vs << " " << tgt_vs << " " << bearing << " " << minBearing << " " << speedFraction << endl; + }*/ + } + if ((dist_to_go < lead_dist) || (bearing > (minBearing * 1.1))) { + minBearing = 360; + return true; + } else { + return false; + } } @@ -1024,7 +1064,7 @@ void FGAIAircraft::updateActualState() { if (onGround()) speed = _performance->actualSpeed(this, groundTargetSpeed, dt); else - speed = _performance->actualSpeed(this, tgt_speed, dt); + speed = _performance->actualSpeed(this, (tgt_speed *speedFraction), dt); updateHeading(); roll = _performance->actualBankAngle(this, tgt_roll, dt); @@ -1045,3 +1085,89 @@ void FGAIAircraft::updateSecondaryTargetValues() { //TODO calculate wind correction angle (tgt_yaw) } + + +bool FGAIAircraft::reachedEndOfCruise(double &distance) { + FGAIFlightPlan::waypoint* curr = fp->getCurrentWaypoint(); + if (curr->name == "BOD") { + double dist = fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr); + double descentSpeed = (getPerformance()->vDescent() * SG_NM_TO_METER) / 3600.0; // convert from kts to meter/s + double descentRate = (getPerformance()->descentRate() * SG_FEET_TO_METER) / 60.0; // convert from feet/min to meter/s + + double verticalDistance = ((altitude_ft - 2000.0) - trafficRef->getArrivalAirport()->getElevation()) *SG_FEET_TO_METER; + double descentTimeNeeded = verticalDistance / descentRate; + double distanceCovered = descentSpeed * descentTimeNeeded; + + //cerr << "Tracking : " << fgGetString("/ai/track-callsign"); + if (trafficRef->getCallSign() == fgGetString("/ai/track-callsign")) { + cerr << "Checking for end of cruise stage for :" << trafficRef->getCallSign() << endl; + cerr << "Descent rate : " << descentRate << endl; + cerr << "Descent speed : " << descentSpeed << endl; + cerr << "VerticalDistance : " << verticalDistance << ". Altitude : " << altitude_ft << ". Elevation " << trafficRef->getArrivalAirport()->getElevation() << endl; + cerr << "DecentTimeNeeded : " << descentTimeNeeded << endl; + cerr << "DistanceCovered : " << distanceCovered << endl; + } + //cerr << "Distance = " << distance << endl; + distance = distanceCovered; + if (dist < distanceCovered) { + if (trafficRef->getCallSign() == fgGetString("/ai/track-callsign")) { + //exit(1); + } + return true; + } else { + return false; + } + } else { + return false; + } +} + +void FGAIAircraft::resetPositionFromFlightPlan() +{ + // the one behind you + FGAIFlightPlan::waypoint* prev = 0; + // the one ahead + FGAIFlightPlan::waypoint* curr = 0; + // the next plus 1 + FGAIFlightPlan::waypoint* next = 0; + + prev = fp->getPreviousWaypoint(); + curr = fp->getCurrentWaypoint(); + next = fp->getNextWaypoint(); + + setLatitude(prev->latitude); + setLongitude(prev->longitude); + double tgt_heading = fp->getBearing(curr, next); + setHeading(tgt_heading); + setAltitude(prev->altitude); + setSpeed(prev->speed); +} + +double FGAIAircraft::getBearing(double crse) +{ + double hdgDiff = fabs(hdg-crse); + if (hdgDiff > 180) + hdgDiff = fabs(hdgDiff - 360); + return hdgDiff; +} + +time_t FGAIAircraft::checkForArrivalTime(string wptName) { + FGAIFlightPlan::waypoint* curr = 0; + curr = fp->getCurrentWaypoint(); + + double tracklength = fp->checkTrackLength(wptName); + if (tracklength > 0.1) { + tracklength += fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr); + } else { + return 0; + } + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + time_t arrivalTime = fp->getArrivalTime(); + + time_t ete = tracklength / ((speed * SG_NM_TO_METER) / 3600.0); + time_t secondsToGo = arrivalTime - now; + if (trafficRef->getCallSign() == fgGetString("/ai/track-callsign")) { + cerr << "Checking arrival time: ete " << ete << ". Time to go : " << secondsToGo << ". Track length = " << tracklength << endl; + } + return (ete - secondsToGo); // Positive when we're too slow... +} \ No newline at end of file diff --git a/src/AIModel/AIAircraft.hxx b/src/AIModel/AIAircraft.hxx index 7e65d0df5..26ceb5120 100644 --- a/src/AIModel/AIAircraft.hxx +++ b/src/AIModel/AIAircraft.hxx @@ -53,6 +53,7 @@ public: void initializeFlightPlan(); FGAIFlightPlan* GetFlightPlan() const { return fp; }; void ProcessFlightPlan( double dt, time_t now ); + time_t checkForArrivalTime(string wptName); void AccelTo(double speed); void PitchTo(double angle); @@ -63,7 +64,9 @@ public: void getGroundElev(double dt); //TODO these 3 really need to be public? void doGroundAltitude(); - bool loadNextLeg (); + bool loadNextLeg (double dist=0); + void resetPositionFromFlightPlan(); + double getBearing(double crse); void setAcType(const std::string& ac) { acType = ac; }; void setCompany(const std::string& comp) { company = comp;}; @@ -90,6 +93,7 @@ public: inline double altitudeAGL() const { return props->getFloatValue("position/altitude-agl-ft");}; inline double airspeed() const { return props->getFloatValue("velocities/airspeed-kt");}; std::string atGate(); + protected: void Run(double dt); @@ -104,6 +108,8 @@ private: double dt_elev_count; double headingChangeRate; double headingError; + double minBearing; + double speedFraction; double groundTargetSpeed; double groundOffset; double dt; @@ -118,6 +124,7 @@ private: void handleFirstWaypoint(void); bool leadPointReached(FGAIFlightPlan::waypoint* curr); bool handleAirportEndPoints(FGAIFlightPlan::waypoint* prev, time_t now); + bool reachedEndOfCruise(double&); bool aiTrafficVisible(void); void controlHeading(FGAIFlightPlan::waypoint* curr); void controlSpeed(FGAIFlightPlan::waypoint* curr, diff --git a/src/AIModel/AIFlightPlan.cxx b/src/AIModel/AIFlightPlan.cxx index 60952d799..132858850 100644 --- a/src/AIModel/AIFlightPlan.cxx +++ b/src/AIModel/AIFlightPlan.cxx @@ -187,8 +187,8 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, time_t now = time(NULL) + fgGetLong("/sim/time/warp"); time_t timeDiff = now-start; leg = 1; - /* - if ((timeDiff > 300) && (timeDiff < 1200)) + + if ((timeDiff > 60) && (timeDiff < 1200)) leg = 2; else if ((timeDiff >= 1200) && (timeDiff < 1500)) leg = 3; @@ -196,14 +196,15 @@ FGAIFlightPlan::FGAIFlightPlan(FGAIAircraft *ac, leg = 4; else if (timeDiff >= 2000) leg = 5; - */ + /* if (timeDiff >= 2000) leg = 5; - + */ SG_LOG(SG_GENERAL, SG_INFO, "Route from " << dep->getId() << " to " << arr->getId() << ". Set leg to : " << leg << " " << ac->getTrafficRef()->getCallSign()); wpt_iterator = waypoints.begin(); + bool dist = 0; create(ac, dep,arr, leg, alt, speed, lat, lon, - firstLeg, radius, fltType, acType, airline); + firstLeg, radius, fltType, acType, airline, dist); wpt_iterator = waypoints.begin(); //cerr << "after create: " << (*wpt_iterator)->name << endl; //leg++; @@ -411,6 +412,7 @@ void FGAIFlightPlan::setLeadDistance(double speed, double bearing, //lead_distance = turn_radius * sin(leadInAngle * SG_DEGREES_TO_RADIANS); lead_distance = turn_radius * tan((leadInAngle * SG_DEGREES_TO_RADIANS)/2); + /* if ((lead_distance > (3*turn_radius)) && (current->on_ground == false)) { // cerr << "Warning: Lead-in distance is large. Inbound = " << inbound // << ". Outbound = " << outbound << ". Lead in angle = " << leadInAngle << ". Turn radius = " << turn_radius << endl; @@ -420,7 +422,7 @@ void FGAIFlightPlan::setLeadDistance(double speed, double bearing, if ((leadInAngle > 90) && (current->on_ground == true)) { lead_distance = turn_radius * tan((90 * SG_DEGREES_TO_RADIANS)/2); return; - } + }*/ } void FGAIFlightPlan::setLeadDistance(double distance_ft){ @@ -493,3 +495,20 @@ int FGAIFlightPlan::getRouteIndex(int i) { else return 0; } + + +double FGAIFlightPlan::checkTrackLength(string wptName) { + // skip the first two waypoints: first one is behind, second one is partially done; + double trackDistance = 0; + wpt_vector_iterator wptvec = waypoints.begin(); + wptvec++; + wptvec++; + while ((wptvec != waypoints.end()) && ((*wptvec)->name != wptName)) { + trackDistance += (*wptvec)->trackLength; + wptvec++; + } + if (wptvec == waypoints.end()) { + trackDistance = 0; // name not found + } + return trackDistance; +} \ No newline at end of file diff --git a/src/AIModel/AIFlightPlan.hxx b/src/AIModel/AIFlightPlan.hxx index c70028dd3..1de5658bd 100644 --- a/src/AIModel/AIFlightPlan.hxx +++ b/src/AIModel/AIFlightPlan.hxx @@ -55,6 +55,7 @@ public: bool on_ground; int routeIndex; // For AI/ATC purposes; double time_sec; + double trackLength; // distance from previous waypoint (for AI purposes); string time; } waypoint; @@ -90,10 +91,12 @@ public: double getLeadDistance( void ) const {return lead_distance;} double getBearing(waypoint* previous, waypoint* next) const; double getBearing(double lat, double lon, waypoint* next) const; + double checkTrackLength(string wptName); time_t getStartTime() const { return start_time; } + time_t getArrivalTime() const { return arrivalTime; } void create(FGAIAircraft *, FGAirport *dep, FGAirport *arr, int leg, double alt, double speed, double lat, double lon, - bool firstLeg, double radius, const string& fltType, const string& aircraftType, const string& airline); + bool firstLeg, double radius, const string& fltType, const string& aircraftType, const string& airline, double distance); void setLeg(int val) { leg = val;} void setTime(time_t st) { start_time = st; } @@ -128,6 +131,7 @@ private: typedef vector wpt_vector_type; typedef wpt_vector_type::const_iterator wpt_vector_iterator; + wpt_vector_type waypoints; wpt_vector_iterator wpt_iterator; @@ -136,6 +140,7 @@ private: double lead_distance; double leadInAngle; time_t start_time; + time_t arrivalTime; // For AI/ATC purposes. int leg; int gateId, lastNodeVisited; string activeRunway; @@ -148,7 +153,7 @@ private: void createTakeOff(FGAIAircraft *, bool, FGAirport *, double, const string&); void createClimb(FGAIAircraft *, bool, FGAirport *, double, double, const string&); void createCruise(FGAIAircraft *, bool, FGAirport*, FGAirport*, double, double, double, double, const string&); - void createDecent(FGAIAircraft *, FGAirport *, const string&); + void createDescent(FGAIAircraft *, FGAirport *, double latitude, double longitude, double speed, double alt,const string&, double distance); void createLanding(FGAIAircraft *, FGAirport *, const string&); void createParking(FGAIAircraft *, FGAirport *, double radius); void deleteWaypoints(); @@ -158,6 +163,8 @@ private: void createDefaultLandingTaxi(FGAIAircraft *, FGAirport* aAirport); void createDefaultTakeoffTaxi(FGAIAircraft *, FGAirport* aAirport, FGRunway* aRunway); void createTakeoffTaxi(FGAIAircraft *, bool firstFlight, FGAirport *apt, double radius, const string& fltType, const string& acType, const string& airline); + + double getTurnRadius(double, bool); waypoint* createOnGround(FGAIAircraft *, const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed); waypoint* createInAir(FGAIAircraft *, const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed); diff --git a/src/AIModel/AIFlightPlanCreate.cxx b/src/AIModel/AIFlightPlanCreate.cxx index d2cd7a81e..d276cc96a 100644 --- a/src/AIModel/AIFlightPlanCreate.cxx +++ b/src/AIModel/AIFlightPlanCreate.cxx @@ -42,326 +42,362 @@ * * This is the top-level function, and the only one that is publicly available. * - */ + */ // Check lat/lon values during initialization; -void FGAIFlightPlan::create(FGAIAircraft *ac, FGAirport *dep, FGAirport *arr, int legNr, - double alt, double speed, double latitude, - double longitude, bool firstFlight,double radius, - const string& fltType, const string& aircraftType, - const string& airline) -{ - int currWpt = wpt_iterator - waypoints.begin(); - switch(legNr) - { - case 1: - createPushBack(ac, firstFlight,dep, latitude, longitude, - radius, fltType, aircraftType, airline); - break; - case 2: - createTakeoffTaxi(ac, firstFlight, dep, radius, fltType, aircraftType, airline); - break; - case 3: - createTakeOff(ac, firstFlight, dep, speed, fltType); - break; - case 4: - createClimb(ac, firstFlight, dep, speed, alt, fltType); - break; - case 5: - createCruise(ac, firstFlight, dep,arr, latitude, longitude, speed, alt, fltType); - break; - case 6: - createDecent(ac, arr, fltType); - break; - case 7: - createLanding(ac, arr, fltType); - break; - case 8: - createLandingTaxi(ac, arr, radius, fltType, aircraftType, airline); - break; - case 9: - createParking(ac, arr, radius); - break; +void FGAIFlightPlan::create(FGAIAircraft * ac, FGAirport * dep, + FGAirport * arr, int legNr, double alt, + double speed, double latitude, + double longitude, bool firstFlight, + double radius, const string & fltType, + const string & aircraftType, + const string & airline, double distance) +{ + int currWpt = wpt_iterator - waypoints.begin(); + switch (legNr) { + case 1: + createPushBack(ac, firstFlight, dep, latitude, longitude, + radius, fltType, aircraftType, airline); + break; + case 2: + createTakeoffTaxi(ac, firstFlight, dep, radius, fltType, + aircraftType, airline); + break; + case 3: + createTakeOff(ac, firstFlight, dep, speed, fltType); + break; + case 4: + createClimb(ac, firstFlight, dep, speed, alt, fltType); + break; + case 5: + createCruise(ac, firstFlight, dep, arr, latitude, longitude, speed, + alt, fltType); + break; + case 6: + createDescent(ac, arr, latitude, longitude, speed, alt, fltType, + distance); + break; + case 7: + createLanding(ac, arr, fltType); + break; + case 8: + createLandingTaxi(ac, arr, radius, fltType, aircraftType, airline); + break; + case 9: + createParking(ac, arr, radius); + break; default: - //exit(1); - SG_LOG(SG_INPUT, SG_ALERT, "AIFlightPlan::create() attempting to create unknown leg" - " this is probably an internal program error"); + //exit(1); + SG_LOG(SG_INPUT, SG_ALERT, + "AIFlightPlan::create() attempting to create unknown leg" + " this is probably an internal program error"); } - wpt_iterator = waypoints.begin()+currWpt; - leg++; + wpt_iterator = waypoints.begin() + currWpt; + leg++; } -FGAIFlightPlan::waypoint* -FGAIFlightPlan::createOnGround(FGAIAircraft *ac, const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed) +FGAIFlightPlan::waypoint * + FGAIFlightPlan::createOnGround(FGAIAircraft * ac, + const std::string & aName, + const SGGeod & aPos, double aElev, + double aSpeed) { - waypoint* wpt = new waypoint; - wpt->name = aName; - wpt->longitude = aPos.getLongitudeDeg(); - wpt->latitude = aPos.getLatitudeDeg(); - wpt->altitude = aElev; - wpt->speed = aSpeed; - wpt->crossat = -10000; - wpt->gear_down = true; - wpt->flaps_down= true; - wpt->finished = false; - wpt->on_ground = true; - wpt->routeIndex= 0; - return wpt; + waypoint *wpt = new waypoint; + wpt->name = aName; + wpt->longitude = aPos.getLongitudeDeg(); + wpt->latitude = aPos.getLatitudeDeg(); + wpt->altitude = aElev; + wpt->speed = aSpeed; + wpt->crossat = -10000.1; + wpt->gear_down = true; + wpt->flaps_down = true; + wpt->finished = false; + wpt->on_ground = true; + wpt->routeIndex = 0; + return wpt; } -FGAIFlightPlan::waypoint* -FGAIFlightPlan::createInAir(FGAIAircraft *ac, const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed) +FGAIFlightPlan::waypoint * + FGAIFlightPlan::createInAir(FGAIAircraft * ac, + const std::string & aName, + const SGGeod & aPos, double aElev, + double aSpeed) { - waypoint* wpt = new waypoint; - wpt->name = aName; - wpt->longitude = aPos.getLongitudeDeg(); - wpt->latitude = aPos.getLatitudeDeg(); - wpt->altitude = aElev; - wpt->speed = aSpeed; - wpt->crossat = -10000; - wpt->gear_down = false; - wpt->flaps_down= false; - wpt->finished = false; - wpt->on_ground = false; - wpt->routeIndex= 0; - return wpt; + waypoint *wpt = new waypoint; + wpt->name = aName; + wpt->longitude = aPos.getLongitudeDeg(); + wpt->latitude = aPos.getLatitudeDeg(); + wpt->altitude = aElev; + wpt->speed = aSpeed; + wpt->crossat = -10000.1; + wpt->gear_down = false; + wpt->flaps_down = false; + wpt->finished = false; + wpt->on_ground = false; + wpt->routeIndex = 0; + return wpt; } -FGAIFlightPlan::waypoint* -FGAIFlightPlan::cloneWithPos(FGAIAircraft *ac, waypoint* aWpt, const std::string& aName, const SGGeod& aPos) +FGAIFlightPlan::waypoint * + FGAIFlightPlan::cloneWithPos(FGAIAircraft * ac, waypoint * aWpt, + const std::string & aName, + const SGGeod & aPos) { - waypoint* wpt = new waypoint; - wpt->name = aName; - wpt->longitude = aPos.getLongitudeDeg(); - wpt->latitude = aPos.getLatitudeDeg(); - - wpt->altitude = aWpt->altitude; - wpt->speed = aWpt->speed; - wpt->crossat = aWpt->crossat; - wpt->gear_down = aWpt->gear_down; - wpt->flaps_down= aWpt->flaps_down; - wpt->finished = aWpt->finished; - wpt->on_ground = aWpt->on_ground; - wpt->routeIndex = 0; - - return wpt; + waypoint *wpt = new waypoint; + wpt->name = aName; + wpt->longitude = aPos.getLongitudeDeg(); + wpt->latitude = aPos.getLatitudeDeg(); + + wpt->altitude = aWpt->altitude; + wpt->speed = aWpt->speed; + wpt->crossat = aWpt->crossat; + wpt->gear_down = aWpt->gear_down; + wpt->flaps_down = aWpt->flaps_down; + wpt->finished = aWpt->finished; + wpt->on_ground = aWpt->on_ground; + wpt->routeIndex = 0; + + return wpt; } -FGAIFlightPlan::waypoint* -FGAIFlightPlan::clone(waypoint* aWpt) +FGAIFlightPlan::waypoint * FGAIFlightPlan::clone(waypoint * aWpt) { - waypoint* wpt = new waypoint; - wpt->name = aWpt->name; - wpt->longitude = aWpt->longitude; - wpt->latitude = aWpt->latitude; + waypoint *wpt = new waypoint; + wpt->name = aWpt->name; + wpt->longitude = aWpt->longitude; + wpt->latitude = aWpt->latitude; - wpt->altitude = aWpt->altitude; - wpt->speed = aWpt->speed; - wpt->crossat = aWpt->crossat; - wpt->gear_down = aWpt->gear_down; - wpt->flaps_down= aWpt->flaps_down; - wpt->finished = aWpt->finished; - wpt->on_ground = aWpt->on_ground; - wpt->routeIndex = 0; - - return wpt; + wpt->altitude = aWpt->altitude; + wpt->speed = aWpt->speed; + wpt->crossat = aWpt->crossat; + wpt->gear_down = aWpt->gear_down; + wpt->flaps_down = aWpt->flaps_down; + wpt->finished = aWpt->finished; + wpt->on_ground = aWpt->on_ground; + wpt->routeIndex = 0; + + return wpt; } -void FGAIFlightPlan::createDefaultTakeoffTaxi(FGAIAircraft *ac, FGAirport* aAirport, FGRunway* aRunway) +void FGAIFlightPlan::createDefaultTakeoffTaxi(FGAIAircraft * ac, + FGAirport * aAirport, + FGRunway * aRunway) { - SGGeod runwayTakeoff = aRunway->pointOnCenterline(5.0); - double airportElev = aAirport->getElevation(); - - waypoint* wpt; - wpt = createOnGround(ac, "Airport Center", aAirport->geod(), airportElev, ac->getPerformance()->vTaxi()); - waypoints.push_back(wpt); - wpt = createOnGround(ac, "Runway Takeoff", runwayTakeoff, airportElev, ac->getPerformance()->vTaxi()); - waypoints.push_back(wpt); -} + SGGeod runwayTakeoff = aRunway->pointOnCenterline(5.0); + double airportElev = aAirport->getElevation(); -void FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft *ac, bool firstFlight, - FGAirport *apt, - double radius, const string& fltType, - const string& acType, const string& airline) -{ - double heading, lat, lon; - - // If this function is called during initialization, - // make sure we obtain a valid gate ID first - // and place the model at the location of the gate. - if (firstFlight) { - if (!(apt->getDynamics()->getAvailableParking(&lat, &lon, - &heading, &gateId, - radius, fltType, - acType, airline))) - { - SG_LOG(SG_INPUT, SG_WARN, "Could not find parking for a " << - acType << - " of flight type " << fltType << - " of airline " << airline << - " at airport " << apt->getId()); - } - } - - string rwyClass = getRunwayClassFromTrafficType(fltType); - - // Only set this if it hasn't been set by ATC already. - if (activeRunway.empty()) { - //cerr << "Getting runway for " << ac->getTrafficRef()->getCallSign() << " at " << apt->getId() << endl; - double depHeading = ac->getTrafficRef()->getCourse(); - apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, depHeading); - } - rwy = apt->getRunwayByIdent(activeRunway); - SGGeod runwayTakeoff = rwy->pointOnCenterline(5.0); - - FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork(); - if (!gn->exists()) { - createDefaultTakeoffTaxi(ac, apt, rwy); - return; - } - - intVec ids; - int runwayId = gn->findNearestNode(runwayTakeoff); - - // A negative gateId indicates an overflow parking, use a - // fallback mechanism for this. - // Starting from gate 0 in this case is a bit of a hack - // which requires a more proper solution later on. - delete taxiRoute; - taxiRoute = new FGTaxiRoute; - - // Determine which node to start from. - int node = 0; - // Find out which node to start from - FGParking *park = apt->getDynamics()->getParking(gateId); - if (park) { - node = park->getPushBackPoint(); - } - - if (node == -1) { - node = gateId; - } - - // HAndle case where parking doens't have a node - if ((node == 0) && park) { - if (firstFlight) { - node = gateId; - } else { - node = lastNodeVisited; - } - } - - *taxiRoute = gn->findShortestRoute(node, runwayId); - intVecIterator i; - - if (taxiRoute->empty()) { - createDefaultTakeoffTaxi(ac, apt, rwy); - return; - } - - taxiRoute->first(); - //bool isPushBackPoint = false; - if (firstFlight) { - // If this is called during initialization, randomly - // skip a number of waypoints to get a more realistic - // taxi situation. - int nrWaypointsToSkip = rand() % taxiRoute->size(); - // but make sure we always keep two active waypoints - // to prevent a segmentation fault - for (int i = 0; i < nrWaypointsToSkip-2; i++) { - taxiRoute->next(&node); - } - apt->getDynamics()->releaseParking(gateId); - } else { - if (taxiRoute->size() > 1) { - taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route - } - } - - // push each node on the taxi route as a waypoint - int route; - while(taxiRoute->next(&node, &route)) { - char buffer[10]; - snprintf (buffer, 10, "%d", node); - FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findNode(node); - waypoint* wpt = createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->routeIndex = route; - waypoints.push_back(wpt); - } -} - -void FGAIFlightPlan::createDefaultLandingTaxi(FGAIAircraft *ac, FGAirport* aAirport) -{ - SGGeod lastWptPos = - SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude); - double airportElev = aAirport->getElevation(); - - waypoint* wpt; - wpt = createOnGround(ac, "Runway Exit", lastWptPos, airportElev, ac->getPerformance()->vTaxi()); - waypoints.push_back(wpt); - wpt = createOnGround(ac, "Airport Center", aAirport->geod(), airportElev, ac->getPerformance()->vTaxi()); - waypoints.push_back(wpt); - - double heading, lat, lon; - aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading); - wpt = createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), airportElev, ac->getPerformance()->vTaxi()); - waypoints.push_back(wpt); -} - -void FGAIFlightPlan::createLandingTaxi(FGAIAircraft *ac, FGAirport *apt, - double radius, const string& fltType, - const string& acType, const string& airline) -{ - double heading, lat, lon; - apt->getDynamics()->getAvailableParking(&lat, &lon, &heading, - &gateId, radius, fltType, acType, airline); - - SGGeod lastWptPos = - SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude); - FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork(); - - // Find a route from runway end to parking/gate. - if (!gn->exists()) { - createDefaultLandingTaxi(ac, apt); - return; - } - - intVec ids; - int runwayId = gn->findNearestNode(lastWptPos); - // A negative gateId indicates an overflow parking, use a - // fallback mechanism for this. - // Starting from gate 0 is a bit of a hack... - //FGTaxiRoute route; - delete taxiRoute; - taxiRoute = new FGTaxiRoute; - if (gateId >= 0) - *taxiRoute = gn->findShortestRoute(runwayId, gateId); - else - *taxiRoute = gn->findShortestRoute(runwayId, 0); - intVecIterator i; - - if (taxiRoute->empty()) { - createDefaultLandingTaxi(ac, apt); - return; - } - - int node; - taxiRoute->first(); - int size = taxiRoute->size(); - // Omit the last two waypoints, as - // those are created by createParking() - int route; - for (int i = 0; i < size-2; i++) { - taxiRoute->next(&node, &route); - char buffer[10]; - snprintf (buffer, 10, "%d", node); - FGTaxiNode *tn = gn->findNode(node); - waypoint* wpt = createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), ac->getPerformance()->vTaxi()); - wpt->routeIndex = route; + waypoint *wpt; + wpt = + createOnGround(ac, "Airport Center", aAirport->geod(), airportElev, + ac->getPerformance()->vTaxi()); waypoints.push_back(wpt); - } + wpt = + createOnGround(ac, "Runway Takeoff", runwayTakeoff, airportElev, + ac->getPerformance()->vTaxi()); + waypoints.push_back(wpt); +} + +void FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight, + FGAirport * apt, + double radius, + const string & fltType, + const string & acType, + const string & airline) +{ + double heading, lat, lon; + + // If this function is called during initialization, + // make sure we obtain a valid gate ID first + // and place the model at the location of the gate. + if (firstFlight) { + if (!(apt->getDynamics()->getAvailableParking(&lat, &lon, + &heading, &gateId, + radius, fltType, + acType, airline))) { + SG_LOG(SG_INPUT, SG_WARN, "Could not find parking for a " << + acType << + " of flight type " << fltType << + " of airline " << airline << + " at airport " << apt->getId()); + } + } + + string rwyClass = getRunwayClassFromTrafficType(fltType); + + // Only set this if it hasn't been set by ATC already. + if (activeRunway.empty()) { + //cerr << "Getting runway for " << ac->getTrafficRef()->getCallSign() << " at " << apt->getId() << endl; + double depHeading = ac->getTrafficRef()->getCourse(); + apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, + depHeading); + } + rwy = apt->getRunwayByIdent(activeRunway); + SGGeod runwayTakeoff = rwy->pointOnCenterline(5.0); + + FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork(); + if (!gn->exists()) { + createDefaultTakeoffTaxi(ac, apt, rwy); + return; + } + + intVec ids; + int runwayId = gn->findNearestNode(runwayTakeoff); + + // A negative gateId indicates an overflow parking, use a + // fallback mechanism for this. + // Starting from gate 0 in this case is a bit of a hack + // which requires a more proper solution later on. + delete taxiRoute; + taxiRoute = new FGTaxiRoute; + + // Determine which node to start from. + int node = 0; + // Find out which node to start from + FGParking *park = apt->getDynamics()->getParking(gateId); + if (park) { + node = park->getPushBackPoint(); + } + + if (node == -1) { + node = gateId; + } + // HAndle case where parking doens't have a node + if ((node == 0) && park) { + if (firstFlight) { + node = gateId; + } else { + node = lastNodeVisited; + } + } + + *taxiRoute = gn->findShortestRoute(node, runwayId); + intVecIterator i; + + if (taxiRoute->empty()) { + createDefaultTakeoffTaxi(ac, apt, rwy); + return; + } + + taxiRoute->first(); + //bool isPushBackPoint = false; + if (firstFlight) { + // If this is called during initialization, randomly + // skip a number of waypoints to get a more realistic + // taxi situation. + int nrWaypointsToSkip = rand() % taxiRoute->size(); + // but make sure we always keep two active waypoints + // to prevent a segmentation fault + for (int i = 0; i < nrWaypointsToSkip - 3; i++) { + taxiRoute->next(&node); + } + apt->getDynamics()->releaseParking(gateId); + } else { + if (taxiRoute->size() > 1) { + taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route + } + } + + // push each node on the taxi route as a waypoint + int route; + while (taxiRoute->next(&node, &route)) { + char buffer[10]; + snprintf(buffer, 10, "%d", node); + FGTaxiNode *tn = + apt->getDynamics()->getGroundNetwork()->findNode(node); + waypoint *wpt = + createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), + ac->getPerformance()->vTaxi()); + wpt->routeIndex = route; + waypoints.push_back(wpt); + } +} + +void FGAIFlightPlan::createDefaultLandingTaxi(FGAIAircraft * ac, + FGAirport * aAirport) +{ + SGGeod lastWptPos = + SGGeod::fromDeg(waypoints.back()->longitude, + waypoints.back()->latitude); + double airportElev = aAirport->getElevation(); + + waypoint *wpt; + wpt = + createOnGround(ac, "Runway Exit", lastWptPos, airportElev, + ac->getPerformance()->vTaxi()); + waypoints.push_back(wpt); + wpt = + createOnGround(ac, "Airport Center", aAirport->geod(), airportElev, + ac->getPerformance()->vTaxi()); + waypoints.push_back(wpt); + + double heading, lat, lon; + aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading); + wpt = + createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), airportElev, + ac->getPerformance()->vTaxi()); + waypoints.push_back(wpt); +} + +void FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt, + double radius, + const string & fltType, + const string & acType, + const string & airline) +{ + double heading, lat, lon; + apt->getDynamics()->getAvailableParking(&lat, &lon, &heading, + &gateId, radius, fltType, + acType, airline); + + SGGeod lastWptPos = + SGGeod::fromDeg(waypoints.back()->longitude, + waypoints.back()->latitude); + FGGroundNetwork *gn = apt->getDynamics()->getGroundNetwork(); + + // Find a route from runway end to parking/gate. + if (!gn->exists()) { + createDefaultLandingTaxi(ac, apt); + return; + } + + intVec ids; + int runwayId = gn->findNearestNode(lastWptPos); + // A negative gateId indicates an overflow parking, use a + // fallback mechanism for this. + // Starting from gate 0 is a bit of a hack... + //FGTaxiRoute route; + delete taxiRoute; + taxiRoute = new FGTaxiRoute; + if (gateId >= 0) + *taxiRoute = gn->findShortestRoute(runwayId, gateId); + else + *taxiRoute = gn->findShortestRoute(runwayId, 0); + intVecIterator i; + + if (taxiRoute->empty()) { + createDefaultLandingTaxi(ac, apt); + return; + } + + int node; + taxiRoute->first(); + int size = taxiRoute->size(); + // Omit the last two waypoints, as + // those are created by createParking() + int route; + for (int i = 0; i < size - 2; i++) { + taxiRoute->next(&node, &route); + char buffer[10]; + snprintf(buffer, 10, "%d", node); + FGTaxiNode *tn = gn->findNode(node); + waypoint *wpt = + createOnGround(ac, buffer, tn->getGeod(), apt->getElevation(), + ac->getPerformance()->vTaxi()); + wpt->routeIndex = route; + waypoints.push_back(wpt); + } } /******************************************************************* @@ -375,38 +411,42 @@ void FGAIFlightPlan::createLandingTaxi(FGAIAircraft *ac, FGAirport *apt, * more likely however. * ******************************************************************/ -void FGAIFlightPlan::createTakeOff(FGAIAircraft *ac, bool firstFlight, FGAirport *apt, double speed, const string &fltType) +void FGAIFlightPlan::createTakeOff(FGAIAircraft * ac, bool firstFlight, + FGAirport * apt, double speed, + const string & fltType) { - double accel = ac->getPerformance()->acceleration(); - double vTaxi = ac->getPerformance()->vTaxi(); - double vRotate = ac->getPerformance()->vRotate(); + double accel = ac->getPerformance()->acceleration(); + double vTaxi = ac->getPerformance()->vTaxi(); + double vRotate = ac->getPerformance()->vRotate(); double vTakeoff = ac->getPerformance()->vTakeoff(); - double vClimb = ac->getPerformance()->vClimb(); + double vClimb = ac->getPerformance()->vClimb(); - double accelMetric = (accel * SG_NM_TO_METER) / 3600; - double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600; - double vRotateMetric = (vRotate * SG_NM_TO_METER) / 3600; - double vTakeoffMetric = (vTakeoff * SG_NM_TO_METER) / 3600; - double vClimbMetric = (vClimb * SG_NM_TO_METER) / 3600; + double accelMetric = (accel * SG_NM_TO_METER) / 3600; + double vTaxiMetric = (vTaxi * SG_NM_TO_METER) / 3600; + double vRotateMetric = (vRotate * SG_NM_TO_METER) / 3600; + double vTakeoffMetric = (vTakeoff * SG_NM_TO_METER) / 3600; + double vClimbMetric = (vClimb * SG_NM_TO_METER) / 3600; // Acceleration = dV / dT // Acceleration X dT = dV // dT = dT / Acceleration //d = (Vf^2 - Vo^2) / (2*a) //double accelTime = (vRotate - vTaxi) / accel; //cerr << "Using " << accelTime << " as total acceleration time" << endl; - double accelDistance = (vRotateMetric*vRotateMetric - vTaxiMetric*vTaxiMetric) / (2*accelMetric); - cerr << "Using " << accelDistance << " " << accelMetric << " " << vRotateMetric << endl; + double accelDistance = + (vRotateMetric * vRotateMetric - + vTaxiMetric * vTaxiMetric) / (2 * accelMetric); + //cerr << "Using " << accelDistance << " " << accelMetric << " " << vRotateMetric << endl; waypoint *wpt; // Get the current active runway, based on code from David Luff // This should actually be unified and extended to include // Preferential runway use schema's // NOTE: DT (2009-01-18: IIRC, this is currently already the case, // because the getActive runway function takes care of that. - if (firstFlight) - { + if (firstFlight) { string rwyClass = getRunwayClassFromTrafficType(fltType); double heading = ac->getTrafficRef()->getCourse(); - apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); + apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, + heading); rwy = apt->getRunwayByIdent(activeRunway); } @@ -417,182 +457,462 @@ void FGAIFlightPlan::createTakeOff(FGAIAircraft *ac, bool firstFlight, FGAirport waypoints.push_back(wpt); - accelDistance = (vTakeoffMetric*vTakeoffMetric - vTaxiMetric*vTaxiMetric) / (2*accelMetric); - cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; - accelPoint = rwy->pointOnCenterline(105.0+accelDistance); + accelDistance = + (vTakeoffMetric * vTakeoffMetric - + vTaxiMetric * vTaxiMetric) / (2 * accelMetric); + //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; + accelPoint = rwy->pointOnCenterline(105.0 + accelDistance); wpt = createOnGround(ac, "rotate", accelPoint, airportElev, vTakeoff); waypoints.push_back(wpt); - accelDistance = ((vTakeoffMetric*1.1)*(vTakeoffMetric*1.1) - vTaxiMetric*vTaxiMetric) / (2*accelMetric); - cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; - accelPoint = rwy->pointOnCenterline(105.0+accelDistance); - wpt = createOnGround(ac, "rotate", accelPoint, airportElev+1000, vTakeoff*1.1); + accelDistance = + ((vTakeoffMetric * 1.1) * (vTakeoffMetric * 1.1) - + vTaxiMetric * vTaxiMetric) / (2 * accelMetric); + //cerr << "Using " << accelDistance << " " << accelMetric << " " << vTakeoffMetric << endl; + accelPoint = rwy->pointOnCenterline(105.0 + accelDistance); + wpt = + createOnGround(ac, "rotate", accelPoint, airportElev + 1000, + vTakeoff * 1.1); wpt->on_ground = false; waypoints.push_back(wpt); wpt = cloneWithPos(ac, wpt, "3000 ft", rwy->end()); - wpt->altitude = airportElev+3000; + wpt->altitude = airportElev + 3000; waypoints.push_back(wpt); // Finally, add two more waypoints, so that aircraft will remain under // Tower control until they have reached the 3000 ft climb point SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5); wpt = cloneWithPos(ac, wpt, "5000 ft", pt); - wpt->altitude = airportElev+5000; + wpt->altitude = airportElev + 5000; waypoints.push_back(wpt); } - + /******************************************************************* * CreateClimb * initialize the Aircraft at the parking location ******************************************************************/ -void FGAIFlightPlan::createClimb(FGAIAircraft *ac, bool firstFlight, FGAirport *apt, double speed, double alt, const string &fltType) +void FGAIFlightPlan::createClimb(FGAIAircraft * ac, bool firstFlight, + FGAirport * apt, double speed, double alt, + const string & fltType) { - waypoint *wpt; + waypoint *wpt; // bool planLoaded = false; - string fPLName; - double vClimb = ac->getPerformance()->vClimb(); + string fPLName; + double vClimb = ac->getPerformance()->vClimb(); - if (firstFlight) { - string rwyClass = getRunwayClassFromTrafficType(fltType); - double heading = ac->getTrafficRef()->getCourse(); - apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); - rwy = apt->getRunwayByIdent(activeRunway); - } - if (sid) { - for (wpt_vector_iterator i = sid->getFirstWayPoint(); - i != sid->getLastWayPoint(); - i++) { + if (firstFlight) { + string rwyClass = getRunwayClassFromTrafficType(fltType); + double heading = ac->getTrafficRef()->getCourse(); + apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, + heading); + rwy = apt->getRunwayByIdent(activeRunway); + } + if (sid) { + for (wpt_vector_iterator i = sid->getFirstWayPoint(); + i != sid->getLastWayPoint(); i++) { waypoints.push_back(clone(*(i))); //cerr << " Cloning waypoint " << endl; - } - } else { - SGGeod climb1 = rwy->pointOnCenterline(10*SG_NM_TO_METER); - wpt = createInAir(ac, "10000ft climb", climb1, vClimb, 10000); - wpt->gear_down = true; - wpt->flaps_down= true; - waypoints.push_back(wpt); + } + } else { + SGGeod climb1 = rwy->pointOnCenterline(10 * SG_NM_TO_METER); + wpt = createInAir(ac, "10000ft climb", climb1, vClimb, 10000); + wpt->gear_down = true; + wpt->flaps_down = true; + waypoints.push_back(wpt); - SGGeod climb2 = rwy->pointOnCenterline(20*SG_NM_TO_METER); - wpt = cloneWithPos(ac, wpt, "18000ft climb", climb2); - wpt->altitude = 18000; - waypoints.push_back(wpt); - } + SGGeod climb2 = rwy->pointOnCenterline(20 * SG_NM_TO_METER); + wpt = cloneWithPos(ac, wpt, "18000ft climb", climb2); + wpt->altitude = 18000; + waypoints.push_back(wpt); + } } /******************************************************************* - * CreateDecent - * initialize the Aircraft at the parking location + * CreateDescent + * Generate a flight path from the last waypoint of the cruise to + * the permission to land point ******************************************************************/ -void FGAIFlightPlan::createDecent(FGAIAircraft *ac, FGAirport *apt, const string &fltType) +void FGAIFlightPlan::createDescent(FGAIAircraft * ac, FGAirport * apt, + double latitude, double longitude, + double speed, double alt, + const string & fltType, + double requiredDistance) { - // Ten thousand ft. Slowing down to 240 kts - waypoint *wpt; - double vDecent = ac->getPerformance()->vDescent(); - double vApproach = ac->getPerformance()->vApproach(); + bool reposition = false; + waypoint *wpt; + double vDescent = ac->getPerformance()->vDescent(); + double vApproach = ac->getPerformance()->vApproach(); + + + //Beginning of Descent + string rwyClass = getRunwayClassFromTrafficType(fltType); + double heading = ac->getTrafficRef()->getCourse(); + apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, + heading); + rwy = apt->getRunwayByIdent(activeRunway); + + + + // Create a slow descent path that ends 250 lateral to the runway. + double initialTurnRadius = getTurnRadius(vDescent, true); + double finalTurnRadius = getTurnRadius(vApproach, true); + +// get length of the downwind leg for the intended runway + double distanceOut = apt->getDynamics()->getApproachController()->getRunway(rwy->name())->getApproachDistance(); //12 * SG_NM_TO_METER; + //time_t previousArrivalTime= apt->getDynamics()->getApproachController()->getRunway(rwy->name())->getEstApproachTime(); + + + SGGeod current = SGGeod::fromDegM(longitude, latitude, 0); + SGGeod initialTarget = rwy->pointOnCenterline(-distanceOut); + SGGeod refPoint = rwy->pointOnCenterline(0); + double distance = SGGeodesy::distanceM(current, initialTarget); + double azimuth = SGGeodesy::courseDeg(current, initialTarget); + double dummyAz2; + + // To prevent absurdly steep approaches, compute the origin from where the approach should have started + SGGeod origin; + + if (ac->getTrafficRef()->getCallSign() == + fgGetString("/ai/track-callsign")) { + //cerr << "Reposition information: Actual distance " << distance << ". required distance " << requiredDistance << endl; + //exit(1); + } + + if (distance < requiredDistance * 0.8) { + reposition = true; + SGGeodesy::direct(initialTarget, azimuth, + -requiredDistance, origin, dummyAz2); + + distance = SGGeodesy::distanceM(current, initialTarget); + azimuth = SGGeodesy::courseDeg(current, initialTarget); + } else { + origin = current; + } + + + double dAlt = alt - (apt->getElevation() + 2000); + + double nPoints = 100; + + char buffer[16]; + + // The descent path contains the following phases: + // 1) a linear glide path from the initial position to + // 2) a semi circle turn to final + // 3) approach + + //cerr << "Phase 1: Linear Descent path to runway" << rwy->name() << endl; + // Create an initial destination point on a semicircle + //cerr << "lateral offset : " << lateralOffset << endl; + //cerr << "Distance : " << distance << endl; + //cerr << "Azimuth : " << azimuth << endl; + //cerr << "Initial Lateral point: " << lateralOffset << endl; + double lat = refPoint.getLatitudeDeg(); + double lon = refPoint.getLongitudeDeg(); + //cerr << "Reference point (" << lat << ", " << lon << ")." << endl; + lat = initialTarget.getLatitudeDeg(); + lon = initialTarget.getLongitudeDeg(); + //cerr << "Initial Target point (" << lat << ", " << lon << ")." << endl; + + double ratio = initialTurnRadius / distance; + if (ratio > 1.0) + ratio = 1.0; + if (ratio < -1.0) + ratio = -1.0; + + double newHeading = asin(ratio) * SG_RADIANS_TO_DEGREES; + double newDistance = + cos(newHeading * SG_DEGREES_TO_RADIANS) * distance; + //cerr << "new distance " << newDistance << ". additional Heading " << newHeading << endl; + double side = azimuth - rwy->headingDeg(); + double lateralOffset = initialTurnRadius; + if (side < 0) + side += 360; + if (side < 180) { + lateralOffset *= -1; + } + // 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" + double turnDistance = (2 * M_PI * initialTurnRadius) * (side / 360.0); + time_t remaining = + (turnDistance + distance) / ((vDescent * SG_NM_TO_METER) / 3600.0); + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + //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; + //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; + } + //if ((eta < (previousArrivalTime+60)) && (reposition == false)) { + arrivalTime = newEta; + time_t additionalTimeNeeded = newEta - eta; + double distanceCovered = + ((vApproach * SG_NM_TO_METER) / 3600.0) * additionalTimeNeeded; + distanceOut += distanceCovered; + //apt->getDynamics()->getApproachController()->getRunway(rwy->name())->setEstApproachTime(eta+additionalTimeNeeded); + //cerr << "Adding additional distance: " << distanceCovered << " to allow " << additionalTimeNeeded << " seconds of flying time" << endl << endl; + //} else { + //apt->getDynamics()->getApproachController()->getRunway(rwy->name())->setEstApproachTime(eta); + //} + //cerr << "Timing information : Previous eta: " << previousArrivalTime << ". Current ETA : " << eta << endl; + + SGGeod secondaryTarget = + rwy->pointOffCenterline(-distanceOut, lateralOffset); + initialTarget = rwy->pointOnCenterline(-distanceOut); + distance = SGGeodesy::distanceM(origin, secondaryTarget); + azimuth = SGGeodesy::courseDeg(origin, secondaryTarget); + + + lat = secondaryTarget.getLatitudeDeg(); + lon = secondaryTarget.getLongitudeDeg(); + //cerr << "Secondary Target point (" << lat << ", " << lon << ")." << endl; + //cerr << "Distance : " << distance << endl; + //cerr << "Azimuth : " << azimuth << endl; + + + ratio = initialTurnRadius / distance; + if (ratio > 1.0) + ratio = 1.0; + if (ratio < -1.0) + ratio = -1.0; + newHeading = asin(ratio) * SG_RADIANS_TO_DEGREES; + newDistance = cos(newHeading * SG_DEGREES_TO_RADIANS) * distance; + //cerr << "new distance realative to secondary target: " << newDistance << ". additional Heading " << newHeading << endl; + if (side < 180) { + azimuth += newHeading; + } else { + azimuth -= newHeading; + } + + SGGeod tertiaryTarget; + SGGeodesy::direct(origin, azimuth, + newDistance, tertiaryTarget, dummyAz2); + + lat = tertiaryTarget.getLatitudeDeg(); + lon = tertiaryTarget.getLongitudeDeg(); + //cerr << "tertiary Target point (" << lat << ", " << lon << ")." << endl; + + + for (int i = 1; i < nPoints; i++) { + SGGeod result; + double currentDist = i * (newDistance / nPoints); + double currentAltitude = alt - (i * (dAlt / nPoints)); + SGGeodesy::direct(origin, azimuth, currentDist, result, dummyAz2); + snprintf(buffer, 16, "descent%03d", i); + wpt = createInAir(ac, buffer, result, currentAltitude, vDescent); + wpt->crossat = currentAltitude; + wpt->trackLength = (newDistance / nPoints); + waypoints.push_back(wpt); + //cerr << "Track Length : " << wpt->trackLength; + //cerr << " Position : " << result.getLatitudeDeg() << " " << result.getLongitudeDeg() << " " << currentAltitude << endl; + } + + //cerr << "Phase 2: Circle " << endl; + double initialAzimuth = + SGGeodesy::courseDeg(secondaryTarget, tertiaryTarget); + double finalAzimuth = + SGGeodesy::courseDeg(secondaryTarget, initialTarget); + + //cerr << "Angles from secondary target: " << initialAzimuth << " " << finalAzimuth << endl; + int increment, startval, endval; + // circle right around secondary target if orig of position is to the right of the runway + // i.e. use negative angles; else circle leftward and use postivi + if (side < 180) { + increment = -1; + startval = floor(initialAzimuth); + endval = ceil(finalAzimuth); + if (endval > startval) { + endval -= 360; + } + } else { + increment = 1; + startval = ceil(initialAzimuth); + endval = floor(finalAzimuth); + if (endval < startval) { + endval += 360; + } + + } + + //cerr << "creating circle between " << startval << " and " << endval << " using " << increment << endl; + double trackLength = (2 * M_PI * initialTurnRadius) / 360.0; + for (int i = startval; i != endval; i += increment) { + SGGeod result; + double currentAltitude = apt->getElevation() + 2000; + SGGeodesy::direct(secondaryTarget, i, + initialTurnRadius, result, dummyAz2); + snprintf(buffer, 16, "turn%03d", i); + wpt = createInAir(ac, buffer, result, currentAltitude, vDescent); + wpt->crossat = currentAltitude; + wpt->trackLength = trackLength; + //cerr << "Track Length : " << wpt->trackLength; + waypoints.push_back(wpt); + //cerr << " Position : " << result.getLatitudeDeg() << " " << result.getLongitudeDeg() << " " << currentAltitude << endl; + } + + + // The approach leg should bring the aircraft to approximately 4-6 out, after which the landing phase should take over. + //cerr << "Phase 3: Approach" << endl; + distanceOut -= distanceCovered; + for (int i = 1; i < nPoints; i++) { + SGGeod result; + double currentDist = i * (distanceOut / nPoints); + double currentAltitude = + apt->getElevation() + 2000 - (i * 2000 / nPoints); + snprintf(buffer, 16, "final%03d", i); + result = rwy->pointOnCenterline((-distanceOut) + currentDist); + wpt = createInAir(ac, buffer, result, currentAltitude, vApproach); + wpt->crossat = currentAltitude; + wpt->trackLength = (distanceOut / nPoints); + // account for the extra distance due to an extended downwind leg + if (i == 1) { + wpt->trackLength += distanceCovered; + } + //cerr << "Track Length : " << wpt->trackLength; + waypoints.push_back(wpt); + //cerr << " Position : " << result.getLatitudeDeg() << " " << result.getLongitudeDeg() << " " << currentAltitude << endl; + } + + //cerr << "Done" << endl; + + // Erase the two bogus BOD points: Note check for conflicts with scripted AI flightPlans + IncrementWaypoint(true); + IncrementWaypoint(true); + + if (reposition) { + double tempDistance; + double minDistance = HUGE_VAL; + string wptName; + tempDistance = SGGeodesy::distanceM(current, initialTarget); + time_t eta = + tempDistance / ((vDescent * SG_NM_TO_METER) / 3600.0) + now; + time_t newEta = + apt->getDynamics()->getApproachController()->getRunway(rwy-> + name + ())-> + requestTimeSlot(eta); + arrivalTime = newEta; + double newDistance = + ((vDescent * SG_NM_TO_METER) / 3600.0) * (newEta - now); + //cerr << "Repositioning information : eta" << eta << ". New ETA " << newEta << ". Diff = " << (newEta - eta) << ". Distance = " << tempDistance << ". New distance = " << newDistance << endl; + IncrementWaypoint(true); // remove waypoint BOD2 + while (checkTrackLength("final001") > newDistance) { + IncrementWaypoint(true); + } + //cerr << "Repositioning to waypoint " << (*waypoints.begin())->name << endl; + ac->resetPositionFromFlightPlan(); + } + - //Beginning of Decent - //string name; - // allow "mil" and "gen" as well - string rwyClass = getRunwayClassFromTrafficType(fltType); - double heading = ac->getTrafficRef()->getCourse(); - apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); - rwy = apt->getRunwayByIdent(activeRunway); - - SGGeod descent1 = rwy->pointOnCenterline(-100000); // 100km out - wpt = createInAir(ac, "Dec 10000ft", descent1, apt->getElevation(), vDecent); - wpt->crossat = 10000; - waypoints.push_back(wpt); - - // Three thousand ft. Slowing down to 160 kts - SGGeod descent2 = rwy->pointOnCenterline(-8*SG_NM_TO_METER); // 8nm out - wpt = createInAir(ac, "DEC 3000ft", descent2, apt->getElevation(), vApproach); - wpt->crossat = 3000; - wpt->gear_down = true; - wpt->flaps_down= true; - waypoints.push_back(wpt); } + /******************************************************************* * CreateLanding - * initialize the Aircraft at the parking location + * Create a flight path from the "permision to land" point (currently + hardcoded at 5000 meters from the threshold) to the threshold, at + a standard glide slope angle of 3 degrees. ******************************************************************/ -void FGAIFlightPlan::createLanding(FGAIAircraft *ac, FGAirport *apt, const string &fltType) +void FGAIFlightPlan::createLanding(FGAIAircraft * ac, FGAirport * apt, + const string & fltType) { - double vTouchdown = ac->getPerformance()->vTouchdown(); - double vTaxi = ac->getPerformance()->vTaxi(); + double vTouchdown = ac->getPerformance()->vTouchdown(); + double vTaxi = ac->getPerformance()->vTaxi(); - string rwyClass = getRunwayClassFromTrafficType(fltType); - double heading = ac->getTrafficRef()->getCourse(); - apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); - rwy = apt->getRunwayByIdent(activeRunway); - + //string rwyClass = getRunwayClassFromTrafficType(fltType); + //double heading = ac->getTrafficRef()->getCourse(); + //apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); + //rwy = apt->getRunwayByIdent(activeRunway); - waypoint *wpt; - double aptElev = apt->getElevation(); - SGGeod coord; - char buffer[12]; - for (int i = 1; i < 10; i++) { - snprintf(buffer, 12, "wpt%d", i); - coord = rwy->pointOnCenterline(rwy->lengthM() * (i/10.0)); - wpt = createOnGround(ac, buffer, coord, aptElev, (vTouchdown/i)); - wpt->crossat = apt->getElevation(); - waypoints.push_back(wpt); - } + waypoint *wpt; + double aptElev = apt->getElevation(); - /* - //Runway Threshold - wpt = createOnGround(ac, "Threshold", rwy->threshold(), aptElev, vTouchdown); - wpt->crossat = apt->getElevation(); - waypoints.push_back(wpt); + SGGeod coord; + char buffer[12]; + for (int i = 1; i < 10; i++) { + snprintf(buffer, 12, "wpt%d", i); + coord = rwy->pointOnCenterline(rwy->lengthM() * (i / 10.0)); + wpt = createOnGround(ac, buffer, coord, aptElev, (vTouchdown / i)); + wpt->crossat = apt->getElevation(); + waypoints.push_back(wpt); + } - // Roll-out - wpt = createOnGround(ac, "Center", rwy->geod(), aptElev, vTaxi*2); - waypoints.push_back(wpt); + /* + //Runway Threshold + wpt = createOnGround(ac, "Threshold", rwy->threshold(), aptElev, vTouchdown); + wpt->crossat = apt->getElevation(); + waypoints.push_back(wpt); - SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9); - wpt = createOnGround(ac, "Roll Out", rollOut, aptElev, vTaxi); - wpt->crossat = apt->getElevation(); - waypoints.push_back(wpt); - */ + // Roll-out + wpt = createOnGround(ac, "Center", rwy->geod(), aptElev, vTaxi*2); + waypoints.push_back(wpt); + + SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9); + wpt = createOnGround(ac, "Roll Out", rollOut, aptElev, vTaxi); + wpt->crossat = apt->getElevation(); + waypoints.push_back(wpt); + */ } /******************************************************************* * CreateParking * initialize the Aircraft at the parking location ******************************************************************/ -void FGAIFlightPlan::createParking(FGAIAircraft *ac, FGAirport *apt, double radius) +void FGAIFlightPlan::createParking(FGAIAircraft * ac, FGAirport * apt, + double radius) { - waypoint* wpt; - double aptElev = apt->getElevation(); - double lat = 0.0, lat2 = 0.0; - double lon = 0.0, lon2 = 0.0; - double az2 = 0.0; - double heading = 0.0; + waypoint *wpt; + double aptElev = apt->getElevation(); + double lat = 0.0, lat2 = 0.0; + double lon = 0.0, lon2 = 0.0; + double az2 = 0.0; + double heading = 0.0; - double vTaxi = ac->getPerformance()->vTaxi(); - double vTaxiReduced = vTaxi * (2.0/3.0); - apt->getDynamics()->getParking(gateId, &lat, &lon, &heading); - heading += 180.0; - if (heading > 360) - heading -= 360; - geo_direct_wgs_84 ( 0, lat, lon, heading, - 2.2*radius, - &lat2, &lon2, &az2 ); - wpt = createOnGround(ac, "taxiStart", SGGeod::fromDeg(lon2, lat2), aptElev, vTaxiReduced); - waypoints.push_back(wpt); - - geo_direct_wgs_84 ( 0, lat, lon, heading, - 0.1 *radius, - &lat2, &lon2, &az2 ); - - wpt = createOnGround(ac, "taxiStart2", SGGeod::fromDeg(lon2, lat2), aptElev, vTaxiReduced); - waypoints.push_back(wpt); + double vTaxi = ac->getPerformance()->vTaxi(); + double vTaxiReduced = vTaxi * (2.0 / 3.0); + apt->getDynamics()->getParking(gateId, &lat, &lon, &heading); + heading += 180.0; + if (heading > 360) + heading -= 360; + geo_direct_wgs_84(0, lat, lon, heading, + 2.2 * radius, &lat2, &lon2, &az2); + wpt = + createOnGround(ac, "taxiStart", SGGeod::fromDeg(lon2, lat2), + aptElev, vTaxiReduced); + waypoints.push_back(wpt); - wpt = createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), aptElev, vTaxiReduced); - waypoints.push_back(wpt); + geo_direct_wgs_84(0, lat, lon, heading, + 0.1 * radius, &lat2, &lon2, &az2); + + wpt = + createOnGround(ac, "taxiStart2", SGGeod::fromDeg(lon2, lat2), + aptElev, vTaxiReduced); + waypoints.push_back(wpt); + + wpt = + createOnGround(ac, "END", SGGeod::fromDeg(lon, lat), aptElev, + vTaxiReduced); + waypoints.push_back(wpt); } /** @@ -620,17 +940,29 @@ void FGAIFlightPlan::createParking(FGAIAircraft *ac, FGAirport *apt, double radi */ string FGAIFlightPlan::getRunwayClassFromTrafficType(string fltType) { - if ((fltType == "gate") || (fltType == "cargo")) { - return string("com"); + if ((fltType == "gate") || (fltType == "cargo")) { + return string("com"); } if (fltType == "ga") { - return string ("gen"); + return string("gen"); } if (fltType == "ul") { return string("ul"); } - if ((fltType == "mil-fighter") || (fltType == "mil-transport")) { - return string("mil"); + if ((fltType == "mil-fighter") || (fltType == "mil-transport")) { + return string("mil"); } - return string("com"); + return string("com"); +} + + +double FGAIFlightPlan::getTurnRadius(double speed, bool inAir) +{ + double turn_radius; + if (inAir == false) { + turn_radius = ((360 / 30) * fabs(speed)) / (2 * M_PI); + } else { + turn_radius = 0.1911 * speed * speed; // an estimate for 25 degrees bank + } + return turn_radius; } diff --git a/src/AIModel/AIFlightPlanCreateCruise.cxx b/src/AIModel/AIFlightPlanCreateCruise.cxx index 13df4b607..269acda94 100644 --- a/src/AIModel/AIFlightPlanCreateCruise.cxx +++ b/src/AIModel/AIFlightPlanCreateCruise.cxx @@ -300,8 +300,11 @@ void FGAIFlightPlan::createCruise(FGAIAircraft *ac, bool firstFlight, FGAirport arr->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading); rwy = arr->getRunwayByIdent(activeRunway); // begin descent 110km out - SGGeod beginDescentPoint = rwy->pointOnCenterline(-110000); + SGGeod beginDescentPoint = rwy->pointOnCenterline(0); + SGGeod secondaryDescentPoint = rwy->pointOnCenterline(-10000); - wpt = createInAir(ac, "BOD", beginDescentPoint, alt, vCruise); + wpt = createInAir(ac, "BOD", beginDescentPoint, alt, vCruise); + waypoints.push_back(wpt); + wpt = createInAir(ac, "BOD2", secondaryDescentPoint, alt, vCruise); waypoints.push_back(wpt); } diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index c7d20bdb5..53ddf28b5 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -24,299 +24,389 @@ # include #endif +#include + #include "trafficcontrol.hxx" #include #include +#include +#include #include #include #include + +using std::sort; + +/*************************************************************************** + * ActiveRunway + **************************************************************************/ +time_t ActiveRunway::requestTimeSlot(time_t eta) +{ + time_t newEta; + time_t separation = 90; + bool found = false; + if (estimatedArrivalTimes.size() == 0) { + estimatedArrivalTimes.push_back(eta); + return eta; + } else { + TimeVectorIterator i = estimatedArrivalTimes.begin(); + //cerr << "Checking eta slots " << eta << ": " << endl; + for (i = estimatedArrivalTimes.begin(); + i != estimatedArrivalTimes.end(); i++) { + //cerr << "Stored time : " << (*i) << endl; + } + i = estimatedArrivalTimes.begin(); + if ((eta + separation) < (*i)) { + newEta = eta; + found = true; + //cerr << "Storing at beginning" << endl; + } + while ((i != estimatedArrivalTimes.end()) && (!found)) { + TimeVectorIterator j = i + 1; + if (j == estimatedArrivalTimes.end()) { + if (((*i) + separation) < eta) { + //cerr << "Storing at end" << endl; + newEta = eta; + } else { + newEta = (*i) + separation; + //cerr << "Storing at end + separation" << endl; + } + } else { + if ((((*j) - (*i)) > (separation * 2))) { // found a potential slot + // now check whether this slow is usable: + // 1) eta should fall between the two points + // i.e. eta > i AND eta < j + // + //cerr << "Found potential slot after " << (*i) << endl; + if (eta > (*i) && (eta < (*j))) { + found = true; + if (eta < ((*i) + separation)) { + newEta = (*i) + separation; + //cerr << "Using original" << (*i) << " + separation " << endl; + } else { + newEta = eta; + //cerr << "Using original after " << (*i) << endl; + } + } else if (eta < (*i)) { + found = true; + newEta = (*i) + separation; + //cerr << "Using delayed slot after " << (*i) << endl; + } + /* + if (((*j) - separation) < eta) { + found = true; + if (((*i) + separation) < eta) { + newEta = eta; + cerr << "Using original after " << (*i) << endl; + } else { + newEta = (*i) + separation; + cerr << "Using " << (*i) << " + separation " << endl; + } + } */ + } + } + i++; + } + } + //cerr << ". done. New ETA : " << newEta << endl; + + estimatedArrivalTimes.push_back(newEta); + sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end()); + // do some housekeeping : remove any timestamps that are past + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + TimeVectorIterator i = estimatedArrivalTimes.begin(); + while (i != estimatedArrivalTimes.end()) { + if ((*i) < now) { + //cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl; + estimatedArrivalTimes.erase(i); + i = estimatedArrivalTimes.begin(); + } else { + i++; + } + } + return newEta; +} + /*************************************************************************** * FGTrafficRecord **************************************************************************/ -FGTrafficRecord::FGTrafficRecord() : - id(0), waitsForId(0), - currentPos(0), - leg(0), - frequencyId(0), - state(0), - allowTransmission(true), - latitude(0), - longitude(0), - heading(0), - speed(0), - altitude(0), - radius(0) +FGTrafficRecord::FGTrafficRecord(): +id(0), waitsForId(0), +currentPos(0), +leg(0), +frequencyId(0), +state(0), +allowTransmission(true), +latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0) { } -void FGTrafficRecord::setPositionAndIntentions(int pos, FGAIFlightPlan *route) +void FGTrafficRecord::setPositionAndIntentions(int pos, + FGAIFlightPlan * route) { - - currentPos = pos; - if (intentions.size()) { - intVecIterator i = intentions.begin(); - if ((*i) != pos) { - SG_LOG(SG_GENERAL, SG_ALERT, "Error in FGTrafficRecord::setPositionAndIntentions"); - //cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl; - for (intVecIterator i = intentions.begin(); i != intentions.end() ; i++) { - //cerr << (*i) << " "; - } - //cerr << endl; - } - intentions.erase(i); - } else { - //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint(); - int size = route->getNrOfWayPoints(); - //cerr << "Setting pos" << pos << " "; - //cerr << "setting intentions "; - for (int i = 0; i < size; i++) { - int val = route->getRouteIndex(i); - //cerr << val<< " "; - if ((val) && (val != pos)) - { - intentions.push_back(val); - //cerr << "[set] "; - } - } - //cerr << endl; - //while (route->next(&legNr, &routeNr)) { - //intentions.push_back(routeNr); - //} - //route->rewind(currentPos); - } - //exit(1); -} -bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord &other) -{ - bool result = false; - //cerr << "Start check 1" << endl; - if (currentPos == other.currentPos) - { - //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl; - result = true; - } - // else if (other.intentions.size()) - // { - // cerr << "Start check 2" << endl; - // intVecIterator i = other.intentions.begin(); - // while (!((i == other.intentions.end()) || ((*i) == currentPos))) - // i++; - // if (i != other.intentions.end()) { - // cerr << "Check Position and intentions: current matches other.intentions" << endl; - // result = true; - // } - else if (intentions.size()) { - //cerr << "Start check 3" << endl; - intVecIterator i = intentions.begin(); - //while (!((i == intentions.end()) || ((*i) == other.currentPos))) - while (i != intentions.end()) { - if ((*i) == other.currentPos) { - break; - } - i++; - } - if (i != intentions.end()) { - //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl; - result = true; - } - } - //cerr << "Done !!" << endl; - return result; -} - -void FGTrafficRecord::setPositionAndHeading(double lat, double lon, double hdg, - double spd, double alt) -{ - latitude = lat; - longitude = lon; - heading = hdg; - speed = spd; - altitude = alt; -} - -int FGTrafficRecord::crosses(FGGroundNetwork *net, FGTrafficRecord &other) -{ - if (checkPositionAndIntentions(other) || (other.checkPositionAndIntentions(*this))) - return -1; - intVecIterator i, j; - int currentTargetNode = 0, otherTargetNode = 0; - if (currentPos > 0) - currentTargetNode = net->findSegment(currentPos )->getEnd()->getIndex(); // OKAY,... - if (other.currentPos > 0) - otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,... - if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0) - return currentTargetNode; - if (intentions.size()) - { - for (i = intentions.begin(); i != intentions.end(); i++) - { - if ((*i) > 0) { - if ((currentTargetNode == net->findSegment(*i)->getEnd()->getIndex())) - { - //cerr << "Current crosses at " << currentTargetNode < 0) { - if (otherTargetNode == net->findSegment(*i)->getEnd()->getIndex()) - { - //cerr << "Other crosses at " << currentTargetNode < 0) && ((*j) > 0)) { - currentTargetNode = net->findSegment(*i)->getEnd()->getIndex(); - otherTargetNode = net->findSegment(*j)->getEnd()->getIndex(); - if (currentTargetNode == otherTargetNode) - { - //cerr << "Routes will cross at " << currentTargetNode << endl; - return currentTargetNode; - } - } - } - } - } - return -1; -} - -bool FGTrafficRecord::onRoute(FGGroundNetwork *net, FGTrafficRecord &other) -{ - int node = -1, othernode = -1; - if (currentPos >0) - node = net->findSegment(currentPos)->getEnd()->getIndex(); - if (other.currentPos > 0) - othernode = net->findSegment(other.currentPos)->getEnd()->getIndex(); - if ((node == othernode) && (node != -1)) - return true; - if (other.intentions.size()) - { - for (intVecIterator i = other.intentions.begin(); i != other.intentions.end(); i++) - { - if (*i > 0) - { - othernode = net->findSegment(*i)->getEnd()->getIndex(); - if ((node == othernode) && (node > -1)) - return true; - } - } + currentPos = pos; + if (intentions.size()) { + intVecIterator i = intentions.begin(); + if ((*i) != pos) { + SG_LOG(SG_GENERAL, SG_ALERT, + "Error in FGTrafficRecord::setPositionAndIntentions"); + //cerr << "Pos : " << pos << " Curr " << *(intentions.begin()) << endl; + for (intVecIterator i = intentions.begin(); + i != intentions.end(); i++) { + //cerr << (*i) << " "; + } + //cerr << endl; + } + intentions.erase(i); + } else { + //FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint(); + int size = route->getNrOfWayPoints(); + //cerr << "Setting pos" << pos << " "; + //cerr << "setting intentions "; + for (int i = 0; i < size; i++) { + int val = route->getRouteIndex(i); + //cerr << val<< " "; + if ((val) && (val != pos)) { + intentions.push_back(val); + //cerr << "[set] "; + } + } + //cerr << endl; + //while (route->next(&legNr, &routeNr)) { + //intentions.push_back(routeNr); + //} + //route->rewind(currentPos); } - //if (other.currentPos > 0) - // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex(); - //if (intentions.size()) - // { - // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++) - // { - // if (*i > 0) - // { - // node = net->findSegment(*i)->getEnd()->getIndex(); - // if ((node == othernode) && (node > -1)) - // return true; - // } - // } - // } - return false; + //exit(1); } - -bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, int node) +bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other) { - // Check if current segment is the reverse segment for the other aircraft - FGTaxiSegment *opp; - //cerr << "Current segment " << currentPos << endl; - if ((currentPos > 0) && (other.currentPos > 0)) - { - opp = net->findSegment(currentPos)->opposite(); - if (opp) { - if (opp->getIndex() == other.currentPos) - return true; - } - - for (intVecIterator i = intentions.begin(); i != intentions.end(); i++) - { - if ((opp = net->findSegment(other.currentPos)->opposite())) - { - if ((*i) > 0) - if (opp->getIndex() == net->findSegment(*i)->getIndex()) - { - if (net->findSegment(*i)->getStart()->getIndex() == node) { - { - //cerr << "Found the node " << node << endl; - return true; - } - } - } - } - if (other.intentions.size()) - { - for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++) - { - // cerr << "Current segment 1 " << (*i) << endl; - if ((*i) > 0) { - if ((opp = net->findSegment(*i)->opposite())) - { - if (opp->getIndex() == - net->findSegment(*j)->getIndex()) - { - //cerr << "Nodes " << net->findSegment(*i)->getIndex() - // << " and " << net->findSegment(*j)->getIndex() - // << " are opposites " << endl; - if (net->findSegment(*i)->getStart()->getIndex() == node) { - { - //cerr << "Found the node " << node << endl; - return true; - } - } - } - } - } - } - } - } - } - return false; + bool result = false; + //cerr << "Start check 1" << endl; + if (currentPos == other.currentPos) { + //cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl; + result = true; + } + // else if (other.intentions.size()) + // { + // cerr << "Start check 2" << endl; + // intVecIterator i = other.intentions.begin(); + // while (!((i == other.intentions.end()) || ((*i) == currentPos))) + // i++; + // if (i != other.intentions.end()) { + // cerr << "Check Position and intentions: current matches other.intentions" << endl; + // result = true; + // } + else if (intentions.size()) { + //cerr << "Start check 3" << endl; + intVecIterator i = intentions.begin(); + //while (!((i == intentions.end()) || ((*i) == other.currentPos))) + while (i != intentions.end()) { + if ((*i) == other.currentPos) { + break; + } + i++; + } + if (i != intentions.end()) { + //cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl; + result = true; + } + } + //cerr << "Done !!" << endl; + return result; } -void FGTrafficRecord::setSpeedAdjustment(double spd) -{ - instruction.setChangeSpeed(true); - instruction.setSpeed(spd); +void FGTrafficRecord::setPositionAndHeading(double lat, double lon, + double hdg, double spd, + double alt) +{ + latitude = lat; + longitude = lon; + heading = hdg; + speed = spd; + altitude = alt; } -void FGTrafficRecord::setHeadingAdjustment(double heading) -{ - instruction.setChangeHeading(true); - instruction.setHeading(heading); +int FGTrafficRecord::crosses(FGGroundNetwork * net, + FGTrafficRecord & other) +{ + if (checkPositionAndIntentions(other) + || (other.checkPositionAndIntentions(*this))) + return -1; + intVecIterator i, j; + int currentTargetNode = 0, otherTargetNode = 0; + if (currentPos > 0) + currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,... + if (other.currentPos > 0) + otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,... + if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0) + return currentTargetNode; + if (intentions.size()) { + for (i = intentions.begin(); i != intentions.end(); i++) { + if ((*i) > 0) { + if ((currentTargetNode == + net->findSegment(*i)->getEnd()->getIndex())) { + //cerr << "Current crosses at " << currentTargetNode < 0) { + if (otherTargetNode == + net->findSegment(*i)->getEnd()->getIndex()) { + //cerr << "Other crosses at " << currentTargetNode < 0) && ((*j) > 0)) { + currentTargetNode = + net->findSegment(*i)->getEnd()->getIndex(); + otherTargetNode = + net->findSegment(*j)->getEnd()->getIndex(); + if (currentTargetNode == otherTargetNode) { + //cerr << "Routes will cross at " << currentTargetNode << endl; + return currentTargetNode; + } + } + } + } + } + return -1; } -bool FGTrafficRecord::pushBackAllowed() { - double course, az2,dist; - SGGeod curr(SGGeod::fromDegM(getLongitude(), - getLatitude(), - getAltitude())); +bool FGTrafficRecord::onRoute(FGGroundNetwork * net, + FGTrafficRecord & other) +{ + int node = -1, othernode = -1; + if (currentPos > 0) + node = net->findSegment(currentPos)->getEnd()->getIndex(); + if (other.currentPos > 0) + othernode = + net->findSegment(other.currentPos)->getEnd()->getIndex(); + if ((node == othernode) && (node != -1)) + return true; + if (other.intentions.size()) { + for (intVecIterator i = other.intentions.begin(); + i != other.intentions.end(); i++) { + if (*i > 0) { + othernode = net->findSegment(*i)->getEnd()->getIndex(); + if ((node == othernode) && (node > -1)) + return true; + } + } + } + //if (other.currentPos > 0) + // othernode = net->findSegment(other.currentPos)->getEnd()->getIndex(); + //if (intentions.size()) + // { + // for (intVecIterator i = intentions.begin(); i != intentions.end(); i++) + // { + // if (*i > 0) + // { + // node = net->findSegment(*i)->getEnd()->getIndex(); + // if ((node == othernode) && (node > -1)) + // return true; + // } + // } + // } + return false; +} - double userLatitude = fgGetDouble("/position/latitude-deg"); - double userLongitude = fgGetDouble("/position/longitude-deg"); - SGGeod user(SGGeod::fromDeg(userLongitude,userLatitude)); - SGGeodesy::inverse(curr, user, course, az2, dist); - //cerr << "Distance to user : " << dist << endl; - return (dist > 250); + +bool FGTrafficRecord::isOpposing(FGGroundNetwork * net, + FGTrafficRecord & other, int node) +{ + // Check if current segment is the reverse segment for the other aircraft + FGTaxiSegment *opp; + //cerr << "Current segment " << currentPos << endl; + if ((currentPos > 0) && (other.currentPos > 0)) { + opp = net->findSegment(currentPos)->opposite(); + if (opp) { + if (opp->getIndex() == other.currentPos) + return true; + } + + for (intVecIterator i = intentions.begin(); i != intentions.end(); + i++) { + if ((opp = net->findSegment(other.currentPos)->opposite())) { + if ((*i) > 0) + if (opp->getIndex() == + net->findSegment(*i)->getIndex()) { + if (net->findSegment(*i)->getStart()->getIndex() == + node) { + { + //cerr << "Found the node " << node << endl; + return true; + } + } + } + } + if (other.intentions.size()) { + for (intVecIterator j = other.intentions.begin(); + j != other.intentions.end(); j++) { + // cerr << "Current segment 1 " << (*i) << endl; + if ((*i) > 0) { + if ((opp = net->findSegment(*i)->opposite())) { + if (opp->getIndex() == + net->findSegment(*j)->getIndex()) { + //cerr << "Nodes " << net->findSegment(*i)->getIndex() + // << " and " << net->findSegment(*j)->getIndex() + // << " are opposites " << endl; + if (net->findSegment(*i)->getStart()-> + getIndex() == node) { + { + //cerr << "Found the node " << node << endl; + return true; + } + } + } + } + } + } + } + } + } + return false; +} + +void FGTrafficRecord::setSpeedAdjustment(double spd) +{ + instruction.setChangeSpeed(true); + instruction.setSpeed(spd); +} + +void FGTrafficRecord::setHeadingAdjustment(double heading) +{ + instruction.setChangeHeading(true); + instruction.setHeading(heading); +} + +bool FGTrafficRecord::pushBackAllowed() +{ + double course, az2, dist; + SGGeod curr(SGGeod::fromDegM(getLongitude(), + getLatitude(), getAltitude())); + + double userLatitude = fgGetDouble("/position/latitude-deg"); + double userLongitude = fgGetDouble("/position/longitude-deg"); + SGGeod user(SGGeod::fromDeg(userLongitude, userLatitude)); + SGGeodesy::inverse(curr, user, course, az2, dist); + //cerr << "Distance to user : " << dist << endl; + return (dist > 250); } @@ -328,22 +418,23 @@ bool FGTrafficRecord::pushBackAllowed() { **************************************************************************/ FGATCInstruction::FGATCInstruction() { - holdPattern = false; - holdPosition = false; - changeSpeed = false; - changeHeading = false; - changeAltitude = false; - resolveCircularWait = false; + holdPattern = false; + holdPosition = false; + changeSpeed = false; + changeHeading = false; + changeAltitude = false; + resolveCircularWait = false; - speed = 0; - heading = 0; - alt = 0; + speed = 0; + heading = 0; + alt = 0; } bool FGATCInstruction::hasInstruction() { - return (holdPattern || holdPosition || changeSpeed || changeHeading || changeAltitude || resolveCircularWait); + return (holdPattern || holdPosition || changeSpeed || changeHeading + || changeAltitude || resolveCircularWait); } /*************************************************************************** @@ -354,24 +445,25 @@ bool FGATCInstruction::hasInstruction() -FGATCController::FGATCController() +FGATCController::FGATCController() { - dt_count = 0; - available = true; - lastTransmission = 0; + dt_count = 0; + available = true; + lastTransmission = 0; } -string FGATCController::getGateName(FGAIAircraft *ref) +string FGATCController::getGateName(FGAIAircraft * ref) { return ref->atGate(); } -void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir) +void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId, + AtcMsgDir msgDir) { string sender, receiver; int stationFreq = 0; - int taxiFreq = 0; - int freqId = 0; + int taxiFreq = 0; + int freqId = 0; string atisInformation; string text; string taxiFreqStr; @@ -388,126 +480,153 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m sender = rec->getAircraft()->getTrafficRef()->getCallSign(); //cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl; switch (rec->getLeg()) { - case 2: - case 3: - freqId = rec->getNextFrequency(); - stationFreq = - rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(rec->getLeg()+freqId); - taxiFreq = - rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getGroundFrequency(3); - receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Ground"; - atisInformation = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getAtisInformation(); - break; - case 4: - receiver = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getName() + "-Tower"; - break; + case 2: + case 3: + freqId = rec->getNextFrequency(); + stationFreq = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getGroundFrequency(rec->getLeg() + freqId); + taxiFreq = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getGroundFrequency(3); + receiver = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getName() + "-Ground"; + atisInformation = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getAtisInformation(); + break; + case 4: + receiver = + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getName() + "-Tower"; + break; } // Swap sender and receiver value in case of a ground to air transmission if (msgDir == ATC_GROUND_TO_AIR) { - string tmp = sender; - sender = receiver; - receiver = tmp; + string tmp = sender; + sender = receiver; + receiver = tmp; } switch (msgId) { - case MSG_ANNOUNCE_ENGINE_START: - text = sender + ". Ready to Start up"; - break; - case MSG_REQUEST_ENGINE_START: - text = receiver + ", This is " + sender + ". Position " +getGateName(rec->getAircraft()) + - ". Information " + atisInformation + ". " + - rec->getAircraft()->getTrafficRef()->getFlightRules() + " to " + - rec->getAircraft()->getTrafficRef()->getArrivalAirport()->getName() + ". Request start-up"; - break; - // Acknowledge engine startup permission - // Assign departure runway - // Assign SID, if necessery (TODO) - case MSG_PERMIT_ENGINE_START: - taxiFreqStr = formatATCFrequency3_2(taxiFreq); + case MSG_ANNOUNCE_ENGINE_START: + text = sender + ". Ready to Start up"; + break; + case MSG_REQUEST_ENGINE_START: + text = + receiver + ", This is " + sender + ". Position " + + getGateName(rec->getAircraft()) + ". Information " + + atisInformation + ". " + + rec->getAircraft()->getTrafficRef()->getFlightRules() + + " to " + + rec->getAircraft()->getTrafficRef()->getArrivalAirport()-> + getName() + ". Request start-up"; + break; + // Acknowledge engine startup permission + // Assign departure runway + // Assign SID, if necessery (TODO) + case MSG_PERMIT_ENGINE_START: + taxiFreqStr = formatATCFrequency3_2(taxiFreq); - heading = rec->getAircraft()->getTrafficRef()->getCourse(); - fltType = rec->getAircraft()->getTrafficRef()->getFlightType(); - rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType); + heading = rec->getAircraft()->getTrafficRef()->getCourse(); + fltType = rec->getAircraft()->getTrafficRef()->getFlightType(); + rwyClass = + rec->getAircraft()->GetFlightPlan()-> + getRunwayClassFromTrafficType(fltType); - rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, heading); - rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway); - fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->getDynamics()->getSID(activeRunway, heading); - rec->getAircraft()->GetFlightPlan()->setSID(fp); - if (fp) { - SID = fp->getName() + " departure"; - } else { - SID = "fly runway heading "; - } - //snprintf(buffer, 7, "%3.2f", heading); - fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules(); - transponderCode = genTransponderCode(fltRules); - rec->getAircraft()->SetTransponderCode(transponderCode); - text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " + activeRunway - + ", " + SID + ", squawk " + transponderCode + ". " + - "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender + " control."; - break; - case MSG_DENY_ENGINE_START: - text = receiver + ". Standby"; - break; - case MSG_ACKNOWLEDGE_ENGINE_START: - fp = rec->getAircraft()->GetFlightPlan()->getSID(); - if (fp) { - SID = rec->getAircraft()->GetFlightPlan()->getSID()->getName() + " departure"; - } else { - SID = "fly runway heading "; - } - taxiFreqStr = formatATCFrequency3_2(taxiFreq); - activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); - transponderCode = rec->getAircraft()->GetTransponderCode(); - text = receiver + ". Start-up approved. " + atisInformation + " correct, runway " + - activeRunway + ", " + SID + ", squawk " + transponderCode + ". " + - "For push-back and taxi clearance call " + taxiFreqStr + ". " + sender; - break; - case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY: - taxiFreqStr = formatATCFrequency3_2(taxiFreq); - text = receiver + ". Switching to " + taxiFreqStr + ". " + sender; - break; - case MSG_INITIATE_CONTACT: - text = receiver + ". With you. " + sender; - break; - case MSG_ACKNOWLEDGE_INITIATE_CONTACT: - text = receiver + ". Roger. " + sender; - break; - case MSG_REQUEST_PUSHBACK_CLEARANCE: - text = receiver + ". Request push-back. " + sender; - break; - case MSG_PERMIT_PUSHBACK_CLEARANCE: - text = receiver + ". Push-back approved. " + sender; - break; - case MSG_HOLD_PUSHBACK_CLEARANCE: - text = receiver + ". Standby. " + sender; - break; - case MSG_REQUEST_TAXI_CLEARANCE: - text = receiver + ". Ready to Taxi. " + sender; - break; - case MSG_ISSUE_TAXI_CLEARANCE: - text = receiver + ". Cleared to taxi. " + sender; - break; - case MSG_ACKNOWLEDGE_TAXI_CLEARANCE: - text = receiver + ". Cleared to taxi. " + sender; - break; - case MSG_HOLD_POSITION: - text = receiver + ". Hold Position. " + sender; - break; - case MSG_ACKNOWLEDGE_HOLD_POSITION: - text = receiver + ". Holding Position. " + sender; - break; - case MSG_RESUME_TAXI: - text = receiver + ". Resume Taxiing. " + sender; - break; - case MSG_ACKNOWLEDGE_RESUME_TAXI: - text = receiver + ". Continuing Taxi. " + sender; - break; - default: - text = text + sender + ". Transmitting unknown Message"; - break; + rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getActiveRunway(rwyClass, 1, activeRunway, + heading); + rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway); + fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()-> + getDynamics()->getSID(activeRunway, heading); + rec->getAircraft()->GetFlightPlan()->setSID(fp); + if (fp) { + SID = fp->getName() + " departure"; + } else { + SID = "fly runway heading "; + } + //snprintf(buffer, 7, "%3.2f", heading); + fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules(); + transponderCode = genTransponderCode(fltRules); + rec->getAircraft()->SetTransponderCode(transponderCode); + text = + receiver + ". Start-up approved. " + atisInformation + + " correct, runway " + activeRunway + ", " + SID + ", squawk " + + transponderCode + ". " + + "For push-back and taxi clearance call " + taxiFreqStr + ". " + + sender + " control."; + break; + case MSG_DENY_ENGINE_START: + text = receiver + ". Standby"; + break; + case MSG_ACKNOWLEDGE_ENGINE_START: + fp = rec->getAircraft()->GetFlightPlan()->getSID(); + if (fp) { + SID = + rec->getAircraft()->GetFlightPlan()->getSID()->getName() + + " departure"; + } else { + SID = "fly runway heading "; + } + taxiFreqStr = formatATCFrequency3_2(taxiFreq); + activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway(); + transponderCode = rec->getAircraft()->GetTransponderCode(); + text = + receiver + ". Start-up approved. " + atisInformation + + " correct, runway " + activeRunway + ", " + SID + ", squawk " + + transponderCode + ". " + + "For push-back and taxi clearance call " + taxiFreqStr + ". " + + sender; + break; + case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY: + taxiFreqStr = formatATCFrequency3_2(taxiFreq); + text = receiver + ". Switching to " + taxiFreqStr + ". " + sender; + break; + case MSG_INITIATE_CONTACT: + text = receiver + ". With you. " + sender; + break; + case MSG_ACKNOWLEDGE_INITIATE_CONTACT: + text = receiver + ". Roger. " + sender; + break; + case MSG_REQUEST_PUSHBACK_CLEARANCE: + text = receiver + ". Request push-back. " + sender; + break; + case MSG_PERMIT_PUSHBACK_CLEARANCE: + text = receiver + ". Push-back approved. " + sender; + break; + case MSG_HOLD_PUSHBACK_CLEARANCE: + text = receiver + ". Standby. " + sender; + break; + case MSG_REQUEST_TAXI_CLEARANCE: + text = receiver + ". Ready to Taxi. " + sender; + break; + case MSG_ISSUE_TAXI_CLEARANCE: + text = receiver + ". Cleared to taxi. " + sender; + break; + case MSG_ACKNOWLEDGE_TAXI_CLEARANCE: + text = receiver + ". Cleared to taxi. " + sender; + break; + case MSG_HOLD_POSITION: + text = receiver + ". Hold Position. " + sender; + break; + case MSG_ACKNOWLEDGE_HOLD_POSITION: + text = receiver + ". Holding Position. " + sender; + break; + case MSG_RESUME_TAXI: + text = receiver + ". Resume Taxiing. " + sender; + break; + case MSG_ACKNOWLEDGE_RESUME_TAXI: + text = receiver + ". Continuing Taxi. " + sender; + break; + default: + text = text + sender + ". Transmitting unknown Message"; + break; } - double onBoardRadioFreq0 = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); - double onBoardRadioFreq1 = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); + double onBoardRadioFreq0 = + fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); + double onBoardRadioFreq1 = + fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5); int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5); //cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl; @@ -515,27 +634,31 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m // Display ATC message only when one of the radios is tuned // the relevant frequency. // Note that distance attenuation is currently not yet implemented - if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) { + if ((onBoardRadioFreqI0 == stationFreq) + || (onBoardRadioFreqI1 == stationFreq)) { if (rec->allowTransmissions()) { fgSetString("/sim/messages/atc", text.c_str()); } } } -string FGATCController::formatATCFrequency3_2(int freq) { +string FGATCController::formatATCFrequency3_2(int freq) +{ char buffer[7]; - snprintf(buffer, 7, "%3.2f", ( (float) freq / 100.0) ); + snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0)); return string(buffer); } // TODO: Set transponder codes according to real-world routes. // The current version just returns a random string of four octal numbers. -string FGATCController::genTransponderCode(string fltRules) { +string FGATCController::genTransponderCode(string fltRules) +{ if (fltRules == "VFR") { return string("1200"); } else { char buffer[5]; - snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8,rand() % 8, rand() % 8); + snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8, + rand() % 8); return string(buffer); } } @@ -544,69 +667,72 @@ string FGATCController::genTransponderCode(string fltRules) { * class FGTowerController * **************************************************************************/ -FGTowerController::FGTowerController() : - FGATCController() +FGTowerController::FGTowerController(): +FGATCController() { } // -void FGTowerController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition, - double lat, double lon, double heading, - double speed, double alt, double radius, int leg, - FGAIAircraft *ref) -{ - TrafficVectorIterator i = activeTraffic.begin(); - // Search whether the current id alread has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } - } - - // Add a new TrafficRecord if no one exsists for this aircraft. - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - FGTrafficRecord rec; - rec.setId(id); - - rec.setPositionAndHeading(lat, lon, heading, speed, alt); - rec.setRunway(intendedRoute->getRunway()); - rec.setLeg(leg); - //rec.setCallSign(callsign); - rec.setAircraft(ref); - activeTraffic.push_back(rec); - } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - } -} - -void FGTowerController::update(int id, double lat, double lon, double heading, double speed, double alt, - double dt) +void FGTowerController::announcePosition(int id, + FGAIFlightPlan * intendedRoute, + int currentPosition, double lat, + double lon, double heading, + double speed, double alt, + double radius, int leg, + FGAIAircraft * ref) { TrafficVectorIterator i = activeTraffic.begin(); - // Search search if the current id has an entry + // Search whether the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + activeTraffic.push_back(rec); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + } +} + +void FGTowerController::update(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search whether the current id has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route TrafficVectorIterator current, closest; if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } - // // update position of the current aircraft if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: updating aircraft without traffic record"); } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - current = i; + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; } setDt(getDt() + dt); @@ -614,65 +740,66 @@ void FGTowerController::update(int id, double lat, double lon, double heading, d ActiveRunwayVecIterator rwy = activeRunways.begin(); // again, a map might be more efficient here if (activeRunways.size()) { - //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) { - while (rwy != activeRunways.end()) { - if (rwy->getRunwayName() == current->getRunway()) { - break; - } - rwy++; - } + //while ((rwy->getRunwayName() != current->getRunway()) && (rwy != activeRunways.end())) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == current->getRunway()) { + break; + } + rwy++; + } } if (rwy == activeRunways.end()) { - ActiveRunway aRwy(current->getRunway(), id); - activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet - current->setHoldPosition(false); // Clear the current aircraft to continue - } - else { - // Okay, we have a clearance record for this runway, so check - // whether the clearence ID matches that of the current aircraft - if (id == rwy->getCleared()) { - current->setHoldPosition(false); - } else { - current->setHoldPosition(true); - } + ActiveRunway aRwy(current->getRunway(), id); + activeRunways.push_back(aRwy); // Since there are no clearance records for this runway yet + current->setHoldPosition(false); // Clear the current aircraft to continue + } else { + // Okay, we have a clearance record for this runway, so check + // whether the clearence ID matches that of the current aircraft + if (id == rwy->getCleared()) { + current->setHoldPosition(false); + } else { + current->setHoldPosition(true); + } } } -void FGTowerController::signOff(int id) +void FGTowerController::signOff(int id) { - TrafficVectorIterator i = activeTraffic.begin(); - // Search search if the current id alread has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } // If this aircraft has left the runway, we can clear the departure record for this runway ActiveRunwayVecIterator rwy = activeRunways.begin(); if (activeRunways.size()) { - //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) { - while (rwy != activeRunways.end()) { - if (rwy->getRunwayName() == i->getRunway()) { - break; - } - rwy++; - } - if (rwy != activeRunways.end()) { - rwy = activeRunways.erase(rwy); - } else { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff"); - } + //while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == i->getRunway()) { + break; + } + rwy++; + } + if (rwy != activeRunways.end()) { + rwy = activeRunways.erase(rwy); + } else { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff"); + } } if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from tower"); } else { - i = activeTraffic.erase(i); + i = activeTraffic.erase(i); } } @@ -687,90 +814,93 @@ bool FGTowerController::hasInstruction(int id) TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) - { + if (activeTraffic.size()) { //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record"); } else { - return i->hasInstruction(); + return i->hasInstruction(); } - return false; + return false; } FGATCInstruction FGTowerController::getInstruction(int id) { - TrafficVectorIterator i = activeTraffic.begin(); - // Search search if the current id has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } - } - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record"); - } else { - return i->getInstruction(); - } - return FGATCInstruction(); + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record"); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); } /*************************************************************************** * class FGStartupController * **************************************************************************/ -FGStartupController::FGStartupController() : - FGATCController() +FGStartupController::FGStartupController(): +FGATCController() { } -void FGStartupController::announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentPosition, - double lat, double lon, double heading, - double speed, double alt, double radius, int leg, - FGAIAircraft *ref) +void FGStartupController::announcePosition(int id, + FGAIFlightPlan * intendedRoute, + int currentPosition, double lat, + double lon, double heading, + double speed, double alt, + double radius, int leg, + FGAIAircraft * ref) { - TrafficVectorIterator i = activeTraffic.begin(); - // Search whether the current id alread has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; + TrafficVectorIterator i = activeTraffic.begin(); + // Search whether the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } - } - - // Add a new TrafficRecord if no one exsists for this aircraft. - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - FGTrafficRecord rec; - rec.setId(id); + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + FGTrafficRecord rec; + rec.setId(id); - rec.setPositionAndHeading(lat, lon, heading, speed, alt); - rec.setRunway(intendedRoute->getRunway()); - rec.setLeg(leg); - //rec.setCallSign(callsign); - rec.setAircraft(ref); - rec.setHoldPosition(true); - activeTraffic.push_back(rec); + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + rec.setHoldPosition(true); + activeTraffic.push_back(rec); } else { i->setPositionAndHeading(lat, lon, heading, speed, alt); - - } + + } } // NOTE: @@ -784,97 +914,101 @@ bool FGStartupController::hasInstruction(int id) TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) - { + if (activeTraffic.size()) { //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: checking ATC instruction for aircraft without traffic record"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record"); } else { - return i->hasInstruction(); + return i->hasInstruction(); } - return false; + return false; } FGATCInstruction FGStartupController::getInstruction(int id) { - TrafficVectorIterator i = activeTraffic.begin(); - // Search search if the current id has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route - if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } - } - if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: requesting ATC instruction for aircraft without traffic record"); - } else { - return i->getInstruction(); - } - return FGATCInstruction(); -} - -void FGStartupController::signOff(int id) -{ - TrafficVectorIterator i = activeTraffic.begin(); - // Search search if the current id alread has an entry - // This might be faster using a map instead of a vector, but let's start by taking a safe route + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: Aircraft without traffic record is signing off from tower"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record"); } else { - i = activeTraffic.erase(i); + return i->getInstruction(); + } + return FGATCInstruction(); +} + +void FGStartupController::signOff(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from tower"); + } else { + i = activeTraffic.erase(i); } } -void FGStartupController::update(int id, double lat, double lon, double heading, double speed, double alt, - double dt) +void FGStartupController::update(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) { TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route TrafficVectorIterator current, closest; if (activeTraffic.size()) { - //while ((i->getId() != id) && i != activeTraffic.end()) { - while (i != activeTraffic.end()) { - if (i->getId() == id) { - break; - } - i++; - } + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } } - // // update position of the current aircraft if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { - SG_LOG(SG_GENERAL, SG_ALERT, "AI error: updating aircraft without traffic record"); + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: updating aircraft without traffic record"); } else { - i->setPositionAndHeading(lat, lon, heading, speed, alt); - current = i; + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; } setDt(getDt() + dt); int state = i->getState(); - time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime(); - time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + time_t startTime = + i->getAircraft()->getTrafficRef()->getDepartureTime(); + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); //cerr << i->getAircraft()->getTrafficRef()->getCallSign() // << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available // << " at parking " << getGateName(i->getAircraft()) << endl; @@ -885,91 +1019,287 @@ void FGStartupController::update(int id, double lat, double lon, double heading, if ((state == 0) && available) { if (now > startTime) { - //cerr << "Transmitting startup msg" << endl; - transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); - i->updateState(); - lastTransmission = now; - available = false; + //cerr << "Transmitting startup msg" << endl; + transmit(&(*i), MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND); + i->updateState(); + lastTransmission = now; + available = false; } } if ((state == 1) && available) { - if (now > startTime+60) { + if (now > startTime + 60) { transmit(&(*i), MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND); i->updateState(); lastTransmission = now; - available = false; + available = false; } } if ((state == 2) && available) { - if (now > startTime+80) { + if (now > startTime + 80) { transmit(&(*i), MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR); i->updateState(); lastTransmission = now; - available = false; + available = false; } } - if ((state == 3) && available){ - if (now > startTime+100) { - transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND); + if ((state == 3) && available) { + if (now > startTime + 100) { + transmit(&(*i), MSG_ACKNOWLEDGE_ENGINE_START, + ATC_AIR_TO_GROUND); i->updateState(); lastTransmission = now; - available = false; + available = false; } - } - // Note: The next four stages are only necessesary when Startup control is - // on a different frequency, compared to ground control - if ((state == 4) && available){ - if (now > startTime+130) { - transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND); + } + // Note: The next four stages are only necessesary when Startup control is + // on a different frequency, compared to ground control + if ((state == 4) && available) { + if (now > startTime + 130) { + transmit(&(*i), MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, + ATC_AIR_TO_GROUND); i->updateState(); i->nextFrequency(); lastTransmission = now; - available = false; + available = false; } - } - if ((state == 5) && available){ - if (now > startTime+140) { + } + if ((state == 5) && available) { + if (now > startTime + 140) { transmit(&(*i), MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND); i->updateState(); lastTransmission = now; - available = false; + available = false; } - } - if ((state == 6) && available){ - if (now > startTime+150) { - transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR); + } + if ((state == 6) && available) { + if (now > startTime + 150) { + transmit(&(*i), MSG_ACKNOWLEDGE_INITIATE_CONTACT, + ATC_GROUND_TO_AIR); i->updateState(); lastTransmission = now; - available = false; + available = false; } - } + } - - // TODO: Switch to APRON control and request pushback Clearance. - // Get Push back clearance - if ((state == 7) && available){ - if (now > startTime+180) { - transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND); + // TODO: Switch to APRON control and request pushback Clearance. + // Get Push back clearance + if ((state == 7) && available) { + if (now > startTime + 180) { + transmit(&(*i), MSG_REQUEST_PUSHBACK_CLEARANCE, + ATC_AIR_TO_GROUND); i->updateState(); lastTransmission = now; - available = false; + available = false; } - } - if ((state == 8) && available){ - if (now > startTime+200) { + } + if ((state == 8) && available) { + if (now > startTime + 200) { if (i->pushBackAllowed()) { - i->allowRepeatedTransmissions(); - transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR); - i->updateState(); + i->allowRepeatedTransmissions(); + transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, + ATC_GROUND_TO_AIR); + i->updateState(); } else { - transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR); - i->suppressRepeatedTransmissions(); + transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, + ATC_GROUND_TO_AIR); + i->suppressRepeatedTransmissions(); } lastTransmission = now; - available = false; + available = false; } - } - if ((state == 9) && available){ - i->setHoldPosition(false); - } + } + if ((state == 9) && available) { + i->setHoldPosition(false); + } +} + + +/*************************************************************************** + * class FGApproachController + * + **************************************************************************/ +FGApproachController::FGApproachController(): +FGATCController() +{ +} + +// +void FGApproachController::announcePosition(int id, + FGAIFlightPlan * intendedRoute, + int currentPosition, + double lat, double lon, + double heading, double speed, + double alt, double radius, + int leg, FGAIAircraft * ref) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search whether the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + // Add a new TrafficRecord if no one exsists for this aircraft. + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + FGTrafficRecord rec; + rec.setId(id); + + rec.setPositionAndHeading(lat, lon, heading, speed, alt); + rec.setRunway(intendedRoute->getRunway()); + rec.setLeg(leg); + //rec.setCallSign(callsign); + rec.setAircraft(ref); + activeTraffic.push_back(rec); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + } +} + +void FGApproachController::update(int id, double lat, double lon, + double heading, double speed, double alt, + double dt) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + TrafficVectorIterator current, closest; + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } +// // update position of the current aircraft + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: updating aircraft without traffic record"); + } else { + i->setPositionAndHeading(lat, lon, heading, speed, alt); + current = i; + //cerr << "ApproachController: checking for speed" << endl; + time_t time_diff = + current->getAircraft()-> + checkForArrivalTime(string("final001")); + if (time_diff > 15) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 1.35); + } else if (time_diff > 5) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 1.2); + } else if (time_diff < -15) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 0.65); + } else if (time_diff < -5) { + current->setSpeedAdjustment(current->getAircraft()-> + getPerformance()->vDescent() * + 0.8); + } else { + current->clearSpeedAdjustment(); + } + //current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff); + } + setDt(getDt() + dt); +} + +void FGApproachController::signOff(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id alread has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: Aircraft without traffic record is signing off from approach"); + } else { + i = activeTraffic.erase(i); + } +} + + + + +bool FGApproachController::hasInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: checking ATC instruction for aircraft without traffic record"); + } else { + return i->hasInstruction(); + } + return false; +} + + +FGATCInstruction FGApproachController::getInstruction(int id) +{ + TrafficVectorIterator i = activeTraffic.begin(); + // Search search if the current id has an entry + // This might be faster using a map instead of a vector, but let's start by taking a safe route + if (activeTraffic.size()) { + //while ((i->getId() != id) && i != activeTraffic.end()) { + while (i != activeTraffic.end()) { + if (i->getId() == id) { + break; + } + i++; + } + } + if (i == activeTraffic.end() || (activeTraffic.size() == 0)) { + SG_LOG(SG_GENERAL, SG_ALERT, + "AI error: requesting ATC instruction for aircraft without traffic record"); + } else { + return i->getInstruction(); + } + return FGATCInstruction(); +} + + +ActiveRunway *FGApproachController::getRunway(string name) +{ + ActiveRunwayVecIterator rwy = activeRunways.begin(); + if (activeRunways.size()) { + while (rwy != activeRunways.end()) { + if (rwy->getRunwayName() == name) { + break; + } + rwy++; + } + } + if (rwy == activeRunways.end()) { + ActiveRunway aRwy(name, 0); + activeRunways.push_back(aRwy); + rwy = activeRunways.end() - 1; + } + return &(*rwy); } diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index d069a5b84..7e83a72b4 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -28,6 +28,8 @@ #include +// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER... +#include #include @@ -185,6 +187,9 @@ public: typedef vector TrafficVector; typedef vector::iterator TrafficVectorIterator; +typedef vector TimeVector; +typedef vector::iterator TimeVectorIterator; + /*********************************************************************** * Active runway, a utility class to keep track of which aircraft has @@ -195,11 +200,18 @@ class ActiveRunway private: string rwy; int currentlyCleared; + double distanceToFinal; + TimeVector estimatedArrivalTimes; public: - ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; }; + ActiveRunway(string r, int cc) { rwy = r; currentlyCleared = cc; distanceToFinal = 6.0 * SG_NM_TO_METER; }; string getRunwayName() { return rwy; }; int getCleared () { return currentlyCleared; }; + double getApproachDistance() { return distanceToFinal; }; + //time_t getEstApproachTime() { return estimatedArrival; }; + + //void setEstApproachTime(time_t time) { estimatedArrival = time; }; + time_t requestTimeSlot(time_t eta); }; typedef vector ActiveRunwayVec; @@ -207,7 +219,7 @@ typedef vector::iterator ActiveRunwayVecIterator; /** * class FGATCController - * NOTE: this class serves as an abstraction layer for all sorts of ATC controller. + * NOTE: this class serves as an abstraction layer for all sorts of ATC controllers. *************************************************************************************/ class FGATCController { @@ -317,4 +329,33 @@ public: }; +/****************************************************************************** + * class FGTowerControl + *****************************************************************************/ +class FGApproachController : public FGATCController +{ +private: + TrafficVector activeTraffic; + ActiveRunwayVec activeRunways; + +public: + FGApproachController(); + virtual ~FGApproachController() {}; + 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 signOff(int id); + virtual void update(int id, double lat, double lon, + double heading, double speed, double alt, double dt); + virtual bool hasInstruction(int id); + virtual FGATCInstruction getInstruction(int id); + + ActiveRunway* getRunway(string name); + + bool hasActiveTraffic() { return activeTraffic.size() != 0; }; + TrafficVector &getActiveTraffic() { return activeTraffic; }; +}; + + #endif // _TRAFFIC_CONTROL_HXX diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index f45341e5e..f5ebd05d1 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -325,17 +325,21 @@ bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType, takeoff.clear(); lastUpdate = dayStart; prevTrafficType = trafficType; - + /* FGEnvironment stationweather = ((FGEnvironmentMgr *) globals->get_subsystem("environment")) ->getEnvironment(getLatitude(), getLongitude(), getElevation()); - - windSpeed = stationweather.get_wind_speed_kt(); - windHeading = stationweather.get_wind_from_heading_deg(); + */ + windSpeed = fgGetInt("/environment/metar/base-wind-speed-kt"); //stationweather.get_wind_speed_kt(); + windHeading = fgGetInt("/environment/metar/base-wind-dir-deg"); + //stationweather.get_wind_from_heading_deg(); string scheduleName; - //cerr << "finding active Runway for" << _ap->getId() << endl; + //cerr << "finding active Runway for : " << _ap->getId() << endl; + //cerr << "Wind Heading : " << windHeading << endl; + //cerr << "Wind Speed : " << windSpeed << endl; + //cerr << "Nr of seconds since day start << " << dayStart << endl; ScheduleTime *currSched; @@ -347,7 +351,7 @@ bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType, scheduleName = currSched->getName(dayStart); maxTail = currSched->getTailWind(); maxCross = currSched->getCrossWind(); - //cerr << "SChedule anme = " << scheduleName << endl; + //cerr << "Current Schedule = : " << scheduleName << endl; if (scheduleName.empty()) return false; //cerr << "C"<< endl; @@ -371,6 +375,13 @@ bool FGAirportDynamics::innerGetActiveRunway(const string & trafficType, currentlyActive = &ulActive; } + //cerr << "Durrently active selection for " << trafficType << ": "; + for (stringVecIterator it = currentlyActive->begin(); + it != currentlyActive->end(); it++) { + //cerr << (*it) << " "; + } + //cerr << endl; + currRunwayGroup->setActive(_ap, windSpeed, windHeading, diff --git a/src/Airports/dynamics.hxx b/src/Airports/dynamics.hxx index 1f142eb52..609218218 100644 --- a/src/Airports/dynamics.hxx +++ b/src/Airports/dynamics.hxx @@ -46,12 +46,13 @@ class FGAirportDynamics { private: FGAirport* _ap; - FGParkingVec parkings; - FGRunwayPreference rwyPrefs; - FGSidStar SIDs; - FGStartupController startupController; - FGGroundNetwork groundNetwork; - FGTowerController towerController; + FGParkingVec parkings; + FGRunwayPreference rwyPrefs; + FGSidStar SIDs; + FGStartupController startupController; + FGGroundNetwork groundNetwork; + FGTowerController towerController; + FGApproachController approachController; time_t lastUpdate; string prevTrafficType; @@ -112,9 +113,10 @@ public: // ATC related functions. - FGStartupController *getStartupController() { return &startupController; }; - FGGroundNetwork *getGroundNetwork() { return &groundNetwork; }; - FGTowerController *getTowerController() { return &towerController; }; + FGStartupController *getStartupController() { return &startupController; }; + FGGroundNetwork *getGroundNetwork() { return &groundNetwork; }; + FGTowerController *getTowerController() { return &towerController; }; + FGApproachController *getApproachController() { return &approachController; }; const string& getAtisInformation() { return atisInformation; }; int getGroundFrequency(unsigned leg); //{ return freqGround.size() ? freqGround[0] : 0; }; diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 011acb866..3d96ccf7d 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -553,35 +553,46 @@ void FGGroundNetwork::update(int id, double lat, double lon, current->clearResolveCircularWait(); current->setWaitsForId(0); checkSpeedAdjustment(id, lat, lon, heading, speed, alt); - checkHoldPosition(id, lat, lon, heading, speed, alt); - if (checkForCircularWaits(id)) { - i->setResolveCircularWait(); - } bool needsTaxiClearance = current->getAircraft()->getTaxiClearanceRequest(); - int state = current->getState(); - time_t now = time(NULL) + fgGetLong("/sim/time/warp"); - if ((now - lastTransmission) > 15) { - available = true; - } - if (needsTaxiClearance && available) { - transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND); - current->getAircraft()->setTaxiClearanceRequest(false); - current->setState(3); - lastTransmission = now; - available = false; - } - if ((state == 3) && available) { - transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR); - current->setState(4); - lastTransmission = now; - available = false; - } - if ((state == 4) && available) { - transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND); - current->setState(0); - lastTransmission = now; - available = false; + if (!needsTaxiClearance) { + checkHoldPosition(id, lat, lon, heading, speed, alt); + if (checkForCircularWaits(id)) { + i->setResolveCircularWait(); + } + } else { + current->setHoldPosition(true); + int state = current->getState(); + time_t now = time(NULL) + fgGetLong("/sim/time/warp"); + if ((now - lastTransmission) > 15) { + available = true; + } + if ((state < 3) && available) { + transmit(&(*current), MSG_REQUEST_TAXI_CLEARANCE, ATC_AIR_TO_GROUND); + current->setState(3); + lastTransmission = now; + available = false; + } + if ((state == 3) && available) { + transmit(&(*current), MSG_ISSUE_TAXI_CLEARANCE, ATC_GROUND_TO_AIR); + current->setState(4); + lastTransmission = now; + available = false; + } + if ((state == 4) && available) { + transmit(&(*current), MSG_ACKNOWLEDGE_TAXI_CLEARANCE, ATC_AIR_TO_GROUND); + current->setState(5); + lastTransmission = now; + available = false; + } + if ((state == 5) && available) { + current->setState(0); + current->getAircraft()->setTaxiClearanceRequest(false); + current->setHoldPosition(true); + available = false; + } + } + } /** diff --git a/src/Airports/runwaybase.cxx b/src/Airports/runwaybase.cxx index 1131e22a0..01855347c 100644 --- a/src/Airports/runwaybase.cxx +++ b/src/Airports/runwaybase.cxx @@ -73,6 +73,27 @@ SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const return result; } + + +SGGeod FGRunwayBase::pointOffCenterline(double aOffset, double lateralOffset) const +{ + SGGeod result; + SGGeod temp; + double dummyAz2; + double halfLengthMetres = lengthM() * 0.5; + + SGGeodesy::direct(mPosition, _heading, + aOffset - halfLengthMetres, + temp, dummyAz2); + + SGGeodesy::direct(temp, (_heading+90.0), + lateralOffset, + result, dummyAz2); + + return result; +} + + bool FGRunwayBase::isHardSurface() const { return ((_surface_code == 1) || (_surface_code == 2)); diff --git a/src/Airports/runwaybase.hxx b/src/Airports/runwaybase.hxx index 1f7e58f12..745d4cb86 100644 --- a/src/Airports/runwaybase.hxx +++ b/src/Airports/runwaybase.hxx @@ -52,6 +52,7 @@ public: * opposited direction. 0.0 corresponds to the (non-displaced) threshold */ SGGeod pointOnCenterline(double aOffset) const; + SGGeod pointOffCenterline(double aOffset, double lateralOffset) const; double lengthFt() const { return _length; } diff --git a/src/Airports/runwayprefs.cxx b/src/Airports/runwayprefs.cxx index b47c6bc77..f3110a1a3 100644 --- a/src/Airports/runwayprefs.cxx +++ b/src/Airports/runwayprefs.cxx @@ -41,375 +41,372 @@ * ScheduleTime ***************e*************************************************************/ void ScheduleTime::clear() -{ - start.clear(); - end.clear(); - scheduleNames.clear(); -} - - -ScheduleTime::ScheduleTime(const ScheduleTime &other) { - //timeVec start; - timeVecConstIterator i; - for (i = other.start.begin(); i != other.start.end(); i++) - start.push_back(*i); - for (i = other.end.begin(); i != other.end.end(); i++) - end.push_back(*i); - stringVecConstIterator k; - for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++) - scheduleNames.push_back(*k); - - //timeVec end; - //stringVec scheduleNames; - tailWind = other.tailWind; - crssWind = other.tailWind; + start.clear(); + end.clear(); + scheduleNames.clear(); } -ScheduleTime & ScheduleTime::operator= (const ScheduleTime &other) +ScheduleTime::ScheduleTime(const ScheduleTime & other) { - //timeVec start; - clear(); - timeVecConstIterator i; - for (i = other.start.begin(); i != other.start.end(); i++) - start.push_back(*i); - for (i = other.end.begin(); i != other.end.end(); i++) - end.push_back(*i); - stringVecConstIterator k; - for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); k++) - scheduleNames.push_back(*k); - - //timeVec end; - //stringVec scheduleNames; - tailWind = other.tailWind; - crssWind = other.tailWind; - return *this; + //timeVec start; + timeVecConstIterator i; + for (i = other.start.begin(); i != other.start.end(); i++) + start.push_back(*i); + for (i = other.end.begin(); i != other.end.end(); i++) + end.push_back(*i); + stringVecConstIterator k; + for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); + k++) + scheduleNames.push_back(*k); + + //timeVec end; + //stringVec scheduleNames; + tailWind = other.tailWind; + crssWind = other.tailWind; } + + +ScheduleTime & ScheduleTime::operator=(const ScheduleTime & other) +{ + //timeVec start; + clear(); + timeVecConstIterator i; + for (i = other.start.begin(); i != other.start.end(); i++) + start.push_back(*i); + for (i = other.end.begin(); i != other.end.end(); i++) + end.push_back(*i); + stringVecConstIterator k; + for (k = other.scheduleNames.begin(); k != other.scheduleNames.end(); + k++) + scheduleNames.push_back(*k); + + //timeVec end; + //stringVec scheduleNames; + tailWind = other.tailWind; + crssWind = other.tailWind; + return *this; +} + string ScheduleTime::getName(time_t dayStart) { - if ((start.size() != end.size()) || (start.size() != scheduleNames.size())) - { - SG_LOG( SG_GENERAL, SG_INFO, "Unable to parse schedule times" ); - exit(1); - } - else - { - int nrItems = start.size(); - //cerr << "Nr of items to process: " << nrItems << endl; - if (nrItems > 0) - { - for (unsigned int i = 0; i < start.size(); i++) - { - //cerr << i << endl; - if ((dayStart >= start[i]) && (dayStart <= end[i])) - return scheduleNames[i]; - } - } - //couldn't find one so return 0; - //cerr << "Returning 0 " << endl; + if ((start.size() != end.size()) + || (start.size() != scheduleNames.size())) { + SG_LOG(SG_GENERAL, SG_INFO, "Unable to parse schedule times"); + exit(1); + } else { + int nrItems = start.size(); + //cerr << "Nr of items to process: " << nrItems << endl; + if (nrItems > 0) { + for (unsigned int i = 0; i < start.size(); i++) { + //cerr << i << endl; + if ((dayStart >= start[i]) && (dayStart <= end[i])) + return scheduleNames[i]; + } + } + //couldn't find one so return 0; + //cerr << "Returning 0 " << endl; } return string(""); -} +} + /****************************************************************************** * RunwayList *****************************************************************************/ -RunwayList::RunwayList(const RunwayList &other) +RunwayList::RunwayList(const RunwayList & other) { - type = other.type; - stringVecConstIterator i; - for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++) - preferredRunways.push_back(*i); -} -RunwayList& RunwayList::operator= (const RunwayList &other) -{ - type = other.type; - preferredRunways.clear(); - stringVecConstIterator i; - for (i = other.preferredRunways.begin(); i != other.preferredRunways.end(); i++) - preferredRunways.push_back(*i); - return *this; -} -void RunwayList::set(const string &tp, const string &lst) -{ - //weekday = atoi(timeCopy.substr(0,1).c_str()); - // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday; - // timeCopy = timeCopy.substr(2,timeCopy.length()); - type = tp; - string rwys = lst; - string rwy; - while (rwys.find(",") != string::npos) - { - rwy = rwys.substr(0, rwys.find(",",0)); - //cerr << "adding runway [" << rwy << "] to the list " << endl; - preferredRunways.push_back(rwy); - rwys.erase(0, rwys.find(",",0)+1); // erase until after the first whitspace - while (rwys[0] == ' ') - rwys.erase(0, 1); // Erase any leading whitespaces. - //cerr << "Remaining runway list " << rwys; - } - preferredRunways.push_back(rwys); - //exit(1); + type = other.type; + stringVecConstIterator i; + for (i = other.preferredRunways.begin(); + i != other.preferredRunways.end(); i++) + preferredRunways.push_back(*i); } -void RunwayList::clear() +RunwayList & RunwayList::operator=(const RunwayList & other) { - type = ""; - preferredRunways.clear(); + type = other.type; + preferredRunways.clear(); + stringVecConstIterator i; + for (i = other.preferredRunways.begin(); + i != other.preferredRunways.end(); i++) + preferredRunways.push_back(*i); + return *this; } + +void RunwayList::set(const string & tp, const string & lst) +{ + //weekday = atoi(timeCopy.substr(0,1).c_str()); + // timeOffsetInDays = weekday - currTimeDate->getGmt()->tm_wday; + // timeCopy = timeCopy.substr(2,timeCopy.length()); + type = tp; + string rwys = lst; + string rwy; + while (rwys.find(",") != string::npos) { + rwy = rwys.substr(0, rwys.find(",", 0)); + //cerr << "adding runway [" << rwy << "] to the list " << endl; + preferredRunways.push_back(rwy); + rwys.erase(0, rwys.find(",", 0) + 1); // erase until after the first whitspace + while (rwys[0] == ' ') + rwys.erase(0, 1); // Erase any leading whitespaces. + //cerr << "Remaining runway list " << rwys; + } + preferredRunways.push_back(rwys); + //exit(1); +} + +void RunwayList::clear() +{ + type = ""; + preferredRunways.clear(); +} + /**************************************************************************** * ***************************************************************************/ -RunwayGroup::RunwayGroup(const RunwayGroup &other) +RunwayGroup::RunwayGroup(const RunwayGroup & other) { - name = other.name; - RunwayListVecConstIterator i; - for (i = other.rwyList.begin(); i != other.rwyList.end(); i++) - rwyList.push_back(*i); - choice[0] = other.choice[0]; - choice[1] = other.choice[1]; - nrActive = other.nrActive; -} -RunwayGroup& RunwayGroup:: operator= (const RunwayGroup &other) -{ - rwyList.clear(); - name = other.name; - RunwayListVecConstIterator i; - for (i = other.rwyList.begin(); i != other.rwyList.end(); i++) - rwyList.push_back(*i); - choice[0] = other.choice[0]; - choice[1] = other.choice[1]; - nrActive = other.nrActive; - return *this; + name = other.name; + RunwayListVecConstIterator i; + for (i = other.rwyList.begin(); i != other.rwyList.end(); i++) + rwyList.push_back(*i); + choice[0] = other.choice[0]; + choice[1] = other.choice[1]; + nrActive = other.nrActive; } -void RunwayGroup::setActive(const FGAirport* airport, - double windSpeed, - double windHeading, - double maxTail, - double maxCross, - stringVec *currentlyActive) +RunwayGroup & RunwayGroup::operator=(const RunwayGroup & other) +{ + rwyList.clear(); + name = other.name; + RunwayListVecConstIterator i; + for (i = other.rwyList.begin(); i != other.rwyList.end(); i++) + rwyList.push_back(*i); + choice[0] = other.choice[0]; + choice[1] = other.choice[1]; + nrActive = other.nrActive; + return *this; +} + +void RunwayGroup::setActive(const FGAirport * airport, + double windSpeed, + double windHeading, + double maxTail, + double maxCross, stringVec * currentlyActive) { - FGRunway* rwy; - int activeRwys = rwyList.size(); // get the number of runways active - int nrOfPreferences; - // bool found = true; - // double heading; - double hdgDiff; - double crossWind; - double tailWind; - string name; - //stringVec names; - int bestMatch = 0, bestChoice = 0; + FGRunway *rwy; + int activeRwys = rwyList.size(); // get the number of runways active + int nrOfPreferences; + // bool found = true; + // double heading; + double hdgDiff; + double crossWind; + double tailWind; + string name; + //stringVec names; + int bestMatch = 0, bestChoice = 0; - if (activeRwys > 0) - { - // Now downward iterate across all the possible preferences - // starting by the least preferred choice working toward the most preferred choice + if (activeRwys > 0) { + // Now downward iterate across all the possible preferences + // starting by the least preferred choice working toward the most preferred choice - nrOfPreferences = rwyList[0].getRwyList()->size(); - bool validSelection = true; - bool foundValidSelection = false; - for (int i = nrOfPreferences-1; i >= 0; i--) - { - int match = 0; - + nrOfPreferences = rwyList[0].getRwyList()->size(); + bool validSelection = true; + bool foundValidSelection = false; + for (int i = nrOfPreferences - 1; i >= 0; i--) { + int match = 0; - // Test each runway listed in the preference to see if it's possible to use - // If one runway of the selection isn't allowed, we need to exclude this - // preference, however, we don't want to stop right there, because we also - // don't want to randomly swap runway preferences, unless there is a need to. - // - validSelection = true; - for (int j = 0; j < activeRwys; j++) - { - string ident(rwyList[j].getRwyList(i)); - if (!airport->hasRunwayWithIdent(ident)) { - SG_LOG(SG_GENERAL, SG_WARN, "no such runway:" << ident << " at " << airport->ident()); - continue; - } - - rwy = airport->getRunwayByIdent(ident); - - //cerr << "Succes" << endl; - hdgDiff = fabs(windHeading - rwy->headingDeg()); - //cerr << "Wind Heading: " << windHeading << "Runway Heading: " < 180) - hdgDiff = 360 - hdgDiff; - //cerr << "Heading diff: " << hdgDiff << endl; - hdgDiff *= ((2*M_PI)/360.0); // convert to radians - crossWind = windSpeed * sin(hdgDiff); - tailWind = -windSpeed * cos(hdgDiff); - //cerr << ". Tailwind : " << tailWind; - //cerr << ". Crosswnd : " << crossWind; - if ((tailWind > maxTail) || (crossWind > maxCross)) - { - //cerr << ". [Invalid] " << endl; - validSelection = false; - } - else - { - //cerr << ". [Valid] "; - } - //cerr << endl; - } // of active runways iteration - - if (validSelection) - { - //cerr << "Valid selection : " << i << endl;; - foundValidSelection = true; - for (stringVecIterator it = currentlyActive->begin(); - it != currentlyActive->end(); it++) - { - if ((*it) == name) - match++; - } - if (match >= bestMatch) { - bestMatch = match; - bestChoice = i; - } - } - //cerr << "Preference " << i << " bestMatch " << bestMatch << " choice " << bestChoice << endl; - } - if (foundValidSelection) - { - //cerr << "Valid runay selection : " << bestChoice << endl; - nrActive = activeRwys; - active = bestChoice; - return; - } - // If this didn't work, due to heavy winds, try again - // but select only one landing and one takeoff runway. - choice[0] = 0; - choice[1] = 0; - for (int i = activeRwys-1; i; i--) - { - if (rwyList[i].getType() == string("landing")) - choice[0] = i; - if (rwyList[i].getType() == string("takeoff")) - choice[1] = i; - } - //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl; - nrOfPreferences = rwyList[0].getRwyList()->size(); - for (int i = 0; i < nrOfPreferences; i++) - { - bool validSelection = true; - for (int j = 0; j < 2; j++) - { - name = rwyList[choice[j]].getRwyList(i); - rwy = airport->getRunwayByIdent(name); - - //cerr << "Succes" << endl; - hdgDiff = fabs(windHeading - rwy->headingDeg()); - if (hdgDiff > 180) - hdgDiff = 360 - hdgDiff; - hdgDiff *= ((2*M_PI)/360.0); // convert to radians - crossWind = windSpeed * sin(hdgDiff); - tailWind = -windSpeed * cos(hdgDiff); - if ((tailWind > maxTail) || (crossWind > maxCross)) - validSelection = false; - - } - if (validSelection) - { - //cerr << "Valid runay selection : " << i << endl; - active = i; - nrActive = 2; - return; - } - } + // Test each runway listed in the preference to see if it's possible to use + // If one runway of the selection isn't allowed, we need to exclude this + // preference, however, we don't want to stop right there, because we also + // don't want to randomly swap runway preferences, unless there is a need to. + // + validSelection = true; + + for (int j = 0; j < activeRwys; j++) { + string ident(rwyList[j].getRwyList(i)); + if (!airport->hasRunwayWithIdent(ident)) { + SG_LOG(SG_GENERAL, SG_WARN, + "no such runway:" << ident << " at " << + airport->ident()); + continue; + } + + rwy = airport->getRunwayByIdent(ident); + + //cerr << "Succes" << endl; + hdgDiff = fabs(windHeading - rwy->headingDeg()); + name = rwy->name(); + + + if (hdgDiff > 180) + hdgDiff = 360 - hdgDiff; + //cerr << "Heading diff: " << hdgDiff << endl; + hdgDiff *= ((2 * M_PI) / 360.0); // convert to radians + crossWind = windSpeed * sin(hdgDiff); + tailWind = -windSpeed * cos(hdgDiff); + //cerr << "Runway : " << rwy->name() << ": " << rwy->headingDeg() << endl; + //cerr << ". Tailwind : " << tailWind; + //cerr << ". Crosswnd : " << crossWind; + if ((tailWind > maxTail) || (crossWind > maxCross)) { + //cerr << ". [Invalid] " << endl; + validSelection = false; + } else { + //cerr << ". [Valid] "; + } + //cerr << endl; + for (stringVecIterator it = currentlyActive->begin(); + it != currentlyActive->end(); it++) { + //cerr << "Checking : \"" << (*it) << "\". vs \"" << name << "\"" << endl; + if ((*it) == name) { + match++; + } + } + } // of active runways iteration + + if (validSelection) { + //cerr << "Valid selection : " << i << endl;; + foundValidSelection = true; + if (match >= bestMatch) { + bestMatch = match; + bestChoice = i; + } + } + //cerr << "Preference " << i << "Match " << match << " bestMatch " << bestMatch << " choice " << bestChoice << " valid selection " << validSelection << endl; + } + if (foundValidSelection) { + //cerr << "Valid runay selection : " << bestChoice << endl; + nrActive = activeRwys; + active = bestChoice; + return; + } + // If this didn't work, due to heavy winds, try again + // but select only one landing and one takeoff runway. + choice[0] = 0; + choice[1] = 0; + for (int i = activeRwys - 1; i; i--) { + if (rwyList[i].getType() == string("landing")) + choice[0] = i; + if (rwyList[i].getType() == string("takeoff")) + choice[1] = i; + } + //cerr << "Choosing " << choice[0] << " for landing and " << choice[1] << "for takeoff" << endl; + nrOfPreferences = rwyList[0].getRwyList()->size(); + for (int i = 0; i < nrOfPreferences; i++) { + bool validSelection = true; + for (int j = 0; j < 2; j++) { + name = rwyList[choice[j]].getRwyList(i); + rwy = airport->getRunwayByIdent(name); + + //cerr << "Succes" << endl; + hdgDiff = fabs(windHeading - rwy->headingDeg()); + if (hdgDiff > 180) + hdgDiff = 360 - hdgDiff; + hdgDiff *= ((2 * M_PI) / 360.0); // convert to radians + crossWind = windSpeed * sin(hdgDiff); + tailWind = -windSpeed * cos(hdgDiff); + if ((tailWind > maxTail) || (crossWind > maxCross)) + validSelection = false; + + + } + if (validSelection) { + //cerr << "Valid runay selection : " << i << endl; + active = i; + nrActive = 2; + return; + } + } } - active = -1; - nrActive = 0; + active = -1; + nrActive = 0; } -void RunwayGroup::getActive(int i, string &name, string &type) +void RunwayGroup::getActive(int i, string & name, string & type) { - if (i == -1) - { - return; + if (i == -1) { + return; } - if (nrActive == (int)rwyList.size()) - { - name = rwyList[i].getRwyList(active); - type = rwyList[i].getType(); - } - else - { - name = rwyList[choice[i]].getRwyList(active); - type = rwyList[choice[i]].getType(); + if (nrActive == (int) rwyList.size()) { + name = rwyList[i].getRwyList(active); + type = rwyList[i].getType(); + } else { + name = rwyList[choice[i]].getRwyList(active); + type = rwyList[choice[i]].getType(); } } + /***************************************************************************** * FGRunway preference ****************************************************************************/ -FGRunwayPreference::FGRunwayPreference(FGAirport* ap) : - _ap(ap) +FGRunwayPreference::FGRunwayPreference(FGAirport * ap): +_ap(ap) { - //cerr << "Running default Constructor" << endl; - initialized = false; + //cerr << "Running default Constructor" << endl; + initialized = false; } -FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference &other) +FGRunwayPreference::FGRunwayPreference(const FGRunwayPreference & other) { - initialized = other.initialized; + initialized = other.initialized; - comTimes = other.comTimes; // Commercial Traffic; - genTimes = other.genTimes; // General Aviation; - milTimes = other.milTimes; // Military Traffic; + comTimes = other.comTimes; // Commercial Traffic; + genTimes = other.genTimes; // General Aviation; + milTimes = other.milTimes; // Military Traffic; - PreferenceListConstIterator i; - for (i = other.preferences.begin(); i != other.preferences.end(); i++) - preferences.push_back(*i); + PreferenceListConstIterator i; + for (i = other.preferences.begin(); i != other.preferences.end(); i++) + preferences.push_back(*i); } - -FGRunwayPreference & FGRunwayPreference::operator= (const FGRunwayPreference &other) + +FGRunwayPreference & FGRunwayPreference::operator=(const FGRunwayPreference + & other) { - initialized = other.initialized; - - comTimes = other.comTimes; // Commercial Traffic; - genTimes = other.genTimes; // General Aviation; - milTimes = other.milTimes; // Military Traffic; - - PreferenceListConstIterator i; - preferences.clear(); - for (i = other.preferences.begin(); i != other.preferences.end(); i++) - preferences.push_back(*i); - return *this; + initialized = other.initialized; + + comTimes = other.comTimes; // Commercial Traffic; + genTimes = other.genTimes; // General Aviation; + milTimes = other.milTimes; // Military Traffic; + + PreferenceListConstIterator i; + preferences.clear(); + for (i = other.preferences.begin(); i != other.preferences.end(); i++) + preferences.push_back(*i); + return *this; } ScheduleTime *FGRunwayPreference::getSchedule(const char *trafficType) { - if (!(strcmp(trafficType, "com"))) { - return &comTimes; - } - if (!(strcmp(trafficType, "gen"))) { - return &genTimes; - } - if (!(strcmp(trafficType, "mil"))) { - return &milTimes; - } - return 0; + if (!(strcmp(trafficType, "com"))) { + return &comTimes; + } + if (!(strcmp(trafficType, "gen"))) { + return &genTimes; + } + if (!(strcmp(trafficType, "mil"))) { + return &milTimes; + } + return 0; } -RunwayGroup *FGRunwayPreference::getGroup(const string &groupName) +RunwayGroup *FGRunwayPreference::getGroup(const string & groupName) { - PreferenceListIterator i = preferences.begin(); - if (preferences.begin() == preferences.end()) - return 0; - while (!(i == preferences.end() || i->getName() == groupName)) - i++; - if (i != preferences.end()) - return &(*i); - else - return 0; + PreferenceListIterator i = preferences.begin(); + if (preferences.begin() == preferences.end()) + return 0; + while (!(i == preferences.end() || i->getName() == groupName)) + i++; + if (i != preferences.end()) + return &(*i); + else + return 0; } - string FGRunwayPreference::getId() { - return _ap->getId(); - }; +string FGRunwayPreference::getId() +{ + return _ap->getId(); +}; diff --git a/src/Traffic/Schedule.cxx b/src/Traffic/Schedule.cxx index 8c0030de1..65e8a2403 100644 --- a/src/Traffic/Schedule.cxx +++ b/src/Traffic/Schedule.cxx @@ -289,7 +289,6 @@ bool FGAISchedule::update(time_t now, const SGVec3d& userCart) if (distanceToUser >= TRAFFICTOAIDISTTOSTART) { return true; // out of visual range, for the moment. } - return createAIAircraft(flight, speed, deptime); } diff --git a/src/Traffic/TrafficMgr.cxx b/src/Traffic/TrafficMgr.cxx index dc2444fba..7c13f0db6 100644 --- a/src/Traffic/TrafficMgr.cxx +++ b/src/Traffic/TrafficMgr.cxx @@ -137,7 +137,9 @@ void FGTrafficManager::init() void FGTrafficManager::update(double /*dt*/) { - + if (fgGetBool("/environment/metar/valid") == false) { + return; + } time_t now = time(NULL) + fgGetLong("/sim/time/warp"); if (scheduledAircraft.size() == 0) { return;