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 <Main/options.hxx>
41 // The below routines were copied right from hud.c ( I hate reinventing
42 // the wheel more than necessary)
44 // The following routines obtain information concerntin the aircraft's
45 // current state and return it to calling instrument display routines.
46 // They should eventually be member functions of the aircraft.
50 static double get_speed( void ) {
51 return( current_aircraft.fdm_state->get_V_equiv_kts() );
54 static double get_aoa( void )
56 return( current_aircraft.fdm_state->get_Gamma_vert_rad() * RAD_TO_DEG );
59 static double fgAPget_roll( void )
61 return( current_aircraft.fdm_state->get_Phi() * RAD_TO_DEG );
64 static double get_pitch( void )
66 return( current_aircraft.fdm_state->get_Theta() );
69 double fgAPget_heading( void )
71 return( current_aircraft.fdm_state->get_Psi() * RAD_TO_DEG );
74 static double fgAPget_altitude( void )
76 return( current_aircraft.fdm_state->get_Altitude() * FEET_TO_METER );
79 static double fgAPget_climb( void )
81 // return in meters per minute
82 return( current_aircraft.fdm_state->get_Climb_Rate() * FEET_TO_METER * 60 );
85 static double get_sideslip( void )
87 return( current_aircraft.fdm_state->get_Beta() );
90 static double fgAPget_agl( void )
94 agl = current_aircraft.fdm_state->get_Altitude() * FEET_TO_METER
100 // End of copied section. ( thanks for the wheel :-)
102 // Local Prototype section
104 double LinearExtrapolate( double x, double x1, double y1, double x2, double y2);
105 double NormalizeDegrees( double Input);
107 // End Local ProtoTypes
109 fgAPDataPtr APDataGlobal; // global variable holding the AP info
110 // I want this gone. Data should be in aircraft structure
113 bool fgAPHeadingEnabled( void )
117 APData = APDataGlobal;
119 // heading hold enabled?
120 return APData->heading_hold;
123 bool fgAPAltitudeEnabled( void )
127 APData = APDataGlobal;
129 // altitude hold or terrain follow enabled?
130 return APData->altitude_hold;
133 bool fgAPTerrainFollowEnabled( void )
137 APData = APDataGlobal;
139 // altitude hold or terrain follow enabled?
140 return APData->terrain_follow ;
143 bool fgAPAutoThrottleEnabled( void )
147 APData = APDataGlobal;
149 // autothrottle enabled?
150 return APData->auto_throttle;
153 void fgAPAltitudeAdjust( double inc )
155 // Remove at a later date
156 fgAPDataPtr APData = APDataGlobal;
159 double target_alt, target_agl;
161 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
162 target_alt = APData->TargetAltitude * METER_TO_FEET;
163 target_agl = APData->TargetAGL * METER_TO_FEET;
165 target_alt = APData->TargetAltitude;
166 target_agl = APData->TargetAGL;
169 target_alt = (int)(target_alt / inc) * inc + inc;
170 target_agl = (int)(target_agl / inc) * inc + inc;
172 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET ) {
173 target_alt *= FEET_TO_METER;
174 target_agl *= FEET_TO_METER;
177 APData->TargetAltitude = target_alt;
178 APData->TargetAGL = target_agl;
181 void fgAPAltitudeSet( double new_altitude ) {
182 // Remove at a later date
183 fgAPDataPtr APData = APDataGlobal;
185 double target_alt = new_altitude;
187 if ( current_options.get_units() == fgOPTIONS::FG_UNITS_FEET )
188 target_alt = new_altitude * FEET_TO_METER;
190 if( target_alt < scenery.cur_elev )
191 target_alt = scenery.cur_elev;
193 APData->TargetAltitude = target_alt;
196 void fgAPHeadingAdjust( double inc )
199 APData = APDataGlobal;
201 double target = (int)(APData->TargetHeading / inc) * inc + inc;
203 APData->TargetHeading = NormalizeDegrees(target);
206 void fgAPHeadingSet( double new_heading ) {
207 fgAPDataPtr APData = APDataGlobal;
209 new_heading = NormalizeDegrees( new_heading );
210 APData->TargetHeading = new_heading;
213 void fgAPAutoThrottleAdjust( double inc )
216 APData = APDataGlobal;
218 double target = (int)(APData->TargetSpeed / inc) * inc + inc;
220 APData->TargetSpeed = target;
225 fgAPDataPtr APData = APDataGlobal;
227 if( fgAPTerrainFollowEnabled() )
228 fgAPToggleTerrainFollow( );
230 if( fgAPAltitudeEnabled() )
231 fgAPToggleAltitude();
233 if( fgAPHeadingEnabled() )
236 if( fgAPAutoThrottleEnabled() )
237 fgAPToggleAutoThrottle();
239 APData->TargetHeading = 0.0; // default direction, due north
240 APData->TargetAltitude = 3000; // default altitude in meters
241 APData->alt_error_accum = 0.0;
244 #define mySlider puSlider
246 /// These statics will eventually go into the class
247 /// they are just here while I am experimenting -- NHV :-)
249 static double MaxRollAdjust; // MaxRollAdjust = 2 * APData->MaxRoll;
250 static double RollOutAdjust; // RollOutAdjust = 2 * APData->RollOut;
251 static double MaxAileronAdjust; // MaxAileronAdjust = 2 * APData->MaxAileron;
252 static double RollOutSmoothAdjust; // RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
254 static float MaxRollValue; // 0.1 -> 1.0
255 static float RollOutValue;
256 static float MaxAileronValue;
257 static float RollOutSmoothValue;
259 static float TmpMaxRollValue; // for cancel operation
260 static float TmpRollOutValue;
261 static float TmpMaxAileronValue;
262 static float TmpRollOutSmoothValue;
264 static puDialogBox *APAdjustDialog;
265 static puFrame *APAdjustFrame;
266 static puText *APAdjustDialogMessage;
267 static puFont APAdjustLegendFont;
268 static puFont APAdjustLabelFont;
270 static puOneShot *APAdjustOkButton;
271 static puOneShot *APAdjustResetButton;
272 static puOneShot *APAdjustCancelButton;
274 //static puButton *APAdjustDragButton;
276 static puText *APAdjustMaxRollTitle;
277 static puText *APAdjustRollOutTitle;
278 static puText *APAdjustMaxAileronTitle;
279 static puText *APAdjustRollOutSmoothTitle;
281 static puText *APAdjustMaxAileronText;
282 static puText *APAdjustMaxRollText;
283 static puText *APAdjustRollOutText;
284 static puText *APAdjustRollOutSmoothText;
286 static mySlider *APAdjustHS0;
287 static mySlider *APAdjustHS1;
288 static mySlider *APAdjustHS2;
289 static mySlider *APAdjustHS3;
291 static char SliderText[4][8];
293 // THIS NEEDS IMPROVEMENT !!!!!!!!!!!!!
294 static int scan_number(char *s, double *new_value)
299 char *WordBufPtr = WordBuf;
304 *WordBufPtr++ = *cptr++;
306 while (isdigit(*cptr) ) {
307 *WordBufPtr++ = *cptr++;
311 *WordBufPtr++ = *cptr++; // put the '.' into the string
312 while (isdigit(*cptr)) {
313 *WordBufPtr++ = *cptr++;
318 sscanf(WordBuf, "%lf", new_value);
325 ///////// AutoPilot New Heading Dialog
327 static puDialogBox *ApHeadingDialog;
328 static puFrame *ApHeadingDialogFrame;
329 static puText *ApHeadingDialogMessage;
330 static puInput *ApHeadingDialogInput;
331 static puOneShot *ApHeadingDialogOkButton;
332 static puOneShot *ApHeadingDialogCancelButton;
334 void ApHeadingDialog_Cancel(puObject *)
336 ApHeadingDialogInput->rejectInput();
337 FG_POP_PUI_DIALOG( ApHeadingDialog );
340 void ApHeadingDialog_OK (puObject *me)
345 ApHeadingDialogInput -> getValue( &c );
349 if( scan_number( c, &NewHeading ) )
351 if(!fgAPHeadingEnabled())
353 fgAPHeadingSet( NewHeading );
357 s += " is not a valid number.";
360 ApHeadingDialog_Cancel(me);
361 if( error ) mkDialog(s.c_str());
364 void NewHeading(puObject *cb)
366 // string ApHeadingLabel( "Enter New Heading" );
367 // ApHeadingDialogMessage -> setLabel(ApHeadingLabel.c_str());
368 ApHeadingDialogInput -> acceptInput();
369 FG_PUSH_PUI_DIALOG( ApHeadingDialog );
372 void NewHeadingInit(void)
374 // printf("NewHeadingInit\n");
375 char NewHeadingLabel[] = "Enter New Heading";
378 float heading = fgAPget_heading();
380 (puGetStringWidth( puGetDefaultLabelFont(), NewHeadingLabel ) /2 );
382 ApHeadingDialog = new puDialogBox (150, 50);
384 ApHeadingDialogFrame = new puFrame (0, 0, 260, 150);
386 ApHeadingDialogMessage = new puText (len, 110);
387 ApHeadingDialogMessage -> setDefaultValue (NewHeadingLabel);
388 ApHeadingDialogMessage -> getDefaultValue (&s);
389 ApHeadingDialogMessage -> setLabel (s);
391 ApHeadingDialogInput = new puInput ( 50, 70, 210, 100 );
392 ApHeadingDialogInput -> setValue ( heading );
394 ApHeadingDialogOkButton = new puOneShot (50, 10, 110, 50);
395 ApHeadingDialogOkButton -> setLegend (gui_msg_OK);
396 ApHeadingDialogOkButton -> makeReturnDefault (TRUE);
397 ApHeadingDialogOkButton -> setCallback (ApHeadingDialog_OK);
399 ApHeadingDialogCancelButton = new puOneShot (140, 10, 210, 50);
400 ApHeadingDialogCancelButton -> setLegend (gui_msg_CANCEL);
401 ApHeadingDialogCancelButton -> setCallback (ApHeadingDialog_Cancel);
404 FG_FINALIZE_PUI_DIALOG( ApHeadingDialog );
407 ///////// AutoPilot New Altitude Dialog
409 static puDialogBox *ApAltitudeDialog = 0;
410 static puFrame *ApAltitudeDialogFrame = 0;
411 static puText *ApAltitudeDialogMessage = 0;
412 static puInput *ApAltitudeDialogInput = 0;
414 static puOneShot *ApAltitudeDialogOkButton = 0;
415 static puOneShot *ApAltitudeDialogCancelButton = 0;
417 void ApAltitudeDialog_Cancel(puObject *)
419 ApAltitudeDialogInput -> rejectInput();
420 FG_POP_PUI_DIALOG( ApAltitudeDialog );
423 void ApAltitudeDialog_OK (puObject *me)
428 ApAltitudeDialogInput->getValue( &c );
432 if( scan_number( c, &NewAltitude) )
434 if(!(fgAPAltitudeEnabled()))
435 fgAPToggleAltitude();
436 fgAPAltitudeSet( NewAltitude );
440 s += " is not a valid number.";
443 ApAltitudeDialog_Cancel(me);
444 if( error ) mkDialog(s.c_str());
447 void NewAltitude(puObject *cb)
449 ApAltitudeDialogInput -> acceptInput();
450 FG_PUSH_PUI_DIALOG( ApAltitudeDialog );
453 void NewAltitudeInit(void)
455 // printf("NewAltitudeInit\n");
456 char NewAltitudeLabel[] = "Enter New Altitude";
459 float alt = current_aircraft.fdm_state->get_Altitude();
461 (puGetStringWidth( puGetDefaultLabelFont(), NewAltitudeLabel )/2);
463 ApAltitudeDialog = new puDialogBox (150, 50);
465 ApAltitudeDialogFrame = new puFrame (0, 0, 260, 150);
466 ApAltitudeDialogMessage = new puText (len, 110);
467 ApAltitudeDialogMessage -> setDefaultValue (NewAltitudeLabel);
468 ApAltitudeDialogMessage -> getDefaultValue (&s);
469 ApAltitudeDialogMessage -> setLabel (s);
471 ApAltitudeDialogInput = new puInput ( 50, 70, 210, 100 );
472 ApAltitudeDialogInput -> setValue ( alt );
473 // Uncomment the next line to have input active on startup
474 // ApAltitudeDialogInput -> acceptInput ( );
475 // cursor at begining or end of line ?
478 // ApAltitudeDialogInput -> setCursor ( len );
479 // ApAltitudeDialogInput -> setSelectRegion ( 5, 9 );
481 ApAltitudeDialogOkButton = new puOneShot (50, 10, 110, 50);
482 ApAltitudeDialogOkButton -> setLegend (gui_msg_OK);
483 ApAltitudeDialogOkButton -> makeReturnDefault (TRUE);
484 ApAltitudeDialogOkButton -> setCallback (ApAltitudeDialog_OK);
486 ApAltitudeDialogCancelButton = new puOneShot (140, 10, 210, 50);
487 ApAltitudeDialogCancelButton -> setLegend (gui_msg_CANCEL);
488 ApAltitudeDialogCancelButton -> setCallback (ApAltitudeDialog_Cancel);
491 FG_FINALIZE_PUI_DIALOG( ApAltitudeDialog );
494 /////// simple AutoPilot GAIN / LIMITS ADJUSTER
497 #define fgAP_CLAMP(val,min,max) \
498 ( (val) = (val) > (max) ? (max) : (val) < (min) ? (min) : (val) )
500 static void maxroll_adj(puObject *hs)
504 APData = APDataGlobal;
506 hs -> getValue ( &val ) ;
507 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
508 // printf ( "maxroll_adj( %p ) %f %f\n", hs, val, MaxRollAdjust * val ) ;
509 APData->MaxRoll = MaxRollAdjust * val;
510 sprintf(SliderText[0],"%05.2f", APData->MaxRoll );
511 APAdjustMaxRollText -> setLabel ( SliderText[0] ) ;
514 static void rollout_adj(puObject *hs)
518 APData = APDataGlobal;
520 hs -> getValue ( &val ) ;
521 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
522 // printf ( "rollout_adj( %p ) %f %f\n", hs, val, RollOutAdjust * val ) ;
523 APData->RollOut = RollOutAdjust * val;
524 sprintf(SliderText[1],"%05.2f", APData->RollOut );
525 APAdjustRollOutText -> setLabel ( SliderText[1] );
528 static void maxaileron_adj( puObject *hs )
532 APData = APDataGlobal;
534 hs -> getValue ( &val ) ;
535 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
536 // printf ( "maxaileron_adj( %p ) %f %f\n", hs, val, MaxAileronAdjust * val ) ;
537 APData->MaxAileron = MaxAileronAdjust * val;
538 sprintf(SliderText[3],"%05.2f", APData->MaxAileron );
539 APAdjustMaxAileronText -> setLabel ( SliderText[3] );
542 static void rolloutsmooth_adj( puObject *hs )
546 APData = APDataGlobal;
548 hs -> getValue ( &val ) ;
549 fgAP_CLAMP ( val, 0.1, 1.0 ) ;
550 // printf ( "rolloutsmooth_adj( %p ) %f %f\n", hs, val, RollOutSmoothAdjust * val ) ;
551 APData->RollOutSmooth = RollOutSmoothAdjust * val;
552 sprintf(SliderText[2],"%5.2f", APData->RollOutSmooth );
553 APAdjustRollOutSmoothText -> setLabel ( SliderText[2] );
557 static void goAwayAPAdjust (puObject *)
559 FG_POP_PUI_DIALOG( APAdjustDialog );
562 void cancelAPAdjust(puObject *self)
564 fgAPDataPtr APData = APDataGlobal;
566 APData->MaxRoll = TmpMaxRollValue;
567 APData->RollOut = TmpRollOutValue;
568 APData->MaxAileron = TmpMaxAileronValue;
569 APData->RollOutSmooth = TmpRollOutSmoothValue;
571 goAwayAPAdjust(self);
574 void resetAPAdjust(puObject *self)
576 fgAPDataPtr APData = APDataGlobal;
578 APData->MaxRoll = MaxRollAdjust / 2;
579 APData->RollOut = RollOutAdjust / 2;
580 APData->MaxAileron = MaxAileronAdjust / 2;
581 APData->RollOutSmooth = RollOutSmoothAdjust / 2;
583 FG_POP_PUI_DIALOG( APAdjustDialog );
588 void fgAPAdjust( puObject * )
590 fgAPDataPtr APData = APDataGlobal;
592 TmpMaxRollValue = APData->MaxRoll;
593 TmpRollOutValue = APData->RollOut;
594 TmpMaxAileronValue = APData->MaxAileron;
595 TmpRollOutSmoothValue = APData->RollOutSmooth;
597 MaxRollValue = APData->MaxRoll / MaxRollAdjust;
598 RollOutValue = APData->RollOut / RollOutAdjust;
599 MaxAileronValue = APData->MaxAileron / MaxAileronAdjust;
600 RollOutSmoothValue = APData->RollOutSmooth / RollOutSmoothAdjust;
602 APAdjustHS0 -> setValue ( MaxRollValue ) ;
603 APAdjustHS1 -> setValue ( RollOutValue ) ;
604 APAdjustHS2 -> setValue ( RollOutSmoothValue ) ;
605 APAdjustHS3 -> setValue ( MaxAileronValue ) ;
607 FG_PUSH_PUI_DIALOG( APAdjustDialog );
611 // Done once at system initialization
612 // Done once at system initialization
613 void fgAPAdjustInit( void ) {
615 // printf("fgAPAdjustInit\n");
616 #define HORIZONTAL FALSE
620 int DialogWidth = 230;
622 char Label[] = "AutoPilot Adjust";
625 fgAPDataPtr APData = APDataGlobal;
627 int labelX = (DialogWidth / 2) -
628 (puGetStringWidth( puGetDefaultLabelFont(), Label ) / 2);
629 labelX -= 30; // KLUDGEY
634 int slider_width = 210;
635 int slider_title_x = 15;
636 int slider_value_x = 160;
637 float slider_delta = 0.1f;
639 TmpMaxRollValue = APData-> MaxRoll;
640 TmpRollOutValue = APData-> RollOut;
641 TmpMaxAileronValue = APData-> MaxAileron;
642 TmpRollOutSmoothValue = APData-> RollOutSmooth;
644 MaxRollValue = APData-> MaxRoll / MaxRollAdjust;
645 RollOutValue = APData-> RollOut / RollOutAdjust;
646 MaxAileronValue = APData-> MaxAileron / MaxAileronAdjust;
647 RollOutSmoothValue = APData-> RollOutSmooth / RollOutSmoothAdjust;
649 puGetDefaultFonts ( &APAdjustLegendFont, &APAdjustLabelFont );
650 APAdjustDialog = new puDialogBox ( DialogX, DialogY ); {
651 int horiz_slider_height = puGetStringHeight (APAdjustLabelFont) +
652 puGetStringDescender (APAdjustLabelFont) +
653 PUSTR_TGAP + PUSTR_BGAP + 5;
655 APAdjustFrame = new puFrame ( 0, 0,
656 DialogWidth, 85 + nSliders * horiz_slider_height );
658 APAdjustDialogMessage = new puText ( labelX, 52 + nSliders * horiz_slider_height );
659 APAdjustDialogMessage -> setDefaultValue ( Label );
660 APAdjustDialogMessage -> getDefaultValue ( &s );
661 APAdjustDialogMessage -> setLabel ( s );
663 APAdjustHS0 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
664 APAdjustHS0-> setDelta ( slider_delta ) ;
665 APAdjustHS0-> setValue ( MaxRollValue ) ;
666 APAdjustHS0-> setCBMode ( PUSLIDER_DELTA ) ;
667 APAdjustHS0-> setCallback ( maxroll_adj ) ;
669 sprintf( SliderText[ 0 ], "%05.2f", APData->MaxRoll );
670 APAdjustMaxRollTitle = new puText ( slider_title_x, slider_y ) ;
671 APAdjustMaxRollTitle-> setDefaultValue ( "MaxRoll" ) ;
672 APAdjustMaxRollTitle-> getDefaultValue ( &s ) ;
673 APAdjustMaxRollTitle-> setLabel ( s ) ;
674 APAdjustMaxRollText = new puText ( slider_value_x, slider_y ) ;
675 APAdjustMaxRollText-> setLabel ( SliderText[ 0 ] ) ;
677 slider_y += horiz_slider_height;
679 APAdjustHS1 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
680 APAdjustHS1-> setDelta ( slider_delta ) ;
681 APAdjustHS1-> setValue ( RollOutValue ) ;
682 APAdjustHS1-> setCBMode ( PUSLIDER_DELTA ) ;
683 APAdjustHS1-> setCallback ( rollout_adj ) ;
685 sprintf( SliderText[ 1 ], "%05.2f", APData->RollOut );
686 APAdjustRollOutTitle = new puText ( slider_title_x, slider_y ) ;
687 APAdjustRollOutTitle-> setDefaultValue ( "AdjustRollOut" ) ;
688 APAdjustRollOutTitle-> getDefaultValue ( &s ) ;
689 APAdjustRollOutTitle-> setLabel ( s ) ;
690 APAdjustRollOutText = new puText ( slider_value_x, slider_y ) ;
691 APAdjustRollOutText-> setLabel ( SliderText[ 1 ] );
693 slider_y += horiz_slider_height;
695 APAdjustHS2 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
696 APAdjustHS2-> setDelta ( slider_delta ) ;
697 APAdjustHS2-> setValue ( RollOutSmoothValue ) ;
698 APAdjustHS2-> setCBMode ( PUSLIDER_DELTA ) ;
699 APAdjustHS2-> setCallback ( rolloutsmooth_adj ) ;
701 sprintf( SliderText[ 2 ], "%5.2f", APData->RollOutSmooth );
702 APAdjustRollOutSmoothTitle = new puText ( slider_title_x, slider_y ) ;
703 APAdjustRollOutSmoothTitle-> setDefaultValue ( "RollOutSmooth" ) ;
704 APAdjustRollOutSmoothTitle-> getDefaultValue ( &s ) ;
705 APAdjustRollOutSmoothTitle-> setLabel ( s ) ;
706 APAdjustRollOutSmoothText = new puText ( slider_value_x, slider_y ) ;
707 APAdjustRollOutSmoothText-> setLabel ( SliderText[ 2 ] );
709 slider_y += horiz_slider_height;
711 APAdjustHS3 = new mySlider ( slider_x, slider_y, slider_width, HORIZONTAL ) ;
712 APAdjustHS3-> setDelta ( slider_delta ) ;
713 APAdjustHS3-> setValue ( MaxAileronValue ) ;
714 APAdjustHS3-> setCBMode ( PUSLIDER_DELTA ) ;
715 APAdjustHS3-> setCallback ( maxaileron_adj ) ;
717 sprintf( SliderText[ 3 ], "%05.2f", APData->MaxAileron );
718 APAdjustMaxAileronTitle = new puText ( slider_title_x, slider_y ) ;
719 APAdjustMaxAileronTitle-> setDefaultValue ( "MaxAileron" ) ;
720 APAdjustMaxAileronTitle-> getDefaultValue ( &s ) ;
721 APAdjustMaxAileronTitle-> setLabel ( s ) ;
722 APAdjustMaxAileronText = new puText ( slider_value_x, slider_y ) ;
723 APAdjustMaxAileronText-> setLabel ( SliderText[ 3 ] );
725 APAdjustOkButton = new puOneShot ( 10, 10, 60, 50 );
726 APAdjustOkButton-> setLegend ( gui_msg_OK );
727 APAdjustOkButton-> makeReturnDefault ( TRUE );
728 APAdjustOkButton-> setCallback ( goAwayAPAdjust );
730 APAdjustCancelButton = new puOneShot ( 70, 10, 150, 50 );
731 APAdjustCancelButton-> setLegend ( gui_msg_CANCEL );
732 APAdjustCancelButton-> setCallback ( cancelAPAdjust );
734 APAdjustResetButton = new puOneShot ( 160, 10, 220, 50 );
735 APAdjustResetButton-> setLegend ( gui_msg_RESET );
736 APAdjustResetButton-> setCallback ( resetAPAdjust );
738 FG_FINALIZE_PUI_DIALOG( APAdjustDialog );
743 void fgAPInit( fgAIRCRAFT *current_aircraft )
747 FG_LOG( FG_AUTOPILOT, FG_INFO, "Init AutoPilot Subsystem" );
749 APData = (fgAPDataPtr)calloc(sizeof(fgAPData),1);
751 if (APData == NULL) {
752 // I couldn't get the mem. Dying
753 FG_LOG( FG_AUTOPILOT, FG_ALERT, "No ram for Autopilot. Dying.");
757 APData->heading_hold = false ; // turn the heading hold off
758 APData->altitude_hold = false ; // turn the altitude hold off
760 APData->TargetHeading = 0.0; // default direction, due north
761 APData->TargetAltitude = 3000; // default altitude in meters
762 APData->alt_error_accum = 0.0;
764 // These eventually need to be read from current_aircaft somehow.
768 // the maximum roll, in Deg
770 // the deg from heading to start rolling out at, in Deg
771 APData->RollOut = 30;
772 // how far can I move the aleron from center.
773 APData->MaxAileron= .1;
774 // Smoothing distance for alerion control
775 APData->RollOutSmooth = 10;
778 // the maximum roll, in Deg
779 APData->MaxRoll = 20;
781 // the deg from heading to start rolling out at, in Deg
782 APData->RollOut = 20;
784 // how far can I move the aleron from center.
785 APData->MaxAileron= .2;
787 // Smoothing distance for alerion control
788 APData->RollOutSmooth = 10;
790 //Remove at a later date
791 APDataGlobal = APData;
793 #if !defined( USING_SLIDER_CLASS )
794 MaxRollAdjust = 2 * APData->MaxRoll;
795 RollOutAdjust = 2 * APData->RollOut;
796 MaxAileronAdjust = 2 * APData->MaxAileron;
797 RollOutSmoothAdjust = 2 * APData->RollOutSmooth;
798 #endif // !defined( USING_SLIDER_CLASS )
807 // Remove the following lines when the calling funcitons start
808 // passing in the data pointer
812 APData = APDataGlobal;
815 // heading hold enabled?
816 if ( APData->heading_hold == true ) {
823 NormalizeDegrees( APData->TargetHeading - fgAPget_heading());
824 // figure out how far off we are from desired heading
826 // Now it is time to deterime how far we should be rolled.
827 FG_LOG( FG_AUTOPILOT, FG_DEBUG, "RelHeading: " << RelHeading );
830 // Check if we are further from heading than the roll out point
831 if ( fabs(RelHeading) > APData->RollOut ) {
832 // set Target Roll to Max in desired direction
833 if (RelHeading < 0 ) {
834 TargetRoll = 0-APData->MaxRoll;
836 TargetRoll = APData->MaxRoll;
839 // We have to calculate the Target roll
841 // This calculation engine thinks that the Target roll
842 // should be a line from (RollOut,MaxRoll) to (-RollOut,
843 // -MaxRoll) I hope this works well. If I get ambitious
844 // some day this might become a fancier curve or
847 TargetRoll = LinearExtrapolate( RelHeading, -APData->RollOut,
848 -APData->MaxRoll, APData->RollOut,
852 // Target Roll has now been Found.
854 // Compare Target roll to Current Roll, Generate Rel Roll
856 FG_LOG( FG_COCKPIT, FG_BULK, "TargetRoll: " << TargetRoll );
858 RelRoll = NormalizeDegrees(TargetRoll - fgAPget_roll());
860 // Check if we are further from heading than the roll out smooth point
861 if ( fabs(RelRoll) > APData->RollOutSmooth ) {
862 // set Target Roll to Max in desired direction
864 AileronSet = 0-APData->MaxAileron;
866 AileronSet = APData->MaxAileron;
869 AileronSet = LinearExtrapolate( RelRoll, -APData->RollOutSmooth,
871 APData->RollOutSmooth,
872 APData->MaxAileron );
875 controls.set_aileron( AileronSet );
876 controls.set_rudder( 0.0 );
879 // altitude hold or terrain follow enabled?
880 if ( APData->altitude_hold || APData->terrain_follow ) {
881 double speed, max_climb, error;
882 double prop_error, int_error;
883 double prop_adj, int_adj, total_adj;
885 if ( APData->altitude_hold ) {
886 // normal altitude hold
887 APData->TargetClimbRate =
888 (APData->TargetAltitude - fgAPget_altitude()) * 8.0;
889 } else if ( APData->terrain_follow ) {
890 // brain dead ground hugging with no look ahead
891 APData->TargetClimbRate =
892 ( APData->TargetAGL - fgAPget_agl() ) * 16.0;
894 // just try to zero out rate of climb ...
895 APData->TargetClimbRate = 0.0;
900 if ( speed < 90.0 ) {
902 } else if ( speed < 100.0 ) {
903 max_climb = (speed - 90.0) * 20;
905 max_climb = ( speed - 100.0 ) * 4.0 + 200.0;
908 if ( APData->TargetClimbRate > max_climb ) {
909 APData->TargetClimbRate = max_climb;
912 if ( APData->TargetClimbRate < -400.0 ) {
913 APData->TargetClimbRate = -400.0;
916 error = fgAPget_climb() - APData->TargetClimbRate;
918 // accumulate the error under the curve ... this really should
920 APData->alt_error_accum += error;
922 // calculate integral error, and adjustment amount
923 int_error = APData->alt_error_accum;
924 // printf("error = %.2f int_error = %.2f\n", error, int_error);
925 int_adj = int_error / 8000.0;
927 // caclulate proportional error
929 prop_adj = prop_error / 2000.0;
931 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
932 if ( total_adj > 0.6 ) { total_adj = 0.6; }
933 if ( total_adj < -0.2 ) { total_adj = -0.2; }
935 controls.set_elevator( total_adj );
938 // auto throttle enabled?
939 if ( APData->auto_throttle ) {
941 double prop_error, int_error;
942 double prop_adj, int_adj, total_adj;
944 error = APData->TargetSpeed - get_speed();
946 // accumulate the error under the curve ... this really should
948 APData->speed_error_accum += error;
949 if ( APData->speed_error_accum > 2000.0 ) {
950 APData->speed_error_accum = 2000.0;
952 if ( APData->speed_error_accum < -2000.0 ) {
953 APData->speed_error_accum = -2000.0;
956 // calculate integral error, and adjustment amount
957 int_error = APData->speed_error_accum;
959 // printf("error = %.2f int_error = %.2f\n", error, int_error);
960 int_adj = int_error / 200.0;
962 // caclulate proportional error
964 prop_adj = 0.5 + prop_error / 50.0;
966 total_adj = 0.9 * prop_adj + 0.1 * int_adj;
967 if ( total_adj > 1.0 ) { total_adj = 1.0; }
968 if ( total_adj < 0.0 ) { total_adj = 0.0; }
970 controls.set_throttle( FGControls::ALL_ENGINES, total_adj );
974 if (APData->Mode == 2) // Glide slope hold
979 // First, calculate Relative slope and normalize it
980 RelSlope = NormalizeDegrees( APData->TargetSlope - get_pitch());
982 // Now calculate the elevator offset from current angle
983 if ( abs(RelSlope) > APData->SlopeSmooth )
985 if ( RelSlope < 0 ) // set RelElevator to max in the correct direction
986 RelElevator = -APData->MaxElevator;
988 RelElevator = APData->MaxElevator;
992 RelElevator = LinearExtrapolate(RelSlope,-APData->SlopeSmooth,-APData->MaxElevator,APData->SlopeSmooth,APData->MaxElevator);
995 fgElevMove(RelElevator);
1006 void fgAPSetMode( int mode)
1008 //Remove the following line when the calling funcitons start passing in the data pointer
1011 APData = APDataGlobal;
1014 fgPrintf( FG_COCKPIT, FG_INFO, "APSetMode : %d\n", mode );
1016 APData->Mode = mode; // set the new mode
1020 void fgAPToggleHeading( void )
1022 // Remove at a later date
1025 APData = APDataGlobal;
1028 if ( APData->heading_hold ) {
1029 // turn off heading hold
1030 APData->heading_hold = false;
1032 // turn on heading hold, lock at current heading
1033 APData->heading_hold = true;
1034 APData->TargetHeading = fgAPget_heading();
1037 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetHeading: ("
1038 << APData->heading_hold << ") " << APData->TargetHeading );
1042 void fgAPToggleAltitude( void )
1044 // Remove at a later date
1047 APData = APDataGlobal;
1050 if ( APData->altitude_hold ) {
1051 // turn off altitude hold
1052 APData->altitude_hold = false;
1054 // turn on altitude hold, lock at current altitude
1055 APData->altitude_hold = true;
1056 APData->terrain_follow = false;
1057 APData->TargetAltitude = fgAPget_altitude();
1058 APData->alt_error_accum = 0.0;
1059 // alt_error_queue.erase( alt_error_queue.begin(),
1060 // alt_error_queue.end() );
1063 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAltitude: ("
1064 << APData->altitude_hold << ") " << APData->TargetAltitude );
1068 void fgAPToggleAutoThrottle ( void )
1070 // Remove at a later date
1073 APData = APDataGlobal;
1076 if ( APData->auto_throttle ) {
1077 // turn off altitude hold
1078 APData->auto_throttle = false;
1080 // turn on terrain follow, lock at current agl
1081 APData->auto_throttle = true;
1082 APData->TargetSpeed = get_speed();
1083 APData->speed_error_accum = 0.0;
1086 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetAutoThrottle: ("
1087 << APData->auto_throttle << ") " << APData->TargetSpeed );
1090 void fgAPToggleTerrainFollow( void )
1092 // Remove at a later date
1095 APData = APDataGlobal;
1098 if ( APData->terrain_follow ) {
1099 // turn off altitude hold
1100 APData->terrain_follow = false;
1102 // turn on terrain follow, lock at current agl
1103 APData->terrain_follow = true;
1104 APData->altitude_hold = false;
1105 APData->TargetAGL = fgAPget_agl();
1106 APData->alt_error_accum = 0.0;
1109 FG_LOG( FG_COCKPIT, FG_INFO, " fgAPSetTerrainFollow: ("
1110 << APData->terrain_follow << ") " << APData->TargetAGL );
1113 double LinearExtrapolate( double x,double x1,double y1,double x2,double y2)
1115 // This procedure extrapolates the y value for the x posistion on a line defined by x1,y1; x2,y2
1116 //assert(x1 != x2); // Divide by zero error. Cold abort for now
1118 double m, b, y; // the constants to find in y=mx+b
1119 m=(y2-y1)/(x2-x1); // calculate the m
1120 b= y1- m * x1; // calculate the b
1121 y = m * x + b; // the final calculation
1126 double NormalizeDegrees(double Input)
1128 // normalize the input to the range (-180,180]
1129 // Input should not be greater than -360 to 360. Current rules send the output to an undefined state.