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