]> git.mxchange.org Git - flightgear.git/blob - Autopilot/autopilot.cxx
A few more altitude-hold refinements. It now appears to be working pretty
[flightgear.git] / Autopilot / autopilot.cxx
1 /**************************************************************************
2  * autopilot.cxx -- autopilot subsystem
3  *
4  * Written by Jeff Goeke-Smith, started April 1998.
5  *
6  * Copyright (C) 1998  Jeff Goeke-Smith, jgoeke@voyager.net
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  * 
23  *
24  **************************************************************************/
25
26
27 #ifdef HAVE_CONFIG_H
28 #  include <config.h>
29 #endif
30
31 #include <assert.h>
32 #include <stdlib.h>
33
34 // #include <list>
35 // #include <Include/fg_stl_config.h>
36
37 #include <Scenery/scenery.hxx>
38
39 // #ifdef NEEDNAMESPACESTD
40 // using namespace std;
41 // #endif
42
43 #include "autopilot.hxx"
44
45 #include <Include/fg_constants.h>
46 #include <Debug/fg_debug.h>
47
48
49 // static list < double > alt_error_queue;
50
51
52 // The below routines were copied right from hud.c ( I hate reinventing
53 // the wheel more than necessary)
54
55 // The following routines obtain information concerntin the aircraft's
56 // current state and return it to calling instrument display routines.
57 // They should eventually be member functions of the aircraft.
58 //
59
60 static double get_throttleval( void )
61 {
62         fgCONTROLS *pcontrols;
63
64   pcontrols = current_aircraft.controls;
65   return pcontrols->throttle[0];     // Hack limiting to one engine
66 }
67
68 static double get_aileronval( void )
69 {
70         fgCONTROLS *pcontrols;
71
72   pcontrols = current_aircraft.controls;
73   return pcontrols->aileron;
74 }
75
76 static double get_elevatorval( void )
77 {
78         fgCONTROLS *pcontrols;
79
80   pcontrols = current_aircraft.controls;
81   return pcontrols->elevator;
82 }
83
84 static double get_elev_trimval( void )
85 {
86         fgCONTROLS *pcontrols;
87
88   pcontrols = current_aircraft.controls;
89   return pcontrols->elevator_trim;
90 }
91
92 static double get_rudderval( void )
93 {
94         fgCONTROLS *pcontrols;
95
96   pcontrols = current_aircraft.controls;
97   return pcontrols->rudder;
98 }
99
100 static double get_speed( void )
101 {
102         fgFLIGHT *f;
103
104         f = current_aircraft.flight;
105         return( FG_V_equiv_kts );    // Make an explicit function call.
106 }
107
108 static double get_aoa( void )
109 {
110         fgFLIGHT *f;
111               
112         f = current_aircraft.flight;
113         return( FG_Gamma_vert_rad * RAD_TO_DEG );
114 }
115
116 static double fgAPget_roll( void )
117 {
118         fgFLIGHT *f;
119
120         f = current_aircraft.flight;
121         return( FG_Phi * RAD_TO_DEG );
122 }
123
124 static double get_pitch( void )
125 {
126         fgFLIGHT *f;
127               
128         f = current_aircraft.flight;
129         return( FG_Theta );
130 }
131
132 double fgAPget_heading( void )
133 {
134         fgFLIGHT *f;
135
136         f = current_aircraft.flight;
137         return( FG_Psi * RAD_TO_DEG );
138 }
139
140 static double fgAPget_altitude( void )
141 {
142         fgFLIGHT *f;
143
144         f = current_aircraft.flight;
145
146         return( FG_Altitude * FEET_TO_METER /* -rough_elev */ );
147 }
148
149 static double fgAPget_climb( void )
150 {
151         fgFLIGHT *f;
152
153         f = current_aircraft.flight;
154
155         // return in meters per minute
156         return( FG_Climb_Rate * FEET_TO_METER * 60 );
157 }
158
159 static double get_sideslip( void )
160 {
161         fgFLIGHT *f;
162         
163         f = current_aircraft.flight;
164         
165         return( FG_Beta );
166 }
167
168 static double fgAPget_agl( void )
169 {
170         fgFLIGHT *f;
171         double agl;
172
173         f = current_aircraft.flight;
174         agl = FG_Altitude * FEET_TO_METER - scenery.cur_elev;
175
176         return( agl );
177 }
178
179 // End of copied section.  ( thanks for the wheel :-)
180
181 // Local Prototype section
182
183 double LinearExtrapolate( double x,double x1, double y1, double x2, double y2);
184 double NormalizeDegrees( double Input);
185
186 // End Local ProtoTypes
187
188 fgAPDataPtr APDataGlobal;       // global variable holding the AP info
189                                 // I want this gone.  Data should be in aircraft structure
190
191
192
193 void fgAPInit( fgAIRCRAFT *current_aircraft )
194 {
195         fgAPDataPtr APData ;
196
197         fgPrintf( FG_AUTOPILOT, FG_INFO, "Init AutoPilot Subsystem\n" );
198
199         APData  = (fgAPDataPtr)calloc(sizeof(fgAPData),1);
200         
201         if (APData == NULL) // I couldn't get the mem.  Dying
202                 fgPrintf( FG_AUTOPILOT, FG_EXIT,"No ram for Autopilot. Dying.\n");
203                 
204         APData->heading_hold = 0 ;     // turn the heading hold off
205         APData->altitude_hold = 0 ;    // turn the altitude hold off
206
207         APData->TargetHeading = 0.0;    // default direction, due north
208         APData->TargetAltitude = 3000;  // default altitude in meters
209         APData->alt_error_accum = 0.0;
210
211         // These eventually need to be read from current_aircaft somehow.
212         
213         APData->MaxRoll = 7;            // the maximum roll, in Deg
214         APData->RollOut = 30;           // the deg from heading to start rolling out at, in Deg
215         APData->MaxAileron= .1;         // how far can I move the aleron from center.
216         APData->RollOutSmooth = 10;     // Smoothing distance for alerion control
217         
218         //Remove at a later date
219         APDataGlobal = APData;
220         
221 };
222
223 int fgAPRun( void )
224 {
225     // Remove the following lines when the calling funcitons start
226     // passing in the data pointer
227
228     fgAPDataPtr APData;
229         
230     APData = APDataGlobal;
231     // end section
232                 
233     // heading hold enabled?
234     if ( APData->heading_hold == 1 ) {
235         double RelHeading;
236         double TargetRoll;
237         double RelRoll;
238         double AileronSet;
239                 
240         RelHeading =  
241             NormalizeDegrees( APData->TargetHeading - fgAPget_heading());
242         // figure out how far off we are from desired heading
243                   
244         // Now it is time to deterime how far we should be rolled.
245         fgPrintf( FG_AUTOPILOT, FG_DEBUG, "RelHeading: %f\n", RelHeading);
246                 
247                 
248         // Check if we are further from heading than the roll out point
249         if ( fabs(RelHeading) > APData->RollOut ) {
250             // set Target Roll to Max in desired direction
251             if (RelHeading < 0 ) {
252                 TargetRoll = 0-APData->MaxRoll;
253             } else {
254                 TargetRoll = APData->MaxRoll;
255             }
256         } else {
257             // We have to calculate the Target roll
258
259             // This calculation engine thinks that the Target roll
260             // should be a line from (RollOut,MaxRoll) to (-RollOut,
261             // -MaxRoll) I hope this works well.  If I get ambitious
262             // some day this might become a fancier curve or
263             // something.
264
265             TargetRoll = LinearExtrapolate( RelHeading, -APData->RollOut,
266                                             -APData->MaxRoll, APData->RollOut,
267                                             APData->MaxRoll );
268         }
269                 
270         // Target Roll has now been Found.
271
272         // Compare Target roll to Current Roll, Generate Rel Roll
273
274         fgPrintf( FG_COCKPIT, FG_BULK, "TargetRoll: %f\n", TargetRoll);
275                 
276         RelRoll = NormalizeDegrees(TargetRoll - fgAPget_roll());
277
278         // Check if we are further from heading than the roll out smooth point
279         if ( fabs(RelRoll) > APData->RollOutSmooth ) {
280             // set Target Roll to Max in desired direction
281             if (RelRoll < 0 ) {
282                 AileronSet = 0-APData->MaxAileron;
283             } else {
284                 AileronSet = APData->MaxAileron;
285             }
286         } else {
287             AileronSet = LinearExtrapolate( RelRoll, -APData->RollOutSmooth,
288                                             -APData->MaxAileron,
289                                             APData->RollOutSmooth,
290                                             APData->MaxAileron );
291         }
292                 
293         fgAileronSet(AileronSet);
294         fgRudderSet(0.0);
295     }
296
297     // altitude hold enabled?
298     if ( APData->altitude_hold == 1 ) {
299         double error;
300         double prop_error, int_error;
301         double prop_adj, int_adj, total_adj;
302
303         // normal altitude hold
304         APData->TargetClimbRate = 
305             (APData->TargetAltitude - fgAPget_altitude()) * 8.0;
306         
307         // brain dead ground hugging with no look ahead
308         // APData->TargetClimbRate = ( 250 - fgAPget_agl() ) * 8.0;
309
310         // just try to zero out rate of climb ...
311         // APData->TargetClimbRate = 0.0;
312
313         if ( APData->TargetClimbRate > 200.0 ) {
314             APData->TargetClimbRate = 200.0;
315         }
316         if ( APData->TargetClimbRate < -200.0 ) {
317             APData->TargetClimbRate = -200.0;
318         }
319
320         error = fgAPget_climb() - APData->TargetClimbRate;
321
322         // push current error onto queue and add into accumulator
323         // alt_error_queue.push_back(error);
324         APData->alt_error_accum += error;
325
326         // if queue size larger than 60 ... pop front and subtract
327         // from accumulator
328         // size = alt_error_queue.size();
329         // if ( size > 300 ) {
330             // APData->alt_error_accum -= alt_error_queue.front();
331             // alt_error_queue.pop_front();
332             // size--;
333         // }
334
335         // calculate integral error, and adjustment amount
336         int_error = APData->alt_error_accum;
337         // printf("error = %.2f  int_error = %.2f\n", error, int_error);
338         int_adj = int_error / 8000.0;
339         
340         // caclulate proportional error
341         prop_error = error;
342         prop_adj = prop_error / 2000.0;
343
344         total_adj = 0.9 * prop_adj + 0.1 * int_adj;
345         if ( total_adj >  0.4 ) { total_adj =  0.4; }
346         if ( total_adj < -0.3 ) { total_adj = -0.3; }
347
348         fgElevSet( total_adj );
349     }
350
351     /*
352     if (APData->Mode == 2) // Glide slope hold
353     {
354         double RelSlope;
355         double RelElevator;
356                 
357         // First, calculate Relative slope and normalize it
358         RelSlope = NormalizeDegrees( APData->TargetSlope - get_pitch());
359         
360         // Now calculate the elevator offset from current angle
361         if ( abs(RelSlope) > APData->SlopeSmooth )
362         {
363             if ( RelSlope < 0 )         //  set RelElevator to max in the correct direction
364                 RelElevator = -APData->MaxElevator;
365             else
366                 RelElevator = APData->MaxElevator;
367         }
368                 
369         else
370             RelElevator = LinearExtrapolate(RelSlope,-APData->SlopeSmooth,-APData->MaxElevator,APData->SlopeSmooth,APData->MaxElevator);
371                 
372         // set the elevator
373         fgElevMove(RelElevator);
374                 
375     }
376     */
377         
378     // Ok, we are done
379     return 0;
380
381 }
382
383 /*
384 void fgAPSetMode( int mode)
385 {
386     //Remove the following line when the calling funcitons start passing in the data pointer
387     fgAPDataPtr APData;
388          
389     APData = APDataGlobal;
390     // end section
391          
392     fgPrintf( FG_COCKPIT, FG_INFO, "APSetMode : %d\n", mode );
393          
394     APData->Mode = mode;  // set the new mode
395 }
396 */
397
398 void fgAPToggleHeading( void )
399 {
400     // Remove at a later date
401     fgAPDataPtr APData;
402
403     APData = APDataGlobal;
404     // end section
405
406     if ( APData->heading_hold ) {
407         // turn off heading hold
408         APData->heading_hold = 0;
409     } else {
410         // turn on heading hold, lock at current heading
411         APData->heading_hold = 1;
412         APData->TargetHeading = fgAPget_heading();
413     }
414
415     fgPrintf( FG_COCKPIT, FG_INFO, " fgAPSetHeading: (%d) %.2f\n",
416               APData->heading_hold,
417               APData->TargetHeading);
418 }
419                  
420
421 void fgAPToggleAltitude( void )
422 {
423     // Remove at a later date
424     fgAPDataPtr APData;
425
426     APData = APDataGlobal;
427     // end section
428
429     if ( APData->altitude_hold ) {
430         // turn off altitude hold
431         APData->altitude_hold = 0;
432     } else {
433         // turn on altitude hold, lock at current altitude
434         APData->altitude_hold = 1;
435         APData->TargetAltitude = fgAPget_altitude();
436         APData->alt_error_accum = 0.0;
437         // alt_error_queue.erase( alt_error_queue.begin(), 
438         //                        alt_error_queue.end() );
439     }
440
441     fgPrintf( FG_COCKPIT, FG_INFO, " fgAPSetAltitude: (%d) %.2f\n",
442               APData->altitude_hold,
443               APData->TargetAltitude);
444 }
445                  
446
447 double LinearExtrapolate( double x,double x1,double y1,double x2,double y2)
448 {
449     // This procedure extrapolates the y value for the x posistion on a line defined by x1,y1; x2,y2
450     //assert(x1 != x2); // Divide by zero error.  Cold abort for now
451     
452     double m, b, y;             // the constants to find in y=mx+b
453         
454     m=(y2-y1)/(x2-x1);  // calculate the m
455         
456     b= y1- m * x1;              // calculate the b
457         
458     y = m * x + b;              // the final calculation
459         
460     return (y);
461         
462 };
463
464 double NormalizeDegrees(double Input)
465 {
466     // normalize the input to the range (-180,180]
467     // Input should not be greater than -360 to 360.  Current rules send the output to an undefined state.
468     if (Input > 180)
469         Input -= 360;
470     if (Input <= -180)
471         Input += 360;
472     
473     return (Input);
474 };