1 // autopilot.cxx -- autopilot subsystem
3 // Started by Jeff Goeke-Smith, started April 1998.
5 // Copyright (C) 1998 Jeff Goeke-Smith, jgoeke@voyager.net
7 // Heavy modifications and additions by Norman Vine and few by Curtis
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License as
12 // published by the Free Software Foundation; either version 2 of the
13 // License, or (at your option) any later version.
15 // This program is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // General Public License for more details.
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 #include <Scenery/scenery.hxx>
36 #include <simgear/constants.h>
37 #include <simgear/debug/logstream.hxx>
38 #include <simgear/misc/fgpath.hxx>
40 #include <Airports/simple.hxx>
42 #include <Main/fg_init.hxx>
43 #include <Main/options.hxx>
44 #include <Time/fg_time.hxx>
46 #include "autopilot.hxx"
49 #define mySlider puSlider
51 // Climb speed constants
52 const double min_climb = 70.0; // kts
53 const double best_climb = 75.0; // kts
54 const double ideal_climb_rate = 500.0; // fpm
56 /// These statics will eventually go into the class
57 /// they are just here while I am experimenting -- NHV :-)
58 // AutoPilot Gain Adjuster members
59 static double MaxRollAdjust; // MaxRollAdjust = 2 * APData->MaxRoll;
60 static double RollOutAdjust; // RollOutAdjust = 2 * APData->RollOut;
61 static double MaxAileronAdjust; // MaxAileronAdjust = 2 * APData->MaxAileron;
62 static double RollOutSmoothAdjust; // RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
64 static float MaxRollValue; // 0.1 -> 1.0
65 static float RollOutValue;
66 static float MaxAileronValue;
67 static float RollOutSmoothValue;
69 static float TmpMaxRollValue; // for cancel operation
70 static float TmpRollOutValue;
71 static float TmpMaxAileronValue;
72 static float TmpRollOutSmoothValue;
74 static puDialogBox *APAdjustDialog;
75 static puFrame *APAdjustFrame;
76 static puText *APAdjustDialogMessage;
77 static puFont APAdjustLegendFont;
78 static puFont APAdjustLabelFont;
80 static puOneShot *APAdjustOkButton;
81 static puOneShot *APAdjustResetButton;
82 static puOneShot *APAdjustCancelButton;
84 //static puButton *APAdjustDragButton;
86 static puText *APAdjustMaxRollTitle;
87 static puText *APAdjustRollOutTitle;
88 static puText *APAdjustMaxAileronTitle;
89 static puText *APAdjustRollOutSmoothTitle;
91 static puText *APAdjustMaxAileronText;
92 static puText *APAdjustMaxRollText;
93 static puText *APAdjustRollOutText;
94 static puText *APAdjustRollOutSmoothText;
96 static mySlider *APAdjustHS0;
97 static mySlider *APAdjustHS1;
98 static mySlider *APAdjustHS2;
99 static mySlider *APAdjustHS3;
101 static char SliderText[ 4 ][ 8 ];
103 ///////// AutoPilot New Heading Dialog
105 static puDialogBox *ApHeadingDialog;
106 static puFrame *ApHeadingDialogFrame;
107 static puText *ApHeadingDialogMessage;
108 static puInput *ApHeadingDialogInput;
109 static puOneShot *ApHeadingDialogOkButton;
110 static puOneShot *ApHeadingDialogCancelButton;
113 ///////// AutoPilot New Altitude Dialog
115 static puDialogBox *ApAltitudeDialog = 0;
116 static puFrame *ApAltitudeDialogFrame = 0;
117 static puText *ApAltitudeDialogMessage = 0;
118 static puInput *ApAltitudeDialogInput = 0;
120 static puOneShot *ApAltitudeDialogOkButton = 0;
121 static puOneShot *ApAltitudeDialogCancelButton = 0;
124 /// The beginnings of Lock AutoPilot to target location :-)
125 // Needs cleaning up but works
126 // These statics should disapear when this is a class
127 static puDialogBox *TgtAptDialog = 0;
128 static puFrame *TgtAptDialogFrame = 0;
129 static puText *TgtAptDialogMessage = 0;
130 static puInput *TgtAptDialogInput = 0;
132 static char NewTgtAirportId[16];
133 static char NewTgtAirportLabel[] = "Enter New TgtAirport ID";
135 static puOneShot *TgtAptDialogOkButton = 0;
136 static puOneShot *TgtAptDialogCancelButton = 0;
137 static puOneShot *TgtAptDialogResetButton = 0;
140 // global variable holding the AP info
141 // I want this gone. Data should be in aircraft structure
142 fgAPDataPtr APDataGlobal;
144 // Local Prototype section
145 static double LinearExtrapolate( double x, double x1, double y1, double x2, double y2 );
146 static double NormalizeDegrees( double Input );
147 // End Local ProtoTypes
149 extern char *coord_format_lat(float);
150 extern char *coord_format_lon(float);
153 static inline double get_ground_speed( void ) {
154 // starts in ft/s so we convert to kts
155 double ft_s = cur_fdm_state->get_V_ground_speed()
156 * current_options.get_speed_up();;
157 double kts = ft_s * FEET_TO_METER * 3600 * METER_TO_NM;
161 // The below routines were copied right from hud.c ( I hate reinventing
162 // the wheel more than necessary)
164 // The following routines obtain information concerntin the aircraft's
165 // current state and return it to calling instrument display routines.
166 // They should eventually be member functions of the aircraft.
168 static void get_control_values( void ) {
170 APData = APDataGlobal;
171 APData->old_aileron = controls.get_aileron();
172 APData->old_elevator = controls.get_elevator();
173 APData->old_elevator_trim = controls.get_elevator_trim();
174 APData->old_rudder = controls.get_rudder();
177 static void MakeTargetHeadingStr( fgAPDataPtr APData, double bearing ) {
180 else if(bearing > 360. )
182 sprintf(APData->TargetHeadingStr, "APHeading %6.1f", bearing);
185 static inline void MakeTargetDistanceStr( fgAPDataPtr APData, double distance ) {
186 double eta = distance*METER_TO_NM / get_ground_speed();
187 if ( eta >= 100.0 ) { eta = 99.999; }
189 if ( eta < (1.0/6.0) ) {
190 // within 10 minutes, bump up to min/secs
194 minor = (int)((eta - (int)eta) * 60.0);
195 sprintf(APData->TargetDistanceStr, "APDistance %.2f NM ETA %d:%02d",
196 distance*METER_TO_NM, major, minor);
197 // cout << "distance = " << distance*METER_TO_NM
198 // << " gndsp = " << get_ground_speed()
199 // << " time = " << eta
200 // << " major = " << major
201 // << " minor = " << minor
205 static inline void MakeTargetAltitudeStr( fgAPDataPtr APData, double altitude ) {
206 sprintf(APData->TargetAltitudeStr, "APAltitude %6.0f", altitude);
209 static inline void MakeTargetLatLonStr( fgAPDataPtr APData, double lat, double lon ) {
211 tmp = APData->TargetLatitude;
212 sprintf( APData->TargetLatitudeStr , "%s", coord_format_lat(tmp) );
213 tmp = APData->TargetLongitude;
214 sprintf( APData->TargetLongitudeStr, "%s", coord_format_lon(tmp) );
216 sprintf(APData->TargetLatLonStr, "%s %s",
217 APData->TargetLatitudeStr,
218 APData->TargetLongitudeStr );
221 static inline double get_speed( void ) {
222 return( cur_fdm_state->get_V_equiv_kts() );
225 static inline double get_aoa( void ) {
226 return( cur_fdm_state->get_Gamma_vert_rad() * RAD_TO_DEG );
229 static inline double fgAPget_latitude( void ) {
230 return( cur_fdm_state->get_Latitude() * RAD_TO_DEG );
233 static inline double fgAPget_longitude( void ) {
234 return( cur_fdm_state->get_Longitude() * RAD_TO_DEG );
237 static inline double fgAPget_roll( void ) {
238 return( cur_fdm_state->get_Phi() * RAD_TO_DEG );
241 static inline double get_pitch( void ) {
242 return( cur_fdm_state->get_Theta() );
245 double fgAPget_heading( void ) {
246 return( cur_fdm_state->get_Psi() * RAD_TO_DEG );
249 static inline double fgAPget_altitude( void ) {
250 return( cur_fdm_state->get_Altitude() * FEET_TO_METER );
253 static inline double fgAPget_climb( void ) {
254 // return in meters per minute
255 return( cur_fdm_state->get_Climb_Rate() * FEET_TO_METER * 60 );
258 static inline double get_sideslip( void ) {
259 return( cur_fdm_state->get_Beta() );
262 static inline double fgAPget_agl( void ) {
265 agl = cur_fdm_state->get_Altitude() * FEET_TO_METER
271 // End of copied section. ( thanks for the wheel :-)
272 double fgAPget_TargetLatitude( void ) {
273 fgAPDataPtr APData = APDataGlobal;
274 return APData->TargetLatitude;
277 double fgAPget_TargetLongitude( void ) {
278 fgAPDataPtr APData = APDataGlobal;
279 return APData->TargetLongitude;
282 double fgAPget_TargetHeading( void ) {
283 fgAPDataPtr APData = APDataGlobal;
284 return APData->TargetHeading;
287 double fgAPget_TargetDistance( void ) {
288 fgAPDataPtr APData = APDataGlobal;
289 return APData->TargetDistance;
292 double fgAPget_TargetAltitude( void ) {
293 fgAPDataPtr APData = APDataGlobal;
294 return APData->TargetAltitude;
297 char *fgAPget_TargetLatitudeStr( void ) {
298 fgAPDataPtr APData = APDataGlobal;
299 return APData->TargetLatitudeStr;
302 char *fgAPget_TargetLongitudeStr( void ) {
303 fgAPDataPtr APData = APDataGlobal;
304 return APData->TargetLongitudeStr;
307 char *fgAPget_TargetDistanceStr( void ) {
308 fgAPDataPtr APData = APDataGlobal;
309 return APData->TargetDistanceStr;
312 char *fgAPget_TargetHeadingStr( void ) {
313 fgAPDataPtr APData = APDataGlobal;
314 return APData->TargetHeadingStr;
317 char *fgAPget_TargetAltitudeStr( void ) {
318 fgAPDataPtr APData = APDataGlobal;
319 return APData->TargetAltitudeStr;
322 char *fgAPget_TargetLatLonStr( void ) {
323 fgAPDataPtr APData = APDataGlobal;
324 return APData->TargetLatLonStr;
327 bool fgAPWayPointEnabled( void )
331 APData = APDataGlobal;
333 // heading hold enabled?
334 return APData->waypoint_hold;
338 bool fgAPHeadingEnabled( void )
342 APData = APDataGlobal;
344 // heading hold enabled?
345 return APData->heading_hold;
348 bool fgAPAltitudeEnabled( void )
352 APData = APDataGlobal;
354 // altitude hold or terrain follow enabled?
355 return APData->altitude_hold;
358 bool fgAPTerrainFollowEnabled( void )
362 APData = APDataGlobal;
364 // altitude hold or terrain follow enabled?
365 return APData->terrain_follow ;
368 bool fgAPAutoThrottleEnabled( void )
372 APData = APDataGlobal;
374 // autothrottle enabled?
375 return APData->auto_throttle;
378 void fgAPAltitudeAdjust( double inc )
380 // Remove at a later date
381 fgAPDataPtr APData = APDataGlobal;
384 double target_alt, target_agl;
386 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
387 target_alt = APData->TargetAltitude * METER_TO_FEET;
388 target_agl = APData->TargetAGL * METER_TO_FEET;
390 target_alt = APData->TargetAltitude;
391 target_agl = APData->TargetAGL;
394 target_alt = ( int ) ( target_alt / inc ) * inc + inc;
395 target_agl = ( int ) ( target_agl / inc ) * inc + inc;
397 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
398 target_alt *= FEET_TO_METER;
399 target_agl *= FEET_TO_METER;
402 APData->TargetAltitude = target_alt;
403 APData->TargetAGL = target_agl;
405 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
406 target_alt *= METER_TO_FEET;
407 ApAltitudeDialogInput->setValue((float)target_alt);
408 MakeTargetAltitudeStr( APData, target_alt);
410 get_control_values();
413 void fgAPAltitudeSet( double new_altitude ) {
414 // Remove at a later date
415 fgAPDataPtr APData = APDataGlobal;
417 double target_alt = new_altitude;
419 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
420 target_alt = new_altitude * FEET_TO_METER;
422 if( target_alt < scenery.cur_elev )
423 target_alt = scenery.cur_elev;
425 APData->TargetAltitude = target_alt;
427 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
428 target_alt *= METER_TO_FEET;
429 ApAltitudeDialogInput->setValue((float)target_alt);
430 MakeTargetAltitudeStr( APData, target_alt);
432 get_control_values();
435 void fgAPHeadingAdjust( double inc ) {
436 fgAPDataPtr APData = APDataGlobal;
438 if ( APData->waypoint_hold )
439 APData->waypoint_hold = false;
441 double target = ( int ) ( APData->TargetHeading / inc ) * inc + inc;
443 APData->TargetHeading = NormalizeDegrees( target );
444 // following cast needed ambiguous plib
445 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
446 MakeTargetHeadingStr( APData, APData->TargetHeading );
447 get_control_values();
450 void fgAPHeadingSet( double new_heading ) {
451 fgAPDataPtr APData = APDataGlobal;
453 if ( APData->waypoint_hold )
454 APData->waypoint_hold = false;
456 new_heading = NormalizeDegrees( new_heading );
457 APData->TargetHeading = new_heading;
458 // following cast needed ambiguous plib
459 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
460 MakeTargetHeadingStr( APData, APData->TargetHeading );
461 get_control_values();
464 void fgAPAutoThrottleAdjust( double inc ) {
465 fgAPDataPtr APData = APDataGlobal;
467 double target = ( int ) ( APData->TargetSpeed / inc ) * inc + inc;
469 APData->TargetSpeed = target;
472 // THIS NEEDS IMPROVEMENT !!!!!!!!!!!!!
473 static int scan_number(char *s, double *new_value)
478 char *WordBufPtr = WordBuf;
483 *WordBufPtr++ = *cptr++;
485 while (isdigit(*cptr) ) {
486 *WordBufPtr++ = *cptr++;
490 *WordBufPtr++ = *cptr++; // put the '.' into the string
491 while (isdigit(*cptr)) {
492 *WordBufPtr++ = *cptr++;
497 sscanf(WordBuf, "%lf", new_value);
504 void ApHeadingDialog_Cancel(puObject *)
506 ApHeadingDialogInput->rejectInput();
507 FG_POP_PUI_DIALOG( ApHeadingDialog );
510 void ApHeadingDialog_OK (puObject *me)
515 ApHeadingDialogInput -> getValue( &c );
519 if( scan_number( c, &NewHeading ) )
521 if(!fgAPHeadingEnabled())
523 fgAPHeadingSet( NewHeading );
527 s += " is not a valid number.";
530 ApHeadingDialog_Cancel(me);
531 if( error ) mkDialog(s.c_str());
534 void NewHeading(puObject *cb)
536 // string ApHeadingLabel( "Enter New Heading" );
537 // ApHeadingDialogMessage -> setLabel(ApHeadingLabel.c_str());
538 ApHeadingDialogInput -> acceptInput();
539 FG_PUSH_PUI_DIALOG( ApHeadingDialog );
542 void NewHeadingInit(void)
544 // printf("NewHeadingInit\n");
545 char NewHeadingLabel[] = "Enter New Heading";
548 float heading = fgAPget_heading();
550 (puGetStringWidth( puGetDefaultLabelFont(), NewHeadingLabel ) /2 );
552 ApHeadingDialog = new puDialogBox (150, 50);
554 ApHeadingDialogFrame = new puFrame (0, 0, 260, 150);
556 ApHeadingDialogMessage = new puText (len, 110);
557 ApHeadingDialogMessage -> setDefaultValue (NewHeadingLabel);
558 ApHeadingDialogMessage -> getDefaultValue (&s);
559 ApHeadingDialogMessage -> setLabel (s);
561 ApHeadingDialogInput = new puInput ( 50, 70, 210, 100 );
562 ApHeadingDialogInput -> setValue ( heading );
564 ApHeadingDialogOkButton = new puOneShot (50, 10, 110, 50);
565 ApHeadingDialogOkButton -> setLegend (gui_msg_OK);
566 ApHeadingDialogOkButton -> makeReturnDefault (TRUE);
567 ApHeadingDialogOkButton -> setCallback (ApHeadingDialog_OK);
569 ApHeadingDialogCancelButton = new puOneShot (140, 10, 210, 50);
570 ApHeadingDialogCancelButton -> setLegend (gui_msg_CANCEL);
571 ApHeadingDialogCancelButton -> setCallback (ApHeadingDialog_Cancel);
574 FG_FINALIZE_PUI_DIALOG( ApHeadingDialog );
577 void ApAltitudeDialog_Cancel(puObject *)
579 ApAltitudeDialogInput -> rejectInput();
580 FG_POP_PUI_DIALOG( ApAltitudeDialog );
583 void ApAltitudeDialog_OK (puObject *me)
588 ApAltitudeDialogInput->getValue( &c );
592 if( scan_number( c, &NewAltitude) )
594 if(!(fgAPAltitudeEnabled()))
595 fgAPToggleAltitude();
596 fgAPAltitudeSet( NewAltitude );
600 s += " is not a valid number.";
603 ApAltitudeDialog_Cancel(me);
604 if( error ) mkDialog(s.c_str());
605 get_control_values();
608 void NewAltitude(puObject *cb)
610 ApAltitudeDialogInput -> acceptInput();
611 FG_PUSH_PUI_DIALOG( ApAltitudeDialog );
614 void NewAltitudeInit(void)
616 // printf("NewAltitudeInit\n");
617 char NewAltitudeLabel[] = "Enter New Altitude";
620 float alt = cur_fdm_state->get_Altitude();
622 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_METERS) {
623 alt *= FEET_TO_METER;
627 (puGetStringWidth( puGetDefaultLabelFont(), NewAltitudeLabel )/2);
629 // ApAltitudeDialog = new puDialogBox (150, 50);
630 ApAltitudeDialog = new puDialogBox (150, 200);
632 ApAltitudeDialogFrame = new puFrame (0, 0, 260, 150);
633 ApAltitudeDialogMessage = new puText (len, 110);
634 ApAltitudeDialogMessage -> setDefaultValue (NewAltitudeLabel);
635 ApAltitudeDialogMessage -> getDefaultValue (&s);
636 ApAltitudeDialogMessage -> setLabel (s);
638 ApAltitudeDialogInput = new puInput ( 50, 70, 210, 100 );
639 ApAltitudeDialogInput -> setValue ( alt );
640 // Uncomment the next line to have input active on startup
641 // ApAltitudeDialogInput -> acceptInput ( );
642 // cursor at begining or end of line ?
645 // ApAltitudeDialogInput -> setCursor ( len );
646 // ApAltitudeDialogInput -> setSelectRegion ( 5, 9 );
648 ApAltitudeDialogOkButton = new puOneShot (50, 10, 110, 50);
649 ApAltitudeDialogOkButton -> setLegend (gui_msg_OK);
650 ApAltitudeDialogOkButton -> makeReturnDefault (TRUE);
651 ApAltitudeDialogOkButton -> setCallback (ApAltitudeDialog_OK);
653 ApAltitudeDialogCancelButton = new puOneShot (140, 10, 210, 50);
654 ApAltitudeDialogCancelButton -> setLegend (gui_msg_CANCEL);
655 ApAltitudeDialogCancelButton -> setCallback (ApAltitudeDialog_Cancel);
658 FG_FINALIZE_PUI_DIALOG( ApAltitudeDialog );
661 /////// simple AutoPilot GAIN / LIMITS ADJUSTER
663 #define fgAP_CLAMP(val,min,max) \
664 ( (val) = (val) > (max) ? (max) : (val) < (min) ? (min) : (val) )
666 static void maxroll_adj( puObject *hs ) {
668 fgAPDataPtr APData = APDataGlobal;
670 hs-> getValue ( &val ) ;
671 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
672 // printf ( "maxroll_adj( %p ) %f %f\n", hs, val, MaxRollAdjust * val ) ;
673 APData-> MaxRoll = MaxRollAdjust * val;
674 sprintf( SliderText[ 0 ], "%05.2f", APData->MaxRoll );
675 APAdjustMaxRollText -> setLabel ( SliderText[ 0 ] ) ;
678 static void rollout_adj( puObject *hs ) {
680 fgAPDataPtr APData = APDataGlobal;
682 hs-> getValue ( &val ) ;
683 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
684 // printf ( "rollout_adj( %p ) %f %f\n", hs, val, RollOutAdjust * val ) ;
685 APData-> RollOut = RollOutAdjust * val;
686 sprintf( SliderText[ 1 ], "%05.2f", APData->RollOut );
687 APAdjustRollOutText -> setLabel ( SliderText[ 1 ] );
690 static void maxaileron_adj( puObject *hs ) {
693 APData = APDataGlobal;
695 hs-> getValue ( &val ) ;
696 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
697 // printf ( "maxaileron_adj( %p ) %f %f\n", hs, val, MaxAileronAdjust * val ) ;
698 APData-> MaxAileron = MaxAileronAdjust * val;
699 sprintf( SliderText[ 3 ], "%05.2f", APData->MaxAileron );
700 APAdjustMaxAileronText -> setLabel ( SliderText[ 3 ] );
703 static void rolloutsmooth_adj( puObject *hs ) {
705 fgAPDataPtr APData = APDataGlobal;
707 hs -> getValue ( &val ) ;
708 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
709 // printf ( "rolloutsmooth_adj( %p ) %f %f\n", hs, val, RollOutSmoothAdjust * val ) ;
710 APData->RollOutSmooth = RollOutSmoothAdjust * val;
711 sprintf( SliderText[ 2 ], "%5.2f", APData-> RollOutSmooth );
712 APAdjustRollOutSmoothText-> setLabel ( SliderText[ 2 ] );
716 static void goAwayAPAdjust (puObject *)
718 FG_POP_PUI_DIALOG( APAdjustDialog );
721 void cancelAPAdjust( puObject *self ) {
722 fgAPDataPtr APData = APDataGlobal;
724 APData-> MaxRoll = TmpMaxRollValue;
725 APData-> RollOut = TmpRollOutValue;
726 APData-> MaxAileron = TmpMaxAileronValue;
727 APData-> RollOutSmooth = TmpRollOutSmoothValue;
729 goAwayAPAdjust(self);
732 void resetAPAdjust( puObject *self ) {
733 fgAPDataPtr APData = APDataGlobal;
735 APData-> MaxRoll = MaxRollAdjust / 2;
736 APData-> RollOut = RollOutAdjust / 2;
737 APData-> MaxAileron = MaxAileronAdjust / 2;
738 APData-> RollOutSmooth = RollOutSmoothAdjust / 2;
740 FG_POP_PUI_DIALOG( APAdjustDialog );
745 void fgAPAdjust( puObject * ) {
746 fgAPDataPtr APData = APDataGlobal;
748 TmpMaxRollValue = APData-> MaxRoll;
749 TmpRollOutValue = APData-> RollOut;
750 TmpMaxAileronValue = APData-> MaxAileron;
751 TmpRollOutSmoothValue = APData-> RollOutSmooth;
753 MaxRollValue = APData-> MaxRoll / MaxRollAdjust;
754 RollOutValue = APData-> RollOut / RollOutAdjust;
755 MaxAileronValue = APData-> MaxAileron / MaxAileronAdjust;
756 RollOutSmoothValue = APData-> RollOutSmooth / RollOutSmoothAdjust;
758 APAdjustHS0-> setValue ( MaxRollValue ) ;
759 APAdjustHS1-> setValue ( RollOutValue ) ;
760 APAdjustHS2-> setValue ( RollOutSmoothValue ) ;
761 APAdjustHS3-> setValue ( MaxAileronValue ) ;
763 FG_PUSH_PUI_DIALOG( APAdjustDialog );
766 // Done once at system initialization
767 void fgAPAdjustInit( void ) {
769 // printf("fgAPAdjustInit\n");
770 #define HORIZONTAL FALSE
774 int DialogWidth = 230;
776 char Label[] = "AutoPilot Adjust";
779 fgAPDataPtr APData = APDataGlobal;
781 int labelX = (DialogWidth / 2) -
782 (puGetStringWidth( puGetDefaultLabelFont(), Label ) / 2);
783 labelX -= 30; // KLUDGEY
788 int slider_width = 210;
789 int slider_title_x = 15;
790 int slider_value_x = 160;
791 float slider_delta = 0.1f;
793 TmpMaxRollValue = APData-> MaxRoll;
794 TmpRollOutValue = APData-> RollOut;
795 TmpMaxAileronValue = APData-> MaxAileron;
796 TmpRollOutSmoothValue = APData-> RollOutSmooth;
798 MaxRollValue = APData-> MaxRoll / MaxRollAdjust;
799 RollOutValue = APData-> RollOut / RollOutAdjust;
800 MaxAileronValue = APData-> MaxAileron / MaxAileronAdjust;
801 RollOutSmoothValue = APData-> RollOutSmooth / RollOutSmoothAdjust;
803 puGetDefaultFonts ( &APAdjustLegendFont, &APAdjustLabelFont );
804 APAdjustDialog = new puDialogBox ( DialogX, DialogY ); {
805 int horiz_slider_height = puGetStringHeight (APAdjustLabelFont) +
806 puGetStringDescender (APAdjustLabelFont) +
807 PUSTR_TGAP + PUSTR_BGAP + 5;
809 APAdjustFrame = new puFrame ( 0, 0,
810 DialogWidth, 85 + nSliders * horiz_slider_height );
812 APAdjustDialogMessage = new puText ( labelX, 52 + nSliders * horiz_slider_height );
813 APAdjustDialogMessage -> setDefaultValue ( Label );
814 APAdjustDialogMessage -> getDefaultValue ( &s );
815 APAdjustDialogMessage -> setLabel ( s );
817 APAdjustHS0 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
818 APAdjustHS0-> setDelta ( slider_delta ) ;
819 APAdjustHS0-> setValue ( MaxRollValue ) ;
820 APAdjustHS0-> setCBMode ( PUSLIDER_DELTA ) ;
821 APAdjustHS0-> setCallback ( maxroll_adj ) ;
823 sprintf( SliderText[ 0 ], "%05.2f", APData->MaxRoll );
824 APAdjustMaxRollTitle = new puText ( slider_title_x, slider_y ) ;
825 APAdjustMaxRollTitle-> setDefaultValue ( "MaxRoll" ) ;
826 APAdjustMaxRollTitle-> getDefaultValue ( &s ) ;
827 APAdjustMaxRollTitle-> setLabel ( s ) ;
828 APAdjustMaxRollText = new puText ( slider_value_x, slider_y ) ;
829 APAdjustMaxRollText-> setLabel ( SliderText[ 0 ] ) ;
831 slider_y += horiz_slider_height;
833 APAdjustHS1 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
834 APAdjustHS1-> setDelta ( slider_delta ) ;
835 APAdjustHS1-> setValue ( RollOutValue ) ;
836 APAdjustHS1-> setCBMode ( PUSLIDER_DELTA ) ;
837 APAdjustHS1-> setCallback ( rollout_adj ) ;
839 sprintf( SliderText[ 1 ], "%05.2f", APData->RollOut );
840 APAdjustRollOutTitle = new puText ( slider_title_x, slider_y ) ;
841 APAdjustRollOutTitle-> setDefaultValue ( "AdjustRollOut" ) ;
842 APAdjustRollOutTitle-> getDefaultValue ( &s ) ;
843 APAdjustRollOutTitle-> setLabel ( s ) ;
844 APAdjustRollOutText = new puText ( slider_value_x, slider_y ) ;
845 APAdjustRollOutText-> setLabel ( SliderText[ 1 ] );
847 slider_y += horiz_slider_height;
849 APAdjustHS2 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
850 APAdjustHS2-> setDelta ( slider_delta ) ;
851 APAdjustHS2-> setValue ( RollOutSmoothValue ) ;
852 APAdjustHS2-> setCBMode ( PUSLIDER_DELTA ) ;
853 APAdjustHS2-> setCallback ( rolloutsmooth_adj ) ;
855 sprintf( SliderText[ 2 ], "%5.2f", APData->RollOutSmooth );
856 APAdjustRollOutSmoothTitle = new puText ( slider_title_x, slider_y ) ;
857 APAdjustRollOutSmoothTitle-> setDefaultValue ( "RollOutSmooth" ) ;
858 APAdjustRollOutSmoothTitle-> getDefaultValue ( &s ) ;
859 APAdjustRollOutSmoothTitle-> setLabel ( s ) ;
860 APAdjustRollOutSmoothText = new puText ( slider_value_x, slider_y ) ;
861 APAdjustRollOutSmoothText-> setLabel ( SliderText[ 2 ] );
863 slider_y += horiz_slider_height;
865 APAdjustHS3 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
866 APAdjustHS3-> setDelta ( slider_delta ) ;
867 APAdjustHS3-> setValue ( MaxAileronValue ) ;
868 APAdjustHS3-> setCBMode ( PUSLIDER_DELTA ) ;
869 APAdjustHS3-> setCallback ( maxaileron_adj ) ;
871 sprintf( SliderText[ 3 ], "%05.2f", APData->MaxAileron );
872 APAdjustMaxAileronTitle = new puText ( slider_title_x, slider_y ) ;
873 APAdjustMaxAileronTitle-> setDefaultValue ( "MaxAileron" ) ;
874 APAdjustMaxAileronTitle-> getDefaultValue ( &s ) ;
875 APAdjustMaxAileronTitle-> setLabel ( s ) ;
876 APAdjustMaxAileronText = new puText ( slider_value_x, slider_y ) ;
877 APAdjustMaxAileronText-> setLabel ( SliderText[ 3 ] );
879 APAdjustOkButton = new puOneShot ( 10, 10, 60, 50 );
880 APAdjustOkButton-> setLegend ( gui_msg_OK );
881 APAdjustOkButton-> makeReturnDefault ( TRUE );
882 APAdjustOkButton-> setCallback ( goAwayAPAdjust );
884 APAdjustCancelButton = new puOneShot ( 70, 10, 150, 50 );
885 APAdjustCancelButton-> setLegend ( gui_msg_CANCEL );
886 APAdjustCancelButton-> setCallback ( cancelAPAdjust );
888 APAdjustResetButton = new puOneShot ( 160, 10, 220, 50 );
889 APAdjustResetButton-> setLegend ( gui_msg_RESET );
890 APAdjustResetButton-> setCallback ( resetAPAdjust );
892 FG_FINALIZE_PUI_DIALOG( APAdjustDialog );
897 // Simple Dialog to input Target Airport
898 void TgtAptDialog_Cancel(puObject *)
900 FG_POP_PUI_DIALOG( TgtAptDialog );
903 void TgtAptDialog_OK (puObject *)
906 APData = APDataGlobal;
909 // FGTime *t = FGTime::cur_time_params;
910 // int PauseMode = t->getPause();
912 // t->togglePauseMode();
915 TgtAptDialogInput->getValue(&s);
918 TgtAptDialog_Cancel( NULL );
920 if ( TgtAptId.length() ) {
921 // set initial position from TgtAirport id
923 FGPath path( current_options.get_fg_root() );
924 path.append( "Airports" );
925 path.append( "simple.gdbm" );
926 FGAirports airports( path.c_str() );
929 FG_LOG( FG_GENERAL, FG_INFO,
930 "Attempting to set starting position from airport code "
933 if ( airports.search( TgtAptId, &a ) )
935 double course, reverse, distance;
936 // fgAPset_tgt_airport_id( TgtAptId.c_str() );
937 current_options.set_airport_id( TgtAptId.c_str() );
938 sprintf( NewTgtAirportId, "%s", TgtAptId.c_str() );
940 APData->TargetLatitude = a.latitude; // * DEG_TO_RAD;
941 APData->TargetLongitude = a.longitude; // * DEG_TO_RAD;
942 MakeTargetLatLonStr( APData,
943 APData->TargetLatitude,
944 APData->TargetLongitude);
946 APData->old_lat = fgAPget_latitude();
947 APData->old_lon = fgAPget_longitude();
949 // need to test for iter
950 if( ! geo_inverse_wgs_84( fgAPget_altitude(),
953 APData->TargetLatitude,
954 APData->TargetLongitude,
958 APData->TargetHeading = course;
959 MakeTargetHeadingStr( APData, APData->TargetHeading );
960 APData->TargetDistance = distance;
961 MakeTargetDistanceStr( APData, distance );
962 // This changes the AutoPilot Heading
963 // following cast needed
964 ApHeadingDialogInput->
965 setValue((float)APData->TargetHeading);
967 APData->waypoint_hold = true ;
968 APData->heading_hold = true;
971 TgtAptId += " not in database.";
972 mkDialog(TgtAptId.c_str());
975 get_control_values();
976 // if( PauseMode != t->getPause() )
977 // t->togglePauseMode();
980 void TgtAptDialog_Reset(puObject *)
982 // strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
983 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
984 TgtAptDialogInput->setValue ( NewTgtAirportId );
985 TgtAptDialogInput->setCursor( 0 ) ;
988 void NewTgtAirport(puObject *cb)
990 // strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
991 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
992 TgtAptDialogInput->setValue( NewTgtAirportId );
994 FG_PUSH_PUI_DIALOG( TgtAptDialog );
997 void NewTgtAirportInit(void)
999 FG_LOG( FG_AUTOPILOT, FG_INFO, " enter NewTgtAirportInit()" );
1000 // fgAPset_tgt_airport_id( current_options.get_airport_id() );
1001 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
1002 FG_LOG( FG_AUTOPILOT, FG_INFO, " NewTgtAirportId " << NewTgtAirportId );
1003 // printf(" NewTgtAirportId %s\n", NewTgtAirportId);
1004 int len = 150 - puGetStringWidth( puGetDefaultLabelFont(),
1005 NewTgtAirportLabel ) / 2;
1007 TgtAptDialog = new puDialogBox (150, 50);
1009 TgtAptDialogFrame = new puFrame (0,0,350, 150);
1010 TgtAptDialogMessage = new puText (len, 110);
1011 TgtAptDialogMessage -> setLabel (NewTgtAirportLabel);
1013 TgtAptDialogInput = new puInput (50, 70, 300, 100);
1014 TgtAptDialogInput -> setValue (NewTgtAirportId);
1015 TgtAptDialogInput -> acceptInput();
1017 TgtAptDialogOkButton = new puOneShot (50, 10, 110, 50);
1018 TgtAptDialogOkButton -> setLegend (gui_msg_OK);
1019 TgtAptDialogOkButton -> setCallback (TgtAptDialog_OK);
1020 TgtAptDialogOkButton -> makeReturnDefault(TRUE);
1022 TgtAptDialogCancelButton = new puOneShot (140, 10, 210, 50);
1023 TgtAptDialogCancelButton -> setLegend (gui_msg_CANCEL);
1024 TgtAptDialogCancelButton -> setCallback (TgtAptDialog_Cancel);
1026 TgtAptDialogResetButton = new puOneShot (240, 10, 300, 50);
1027 TgtAptDialogResetButton -> setLegend (gui_msg_RESET);
1028 TgtAptDialogResetButton -> setCallback (TgtAptDialog_Reset);
1030 FG_FINALIZE_PUI_DIALOG( TgtAptDialog );
1031 printf("leave NewTgtAirportInit()");
1035 // Finally actual guts of AutoPilot
1036 void fgAPInit( fgAIRCRAFT *current_aircraft ) {
1039 FG_LOG( FG_AUTOPILOT, FG_INFO, "Init AutoPilot Subsystem" );
1041 APData = ( fgAPDataPtr ) calloc( sizeof( fgAPData ), 1 );
1043 if ( APData == NULL ) {
1044 // I couldn't get the mem. Dying
1045 FG_LOG( FG_AUTOPILOT, FG_ALERT, "No ram for Autopilot. Dying." );
1049 FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot allocated " );
1051 APData->waypoint_hold = false ; // turn the target hold off
1052 APData->heading_hold = false ; // turn the heading hold off
1053 APData->altitude_hold = false ; // turn the altitude hold off
1055 // Initialize target location to startup location
1056 // FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot setting startup location" );
1058 APData->TargetLatitude = fgAPget_latitude();
1060 APData->TargetLongitude = fgAPget_longitude();
1062 // FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot setting TargetLatitudeStr" );
1063 MakeTargetLatLonStr( APData, APData->TargetLatitude, APData->TargetLongitude);
1065 APData->TargetHeading = 0.0; // default direction, due north
1066 APData->TargetAltitude = 3000; // default altitude in meters
1067 APData->alt_error_accum = 0.0;
1069 MakeTargetAltitudeStr( APData, 3000.0);
1070 MakeTargetHeadingStr( APData, 0.0 );
1072 // These eventually need to be read from current_aircaft somehow.
1076 // the maximum roll, in Deg
1077 APData->MaxRoll = 7;
1078 // the deg from heading to start rolling out at, in Deg
1079 APData->RollOut = 30;
1080 // how far can I move the aleron from center.
1081 APData->MaxAileron = .1;
1082 // Smoothing distance for alerion control
1083 APData->RollOutSmooth = 10;
1086 // the maximum roll, in Deg
1087 APData->MaxRoll = 20;
1089 // the deg from heading to start rolling out at, in Deg
1090 APData->RollOut = 20;
1092 // how far can I move the aleron from center.
1093 APData->MaxAileron = .2;
1095 // Smoothing distance for alerion control
1096 APData->RollOutSmooth = 10;
1098 //Remove at a later date
1099 APDataGlobal = APData;
1101 // Hardwired for now should be in options
1102 // 25% max control variablilty 0.5 / 2.0
1103 APData->disengage_threshold = 1.0;
1105 #if !defined( USING_SLIDER_CLASS )
1106 MaxRollAdjust = 2 * APData->MaxRoll;
1107 RollOutAdjust = 2 * APData->RollOut;
1108 MaxAileronAdjust = 2 * APData->MaxAileron;
1109 RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
1110 #endif // !defined( USING_SLIDER_CLASS )
1112 get_control_values();
1114 // FG_LOG( FG_AUTOPILOT, FG_INFO, " calling NewTgtAirportInit" );
1115 NewTgtAirportInit();
1122 void fgAPReset( void ) {
1123 fgAPDataPtr APData = APDataGlobal;
1125 if ( fgAPTerrainFollowEnabled() )
1126 fgAPToggleTerrainFollow( );
1128 if ( fgAPAltitudeEnabled() )
1129 fgAPToggleAltitude();
1131 if ( fgAPHeadingEnabled() )
1132 fgAPToggleHeading();
1134 if ( fgAPAutoThrottleEnabled() )
1135 fgAPToggleAutoThrottle();
1137 APData->TargetHeading = 0.0; // default direction, due north
1138 MakeTargetHeadingStr( APData, APData->TargetHeading );
1140 APData->TargetAltitude = 3000; // default altitude in meters
1141 MakeTargetAltitudeStr( APData, 3000);
1143 APData->alt_error_accum = 0.0;
1146 get_control_values();
1148 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
1150 APData->TargetLatitude = fgAPget_latitude();
1151 APData->TargetLongitude = fgAPget_longitude();
1152 MakeTargetLatLonStr( APData,
1153 APData->TargetLatitude,
1154 APData->TargetLongitude);
1158 int fgAPRun( void ) {
1159 // Remove the following lines when the calling funcitons start
1160 // passing in the data pointer
1164 APData = APDataGlobal;
1167 // get control settings
1168 double aileron = controls.get_aileron();
1169 double elevator = controls.get_elevator();
1170 double elevator_trim = controls.get_elevator_trim();
1171 double rudder = controls.get_rudder();
1173 double lat = fgAPget_latitude();
1174 double lon = fgAPget_longitude();
1176 #ifdef FG_FORCE_AUTO_DISENGAGE
1177 // see if somebody else has changed them
1178 if( fabs(aileron - APData->old_aileron) > APData->disengage_threshold ||
1179 fabs(elevator - APData->old_elevator) > APData->disengage_threshold ||
1180 fabs(elevator_trim - APData->old_elevator_trim) >
1181 APData->disengage_threshold ||
1182 fabs(rudder - APData->old_rudder) > APData->disengage_threshold )
1184 // if controls changed externally turn autopilot off
1185 APData->waypoint_hold = false ; // turn the target hold off
1186 APData->heading_hold = false ; // turn the heading hold off
1187 APData->altitude_hold = false ; // turn the altitude hold off
1188 APData->terrain_follow = false; // turn the terrain_follow hold off
1189 // APData->auto_throttle = false; // turn the auto_throttle off
1191 // stash this runs control settings
1192 APData->old_aileron = aileron;
1193 APData->old_elevator = elevator;
1194 APData->old_elevator_trim = elevator_trim;
1195 APData->old_rudder = rudder;
1201 // waypoint hold enabled?
1202 if ( APData->waypoint_hold == true )
1204 double wp_course, wp_reverse, wp_distance;
1206 #ifdef DO_fgAP_CORRECTED_COURSE
1207 // compute course made good
1208 // this needs lots of special casing before use
1209 double course, reverse, distance, corrected_course;
1210 // need to test for iter
1211 geo_inverse_wgs_84( 0, //fgAPget_altitude(),
1219 #endif // DO_fgAP_CORRECTED_COURSE
1221 // compute course to way_point
1222 // need to test for iter
1223 if( ! geo_inverse_wgs_84( 0, //fgAPget_altitude(),
1226 APData->TargetLatitude,
1227 APData->TargetLongitude,
1232 #ifdef DO_fgAP_CORRECTED_COURSE
1233 corrected_course = course - wp_course;
1234 if( fabs(corrected_course) > 0.1 )
1235 printf("fgAP: course %f wp_course %f %f %f\n",
1236 course, wp_course, fabs(corrected_course), distance );
1237 #endif // DO_fgAP_CORRECTED_COURSE
1239 if ( wp_distance > 100 ) {
1240 // corrected_course = course - wp_course;
1241 APData->TargetHeading = NormalizeDegrees(wp_course);
1243 printf("APData->distance(%f) to close\n", wp_distance);
1244 // Real Close -- set heading hold to current heading
1245 // and Ring the arival bell !!
1246 NewTgtAirport(NULL);
1247 APData->waypoint_hold = false;
1249 APData->TargetHeading = fgAPget_heading();
1251 MakeTargetHeadingStr( APData, APData->TargetHeading );
1252 // Force this just in case
1253 APData->TargetDistance = wp_distance;
1254 MakeTargetDistanceStr( APData, wp_distance );
1255 // This changes the AutoPilot Heading Read Out
1256 // following cast needed
1257 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
1259 APData->heading_hold = true;
1262 // heading hold enabled?
1263 if ( APData->heading_hold == true ) {
1270 NormalizeDegrees( APData->TargetHeading - fgAPget_heading() );
1271 // figure out how far off we are from desired heading
1273 // Now it is time to deterime how far we should be rolled.
1274 FG_LOG( FG_AUTOPILOT, FG_DEBUG, "RelHeading: " << RelHeading );
1277 // Check if we are further from heading than the roll out point
1278 if ( fabs( RelHeading ) > APData->RollOut ) {
1279 // set Target Roll to Max in desired direction
1280 if ( RelHeading < 0 ) {
1281 TargetRoll = 0 - APData->MaxRoll;
1283 TargetRoll = APData->MaxRoll;
1286 // We have to calculate the Target roll
1288 // This calculation engine thinks that the Target roll
1289 // should be a line from (RollOut,MaxRoll) to (-RollOut,
1290 // -MaxRoll) I hope this works well. If I get ambitious
1291 // some day this might become a fancier curve or
1294 TargetRoll = LinearExtrapolate( RelHeading, -APData->RollOut,
1295 -APData->MaxRoll, APData->RollOut,
1299 // Target Roll has now been Found.
1301 // Compare Target roll to Current Roll, Generate Rel Roll
1303 FG_LOG( FG_COCKPIT, FG_BULK, "TargetRoll: " << TargetRoll );
1305 RelRoll = NormalizeDegrees( TargetRoll - fgAPget_roll() );
1307 // Check if we are further from heading than the roll out smooth point
1308 if ( fabs( RelRoll ) > APData->RollOutSmooth ) {
1309 // set Target Roll to Max in desired direction
1310 if ( RelRoll < 0 ) {
1311 AileronSet = 0 - APData->MaxAileron;
1313 AileronSet = APData->MaxAileron;
1316 AileronSet = LinearExtrapolate( RelRoll, -APData->RollOutSmooth,
1317 -APData->MaxAileron,
1318 APData->RollOutSmooth,
1319 APData->MaxAileron );
1322 controls.set_aileron( AileronSet );
1323 controls.set_rudder( AileronSet / 2.0 );
1324 // controls.set_rudder( 0.0 );
1327 // altitude hold or terrain follow enabled?
1328 if ( APData->altitude_hold || APData->terrain_follow ) {
1329 double speed, max_climb, error;
1330 double prop_error, int_error;
1331 double prop_adj, int_adj, total_adj;
1333 if ( APData->altitude_hold ) {
1334 // normal altitude hold
1335 APData->TargetClimbRate =
1336 ( APData->TargetAltitude - fgAPget_altitude() ) * 8.0;
1337 } else if ( APData->terrain_follow ) {
1338 // brain dead ground hugging with no look ahead
1339 APData->TargetClimbRate =
1340 ( APData->TargetAGL - fgAPget_agl() ) * 16.0;
1341 // cout << "target agl = " << APData->TargetAGL
1342 // << " current agl = " << fgAPget_agl()
1343 // << " target climb rate = " << APData->TargetClimbRate
1346 // just try to zero out rate of climb ...
1347 APData->TargetClimbRate = 0.0;
1350 speed = get_speed();
1352 if ( speed < min_climb ) {
1354 } else if ( speed < best_climb ) {
1355 max_climb = ((best_climb - min_climb) - (best_climb - speed))
1357 / (best_climb - min_climb);
1359 max_climb = ( speed - best_climb ) * 10.0 + ideal_climb_rate;
1362 // this first one could be optional if we wanted to allow
1363 // better climb performance assuming we have the airspeed to
1365 if ( APData->TargetClimbRate > ideal_climb_rate ) {
1366 APData->TargetClimbRate = ideal_climb_rate;
1369 if ( APData->TargetClimbRate > max_climb ) {
1370 APData->TargetClimbRate = max_climb;
1373 if ( APData->TargetClimbRate < -ideal_climb_rate ) {
1374 APData->TargetClimbRate = -ideal_climb_rate;
1377 error = fgAPget_climb() - APData->TargetClimbRate;
1378 // cout << "climb rate = " << fgAPget_climb()
1379 // << " error = " << error << endl;
1381 // accumulate the error under the curve ... this really should
1383 APData->alt_error_accum += error;
1385 // calculate integral error, and adjustment amount
1386 int_error = APData->alt_error_accum;
1387 // printf("error = %.2f int_error = %.2f\n", error, int_error);
1388 int_adj = int_error / 8000.0;
1390 // caclulate proportional error
1392 prop_adj = prop_error / 2000.0;
1394 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
1395 // if ( total_adj > 0.6 ) {
1397 // } else if ( total_adj < -0.2 ) {
1398 // total_adj = -0.2;
1400 if ( total_adj > 1.0 ) {
1402 } else if ( total_adj < -1.0 ) {
1406 controls.set_elevator( total_adj );
1409 // auto throttle enabled?
1410 if ( APData->auto_throttle ) {
1412 double prop_error, int_error;
1413 double prop_adj, int_adj, total_adj;
1415 error = APData->TargetSpeed - get_speed();
1417 // accumulate the error under the curve ... this really should
1419 APData->speed_error_accum += error;
1420 if ( APData->speed_error_accum > 2000.0 ) {
1421 APData->speed_error_accum = 2000.0;
1423 else if ( APData->speed_error_accum < -2000.0 ) {
1424 APData->speed_error_accum = -2000.0;
1427 // calculate integral error, and adjustment amount
1428 int_error = APData->speed_error_accum;
1430 // printf("error = %.2f int_error = %.2f\n", error, int_error);
1431 int_adj = int_error / 200.0;
1433 // caclulate proportional error
1435 prop_adj = 0.5 + prop_error / 50.0;
1437 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
1438 if ( total_adj > 1.0 ) {
1441 else if ( total_adj < 0.0 ) {
1445 controls.set_throttle( FGControls::ALL_ENGINES, total_adj );
1448 #ifdef THIS_CODE_IS_NOT_USED
1449 if (APData->Mode == 2) // Glide slope hold
1454 // First, calculate Relative slope and normalize it
1455 RelSlope = NormalizeDegrees( APData->TargetSlope - get_pitch());
1457 // Now calculate the elevator offset from current angle
1458 if ( abs(RelSlope) > APData->SlopeSmooth )
1460 if ( RelSlope < 0 ) // set RelElevator to max in the correct direction
1461 RelElevator = -APData->MaxElevator;
1463 RelElevator = APData->MaxElevator;
1467 RelElevator = LinearExtrapolate(RelSlope,-APData->SlopeSmooth,-APData->MaxElevator,APData->SlopeSmooth,APData->MaxElevator);
1470 fgElevMove(RelElevator);
1473 #endif // THIS_CODE_IS_NOT_USED
1475 // stash this runs control settings
1476 // get_control_values();
1477 APData->old_aileron = controls.get_aileron();
1478 APData->old_elevator = controls.get_elevator();
1479 APData->old_elevator_trim = controls.get_elevator_trim();
1480 APData->old_rudder = controls.get_rudder();
1482 // for cross track error
1483 APData->old_lat = lat;
1484 APData->old_lon = lon;
1491 void fgAPSetMode( int mode)
1493 //Remove the following line when the calling funcitons start passing in the data pointer
1496 APData = APDataGlobal;
1499 fgPrintf( FG_COCKPIT, FG_INFO, "APSetMode : %d\n", mode );
1501 APData->Mode = mode; // set the new mode
1505 void fgAPset_tgt_airport_id( const string id ) {
1506 FG_LOG( FG_AUTOPILOT, FG_INFO, "entering fgAPset_tgt_airport_id " << id );
1508 APData = APDataGlobal;
1509 APData->tgt_airport_id = id;
1510 FG_LOG( FG_AUTOPILOT, FG_INFO, "leaving fgAPset_tgt_airport_id "
1511 << APData->tgt_airport_id );
1514 string fgAPget_tgt_airport_id( void ) {
1515 fgAPDataPtr APData = APDataGlobal;
1516 return APData->tgt_airport_id;
1520 void fgAPToggleHeading( void ) {
1521 // Remove at a later date
1524 APData = APDataGlobal;
1527 if ( APData->heading_hold || APData->waypoint_hold ) {
1528 // turn off heading hold
1529 APData->heading_hold = false;
1530 APData->waypoint_hold = false;
1532 // turn on heading hold, lock at current heading
1533 APData->heading_hold = true;
1534 APData->TargetHeading = fgAPget_heading();
1535 MakeTargetHeadingStr( APData, APData->TargetHeading );
1536 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
1539 get_control_values();
1540 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetHeading: ("
1541 << APData->heading_hold << ") " << APData->TargetHeading );
1544 void fgAPToggleWayPoint( void ) {
1545 // Remove at a later date
1548 APData = APDataGlobal;
1551 if ( APData->waypoint_hold ) {
1552 // turn off location hold
1553 APData->waypoint_hold = false;
1554 // set heading hold to current heading
1555 // APData->heading_hold = true;
1556 APData->TargetHeading = fgAPget_heading();
1558 double course, reverse, distance;
1559 // turn on location hold
1560 // turn on heading hold
1561 APData->old_lat = fgAPget_latitude();
1562 APData->old_lon = fgAPget_longitude();
1564 // need to test for iter
1565 if(!geo_inverse_wgs_84( fgAPget_altitude(),
1567 fgAPget_longitude(),
1568 APData->TargetLatitude,
1569 APData->TargetLongitude,
1573 APData->TargetHeading = course;
1574 APData->TargetDistance = distance;
1575 MakeTargetDistanceStr( APData, distance );
1578 APData->waypoint_hold = true;
1579 APData->heading_hold = true;
1582 // This changes the AutoPilot Heading
1583 // following cast needed
1584 ApHeadingDialogInput->setValue ((float)APData->TargetHeading );
1585 MakeTargetHeadingStr( APData, APData->TargetHeading );
1587 get_control_values();
1589 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetLocation: ( "
1590 << APData->waypoint_hold << " "
1591 << APData->TargetLatitude << " "
1592 << APData->TargetLongitude << " ) "
1597 void fgAPToggleAltitude( void ) {
1598 // Remove at a later date
1601 APData = APDataGlobal;
1604 if ( APData->altitude_hold ) {
1605 // turn off altitude hold
1606 APData->altitude_hold = false;
1608 // turn on altitude hold, lock at current altitude
1609 APData->altitude_hold = true;
1610 APData->terrain_follow = false;
1611 APData->TargetAltitude = fgAPget_altitude();
1612 APData->alt_error_accum = 0.0;
1613 // alt_error_queue.erase( alt_error_queue.begin(),
1614 // alt_error_queue.end() );
1615 float target_alt = APData->TargetAltitude;
1616 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
1617 target_alt *= METER_TO_FEET;
1619 ApAltitudeDialogInput->setValue(target_alt);
1620 MakeTargetAltitudeStr( APData, target_alt);
1623 get_control_values();
1624 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAltitude: ("
1625 << APData->altitude_hold << ") " << APData->TargetAltitude );
1629 void fgAPToggleAutoThrottle ( void ) {
1630 // Remove at a later date
1633 APData = APDataGlobal;
1636 if ( APData->auto_throttle ) {
1637 // turn off altitude hold
1638 APData->auto_throttle = false;
1640 // turn on terrain follow, lock at current agl
1641 APData->auto_throttle = true;
1642 APData->TargetSpeed = get_speed();
1643 APData->speed_error_accum = 0.0;
1646 get_control_values();
1647 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAutoThrottle: ("
1648 << APData->auto_throttle << ") " << APData->TargetSpeed );
1651 void fgAPToggleTerrainFollow( void ) {
1652 // Remove at a later date
1655 APData = APDataGlobal;
1658 if ( APData->terrain_follow ) {
1659 // turn off altitude hold
1660 APData->terrain_follow = false;
1662 // turn on terrain follow, lock at current agl
1663 APData->terrain_follow = true;
1664 APData->altitude_hold = false;
1665 APData->TargetAGL = fgAPget_agl();
1666 APData->alt_error_accum = 0.0;
1668 get_control_values();
1670 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetTerrainFollow: ("
1671 << APData->terrain_follow << ") " << APData->TargetAGL );
1674 static double NormalizeDegrees( double Input ) {
1675 // normalize the input to the range (-180,180]
1676 // Input should not be greater than -360 to 360.
1677 // Current rules send the output to an undefined state.
1681 else if ( Input <= -180 )
1682 while ( Input <= -180 )
1687 static double LinearExtrapolate( double x, double x1, double y1, double x2, double y2 ) {
1688 // This procedure extrapolates the y value for the x posistion on a line defined by x1,y1; x2,y2
1689 //assert(x1 != x2); // Divide by zero error. Cold abort for now
1692 // static double y = 0.0;
1693 // double dx = x2 -x1;
1694 // if( (dx < -FG_EPSILON ) || ( dx > FG_EPSILON ) )
1697 double m, b, y; // the constants to find in y=mx+b
1700 m = ( y2 - y1 ) / ( x2 - x1 ); // calculate the m
1702 b = y1 - m * x1; // calculate the b
1704 y = m * x + b; // the final calculation