46 #include "models/FGGroundReactions.h" 47 #include "models/FGInertial.h" 48 #include "models/FGAccelerations.h" 49 #include "models/FGMassBalance.h" 50 #include "models/FGFCS.h" 53 #pragma warning (disable : 4786 4788) 60 IDENT(IdSrc,
"$Id: FGTrim.cpp,v 1.34 2016/06/12 09:09:02 bcoconni Exp $");
71 max_sub_iterations=100;
73 A_Tolerance = Tolerance / 10;
77 fgic = *fdmex->
GetIC();
85 if (debug_lvl & 2) cout <<
"Instantiated: FGTrim" << endl;
90 FGTrim::~FGTrim(
void) {
91 if (debug_lvl & 2) cout <<
"Destroyed: FGTrim" << endl;
98 cout << endl <<
" Trim Statistics: " << endl;
99 cout <<
" Total Iterations: " << total_its << endl;
101 cout <<
" Sub-iterations:" << endl;
102 for (
unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++) {
103 run_sum += TrimAxes[current_axis].GetRunCount();
104 cout <<
" " << setw(5) << TrimAxes[current_axis].GetStateName().c_str()
105 <<
": " << setprecision(3) << sub_iterations[current_axis]
106 <<
" average: " << setprecision(5) << sub_iterations[current_axis]/double(total_its)
107 <<
" successful: " << setprecision(3) << successful[current_axis]
108 <<
" stability: " << setprecision(5) << TrimAxes[current_axis].GetAvgStability()
111 cout <<
" Run Count: " << run_sum << endl;
118 cout <<
" Trim Results: " << endl;
119 for(
unsigned int current_axis=0; current_axis<TrimAxes.size(); current_axis++)
120 TrimAxes[current_axis].AxisReport();
136 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
137 for (; iAxes != TrimAxes.end(); ++iAxes) {
138 if (iAxes->GetStateType() == state)
142 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,state,control));
143 sub_iterations.resize(TrimAxes.size());
144 successful.resize(TrimAxes.size());
145 solution.resize(TrimAxes.size());
156 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
157 while (iAxes != TrimAxes.end()) {
158 if( iAxes->GetStateType() == state ) {
159 iAxes = TrimAxes.erase(iAxes);
166 sub_iterations.resize(TrimAxes.size());
167 successful.resize(TrimAxes.size());
168 solution.resize(TrimAxes.size());
177 vector <FGTrimAxis>::iterator iAxes = TrimAxes.begin();
178 while (iAxes != TrimAxes.end()) {
179 if( iAxes->GetStateType() == state ) {
180 *iAxes =
FGTrimAxis(fdmex,&fgic,state,new_control);
191 bool trim_failed=
false;
193 unsigned int axis_count = 0;
204 fdmex->SetTrimStatus(
true);
211 if (mode == tGround) {
220 TrimAxes[1].SetControlLimits(theta - 5.0 * degtorad, theta + 5.0 * degtorad);
221 TrimAxes[2].SetControlLimits(phi - 30.0 * degtorad, phi + 30.0 * degtorad);
225 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
228 xlo=TrimAxes[current_axis].GetControlMin();
229 xhi=TrimAxes[current_axis].GetControlMax();
230 TrimAxes[current_axis].SetControl((xlo+xhi)/2);
231 TrimAxes[current_axis].Run();
233 sub_iterations[current_axis]=0;
234 successful[current_axis]=0;
235 solution[current_axis]=
false;
238 if(mode == tPullup ) {
239 cout <<
"Setting pitch rate and nlf... " << endl;
241 cout <<
"pitch rate done ... " << endl;
242 TrimAxes[0].SetStateTarget(targetNlf);
243 cout <<
"nlf done" << endl;
244 }
else if (mode == tTurn) {
251 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
252 setDebug(TrimAxes[current_axis]);
255 if(!solution[current_axis]) {
256 if(checkLimits(TrimAxes[current_axis])) {
257 solution[current_axis]=
true;
258 solve(TrimAxes[current_axis]);
260 }
else if(findInterval(TrimAxes[current_axis])) {
261 solve(TrimAxes[current_axis]);
263 solution[current_axis]=
false;
265 sub_iterations[current_axis]+=Nsub;
267 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
269 if(Debug > 0) TrimAxes[current_axis].AxisReport();
270 if(TrimAxes[current_axis].InTolerance()) {
272 successful[current_axis]++;
276 if((axis_count == TrimAxes.size()-1) && (TrimAxes.size() > 1)) {
283 for(
unsigned int current_axis=0;current_axis<TrimAxes.size();current_axis++) {
285 if(!TrimAxes[current_axis].InTolerance()) {
286 if(!checkLimits(TrimAxes[current_axis])) {
289 if( (gamma_fallback) &&
290 (TrimAxes[current_axis].GetStateType() == tUdot) &&
291 (TrimAxes[current_axis].GetControlType() == tThrottle)) {
292 cout <<
" Can't trim udot with throttle, trying flight" 293 <<
" path angle. (" << N <<
")" << endl;
294 if(TrimAxes[current_axis].GetState() > 0)
295 TrimAxes[current_axis].SetControlToMin();
297 TrimAxes[current_axis].SetControlToMax();
298 TrimAxes[current_axis].Run();
299 TrimAxes[current_axis]=
FGTrimAxis(fdmex,&fgic,tUdot,tGamma);
301 cout <<
" Sorry, " << TrimAxes[current_axis].GetStateName()
302 <<
" doesn't appear to be trimmable" << endl;
311 if(N > max_iterations)
313 }
while((axis_count < TrimAxes.size()) && (!trim_failed));
315 if((!trim_failed) && (axis_count >= TrimAxes.size())) {
318 cout << endl <<
" Trim successful" << endl;
323 fgic = *fdmex->
GetIC();
328 for (
unsigned int i=0; i < throttle0.size(); i++)
339 cout << endl <<
" Trim failed" << endl;
344 fdmex->SetTrimStatus(
false);
376 void FGTrim::trimOnGround(
void)
382 vector<ContactPoints> contacts;
383 FGLocation CGLocation = Propagate->GetLocation();
391 for (
int i = 0; i < GroundReactions->GetNumGearUnits(); ++i) {
396 if (!gear->GetGearUnitDown())
404 double height = gearLoc.
GetContactPoint(lDummy, normal, vDummy, vDummy);
405 c.normal = Tec2b * normal;
407 contacts.push_back(c);
411 contactRef = contacts.size() - 1;
420 contacts.erase(contacts.begin() + contactRef);
437 RotationParameters rParam = calcRotation(contacts, rotationAxis, contact0);
442 vector<ContactPoints>::iterator iter;
443 for (iter = contacts.begin(); iter != contacts.end(); ++iter)
444 iter->location = contact0 + rot * (iter->location - contact0);
450 contacts.erase(rParam.contactRef);
455 rotationAxis = contact1 - contact0;
457 if (DotProduct(rotationAxis, moment) < 0.0)
458 rotationAxis = contact0 - contact1;
463 rParam = calcRotation(contacts, rotationAxis, contact0);
483 FGTrim::RotationParameters FGTrim::calcRotation(vector<ContactPoints>& contacts,
487 RotationParameters rParam;
488 vector<ContactPoints>::iterator iter;
490 rParam.angleMin = 3.0 * M_PI;
492 for (iter = contacts.begin(); iter != contacts.end(); ++iter) {
502 double d0 = DotProduct(MM0, u);
505 double sqrRadius = DotProduct(MM0, MM0) - d0 * d0;
508 double DistPlane = d0 * DotProduct(u, iter->normal) / length;
511 double mag = sqrRadius - DistPlane * DistPlane;
513 cout <<
"FGTrim::calcRotation DistPlane^2 larger than sqrRadius" << endl;
515 double alpha = sqrt(max(mag, 0.0));
520 double cosine = -DotProduct(MM0, CP) / sqrRadius;
521 double sine = DotProduct(MM0 * u, CP) / sqrRadius;
522 double angle = atan2(sine, cosine);
523 if (angle < 0.0) angle += 2.0 * M_PI;
524 if (angle < rParam.angleMin) {
525 rParam.angleMin = angle;
526 rParam.contactRef = iter;
537 double x1,x2,x3,f1,f2,f3,d,d0;
538 const double relax =0.9;
539 double eps=axis.GetSolverEps();
545 if( solutionDomain != 0) {
556 while ( (axis.InTolerance() == false )
557 && (fabs(d) > eps) && (Nsub < max_sub_iterations)) {
560 x2=x1-d*d0*f1/(f3-f1);
565 cout <<
"FGTrim::solve Nsub,x1,x2,x3: " << Nsub <<
", " << x1
566 <<
", " << x2 <<
", " << x3 << endl;
567 cout <<
" " << f1 <<
", " << f2 <<
", " << f3 << endl;
575 else if(f2*f3 <= 0.0) {
586 if(Nsub < max_sub_iterations) success=
true;
619 double current_control=axis.GetControl();
620 double current_accel=axis.GetState();;
621 double xmin=axis.GetControlMin();
622 double xmax=axis.GetControlMax();
623 double lastxlo,lastxhi,lastalo,lastahi;
625 step=0.025*fabs(xmax);
626 xlo=xhi=current_control;
627 alo=ahi=current_accel;
628 lastxlo=xlo;lastxhi=xhi;
629 lastalo=alo;lastahi=ahi;
635 if(xlo < xmin) xlo=xmin;
637 if(xhi > xmax) xhi=xmax;
638 axis.SetControl(xlo);
641 axis.SetControl(xhi);
644 if(fabs(ahi-alo) <= axis.GetTolerance())
continue;
647 if(alo*current_accel <= 0) {
661 lastxlo=xlo;lastxhi=xhi;
662 lastalo=alo;lastahi=ahi;
663 if( !found && xlo==xmin && xhi==xmax )
continue;
665 cout <<
"FGTrim::findInterval: Nsub=" << Nsub <<
" Lo= " << xlo
666 <<
" Hi= " << xhi <<
" alo*ahi: " << alo*ahi << endl;
667 }
while(!found && (Nsub <= max_sub_iterations) );
689 double current_control=axis.GetControl();
690 double current_accel=axis.GetState();
691 xlo=axis.GetControlMin();
692 xhi=axis.GetControlMax();
694 axis.SetControl(xlo);
697 axis.SetControl(xhi);
701 cout <<
"checkLimits() xlo,xhi,alo,ahi: " << xlo <<
", " << xhi <<
", " 702 << alo <<
", " << ahi << endl;
704 solutionExists=
false;
705 if(fabs(ahi-alo) > axis.GetTolerance()) {
706 if(alo*current_accel <= 0) {
711 }
else if(current_accel*ahi < 0){
718 axis.SetControl(current_control);
720 return solutionExists;
725 void FGTrim::setupPullup() {
729 cout <<
"setPitchRateInPullup(): " << g <<
", " << cgamma <<
", " 732 cout << targetNlf <<
", " << q << endl;
734 cout <<
"setPitchRateInPullup() complete" << endl;
740 void FGTrim::setupTurn(
void){
743 if( fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
744 targetNlf = 1 / cos(phi);
747 cout << targetNlf <<
", " << psidot << endl;
754 void FGTrim::updateRates(
void){
755 if( mode == tTurn ) {
759 if(fabs(phi) > 0.001 && fabs(phi) < 1.56 ) {
763 p=-psidot*sin(theta);
764 q=psidot*cos(theta)*sin(phi);
765 r=psidot*cos(theta)*cos(phi);
772 }
else if( mode == tPullup && fabs(targetNlf-1) > 0.01) {
784 if(debug_axis == tAll ||
785 axis.GetStateType() == debug_axis ) {
802 cout <<
" Full Trim" << endl;
803 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha));
804 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
805 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
807 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
808 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
809 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
813 cout <<
" Longitudinal Trim" << endl;
814 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
815 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
816 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
820 cout <<
" Ground Trim" << endl;
821 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAltAGL ));
822 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tTheta ));
823 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tPhi ));
826 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tNlf,tAlpha ));
827 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
828 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
829 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tHmgt,tBeta ));
830 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tPhi ));
831 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
832 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
835 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tWdot,tAlpha ));
836 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tUdot,tThrottle ));
837 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tQdot,tPitchTrim ));
838 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tVdot,tBeta ));
839 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tPdot,tAileron ));
840 TrimAxes.push_back(
FGTrimAxis(fdmex,&fgic,tRdot,tRudder ));
847 sub_iterations.resize(TrimAxes.size());
848 successful.resize(TrimAxes.size());
849 solution.resize(TrimAxes.size());
FGAccelerations * GetAccelerations(void)
Returns the FGAccelerations pointer.
void SetThetaRadIC(double theta)
Sets the initial pitch angle.
FGInitialCondition * GetIC(void)
Returns a pointer to the FGInitialCondition object.
const FGMatrix33 & GetTec2b(void) const
Retrieves the ECEF-to-body transformation matrix.
Models the Quaternion representation of rotations.
FGColumnVector3 GetBodyLocation(void) const
Gets the location of the gear in Body axes.
double GetVtrueFpsIC(void) const
Gets the initial true velocity.
FGInertial * GetInertial(void)
Returns the FGInertial pointer.
bool AddState(State state, Control control)
Add a state-control pair to the current configuration.
double GetFlightPathAngleRadIC(void) const
Gets the initial flight path angle.
double GetDeCmd(void) const
Gets the elevator command.
double GetAltitudeAGLFtIC(void) const
Gets the initial altitude above ground level.
void SuspendIntegration(void)
Suspends the simulation and sets the delta T to zero.
void SetPhiRadIC(double phi)
Sets the initial roll angle.
double GetPhiRadIC(void) const
Gets the initial roll angle.
double GetUBodyFpsIC(void) const
Gets the initial body axis X velocity.
const FGMatrix33 & GetTInv(void) const
Backward transformation matrix.
FGLocation holds an arbitrary location in the Earth centered Earth fixed reference frame (ECEF)...
void SetQRadpsIC(double Q)
Sets the initial body axis pitch rate.
FGColumnVector3 & Normalize(void)
Normalize.
double GetContactPoint(FGLocation &contact, FGColumnVector3 &normal, FGColumnVector3 &v, FGColumnVector3 &w) const
Get terrain contact point information below the current location.
void Report(void)
Print the results of the trim.
const FGColumnVector3 & GetUVWdot(void) const
Retrieves the body axis acceleration.
void SetThrottleCmd(int engine, double cmd)
Sets the throttle command for the specified engine.
void SetDaCmd(double cmd)
Sets the aileron command.
void SetPitchTrimCmd(double cmd)
Sets the pitch trim command.
void Initialize(FGInitialCondition *FGIC)
Initializes the simulation with initial conditions.
double GetPitchTrimCmd(void) const
Gets the pitch trim command.
double GetThrottleCmd(int engine) const
Gets the throttle command.
Models the EOM and integration/propagation of state.
void SetRRadpsIC(double R)
Sets the initial body axis yaw rate.
void SetMode(TrimMode tm)
Clear all state-control pairs and set a predefined trim mode.
FGGroundReactions * GetGroundReactions(void)
Returns the FGGroundReactions pointer.
FGLocation LocalToLocation(const FGColumnVector3 &lvec) const
Conversion from Local frame coordinates to a location in the earth centered and fixed frame...
const FGMatrix33 & GetTb2l(void) const
Retrieves the body-to-local transformation matrix.
const FGQuaternion & GetOrientation(void) const
Gets the initial orientation.
void SetDrCmd(double cmd)
Sets the rudder command.
void SetDeCmd(double cmd)
Sets the elevator command.
This class implements a 3 element column vector.
bool EditState(State state, Control new_control)
Change the control used to zero a state previously configured.
void ResumeIntegration(void)
Resumes the simulation by resetting delta T to the correct value.
double GetDaCmd(void) const
Gets the aileron command.
void Run(void)
This function iterates through a call to the FGFDMExec::RunIC() function until the desired trimming c...
FGFCS * GetFCS(void)
Returns the FGFCS pointer.
Encapsulates the Flight Control System (FCS) functionality.
bool RemoveState(State state)
Remove a specific state-control pair from the current configuration.
Manages ground reactions modeling.
Handles the calculation of accelerations.
Handles matrix math operations.
FGLGear * GetGearUnit(int gear) const
Gets a gear instance.
double Magnitude(void) const
Length of the vector.
void ClearStates(void)
Clear all state-control pairs from the current configuration.
const FGColumnVector3 & GetPQRdot(void) const
Retrieves the body axis angular acceleration vector.
bool Run(void)
This function executes each scheduled model in succession.
void SetPRadpsIC(double P)
Sets the initial body axis roll rate.
double GetThetaRadIC(void) const
Gets the initial pitch angle.
Encapsulates the JSBSim simulation executive.
double GetDrCmd(void) const
Gets the rudder command.
Models weight, balance and moment of inertia information.
void SetAltitudeASLFtIC(double altitudeASL)
Sets the altitude above sea level initial condition in feet.
void SetReport(bool flag)
Set the console touchdown reporting feature.
void SetPsiRadIC(double psi)
Sets the initial heading angle.
double GetAltitudeASLFtIC(void) const
Gets the initial altitude above sea level.
bool DoTrim(void)
Execute the trim.
FGPropagate * GetPropagate(void)
Returns the FGPropagate pointer.
void TrimStats()
Iteration statistics.
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.