1 /******************************************************************************
2 * AIFlightPlanCreate.cxx
3 * Written by Durk Talsma, started May, 2004.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 **************************************************************************/
25 #include "AIFlightPlan.hxx"
26 #include <simgear/math/sg_geodesy.hxx>
27 #include <Airports/runways.hxx>
28 #include <Airports/dynamics.hxx>
29 #include <AIAircraft.hxx>
30 #include <performancedata.hxx>
32 #include <Environment/environment_mgr.hxx>
33 #include <Environment/environment.hxx>
36 /* FGAIFlightPlan::create()
37 * dynamically create a flight plan for AI traffic, based on data provided by the
38 * Traffic Manager, when reading a filed flightplan failes. (DT, 2004/07/10)
40 * This is the top-level function, and the only one that is publicly available.
45 // Check lat/lon values during initialization;
46 void FGAIFlightPlan::create(FGAIAircraft *ac, FGAirport *dep, FGAirport *arr, int legNr,
47 double alt, double speed, double latitude,
48 double longitude, bool firstFlight,double radius,
49 const string& fltType, const string& aircraftType,
50 const string& airline)
52 int currWpt = wpt_iterator - waypoints.begin();
56 createPushBack(firstFlight,dep, latitude, longitude,
57 radius, fltType, aircraftType, airline);
60 createTakeoffTaxi(firstFlight, dep, radius, fltType, aircraftType, airline);
63 createTakeOff(ac, firstFlight, dep, speed, fltType);
66 createClimb(firstFlight, dep, speed, alt, fltType);
69 createCruise(firstFlight, dep,arr, latitude, longitude, speed, alt, fltType);
72 createDecent(arr, fltType);
78 createLandingTaxi(arr, radius, fltType, aircraftType, airline);
81 createParking(arr, radius);
85 SG_LOG(SG_INPUT, SG_ALERT, "AIFlightPlan::create() attempting to create unknown leg"
86 " this is probably an internal program error");
88 wpt_iterator = waypoints.begin()+currWpt;
92 FGAIFlightPlan::waypoint*
93 FGAIFlightPlan::createOnGround(const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed)
95 waypoint* wpt = new waypoint;
97 wpt->longitude = aPos.getLongitudeDeg();
98 wpt->latitude = aPos.getLatitudeDeg();
99 wpt->altitude = aElev;
101 wpt->crossat = -10000;
102 wpt->gear_down = true;
103 wpt->flaps_down= true;
104 wpt->finished = false;
105 wpt->on_ground = true;
110 FGAIFlightPlan::waypoint*
111 FGAIFlightPlan::createInAir(const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed)
113 waypoint* wpt = new waypoint;
115 wpt->longitude = aPos.getLongitudeDeg();
116 wpt->latitude = aPos.getLatitudeDeg();
117 wpt->altitude = aElev;
119 wpt->crossat = -10000;
120 wpt->gear_down = false;
121 wpt->flaps_down= false;
122 wpt->finished = false;
123 wpt->on_ground = false;
128 FGAIFlightPlan::waypoint*
129 FGAIFlightPlan::cloneWithPos(waypoint* aWpt, const std::string& aName, const SGGeod& aPos)
131 waypoint* wpt = new waypoint;
133 wpt->longitude = aPos.getLongitudeDeg();
134 wpt->latitude = aPos.getLatitudeDeg();
136 wpt->altitude = aWpt->altitude;
137 wpt->speed = aWpt->speed;
138 wpt->crossat = aWpt->crossat;
139 wpt->gear_down = aWpt->gear_down;
140 wpt->flaps_down= aWpt->flaps_down;
141 wpt->finished = aWpt->finished;
142 wpt->on_ground = aWpt->on_ground;
148 void FGAIFlightPlan::createDefaultTakeoffTaxi(FGAirport* aAirport, FGRunway* aRunway)
150 SGGeod runwayTakeoff = aRunway->pointOnCenterline(5.0);
151 double airportElev = aAirport->getElevation();
154 wpt = createOnGround("Airport Center", aAirport->geod(), airportElev, 15);
155 waypoints.push_back(wpt);
156 wpt = createOnGround("Runway Takeoff", runwayTakeoff, airportElev, 15);
157 waypoints.push_back(wpt);
160 void FGAIFlightPlan::createTakeoffTaxi(bool firstFlight,
162 double radius, const string& fltType,
163 const string& acType, const string& airline)
165 double heading, lat, lon;
167 // If this function is called during initialization,
168 // make sure we obtain a valid gate ID first
169 // and place the model at the location of the gate.
171 if (!(apt->getDynamics()->getAvailableParking(&lat, &lon,
176 SG_LOG(SG_INPUT, SG_WARN, "Could not find parking for a " <<
178 " of flight type " << fltType <<
179 " of airline " << airline <<
180 " at airport " << apt->getId());
184 string rwyClass = getRunwayClassFromTrafficType(fltType);
185 apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
186 rwy = apt->getRunwayByIdent(activeRunway);
187 SGGeod runwayTakeoff = rwy->pointOnCenterline(5.0);
189 FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork();
191 createDefaultTakeoffTaxi(apt, rwy);
196 int runwayId = gn->findNearestNode(runwayTakeoff);
198 // A negative gateId indicates an overflow parking, use a
199 // fallback mechanism for this.
200 // Starting from gate 0 in this case is a bit of a hack
201 // which requires a more proper solution later on.
203 taxiRoute = new FGTaxiRoute;
205 // Determine which node to start from.
207 // Find out which node to start from
208 FGParking *park = apt->getDynamics()->getParking(gateId);
210 node = park->getPushBackPoint();
217 // HAndle case where parking doens't have a node
218 if ((node == 0) && park) {
222 node = lastNodeVisited;
226 *taxiRoute = gn->findShortestRoute(node, runwayId);
229 if (taxiRoute->empty()) {
230 createDefaultTakeoffTaxi(apt, rwy);
235 //bool isPushBackPoint = false;
237 // If this is called during initialization, randomly
238 // skip a number of waypoints to get a more realistic
240 int nrWaypointsToSkip = rand() % taxiRoute->size();
241 // but make sure we always keep two active waypoints
242 // to prevent a segmentation fault
243 for (int i = 0; i < nrWaypointsToSkip-2; i++) {
244 taxiRoute->next(&node);
246 apt->getDynamics()->releaseParking(gateId);
248 if (taxiRoute->size() > 1) {
249 taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route
253 // push each node on the taxi route as a waypoint
255 while(taxiRoute->next(&node, &route)) {
257 snprintf (buffer, 10, "%d", node);
258 FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findNode(node);
259 waypoint* wpt = createOnGround(buffer, tn->geod(), apt->getElevation(), 15);
260 wpt->routeIndex = route;
261 waypoints.push_back(wpt);
265 void FGAIFlightPlan::createDefaultLandingTaxi(FGAirport* aAirport)
268 SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude);
269 double airportElev = aAirport->getElevation();
272 wpt = createOnGround("Runway Exit", lastWptPos, airportElev, 15);
273 waypoints.push_back(wpt);
274 wpt = createOnGround("Airport Center", aAirport->geod(), airportElev, 15);
275 waypoints.push_back(wpt);
277 double heading, lat, lon;
278 aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading);
279 wpt = createOnGround("END", SGGeod::fromDeg(lon, lat), airportElev, 15);
280 waypoints.push_back(wpt);
283 void FGAIFlightPlan::createLandingTaxi(FGAirport *apt,
284 double radius, const string& fltType,
285 const string& acType, const string& airline)
287 double heading, lat, lon;
288 apt->getDynamics()->getAvailableParking(&lat, &lon, &heading,
289 &gateId, radius, fltType, acType, airline);
292 SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude);
293 FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork();
295 // Find a route from runway end to parking/gate.
297 createDefaultLandingTaxi(apt);
302 int runwayId = gn->findNearestNode(lastWptPos);
303 // A negative gateId indicates an overflow parking, use a
304 // fallback mechanism for this.
305 // Starting from gate 0 is a bit of a hack...
308 taxiRoute = new FGTaxiRoute;
310 *taxiRoute = gn->findShortestRoute(runwayId, gateId);
312 *taxiRoute = gn->findShortestRoute(runwayId, 0);
315 if (taxiRoute->empty()) {
316 createDefaultLandingTaxi(apt);
322 int size = taxiRoute->size();
323 // Omit the last two waypoints, as
324 // those are created by createParking()
326 for (int i = 0; i < size-2; i++) {
327 taxiRoute->next(&node, &route);
329 snprintf (buffer, 10, "%d", node);
330 FGTaxiNode *tn = gn->findNode(node);
331 waypoint* wpt = createOnGround(buffer, tn->geod(), apt->getElevation(), 15);
332 wpt->routeIndex = route;
333 waypoints.push_back(wpt);
337 /*******************************************************************
339 * initialize the Aircraft at the parking location
340 ******************************************************************/
341 void FGAIFlightPlan::createTakeOff(FGAIAircraft *ac, bool firstFlight, FGAirport *apt, double speed, const string &fltType)
343 double accel = ac->getPerformance()->acceleration();
344 double vRotate = ac->getPerformance()->vRotate();
345 // Acceleration = dV / dT
346 // Acceleration X dT = dV
347 // dT = dT / Acceleration
348 //d = (Vf^2 - Vo^2) / (2*a)
349 double accelTime = (vRotate - 15) / accel;
350 cerr << "Using " << accelTime << " as total acceleration time" << endl;
351 double accelDistance = (vRotate*vRotate - 15*15) / (2*accel);
352 cerr << "Using " << accelDistance << " " << accel << " " << vRotate << endl;
354 // Get the current active runway, based on code from David Luff
355 // This should actually be unified and extended to include
356 // Preferential runway use schema's
357 // NOTE: DT (2009-01-18: IIRC, this is currently already the case,
358 // because the getActive runway function takes care of that.
361 string rwyClass = getRunwayClassFromTrafficType(fltType);
362 apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
363 rwy = apt->getRunwayByIdent(activeRunway);
366 double airportElev = apt->getElevation();
367 // Acceleration point, 105 meters into the runway,
368 SGGeod accelPoint = rwy->pointOnCenterline(105.0);
369 wpt = createOnGround("accel", accelPoint, airportElev, speed);
370 waypoints.push_back(wpt);
372 //Start Climbing to 3000 ft. Let's do this
373 // at the center of the runway for now:
374 SGGeod rotate = rwy->pointOnCenterline(105.0+accelDistance);
375 wpt = cloneWithPos(wpt, "SOC", rotate);
376 wpt->altitude = airportElev+1000;
377 wpt->on_ground = false;
378 waypoints.push_back(wpt);
380 wpt = cloneWithPos(wpt, "3000 ft", rwy->end());
381 wpt->altitude = airportElev+3000;
382 waypoints.push_back(wpt);
384 // Finally, add two more waypoints, so that aircraft will remain under
385 // Tower control until they have reached the 3000 ft climb point
386 SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5);
387 wpt = cloneWithPos(wpt, "5000 ft", pt);
388 wpt->altitude = airportElev+5000;
389 waypoints.push_back(wpt);
392 /*******************************************************************
394 * initialize the Aircraft at the parking location
395 ******************************************************************/
396 void FGAIFlightPlan::createClimb(bool firstFlight, FGAirport *apt, double speed, double alt, const string &fltType)
401 string rwyClass = getRunwayClassFromTrafficType(fltType);
402 apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
403 rwy = apt->getRunwayByIdent(activeRunway);
406 SGGeod climb1 = rwy->pointOnCenterline(10*SG_NM_TO_METER);
407 wpt = createInAir("10000ft climb", climb1, speed, 10000);
408 wpt->gear_down = true;
409 wpt->flaps_down= true;
410 waypoints.push_back(wpt);
412 SGGeod climb2 = rwy->pointOnCenterline(20*SG_NM_TO_METER);
413 wpt = cloneWithPos(wpt, "18000ft climb", climb2);
414 wpt->altitude = 18000;
415 waypoints.push_back(wpt);
419 /*******************************************************************
421 * initialize the Aircraft at the parking location
422 ******************************************************************/
423 void FGAIFlightPlan::createDecent(FGAirport *apt, const string &fltType)
425 // Ten thousand ft. Slowing down to 240 kts
428 //Beginning of Decent
430 // allow "mil" and "gen" as well
431 string rwyClass = getRunwayClassFromTrafficType(fltType);
432 apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway);
433 rwy = apt->getRunwayByIdent(activeRunway);
435 SGGeod descent1 = rwy->pointOnCenterline(-100000); // 100km out
436 wpt = createInAir("Dec 10000ft", descent1, apt->getElevation(), 240);
437 wpt->crossat = 10000;
438 waypoints.push_back(wpt);
440 // Three thousand ft. Slowing down to 160 kts
441 SGGeod descent2 = rwy->pointOnCenterline(-8*SG_NM_TO_METER); // 8nm out
442 wpt = createInAir("DEC 3000ft", descent2, apt->getElevation(), 160);
444 wpt->gear_down = true;
445 wpt->flaps_down= true;
446 waypoints.push_back(wpt);
448 /*******************************************************************
450 * initialize the Aircraft at the parking location
451 ******************************************************************/
452 void FGAIFlightPlan::createLanding(FGAirport *apt)
454 // Ten thousand ft. Slowing down to 150 kts
456 double aptElev = apt->getElevation();
458 wpt = createOnGround("Threshold", rwy->threshold(), aptElev, 150);
459 wpt->crossat = apt->getElevation();
460 waypoints.push_back(wpt);
463 wpt = createOnGround("Center", rwy->geod(), aptElev, 30);
464 waypoints.push_back(wpt);
466 SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9);
467 wpt = createOnGround("Roll Out", rollOut, aptElev, 15);
468 wpt->crossat = apt->getElevation();
469 waypoints.push_back(wpt);
472 /*******************************************************************
474 * initialize the Aircraft at the parking location
475 ******************************************************************/
476 void FGAIFlightPlan::createParking(FGAirport *apt, double radius)
479 double aptElev = apt->getElevation();
484 apt->getDynamics()->getParking(gateId, &lat, &lon, &heading);
488 geo_direct_wgs_84 ( 0, lat, lon, heading,
490 &lat2, &lon2, &az2 );
491 wpt = createOnGround("taxiStart", SGGeod::fromDeg(lon2, lat2), aptElev, 10);
492 waypoints.push_back(wpt);
494 geo_direct_wgs_84 ( 0, lat, lon, heading,
496 &lat2, &lon2, &az2 );
498 wpt = createOnGround("taxiStart2", SGGeod::fromDeg(lon2, lat2), aptElev, 10);
499 waypoints.push_back(wpt);
501 wpt = createOnGround("END", SGGeod::fromDeg(lon, lat), aptElev, 10);
502 waypoints.push_back(wpt);
507 * @param fltType a string describing the type of
508 * traffic, normally used for gate assignments
509 * @return a converted string that gives the runway
510 * preference schedule to be used at aircraft having
511 * a preferential runway schedule implemented (i.e.
512 * having a rwyprefs.xml file
514 * Currently valid traffic types for gate assignment:
515 * - gate (commercial gate)
516 * - cargo (commercial gargo),
517 * - ga (general aviation) ,
519 * - mil-fighter (military - fighter),
520 * - mil-transport (military - transport)
522 * Valid runway classes:
523 * - com (commercial traffic: jetliners, passenger and cargo)
524 * - gen (general aviation)
525 * - ul (ultralight: I can imagine that these may share a runway with ga on some airports)
526 * - mil (all military traffic)
528 string FGAIFlightPlan::getRunwayClassFromTrafficType(string fltType)
530 if ((fltType == "gate") || (fltType == "cargo")) {
531 return string("com");
533 if (fltType == "ga") {
534 return string ("gen");
536 if (fltType == "ul") {
539 if ((fltType == "mil-fighter") || (fltType == "mil-transport")) {
540 return string("mil");
542 return string("com");