46 #include "models/FGInertial.h"
47 #include "models/FGAccelerations.h"
48 #include "models/FGMassBalance.h"
49 #include "models/FGFCS.h"
52 #pragma warning (disable : 4786 4788)
67 max_sub_iterations=100;
69 A_Tolerance = Tolerance / 10;
73 fgic = *fdmex->
GetIC();
81 if (debug_lvl & 2) cout <<
"Instantiated: FGTrim" << endl;
86 FGTrim::~FGTrim(
void) {
87 if (debug_lvl & 2) cout <<
"Destroyed: FGTrim" << endl;
94 cout << endl <<
" Trim Statistics: " << endl;
95 cout <<
" Total Iterations: " << total_its << endl;
97 cout <<
" Sub-iterations:" << endl;
98 for (
unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
99 run_sum += TrimAxes[current_axis].GetRunCount();
100 cout <<
" " << setw(5) << TrimAxes[current_axis].GetStateName().c_str()
101 <<
": " << setprecision(3) << sub_iterations[current_axis]
102 <<
" average: " << setprecision(5) << sub_iterations[current_axis]/double(total_its)
103 <<
" successful: " << setprecision(3) << successful[current_axis]
104 <<
" stability: " << setprecision(5) << TrimAxes[current_axis].GetAvgStability()
107 cout <<
" Run Count: " << run_sum << endl;
114 cout <<
" Trim Results: " << endl;
115 for(
unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++)
116 TrimAxes[current_axis].AxisReport();
132 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
133 for (; iAxes != TrimAxes.end(); ++iAxes) {
134 if (iAxes->GetStateType() == state)
138 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,state,control));
139 sub_iterations.resize(TrimAxes.size());
140 successful.resize(TrimAxes.size());
141 solution.resize(TrimAxes.size());
152 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
153 while (iAxes != TrimAxes.end()) {
154 if( iAxes->GetStateType() == state ) {
155 iAxes = TrimAxes.erase(iAxes);
162 sub_iterations.resize(TrimAxes.size());
163 successful.resize(TrimAxes.size());
164 solution.resize(TrimAxes.size());
173 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
174 while (iAxes != TrimAxes.end()) {
175 if( iAxes->GetStateType() == state ) {
176 *iAxes =
FGTrimAxis(fdmex,&fgic,state,new_control);
187 bool trim_failed=
false;
189 unsigned int axis_count = 0;
200 fdmex->SetTrimStatus(
true);
207 if (mode == tGround) {
216 TrimAxes[1].SetControlLimits(theta - 5.0 * degtorad, theta + 5.0 * degtorad);
217 TrimAxes[2].SetControlLimits(phi - 30.0 * degtorad, phi + 30.0 * degtorad);
221 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
224 xlo=TrimAxes[current_axis].GetControlMin();
225 xhi=TrimAxes[current_axis].GetControlMax();
226 TrimAxes[current_axis].SetControl((xlo+xhi)/2);
227 TrimAxes[current_axis].Run();
229 sub_iterations[current_axis]=0;
230 successful[current_axis]=0;
231 solution[current_axis]=
false;
234 if(mode == tPullup ) {
235 cout <<
"Setting pitch rate and nlf... " << endl;
237 cout <<
"pitch rate done ... " << endl;
238 TrimAxes[0].SetStateTarget(targetNlf);
239 cout <<
"nlf done" << endl;
240 }
else if (mode == tTurn) {
247 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
248 setDebug(TrimAxes[current_axis]);
251 if(!solution[current_axis]) {
252 if(checkLimits(TrimAxes[current_axis])) {
253 solution[current_axis]=
true;
254 solve(TrimAxes[current_axis]);
256 }
else if(findInterval(TrimAxes[current_axis])) {
257 solve(TrimAxes[current_axis]);
259 solution[current_axis]=
false;
261 sub_iterations[current_axis]+=Nsub;
263 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
265 if(Debug > 0) TrimAxes[current_axis].AxisReport();
266 if(TrimAxes[current_axis].InTolerance()) {
268 successful[current_axis]++;
272 if((axis_count == TrimAxes.size()-1) && (TrimAxes.size() > 1)) {
279 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
281 if(!TrimAxes[current_axis].InTolerance()) {
282 if(!checkLimits(TrimAxes[current_axis])) {
285 if( (gamma_fallback) &&
286 (TrimAxes[current_axis].GetStateType() == tUdot) &&
287 (TrimAxes[current_axis].GetControlType() == tThrottle)) {
288 cout <<
" Can't trim udot with throttle, trying flight"
289 <<
" path angle. (" << N <<
")" << endl;
290 if(TrimAxes[current_axis].GetState() > 0)
291 TrimAxes[current_axis].SetControlToMin();
293 TrimAxes[current_axis].SetControlToMax();
294 TrimAxes[current_axis].Run();
295 TrimAxes[current_axis]=
FGTrimAxis(fdmex,&fgic,tUdot,tGamma);
297 cout <<
" Sorry, " << TrimAxes[current_axis].GetStateName()
298 <<
" doesn't appear to be trimmable" << endl;
307 if(N > max_iterations)
309 }
while((axis_count < TrimAxes.size()) && (!trim_failed));
311 if((!trim_failed) && (axis_count >= TrimAxes.size())) {
314 cout << endl <<
" Trim successful" << endl;
319 fgic = *fdmex->
GetIC();
324 for (
unsigned int i=0; i < throttle0.size(); i++)
335 cout << endl <<
" Trim failed" << endl;
340 fdmex->SetTrimStatus(
false);
372 void FGTrim::trimOnGround(
void)
378 vector<ContactPoints> contacts;
379 FGLocation CGLocation = Propagate->GetLocation();
392 if (!gear->GetGearUnitDown())
404 if (gear->IsBogey() && !GroundReactions->
GetSolid())
407 c.normal = Tec2b * normal;
408 contacts.push_back(c);
412 contactRef = contacts.size() - 1;
416 if (contacts.size() < 3)
423 FGColumnVector3 contact0 = contacts[contactRef].location;
424 contacts.erase(contacts.begin() + contactRef);
434 FGColumnVector3 force = MassBalance->GetMass() * Accelerations->
GetUVWdot();
435 FGColumnVector3 moment = MassBalance->
GetJ() * Accelerations->
GetPQRdot()
437 FGColumnVector3 rotationAxis = moment.
Normalize();
441 RotationParameters rParam = calcRotation(contacts, rotationAxis, contact0);
442 FGQuaternion q0(rParam.angleMin, rotationAxis);
445 FGMatrix33 rot = q0.GetTInv();
446 vector<ContactPoints>::iterator iter;
447 for (iter = contacts.begin(); iter != contacts.end(); ++iter)
448 iter->location = contact0 + rot * (iter->location - contact0);
453 FGColumnVector3 contact1 = rParam.contactRef->location;
454 contacts.erase(rParam.contactRef);
459 rotationAxis = contact1 - contact0;
461 if (DotProduct(rotationAxis, moment) < 0.0)
462 rotationAxis = contact0 - contact1;
464 rotationAxis.Normalize();
467 rParam = calcRotation(contacts, rotationAxis, contact0);
468 FGQuaternion q1(rParam.angleMin, rotationAxis);
471 FGColumnVector3 euler = (fgic.
GetOrientation() * q0 * q1).GetEuler();
487 FGTrim::RotationParameters FGTrim::calcRotation(vector<ContactPoints>& contacts,
488 const FGColumnVector3& u,
489 const FGColumnVector3& GM0)
491 RotationParameters rParam;
492 vector<ContactPoints>::iterator iter;
494 rParam.angleMin = 3.0 * M_PI;
496 for (iter = contacts.begin(); iter != contacts.end(); ++iter) {
500 FGColumnVector3 t = u * iter->normal;
501 double length = t.Magnitude();
503 FGColumnVector3 v = t * u;
504 FGColumnVector3 MM0 = GM0 - iter->location;
506 double d0 = DotProduct(MM0, u);
509 double sqrRadius = DotProduct(MM0, MM0) - d0 * d0;
512 double DistPlane = d0 * DotProduct(u, iter->normal) / length;
515 double mag = sqrRadius - DistPlane * DistPlane;
517 cout <<
"FGTrim::calcRotation DistPlane^2 larger than sqrRadius" << endl;
519 double alpha = sqrt(max(mag, 0.0));
520 FGColumnVector3 CP = alpha * t + DistPlane * v;
524 double cosine = -DotProduct(MM0, CP) / sqrRadius;
525 double sine = DotProduct(MM0 * u, CP) / sqrRadius;
526 double angle = atan2(sine, cosine);
527 if (angle < 0.0) angle += 2.0 * M_PI;
528 if (angle < rParam.angleMin) {
529 rParam.angleMin = angle;
530 rParam.contactRef = iter;
539 bool FGTrim::solve(FGTrimAxis& axis) {
541 double x1,x2,x3,f1,f2,f3,d,d0;
542 const double relax =0.9;
543 double eps=axis.GetSolverEps();
549 if( solutionDomain != 0) {
560 while ( (axis.InTolerance() ==
false )
561 && (fabs(d) > eps) && (Nsub < max_sub_iterations)) {
564 x2=x1-d*d0*f1/(f3-f1);
569 cout <<
"FGTrim::solve Nsub,x1,x2,x3: " << Nsub <<
", " << x1
570 <<
", " << x2 <<
", " << x3 << endl;
571 cout <<
" " << f1 <<
", " << f2 <<
", " << f3 << endl;
579 else if(f2*f3 <= 0.0) {
590 if(Nsub < max_sub_iterations) success=
true;
620 bool FGTrim::findInterval(FGTrimAxis& axis) {
623 double current_control=axis.GetControl();
624 double current_accel=axis.GetState();;
625 double xmin=axis.GetControlMin();
626 double xmax=axis.GetControlMax();
627 double lastxlo,lastxhi,lastalo,lastahi;
629 step=0.025*fabs(xmax);
630 xlo=xhi=current_control;
631 alo=ahi=current_accel;
632 lastxlo=xlo;lastxhi=xhi;
633 lastalo=alo;lastahi=ahi;
639 if(xlo < xmin) xlo=xmin;
641 if(xhi > xmax) xhi=xmax;
642 axis.SetControl(xlo);
645 axis.SetControl(xhi);
648 if(fabs(ahi-alo) <= axis.GetTolerance())
continue;
651 if(alo*current_accel <= 0) {
665 lastxlo=xlo;lastxhi=xhi;
666 lastalo=alo;lastahi=ahi;
667 if( !found && xlo==xmin && xhi==xmax )
continue;
669 cout <<
"FGTrim::findInterval: Nsub=" << Nsub <<
" Lo= " << xlo
670 <<
" Hi= " << xhi <<
" alo*ahi: " << alo*ahi << endl;
671 }
while(!found && (Nsub <= max_sub_iterations) );
690 bool FGTrim::checkLimits(FGTrimAxis& axis)
693 double current_control=axis.GetControl();
694 double current_accel=axis.GetState();
695 xlo=axis.GetControlMin();
696 xhi=axis.GetControlMax();
698 axis.SetControl(xlo);
701 axis.SetControl(xhi);
705 cout <<
"checkLimits() xlo,xhi,alo,ahi: " << xlo <<
", " << xhi <<
", "
706 << alo <<
", " << ahi << endl;
708 solutionExists=
false;
709 if(fabs(ahi-alo) > axis.GetTolerance()) {
710 if(alo*current_accel <= 0) {
715 }
else if(current_accel*ahi < 0){
722 axis.SetControl(current_control);
724 return solutionExists;
729 void FGTrim::setupPullup() {
733 cout <<
"setPitchRateInPullup(): " << g <<
", " << cgamma <<
", "
736 cout << targetNlf <<
", " << q << endl;
738 cout <<
"setPitchRateInPullup() complete" << endl;
744 void FGTrim::setupTurn(
void){
747 if( fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
748 targetNlf = 1 / cos(phi);
751 cout << targetNlf <<
", " << psidot << endl;
758 void FGTrim::updateRates(
void){
759 if( mode == tTurn ) {
763 if(fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
767 p=-psidot*sin(theta);
768 q=psidot*cos(theta)*sin(phi);
769 r=psidot*cos(theta)*cos(phi);
776 }
else if( mode == tPullup && fabs(targetNlf-1) > 0.01) {
787 void FGTrim::setDebug(FGTrimAxis& axis) {
788 if(debug_axis == tAll ||
789 axis.GetStateType() == debug_axis ) {
806 cout <<
" Full Trim" << endl;
807 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha));
808 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
809 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
811 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
812 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
813 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
817 cout <<
" Longitudinal Trim" << endl;
818 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
819 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
820 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
824 cout <<
" Ground Trim" << endl;
825 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAltAGL ));
826 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tTheta ));
827 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tPhi ));
830 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tNlf,tAlpha ));
831 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
832 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
833 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
834 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
835 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
836 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
839 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
840 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
841 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
842 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tBeta ));
843 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
844 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
851 sub_iterations.resize(TrimAxes.size());
852 successful.resize(TrimAxes.size());
853 solution.resize(TrimAxes.size());