]> git.mxchange.org Git - flightgear.git/blob - Autopilot/autopilot.cxx
More altitude hold tweaks.
[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 or terrain follow enabled?
298     if ( (APData->altitude_hold == 1) || (APData->terrain_follow == 1) ) {
299         double speed, max_climb, error;
300         double prop_error, int_error;
301         double prop_adj, int_adj, total_adj;
302
303         if (APData->altitude_hold == 1) {
304             // normal altitude hold
305             APData->TargetClimbRate = 
306                 (APData->TargetAltitude - fgAPget_altitude()) * 8.0;
307         } else if (APData->terrain_follow == 1) {
308             // brain dead ground hugging with no look ahead
309             APData->TargetClimbRate = 
310                 ( APData->TargetAGL - fgAPget_agl() ) * 16.0;
311         } else {
312             // just try to zero out rate of climb ...
313             APData->TargetClimbRate = 0.0;
314         }
315
316         speed = get_speed();
317
318         if ( speed < 90.0 ) {
319             max_climb = 0.0;
320         } else if ( speed < 100.0 ) {
321             max_climb = (speed - 90.0) * 20;
322         } else {
323             max_climb = ( speed - 100.0 ) * 4.0 + 200.0;
324         }
325
326         if ( APData->TargetClimbRate > max_climb ) {
327             APData->TargetClimbRate = max_climb;
328         }
329
330         if ( APData->TargetClimbRate < -400.0 ) {
331             APData->TargetClimbRate = -400.0;
332         }
333
334         error = fgAPget_climb() - APData->TargetClimbRate;
335
336         // push current error onto queue and add into accumulator
337         // alt_error_queue.push_back(error);
338         APData->alt_error_accum += error;
339
340         // if queue size larger than 60 ... pop front and subtract
341         // from accumulator
342         // size = alt_error_queue.size();
343         // if ( size > 300 ) {
344             // APData->alt_error_accum -= alt_error_queue.front();
345             // alt_error_queue.pop_front();
346             // size--;
347         // }
348
349         // calculate integral error, and adjustment amount
350         int_error = APData->alt_error_accum;
351         // printf("error = %.2f  int_error = %.2f\n", error, int_error);
352         int_adj = int_error / 8000.0;
353         
354         // caclulate proportional error
355         prop_error = error;
356         prop_adj = prop_error / 2000.0;
357
358         total_adj = 0.9 * prop_adj + 0.1 * int_adj;
359         if ( total_adj >  0.6 ) { total_adj =  0.6; }
360         if ( total_adj < -0.2 ) { total_adj = -0.2; }
361
362         fgElevSet( total_adj );
363     }
364
365     /*
366     if (APData->Mode == 2) // Glide slope hold
367     {
368         double RelSlope;
369         double RelElevator;
370                 
371         // First, calculate Relative slope and normalize it
372         RelSlope = NormalizeDegrees( APData->TargetSlope - get_pitch());
373         
374         // Now calculate the elevator offset from current angle
375         if ( abs(RelSlope) > APData->SlopeSmooth )
376         {
377             if ( RelSlope < 0 )         //  set RelElevator to max in the correct direction
378                 RelElevator = -APData->MaxElevator;
379             else
380                 RelElevator = APData->MaxElevator;
381         }
382                 
383         else
384             RelElevator = LinearExtrapolate(RelSlope,-APData->SlopeSmooth,-APData->MaxElevator,APData->SlopeSmooth,APData->MaxElevator);
385                 
386         // set the elevator
387         fgElevMove(RelElevator);
388                 
389     }
390     */
391         
392     // Ok, we are done
393     return 0;
394
395 }
396
397 /*
398 void fgAPSetMode( int mode)
399 {
400     //Remove the following line when the calling funcitons start passing in the data pointer
401     fgAPDataPtr APData;
402          
403     APData = APDataGlobal;
404     // end section
405          
406     fgPrintf( FG_COCKPIT, FG_INFO, "APSetMode : %d\n", mode );
407          
408     APData->Mode = mode;  // set the new mode
409 }
410 */
411
412 void fgAPToggleHeading( void )
413 {
414     // Remove at a later date
415     fgAPDataPtr APData;
416
417     APData = APDataGlobal;
418     // end section
419
420     if ( APData->heading_hold ) {
421         // turn off heading hold
422         APData->heading_hold = 0;
423     } else {
424         // turn on heading hold, lock at current heading
425         APData->heading_hold = 1;
426         APData->TargetHeading = fgAPget_heading();
427     }
428
429     fgPrintf( FG_COCKPIT, FG_INFO, " fgAPSetHeading: (%d) %.2f\n",
430               APData->heading_hold,
431               APData->TargetHeading);
432 }
433                  
434
435 void fgAPToggleAltitude( void )
436 {
437     // Remove at a later date
438     fgAPDataPtr APData;
439
440     APData = APDataGlobal;
441     // end section
442
443     if ( APData->altitude_hold ) {
444         // turn off altitude hold
445         APData->altitude_hold = 0;
446     } else {
447         // turn on altitude hold, lock at current altitude
448         APData->altitude_hold = 1;
449         APData->terrain_follow = 0;
450         APData->TargetAltitude = fgAPget_altitude();
451         APData->alt_error_accum = 0.0;
452         // alt_error_queue.erase( alt_error_queue.begin(), 
453         //                        alt_error_queue.end() );
454     }
455
456     fgPrintf( FG_COCKPIT, FG_INFO, " fgAPSetAltitude: (%d) %.2f\n",
457               APData->altitude_hold,
458               APData->TargetAltitude);
459 }
460                  
461
462 void fgAPToggleTerrainFollow( void )
463 {
464     // Remove at a later date
465     fgAPDataPtr APData;
466
467     APData = APDataGlobal;
468     // end section
469
470     if ( APData->terrain_follow ) {
471         // turn off altitude hold
472         APData->terrain_follow = 0;
473     } else {
474         // turn on terrain follow, lock at current agl
475         APData->terrain_follow = 1;
476         APData->altitude_hold = 0;
477         APData->TargetAGL = fgAPget_agl();
478         APData->alt_error_accum = 0.0;
479     }
480
481     fgPrintf( FG_COCKPIT, FG_INFO, " fgAPSetTerrainFollow: (%d) %.2f\n",
482               APData->terrain_follow,
483               APData->TargetAGL);
484 }
485
486 double LinearExtrapolate( double x,double x1,double y1,double x2,double y2)
487 {
488     // This procedure extrapolates the y value for the x posistion on a line defined by x1,y1; x2,y2
489     //assert(x1 != x2); // Divide by zero error.  Cold abort for now
490     
491     double m, b, y;             // the constants to find in y=mx+b
492         
493     m=(y2-y1)/(x2-x1);  // calculate the m
494         
495     b= y1- m * x1;              // calculate the b
496         
497     y = m * x + b;              // the final calculation
498         
499     return (y);
500         
501 };
502
503 double NormalizeDegrees(double Input)
504 {
505     // normalize the input to the range (-180,180]
506     // Input should not be greater than -360 to 360.  Current rules send the output to an undefined state.
507     if (Input > 180)
508         Input -= 360;
509     if (Input <= -180)
510         Input += 360;
511     
512     return (Input);
513 };