1 // autopilot.cxx -- autopilot subsystem
3 // Written by Jeff Goeke-Smith, started April 1998.
5 // Copyright (C) 1998 Jeff Goeke-Smith, jgoeke@voyager.net
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.
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.
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.
31 #include <Scenery/scenery.hxx>
33 #include "autopilot.hxx"
35 #include <Include/fg_constants.h>
36 #include <Debug/logstream.hxx>
37 #include <Airports/simple.hxx>
39 #include <Main/fg_init.hxx>
40 #include <Main/options.hxx>
41 #include <Time/fg_time.hxx>
44 #define mySlider puSlider
46 /// These statics will eventually go into the class
47 /// they are just here while I am experimenting -- NHV :-)
48 // AutoPilot Gain Adjuster members
49 static double MaxRollAdjust; // MaxRollAdjust = 2 * APData->MaxRoll;
50 static double RollOutAdjust; // RollOutAdjust = 2 * APData->RollOut;
51 static double MaxAileronAdjust; // MaxAileronAdjust = 2 * APData->MaxAileron;
52 static double RollOutSmoothAdjust; // RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
54 static float MaxRollValue; // 0.1 -> 1.0
55 static float RollOutValue;
56 static float MaxAileronValue;
57 static float RollOutSmoothValue;
59 static float TmpMaxRollValue; // for cancel operation
60 static float TmpRollOutValue;
61 static float TmpMaxAileronValue;
62 static float TmpRollOutSmoothValue;
64 static puDialogBox *APAdjustDialog;
65 static puFrame *APAdjustFrame;
66 static puText *APAdjustDialogMessage;
67 static puFont APAdjustLegendFont;
68 static puFont APAdjustLabelFont;
70 static puOneShot *APAdjustOkButton;
71 static puOneShot *APAdjustResetButton;
72 static puOneShot *APAdjustCancelButton;
74 //static puButton *APAdjustDragButton;
76 static puText *APAdjustMaxRollTitle;
77 static puText *APAdjustRollOutTitle;
78 static puText *APAdjustMaxAileronTitle;
79 static puText *APAdjustRollOutSmoothTitle;
81 static puText *APAdjustMaxAileronText;
82 static puText *APAdjustMaxRollText;
83 static puText *APAdjustRollOutText;
84 static puText *APAdjustRollOutSmoothText;
86 static mySlider *APAdjustHS0;
87 static mySlider *APAdjustHS1;
88 static mySlider *APAdjustHS2;
89 static mySlider *APAdjustHS3;
91 static char SliderText[ 4 ][ 8 ];
93 ///////// AutoPilot New Heading Dialog
95 static puDialogBox *ApHeadingDialog;
96 static puFrame *ApHeadingDialogFrame;
97 static puText *ApHeadingDialogMessage;
98 static puInput *ApHeadingDialogInput;
99 static puOneShot *ApHeadingDialogOkButton;
100 static puOneShot *ApHeadingDialogCancelButton;
103 ///////// AutoPilot New Altitude Dialog
105 static puDialogBox *ApAltitudeDialog = 0;
106 static puFrame *ApAltitudeDialogFrame = 0;
107 static puText *ApAltitudeDialogMessage = 0;
108 static puInput *ApAltitudeDialogInput = 0;
110 static puOneShot *ApAltitudeDialogOkButton = 0;
111 static puOneShot *ApAltitudeDialogCancelButton = 0;
114 /// The beginnings of Lock AutoPilot to target location :-)
115 // Needs cleaning up but works
116 // These statics should disapear when this is a class
117 static puDialogBox *TgtAptDialog = 0;
118 static puFrame *TgtAptDialogFrame = 0;
119 static puText *TgtAptDialogMessage = 0;
120 static puInput *TgtAptDialogInput = 0;
122 static char NewTgtAirportId[16];
123 static char NewTgtAirportLabel[] = "Enter New TgtAirport ID";
125 static puOneShot *TgtAptDialogOkButton = 0;
126 static puOneShot *TgtAptDialogCancelButton = 0;
127 static puOneShot *TgtAptDialogResetButton = 0;
130 // global variable holding the AP info
131 // I want this gone. Data should be in aircraft structure
132 fgAPDataPtr APDataGlobal;
134 // Local Prototype section
135 static double LinearExtrapolate( double x, double x1, double y1, double x2, double y2 );
136 static double NormalizeDegrees( double Input );
137 // End Local ProtoTypes
139 extern char *coord_format_lat(float);
140 extern char *coord_format_lon(float);
143 static inline double get_ground_speed( void ) {
144 // starts in ft/s so we convert to kts
145 double ft_s = cur_fdm_state->get_V_ground_speed()
146 * current_options.get_speed_up();;
147 double kts = ft_s * FEET_TO_METER * 3600 * METER_TO_NM;
151 // The below routines were copied right from hud.c ( I hate reinventing
152 // the wheel more than necessary)
154 // The following routines obtain information concerntin the aircraft's
155 // current state and return it to calling instrument display routines.
156 // They should eventually be member functions of the aircraft.
158 static void get_control_values( void ) {
160 APData = APDataGlobal;
161 APData->old_aileron = controls.get_aileron();
162 APData->old_elevator = controls.get_elevator();
163 APData->old_elevator_trim = controls.get_elevator_trim();
164 APData->old_rudder = controls.get_rudder();
167 static void MakeTargetHeadingStr( fgAPDataPtr APData, double bearing ) {
170 else if(bearing > 360. )
172 sprintf(APData->TargetHeadingStr, "APHeading %6.1f", bearing);
175 static inline void MakeTargetDistanceStr( fgAPDataPtr APData, double distance ) {
176 double eta = distance*METER_TO_NM / get_ground_speed();
177 if ( eta >= 100.0 ) { eta = 99.999; }
179 if ( eta < (1.0/6.0) ) {
180 // within 10 minutes, bump up to min/secs
184 minor = (int)((eta - (int)eta) * 60.0);
185 sprintf(APData->TargetDistanceStr, "APDistance %.2f NM ETA %d:%02d",
186 distance*METER_TO_NM, major, minor);
187 // cout << "distance = " << distance*METER_TO_NM
188 // << " gndsp = " << get_ground_speed()
189 // << " time = " << eta
190 // << " major = " << major
191 // << " minor = " << minor
195 static inline void MakeTargetAltitudeStr( fgAPDataPtr APData, double altitude ) {
196 sprintf(APData->TargetAltitudeStr, "APAltitude %6.0f", altitude);
199 static inline void MakeTargetLatLonStr( fgAPDataPtr APData, double lat, double lon ) {
201 tmp = APData->TargetLatitude;
202 sprintf( APData->TargetLatitudeStr , "%s", coord_format_lat(tmp) );
203 tmp = APData->TargetLongitude;
204 sprintf( APData->TargetLongitudeStr, "%s", coord_format_lon(tmp) );
206 sprintf(APData->TargetLatLonStr, "%s %s",
207 APData->TargetLatitudeStr,
208 APData->TargetLongitudeStr );
211 static inline double get_speed( void ) {
212 return( cur_fdm_state->get_V_equiv_kts() );
215 static inline double get_aoa( void ) {
216 return( cur_fdm_state->get_Gamma_vert_rad() * RAD_TO_DEG );
219 static inline double fgAPget_latitude( void ) {
220 return( cur_fdm_state->get_Latitude() * RAD_TO_DEG );
223 static inline double fgAPget_longitude( void ) {
224 return( cur_fdm_state->get_Longitude() * RAD_TO_DEG );
227 static inline double fgAPget_roll( void ) {
228 return( cur_fdm_state->get_Phi() * RAD_TO_DEG );
231 static inline double get_pitch( void ) {
232 return( cur_fdm_state->get_Theta() );
235 double fgAPget_heading( void ) {
236 return( cur_fdm_state->get_Psi() * RAD_TO_DEG );
239 static inline double fgAPget_altitude( void ) {
240 return( cur_fdm_state->get_Altitude() * FEET_TO_METER );
243 static inline double fgAPget_climb( void ) {
244 // return in meters per minute
245 return( cur_fdm_state->get_Climb_Rate() * FEET_TO_METER * 60 );
248 static inline double get_sideslip( void ) {
249 return( cur_fdm_state->get_Beta() );
252 static inline double fgAPget_agl( void ) {
255 agl = cur_fdm_state->get_Altitude() * FEET_TO_METER
261 // End of copied section. ( thanks for the wheel :-)
262 double fgAPget_TargetLatitude( void ) {
263 fgAPDataPtr APData = APDataGlobal;
264 return APData->TargetLatitude;
267 double fgAPget_TargetLongitude( void ) {
268 fgAPDataPtr APData = APDataGlobal;
269 return APData->TargetLongitude;
272 double fgAPget_TargetHeading( void ) {
273 fgAPDataPtr APData = APDataGlobal;
274 return APData->TargetHeading;
277 double fgAPget_TargetDistance( void ) {
278 fgAPDataPtr APData = APDataGlobal;
279 return APData->TargetDistance;
282 double fgAPget_TargetAltitude( void ) {
283 fgAPDataPtr APData = APDataGlobal;
284 return APData->TargetAltitude;
287 char *fgAPget_TargetLatitudeStr( void ) {
288 fgAPDataPtr APData = APDataGlobal;
289 return APData->TargetLatitudeStr;
292 char *fgAPget_TargetLongitudeStr( void ) {
293 fgAPDataPtr APData = APDataGlobal;
294 return APData->TargetLongitudeStr;
297 char *fgAPget_TargetDistanceStr( void ) {
298 fgAPDataPtr APData = APDataGlobal;
299 return APData->TargetDistanceStr;
302 char *fgAPget_TargetHeadingStr( void ) {
303 fgAPDataPtr APData = APDataGlobal;
304 return APData->TargetHeadingStr;
307 char *fgAPget_TargetAltitudeStr( void ) {
308 fgAPDataPtr APData = APDataGlobal;
309 return APData->TargetAltitudeStr;
312 char *fgAPget_TargetLatLonStr( void ) {
313 fgAPDataPtr APData = APDataGlobal;
314 return APData->TargetLatLonStr;
317 bool fgAPWayPointEnabled( void )
321 APData = APDataGlobal;
323 // heading hold enabled?
324 return APData->waypoint_hold;
328 bool fgAPHeadingEnabled( void )
332 APData = APDataGlobal;
334 // heading hold enabled?
335 return APData->heading_hold;
338 bool fgAPAltitudeEnabled( void )
342 APData = APDataGlobal;
344 // altitude hold or terrain follow enabled?
345 return APData->altitude_hold;
348 bool fgAPTerrainFollowEnabled( void )
352 APData = APDataGlobal;
354 // altitude hold or terrain follow enabled?
355 return APData->terrain_follow ;
358 bool fgAPAutoThrottleEnabled( void )
362 APData = APDataGlobal;
364 // autothrottle enabled?
365 return APData->auto_throttle;
368 void fgAPAltitudeAdjust( double inc )
370 // Remove at a later date
371 fgAPDataPtr APData = APDataGlobal;
374 double target_alt, target_agl;
376 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
377 target_alt = APData->TargetAltitude * METER_TO_FEET;
378 target_agl = APData->TargetAGL * METER_TO_FEET;
380 target_alt = APData->TargetAltitude;
381 target_agl = APData->TargetAGL;
384 target_alt = ( int ) ( target_alt / inc ) * inc + inc;
385 target_agl = ( int ) ( target_agl / inc ) * inc + inc;
387 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
388 target_alt *= FEET_TO_METER;
389 target_agl *= FEET_TO_METER;
392 APData->TargetAltitude = target_alt;
393 APData->TargetAGL = target_agl;
395 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
396 target_alt *= METER_TO_FEET;
397 ApAltitudeDialogInput->setValue((float)target_alt);
398 MakeTargetAltitudeStr( APData, target_alt);
400 get_control_values();
403 void fgAPAltitudeSet( double new_altitude ) {
404 // Remove at a later date
405 fgAPDataPtr APData = APDataGlobal;
407 double target_alt = new_altitude;
409 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
410 target_alt = new_altitude * FEET_TO_METER;
412 if( target_alt < scenery.cur_elev )
413 target_alt = scenery.cur_elev;
415 APData->TargetAltitude = target_alt;
417 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
418 target_alt *= METER_TO_FEET;
419 ApAltitudeDialogInput->setValue((float)target_alt);
420 MakeTargetAltitudeStr( APData, target_alt);
422 get_control_values();
425 void fgAPHeadingAdjust( double inc ) {
426 fgAPDataPtr APData = APDataGlobal;
428 if ( APData->waypoint_hold )
429 APData->waypoint_hold = false;
431 double target = ( int ) ( APData->TargetHeading / inc ) * inc + inc;
433 APData->TargetHeading = NormalizeDegrees( target );
434 // following cast needed ambiguous plib
435 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
436 MakeTargetHeadingStr( APData, APData->TargetHeading );
437 get_control_values();
440 void fgAPHeadingSet( double new_heading ) {
441 fgAPDataPtr APData = APDataGlobal;
443 if ( APData->waypoint_hold )
444 APData->waypoint_hold = false;
446 new_heading = NormalizeDegrees( new_heading );
447 APData->TargetHeading = new_heading;
448 // following cast needed ambiguous plib
449 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
450 MakeTargetHeadingStr( APData, APData->TargetHeading );
451 get_control_values();
454 void fgAPAutoThrottleAdjust( double inc ) {
455 fgAPDataPtr APData = APDataGlobal;
457 double target = ( int ) ( APData->TargetSpeed / inc ) * inc + inc;
459 APData->TargetSpeed = target;
462 // THIS NEEDS IMPROVEMENT !!!!!!!!!!!!!
463 static int scan_number(char *s, double *new_value)
468 char *WordBufPtr = WordBuf;
473 *WordBufPtr++ = *cptr++;
475 while (isdigit(*cptr) ) {
476 *WordBufPtr++ = *cptr++;
480 *WordBufPtr++ = *cptr++; // put the '.' into the string
481 while (isdigit(*cptr)) {
482 *WordBufPtr++ = *cptr++;
487 sscanf(WordBuf, "%lf", new_value);
494 void ApHeadingDialog_Cancel(puObject *)
496 ApHeadingDialogInput->rejectInput();
497 FG_POP_PUI_DIALOG( ApHeadingDialog );
500 void ApHeadingDialog_OK (puObject *me)
505 ApHeadingDialogInput -> getValue( &c );
509 if( scan_number( c, &NewHeading ) )
511 if(!fgAPHeadingEnabled())
513 fgAPHeadingSet( NewHeading );
517 s += " is not a valid number.";
520 ApHeadingDialog_Cancel(me);
521 if( error ) mkDialog(s.c_str());
524 void NewHeading(puObject *cb)
526 // string ApHeadingLabel( "Enter New Heading" );
527 // ApHeadingDialogMessage -> setLabel(ApHeadingLabel.c_str());
528 ApHeadingDialogInput -> acceptInput();
529 FG_PUSH_PUI_DIALOG( ApHeadingDialog );
532 void NewHeadingInit(void)
534 // printf("NewHeadingInit\n");
535 char NewHeadingLabel[] = "Enter New Heading";
538 float heading = fgAPget_heading();
540 (puGetStringWidth( puGetDefaultLabelFont(), NewHeadingLabel ) /2 );
542 ApHeadingDialog = new puDialogBox (150, 50);
544 ApHeadingDialogFrame = new puFrame (0, 0, 260, 150);
546 ApHeadingDialogMessage = new puText (len, 110);
547 ApHeadingDialogMessage -> setDefaultValue (NewHeadingLabel);
548 ApHeadingDialogMessage -> getDefaultValue (&s);
549 ApHeadingDialogMessage -> setLabel (s);
551 ApHeadingDialogInput = new puInput ( 50, 70, 210, 100 );
552 ApHeadingDialogInput -> setValue ( heading );
554 ApHeadingDialogOkButton = new puOneShot (50, 10, 110, 50);
555 ApHeadingDialogOkButton -> setLegend (gui_msg_OK);
556 ApHeadingDialogOkButton -> makeReturnDefault (TRUE);
557 ApHeadingDialogOkButton -> setCallback (ApHeadingDialog_OK);
559 ApHeadingDialogCancelButton = new puOneShot (140, 10, 210, 50);
560 ApHeadingDialogCancelButton -> setLegend (gui_msg_CANCEL);
561 ApHeadingDialogCancelButton -> setCallback (ApHeadingDialog_Cancel);
564 FG_FINALIZE_PUI_DIALOG( ApHeadingDialog );
567 void ApAltitudeDialog_Cancel(puObject *)
569 ApAltitudeDialogInput -> rejectInput();
570 FG_POP_PUI_DIALOG( ApAltitudeDialog );
573 void ApAltitudeDialog_OK (puObject *me)
578 ApAltitudeDialogInput->getValue( &c );
582 if( scan_number( c, &NewAltitude) )
584 if(!(fgAPAltitudeEnabled()))
585 fgAPToggleAltitude();
586 fgAPAltitudeSet( NewAltitude );
590 s += " is not a valid number.";
593 ApAltitudeDialog_Cancel(me);
594 if( error ) mkDialog(s.c_str());
595 get_control_values();
598 void NewAltitude(puObject *cb)
600 ApAltitudeDialogInput -> acceptInput();
601 FG_PUSH_PUI_DIALOG( ApAltitudeDialog );
604 void NewAltitudeInit(void)
606 // printf("NewAltitudeInit\n");
607 char NewAltitudeLabel[] = "Enter New Altitude";
610 float alt = cur_fdm_state->get_Altitude();
612 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_METERS) {
613 alt *= FEET_TO_METER;
617 (puGetStringWidth( puGetDefaultLabelFont(), NewAltitudeLabel )/2);
619 // ApAltitudeDialog = new puDialogBox (150, 50);
620 ApAltitudeDialog = new puDialogBox (150, 200);
622 ApAltitudeDialogFrame = new puFrame (0, 0, 260, 150);
623 ApAltitudeDialogMessage = new puText (len, 110);
624 ApAltitudeDialogMessage -> setDefaultValue (NewAltitudeLabel);
625 ApAltitudeDialogMessage -> getDefaultValue (&s);
626 ApAltitudeDialogMessage -> setLabel (s);
628 ApAltitudeDialogInput = new puInput ( 50, 70, 210, 100 );
629 ApAltitudeDialogInput -> setValue ( alt );
630 // Uncomment the next line to have input active on startup
631 // ApAltitudeDialogInput -> acceptInput ( );
632 // cursor at begining or end of line ?
635 // ApAltitudeDialogInput -> setCursor ( len );
636 // ApAltitudeDialogInput -> setSelectRegion ( 5, 9 );
638 ApAltitudeDialogOkButton = new puOneShot (50, 10, 110, 50);
639 ApAltitudeDialogOkButton -> setLegend (gui_msg_OK);
640 ApAltitudeDialogOkButton -> makeReturnDefault (TRUE);
641 ApAltitudeDialogOkButton -> setCallback (ApAltitudeDialog_OK);
643 ApAltitudeDialogCancelButton = new puOneShot (140, 10, 210, 50);
644 ApAltitudeDialogCancelButton -> setLegend (gui_msg_CANCEL);
645 ApAltitudeDialogCancelButton -> setCallback (ApAltitudeDialog_Cancel);
648 FG_FINALIZE_PUI_DIALOG( ApAltitudeDialog );
651 /////// simple AutoPilot GAIN / LIMITS ADJUSTER
653 #define fgAP_CLAMP(val,min,max) \
654 ( (val) = (val) > (max) ? (max) : (val) < (min) ? (min) : (val) )
656 static void maxroll_adj( puObject *hs ) {
658 fgAPDataPtr APData = APDataGlobal;
660 hs-> getValue ( &val ) ;
661 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
662 // printf ( "maxroll_adj( %p ) %f %f\n", hs, val, MaxRollAdjust * val ) ;
663 APData-> MaxRoll = MaxRollAdjust * val;
664 sprintf( SliderText[ 0 ], "%05.2f", APData->MaxRoll );
665 APAdjustMaxRollText -> setLabel ( SliderText[ 0 ] ) ;
668 static void rollout_adj( puObject *hs ) {
670 fgAPDataPtr APData = APDataGlobal;
672 hs-> getValue ( &val ) ;
673 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
674 // printf ( "rollout_adj( %p ) %f %f\n", hs, val, RollOutAdjust * val ) ;
675 APData-> RollOut = RollOutAdjust * val;
676 sprintf( SliderText[ 1 ], "%05.2f", APData->RollOut );
677 APAdjustRollOutText -> setLabel ( SliderText[ 1 ] );
680 static void maxaileron_adj( puObject *hs ) {
683 APData = APDataGlobal;
685 hs-> getValue ( &val ) ;
686 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
687 // printf ( "maxaileron_adj( %p ) %f %f\n", hs, val, MaxAileronAdjust * val ) ;
688 APData-> MaxAileron = MaxAileronAdjust * val;
689 sprintf( SliderText[ 3 ], "%05.2f", APData->MaxAileron );
690 APAdjustMaxAileronText -> setLabel ( SliderText[ 3 ] );
693 static void rolloutsmooth_adj( puObject *hs ) {
695 fgAPDataPtr APData = APDataGlobal;
697 hs -> getValue ( &val ) ;
698 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
699 // printf ( "rolloutsmooth_adj( %p ) %f %f\n", hs, val, RollOutSmoothAdjust * val ) ;
700 APData->RollOutSmooth = RollOutSmoothAdjust * val;
701 sprintf( SliderText[ 2 ], "%5.2f", APData-> RollOutSmooth );
702 APAdjustRollOutSmoothText-> setLabel ( SliderText[ 2 ] );
706 static void goAwayAPAdjust (puObject *)
708 FG_POP_PUI_DIALOG( APAdjustDialog );
711 void cancelAPAdjust( puObject *self ) {
712 fgAPDataPtr APData = APDataGlobal;
714 APData-> MaxRoll = TmpMaxRollValue;
715 APData-> RollOut = TmpRollOutValue;
716 APData-> MaxAileron = TmpMaxAileronValue;
717 APData-> RollOutSmooth = TmpRollOutSmoothValue;
719 goAwayAPAdjust(self);
722 void resetAPAdjust( puObject *self ) {
723 fgAPDataPtr APData = APDataGlobal;
725 APData-> MaxRoll = MaxRollAdjust / 2;
726 APData-> RollOut = RollOutAdjust / 2;
727 APData-> MaxAileron = MaxAileronAdjust / 2;
728 APData-> RollOutSmooth = RollOutSmoothAdjust / 2;
730 FG_POP_PUI_DIALOG( APAdjustDialog );
735 void fgAPAdjust( puObject * ) {
736 fgAPDataPtr APData = APDataGlobal;
738 TmpMaxRollValue = APData-> MaxRoll;
739 TmpRollOutValue = APData-> RollOut;
740 TmpMaxAileronValue = APData-> MaxAileron;
741 TmpRollOutSmoothValue = APData-> RollOutSmooth;
743 MaxRollValue = APData-> MaxRoll / MaxRollAdjust;
744 RollOutValue = APData-> RollOut / RollOutAdjust;
745 MaxAileronValue = APData-> MaxAileron / MaxAileronAdjust;
746 RollOutSmoothValue = APData-> RollOutSmooth / RollOutSmoothAdjust;
748 APAdjustHS0-> setValue ( MaxRollValue ) ;
749 APAdjustHS1-> setValue ( RollOutValue ) ;
750 APAdjustHS2-> setValue ( RollOutSmoothValue ) ;
751 APAdjustHS3-> setValue ( MaxAileronValue ) ;
753 FG_PUSH_PUI_DIALOG( APAdjustDialog );
756 // Done once at system initialization
757 void fgAPAdjustInit( void ) {
759 // printf("fgAPAdjustInit\n");
760 #define HORIZONTAL FALSE
764 int DialogWidth = 230;
766 char Label[] = "AutoPilot Adjust";
769 fgAPDataPtr APData = APDataGlobal;
771 int labelX = (DialogWidth / 2) -
772 (puGetStringWidth( puGetDefaultLabelFont(), Label ) / 2);
773 labelX -= 30; // KLUDGEY
778 int slider_width = 210;
779 int slider_title_x = 15;
780 int slider_value_x = 160;
781 float slider_delta = 0.1f;
783 TmpMaxRollValue = APData-> MaxRoll;
784 TmpRollOutValue = APData-> RollOut;
785 TmpMaxAileronValue = APData-> MaxAileron;
786 TmpRollOutSmoothValue = APData-> RollOutSmooth;
788 MaxRollValue = APData-> MaxRoll / MaxRollAdjust;
789 RollOutValue = APData-> RollOut / RollOutAdjust;
790 MaxAileronValue = APData-> MaxAileron / MaxAileronAdjust;
791 RollOutSmoothValue = APData-> RollOutSmooth / RollOutSmoothAdjust;
793 puGetDefaultFonts ( &APAdjustLegendFont, &APAdjustLabelFont );
794 APAdjustDialog = new puDialogBox ( DialogX, DialogY ); {
795 int horiz_slider_height = puGetStringHeight (APAdjustLabelFont) +
796 puGetStringDescender (APAdjustLabelFont) +
797 PUSTR_TGAP + PUSTR_BGAP + 5;
799 APAdjustFrame = new puFrame ( 0, 0,
800 DialogWidth, 85 + nSliders * horiz_slider_height );
802 APAdjustDialogMessage = new puText ( labelX, 52 + nSliders * horiz_slider_height );
803 APAdjustDialogMessage -> setDefaultValue ( Label );
804 APAdjustDialogMessage -> getDefaultValue ( &s );
805 APAdjustDialogMessage -> setLabel ( s );
807 APAdjustHS0 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
808 APAdjustHS0-> setDelta ( slider_delta ) ;
809 APAdjustHS0-> setValue ( MaxRollValue ) ;
810 APAdjustHS0-> setCBMode ( PUSLIDER_DELTA ) ;
811 APAdjustHS0-> setCallback ( maxroll_adj ) ;
813 sprintf( SliderText[ 0 ], "%05.2f", APData->MaxRoll );
814 APAdjustMaxRollTitle = new puText ( slider_title_x, slider_y ) ;
815 APAdjustMaxRollTitle-> setDefaultValue ( "MaxRoll" ) ;
816 APAdjustMaxRollTitle-> getDefaultValue ( &s ) ;
817 APAdjustMaxRollTitle-> setLabel ( s ) ;
818 APAdjustMaxRollText = new puText ( slider_value_x, slider_y ) ;
819 APAdjustMaxRollText-> setLabel ( SliderText[ 0 ] ) ;
821 slider_y += horiz_slider_height;
823 APAdjustHS1 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
824 APAdjustHS1-> setDelta ( slider_delta ) ;
825 APAdjustHS1-> setValue ( RollOutValue ) ;
826 APAdjustHS1-> setCBMode ( PUSLIDER_DELTA ) ;
827 APAdjustHS1-> setCallback ( rollout_adj ) ;
829 sprintf( SliderText[ 1 ], "%05.2f", APData->RollOut );
830 APAdjustRollOutTitle = new puText ( slider_title_x, slider_y ) ;
831 APAdjustRollOutTitle-> setDefaultValue ( "AdjustRollOut" ) ;
832 APAdjustRollOutTitle-> getDefaultValue ( &s ) ;
833 APAdjustRollOutTitle-> setLabel ( s ) ;
834 APAdjustRollOutText = new puText ( slider_value_x, slider_y ) ;
835 APAdjustRollOutText-> setLabel ( SliderText[ 1 ] );
837 slider_y += horiz_slider_height;
839 APAdjustHS2 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
840 APAdjustHS2-> setDelta ( slider_delta ) ;
841 APAdjustHS2-> setValue ( RollOutSmoothValue ) ;
842 APAdjustHS2-> setCBMode ( PUSLIDER_DELTA ) ;
843 APAdjustHS2-> setCallback ( rolloutsmooth_adj ) ;
845 sprintf( SliderText[ 2 ], "%5.2f", APData->RollOutSmooth );
846 APAdjustRollOutSmoothTitle = new puText ( slider_title_x, slider_y ) ;
847 APAdjustRollOutSmoothTitle-> setDefaultValue ( "RollOutSmooth" ) ;
848 APAdjustRollOutSmoothTitle-> getDefaultValue ( &s ) ;
849 APAdjustRollOutSmoothTitle-> setLabel ( s ) ;
850 APAdjustRollOutSmoothText = new puText ( slider_value_x, slider_y ) ;
851 APAdjustRollOutSmoothText-> setLabel ( SliderText[ 2 ] );
853 slider_y += horiz_slider_height;
855 APAdjustHS3 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
856 APAdjustHS3-> setDelta ( slider_delta ) ;
857 APAdjustHS3-> setValue ( MaxAileronValue ) ;
858 APAdjustHS3-> setCBMode ( PUSLIDER_DELTA ) ;
859 APAdjustHS3-> setCallback ( maxaileron_adj ) ;
861 sprintf( SliderText[ 3 ], "%05.2f", APData->MaxAileron );
862 APAdjustMaxAileronTitle = new puText ( slider_title_x, slider_y ) ;
863 APAdjustMaxAileronTitle-> setDefaultValue ( "MaxAileron" ) ;
864 APAdjustMaxAileronTitle-> getDefaultValue ( &s ) ;
865 APAdjustMaxAileronTitle-> setLabel ( s ) ;
866 APAdjustMaxAileronText = new puText ( slider_value_x, slider_y ) ;
867 APAdjustMaxAileronText-> setLabel ( SliderText[ 3 ] );
869 APAdjustOkButton = new puOneShot ( 10, 10, 60, 50 );
870 APAdjustOkButton-> setLegend ( gui_msg_OK );
871 APAdjustOkButton-> makeReturnDefault ( TRUE );
872 APAdjustOkButton-> setCallback ( goAwayAPAdjust );
874 APAdjustCancelButton = new puOneShot ( 70, 10, 150, 50 );
875 APAdjustCancelButton-> setLegend ( gui_msg_CANCEL );
876 APAdjustCancelButton-> setCallback ( cancelAPAdjust );
878 APAdjustResetButton = new puOneShot ( 160, 10, 220, 50 );
879 APAdjustResetButton-> setLegend ( gui_msg_RESET );
880 APAdjustResetButton-> setCallback ( resetAPAdjust );
882 FG_FINALIZE_PUI_DIALOG( APAdjustDialog );
887 // Simple Dialog to input Target Airport
888 void TgtAptDialog_Cancel(puObject *)
890 FG_POP_PUI_DIALOG( TgtAptDialog );
893 void TgtAptDialog_OK (puObject *)
896 APData = APDataGlobal;
899 // FGTime *t = FGTime::cur_time_params;
900 // int PauseMode = t->getPause();
902 // t->togglePauseMode();
905 TgtAptDialogInput->getValue(&s);
908 TgtAptDialog_Cancel( NULL );
910 if ( TgtAptId.length() ) {
911 // set initial position from TgtAirport id
916 FG_LOG( FG_GENERAL, FG_INFO,
917 "Attempting to set starting position from airport code "
920 airports.load("apt_simple");
921 if ( airports.search( TgtAptId, &a ) )
923 double course, reverse, distance;
924 // fgAPset_tgt_airport_id( TgtAptId.c_str() );
925 current_options.set_airport_id( TgtAptId.c_str() );
926 sprintf( NewTgtAirportId, "%s", TgtAptId.c_str() );
928 APData->TargetLatitude = a.latitude; // * DEG_TO_RAD;
929 APData->TargetLongitude = a.longitude; // * DEG_TO_RAD;
930 MakeTargetLatLonStr( APData,
931 APData->TargetLatitude,
932 APData->TargetLongitude);
934 APData->old_lat = fgAPget_latitude();
935 APData->old_lon = fgAPget_longitude();
937 // need to test for iter
938 if( ! geo_inverse_wgs_84( fgAPget_altitude(),
941 APData->TargetLatitude,
942 APData->TargetLongitude,
946 APData->TargetHeading = course;
947 MakeTargetHeadingStr( APData, APData->TargetHeading );
948 APData->TargetDistance = distance;
949 MakeTargetDistanceStr( APData, distance );
950 // This changes the AutoPilot Heading
951 // following cast needed
952 ApHeadingDialogInput->setValue ((float)APData->TargetHeading );
954 APData->waypoint_hold = true ;
955 APData->heading_hold = true;
958 TgtAptId += " not in database.";
959 mkDialog(TgtAptId.c_str());
962 get_control_values();
963 // if( PauseMode != t->getPause() )
964 // t->togglePauseMode();
967 void TgtAptDialog_Reset(puObject *)
969 // strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
970 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
971 TgtAptDialogInput->setValue ( NewTgtAirportId );
972 TgtAptDialogInput->setCursor( 0 ) ;
975 void NewTgtAirport(puObject *cb)
977 // strncpy( NewAirportId, current_options.get_airport_id().c_str(), 16 );
978 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
979 TgtAptDialogInput->setValue( NewTgtAirportId );
981 FG_PUSH_PUI_DIALOG( TgtAptDialog );
984 void NewTgtAirportInit(void)
986 FG_LOG( FG_AUTOPILOT, FG_INFO, " enter NewTgtAirportInit()" );
987 // fgAPset_tgt_airport_id( current_options.get_airport_id() );
988 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
989 FG_LOG( FG_AUTOPILOT, FG_INFO, " NewTgtAirportId " << NewTgtAirportId );
990 // printf(" NewTgtAirportId %s\n", NewTgtAirportId);
991 int len = 150 - puGetStringWidth( puGetDefaultLabelFont(),
992 NewTgtAirportLabel ) / 2;
994 TgtAptDialog = new puDialogBox (150, 50);
996 TgtAptDialogFrame = new puFrame (0,0,350, 150);
997 TgtAptDialogMessage = new puText (len, 110);
998 TgtAptDialogMessage -> setLabel (NewTgtAirportLabel);
1000 TgtAptDialogInput = new puInput (50, 70, 300, 100);
1001 TgtAptDialogInput -> setValue (NewTgtAirportId);
1002 TgtAptDialogInput -> acceptInput();
1004 TgtAptDialogOkButton = new puOneShot (50, 10, 110, 50);
1005 TgtAptDialogOkButton -> setLegend (gui_msg_OK);
1006 TgtAptDialogOkButton -> setCallback (TgtAptDialog_OK);
1007 TgtAptDialogOkButton -> makeReturnDefault(TRUE);
1009 TgtAptDialogCancelButton = new puOneShot (140, 10, 210, 50);
1010 TgtAptDialogCancelButton -> setLegend (gui_msg_CANCEL);
1011 TgtAptDialogCancelButton -> setCallback (TgtAptDialog_Cancel);
1013 TgtAptDialogResetButton = new puOneShot (240, 10, 300, 50);
1014 TgtAptDialogResetButton -> setLegend (gui_msg_RESET);
1015 TgtAptDialogResetButton -> setCallback (TgtAptDialog_Reset);
1017 FG_FINALIZE_PUI_DIALOG( TgtAptDialog );
1018 printf("leave NewTgtAirportInit()");
1022 // Finally actual guts of AutoPilot
1023 void fgAPInit( fgAIRCRAFT *current_aircraft ) {
1026 FG_LOG( FG_AUTOPILOT, FG_INFO, "Init AutoPilot Subsystem" );
1028 APData = ( fgAPDataPtr ) calloc( sizeof( fgAPData ), 1 );
1030 if ( APData == NULL ) {
1031 // I couldn't get the mem. Dying
1032 FG_LOG( FG_AUTOPILOT, FG_ALERT, "No ram for Autopilot. Dying." );
1036 FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot allocated " );
1038 APData->waypoint_hold = false ; // turn the target hold off
1039 APData->heading_hold = false ; // turn the heading hold off
1040 APData->altitude_hold = false ; // turn the altitude hold off
1042 // Initialize target location to startup location
1043 // FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot setting startup location" );
1045 APData->TargetLatitude = fgAPget_latitude();
1047 APData->TargetLongitude = fgAPget_longitude();
1049 // FG_LOG( FG_AUTOPILOT, FG_INFO, " Autopilot setting TargetLatitudeStr" );
1050 MakeTargetLatLonStr( APData, APData->TargetLatitude, APData->TargetLongitude);
1052 APData->TargetHeading = 0.0; // default direction, due north
1053 APData->TargetAltitude = 3000; // default altitude in meters
1054 APData->alt_error_accum = 0.0;
1056 MakeTargetAltitudeStr( APData, 3000.0);
1057 MakeTargetHeadingStr( APData, 0.0 );
1059 // These eventually need to be read from current_aircaft somehow.
1063 // the maximum roll, in Deg
1064 APData->MaxRoll = 7;
1065 // the deg from heading to start rolling out at, in Deg
1066 APData->RollOut = 30;
1067 // how far can I move the aleron from center.
1068 APData->MaxAileron = .1;
1069 // Smoothing distance for alerion control
1070 APData->RollOutSmooth = 10;
1073 // the maximum roll, in Deg
1074 APData->MaxRoll = 20;
1076 // the deg from heading to start rolling out at, in Deg
1077 APData->RollOut = 20;
1079 // how far can I move the aleron from center.
1080 APData->MaxAileron = .2;
1082 // Smoothing distance for alerion control
1083 APData->RollOutSmooth = 10;
1085 //Remove at a later date
1086 APDataGlobal = APData;
1088 // Hardwired for now should be in options
1089 // 25% max control variablilty 0.5 / 2.0
1090 APData->disengage_threshold = 1.0;
1092 #if !defined( USING_SLIDER_CLASS )
1093 MaxRollAdjust = 2 * APData->MaxRoll;
1094 RollOutAdjust = 2 * APData->RollOut;
1095 MaxAileronAdjust = 2 * APData->MaxAileron;
1096 RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
1097 #endif // !defined( USING_SLIDER_CLASS )
1099 get_control_values();
1101 // FG_LOG( FG_AUTOPILOT, FG_INFO, " calling NewTgtAirportInit" );
1102 NewTgtAirportInit();
1109 void fgAPReset( void ) {
1110 fgAPDataPtr APData = APDataGlobal;
1112 if ( fgAPTerrainFollowEnabled() )
1113 fgAPToggleTerrainFollow( );
1115 if ( fgAPAltitudeEnabled() )
1116 fgAPToggleAltitude();
1118 if ( fgAPHeadingEnabled() )
1119 fgAPToggleHeading();
1121 if ( fgAPAutoThrottleEnabled() )
1122 fgAPToggleAutoThrottle();
1124 APData->TargetHeading = 0.0; // default direction, due north
1125 MakeTargetHeadingStr( APData, APData->TargetHeading );
1127 APData->TargetAltitude = 3000; // default altitude in meters
1128 MakeTargetAltitudeStr( APData, 3000);
1130 APData->alt_error_accum = 0.0;
1133 get_control_values();
1135 sprintf( NewTgtAirportId, "%s", current_options.get_airport_id().c_str() );
1137 APData->TargetLatitude = fgAPget_latitude();
1138 APData->TargetLongitude = fgAPget_longitude();
1139 MakeTargetLatLonStr( APData,
1140 APData->TargetLatitude,
1141 APData->TargetLongitude);
1145 int fgAPRun( void ) {
1146 // Remove the following lines when the calling funcitons start
1147 // passing in the data pointer
1151 APData = APDataGlobal;
1154 // get control settings
1155 double aileron = controls.get_aileron();
1156 double elevator = controls.get_elevator();
1157 double elevator_trim = controls.get_elevator_trim();
1158 double rudder = controls.get_rudder();
1160 double lat = fgAPget_latitude();
1161 double lon = fgAPget_longitude();
1163 #ifdef FG_FORCE_AUTO_DISENGAGE
1164 // see if somebody else has changed them
1165 if( fabs(aileron - APData->old_aileron) > APData->disengage_threshold ||
1166 fabs(elevator - APData->old_elevator) > APData->disengage_threshold ||
1167 fabs(elevator_trim - APData->old_elevator_trim) >
1168 APData->disengage_threshold ||
1169 fabs(rudder - APData->old_rudder) > APData->disengage_threshold )
1171 // if controls changed externally turn autopilot off
1172 APData->waypoint_hold = false ; // turn the target hold off
1173 APData->heading_hold = false ; // turn the heading hold off
1174 APData->altitude_hold = false ; // turn the altitude hold off
1175 APData->terrain_follow = false; // turn the terrain_follow hold off
1176 // APData->auto_throttle = false; // turn the auto_throttle off
1178 // stash this runs control settings
1179 APData->old_aileron = aileron;
1180 APData->old_elevator = elevator;
1181 APData->old_elevator_trim = elevator_trim;
1182 APData->old_rudder = rudder;
1188 // waypoint hold enabled?
1189 if ( APData->waypoint_hold == true )
1191 double wp_course, wp_reverse, wp_distance;
1193 #ifdef DO_fgAP_CORRECTED_COURSE
1194 // compute course made good
1195 // this needs lots of special casing before use
1196 double course, reverse, distance, corrected_course;
1197 // need to test for iter
1198 geo_inverse_wgs_84( 0, //fgAPget_altitude(),
1206 #endif // DO_fgAP_CORRECTED_COURSE
1208 // compute course to way_point
1209 // need to test for iter
1210 if( ! geo_inverse_wgs_84( 0, //fgAPget_altitude(),
1213 APData->TargetLatitude,
1214 APData->TargetLongitude,
1219 #ifdef DO_fgAP_CORRECTED_COURSE
1220 corrected_course = course - wp_course;
1221 if( fabs(corrected_course) > 0.1 )
1222 printf("fgAP: course %f wp_course %f %f %f\n",
1223 course, wp_course, fabs(corrected_course), distance );
1224 #endif // DO_fgAP_CORRECTED_COURSE
1226 if ( wp_distance > 100 ) {
1227 // corrected_course = course - wp_course;
1228 APData->TargetHeading = NormalizeDegrees(wp_course);
1230 printf("APData->distance(%f) to close\n", wp_distance);
1231 // Real Close -- set heading hold to current heading
1232 // and Ring the arival bell !!
1233 NewTgtAirport(NULL);
1234 APData->waypoint_hold = false;
1236 APData->TargetHeading = fgAPget_heading();
1238 MakeTargetHeadingStr( APData, APData->TargetHeading );
1239 // Force this just in case
1240 APData->TargetDistance = wp_distance;
1241 MakeTargetDistanceStr( APData, wp_distance );
1242 // This changes the AutoPilot Heading Read Out
1243 // following cast needed
1244 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
1246 APData->heading_hold = true;
1249 // heading hold enabled?
1250 if ( APData->heading_hold == true ) {
1257 NormalizeDegrees( APData->TargetHeading - fgAPget_heading() );
1258 // figure out how far off we are from desired heading
1260 // Now it is time to deterime how far we should be rolled.
1261 FG_LOG( FG_AUTOPILOT, FG_DEBUG, "RelHeading: " << RelHeading );
1264 // Check if we are further from heading than the roll out point
1265 if ( fabs( RelHeading ) > APData->RollOut ) {
1266 // set Target Roll to Max in desired direction
1267 if ( RelHeading < 0 ) {
1268 TargetRoll = 0 - APData->MaxRoll;
1270 TargetRoll = APData->MaxRoll;
1273 // We have to calculate the Target roll
1275 // This calculation engine thinks that the Target roll
1276 // should be a line from (RollOut,MaxRoll) to (-RollOut,
1277 // -MaxRoll) I hope this works well. If I get ambitious
1278 // some day this might become a fancier curve or
1281 TargetRoll = LinearExtrapolate( RelHeading, -APData->RollOut,
1282 -APData->MaxRoll, APData->RollOut,
1286 // Target Roll has now been Found.
1288 // Compare Target roll to Current Roll, Generate Rel Roll
1290 FG_LOG( FG_COCKPIT, FG_BULK, "TargetRoll: " << TargetRoll );
1292 RelRoll = NormalizeDegrees( TargetRoll - fgAPget_roll() );
1294 // Check if we are further from heading than the roll out smooth point
1295 if ( fabs( RelRoll ) > APData->RollOutSmooth ) {
1296 // set Target Roll to Max in desired direction
1297 if ( RelRoll < 0 ) {
1298 AileronSet = 0 - APData->MaxAileron;
1300 AileronSet = APData->MaxAileron;
1303 AileronSet = LinearExtrapolate( RelRoll, -APData->RollOutSmooth,
1304 -APData->MaxAileron,
1305 APData->RollOutSmooth,
1306 APData->MaxAileron );
1309 controls.set_aileron( AileronSet );
1310 controls.set_rudder( AileronSet / 2.0 );
1311 // controls.set_rudder( 0.0 );
1314 // altitude hold or terrain follow enabled?
1315 if ( APData->altitude_hold || APData->terrain_follow ) {
1316 double speed, max_climb, error;
1317 double prop_error, int_error;
1318 double prop_adj, int_adj, total_adj;
1320 if ( APData->altitude_hold ) {
1321 // normal altitude hold
1322 APData->TargetClimbRate =
1323 ( APData->TargetAltitude - fgAPget_altitude() ) * 8.0;
1324 } else if ( APData->terrain_follow ) {
1325 // brain dead ground hugging with no look ahead
1326 APData->TargetClimbRate =
1327 ( APData->TargetAGL - fgAPget_agl() ) * 16.0;
1329 // just try to zero out rate of climb ...
1330 APData->TargetClimbRate = 0.0;
1333 speed = get_speed();
1335 if ( speed < 90.0 ) {
1337 } else if ( speed < 100.0 ) {
1338 max_climb = ( speed - 90.0 ) * 20;
1339 // } else if ( speed < 150.0 ) {
1341 max_climb = ( speed - 100.0 ) * 4.0 + 200.0;
1342 } //else { // this is NHV hack
1343 // max_climb = ( speed - 150.0 ) * 6.0 + 300.0;
1346 if ( APData->TargetClimbRate > max_climb ) {
1347 APData->TargetClimbRate = max_climb;
1350 else if ( APData->TargetClimbRate < -400.0 ) {
1351 APData->TargetClimbRate = -400.0;
1354 error = fgAPget_climb() - APData->TargetClimbRate;
1356 // accumulate the error under the curve ... this really should
1358 APData->alt_error_accum += error;
1360 // calculate integral error, and adjustment amount
1361 int_error = APData->alt_error_accum;
1362 // printf("error = %.2f int_error = %.2f\n", error, int_error);
1363 int_adj = int_error / 8000.0;
1365 // caclulate proportional error
1367 prop_adj = prop_error / 2000.0;
1369 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
1370 if ( total_adj > 0.6 ) {
1373 else if ( total_adj < -0.2 ) {
1377 controls.set_elevator( total_adj );
1380 // auto throttle enabled?
1381 if ( APData->auto_throttle ) {
1383 double prop_error, int_error;
1384 double prop_adj, int_adj, total_adj;
1386 error = APData->TargetSpeed - get_speed();
1388 // accumulate the error under the curve ... this really should
1390 APData->speed_error_accum += error;
1391 if ( APData->speed_error_accum > 2000.0 ) {
1392 APData->speed_error_accum = 2000.0;
1394 else if ( APData->speed_error_accum < -2000.0 ) {
1395 APData->speed_error_accum = -2000.0;
1398 // calculate integral error, and adjustment amount
1399 int_error = APData->speed_error_accum;
1401 // printf("error = %.2f int_error = %.2f\n", error, int_error);
1402 int_adj = int_error / 200.0;
1404 // caclulate proportional error
1406 prop_adj = 0.5 + prop_error / 50.0;
1408 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
1409 if ( total_adj > 1.0 ) {
1412 else if ( total_adj < 0.0 ) {
1416 controls.set_throttle( FGControls::ALL_ENGINES, total_adj );
1419 #ifdef THIS_CODE_IS_NOT_USED
1420 if (APData->Mode == 2) // Glide slope hold
1425 // First, calculate Relative slope and normalize it
1426 RelSlope = NormalizeDegrees( APData->TargetSlope - get_pitch());
1428 // Now calculate the elevator offset from current angle
1429 if ( abs(RelSlope) > APData->SlopeSmooth )
1431 if ( RelSlope < 0 ) // set RelElevator to max in the correct direction
1432 RelElevator = -APData->MaxElevator;
1434 RelElevator = APData->MaxElevator;
1438 RelElevator = LinearExtrapolate(RelSlope,-APData->SlopeSmooth,-APData->MaxElevator,APData->SlopeSmooth,APData->MaxElevator);
1441 fgElevMove(RelElevator);
1444 #endif // THIS_CODE_IS_NOT_USED
1446 // stash this runs control settings
1447 // get_control_values();
1448 APData->old_aileron = controls.get_aileron();
1449 APData->old_elevator = controls.get_elevator();
1450 APData->old_elevator_trim = controls.get_elevator_trim();
1451 APData->old_rudder = controls.get_rudder();
1453 // for cross track error
1454 APData->old_lat = lat;
1455 APData->old_lon = lon;
1462 void fgAPSetMode( int mode)
1464 //Remove the following line when the calling funcitons start passing in the data pointer
1467 APData = APDataGlobal;
1470 fgPrintf( FG_COCKPIT, FG_INFO, "APSetMode : %d\n", mode );
1472 APData->Mode = mode; // set the new mode
1476 void fgAPset_tgt_airport_id( const string id ) {
1477 FG_LOG( FG_AUTOPILOT, FG_INFO, "entering fgAPset_tgt_airport_id " << id );
1479 APData = APDataGlobal;
1480 APData->tgt_airport_id = id;
1481 FG_LOG( FG_AUTOPILOT, FG_INFO, "leaving fgAPset_tgt_airport_id "
1482 << APData->tgt_airport_id );
1485 string fgAPget_tgt_airport_id( void ) {
1486 fgAPDataPtr APData = APDataGlobal;
1487 return APData->tgt_airport_id;
1491 void fgAPToggleHeading( void ) {
1492 // Remove at a later date
1495 APData = APDataGlobal;
1498 if ( APData->heading_hold || APData->waypoint_hold ) {
1499 // turn off heading hold
1500 APData->heading_hold = false;
1501 APData->waypoint_hold = false;
1503 // turn on heading hold, lock at current heading
1504 APData->heading_hold = true;
1505 APData->TargetHeading = fgAPget_heading();
1506 MakeTargetHeadingStr( APData, APData->TargetHeading );
1507 ApHeadingDialogInput -> setValue ((float)APData->TargetHeading );
1510 get_control_values();
1511 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetHeading: ("
1512 << APData->heading_hold << ") " << APData->TargetHeading );
1515 void fgAPToggleWayPoint( void ) {
1516 // Remove at a later date
1519 APData = APDataGlobal;
1522 if ( APData->waypoint_hold ) {
1523 // turn off location hold
1524 APData->waypoint_hold = false;
1525 // set heading hold to current heading
1526 // APData->heading_hold = true;
1527 APData->TargetHeading = fgAPget_heading();
1529 double course, reverse, distance;
1530 // turn on location hold
1531 // turn on heading hold
1532 APData->old_lat = fgAPget_latitude();
1533 APData->old_lon = fgAPget_longitude();
1535 // need to test for iter
1536 if(!geo_inverse_wgs_84( fgAPget_altitude(),
1538 fgAPget_longitude(),
1539 APData->TargetLatitude,
1540 APData->TargetLongitude,
1544 APData->TargetHeading = course;
1545 APData->TargetDistance = distance;
1546 MakeTargetDistanceStr( APData, distance );
1549 APData->waypoint_hold = true;
1550 APData->heading_hold = true;
1553 // This changes the AutoPilot Heading
1554 // following cast needed
1555 ApHeadingDialogInput->setValue ((float)APData->TargetHeading );
1556 MakeTargetHeadingStr( APData, APData->TargetHeading );
1558 get_control_values();
1560 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetLocation: ( "
1561 << APData->waypoint_hold << " "
1562 << APData->TargetLatitude << " "
1563 << APData->TargetLongitude << " ) "
1568 void fgAPToggleAltitude( void ) {
1569 // Remove at a later date
1572 APData = APDataGlobal;
1575 if ( APData->altitude_hold ) {
1576 // turn off altitude hold
1577 APData->altitude_hold = false;
1579 // turn on altitude hold, lock at current altitude
1580 APData->altitude_hold = true;
1581 APData->terrain_follow = false;
1582 APData->TargetAltitude = fgAPget_altitude();
1583 APData->alt_error_accum = 0.0;
1584 // alt_error_queue.erase( alt_error_queue.begin(),
1585 // alt_error_queue.end() );
1586 float target_alt = APData->TargetAltitude;
1587 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
1588 target_alt *= METER_TO_FEET;
1590 ApAltitudeDialogInput->setValue(target_alt);
1591 MakeTargetAltitudeStr( APData, target_alt);
1594 get_control_values();
1595 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAltitude: ("
1596 << APData->altitude_hold << ") " << APData->TargetAltitude );
1600 void fgAPToggleAutoThrottle ( void ) {
1601 // Remove at a later date
1604 APData = APDataGlobal;
1607 if ( APData->auto_throttle ) {
1608 // turn off altitude hold
1609 APData->auto_throttle = false;
1611 // turn on terrain follow, lock at current agl
1612 APData->auto_throttle = true;
1613 APData->TargetSpeed = get_speed();
1614 APData->speed_error_accum = 0.0;
1617 get_control_values();
1618 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAutoThrottle: ("
1619 << APData->auto_throttle << ") " << APData->TargetSpeed );
1622 void fgAPToggleTerrainFollow( void ) {
1623 // Remove at a later date
1626 APData = APDataGlobal;
1629 if ( APData->terrain_follow ) {
1630 // turn off altitude hold
1631 APData->terrain_follow = false;
1633 // turn on terrain follow, lock at current agl
1634 APData->terrain_follow = true;
1635 APData->altitude_hold = false;
1636 APData->TargetAGL = fgAPget_agl();
1637 APData->alt_error_accum = 0.0;
1639 get_control_values();
1641 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetTerrainFollow: ("
1642 << APData->terrain_follow << ") " << APData->TargetAGL );
1645 static double NormalizeDegrees( double Input ) {
1646 // normalize the input to the range (-180,180]
1647 // Input should not be greater than -360 to 360.
1648 // Current rules send the output to an undefined state.
1652 else if ( Input <= -180 )
1653 while ( Input <= -180 )
1658 static double LinearExtrapolate( double x, double x1, double y1, double x2, double y2 ) {
1659 // This procedure extrapolates the y value for the x posistion on a line defined by x1,y1; x2,y2
1660 //assert(x1 != x2); // Divide by zero error. Cold abort for now
1663 // static double y = 0.0;
1664 // double dx = x2 -x1;
1665 // if( (dx < -FG_EPSILON ) || ( dx > FG_EPSILON ) )
1668 double m, b, y; // the constants to find in y=mx+b
1671 m = ( y2 - y1 ) / ( x2 - x1 ); // calculate the m
1673 b = y1 - m * x1; // calculate the b
1675 y = m * x + b; // the final calculation
1684 /* Direct and inverse distance functions */
1685 /** Proceedings of the 7th International Symposium on Geodetic
1687 "The Nested Coefficient Method for Accurate Solutions of Direct
1689 Inverse Geodetic Problems With Any Length"
1693 /* modified for FlightGear to use WGS84 only Norman Vine */
1695 //#include "dstazfns.h"
1697 #define GEOD_INV_PI (3.14159265358979323846)
1702 /* for WGS_84 a = 6378137.000, rf = 298.257223563; */
1704 static double M0( double e2 )
1705 { //double e4 = e2*e2;
1706 return GEOD_INV_PI*(1.0 - e2*( 1.0/4.0 + e2*( 3.0/64.0 + e2*(5.0/256.0) )))/2.0;
1709 int geo_direct_wgs_84 ( double alt, double lat1, double lon1, double az1, double s,
1710 double *lat2, double *lon2, double *az2 )
1712 double a = 6378137.000, rf = 298.257223563;
1713 double RADDEG = (GEOD_INV_PI)/180.0, testv = 1.0E-10;
1714 double f = ( rf > 0.0 ? 1.0/rf : 0.0 );
1715 double b = a*(1.0-f), e2 = f*(2.0-f);
1716 double phi1 = lat1*RADDEG, lam1 = lon1*RADDEG;
1717 double sinphi1 = sin(phi1), cosphi1 = cos(phi1);
1718 double azm1 = az1*RADDEG;
1719 double sinaz1 = sin(azm1), cosaz1 = cos(azm1);
1722 if( fabs(s) < 0.01 ) /* distance < centimeter => congruency */
1726 if( *az2 > 360.0 ) *az2 -= 360.0;
1730 if( cosphi1 ) /* non-polar origin */
1731 { /* u1 is reduced latitude */
1732 double tanu1 = sqrt(1.0-e2)*sinphi1/cosphi1;
1733 double sig1 = atan2(tanu1,cosaz1);
1734 double cosu1 = 1.0/sqrt( 1.0 + tanu1*tanu1 ), sinu1 = tanu1*cosu1;
1735 double sinaz = cosu1*sinaz1, cos2saz = 1.0-sinaz*sinaz;
1736 double us = cos2saz*e2/(1.0-e2);
1738 double ta = 1.0+us*(4096.0+us*(-768.0+us*(320.0-175.0*us)))/16384.0,
1739 tb = us*(256.0+us*(-128.0+us*(74.0-47.0*us)))/1024.0,
1741 /* FIRST ESTIMATE OF SIGMA (SIG) */
1742 double first = s/(b*ta); /* !!*/
1744 double c2sigm, sinsig,cossig, temp,denom,rnumer, dlams, dlam;
1746 { c2sigm = cos(2.0*sig1+sig);
1747 sinsig = sin(sig); cossig = cos(sig);
1750 tb*sinsig*(c2sigm+tb*(cossig*(-1.0+2.0*pow(c2sigm,2.0)) -
1751 tb*c2sigm*(-3.0+4.0*pow(sinsig,2.0))*(-3.0+4.0*pow(c2sigm,2.0))/6.0)/4.0);
1753 while( fabs(sig-temp) > testv);
1754 /* LATITUDE OF POINT 2 */
1755 /* DENOMINATOR IN 2 PARTS (TEMP ALSO USED LATER) */
1756 temp = sinu1*sinsig-cosu1*cossig*cosaz1;
1757 denom = (1.0-f)*sqrt(sinaz*sinaz+temp*temp);
1759 rnumer = sinu1*cossig+cosu1*sinsig*cosaz1;
1760 *lat2 = atan2(rnumer,denom)/RADDEG;
1761 /* DIFFERENCE IN LONGITUDE ON AUXILARY SPHERE (DLAMS ) */
1762 rnumer = sinsig*sinaz1;
1763 denom = cosu1*cossig-sinu1*sinsig*cosaz1;
1764 dlams = atan2(rnumer,denom);
1766 tc = f*cos2saz*(4.0+f*(4.0-3.0*cos2saz))/16.0;
1767 /* DIFFERENCE IN LONGITUDE */
1768 dlam = dlams-(1.0-tc)*f*sinaz*(sig+tc*sinsig*(c2sigm+tc*cossig*(-1.0+2.0*
1770 *lon2 = (lam1+dlam)/RADDEG;
1771 if(*lon2 > 180.0 ) *lon2 -= 360.0;
1772 if(*lon2 < -180.0 ) *lon2 += 360.0;
1773 /* AZIMUTH - FROM NORTH */
1774 *az2 = atan2(-sinaz,temp)/RADDEG;
1775 if( fabs(*az2) < testv ) *az2 = 0.0;
1776 if( *az2 < 0.0) *az2 += 360.0;
1779 else /* phi1 == 90 degrees, polar origin */
1780 { double dM = a*M0(e2) - s;
1781 double paz = ( phi1 < 0.0 ? 180.0 : 0.0 );
1782 return geo_direct_wgs_84( alt, 0.0, lon1, paz, dM,lat2,lon2,az2 );
1786 int geo_inverse_wgs_84( double alt, double lat1, double lon1, double lat2,
1787 double lon2, double *az1, double *az2, double *s )
1789 double a = 6378137.000, rf = 298.257223563;
1791 double RADDEG = (GEOD_INV_PI)/180.0, testv = 1.0E-10;
1792 double f = ( rf > 0.0 ? 1.0/rf : 0.0 );
1793 double b = a*(1.0-f), e2 = f*(2.0-f);
1794 double phi1 = lat1*RADDEG, lam1 = lon1*RADDEG;
1795 double sinphi1 = sin(phi1), cosphi1 = cos(phi1);
1796 double phi2 = lat2*RADDEG, lam2 = lon2*RADDEG;
1797 double sinphi2 = sin(phi2), cosphi2 = cos(phi2);
1799 if( (fabs(lat1-lat2) < testv &&
1800 ( fabs(lon1-lon2) < testv) || fabs(lat1-90.0) < testv ) )
1801 { /* TWO STATIONS ARE IDENTICAL : SET DISTANCE & AZIMUTHS TO ZERO */
1802 *az1 = 0.0; *az2 = 0.0; *s = 0.0;
1805 if( fabs(cosphi1) < testv ) /* initial point is polar */
1807 int k = geo_inverse_wgs_84( alt, lat2,lon2,lat1,lon1, az1,az2,s );
1808 b = *az1; *az1 = *az2; *az2 = b;
1811 if( fabs(cosphi2) < testv ) /* terminal point is polar */
1813 int k = geo_inverse_wgs_84( alt, lat1,lon1,lat1,lon1+180.0,
1816 *az2 = *az1 + 180.0;
1817 if( *az2 > 360.0 ) *az2 -= 360.0;
1819 } else /* Geodesic passes through the pole (antipodal) */
1820 if( (fabs( fabs(lon1-lon2) - 180 ) < testv) &&
1821 (fabs(lat1+lat2) < testv) )
1824 geo_inverse_wgs_84( alt, lat1,lon1, lat1,lon2, az1,az2, &s1 );
1825 geo_inverse_wgs_84( alt, lat2,lon2, lat1,lon2, az1,az2, &s2 );
1829 } else /* antipodal and polar points don't get here */
1831 double dlam = lam2 - lam1, dlams = dlam;
1832 double sdlams,cdlams, sig,sinsig,cossig, sinaz,
1834 double tc,temp, us,rnumer,denom, ta,tb;
1835 double cosu1,sinu1, sinu2,cosu2;
1836 /* Reduced latitudes */
1837 temp = (1.0-f)*sinphi1/cosphi1;
1838 cosu1 = 1.0/sqrt(1.0+temp*temp);
1840 temp = (1.0-f)*sinphi2/cosphi2;
1841 cosu2 = 1.0/sqrt(1.0+temp*temp);
1845 sdlams = sin(dlams), cdlams = cos(dlams);
1846 sinsig = sqrt(pow(cosu2*sdlams,2.0)+
1847 pow(cosu1*sinu2-sinu1*cosu2*cdlams,2.0));
1848 cossig = sinu1*sinu2+cosu1*cosu2*cdlams;
1850 sig = atan2(sinsig,cossig);
1851 sinaz = cosu1*cosu2*sdlams/sinsig;
1852 cos2saz = 1.0-sinaz*sinaz;
1853 c2sigm = (sinu1 == 0.0 || sinu2 == 0.0 ? cossig :
1854 cossig-2.0*sinu1*sinu2/cos2saz);
1855 tc = f*cos2saz*(4.0+f*(4.0-3.0*cos2saz))/16.0;
1857 dlams = dlam+(1.0-tc)*f*sinaz*
1859 (c2sigm+tc*cossig*(-1.0+2.0*pow(c2sigm,2.0))));
1860 if (fabs(dlams) > GEOD_INV_PI && iter++ > 50)
1862 } while ( fabs(temp-dlams) > testv);
1864 us = cos2saz*(pow(a,2.0)-pow(b,2.0))/pow(b,2.0); /* !! */
1865 /* BACK AZIMUTH FROM NORTH */
1866 rnumer = -(cosu1*sdlams);
1867 denom = sinu1*cosu2-cosu1*sinu2*cdlams;
1868 *az2 = atan2(rnumer,denom)/RADDEG;
1869 if( fabs(*az2) < testv ) *az2 = 0.0;
1870 if(*az2 < 0.0) *az2 += 360.0;
1871 /* FORWARD AZIMUTH FROM NORTH */
1872 rnumer = cosu2*sdlams;
1873 denom = cosu1*sinu2-sinu1*cosu2*cdlams;
1874 *az1 = atan2(rnumer,denom)/RADDEG;
1875 if( fabs(*az1) < testv ) *az1 = 0.0;
1876 if(*az1 < 0.0) *az1 += 360.0;
1878 ta = 1.0+us*(4096.0+us*(-768.0+us*(320.0-175.0*us)))/
1880 tb = us*(256.0+us*(-128.0+us*(74.0-47.0*us)))/1024.0;
1881 /* GEODETIC DISTANCE */
1882 *s = b*ta*(sig-tb*sinsig*
1883 (c2sigm+tb*(cossig*(-1.0+2.0*pow(c2sigm,2.0))-tb*
1884 c2sigm*(-3.0+4.0*pow(sinsig,2.0))*
1885 (-3.0+4.0*pow(c2sigm,2.0))/6.0)/