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