]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AIAircraft.cxx
first stab at label box pointers. <option>top</option> on a <label> makes
[flightgear.git] / src / AIModel / AIAircraft.cxx
1 // FGAIAircraft - FGAIBase-derived class creates an AI airplane
2 //
3 // Written by David Culp, started October 2003.
4 //
5 // Copyright (C) 2003  David P. Culp - davidculp2@comcast.net
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #include <simgear/math/point3d.hxx>
26 #include <simgear/route/waypoint.hxx>
27 #include <Main/fg_props.hxx>
28 #include <Main/globals.hxx>
29 #include <Main/viewer.hxx>
30 #include <Scenery/scenery.hxx>
31 #include <Scenery/tilemgr.hxx>
32
33 #include <string>
34 #include <math.h>
35 #include <time.h>
36 #ifdef _MSC_VER
37 #  include <float.h>
38 #  define finite _finite
39 #elif defined(__sun) || defined(sgi)
40 #  include <ieeefp.h>
41 #endif
42
43 SG_USING_STD(string);
44
45 #include "AIAircraft.hxx"
46    static string tempReg;
47 //
48 // accel, decel, climb_rate, descent_rate, takeoff_speed, climb_speed,
49 // cruise_speed, descent_speed, land_speed
50 //
51 const FGAIAircraft::PERF_STRUCT FGAIAircraft::settings[] = {
52     // light aircraft
53     {2.0, 2.0,  450.0, 1000.0,  70.0,  80.0, 100.0,  80.0,  60.0},
54     // ww2_fighter
55     {4.0, 2.0, 3000.0, 1500.0, 110.0, 180.0, 250.0, 200.0, 100.0},
56     // jet_transport
57     {5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
58     // jet_fighter
59     {7.0, 3.0, 4000.0, 2000.0, 150.0, 350.0, 500.0, 350.0, 150.0},
60     // tanker
61     {5.0, 2.0, 3000.0, 1500.0, 140.0, 300.0, 430.0, 300.0, 130.0},
62     // ufo (extreme accel/decel)
63     {30.0, 30.0, 6000.0, 6000.0, 150.0, 300.0, 430.0, 300.0, 130.0}
64 };
65
66
67 FGAIAircraft::FGAIAircraft(FGAISchedule *ref) :
68     FGAIBase(otAircraft)
69 {
70     trafficRef = ref;
71     if (trafficRef)
72         groundOffset = trafficRef->getGroundOffset();
73     else
74         groundOffset = 0;
75
76     fp = 0;
77     dt_count = 0;
78     dt_elev_count = 0;
79     use_perf_vs = true;
80     isTanker = false;
81
82     // set heading and altitude locks
83     hdg_lock = false;
84     alt_lock = false;
85     roll = 0;
86     headingChangeRate = 0.0;
87 }
88
89
90 FGAIAircraft::~FGAIAircraft() {
91     //delete fp;
92 }
93
94
95 void FGAIAircraft::readFromScenario(SGPropertyNode* scFileNode) {
96     if (!scFileNode)
97         return;
98
99     FGAIBase::readFromScenario(scFileNode);
100
101     setPerformance(scFileNode->getStringValue("class", "jet_transport"));
102     setFlightPlan(scFileNode->getStringValue("flightplan"),
103                   scFileNode->getBoolValue("repeat", false));
104     setCallSign(scFileNode->getStringValue("callsign"));
105     setTACANChannelID(scFileNode->getStringValue("TACAN-channel-ID"));
106 }
107
108
109 bool FGAIAircraft::init() {
110     //refuel_node = fgGetNode("systems/refuel/contact", true);
111     return FGAIBase::init();
112 }
113
114
115 void FGAIAircraft::bind() {
116     FGAIBase::bind();
117
118     props->tie("controls/gear/gear-down",
119                SGRawValueMethods<FGAIAircraft,bool>(*this,
120                                               &FGAIAircraft::_getGearDown));
121     props->tie("refuel/contact", SGRawValuePointer<bool>(&contact));
122     props->setStringValue("callsign", callsign.c_str());
123     props->setStringValue("navaids/tacan/channel-ID", TACAN_channel_id.c_str());
124     props->setBoolValue("tanker",isTanker);
125 }
126
127
128 void FGAIAircraft::unbind() {
129     FGAIBase::unbind();
130
131     props->untie("controls/gear/gear-down");
132     props->untie("refuel/contact");
133 }
134
135
136 void FGAIAircraft::update(double dt) {
137     FGAIBase::update(dt);
138     Run(dt);
139     Transform();
140 }
141
142
143 void FGAIAircraft::setPerformance(const std::string& acclass) {
144     if (acclass == "light") {
145         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]);
146     } else if (acclass == "ww2_fighter") {
147         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]);
148     } else if (acclass == "jet_transport") {
149         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
150     } else if (acclass == "jet_fighter") {
151         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]);
152     } else if (acclass == "tanker") {
153         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
154         SetTanker(true);
155     } else if (acclass == "ufo") {
156         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::UFO]);
157     } else {
158         SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
159     }
160 }
161
162
163 void FGAIAircraft::SetPerformance(const PERF_STRUCT *ps) {
164
165    performance = ps;
166 }
167
168
169 void FGAIAircraft::Run(double dt) {
170
171     FGAIAircraft::dt = dt;
172
173     if (fp) {
174         time_t now = time(NULL) + fgGetLong("/sim/time/warp");
175         ProcessFlightPlan(dt, now);
176         if (now < fp->getStartTime()) {
177             // Do execute Ground elev for inactive aircraft, so they
178             // Are repositioned to the correct ground altitude when the user flies within visibility range.
179             if (no_roll) {
180                 Transform();       // make sure aip is initialized.
181                 getGroundElev(dt); // make sure it's exectuted first time around, so force a large dt value
182                 //getGroundElev(dt); // Need to do this twice.
183                 //cerr << trafficRef->getRegistration() << " Setting altitude to " << tgt_altitude;
184                 doGroundAltitude();
185                 //cerr << " Actual altitude " << altitude << endl;
186                 // Transform();
187                 pos.setElevationFt(altitude_ft);
188             }
189             return;
190         }
191     } else {
192         // no flight plan, update target heading, speed, and altitude
193         // from control properties.  These default to the initial
194         // settings in the config file, but can be changed "on the
195         // fly".
196
197         string lat_mode = props->getStringValue("controls/flight/lateral-mode");
198         if ( lat_mode == "roll" ) {
199             double angle
200                 = props->getDoubleValue("controls/flight/target-roll" );
201             RollTo( angle );
202         } else {
203              double angle
204                 = props->getDoubleValue("controls/flight/target-hdg" );
205             TurnTo( angle );
206         }
207
208         string lon_mode
209             = props->getStringValue("controls/flight/longitude-mode");
210         if ( lon_mode == "alt" ) {
211             double alt = props->getDoubleValue("controls/flight/target-alt" );
212             ClimbTo( alt );
213         } else {
214             double angle
215                 = props->getDoubleValue("controls/flight/target-pitch" );
216             PitchTo( angle );
217         }
218
219         AccelTo( props->getDoubleValue("controls/flight/target-spd" ) );
220     }
221
222     double turn_radius_ft;
223     double turn_circum_ft;
224     double speed_north_deg_sec;
225     double speed_east_deg_sec;
226     double dist_covered_ft;
227     double alpha;
228
229     // adjust speed
230     double speed_diff; //= tgt_speed - speed;
231     if (!no_roll) {
232         speed_diff = tgt_speed - speed;
233     } else {
234         speed_diff = groundTargetSpeed - speed;
235     }
236     if (fabs(speed_diff) > 0.2) {
237         if (speed_diff > 0.0)
238             speed += performance->accel * dt;
239         if (speed_diff < 0.0) {
240             if (no_roll) { // was (!no_roll) but seems more logical this way (ground brakes).
241                 speed -= performance->decel * dt * 3;
242             } else {
243                 speed -= performance->decel * dt;
244             }
245         }
246     }
247
248     // convert speed to degrees per second
249     speed_north_deg_sec = cos( hdg / SG_RADIANS_TO_DEGREES )
250                           * speed * 1.686 / ft_per_deg_lat;
251     speed_east_deg_sec  = sin( hdg / SG_RADIANS_TO_DEGREES )
252                           * speed * 1.686 / ft_per_deg_lon;
253
254     // set new position
255     pos.setLatitudeDeg( pos.getLatitudeDeg() + speed_north_deg_sec * dt);
256     pos.setLongitudeDeg( pos.getLongitudeDeg() + speed_east_deg_sec * dt); 
257     //if (!(finite(pos.lat()) && finite(pos.lon())))
258     //  {
259     //    cerr << "Position is not finite" << endl;
260     //    cerr << "speed = " << speed << endl;
261     //    cerr << "dt" << dt << endl;
262     //    cerr << "heading " << hdg << endl;
263     //    cerr << "speed east " << speed_east_deg_sec << endl;
264     //    cerr << "speed nrth " << speed_north_deg_sec << endl;
265     //    cerr << "deg_lat    " << ft_per_deg_lat << endl;
266     //    cerr << "deg_lon    " << ft_per_deg_lon << endl;
267     //  }
268
269     // adjust heading based on current bank angle
270     if (roll == 0.0)
271         roll = 0.01;
272
273     if (roll != 0.0) {
274         // double turnConstant;
275         //if (no_roll)
276         //  turnConstant = 0.0088362;
277         //else
278         //  turnConstant = 0.088362;
279         // If on ground, calculate heading change directly
280         if (no_roll) {
281             double headingDiff = fabs(hdg-tgt_heading);
282
283             if (headingDiff > 180)
284                 headingDiff = fabs(headingDiff - 360);
285
286             groundTargetSpeed = tgt_speed - (tgt_speed * (headingDiff/45));
287             if (sign(groundTargetSpeed) != sign(tgt_speed))
288                 groundTargetSpeed = 0.21 * sign(tgt_speed); // to prevent speed getting stuck in 'negative' mode
289
290             if (headingDiff > 30.0) {
291                 headingChangeRate += dt * sign(roll); // invert if pushed backward
292                 // Print some debug statements to find out why aircraft may get stuck
293                 // forever turning
294                 //if (trafficRef->getDepartureAirport()->getId() == string("EHAM")) {
295                 //    cerr << "Turning : " << trafficRef->getRegistration()
296                 //    cerr <<  " Speed = " << speed << " Heading " << hdg
297                 //         << " Target Heading " << tgt_heading
298                 //         << " Lead Distance " <<  fp->getLeadDistance()
299                 //         << " Distance to go "
300                 //         << fp->getDistanceToGo(pos.lat(), pos.lon(), fp->getCurrentWaypoint())
301                 //         << "waypoint name " << fp->getCurrentWaypoint()->name
302                 //         << endl;
303                 //}
304                 if (headingChangeRate > 30)
305                     headingChangeRate = 30;
306                 else if (headingChangeRate < -30)
307                     headingChangeRate = -30;
308
309             } else {
310                 if (fabs(headingChangeRate) > headingDiff)
311                     headingChangeRate = headingDiff*sign(roll);
312                 else
313                     headingChangeRate += dt * sign(roll);
314             }
315             hdg += headingChangeRate * dt;
316             //cerr << "On ground. Heading: " << hdg << ". Target Heading: " << tgt_heading
317             //     << ". Target speed: " << groundTargetSpeed << ". heading change rate"
318             //     << headingChangeRate << endl;
319
320         } else {
321             if (fabs(speed) > 1.0) {
322                 turn_radius_ft = 0.088362 * speed * speed
323                         / tan( fabs(roll) / SG_RADIANS_TO_DEGREES );
324             } else {
325                 turn_radius_ft = 1.0; // Check if turn_radius_ft == 0; this might lead to a division by 0.
326             }
327             turn_circum_ft = SGD_2PI * turn_radius_ft;
328             dist_covered_ft = speed * 1.686 * dt;
329             alpha = dist_covered_ft / turn_circum_ft * 360.0;
330             hdg += alpha * sign(roll);
331         }
332         while ( hdg > 360.0 ) {
333             hdg -= 360.0;
334             spinCounter++;
335         }
336         while ( hdg < 0.0) {
337             hdg += 360.0;
338             spinCounter--;
339         }
340     }
341
342
343     // adjust target bank angle if heading lock engaged
344     if (hdg_lock) {
345         double bank_sense = 0.0;
346         double diff = fabs(hdg - tgt_heading);
347         if (diff > 180)
348             diff = fabs(diff - 360);
349
350         double sum = hdg + diff;
351         if (sum > 360.0)
352             sum -= 360.0;
353         if (fabs(sum - tgt_heading) < 1.0) {
354             bank_sense = 1.0;   // right turn
355         } else {
356             bank_sense = -1.0;  // left turn
357         }
358         if (diff < 30) {
359             tgt_roll = diff * bank_sense;
360         } else {
361             tgt_roll = 30.0 * bank_sense;
362         }
363         if ((fabs((double) spinCounter) > 1) && (diff > 30)) {
364             tgt_speed *= 0.999; // Ugly hack: If aircraft get stuck, they will continually spin around.
365             // The only way to resolve this is to make them slow down.
366             //if (tempReg.empty())
367             //  tempReg = trafficRef->getRegistration();
368             //if (trafficRef->getRegistration() == tempReg) {
369             //    cerr << trafficRef->getRegistration()
370             //         << " appears to be spinning: " << spinCounter << endl
371             //         << " speed          " << speed << endl
372             //         << " heading        " << hdg << endl
373             //         << " lead distance  " << fp->getLeadDistance() << endl
374             //         << " waypoint       " << fp->getCurrentWaypoint()->name
375             //         << " target heading " << tgt_heading << endl
376             //         << " lead in angle  " << fp->getLeadInAngle()<< endl
377             //         << " roll           " << roll << endl
378             //         << " target_roll    " << tgt_roll << endl;
379             //}
380         }
381     }
382
383     // adjust bank angle, use 9 degrees per second
384     double bank_diff = tgt_roll - roll;
385     if (fabs(bank_diff) > 0.2) {
386         if (bank_diff > 0.0)
387             roll += 9.0 * dt;
388
389         if (bank_diff < 0.0)
390             roll -= 9.0 * dt;
391         //while (roll > 180) roll -= 360;
392         //while (roll < 180) roll += 360;
393     }
394
395     // adjust altitude (meters) based on current vertical speed (fpm)
396     altitude_ft += vs / 60.0 * dt;
397     pos.setElevationFt(altitude_ft);  
398
399     // adjust target Altitude, based on ground elevation when on ground
400     if (no_roll) {
401         getGroundElev(dt);
402         doGroundAltitude();
403     } else {
404         // find target vertical speed if altitude lock engaged
405         if (alt_lock && use_perf_vs) {
406             if (altitude_ft < tgt_altitude_ft) {
407                 tgt_vs = tgt_altitude_ft - altitude_ft;
408                 if (tgt_vs > performance->climb_rate)
409                     tgt_vs = performance->climb_rate;
410             } else {
411                 tgt_vs = tgt_altitude_ft - altitude_ft;
412                 if (tgt_vs  < (-performance->descent_rate))
413                     tgt_vs = -performance->descent_rate;
414             }
415         }
416
417         if (alt_lock && !use_perf_vs) {
418             double max_vs = 4*(tgt_altitude_ft - altitude_ft);
419             double min_vs = 100;
420             if (tgt_altitude_ft < altitude_ft)
421                 min_vs = -100.0;
422             if ((fabs(tgt_altitude_ft - altitude_ft) < 1500.0)
423                     && (fabs(max_vs) < fabs(tgt_vs)))
424                 tgt_vs = max_vs;
425
426             if (fabs(tgt_vs) < fabs(min_vs))
427                 tgt_vs = min_vs;
428         }
429     }
430     // adjust vertical speed
431     double vs_diff = tgt_vs - vs;
432     if (fabs(vs_diff) > 10.0) {
433         if (vs_diff > 0.0) {
434             vs += 900.0 * dt;
435
436             if (vs > tgt_vs)
437                 vs = tgt_vs;
438         } else {
439             vs -= 400.0 * dt;
440
441            if (vs < tgt_vs)
442                vs = tgt_vs;
443         }
444     }
445
446     // match pitch angle to vertical speed
447     if (vs > 0) {
448         pitch = vs * 0.005;
449     } else {
450         pitch = vs * 0.002;
451     }
452
453     //###########################//
454     // do calculations for radar //
455     //###########################//
456     double range_ft2 = UpdateRadar(manager);
457
458     //************************************//
459     // Tanker code                        //
460     //************************************//
461
462     if ( isTanker) {
463         if ( (range_ft2 < 250.0 * 250.0) && (y_shift > 0.0)
464                 && (elevation > 0.0) ) {
465             //refuel_node->setBoolValue(true);
466             contact = true;
467         } else {
468             //refuel_node->setBoolValue(false);
469             contact = false;
470         }
471     } else {
472         contact = false;
473     }
474 }
475
476
477 void FGAIAircraft::AccelTo(double speed) {
478     tgt_speed = speed;
479 }
480
481
482 void FGAIAircraft::PitchTo(double angle) {
483     tgt_pitch = angle;
484     alt_lock = false;
485 }
486
487
488 void FGAIAircraft::RollTo(double angle) {
489     tgt_roll = angle;
490     hdg_lock = false;
491 }
492
493
494 void FGAIAircraft::YawTo(double angle) {
495     tgt_yaw = angle;
496 }
497
498
499 void FGAIAircraft::ClimbTo(double alt_ft ) {
500     tgt_altitude_ft = alt_ft;
501     alt_lock = true;
502 }
503
504
505 void FGAIAircraft::TurnTo(double heading) {
506     tgt_heading = heading;
507     hdg_lock = true;
508 }
509
510
511 double FGAIAircraft::sign(double x) {
512     if ( x < 0.0 )
513         return -1.0;
514     else
515         return 1.0;
516 }
517
518
519 void FGAIAircraft::setFlightPlan(const std::string& flightplan, bool repeat) {
520     if (!flightplan.empty()) {
521         FGAIFlightPlan* fp = new FGAIFlightPlan(flightplan);
522         fp->setRepeat(repeat);
523         SetFlightPlan(fp);
524     }
525 }
526
527
528 void FGAIAircraft::SetFlightPlan(FGAIFlightPlan *f) {
529     delete fp;
530     fp = f;
531 }
532
533
534 void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) {
535     bool eraseWaypoints;
536     if (trafficRef) {
537 //      FGAirport *arr;
538 //      FGAirport *dep;
539         eraseWaypoints = true;
540 //      cerr << trafficRef->getRegistration();
541 //      cerr << "Departure airport " << endl;
542 //      dep = trafficRef->getDepartureAirport();
543 //      if (dep)
544 //          cerr << dep->getId() << endl;
545 //      cerr << "Arrival   airport " << endl;
546 //      arr = trafficRef->getArrivalAirport();
547 //      if (arr)
548 //          cerr << arr->getId() <<endl << endl;;
549     } else
550         eraseWaypoints = false;
551
552     //cerr << "Processing Flightplan" << endl;
553     FGAIFlightPlan::waypoint* prev = 0; // the one behind you
554     FGAIFlightPlan::waypoint* curr = 0; // the one ahead
555     FGAIFlightPlan::waypoint* next = 0; // the next plus 1
556     prev = fp->getPreviousWaypoint();
557     curr = fp->getCurrentWaypoint();
558     next = fp->getNextWaypoint();
559     dt_count += dt;
560
561     if (!prev) {  //beginning of flightplan, do this initialization once
562         //setBank(0.0);
563         spinCounter = 0;
564         tempReg = "";
565         //prev_dist_to_go = HUGE;
566         //cerr << "Before increment " << curr-> name << endl;
567         fp->IncrementWaypoint(eraseWaypoints);
568         //prev = fp->getPreviousWaypoint(); //first waypoint
569         //curr = fp->getCurrentWaypoint();  //second waypoint
570         //next = fp->getNextWaypoint();     //third waypoint (might not exist!)
571         //cerr << "After increment " << prev-> name << endl;
572         if (!(fp->getNextWaypoint()) && trafficRef)
573             loadNextLeg();
574
575         //cerr << "After load " << prev-> name << endl;
576         prev = fp->getPreviousWaypoint(); //first waypoint
577         curr = fp->getCurrentWaypoint();  //second waypoint
578         next = fp->getNextWaypoint();     //third waypoint (might not exist!)
579         //cerr << "After load " << prev-> name << endl;
580         setLatitude(prev->latitude);
581         setLongitude(prev->longitude);
582         setSpeed(prev->speed);
583         setAltitude(prev->altitude);
584
585         if (prev->speed > 0.0)
586             setHeading(fp->getBearing(prev->latitude, prev->longitude, curr));
587         else
588             setHeading(fp->getBearing(curr->latitude, curr->longitude, prev));
589
590         // If next doesn't exist, as in incrementally created flightplans for
591         // AI/Trafficmanager created plans,
592         // Make sure lead distance is initialized otherwise
593         if (next)
594             fp->setLeadDistance(speed, hdg, curr, next);
595
596         if (curr->crossat > -1000.0) { //use a calculated descent/climb rate
597             use_perf_vs = false;
598             tgt_vs = (curr->crossat - prev->altitude)
599                     / (fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)
600                     / 6076.0 / prev->speed*60.0);
601             tgt_altitude_ft = curr->crossat;
602         } else {
603             use_perf_vs = true;
604             tgt_altitude_ft = prev->altitude;
605         }
606         alt_lock = hdg_lock = true;
607         no_roll = prev->on_ground;
608         if (no_roll) {
609             Transform();         // make sure aip is initialized.
610             getGroundElev(60.1); // make sure it's exectuted first time around, so force a large dt value
611             //getGroundElev(60.1); // Need to do this twice.
612             //cerr << trafficRef->getRegistration() << " Setting altitude to " << tgt_altitude << endl;
613             doGroundAltitude(); //(tgt_altitude);
614         }
615         prevSpeed = 0;
616         //cout << "First waypoint:  " << prev->name << endl;
617         //cout << "  Target speed:    " << tgt_speed << endl;
618         //cout << "  Target altitude: " << tgt_altitude << endl;
619         //cout << "  Target heading:  " << tgt_heading << endl << endl;
620         //cerr << "Done Flightplan init" << endl;
621         return;
622     } // end of initialization
623
624     // let's only process the flight plan every 100 ms.
625     if ((dt_count < 0.1) || (now < fp->getStartTime())) {
626         //cerr  << "done fp dt" << endl;
627         return;
628     } else {
629         dt_count = 0;
630     }
631     // check to see if we've reached the lead point for our next turn
632     double dist_to_go = fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr);
633
634     //cerr << "2" << endl;
635     double lead_dist = fp->getLeadDistance();
636     //cerr << " Distance : " << dist_to_go << ": Lead distance " << lead_dist << endl;
637     // experimental: Use fabs, because speed can be negative (I hope) during push_back.
638
639     if (lead_dist < fabs(2*speed)) {
640         lead_dist = fabs(2*speed);  //don't skip over the waypoint
641         //cerr << "Extending lead distance to " << lead_dist << endl;
642     }
643 //  FGAirport * apt = trafficRef->getDepartureAirport();
644 //  if ((dist_to_go > prev_dist_to_go) && trafficRef && apt) {
645 //      if (apt->getId() == string("EHAM"))
646 //          cerr << "Alert: " << trafficRef->getRegistration() << " is moving away from waypoint " << curr->name  << endl
647 //               << "Target heading : " << tgt_heading << "act heading " << hdg << " Tgt speed : " << tgt_speed << endl
648 //               << "Lead distance : " << lead_dist  << endl
649 //               << "Distance to go: " << dist_to_go << endl;
650 //  }
651
652     prev_dist_to_go = dist_to_go;
653     //cerr << "2" << endl;
654     //if (no_roll)
655     //  lead_dist = 10.0;
656     //cout << "Leg : " << (fp->getLeg()-1) << ". dist_to_go: " << dist_to_go << ",  lead_dist: "
657     //     << lead_dist << ", tgt_speed " << tgt_speed << ", tgt_heading " << tgt_heading
658     //     << " speed " << speed << " hdg " << hdg << ". Altitude "  << altitude << " TAget alt :"
659     //     << tgt_altitude << endl;
660
661     if ( dist_to_go < lead_dist ) {
662         //prev_dist_to_go = HUGE;
663         // For traffic manager generated aircraft:
664         // check if the aircraft flies of of user range. And adjust the
665         // Current waypoint's elevation according to Terrain Elevation
666         if (curr->finished) {  //end of the flight plan
667             if (fp->getRepeat())
668                 fp->restart();
669             else
670                 setDie(true);
671
672             //cerr << "Done die end of fp" << endl;
673             return;
674         }
675
676         // we've reached the lead-point for the waypoint ahead
677         //cerr << "4" << endl;
678         //cerr << "Situation after lead point" << endl;
679         //cerr << "Prviious: " << prev->name << endl;
680         //cerr << "Current : " << curr->name << endl;
681         //cerr << "Next    : " << next->name << endl;
682         if (next) {
683             tgt_heading = fp->getBearing(curr, next);
684             spinCounter = 0;
685         }
686
687         fp->IncrementWaypoint(eraseWaypoints);
688         if (!(fp->getNextWaypoint()) && trafficRef)
689             loadNextLeg();
690
691         prev = fp->getPreviousWaypoint();
692         curr = fp->getCurrentWaypoint();
693         next = fp->getNextWaypoint();
694
695         // Now that we have incremented the waypoints, excute some traffic manager specific code
696         // based on the name of the waypoint we just passed.
697         if (trafficRef) {
698             double userLatitude  = fgGetDouble("/position/latitude-deg");
699             double userLongitude = fgGetDouble("/position/longitude-deg");
700             double course, distance;
701             SGWayPoint current(pos.getLongitudeDeg(), pos.getLatitudeDeg(), 0);
702             SGWayPoint user (userLongitude, userLatitude, 0);
703             user.CourseAndDistance(current, &course, &distance);
704             if ((distance * SG_METER_TO_NM) > TRAFFICTOAIDIST) {
705                 setDie(true);
706                 //cerr << "done fp die out of range" << endl;
707                 return;
708             }
709
710             FGAirport * dep = trafficRef->getDepartureAirport();
711             FGAirport * arr = trafficRef->getArrivalAirport();
712             // At parking the beginning of the airport
713             if (!( dep && arr)) {
714                 setDie(true);
715                 return;
716             }
717
718             //if ((dep->getId() == string("EHAM") || (arr->getId() == string("EHAM")))) {
719             //    cerr << trafficRef->getRegistration()
720             //         << " Enroute from " << dep->getId()
721             //         << " to "           << arr->getId()
722             //         << " just crossed " << prev->name
723             //         << " Assigned rwy     " << fp->getRunwayId()
724             //         << " " << fp->getRunway() << endl;
725             // }
726             //if ((dep->getId() == string("EHAM")) && (prev->name == "park2")) {
727             //    cerr << "Schiphol ground "
728             //         << trafficRef->getCallSign();
729             //    if (trafficRef->getHeavy())
730             //        cerr << "Heavy";
731             //    cerr << ", is type "
732             //         << trafficRef->getAircraft()
733             //         << " ready to go. IFR to "
734             //         << arr->getId() <<endl;
735             // }
736
737             if (prev->name == "park2")
738                 dep->getDynamics()->releaseParking(fp->getGate());
739
740             // Some debug messages, specific to testing the Logical networks.
741             // if ((arr->getId() == string("EHAM")) && (prev->name  == "Center")) {
742             //     cerr << "Schiphol ground "
743             //          << trafficRef->getRegistration() << " "
744             //          << trafficRef->getCallSign();
745             //     if (trafficRef->getHeavy())
746             //         cerr << "Heavy";
747             //     cerr << ", arriving from " << dep->getName() ;
748             //     cerr << " landed runway "
749             //          << fp->getRunway()
750             //          << " request taxi to gate "
751             //          << arr->getDynamics()->getParkingName(fp->getGate())
752             //          << endl;
753             // }
754             if (prev->name == "END")
755                 fp->setTime(trafficRef->getDepartureTime());
756             //cerr << "5" << endl;
757         }
758
759         if (next) {
760             //cerr << "Current waypoint" << curr->name << endl;
761             //cerr << "Next waypoint" << next->name << endl;
762             fp->setLeadDistance(speed, tgt_heading, curr, next);
763         }
764
765         //cerr << "5.1" << endl;
766         if (!(prev->on_ground)) { // only update the tgt altitude from flightplan if not on the ground
767             tgt_altitude_ft = prev->altitude;
768             if (curr->crossat > -1000.0) {
769                 //cerr << "5.1a" << endl;
770                 use_perf_vs = false;
771                 tgt_vs = (curr->crossat - altitude_ft) / (fp->getDistanceToGo(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr)
772                         / 6076.0 / speed*60.0);
773                 //cerr << "5.1b" << endl;
774                 tgt_altitude_ft = curr->crossat;
775             } else {
776                 //cerr << "5.1c" << endl;
777                 use_perf_vs = true;
778                 //cerr << "5.1d" << endl;
779                 //cerr << "Setting target altitude : " <<tgt_altitude_ft << endl;
780             }
781         }
782         //cerr << "6" << endl;
783         tgt_speed = prev->speed;
784         hdg_lock = alt_lock = true;
785         no_roll = prev->on_ground;
786         //cout << "Crossing waypoint: " << prev->name << endl;
787         //cout << "  Target speed:    " << tgt_speed << endl;
788         //cout << "  Target altitude: " << tgt_altitude_ft << endl;
789         //cout << "  Target heading:  " << tgt_heading << endl << endl;
790
791     } else {
792         double calc_bearing = fp->getBearing(pos.getLatitudeDeg(), pos.getLongitudeDeg(), curr);
793         //cerr << "Bearing = " << calc_bearing << endl;
794         if (speed < 0) {
795             calc_bearing +=180;
796             if (calc_bearing > 360)
797                 calc_bearing -= 360;
798         }
799
800         if (finite(calc_bearing)) {
801             double hdg_error = calc_bearing - tgt_heading;
802             if (fabs(hdg_error) > 1.0) {
803                 TurnTo( calc_bearing );
804             }
805
806         } else {
807             cerr << "calc_bearing is not a finite number : "
808                  << "Speed " << speed
809                  << "pos : " << pos.getLatitudeDeg() << ", " << pos.getLongitudeDeg()
810                  << "waypoint " << curr->latitude << ", " << curr->longitude << endl;
811             cerr << "waypoint name " << curr->name;
812             exit(1);  // FIXME
813         }
814
815         double speed_diff = speed - prevSpeed;
816         // Update the lead distance calculation if speed has changed sufficiently
817         // to prevent spinning (hopefully);
818         if (fabs(speed_diff) > 10) {
819             prevSpeed = speed;
820             fp->setLeadDistance(speed, tgt_heading, curr, next);
821         }
822
823         //cerr << "Done Processing FlightPlan"<< endl;
824     }
825 }
826
827
828 bool FGAIAircraft::_getGearDown() const {
829     return ((props->getFloatValue("position/altitude-agl-ft") < 900.0)
830             && (props->getFloatValue("velocities/airspeed-kt")
831             < performance->land_speed*1.25));
832 }
833
834
835 void FGAIAircraft::loadNextLeg() {
836     //delete fp;
837     //time_t now = time(NULL) + fgGetLong("/sim/time/warp");
838
839     //FGAIModelEntity entity;
840     //entity.m_class = "jet_transport";
841     //entity.path = modelPath.c_str();
842     //entity.flightplan = "none";
843     //entity.latitude = _getLatitude();
844     //entity.longitude = _getLongitude();
845     //entity.altitude = trafficRef->getCruiseAlt() * 100; // convert from FL to feet
846     //entity.speed = 450; // HACK ALERT
847     //entity.fp = new FGAIFlightPlan(&entity, courseToDest, i->getDepartureTime(), dep, arr);
848     int leg;
849     if ((leg = fp->getLeg())  == 10) {
850         trafficRef->next();
851         leg = 1;
852         fp->setLeg(leg);
853         //cerr << "Resetting leg : " << leg << endl;
854     }
855     //leg++;
856     //fp->setLeg(leg);
857     //cerr << "Creating leg number : " << leg << endl;
858     FGAirport *dep = trafficRef->getDepartureAirport();
859     FGAirport *arr = trafficRef->getArrivalAirport();
860     if (!(dep && arr)) {
861         setDie(true);
862         //cerr << "Failed to get airport in AIAircraft::ProcessFlightplan()" << endl;
863         //if (dep)
864         //  cerr << "Departure " << dep->getId() << endl;
865         //if (arr)
866         //  cerr << "Arrival   " << arr->getId() << endl;
867
868     } else {
869         double cruiseAlt = trafficRef->getCruiseAlt() * 100;
870         //cerr << "Creating new leg using " << cruiseAlt << " as cruise altitude."<< endl;
871
872         fp->create (dep,
873                     arr,
874                     leg,
875                     cruiseAlt, //(trafficRef->getCruiseAlt() * 100), // convert from FL to feet
876                     trafficRef->getSpeed(),
877                     _getLatitude(),
878                     _getLongitude(),
879                     false,
880                     trafficRef->getRadius(),
881                     trafficRef->getFlightType(),
882                     acType,
883                     company);
884         //prev = fp->getPreviousWaypoint();
885         //curr = fp->getCurrentWaypoint();
886         //next = fp->getNextWaypoint();
887         //cerr << "25" << endl;
888         //if (next)
889         //        {
890         //  //cerr << "Next waypoint" << next->name << endl;
891         //          fp->setLeadDistance(speed, tgt_heading, curr, next);
892         //        }
893         //cerr << "25.1" << endl;
894         //if (curr->crossat > -1000.0) {
895         //        //cerr << "25.1a" << endl;
896         //        use_perf_vs = false;
897         //
898         //        tgt_vs = (curr->crossat - altitude_ft)/
899         //          (fp->getDistanceToGo(pos.lat(), pos.lon(), curr)/6076.0/speed*60.0);
900         //        //cerr << "25.1b" << endl;
901         //        tgt_altitude_ft = curr->crossat;
902         //} else {
903         //        //cerr << "25.1c" << endl;
904         //        use_perf_vs = true;
905         //        //cerr << "25.1d" << endl;
906         //        tgt_altitude_ft = prev->altitude;
907         //        //cerr << "Setting target altitude : " <<tgt_altitude_ft << endl;
908         // }
909         //cerr << "26" << endl;
910         //tgt_speed = prev->speed;
911         //hdg_lock = alt_lock = true;
912         //no_roll = prev->on_ground;
913     }
914     //else
915     //{
916     //delete entity.fp;
917     //entity.fp = new FGAIFlightPlan(&entity,
918     //                                 999,  // A hack
919     //                                 trafficRef->getDepartureTime(),
920     //                                 trafficRef->getDepartureAirport(),
921     //                                 trafficRef->getArrivalAirport(),
922     //                                 false,
923     //                                 acType,
924     //                                 company);
925     //SetFlightPlan(entity.fp);
926 }
927
928
929 // Note: This code is copied from David Luff's AILocalTraffic
930 // Warning - ground elev determination is CPU intensive
931 // Either this function or the logic of how often it is called
932 // will almost certainly change.
933
934 void FGAIAircraft::getGroundElev(double dt) {
935     dt_elev_count += dt;
936
937     // Update minimally every three secs, but add some randomness
938     // to prevent all IA objects doing this in synchrony
939     if (dt_elev_count < (3.0) + (rand() % 10))
940         return;
941     else
942        dt_elev_count = 0;
943
944     // Only do the proper hitlist stuff if we are within visible range of the viewer.
945     if (!invisible) {
946         double visibility_meters = fgGetDouble("/environment/visibility-m");
947
948         FGViewer* vw = globals->get_current_view();
949         double course, distance;
950
951         SGWayPoint current(pos.getLongitudeDeg(), pos.getLatitudeDeg(), 0);
952         SGWayPoint view (vw->getLongitude_deg(), vw->getLatitude_deg(), 0);
953         view.CourseAndDistance(current, &course, &distance);
954         if(distance > visibility_meters) {
955             //aip.getSGLocation()->set_cur_elev_m(aptElev);
956             return;
957         }
958
959         // FIXME: make sure the pos.lat/pos.lon values are in degrees ...
960         double range = 500.0;
961         if (!globals->get_tile_mgr()->scenery_available(pos.getLatitudeDeg(), pos.getLongitudeDeg(), range)) {
962             // Try to shedule tiles for that position.
963             globals->get_tile_mgr()->update( aip.getSGLocation(), range );
964         }
965
966         // FIXME: make sure the pos.lat/pos.lon values are in degrees ...
967         double alt;
968         if (globals->get_scenery()->get_elevation_m(pos.getLatitudeDeg(), pos.getLongitudeDeg(), 20000.0, alt, 0))
969             tgt_altitude_ft = alt * SG_METER_TO_FEET;
970
971         //cerr << "Target altitude : " << tgt_altitude_ft << endl;
972         // if (globals->get_scenery()->get_elevation_m(pos.lat(), pos.lon(),
973         //                                            20000.0, alt))
974         //    tgt_altitude_ft = alt * SG_METER_TO_FEET;
975         //cerr << "Target altitude : " << tgt_altitude_ft << endl;
976     }
977 }
978
979
980 void FGAIAircraft::setCallSign(const string& s) {
981     callsign = s;
982 }
983
984
985 void FGAIAircraft::setTACANChannelID(const string& id) {
986     TACAN_channel_id = id;
987 }
988
989
990 void FGAIAircraft::doGroundAltitude() {
991    if (fabs(altitude_ft - (tgt_altitude_ft+groundOffset)) > 1000.0)
992        altitude_ft = (tgt_altitude_ft + groundOffset);
993    else
994       altitude_ft += 0.1 * ((tgt_altitude_ft+groundOffset) - altitude_ft);
995 }
996