JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGTrimmer.cpp
1 /*
2  * FGTrimmer.cpp
3  * Copyright (C) James Goppert 2010 <james.goppert@gmail.com>
4  *
5  * FGTrimmer.cpp is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by the
7  * Free Software Foundation, either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * FGTrimmer.cpp is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License along
16  * with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "FGTrimmer.h"
20 #include "models/FGFCS.h"
21 #include "models/FGPropulsion.h"
22 #include "models/FGAccelerations.h"
23 #include "models/propulsion/FGEngine.h"
24 #include "models/propulsion/FGThruster.h"
25 #include "models/propulsion/FGTank.h"
26 #include "models/FGMassBalance.h"
27 #include "models/FGAuxiliary.h"
28 #include "models/FGAircraft.h"
29 #include <iomanip>
30 #include <cstdlib>
31 #include <stdexcept>
32 #include "simgear/misc/stdint.hxx"
33 #include "FGInitialCondition.h"
34 
35 namespace JSBSim
36 {
37 
38 FGTrimmer::FGTrimmer(FGFDMExec * fdm, Constraints * constraints) :
39  m_fdm(fdm), m_constraints(constraints)
40 {
41 }
42 
43 FGTrimmer::~FGTrimmer()
44 {
45 }
46 
47 std::vector<double> FGTrimmer::constrain(const std::vector<double> & dv)
48 {
49  // unpack design vector
50  double throttle = dv[0];
51  double elevator = dv[1];
52  double alpha = dv[2];
53  double aileron = dv[3];
54  double rudder = dv[4];
55  double beta = dv[5];
56 
57  // initialize constraints
58  double vt = m_constraints->velocity;
59  double altitude = m_constraints->altitude;
60  double gamma = m_constraints->gamma;
61  double phi = m_fdm->GetIC()->GetPhiRadIC();
62  double theta = m_fdm->GetIC()->GetThetaRadIC();
63  double psi = m_fdm->GetIC()->GetPsiRadIC();
64  double p = 0.0, q = 0.0, r= 0.0;
65  double u = vt*cos(alpha)*cos(beta);
66  double v = vt*sin(beta);
67  double w = vt*sin(alpha)*cos(beta);
68  double lat = m_fdm->GetIC()->GetLatitudeRadIC();
69  double lon = m_fdm->GetIC()->GetLongitudeRadIC();
70 
71  // precomputation
72  double sGam = sin(gamma);
73  double sBeta = sin(beta);
74  double cBeta = cos(beta);
75  double tAlpha = tan(alpha);
76  double cAlpha = cos(alpha);
77 
78  // turn coordination constraint, lewis pg. 190
79  double gd = m_fdm->GetInertial()->gravity();
80  double gc = m_constraints->yawRate*vt/gd;
81  double a = 1 - gc*tAlpha*sBeta;
82  double b = sGam/cBeta;
83  double c = 1 + gc*gc*cBeta*cBeta;
84  phi = atan((gc*cBeta*((a-b*b)+
85  b*tAlpha*sqrt(c*(1-b*b)+gc*gc*sBeta*sBeta)))/
86  (cAlpha*(a*a-b*b*(1+c*tAlpha*tAlpha))));
87 
88  // rate of climb constraint
89  a = cAlpha*cBeta;
90  b = sin(phi)*sBeta+cos(phi)*sin(alpha)*cBeta;
91  theta = atan((a*b+sGam*sqrt(a*a-sGam*sGam+b*b))/(a*a-sGam*sGam));
92 
93  // turn rates
94  if (m_constraints->rollRate != 0.0) // rolling
95  {
96  p = m_constraints->rollRate;
97  q = 0.0;
98  if (m_constraints->stabAxisRoll) // stability axis roll
99  {
100  r = m_constraints->rollRate*tan(alpha);
101  }
102  else // body axis roll
103  {
104  r = m_constraints->rollRate;
105  }
106  }
107  else if (m_constraints->yawRate != 0.0) // yawing
108  {
109  p = -m_constraints->yawRate*sin(theta);
110  q = m_constraints->yawRate*sin(phi)*cos(theta);
111  r = m_constraints->yawRate*cos(phi)*cos(theta);
112  }
113  else if (m_constraints->pitchRate != 0.0) // pitching
114  {
115  p = 0.0;
116  q = m_constraints->pitchRate;
117  r = 0.0;
118  }
119 
120  // apply state
121  m_fdm->GetIC()->ResetIC(u, v, w,
122  p, q, r,
123  alpha, beta,
124  phi, theta, psi,
125  lat, lon, altitude,
126  gamma);
127 
128  // set controls
129  m_fdm->GetFCS()->SetDeCmd(elevator);
130  m_fdm->GetFCS()->SetDePos(ofNorm,elevator);
131 
132  m_fdm->GetFCS()->SetDaCmd(aileron);
133  m_fdm->GetFCS()->SetDaLPos(ofNorm,aileron);
134  m_fdm->GetFCS()->SetDaRPos(ofNorm,aileron);
135 
136  m_fdm->GetFCS()->SetDrPos(ofNorm,rudder);
137  m_fdm->GetFCS()->SetDrCmd(rudder);
138 
139  for (unsigned int i=0; i<m_fdm->GetPropulsion()->GetNumEngines(); i++)
140  {
141  m_fdm->GetFCS()->SetThrottleCmd(i,throttle);
142  m_fdm->GetFCS()->SetThrottlePos(i,throttle);
143  }
144 
145  // initialize
146  m_fdm->Initialize(m_fdm->GetIC());
147  for (unsigned int i=0; i<m_fdm->GetPropulsion()->GetNumEngines(); i++) {
148  m_fdm->GetPropulsion()->GetEngine(i)->InitRunning();
149  }
150 
151  // wait for stable state
152  double cost = compute_cost();
153  for(int i=0;;i++) {
154  m_fdm->GetPropulsion()->GetSteadyState();
155  m_fdm->SetTrimStatus(true);
156  m_fdm->DisableOutput();
157  m_fdm->SuspendIntegration();
158  m_fdm->Run();
159  m_fdm->SetTrimStatus(false);
160  m_fdm->EnableOutput();
161  m_fdm->ResumeIntegration();
162 
163  double costNew = compute_cost();
164  double dcost = fabs(costNew - cost);
165  if (dcost < std::numeric_limits<double>::epsilon()) {
166  if(m_fdm->GetDebugLevel() > 1) {
167  std::cout << "cost convergd, i: " << i << std::endl;
168  }
169  break;
170  }
171  if (i > 1000) {
172  if(m_fdm->GetDebugLevel() > 1) {
173  std::cout << "cost failed to converge, dcost: "
174  << std::scientific
175  << dcost << std::endl;
176  }
177  break;
178  }
179  cost = costNew;
180  }
181 
182  std::vector<double> data;
183  data.push_back(phi);
184  data.push_back(theta);
185  return data;
186 }
187 
188 void FGTrimmer::printSolution(std::ostream & stream, const std::vector<double> & v)
189 {
190  eval(v);
191 
192  //double dt = m_fdm->GetDeltaT();
193  double elevator = m_fdm->GetFCS()->GetDePos(ofNorm);
194  double aileron = m_fdm->GetFCS()->GetDaLPos(ofNorm);
195  double rudder = m_fdm->GetFCS()->GetDrPos(ofNorm);
196  double throttle = m_fdm->GetFCS()->GetThrottlePos(0);
197  double lat = m_fdm->GetPropagate()->GetLatitudeDeg();
198  double lon = m_fdm->GetPropagate()->GetLongitudeDeg();
199  double vt = m_fdm->GetAuxiliary()->GetVt();
200 
201  //double dthrust = (m_fdm->GetPropulsion()->GetEngine(0)->
202  //GetThruster()->GetThrust()-thrust)/dt;
203  //double delevator = (m_fdm->GetFCS()->GetDePos(ofNorm)-elevator)/dt;
204  //double daileron = (m_fdm->GetFCS()->GetDaLPos(ofNorm)-aileron)/dt;
205  //double drudder = (m_fdm->GetFCS()->GetDrPos(ofNorm)-rudder)/dt;
206  //double dthrottle = (m_fdm->GetFCS()->GetThrottlePos(0)-throttle)/dt;
207  //double dlat = (m_fdm->GetPropagate()->GetLatitudeDeg()-lat)/dt;
208  //double dlon = (m_fdm->GetPropagate()->GetLongitudeDeg()-lon)/dt;
209  //double dvt = (m_fdm->GetAuxiliary()->GetVt()-vt)/dt;
210 
211  // reinitialize with correct state
212  eval(v);
213 
214  // state
215  stream << std::setw(10)
216 
217  // aircraft state
218  << "\naircraft state"
219  << "\n\tvt\t\t:\t" << vt
220  << "\n\talpha, deg\t:\t" << m_fdm->GetIC()->GetAlphaDegIC()
221  << "\n\ttheta, deg\t:\t" << m_fdm->GetIC()->GetThetaDegIC()
222  << "\n\tq, rad/s\t:\t" << m_fdm->GetIC()->GetQRadpsIC()
223  << "\n\tthrust, lbf\t:\t" << m_fdm->GetPropulsion()->GetEngine(0)->GetThruster()->GetThrust()
224  << "\n\tbeta, deg\t:\t" << m_fdm->GetIC()->GetBetaDegIC()
225  << "\n\tphi, deg\t:\t" << m_fdm->GetIC()->GetPhiDegIC()
226  << "\n\tp, rad/s\t:\t" << m_fdm->GetIC()->GetPRadpsIC()
227  << "\n\tr, rad/s\t:\t" << m_fdm->GetIC()->GetRRadpsIC()
228  << "\n\tmass (lbm)\t:\t" << m_fdm->GetMassBalance()->GetWeight()
229 
230  // actuator states
231  << "\n\nactuator state"
232  << "\n\tthrottle, %\t:\t" << 100*throttle
233  << "\n\televator, %\t:\t" << 100*elevator
234  << "\n\taileron, %\t:\t" << 100*aileron
235  << "\n\trudder, %\t:\t" << 100*rudder
236 
237  // nav state
238  << "\n\nnav state"
239  << "\n\taltitude, ft\t:\t" << m_fdm->GetIC()->GetAltitudeASLFtIC()
240  << "\n\tpsi, deg\t:\t" << m_fdm->GetIC()->GetPsiDegIC()
241  << "\n\tlat, deg\t:\t" << lat
242  << "\n\tlon, deg\t:\t" << lon
243 
244  // d/dt aircraft state
245  << "\n\naircraft d/dt state"
246  << std::scientific
247 
248  //<< "\n\td/dt vt\t\t\t:\t" << dvt
249  << "\n\td/dt alpha, deg/s\t:\t" << m_fdm->GetAuxiliary()->Getadot()*180/M_PI
250  << "\n\td/dt theta, deg/s\t:\t" << m_fdm->GetAuxiliary()->GetEulerRates(2)*180/M_PI
251  << "\n\td/dt q, rad/s^2\t\t:\t" << m_fdm->GetAccelerations()->GetPQRdot(2)
252  //<< "\n\td/dt thrust, lbf\t:\t" << dthrust
253  << "\n\td/dt beta, deg/s\t:\t" << m_fdm->GetAuxiliary()->Getbdot()*180/M_PI
254  << "\n\td/dt phi, deg/s\t\t:\t" << m_fdm->GetAuxiliary()->GetEulerRates(1)*180/M_PI
255  << "\n\td/dt p, rad/s^2\t\t:\t" << m_fdm->GetAccelerations()->GetPQRdot(1)
256  << "\n\td/dt r, rad/s^2\t\t:\t" << m_fdm->GetAccelerations()->GetPQRdot(3)
257 
258  // d/dt actuator states
259  //<< "\n\nd/dt actuator state"
260  //<< "\n\td/dt throttle, %/s\t:\t" << dthrottle
261  //<< "\n\td/dt elevator, %/s\t:\t" << delevator
262  //<< "\n\td/dt aileron, %/s\t:\t" << daileron
263  //<< "\n\td/dt rudder, %/s\t:\t" << drudder
264 
265  // nav state
266  << "\n\nd/dt nav state"
267  << "\n\td/dt altitude, ft/s\t:\t" << m_fdm->GetPropagate()->Gethdot()
268  << "\n\td/dt psi, deg/s\t\t:\t" << m_fdm->GetAuxiliary()->GetEulerRates(3)
269  //<< "\n\td/dt lat, deg/s\t\t:\t" << dlat
270  //<< "\n\td/dt lon, deg/s\t\t:\t" << dlon
271  << std::fixed
272 
273  << "\n\npropulsion system state"
274  << std::scientific << std::setw(10);
275 
276  for (unsigned int i=0;i<m_fdm->GetPropulsion()->GetNumTanks();i++) {
277  stream
278  << "\n\ttank " << i << ": fuel (lbm)\t\t\t:\t"
279  << m_fdm->GetPropulsion()->GetTank(i)->GetContents();
280  }
281 
282  for (unsigned int i=0;i<m_fdm->GetPropulsion()->GetNumEngines();i++) {
283  m_fdm->GetPropulsion()->GetEngine(i)->CalcFuelNeed();
284  stream
285  << "\n\tengine " << i
286  << "\n\t\tfuel flow rate (lbm/s)\t\t:\t" << m_fdm->GetPropulsion()->GetEngine(i)->GetFuelFlowRate()
287  << "\n\t\tfuel flow rate (gph)\t\t:\t" << m_fdm->GetPropulsion()->GetEngine(i)->GetFuelFlowRateGPH()
288  << "\n\t\tstarved\t\t\t\t:\t" << m_fdm->GetPropulsion()->GetEngine(i)->GetStarved()
289  << "\n\t\trunning\t\t\t\t:\t" << m_fdm->GetPropulsion()->GetEngine(i)->GetRunning()
290  << std::endl;
291  }
292 }
293 
294 void FGTrimmer::printState(std::ostream & stream)
295 {
296  // state
297  stream << std::setw(10)
298 
299  // interval method comparison
300  //<< "\n\ninterval method comparison"
301  //<< "\nAngle of Attack: \t:\t" << m_fdm->GetAuxiliary()->Getalpha(ofDeg) << "\twdot: " << m_fdm->GetAccelerations()->GetUVWdot(3)
302  //<< "\nThrottle: \t:\t" << 100*m_fdm->GetFCS()->GetThrottlePos(0) << "\tudot: " << m_fdm->GetAccelerations()->GetUVWdot(1)
303  //<< "\nPitch Trim: \t:\t" << 100*m_fdm->GetFCS()->GetDePos(ofNorm) << "\tqdot: " << m_fdm->GetAccelerations()->GetPQRdot(2)
304  //<< "\nSideslip: \t:\t" << m_fdm->GetAuxiliary()->Getbeta(ofDeg) << "\tvdot: " << m_fdm->GetAccelerations()->GetUVWdot(2)
305  //<< "\nAilerons: \t:\t" << 100*m_fdm->GetFCS()->GetDaLPos(ofNorm) << "\tpdot: " << m_fdm->GetAccelerations()->GetPQRdot(1)
306  //<< "\nRudder: \t:\t" << 100*m_fdm->GetFCS()->GetDrPos(ofNorm) << "\trdot: " << m_fdm->GetAccelerations()->GetPQRdot(3)
307 
308  << "\n\naircraft state"
309  << "\nvt\t\t:\t" << m_fdm->GetAuxiliary()->GetVt()
310  << "\nalpha, deg\t:\t" << m_fdm->GetAuxiliary()->Getalpha(ofDeg)
311  << "\ntheta, deg\t:\t" << m_fdm->GetPropagate()->GetEuler(2)*180/M_PI
312  << "\nq, rad/s\t:\t" << m_fdm->GetPropagate()->GetPQR(2)
313  << "\nthrust, lbf\t:\t" << m_fdm->GetPropulsion()->GetEngine(0)->GetThruster()->GetThrust()
314  << "\nbeta, deg\t:\t" << m_fdm->GetAuxiliary()->Getbeta(ofDeg)
315  << "\nphi, deg\t:\t" << m_fdm->GetPropagate()->GetEuler(1)*180/M_PI
316  << "\np, rad/s\t:\t" << m_fdm->GetPropagate()->GetPQR(1)
317  << "\nr, rad/s\t:\t" << m_fdm->GetPropagate()->GetPQR(3)
318 
319  // actuator states
320  << "\n\nactuator state"
321  << "\nthrottle, %\t:\t" << 100*m_fdm->GetFCS()->GetThrottlePos(0)
322  << "\nelevator, %\t:\t" << 100*m_fdm->GetFCS()->GetDePos(ofNorm)
323  << "\naileron, %\t:\t" << 100*m_fdm->GetFCS()->GetDaLPos(ofNorm)
324  << "\nrudder, %\t:\t" << 100*m_fdm->GetFCS()->GetDrPos(ofNorm)
325 
326  // nav state
327  << "\n\nnav state"
328  << "\naltitude, ft\t:\t" << m_fdm->GetPropagate()->GetAltitudeASL()
329  << "\npsi, deg\t:\t" << m_fdm->GetPropagate()->GetEuler(3)*180/M_PI
330  << "\nlat, deg\t:\t" << m_fdm->GetPropagate()->GetLatitudeDeg()
331  << "\nlon, deg\t:\t" << m_fdm->GetPropagate()->GetLongitudeDeg()
332 
333  // input
334  << "\n\ninput"
335  << "\nthrottle cmd, %\t:\t" << 100*m_fdm->GetFCS()->GetThrottleCmd(0)
336  << "\nelevator cmd, %\t:\t" << 100*m_fdm->GetFCS()->GetDeCmd()
337  << "\naileron cmd, %\t:\t" << 100*m_fdm->GetFCS()->GetDaCmd()
338  << "\nrudder cmd, %\t:\t" << 100*m_fdm->GetFCS()->GetDrCmd()
339 
340  << std::endl;
341 
342 }
343 
344 double FGTrimmer::compute_cost()
345 {
346  double dvt = (m_fdm->GetPropagate()->GetUVW(1)*m_fdm->GetAccelerations()->GetUVWdot(1) +
347  m_fdm->GetPropagate()->GetUVW(2)*m_fdm->GetAccelerations()->GetUVWdot(2) +
348  m_fdm->GetPropagate()->GetUVW(3)*m_fdm->GetAccelerations()->GetUVWdot(3))/
349  m_fdm->GetAuxiliary()->GetVt(); // from lewis, vtrue dot
350  double dalpha = m_fdm->GetAuxiliary()->Getadot();
351  double dbeta = m_fdm->GetAuxiliary()->Getbdot();
352  double dp = m_fdm->GetAccelerations()->GetPQRdot(1);
353  double dq = m_fdm->GetAccelerations()->GetPQRdot(2);
354  double dr = m_fdm->GetAccelerations()->GetPQRdot(3);
355 
356  if(m_fdm->GetDebugLevel() > 1) {
357  std::cout
358  << "dvt: " << dvt
359  << "\tdalpha: " << dalpha
360  << "\tdbeta: " << dbeta
361  << "\tdp: " << dp
362  << "\tdq: " << dq
363  << "\tdr: " << dr
364  << std::endl;
365  }
366 
367  return dvt*dvt +
368  100.0*(dalpha*dalpha + dbeta*dbeta) +
369  10.0*(dp*dp + dq*dq + dr*dr);
370  }
371 
372 double FGTrimmer::eval(const std::vector<double> & v)
373 {
374  constrain(v);
375  return compute_cost();
376 }
377 
378 } // JSBSim
379 
380 
381 // vim:ts=4:sw=4:expandtab