]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AIFlightPlanCreate.cxx
Merge branch 'jt/runway' into next
[flightgear.git] / src / AIModel / AIFlightPlanCreate.cxx
1 /******************************************************************************
2  * AIFlightPlanCreate.cxx
3  * Written by Durk Talsma, started May, 2004.
4  *
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.
9  *
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.
14  *
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.
18  *
19  **************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include "AIFlightPlan.hxx"
26 #include <simgear/math/sg_geodesy.hxx>
27 #include <Airports/runways.hxx>
28 #include <Airports/dynamics.hxx>
29
30 #include <Environment/environment_mgr.hxx>
31 #include <Environment/environment.hxx>
32
33
34 /* FGAIFlightPlan::create()
35  * dynamically create a flight plan for AI traffic, based on data provided by the
36  * Traffic Manager, when reading a filed flightplan failes. (DT, 2004/07/10) 
37  *
38  * This is the top-level function, and the only one that is publicly available.
39  *
40  */ 
41
42
43 // Check lat/lon values during initialization;
44 void FGAIFlightPlan::create(FGAirport *dep, FGAirport *arr, int legNr, 
45                             double alt, double speed, double latitude, 
46                             double longitude, bool firstFlight,double radius, 
47                             const string& fltType, const string& aircraftType, 
48                             const string& airline)
49
50   int currWpt = wpt_iterator - waypoints.begin();
51   switch(legNr)
52     {
53       case 1:
54       createPushBack(firstFlight,dep, latitude, longitude, 
55                      radius, fltType, aircraftType, airline);
56       break;
57     case 2: 
58       createTakeoffTaxi(firstFlight, dep, radius, fltType, aircraftType, airline);
59       break;
60     case 3: 
61       createTakeOff(firstFlight, dep, speed, fltType);
62       break;
63     case 4: 
64       createClimb(firstFlight, dep, speed, alt, fltType);
65       break;
66     case 5: 
67       createCruise(firstFlight, dep,arr, latitude, longitude, speed, alt, fltType);
68       break;
69     case 6: 
70       createDecent(arr, fltType);
71       break;
72     case 7: 
73       createLanding(arr);
74       break;
75     case 8: 
76       createLandingTaxi(arr, radius, fltType, aircraftType, airline);
77       break;
78       case 9: 
79         createParking(arr, radius);
80       break;
81     default:
82       //exit(1);
83       SG_LOG(SG_INPUT, SG_ALERT, "AIFlightPlan::create() attempting to create unknown leg"
84              " this is probably an internal program error");
85     }
86   wpt_iterator = waypoints.begin()+currWpt;
87   leg++;
88 }
89
90 FGAIFlightPlan::waypoint*
91 FGAIFlightPlan::createOnGround(const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed)
92 {
93   waypoint* wpt = new waypoint;
94   wpt->name = aName;
95   wpt->longitude = aPos.getLongitudeDeg();
96   wpt->latitude = aPos.getLatitudeDeg();
97   wpt->altitude  = aElev;
98   wpt->speed     = aSpeed; 
99   wpt->crossat   = -10000;
100   wpt->gear_down = true;
101   wpt->flaps_down= true;
102   wpt->finished  = false;
103   wpt->on_ground = true;
104   wpt->routeIndex= 0;
105   return wpt;
106 }
107
108 FGAIFlightPlan::waypoint*
109 FGAIFlightPlan::createInAir(const std::string& aName, const SGGeod& aPos, double aElev, double aSpeed)
110 {
111   waypoint* wpt = new waypoint;
112   wpt->name = aName;
113   wpt->longitude = aPos.getLongitudeDeg();
114   wpt->latitude = aPos.getLatitudeDeg();
115   wpt->altitude  = aElev;
116   wpt->speed     = aSpeed; 
117   wpt->crossat   = -10000;
118   wpt->gear_down = false;
119   wpt->flaps_down= false;
120   wpt->finished  = false;
121   wpt->on_ground = false;
122   wpt->routeIndex= 0;
123   return wpt;
124 }
125
126 FGAIFlightPlan::waypoint*
127 FGAIFlightPlan::cloneWithPos(waypoint* aWpt, const std::string& aName, const SGGeod& aPos)
128 {
129   waypoint* wpt = new waypoint;
130   wpt->name = aName;
131   wpt->longitude = aPos.getLongitudeDeg();
132   wpt->latitude = aPos.getLatitudeDeg();
133   
134   wpt->altitude  = aWpt->altitude;
135   wpt->speed     = aWpt->speed; 
136   wpt->crossat   = aWpt->crossat;
137   wpt->gear_down = aWpt->gear_down;
138   wpt->flaps_down= aWpt->flaps_down;
139   wpt->finished  = aWpt->finished;
140   wpt->on_ground = aWpt->on_ground;
141   wpt->routeIndex = 0;
142   
143   return wpt;
144 }
145
146 void FGAIFlightPlan::createDefaultTakeoffTaxi(FGAirport* aAirport, FGRunway* aRunway)
147 {
148   SGGeod runwayTakeoff = aRunway->pointOnCenterline(5.0);
149   double airportElev = aAirport->getElevation();
150   
151   waypoint* wpt;
152   wpt = createOnGround("Airport Center", aAirport->geod(), airportElev, 15);
153   waypoints.push_back(wpt);
154   wpt = createOnGround("Runway Takeoff", runwayTakeoff, airportElev, 15);
155   waypoints.push_back(wpt);     
156 }
157
158 void FGAIFlightPlan::createTakeoffTaxi(bool firstFlight, 
159                                 FGAirport *apt,
160                                 double radius, const string& fltType, 
161                                 const string& acType, const string& airline)
162 {
163   double heading, lat, lon;
164   
165   // If this function is called during initialization,
166   // make sure we obtain a valid gate ID first
167   // and place the model at the location of the gate.
168   if (firstFlight) {
169     if (!(apt->getDynamics()->getAvailableParking(&lat, &lon, 
170               &heading, &gateId, 
171               radius, fltType, 
172               acType, airline)))
173     {
174       SG_LOG(SG_INPUT, SG_WARN, "Could not find parking for a " << 
175        acType <<
176        " of flight type " << fltType <<
177        " of airline     " << airline <<
178        " at airport     " << apt->getId());
179     }
180   }
181   
182   string rwyClass = getRunwayClassFromTrafficType(fltType);
183   apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
184   rwy = apt->getRunwayByIdent(activeRunway);
185   SGGeod runwayTakeoff = rwy->pointOnCenterline(5.0);
186
187   FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork();
188   if (!gn->exists()) {
189     createDefaultTakeoffTaxi(apt, rwy);
190     return;
191   }
192   
193   intVec ids;
194   int runwayId = gn->findNearestNode(runwayTakeoff);
195
196   // A negative gateId indicates an overflow parking, use a
197   // fallback mechanism for this. 
198   // Starting from gate 0 in this case is a bit of a hack
199   // which requires a more proper solution later on.
200   delete taxiRoute;
201   taxiRoute = new FGTaxiRoute;
202
203   // Determine which node to start from.
204   int node = 0;
205   // Find out which node to start from
206   FGParking *park = apt->getDynamics()->getParking(gateId);
207   if (park) {
208     node = park->getPushBackPoint();
209   }
210   
211   if (node == -1) {
212     node = gateId;
213   }
214   
215   // HAndle case where parking doens't have a node
216   if ((node == 0) && park) {
217     if (firstFlight) {
218       node = gateId;
219     } else {
220       node = lastNodeVisited;
221     }
222   }
223
224   *taxiRoute = gn->findShortestRoute(node, runwayId);
225   intVecIterator i;
226          
227   if (taxiRoute->empty()) {
228     createDefaultTakeoffTaxi(apt, rwy);
229     return;
230   }
231   
232   taxiRoute->first();
233   //bool isPushBackPoint = false;
234   if (firstFlight) {
235     // If this is called during initialization, randomly
236     // skip a number of waypoints to get a more realistic
237     // taxi situation.
238     int nrWaypointsToSkip = rand() % taxiRoute->size();
239     // but make sure we always keep two active waypoints
240     // to prevent a segmentation fault
241     for (int i = 0; i < nrWaypointsToSkip-2; i++) {
242       taxiRoute->next(&node);
243     }
244     apt->getDynamics()->releaseParking(gateId);
245   } else {
246     if (taxiRoute->size() > 1) {
247       taxiRoute->next(&node); // chop off the first waypoint, because that is already the last of the pushback route
248     }
249   }
250   
251   // push each node on the taxi route as a waypoint
252   int route;
253   while(taxiRoute->next(&node, &route)) {
254                 char buffer[10];
255                 snprintf (buffer, 10, "%d", node);
256                 FGTaxiNode *tn = apt->getDynamics()->getGroundNetwork()->findNode(node);
257     waypoint* wpt = createOnGround(buffer, tn->geod(), apt->getElevation(), 15);
258     wpt->routeIndex = route;
259                 waypoints.push_back(wpt);
260   }
261 }
262
263 void FGAIFlightPlan::createDefaultLandingTaxi(FGAirport* aAirport)
264 {
265   SGGeod lastWptPos = 
266     SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude);
267   double airportElev = aAirport->getElevation();
268   
269   waypoint* wpt;
270   wpt = createOnGround("Runway Exit", lastWptPos, airportElev, 15);
271   waypoints.push_back(wpt);     
272   wpt = createOnGround("Airport Center", aAirport->geod(), airportElev, 15);
273   waypoints.push_back(wpt);
274   
275   double heading, lat, lon;
276   aAirport->getDynamics()->getParking(gateId, &lat, &lon, &heading);
277   wpt = createOnGround("END", SGGeod::fromDeg(lon, lat), airportElev, 15);
278   waypoints.push_back(wpt);
279 }
280
281 void FGAIFlightPlan::createLandingTaxi(FGAirport *apt,
282                                 double radius, const string& fltType, 
283                                 const string& acType, const string& airline)
284 {
285   double heading, lat, lon;
286   apt->getDynamics()->getAvailableParking(&lat, &lon, &heading, 
287         &gateId, radius, fltType, acType, airline);
288   
289   SGGeod lastWptPos = 
290     SGGeod::fromDeg(waypoints.back()->longitude, waypoints.back()->latitude);
291   FGGroundNetwork* gn = apt->getDynamics()->getGroundNetwork();
292   
293    // Find a route from runway end to parking/gate.
294   if (!gn->exists()) {
295     createDefaultLandingTaxi(apt);
296     return;
297   }
298   
299         intVec ids;
300   int runwayId = gn->findNearestNode(lastWptPos);
301   // A negative gateId indicates an overflow parking, use a
302   // fallback mechanism for this. 
303   // Starting from gate 0 is a bit of a hack...
304   //FGTaxiRoute route;
305   delete taxiRoute;
306   taxiRoute = new FGTaxiRoute;
307   if (gateId >= 0)
308     *taxiRoute = gn->findShortestRoute(runwayId, gateId);
309   else
310     *taxiRoute = gn->findShortestRoute(runwayId, 0);
311   intVecIterator i;
312   
313   if (taxiRoute->empty()) {
314     createDefaultLandingTaxi(apt);
315     return;
316   }
317   
318   int node;
319   taxiRoute->first();
320   int size = taxiRoute->size();
321   // Omit the last two waypoints, as 
322   // those are created by createParking()
323   int route;
324   for (int i = 0; i < size-2; i++) {
325     taxiRoute->next(&node, &route);
326     char buffer[10];
327     snprintf (buffer, 10, "%d", node);
328     FGTaxiNode *tn = gn->findNode(node);
329     waypoint* wpt = createOnGround(buffer, tn->geod(), apt->getElevation(), 15);
330     wpt->routeIndex = route;
331     waypoints.push_back(wpt);
332   }
333 }
334
335 /*******************************************************************
336  * CreateTakeOff 
337  * initialize the Aircraft at the parking location
338  ******************************************************************/
339 void FGAIFlightPlan::createTakeOff(bool firstFlight, FGAirport *apt, double speed, const string &fltType)
340 {
341   waypoint *wpt;
342   
343   // Get the current active runway, based on code from David Luff
344   // This should actually be unified and extended to include 
345   // Preferential runway use schema's 
346   if (firstFlight)
347  {
348    string rwyClass = getRunwayClassFromTrafficType(fltType);
349    apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
350    rwy = apt->getRunwayByIdent(activeRunway);
351  }
352   
353  double airportElev = apt->getElevation();
354  // Acceleration point, 105 meters into the runway,
355  SGGeod accelPoint = rwy->pointOnCenterline(105.0);
356  wpt = createOnGround("accel", accelPoint, airportElev, speed);
357  waypoints.push_back(wpt); 
358  
359  //Start Climbing to 3000 ft. Let's do this 
360  // at the center of the runway for now:
361  wpt = cloneWithPos(wpt, "SOC", rwy->geod());
362  wpt->altitude  = airportElev+1000;
363  wpt->on_ground = false;
364  waypoints.push_back(wpt);
365
366  wpt = cloneWithPos(wpt, "3000 ft", rwy->reverseThreshold());
367  wpt->altitude  = airportElev+3000;
368  waypoints.push_back(wpt);
369
370 // Finally, add two more waypoints, so that aircraft will remain under
371  // Tower control until they have reached the 3000 ft climb point
372  SGGeod pt = rwy->pointOnCenterline(5000 + rwy->lengthM() * 0.5);
373  wpt = cloneWithPos(wpt, "5000 ft", pt);
374  wpt->altitude  = airportElev+5000;
375   waypoints.push_back(wpt);  
376 }
377   
378 /*******************************************************************
379  * CreateClimb
380  * initialize the Aircraft at the parking location
381  ******************************************************************/
382 void FGAIFlightPlan::createClimb(bool firstFlight, FGAirport *apt, double speed, double alt, const string &fltType)
383 {
384   waypoint *wpt;
385
386   if (firstFlight) {
387     string rwyClass = getRunwayClassFromTrafficType(fltType);
388     apt->getDynamics()->getActiveRunway(rwyClass, 1, activeRunway);
389     rwy = apt->getRunwayByIdent(activeRunway);
390   }
391   
392   SGGeod climb1 = rwy->pointOnCenterline(10*SG_NM_TO_METER);
393   wpt = createInAir("10000ft climb", climb1, speed, 10000);
394   wpt->gear_down = true;
395   wpt->flaps_down= true;
396   waypoints.push_back(wpt); 
397
398   SGGeod climb2 = rwy->pointOnCenterline(20*SG_NM_TO_METER);
399   wpt = cloneWithPos(wpt, "18000ft climb", climb2);
400   wpt->altitude  = 18000;
401   waypoints.push_back(wpt); 
402 }
403
404
405 /*******************************************************************
406  * CreateDecent
407  * initialize the Aircraft at the parking location
408  ******************************************************************/
409 void FGAIFlightPlan::createDecent(FGAirport *apt, const string &fltType)
410 {
411   // Ten thousand ft. Slowing down to 240 kts
412   waypoint *wpt;
413
414   //Beginning of Decent
415   //string name;
416   // allow "mil" and "gen" as well
417   string rwyClass = getRunwayClassFromTrafficType(fltType);
418   apt->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway);
419   rwy = apt->getRunwayByIdent(activeRunway);
420      
421   SGGeod descent1 = rwy->pointOnCenterline(-100000); // 100km out
422   wpt = createInAir("Dec 10000ft", descent1, apt->getElevation(), 240);
423   wpt->crossat   = 10000;
424   waypoints.push_back(wpt);  
425   
426   // Three thousand ft. Slowing down to 160 kts
427   SGGeod descent2 = rwy->pointOnCenterline(-8*SG_NM_TO_METER); // 8nm out
428   wpt = createInAir("DEC 3000ft", descent2, apt->getElevation(), 160);
429   wpt->crossat   = 3000;
430   wpt->gear_down = true;
431   wpt->flaps_down= true;
432   waypoints.push_back(wpt);
433 }
434 /*******************************************************************
435  * CreateLanding
436  * initialize the Aircraft at the parking location
437  ******************************************************************/
438 void FGAIFlightPlan::createLanding(FGAirport *apt)
439 {
440   // Ten thousand ft. Slowing down to 150 kts
441   waypoint *wpt;
442   double aptElev = apt->getElevation();
443   //Runway Threshold
444   wpt = createOnGround("Threshold", rwy->displacedThreshold(), aptElev, 150);
445   wpt->crossat = apt->getElevation();
446   waypoints.push_back(wpt); 
447
448  // Roll-out
449   wpt = createOnGround("Center", rwy->geod(), aptElev, 30);
450   waypoints.push_back(wpt);
451
452   SGGeod rollOut = rwy->pointOnCenterline(rwy->lengthM() * 0.9);
453   wpt = createOnGround("Roll Out", rollOut, aptElev, 15);
454   wpt->crossat   = apt->getElevation();
455   waypoints.push_back(wpt); 
456 }
457
458 /*******************************************************************
459  * CreateParking
460  * initialize the Aircraft at the parking location
461  ******************************************************************/
462 void FGAIFlightPlan::createParking(FGAirport *apt, double radius)
463 {
464   waypoint* wpt;
465   double aptElev = apt->getElevation();
466   double lat, lat2;
467   double lon, lon2;
468   double az2;
469   double heading;
470   apt->getDynamics()->getParking(gateId, &lat, &lon, &heading);
471   heading += 180.0;
472   if (heading > 360)
473     heading -= 360; 
474   geo_direct_wgs_84 ( 0, lat, lon, heading, 
475                       2.2*radius,           
476                       &lat2, &lon2, &az2 );
477   wpt = createOnGround("taxiStart", SGGeod::fromDeg(lon2, lat2), aptElev, 10);
478   waypoints.push_back(wpt);
479   
480   geo_direct_wgs_84 ( 0, lat, lon, heading, 
481                       0.1 *radius,           
482                       &lat2, &lon2, &az2 );
483           
484   wpt = createOnGround("taxiStart2", SGGeod::fromDeg(lon2, lat2), aptElev, 10);
485   waypoints.push_back(wpt);   
486
487   wpt = createOnGround("END", SGGeod::fromDeg(lon, lat), aptElev, 10);
488   waypoints.push_back(wpt);
489 }
490
491 /**
492  *
493  * @param fltType a string describing the type of
494  * traffic, normally used for gate assignments
495  * @return a converted string that gives the runway
496  * preference schedule to be used at aircraft having
497  * a preferential runway schedule implemented (i.e.
498  * having a rwyprefs.xml file
499  * 
500  * Currently valid traffic types for gate assignment:
501  * - gate (commercial gate)
502  * - cargo (commercial gargo),
503  * - ga (general aviation) ,
504  * - ul (ultralight),
505  * - mil-fighter (military - fighter),
506  * - mil-transport (military - transport)
507  *
508  * Valid runway classes:
509  * - com (commercial traffic: jetliners, passenger and cargo)
510  * - gen (general aviation)
511  * - ul (ultralight: I can imagine that these may share a runway with ga on some airports)
512  * - mil (all military traffic)
513  */
514 string FGAIFlightPlan::getRunwayClassFromTrafficType(string fltType)
515 {
516     if ((fltType == "gate") || (fltType == "cargo")) { 
517         return string("com");
518     }
519     if (fltType == "ga") {
520         return string ("gen");
521     }
522     if (fltType == "ul") {
523         return string("ul");
524     }
525     if ((fltType == "mil-fighter") || (fltType == "mil-transport")) { 
526         return string("mil");
527     }
528    return string("com");
529 }