52#pragma warning (disable : 4786 4788)
67 max_sub_iterations=100;
69 A_Tolerance = Tolerance / 10;
73 fgic = *fdmex->GetIC();
78 targetNlf=fgic.GetTargetNlfIC();
81 if (
debug_lvl & 2) cout <<
"Instantiated: FGTrim" << endl;
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;
190 FGFCS *FCS = fdmex->GetFCS();
197 for(
int i=0;
i < fdmex->GetGroundReactions()->GetNumGearUnits();
i++)
198 fdmex->GetGroundReactions()->GetGearUnit(
i)->SetReport(
false);
200 fdmex->SetTrimStatus(
true);
201 fdmex->SuspendIntegration();
203 fgic.SetPRadpsIC(0.0);
204 fgic.SetQRadpsIC(0.0);
205 fgic.SetRRadpsIC(0.0);
208 fdmex->Initialize(&fgic);
211 double theta = fgic.GetThetaRadIC();
212 double phi = fgic.GetPhiRadIC();
215 TrimAxes[0].SetControlLimits(0., fgic.GetAltitudeAGLFtIC());
216 TrimAxes[1].SetControlLimits(theta - 5.0 *
degtorad, theta + 5.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;
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();
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++)
327 fdmex->Initialize(&fgic);
331 if (fdmex->GetGroundReactions()->GetWOW())
335 cout << endl <<
" Trim failed" << endl;
338 fdmex->GetPropagate()->InitializeDerivatives();
339 fdmex->ResumeIntegration();
340 fdmex->SetTrimStatus(
false);
342 for(
int i=0;
i < fdmex->GetGroundReactions()->GetNumGearUnits();
i++)
343 fdmex->GetGroundReactions()->GetGearUnit(
i)->SetReport(
true);
372void FGTrim::trimOnGround(
void)
378 vector<ContactPoints> contacts;
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);
428 fgic.SetAltitudeASLFtIC(fgic.GetAltitudeASLFtIC() - hmin);
429 fdmex->Initialize(&fgic);
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;
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();
473 fgic.SetPhiRadIC(euler(1));
474 fgic.SetThetaRadIC(euler(2));
475 fgic.SetPsiRadIC(euler(3));
487FGTrim::RotationParameters FGTrim::calcRotation(vector<ContactPoints>& contacts,
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;
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;
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;
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) );
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;
729void FGTrim::setupPullup() {
731 g=fdmex->GetInertial()->GetGravity().Magnitude();
732 cgamma=cos(fgic.GetFlightPathAngleRadIC());
733 cout <<
"setPitchRateInPullup(): " <<
g <<
", " << cgamma <<
", "
734 << fgic.GetVtrueFpsIC() << endl;
735 q=
g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
736 cout << targetNlf <<
", " << q << endl;
738 cout <<
"setPitchRateInPullup() complete" << endl;
744void FGTrim::setupTurn(
void){
746 phi = fgic.GetPhiRadIC();
747 if( fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
748 targetNlf = 1 / cos(phi);
749 g = fdmex->GetInertial()->GetGravity().Magnitude();
750 psidot =
g*tan(phi) / fgic.GetUBodyFpsIC();
751 cout << targetNlf <<
", " << psidot << endl;
758void FGTrim::updateRates(
void){
759 if( mode ==
tTurn ) {
760 double phi = fgic.GetPhiRadIC();
761 double g = fdmex->GetInertial()->GetGravity().Magnitude();
763 if(fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
764 theta=fgic.GetThetaRadIC();
765 phi=fgic.GetPhiRadIC();
766 psidot =
g*tan(phi) / fgic.GetUBodyFpsIC();
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) {
778 g=fdmex->GetInertial()->GetGravity().Magnitude();
779 cgamma=cos(fgic.GetFlightPathAngleRadIC());
780 q=
g*(targetNlf-cgamma)/fgic.GetVtrueFpsIC();
788 if(debug_axis ==
tAll ||
789 axis.GetStateType() == debug_axis ) {
806 cout <<
" Full Trim" << endl;
817 cout <<
" Longitudinal Trim" << endl;
824 cout <<
" Ground Trim" << endl;
851 sub_iterations.resize(TrimAxes.size());
852 successful.resize(TrimAxes.size());
853 solution.resize(TrimAxes.size());
JSBSim::FGFDMExec * FDMExec
const FGColumnVector3 & GetUVWdot(void) const
Retrieves the body axis acceleration.
const FGColumnVector3 & GetPQRdot(void) const
Retrieves the body axis angular acceleration vector.
This class implements a 3 element column vector.
FGColumnVector3 & Normalize(void)
Normalize.
double GetDeCmd(void) const
Gets the elevator command.
void SetPitchTrimCmd(double cmd)
Sets the pitch trim command.
void SetDeCmd(double cmd)
Sets the elevator command.
double GetDrCmd(void) const
Gets the rudder command.
void SetDaCmd(double cmd)
Sets the aileron command.
double GetThrottleCmd(int engine) const
Gets the throttle command.
void SetThrottleCmd(int engine, double cmd)
Sets the throttle command for the specified engine.
void SetDrCmd(double cmd)
Sets the rudder command.
double GetDaCmd(void) const
Gets the aileron command.
double GetPitchTrimCmd(void) const
Gets the pitch trim command.
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.
FGAccelerations * GetAccelerations(void)
Returns the FGAccelerations pointer.
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
FGGroundReactions * GetGroundReactions(void)
Returns the FGGroundReactions pointer.
FGInertial * GetInertial(void)
Returns the FGInertial pointer.
Manages ground reactions modeling.
double GetContactPoint(const FGLocation &location, FGLocation &contact, FGColumnVector3 &normal, FGColumnVector3 &velocity, FGColumnVector3 &ang_velocity) const
Get terrain contact point information below the current location.
static constexpr double degtorad
bool GetGearUnitDown(void) const
FGColumnVector3 GetBodyLocation(void) const
Gets the location of the gear in Body axes.
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF).
FGLocation LocalToLocation(const FGColumnVector3 &lvec) const
Conversion from Local frame coordinates to a location in the earth centered and fixed frame.
Models weight, balance and moment of inertia information.
double GetMass(void) const
const FGMatrix33 & GetJ(void) const
Returns the inertia matrix expressed in the body frame.
Handles matrix math operations.
Models the EOM and integration/propagation of state.
const FGLocation & GetLocation(void) const
const FGMatrix33 & GetTec2b(void) const
Retrieves the ECEF-to-body transformation matrix.
const FGMatrix33 & GetTb2l(void) const
Retrieves the body-to-local transformation matrix.
void SetMode(TrimMode tm)
Clear all state-control pairs and set a predefined trim mode.
bool DoTrim(void)
Execute the trim.
void Report(void)
Print the results of the trim.
void TrimStats()
Iteration statistics.
void ClearStates(void)
Clear all state-control pairs from the current configuration.
bool EditState(State state, Control new_control)
Change the control used to zero a state previously configured.
bool AddState(State state, Control control)
Add a state-control pair to the current configuration.
bool RemoveState(State state)
Remove a specific state-control pair from the current configuration.
FGTrim(FGFDMExec *FDMExec, TrimMode tm=tGround)
Initializes the trimming class.
double DotProduct(const FGColumnVector3 &v1, const FGColumnVector3 &v2)
Dot product of two vectors Compute and return the euclidean dot (or scalar) product of two vectors v1...
State
Models an aircraft axis for purposes of trimming.