]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGTrim.cpp
Sync with latest JSBSim CVS
[flightgear.git] / src / FDM / JSBSim / FGTrim.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2  
3  Header:       FGTrim.cpp
4  Author:       Tony Peden
5  Date started: 9/8/99
6  
7  --------- Copyright (C) 1999  Anthony K. Peden (apeden@earthlink.net) ---------
8  
9  This program is free software; you can redistribute it and/or modify it under
10  the terms of the GNU General Public License as published by the Free Software
11  Foundation; either version 2 of the License, or (at your option) any later
12  version.
13  
14  This program is distributed in the hope that it will be useful, but WITHOUT
15  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  details.
18  
19  You should have received a copy of the GNU General Public License along with
20  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21  Place - Suite 330, Boston, MA  02111-1307, USA.
22  
23  Further information about the GNU General Public License can also be found on
24  the world wide web at http://www.gnu.org.
25  
26  
27  HISTORY
28 --------------------------------------------------------------------------------
29 9/8/99   TP   Created
30  
31  
32 FUNCTIONAL DESCRIPTION
33 --------------------------------------------------------------------------------
34  
35 This class takes the given set of IC's and finds the angle of attack, elevator,
36 and throttle setting required to fly steady level. This is currently for in-air
37 conditions only.  It is implemented using an iterative, one-axis-at-a-time
38 scheme. */
39
40 //  !!!!!!! BEWARE ALL YE WHO ENTER HERE !!!!!!!
41
42
43 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 INCLUDES
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
46
47 #include <stdlib.h>
48
49 #include "FGFDMExec.h"
50 #include "FGAtmosphere.h"
51 #include "FGInitialCondition.h"
52 #include "FGTrim.h"
53 #include "FGAircraft.h"
54 #include "FGMassBalance.h"
55 #include "FGAerodynamics.h"
56 #if _MSC_VER
57 #pragma warning (disable : 4786 4788)
58 #endif
59
60 static const char *IdSrc = "$Id$";
61 static const char *IdHdr = ID_TRIM;
62
63 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64
65 FGTrim::FGTrim(FGFDMExec *FDMExec,FGInitialCondition *FGIC, TrimMode tt ) {
66
67   N=Nsub=0;
68   max_iterations=60;
69   max_sub_iterations=100;
70   Tolerance=1E-3;
71   A_Tolerance = Tolerance / 10;
72   
73   Debug=0;
74   fdmex=FDMExec;
75   fgic=FGIC;
76   total_its=0;
77   trimudot=true;
78   gamma_fallback=true;
79   axis_count=0;
80   mode=tt;
81   xlo=xhi=alo=ahi;
82   switch(mode) {
83   case tFull:
84     cout << "  Full Trim" << endl;
85     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tWdot,tAlpha ));
86     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tUdot,tThrottle ));
87     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tQdot,tPitchTrim ));
88     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tHmgt,tBeta ));
89     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tVdot,tPhi ));
90     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tPdot,tAileron ));
91     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tRdot,tRudder ));
92     break;
93   case tLongitudinal:
94     cout << "  Longitudinal Trim" << endl;
95     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tWdot,tAlpha ));
96     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tUdot,tThrottle ));
97     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tQdot,tPitchTrim ));
98     break;
99   case tGround:
100     cout << "  Ground Trim" << endl;
101     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tWdot,tAltAGL ));
102     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tQdot,tTheta ));
103     //TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,tPdot,tPhi ));
104     break;
105   }
106   //cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
107   sub_iterations=new float[TrimAxes.size()];
108   successful=new float[TrimAxes.size()];
109   solution=new bool[TrimAxes.size()];
110   current_axis=0;
111   
112   if (debug_lvl & 2) cout << "Instantiated: FGTrim" << endl;
113 }
114
115 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116
117 FGTrim::~FGTrim(void) {
118   for(current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
119     delete TrimAxes[current_axis];
120   }
121   delete[] sub_iterations;
122   delete[] successful;
123   delete[] solution;
124   if (debug_lvl & 2) cout << "Destroyed:    FGTrim" << endl;
125 }
126
127 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
128
129 void FGTrim::TrimStats() {
130   char out[80];
131   int run_sum=0;
132   cout << endl << "  Trim Statistics: " << endl;
133   cout << "    Total Iterations: " << total_its << endl;
134   if(total_its > 0) {
135     cout << "    Sub-iterations:" << endl;
136     for(current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
137       run_sum+=TrimAxes[current_axis]->GetRunCount();
138       snprintf(out,80,"   %5s: %3.0f average: %5.2f  successful: %3.0f  stability: %5.2f\n",
139                   TrimAxes[current_axis]->GetStateName().c_str(),
140                   sub_iterations[current_axis],
141                   sub_iterations[current_axis]/float(total_its),
142                   successful[current_axis],
143                   TrimAxes[current_axis]->GetAvgStability() );
144       cout << out;
145     }
146     cout << "    Run Count: " << run_sum << endl;
147   }
148 }
149
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151
152 void FGTrim::Report(void) {
153   cout << "  Trim Results: " << endl;
154   for(current_axis=0; current_axis<TrimAxes.size(); current_axis++)
155     TrimAxes[current_axis]->AxisReport();
156
157 }
158
159 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160
161 void FGTrim::ClearStates(void) {
162     FGTrimAxis* ta;
163     
164     mode=tCustom;
165     vector<FGTrimAxis*>::iterator iAxes;
166     iAxes = TrimAxes.begin();
167     while (iAxes != TrimAxes.end()) {
168       ta=*iAxes;
169       delete ta;
170       iAxes++;
171     }
172     TrimAxes.clear();
173     cout << "TrimAxes.size(): " << TrimAxes.size() << endl;
174 }
175     
176 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
177
178 bool FGTrim::AddState( State state, Control control ) {
179   FGTrimAxis* ta;
180   bool result=true;
181   
182   mode = tCustom;
183   vector <FGTrimAxis*>::iterator iAxes = TrimAxes.begin();
184   while (iAxes != TrimAxes.end()) {
185       ta=*iAxes;
186       if( ta->GetStateType() == state )
187         result=false;
188       iAxes++;
189   }
190   if(result) {
191     TrimAxes.push_back(new FGTrimAxis(fdmex,fgic,state,control));
192     delete[] sub_iterations;
193     delete[] successful;
194     delete[] solution;
195     sub_iterations=new float[TrimAxes.size()];
196     successful=new float[TrimAxes.size()];
197     solution=new bool[TrimAxes.size()];
198   }
199   return result;
200 }  
201
202 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203
204 bool FGTrim::RemoveState( State state ) {
205   FGTrimAxis* ta;
206   bool result=false;
207   
208   mode = tCustom;
209   vector <FGTrimAxis*>::iterator iAxes = TrimAxes.begin();
210   while (iAxes != TrimAxes.end()) {
211       ta=*iAxes;
212       if( ta->GetStateType() == state ) {
213         delete ta;
214         TrimAxes.erase(iAxes);
215         result=true;
216         continue;
217       }
218       iAxes++;
219   }
220   if(result) {
221     delete[] sub_iterations;
222     delete[] successful;
223     delete[] solution;
224     sub_iterations=new float[TrimAxes.size()];
225     successful=new float[TrimAxes.size()];
226     solution=new bool[TrimAxes.size()];
227   }  
228   return result;
229 }  
230
231 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232
233 bool FGTrim::EditState( State state, Control new_control ){       
234   FGTrimAxis* ta;
235   bool result=false;
236   
237   mode = tCustom;
238   vector <FGTrimAxis*>::iterator iAxes = TrimAxes.begin();
239   while (iAxes != TrimAxes.end()) {
240       ta=*iAxes;
241       if( ta->GetStateType() == state ) {
242         TrimAxes.insert(iAxes,1,new FGTrimAxis(fdmex,fgic,state,new_control));
243         delete ta;
244         TrimAxes.erase(iAxes+1);
245         result=true;
246         break;
247       }
248       iAxes++;
249   }
250   return result;
251 }  
252        
253
254 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255
256 bool FGTrim::DoTrim(void) {
257   
258   trim_failed=false;
259   int i;
260
261   for(i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){
262     fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(false);
263   }
264
265   fdmex->GetOutput()->Disable();
266
267   //clear the sub iterations counts & zero out the controls
268   for(current_axis=0;current_axis<TrimAxes.size();current_axis++) {
269     //cout << current_axis << "  " << TrimAxes[current_axis]->GetStateName()
270     //<< "  " << TrimAxes[current_axis]->GetControlName()<< endl;
271     if(TrimAxes[current_axis]->GetStateType() == tQdot) {
272       if(mode == tGround) 
273           TrimAxes[current_axis]->initTheta();
274     }  
275     xlo=TrimAxes[current_axis]->GetControlMin();
276     xhi=TrimAxes[current_axis]->GetControlMax();
277     TrimAxes[current_axis]->SetControl((xlo+xhi)/2);
278     TrimAxes[current_axis]->Run();
279     //TrimAxes[current_axis]->AxisReport();
280     sub_iterations[current_axis]=0;
281     successful[current_axis]=0;
282     solution[current_axis]=false;
283   }
284   do {
285     axis_count=0;
286     for(current_axis=0;current_axis<TrimAxes.size();current_axis++) {
287       Nsub=0;
288       if(!solution[current_axis]) {
289         if(checkLimits()) { 
290           solution[current_axis]=true;
291           solve();
292         }  
293       } else if(findInterval()) {
294         solve();
295       } else {
296         solution[current_axis]=false;
297       }  
298       sub_iterations[current_axis]+=Nsub;
299     } 
300     for(current_axis=0;current_axis<TrimAxes.size();current_axis++) {
301       //these checks need to be done after all the axes have run
302       if(Debug > 0) TrimAxes[current_axis]->AxisReport();
303       if(TrimAxes[current_axis]->InTolerance()) {
304         axis_count++;
305         successful[current_axis]++;
306       } 
307     }
308     
309
310     if((axis_count == TrimAxes.size()-1) && (TrimAxes.size() > 1)) {
311       //cout << TrimAxes.size()-1 << " out of " << TrimAxes.size() << "!" << endl;
312       //At this point we can check the input limits of the failed axis
313       //and declare the trim failed if there is no sign change. If there
314       //is, keep going until success or max iteration count
315
316       //Oh, well: two out of three ain't bad
317       for(current_axis=0;current_axis<TrimAxes.size();current_axis++) {
318         //these checks need to be done after all the axes have run
319         if(!TrimAxes[current_axis]->InTolerance()) {
320           if(!checkLimits()) {
321             // special case this for now -- if other cases arise proper
322             // support can be added to FGTrimAxis
323             if( (gamma_fallback) &&
324                 (TrimAxes[current_axis]->GetStateType() == tUdot) &&
325                 (TrimAxes[current_axis]->GetControlType() == tThrottle)) {
326               cout << "  Can't trim udot with throttle, trying flight"
327               << " path angle. (" << N << ")" << endl;
328               if(TrimAxes[current_axis]->GetState() > 0)
329                 TrimAxes[current_axis]->SetControlToMin();
330               else
331                 TrimAxes[current_axis]->SetControlToMax();
332               TrimAxes[current_axis]->Run();
333               delete TrimAxes[current_axis];
334               TrimAxes[current_axis]=new FGTrimAxis(fdmex,fgic,tUdot,
335                                                     tGamma );
336             } else {
337               cout << "  Sorry, " << TrimAxes[current_axis]->GetStateName()
338               << " doesn't appear to be trimmable" << endl;
339               //total_its=k;
340               trim_failed=true; //force the trim to fail
341             } //gamma_fallback
342           }
343         } //solution check
344       } //for loop
345     } //all-but-one check
346     N++;
347     if(N > max_iterations)
348       trim_failed=true;
349   } while((axis_count < TrimAxes.size()) && (!trim_failed));
350   if((!trim_failed) && (axis_count >= TrimAxes.size())) {
351     total_its=N;
352     cout << endl << "  Trim successful" << endl;
353   } else {
354     total_its=N;
355     cout << endl << "  Trim failed" << endl;
356   }
357   for(i=0;i < fdmex->GetGroundReactions()->GetNumGearUnits();i++){
358     fdmex->GetGroundReactions()->GetGearUnit(i)->SetReport(true);
359   }
360   fdmex->GetOutput()->Enable();
361   return !trim_failed;
362 }
363
364 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
365
366 bool FGTrim::solve(void) {
367
368   float x1,x2,x3,f1,f2,f3,d,d0;
369   const float relax =0.9;
370   float eps=TrimAxes[current_axis]->GetSolverEps();
371
372   x1=x2=x3=0;
373   d=1;
374   bool success=false;
375   //initializations
376   if( solutionDomain != 0) {
377    /* if(ahi > alo) { */
378       x1=xlo;f1=alo;
379       x3=xhi;f3=ahi;
380    /* } else {
381       x1=xhi;f1=ahi;
382       x3=xlo;f3=alo;
383     }   */
384     d0=fabs(x3-x1);
385     //iterations
386     //max_sub_iterations=TrimAxes[current_axis]->GetIterationLimit();
387     while ( (TrimAxes[current_axis]->InTolerance() == false )
388              && (fabs(d) > eps) && (Nsub < max_sub_iterations)) {
389       Nsub++;
390       d=(x3-x1)/d0;
391       x2=x1-d*d0*f1/(f3-f1);
392       TrimAxes[current_axis]->SetControl(x2);
393       TrimAxes[current_axis]->Run();
394       f2=TrimAxes[current_axis]->GetState();
395       if(Debug > 1) {
396         cout << "FGTrim::solve Nsub,x1,x2,x3: " << Nsub << ", " << x1
397         << ", " << x2 << ", " << x3 << endl;
398         cout << "                             " << f1 << ", " << f2 << ", " << f3 << endl;
399       }
400       if(f1*f2 <= 0.0) {
401         x3=x2;
402         f3=f2;
403         f1=relax*f1;
404         //cout << "Solution is between x1 and x2" << endl;
405       }
406       else if(f2*f3 <= 0.0) {
407         x1=x2;
408         f1=f2;
409         f3=relax*f3;
410         //cout << "Solution is between x2 and x3" << endl;
411
412       }
413       //cout << i << endl;
414
415       
416     }//end while
417     if(Nsub < max_sub_iterations) success=true;
418   }  
419   return success;
420 }
421
422 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423 /*
424  produces an interval (xlo..xhi) on one side or the other of the current 
425  control value in which a solution exists.  This domain is, hopefully, 
426  smaller than xmin..0 or 0..xmax and the solver will require fewer iterations 
427  to find the solution. This is, hopefully, more efficient than having the 
428  solver start from scratch every time. Maybe it isn't though...
429  This tries to take advantage of the idea that the changes from iteration to
430  iteration will be small after the first one or two top-level iterations.
431
432  assumes that changing the control will a produce significant change in the
433  accel i.e. checkLimits() has already been called.
434
435  if a solution is found above the current control, the function returns true 
436  and xlo is set to the current control, xhi to the interval max it found, and 
437  solutionDomain is set to 1.
438  if the solution lies below the current control, then the function returns 
439  true and xlo is set to the interval min it found and xmax to the current 
440  control. if no solution is found, then the function returns false.
441  
442  
443  in all cases, alo=accel(xlo) and ahi=accel(xhi) after the function exits.
444  no assumptions about the state of the sim after this function has run 
445  can be made.
446 */
447 bool FGTrim::findInterval(void) {
448   bool found=false;
449   float step;
450   float current_control=TrimAxes[current_axis]->GetControl();
451   float current_accel=TrimAxes[current_axis]->GetState();;
452   float xmin=TrimAxes[current_axis]->GetControlMin();
453   float xmax=TrimAxes[current_axis]->GetControlMax();
454   float lastxlo,lastxhi,lastalo,lastahi;
455   
456   step=0.025*fabs(xmax);
457   xlo=xhi=current_control;
458   alo=ahi=current_accel;
459   lastxlo=xlo;lastxhi=xhi;
460   lastalo=alo;lastahi=ahi;
461   do {
462     
463     Nsub++;
464     step*=2;
465     xlo-=step;
466     if(xlo < xmin) xlo=xmin;
467     xhi+=step;
468     if(xhi > xmax) xhi=xmax;
469     TrimAxes[current_axis]->SetControl(xlo);
470     TrimAxes[current_axis]->Run();
471     alo=TrimAxes[current_axis]->GetState();
472     TrimAxes[current_axis]->SetControl(xhi);
473     TrimAxes[current_axis]->Run();
474     ahi=TrimAxes[current_axis]->GetState();
475     if(fabs(ahi-alo) <= TrimAxes[current_axis]->GetTolerance()) continue;
476     if(alo*ahi <=0) {  //found interval with root
477       found=true;
478       if(alo*current_accel <= 0) { //narrow interval down a bit
479         solutionDomain=-1;
480         xhi=lastxlo;
481         ahi=lastalo;
482         //xhi=current_control;
483         //ahi=current_accel;
484       } else {
485         solutionDomain=1;
486         xlo=lastxhi;
487         alo=lastahi;
488         //xlo=current_control;
489         //alo=current_accel;
490       }     
491     }
492     lastxlo=xlo;lastxhi=xhi;
493     lastalo=alo;lastahi=ahi;
494     if( !found && xlo==xmin && xhi==xmax ) continue;
495     if(Debug > 1)
496       cout << "FGTrim::findInterval: Nsub=" << Nsub << " Lo= " << xlo
497                            << " Hi= " << xhi << " alo*ahi: " << alo*ahi << endl;
498   } while(!found && (Nsub <= max_sub_iterations) );
499   return found;
500 }
501
502 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503 //checks to see which side of the current control value the solution is on
504 //and sets solutionDomain accordingly:
505 //  1 if solution is between the current and max
506 // -1 if solution is between the min and current
507 //  0 if there is no solution
508 // 
509 //if changing the control produces no significant change in the accel then
510 //solutionDomain is set to zero and the function returns false
511 //if a solution is found, then xlo and xhi are set so that they bracket
512 //the solution, alo is set to accel(xlo), and ahi is set to accel(xhi)
513 //if there is no change or no solution then xlo=xmin, alo=accel(xmin) and
514 //xhi=xmax and ahi=accel(xmax) 
515 //in all cases the sim is left such that the control=xmax and accel=ahi
516
517 bool FGTrim::checkLimits(void) {
518   bool solutionExists;
519   float current_control=TrimAxes[current_axis]->GetControl();
520   float current_accel=TrimAxes[current_axis]->GetState();
521   xlo=TrimAxes[current_axis]->GetControlMin();
522   xhi=TrimAxes[current_axis]->GetControlMax();
523
524   TrimAxes[current_axis]->SetControl(xlo);
525   TrimAxes[current_axis]->Run();
526   alo=TrimAxes[current_axis]->GetState();
527   TrimAxes[current_axis]->SetControl(xhi);
528   TrimAxes[current_axis]->Run();
529   ahi=TrimAxes[current_axis]->GetState();
530   if(Debug > 1)
531     cout << "checkLimits() xlo,xhi,alo,ahi: " << xlo << ", " << xhi << ", "
532                                               << alo << ", " << ahi << endl;
533   solutionDomain=0;
534   solutionExists=false;
535   if(fabs(ahi-alo) > TrimAxes[current_axis]->GetTolerance()) {
536     if(alo*current_accel <= 0) {
537       solutionExists=true;
538       solutionDomain=-1;
539       xhi=current_control;
540       ahi=current_accel;
541     } else if(current_accel*ahi < 0){
542       solutionExists=true;
543       solutionDomain=1;
544       xlo=current_control;
545       alo=current_accel;  
546     }
547   } 
548   TrimAxes[current_axis]->SetControl(current_control);
549   TrimAxes[current_axis]->Run();
550   return solutionExists;
551 }
552
553 //YOU WERE WARNED, BUT YOU DID IT ANYWAY.
554