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