1 // -*- mode: c; mode: fold -*-
2 /*
3  * CRRCsim - the Charles River Radio Control Club Flight Simulator Project
4  *   Copyright (C) 2005, 2006, 2008, 2009 - Jens Wilhelm Wulf (original author)
5  *   Copyright (C) 2006 - 2009 - Jan Reucker
6  *   Copyright (C) 2006 - Todd Templeton
7  *
8  * This file is partially based on work by
9  *   Jan Kansky
10  *   Bruce Jackson
11  * The respective methods have a header containing more details, see below.
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License version 2
15  * as published by the Free Software Foundation.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330,
25  * Boston, MA 02111-1307, USA.
26  *
27  */
28 #include "fdm_larcsim.h"
29 
30 #include <math.h>
31 #include <iostream>
32 
33 #include "../../mod_misc/ls_constants.h"
34 #include "../ls_geodesy.h"
35 #include "../../mod_misc/SimpleXMLTransfer.h"
36 #include "../../mod_misc/lib_conversions.h"
37 #include "../xmlmodelfile.h"
38 
39 // 0, 1, 2
40 #define EOM_TEST 0
41 
42 /**
43  * *****************************************************************************
44  */
45 
initAirplaneState(double dRelVel,double dPhi,double dTheta,double dPsi,double X,double Y,double Z,double R_X,double R_Y,double R_Z)46 void CRRC_AirplaneSim_Larcsim::initAirplaneState(double dRelVel,
47                                                  double dPhi,
48                                                  double dTheta,
49                                                  double dPsi,
50                                                  double X,
51                                                  double Y,
52                                                  double Z,
53                                                  double R_X,
54                                                  double R_Y,
55                                                  double R_Z)
56 {
57   float flVelocity = dRelVel * getTrimmedFlightVelocity();
58 
59   Phi       = dPhi;         // bank/roll angle  [rad]
60   Theta     = dTheta;       // pitch attitude angle [rad]
61   Psi       = dPsi;         // heading angle [rad]
62 
63   Alpha     = 0;            // angle of attack            [rad]
64   Beta      = 0;            // sideslip angle             [rad]
65 
66   {
67     // see ls_aux(): 'determine location in runway coordinates'
68     double slr, dummy;
69     ls_geod_to_geoc( 0, 0, &slr, &dummy);
70 
71     Latitude  = X/slr;
72     Longitude = Y/slr;
73     Altitude  = -1*Z;
74   }
75 
76   {
77     // horizontal velocity
78     float flVHor = flVelocity * cos(dTheta);
79     // horizontal velocity has to be upwind:
80     v_V_local.r[0]  = cos(Psi)*flVHor;      // local x-velocity (north)   [ft/s]
81     v_V_local.r[1]  = sin(Psi)*flVHor;      // local y-velocity (east)    [ft/s]
82 
83     v_V_local_rel_ground.r[1] = v_V_local.r[1];
84   }
85   v_V_local.r[2]         = flVelocity * sin(-dTheta);          // local z-velocity (down)     [ft/s]
86 
87   v_R_omega_body    = CRRCMath::Vector3(R_X, R_Y, R_Z); // body rate   [rad/s]
88   v_V_dot_local     = CRRCMath::Vector3(); // local acceleration   [ft/s^2]
89 
90   CD_stall        = 0.05;   // drag coeff. during stalling    []
91 
92   ls_step_init();
93 
94   power->InitStates(v_V_wind_body * FT_TO_M);
95 }
96 
97 
update(TSimInputs * inputs,double dt,int multiloop)98 void CRRC_AirplaneSim_Larcsim::update(TSimInputs* inputs,
99                                       double      dt,
100                                       int         multiloop)
101 {
102   CRRCMath::Vector3 v_V_local_airmass;
103   CRRCMath::Vector3 v_V_gust_body, v_R_omega_gust_body;
104   CRRCMath::Matrix33 m_V_local_airmass_grad;
105 
106   CRRCMath::Vector3 v_F_aero, v_F_engine, v_F_gear; // Force x/y/z
107   CRRCMath::Vector3 v_M_aero, v_M_engine, v_M_gear; // l/m/n <-> roll/pitch/yaw
108 
109   int nAircraftOutsideWindfieldSim =
110     env->CalculateWind(v_P_CG_Rwy.r[0],        v_P_CG_Rwy.r[1],        v_P_CG_Rwy.r[2],
111                        v_V_local_airmass.r[0], v_V_local_airmass.r[1], v_V_local_airmass.r[2]);
112 
113   /**
114    * Using a length of about roughly one half of the aircraft's
115    * span to calculate wind gradients. 0.1 foot had been used before,
116    * which leads to very high or zero gradients.
117    */
118   double delta_space = 0.5*getWingspan();
119 
120   nAircraftOutsideWindfieldSim |=
121     env->CalculateWindGrad(v_P_CG_Rwy.r[0], v_P_CG_Rwy.r[1], v_P_CG_Rwy.r[2], delta_space,
122                            m_V_local_airmass_grad);
123 
124   if (nAircraftOutsideWindfieldSim)
125   {
126     env->AddLogMsg("Error: aircraft outside windfield simulation");
127   }
128 
129   for (int n=0; n<multiloop; n++)
130   {
131     logNewline();
132 
133 #if FDM_LOG_POS != 0
134     logVal(v_P_CG_Rwy.r[0]);
135     logVal(v_P_CG_Rwy.r[1]);
136     logVal(v_P_CG_Rwy.r[2]);
137     logVal(getPhi());
138     logVal(getTheta());
139     logVal(getPsi());
140 #endif
141 #if FDM_LOG_WIND_IN != 0
142     logVal(v_V_local_airmass);
143     logVal(m_V_atmo_rwy);
144 #endif
145 
146     ls_step(dt);
147 
148     // update wind turbulence linear & rotational velocities
149     env->CalculateWindGust(dt, getAlt(), v_V_local_rel_airmass.length(), getWingspan(),
150                            v_V_local_airmass, LocalToBody,
151                            v_V_gust_body, v_R_omega_gust_body);
152 
153     ls_aux(v_V_local_airmass, v_V_gust_body);
154 
155     env->ControllerCallback(dt, this, inputs, &myInputs);
156 
157     aero(&myInputs, m_V_local_airmass_grad, v_R_omega_gust_body, v_F_aero, v_M_aero);
158 
159 #if FDM_LOG_AERO_OUT != 0
160     logVal(v_F_aero);
161     logVal(v_M_aero);
162 #endif
163 
164     engine(dt, &myInputs, v_F_engine, v_M_engine);
165     gear(&myInputs, v_F_gear, v_M_gear);
166 
167     // Only a fraction of the total rolling torque is not cancelled
168     // by the aero effect of the prop wash on fuselage, wing and tail
169     v_M_engine.r[0] *= effectivePropellerTorqueFactor;
170 
171     // Sum forces and moments at reference point (center of gravity)
172     ls_accel(v_F_aero + v_F_engine + v_F_gear, v_M_aero + v_M_engine + v_M_gear);
173   }
174 }
175 
176 
CRRC_AirplaneSim_Larcsim(const char * filename,FDMEnviroment * myEnv,SimpleXMLTransfer * cfg)177 CRRC_AirplaneSim_Larcsim::CRRC_AirplaneSim_Larcsim(const char* filename, FDMEnviroment* myEnv, SimpleXMLTransfer* cfg) : EOM01("fdm_larcsim.dat", myEnv)
178 {
179   // Previously there has been code to load an old-style .air-file.
180   // This has been removed as CRRCSim includes an automatic converter.
181   SimpleXMLTransfer* fileinmemory = new SimpleXMLTransfer(filename);
182 
183   power = 0;
184   LoadFromXML(fileinmemory, cfg->getInt("airplane.verbosity", 5));
185 
186   delete fileinmemory;
187 }
188 
189 
CRRC_AirplaneSim_Larcsim(SimpleXMLTransfer * xml,FDMEnviroment * myEnv,SimpleXMLTransfer * cfg)190 CRRC_AirplaneSim_Larcsim::CRRC_AirplaneSim_Larcsim(SimpleXMLTransfer* xml, FDMEnviroment* myEnv, SimpleXMLTransfer* cfg) : EOM01("fdm_larcsim.dat", myEnv)
191 {
192   power = 0;
193   LoadFromXML(xml, cfg->getInt("airplane.verbosity", 5));
194 }
195 
LoadFromXML(SimpleXMLTransfer * xml,int nVerbosity)196 void CRRC_AirplaneSim_Larcsim::LoadFromXML(SimpleXMLTransfer* xml, int nVerbosity)
197 {
198   SimpleXMLTransfer* i;
199   SimpleXMLTransfer* cfg = XMLModelFile::getConfig(xml);
200   SimpleXMLTransfer* aero;
201 
202   // File format extension: an aero section inside of config takes
203   // precedence over the general aero section.
204   {
205     int i = cfg->indexOfChild("aero");
206     if (i >= 0)
207       aero = cfg->getChildAt(i);
208     else
209       aero = xml->getChild("aero");
210   }
211 
212   {
213     double to_ft;
214 
215     switch (aero->getInt("units"))
216     {
217      case 0:
218       to_ft = 1;
219       break;
220      case 1:
221       to_ft = M_TO_FT;
222       break;
223      default:
224       {
225         throw std::runtime_error("Unknown units in aero");
226       }
227       break;
228     }
229     i = aero->getChild("ref");
230     C_ref = i->getDouble("chord") * to_ft;
231     B_ref = i->getDouble("span") * to_ft;
232     S_ref = i->getDouble("area") * to_ft * to_ft;
233     U_ref = i->getDouble("speed") * to_ft;
234   }
235 
236   i = aero->getChild("misc");
237   Alpha_0  = i->getDouble("Alpha_0");
238   eta_loc  = i->getDouble("eta_loc");
239   CG_arm   = i->getDouble("CG_arm");
240   span_eff = i->getDouble("span_eff");
241 
242   i = aero->getChild("m");
243   Cm_0  = i->getDouble("Cm_0");
244   Cm_a  = i->getDouble("Cm_a");
245   Cm_q  = i->getDouble("Cm_q");
246   Cm_de = i->getDouble("Cm_de");
247 
248   i = aero->getChild("lift");
249   CL_0    = i->getDouble("CL_0");
250   CL_max  = i->getDouble("CL_max");
251   CL_min  = i->getDouble("CL_min");
252   CL_a    = i->getDouble("CL_a");
253   CL_q    = i->getDouble("CL_q");
254   CL_de   = i->getDouble("CL_de");
255   CL_drop = i->getDouble("CL_drop");
256   CL_CD0  = i->getDouble("CL_CD0");
257 
258   i = aero->getChild("drag");
259   CD_prof  = i->getDouble("CD_prof");
260   Uexp_CD  = i->getDouble("Uexp_CD");
261   CD_stall = i->getDouble("CD_stall");
262   CD_CLsq  = i->getDouble("CD_CLsq");
263   CD_AIsq  = i->getDouble("CD_AIsq");
264   CD_ELsq  = i->getDouble("CD_ELsq");
265 
266   i = aero->getChild("Y");
267   CY_b  = i->getDouble("CY_b");
268   CY_p  = i->getDouble("CY_p");
269   CY_r  = i->getDouble("CY_r");
270   CY_dr = i->getDouble("CY_dr");
271   CY_da = i->getDouble("CY_da");
272 
273   i = aero->getChild("l");
274   Cl_b  = i->getDouble("Cl_b");
275   Cl_p  = i->getDouble("Cl_p");
276   Cl_r  = i->getDouble("Cl_r");
277   Cl_dr = i->getDouble("Cl_dr");
278   Cl_da = i->getDouble("Cl_da");
279 
280   i = aero->getChild("n");
281   Cn_b  = i->getDouble("Cn_b");
282   Cn_p  = i->getDouble("Cn_p");
283   Cn_r  = i->getDouble("Cn_r");
284   Cn_dr = i->getDouble("Cn_dr");
285   Cn_da = i->getDouble("Cn_da");
286 
287   flap_drag      = aero->getDouble("flap.drag", 0);
288   flap_lift      = aero->getDouble("flap.lift", 0);
289   flap_moment    = aero->getDouble("flap.moment", 0);
290   flap_eff_ratio = aero->getDouble("flap.eff_ratio", 1.0);
291 
292   spoiler_drag   = aero->getDouble("spoiler.drag", 0);
293   spoiler_lift   = aero->getDouble("spoiler.lift", 0);
294   spoiler_moment = aero->getDouble("spoiler.moment", 0);
295 
296   retract_drag = aero->getDouble("retract.drag", 0);
297   retract_lift = aero->getDouble("retract.lift", 0);
298 
299   effectivePropellerTorqueFactor = aero->getDouble("prop.torquefactor", 0.25);
300 
301   {
302     double to_slug;
303     double to_slug_ft_ft;
304 
305     i = cfg->getChild("mass_inertia");
306     switch (i->getInt("units"))
307     {
308      case 0:
309       to_slug       = 1;
310       to_slug_ft_ft = 1;
311       break;
312      case 1:
313       to_slug       = KG_TO_SLUG;
314       to_slug_ft_ft = KG_M_M_TO_SLUG_FT_FT;
315       break;
316      default:
317       {
318         throw std::runtime_error("Unknown units in mass_inertia");
319       }
320       break;
321     }
322     Mass  = i->getDouble("Mass") * to_slug;
323     I_xx  = i->getDouble("I_xx") * to_slug_ft_ft;
324     I_yy  = i->getDouble("I_yy") * to_slug_ft_ft;
325     I_zz  = i->getDouble("I_zz") * to_slug_ft_ft;
326     I_xz  = i->getDouble("I_xz") * to_slug_ft_ft;
327   }
328 
329   wheels.init(xml, B_ref);
330 
331   // calculate velocity in trimmed flight,
332   // approximately since Gravity & Density are yet unknown.
333   // TrimmedFlightVelocity is used to initialise airplane state
334   // and program crash if velocity is not reasonable
335   // (e.g. trimmed CL <= 0)
336 {
337     // crrc_aero says: Cm = Cm_0 + Cm_a*(Alpha-Alpha_0)
338     // So Cm is zero at
339     float alpha = ((Cm_a * Alpha_0) - Cm_0 ) / Cm_a;
340     // crrc_aero says: CL  = CL_0 + CL_a*(Alpha - Alpha_0);
341     float cl = CL_0 + CL_a * (alpha - Alpha_0);
342 
343     // sanity check, to avoid extreme (or negative) launch speed.
344     // E.g. Cm_a data for biplane2.air is most likely wrong (should be < 0!)
345     // so the equilibrium is achieved for a negative cl.
346     if (cl < 0.1)
347     {
348       cl = 0.1;
349       printf("airplane: Cm_a=%f   Alpha_0=%f  Cm_0=%f\n", Cm_a, Alpha_0, Cm_0);
350       printf("airplane: CL_a=%f               CL_0=%f\n", CL_a,          CL_0);
351       printf("airplane: alpha=%f  C_L=%f\n", alpha, cl);
352     }
353 
354     // Gravity & Density yet not initialised: assume 0 height
355     float Gravity = env->GetG(0.0);
356     float Density = env->GetRho(0.0);
357 
358     // v = sqrt(m * g * 2 / (S * CL * rho))
359     trimmedFlightVelocity = sqrt(Mass * Gravity * 2.0 / (S_ref * cl * Density));
360   }
361 
362   // possibly there is no power description:
363   if (power == 0)
364   {
365     if (cfg->indexOfChild("power") < 0)
366     {
367       power = new Power::Power();
368     }
369     else
370     {
371       power = new Power::Power(cfg, nVerbosity);
372     }
373   }
374   else if (cfg->indexOfChild("power") >= 0)
375     power->ReloadParams(cfg, nVerbosity);
376 
377   if (nVerbosity > 1)
378   {
379     std::cout << "--- Airplane description: ---------------------------------------\n";
380     std::cout << "  Wingspan:                   " << (B_ref*FT_TO_M) << " m\n";
381     std::cout << "  Mass:                       " << (Mass*SLUG_TO_KG) << " kg\n";
382     std::cout << "  Velocity in trimmed flight: " << (trimmedFlightVelocity*FT_TO_M) << " m/s\n";
383     std::cout << "-----------------------------------------------------------------\n";
384   }
385 }
386 
~CRRC_AirplaneSim_Larcsim()387 CRRC_AirplaneSim_Larcsim::~CRRC_AirplaneSim_Larcsim()
388 {
389   delete power;
390 }
391 
392 /** \brief Calculate influence of gear and hardpoints
393  *
394  *  This method calculates the forces and moments on
395  *  the airplane's body caused by the gear or other
396  *  hardpoints touching the ground.
397  *
398  *  \param inputs   Current control inputs
399  */
gear(TSimInputs * inputs,CRRCMath::Vector3 & v_F,CRRCMath::Vector3 & v_M)400 void CRRC_AirplaneSim_Larcsim::gear(TSimInputs* inputs, CRRCMath::Vector3& v_F, CRRCMath::Vector3& v_M)
401 {
402   wheels.update(inputs,
403                 env,
404                 LocalToBody,
405                 v_P_CG_Rwy,
406                 v_R_omega_body,
407                 v_V_local_rel_ground,
408                 Psi);
409 
410   v_F = wheels.getForces();
411   v_M = wheels.getMoments();
412 }
413 
414 
engine(SCALAR dt,TSimInputs * inputs,CRRCMath::Vector3 & v_F,CRRCMath::Vector3 & v_M)415 void CRRC_AirplaneSim_Larcsim::engine( SCALAR dt, TSimInputs* inputs, CRRCMath::Vector3& v_F, CRRCMath::Vector3& v_M)
416 {
417   v_F = CRRCMath::Vector3();
418   v_M = CRRCMath::Vector3();
419 
420   inputs->pitch = 1;
421   power->step(dt, inputs, v_V_wind_body*FT_TO_M, &v_F, &v_M);
422 
423   // Convert SI to that other buggy system.
424   v_F *= N_TO_LBF;
425   v_M *= NM_TO_LBFFT;
426 }
427 
428 
429 /**
430  * This method is based on crrc_aero.c in the initial version of CRRCSim.
431  * It did not contain a copyright or author note and was committed to CVS
432  * by Jan Kansky.
433  *
434  * Calculate forces and moments for the current time step.
435  */
aero(TSimInputs * inputs,CRRCMath::Matrix33 m_V_atmo_rwy,CRRCMath::Vector3 v_R_omega_gust_body,CRRCMath::Vector3 & v_F,CRRCMath::Vector3 & v_M)436 void CRRC_AirplaneSim_Larcsim::aero(TSimInputs* inputs,
437                                     CRRCMath::Matrix33 m_V_atmo_rwy,
438                                     CRRCMath::Vector3 v_R_omega_gust_body,
439                                     CRRCMath::Vector3& v_F, CRRCMath::Vector3& v_M)
440 {
441   SCALAR elevator, aileron, rudder, flap, spoiler, gear_ext;
442   SCALAR flap_eff_factor;
443   SCALAR Phat, Qhat, Rhat; // dimensionless rotation rates
444   SCALAR CL_left, CL_cent, CL_right; // CL values across the span
445   SCALAR dCL_left,dCL_cent,dCL_right; // stall-induced CL changes
446   SCALAR dCD_left,dCD_cent,dCD_right; // stall-induced CD changes
447   SCALAR dCl,dCn,dCl_stall,dCn_stall; // stall-induced roll,yaw moments
448   SCALAR dCm_stall; // Stall induced pitching moment.
449   SCALAR CL_flap_offset, CL_offset, CL_wing, CD_all, CD_scaled, Cl_w;
450   SCALAR Cl_r_mod,Cn_p_mod;
451   SCALAR CL,CD,Cl,Cm,Cn;
452   SCALAR QS;
453 
454   CRRCMath::Vector3 C_xyz;
455 
456   CRRCMath::Vector3 v_R_omega_atmo;
457 
458   CRRCMath::Matrix33 m_V_body;
459 
460   SCALAR Cos_alpha, Sin_alpha, Cos_beta;
461 
462   Cos_alpha = cos(Alpha);
463   Sin_alpha = sin(Alpha);
464   Cos_beta  = cos(Beta);
465 
466   elevator = inputs->elevator;
467   aileron  = inputs->aileron;
468   rudder   = inputs->rudder;
469   flap     = inputs->flap;
470   spoiler  = inputs->spoiler;
471 
472   /* To simulate the non linearity of flap effectiveness for large flap
473    * deflections (e.g. landing flap) a flap effectiveness ratio can be
474    * defined, giving the ratio R of flap derivatives at full deflection
475    * (i.e. flap = 0.5) vs at zero deflection.
476    * R=1 for standard constant effectiveness (linear behaviour).
477    * The resulting flap effectiveness factor for the current deflection
478    * is then computed as k = 1 - (1 - R)*flap
479    */
480   flap_eff_factor = 1. - (1. - flap_eff_ratio)*flap;
481 
482   /* The retract input is 0.0 for fully extended gear, 1.0 for fully
483    * retracted gear.
484    */
485   gear_ext = 1.0 - inputs->retract;
486 
487   /* transform wind velocity gradient from local to body frame */
488   m_V_body = LocalToBody * (m_V_atmo_rwy * LocalToBody.trans());
489 
490   /* set rotation rates of airmass motion includin wind turbulence effect */
491   v_R_omega_atmo.r[0] =  m_V_body.v[2][1] + v_R_omega_gust_body.r[0];
492   v_R_omega_atmo.r[1] = -m_V_body.v[2][0] + v_R_omega_gust_body.r[1];
493   v_R_omega_atmo.r[2] =  m_V_body.v[1][0] + v_R_omega_gust_body.r[2];
494 
495   if (V_rel_wind != 0)
496   {
497     /* set net effective dimensionless rotation rates */
498     // jww: the comment above is misleading. The unit of those values must be rad!
499     Phat = (v_R_omega_body.r[0] - v_R_omega_atmo.r[0]) * B_ref / (2.0*V_rel_wind);
500     Qhat = (v_R_omega_body.r[1] - v_R_omega_atmo.r[1]) * C_ref / (2.0*V_rel_wind);
501     Rhat = (v_R_omega_body.r[2] - v_R_omega_atmo.r[2]) * B_ref / (2.0*V_rel_wind);
502   }
503   else
504   {
505     Phat=0;
506     Qhat=0;
507     Rhat=0;
508   }
509 
510   /* compute local CL at three spanwise locations */
511   CL_flap_offset = flap_lift*flap_eff_factor*flap;
512   CL_offset = CL_flap_offset + spoiler_lift*spoiler + retract_lift*gear_ext;
513   CL_left   = CL_0 + CL_a*(Alpha - Alpha_0 - Phat*eta_loc) + CL_offset;
514   CL_cent   = CL_0 + CL_a*(Alpha - Alpha_0               ) + CL_offset;
515   CL_right  = CL_0 + CL_a*(Alpha - Alpha_0 + Phat*eta_loc) + CL_offset;
516 
517   /* set CL-limit changes */
518   dCL_left  = 0.;
519   dCL_cent  = 0.;
520   dCL_right = 0.;
521 
522   // careful treatment of both positive and negative stall.
523   // The CL_max & CL_min values specified in input must be those of a clean
524   // wing, i.e. for flap=spoiler=gear_ext=0.
525   // The effect of flap is to add an offset to the lift, (i.e. change the
526   // zero-lift angle of attack) but the CL_max & CL_min are not offset by
527   // a similar amount: a crude approximation is that they are only offset
528   // by half of the amount.
529   stalling=0;
530   {
531     SCALAR CL_max_flap = CL_max + 0.5*CL_flap_offset;
532     SCALAR CL_min_flap = CL_min + 0.5*CL_flap_offset;
533 
534     if (CL_left  > CL_max_flap)
535     {
536       dCL_left  = CL_max_flap - CL_left - CL_drop;
537       stalling=1;
538     }
539     if (CL_cent  > CL_max_flap)
540     {
541       dCL_cent  = CL_max_flap - CL_cent - CL_drop;
542       stalling=1;
543     }
544     if (CL_right > CL_max_flap)
545     {
546       dCL_right = CL_max_flap - CL_right - CL_drop;
547       stalling=1;
548     }
549     if (CL_left  < CL_min_flap)
550     {
551       dCL_left  = CL_min_flap - CL_left - CL_drop*CL_min/CL_max;
552       stalling=1;
553     }
554     if (CL_cent  < CL_min_flap)
555     {
556       dCL_cent  = CL_min_flap - CL_cent - CL_drop*CL_min/CL_max;
557       stalling=1;
558     }
559     if (CL_right < CL_min_flap)
560     {
561       dCL_right  = CL_min_flap - CL_right - CL_drop*CL_min/CL_max;
562       stalling=1;
563     }
564   }
565 
566   /* set average wing CL */
567   CL_wing = 0.5*(CL_cent + dCL_cent)
568     + 0.25*(CL_left + dCL_left) + 0.25*(CL_right + dCL_right);
569 
570   /* total CL, with pitch rate and control contributions */
571   CL = (CL_wing + CL_q*Qhat
572     + CL_de*elevator
573     )*Cos_alpha;
574 
575   /* save flight CL for public access */
576   flight_CL = CL;
577 
578   /* correct profile CD for CL dependence and controls dependence */
579   // Note quadratic effect of flap deflection
580   CD_all = CD_prof
581     + CD_CLsq*(CL_wing-CL_CD0)*(CL_wing-CL_CD0)
582     + CD_AIsq*aileron*aileron
583     + CD_ELsq*elevator*elevator
584     + flap_drag*flap_eff_factor*flap*flap
585     + spoiler_drag*spoiler
586     + retract_drag*gear_ext;
587 
588   /* scale profile CD with Reynolds number via simple power law */
589   if (V_rel_wind > 0.1)
590   {
591     CD_scaled = CD_all*pow(((double)V_rel_wind/(double)U_ref),Uexp_CD);
592   }
593   else
594   {
595     CD_scaled = CD_all;
596   }
597 
598   /* Scale lateral cross-coupling derivatives with wing CL */
599   Cl_r_mod = Cl_r*CL_wing/CL_0;
600   Cn_p_mod = Cn_p*CL_wing/CL_0;
601 
602   /* stall-caused CD */
603   // CD_stall is max delta CD due to a (deep) stall, assumed to be one
604   // which causes a drop of 0.4 (e.g.) from linear CL curve.
605   // The harder the stall (that is CL_drop) the quicker this drag level
606   // is approached.
607   dCD_left  = CD_stall*(1.0 - exp(-4.0*fabs(dCL_left )/0.4));
608   dCD_cent  = CD_stall*(1.0 - exp(-4.0*fabs(dCL_cent )/0.4));
609   dCD_right = CD_stall*(1.0 - exp(-4.0*fabs(dCL_right)/0.4));
610 
611   /* asymetric stall will cause roll and yaw moments */
612   dCl = -0.25*(dCL_right - dCL_left)*eta_loc;
613   dCn =  0.25*(dCD_right - dCD_left)*eta_loc;
614 
615   /* stall-caused moments in body axes */
616   dCl_stall = dCl*Cos_alpha - dCn*Sin_alpha;
617   dCn_stall = dCl*Sin_alpha + dCn*Cos_alpha;
618 
619   /* stall-caused pitching moment in body axes */
620   dCm_stall = (0.25*dCL_left + 0.5*dCL_cent + 0.25*dCL_right)*CG_arm;
621 
622   /* roll moment due to wing only */
623   Cl_w = Cl_b*Beta  + Cl_p*Phat + Cl_r_mod*Rhat
624     + dCl_stall  + Cl_da*aileron;
625 
626   /* total CD, with induced and stall contributions */
627   // Roll moment is due to asymmetric lift contributions
628   // adding to induced drag. Approximately |Cl_w| = 0.5*|dCL|
629   CD = CD_scaled
630     + pow(fabs(CL) + fabs(2.0*Cl_w),2)*S_ref/(B_ref*B_ref*M_PI*span_eff)
631       + 0.25*dCD_left + 0.5*dCD_cent + 0.25*dCD_right;
632 
633   /* total forces in body axes */
634   C_xyz.r[0] = -CD*Cos_alpha + CL*Sin_alpha*Cos_beta*Cos_beta;
635   C_xyz.r[2] = -CD*Sin_alpha - CL*Cos_alpha*Cos_beta*Cos_beta;
636   C_xyz.r[1] = CY_b*Beta  + CY_p*Phat + CY_r*Rhat + CY_dr*rudder;
637 
638   /* total moments in body axes */
639   // Flap contribution to Cm zeroed if stalling, since major
640   // effect is coming from lift variation rather than airfoil cm
641   Cl = Cl_b*Beta + Cl_p*Phat + Cl_r_mod*Rhat
642     + Cl_dr*rudder + Cl_da*aileron
643     + dCl_stall;
644   Cn = Cn_b*Beta + Cn_p_mod*Phat + Cn_r*Rhat
645     + Cn_dr*rudder + Cn_da*aileron
646     + dCn_stall;
647   Cm = Cm_0 + Cm_a*(Alpha-Alpha_0) + Cm_q*Qhat
648     + Cm_de*elevator
649     + dCm_stall
650     + flap_moment*flap_eff_factor*flap*(1.0 - stalling)
651     + spoiler_moment*spoiler;
652 
653   /* set dimensional forces and moments */
654   QS = 0.5*Density*V_rel_wind*V_rel_wind * S_ref;
655 
656   v_F = C_xyz * QS;
657 
658   v_M.r[0] = Cl * QS * B_ref;
659   v_M.r[1] = Cm * QS * C_ref;
660   v_M.r[2] = Cn * QS * B_ref;
661 
662 #if (EOM_TEST == 1)
663   {
664     double ele = 0.8*(-inputs->elevator);
665     double ail = 0.8*(inputs->aileron);
666     double rud = 0.8*(0.5*inputs->aileron);
667 
668     v_F = CRRCMath::Vector3();
669     v_M = CRRCMath::Vector3(ail, ele, rud);
670     stalling = 0;
671   }
672 #endif
673 
674 }
675 
676 
ls_step_init()677 void CRRC_AirplaneSim_Larcsim::ls_step_init()
678 {
679   CRRCMath::Vector3 v_F_aero, v_F_engine, v_F_gear; // Force x/y/z
680   CRRCMath::Vector3 v_M_aero, v_M_engine, v_M_gear; // l/m/n <-> roll/pitch/yaw
681 
682   TSimInputs ZeroInput = TSimInputs();
683 
684   EOM01::ls_step_init();
685 
686   /*    Initialize vehicle model                        */
687   ls_aux(CRRCMath::Vector3(), CRRCMath::Vector3());
688 
689   aero(&ZeroInput, CRRCMath::Matrix33(), CRRCMath::Vector3(), v_F_aero, v_M_aero);
690   engine(0, &ZeroInput, v_F_engine, v_M_engine);
691   gear(&ZeroInput, v_F_gear, v_M_gear);
692 
693   /*    Calculate initial accelerations */
694   ls_accel(v_F_aero + v_F_engine + v_F_gear, v_M_aero + v_M_engine + v_M_gear);
695 
696   /* Initialize auxiliary variables */
697   ls_aux(CRRCMath::Vector3(), CRRCMath::Vector3());
698 }
699 
700 
getPropFreq()701 double CRRC_AirplaneSim_Larcsim::getPropFreq()
702 {
703   return(power->getPropFreq());
704 }
705 
706 
ReloadParams(SimpleXMLTransfer * xml,SimpleXMLTransfer * cfg)707 int CRRC_AirplaneSim_Larcsim::ReloadParams(SimpleXMLTransfer* xml,
708                                            SimpleXMLTransfer* cfg)
709 {
710   LoadFromXML(xml, cfg->getInt("airplane.verbosity", 5));
711 
712   return(1);
713 }
714