JSBSim Flight Dynamics Model 1.3.0 (09 Apr 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGAerodynamics.cpp
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGAerodynamics.cpp
4 Author: Jon S. Berndt
5 Date started: 09/13/00
6 Purpose: Encapsulates the aerodynamic forces
7
8 ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free
12 Software Foundation; either version 2 of the License, or (at your option) any
13 later version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 details.
19
20 You should have received a copy of the GNU Lesser General Public License along
21 with this program; if not, write to the Free Software Foundation, Inc., 59
22 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Further information about the GNU Lesser General Public License can also be
25 found on the world wide web at http://www.gnu.org.
26
27FUNCTIONAL DESCRIPTION
28--------------------------------------------------------------------------------
29
30HISTORY
31--------------------------------------------------------------------------------
3209/13/00 JSB Created
3304/22/01 JSB Moved code into here from FGAircraft
34
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36INCLUDES
37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
38
39#include "FGAerodynamics.h"
40#include "FGFDMExec.h"
41#include "input_output/FGXMLElement.h"
42#include "input_output/FGLog.h"
43
44using namespace std;
45
46namespace JSBSim {
47
48/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
49CLASS IMPLEMENTATION
50%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
51
52
54{
55 Name = "FGAerodynamics";
56
57 AxisIdx["DRAG"] = 0;
58 AxisIdx["SIDE"] = 1;
59 AxisIdx["LIFT"] = 2;
60 AxisIdx["ROLL"] = 3;
61 AxisIdx["PITCH"] = 4;
62 AxisIdx["YAW"] = 5;
63
64 AxisIdx["AXIAL"] = 0;
65 AxisIdx["NORMAL"] = 2;
66
67 AxisIdx["X"] = 0;
68 AxisIdx["Y"] = 1;
69 AxisIdx["Z"] = 2;
70
71 forceAxisType = atNone;
72 momentAxisType = atNone;
73
74 AeroFunctions = new AeroFunctionArray[6];
75 AeroFunctionsAtCG = new AeroFunctionArray[6];
76
77 impending_stall = stall_hyst = 0.0;
78 alphaclmin = alphaclmax = 0.0;
79 alphaclmin0 = alphaclmax0 = 0.0;
80 alphahystmin = alphahystmax = 0.0;
81 clsq = lod = 0.0;
82 alphaw = 0.0;
83 bi2vel = ci2vel = 0.0;
84 AeroRPShift = 0;
85 vDeltaRP.InitMatrix();
86
87 bind();
88
89 Debug(0);
90}
91
92//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93
95{
96 unsigned int i,j;
97
98 for (i=0; i<6; i++)
99 for (j=0; j<AeroFunctions[i].size(); j++)
100 delete AeroFunctions[i][j];
101 for (i=0; i<6; i++)
102 for (j=0; j<AeroFunctionsAtCG[i].size(); j++)
103 delete AeroFunctionsAtCG[i][j];
104
105 delete[] AeroFunctions;
106 delete[] AeroFunctionsAtCG;
107
108 delete AeroRPShift;
109
110 Debug(1);
111}
112
113//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114
115bool FGAerodynamics::InitModel(void)
116{
117 if (!FGModel::InitModel()) return false;
118
119 impending_stall = stall_hyst = 0.0;
120 alphaclmin = alphaclmin0;
121 alphaclmax = alphaclmax0;
122 alphahystmin = alphahystmax = 0.0;
123 clsq = lod = 0.0;
124 alphaw = 0.0;
125 bi2vel = ci2vel = 0.0;
126 AeroRPShift = 0;
127 vDeltaRP.InitMatrix();
128 vForces.InitMatrix();
129 vMoments.InitMatrix();
130 return true;
131}
132//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
133
134bool FGAerodynamics::Run(bool Holding)
135{
136
137 if (FGModel::Run(Holding)) return true;
138 if (Holding) return false; // if paused don't execute
139
140 unsigned int axis_ctr;
141 const double twovel=2*in.Vt;
142
143 // The lift coefficient squared (property aero/cl-squared) is computed before
144 // the aero functions are called to make sure that they use the same value for
145 // qbar.
146 if ( in.Qbar > 1.0) {
147 // Skip the computation if qbar is close to zero to avoid huge values for
148 // aero/cl-squared when a non-null lift coincides with a very small aero
149 // velocity (i.e. when qbar is close to zero).
150 clsq = vFw(eLift) / (in.Wingarea*in.Qbar);
151 clsq *= clsq;
152 }
153
154 RunPreFunctions();
155
156 // calculate some oft-used quantities for speed
157
158 if (twovel != 0) {
159 bi2vel = in.Wingspan / twovel;
160 ci2vel = in.Wingchord / twovel;
161 }
162 alphaw = in.Alpha + in.Wingincidence;
163 qbar_area = in.Wingarea * in.Qbar;
164
165 if (alphaclmax != 0) {
166 if (in.Alpha > 0.85*alphaclmax) {
167 impending_stall = 10*(in.Alpha/alphaclmax - 0.85);
168 } else {
169 impending_stall = 0;
170 }
171 }
172
173 if (alphahystmax != 0.0 && alphahystmin != 0.0) {
174 if (in.Alpha > alphahystmax) {
175 stall_hyst = 1;
176 } else if (in.Alpha < alphahystmin) {
177 stall_hyst = 0;
178 }
179 }
180
181 vFw.InitMatrix();
182 vFnative.InitMatrix();
183 vFnativeAtCG.InitMatrix();
184
185 BuildStabilityTransformMatrices();
186
187 for (axis_ctr = 0; axis_ctr < 3; ++axis_ctr) {
188 AeroFunctionArray::iterator f;
189
190 AeroFunctionArray* array = &AeroFunctions[axis_ctr];
191 for (f=array->begin(); f != array->end(); ++f) {
192 // Tell the Functions to cache values, so when the function values are
193 // being requested for output, the functions do not get calculated again
194 // in a context that might have changed, but instead use the values that
195 // have already been calculated for this frame.
196 (*f)->cacheValue(true);
197 vFnative(axis_ctr+1) += (*f)->GetValue();
198 }
199
200 array = &AeroFunctionsAtCG[axis_ctr];
201 for (f=array->begin(); f != array->end(); ++f) {
202 (*f)->cacheValue(true); // Same as above
203 vFnativeAtCG(axis_ctr+1) += (*f)->GetValue();
204 }
205 }
206
207 switch (forceAxisType) {
208 case atBodyXYZ: // Forces already in body axes; no manipulation needed
209 vForces = vFnative;
210 vForcesAtCG = vFnativeAtCG;
211 break;
212 case atWind: // Copy forces into wind axes
213 vFnative(eDrag)*=-1; vFnative(eLift)*=-1;
214 vForces = in.Tw2b*vFnative;
215
216 vFnativeAtCG(eDrag)*=-1; vFnativeAtCG(eLift)*=-1;
217 vForcesAtCG = in.Tw2b*vFnativeAtCG;
218 break;
219 case atBodyAxialNormal: // Convert native forces into Axial|Normal|Side system
220 vFnative(eX)*=-1; vFnative(eZ)*=-1;
221 vForces = vFnative;
222
223 vFnativeAtCG(eX)*=-1; vFnativeAtCG(eZ)*=-1;
224 vForcesAtCG = vFnativeAtCG;
225 break;
226 case atStability: // Convert from stability axes to both body and wind axes
227 vFnative(eDrag) *= -1; vFnative(eLift) *= -1;
228 vForces = Ts2b*vFnative;
229
230 vFnativeAtCG(eDrag) *= -1; vFnativeAtCG(eLift) *= -1;
231 vForcesAtCG = Ts2b*vFnativeAtCG;
232 break;
233 default:
234 {
235 LogException err;
236 err << "\n A proper axis type has NOT been selected. Check "
237 << "your aerodynamics definition.\n";
238 throw err;
239 }
240 }
241 // Calculate aerodynamic reference point shift, if any. The shift takes place
242 // in the structual axis. That is, if the shift is positive, it is towards the
243 // back (tail) of the vehicle. The AeroRPShift function should be
244 // non-dimensionalized by the wing chord. The calculated vDeltaRP will be in
245 // feet.
246 if (AeroRPShift) vDeltaRP(eX) = AeroRPShift->GetValue()*in.Wingchord;
247
248 vDXYZcg(eX) = in.RPBody(eX) - vDeltaRP(eX); // vDeltaRP is given in the
249 vDXYZcg(eY) = in.RPBody(eY) + vDeltaRP(eY); // structural frame.
250 vDXYZcg(eZ) = in.RPBody(eZ) - vDeltaRP(eZ);
251
252 vMomentsMRC.InitMatrix();
253
254 for (axis_ctr = 0; axis_ctr < 3; axis_ctr++) {
255 AeroFunctionArray* array = &AeroFunctions[axis_ctr+3];
256 for (AeroFunctionArray::iterator f=array->begin(); f != array->end(); ++f) {
257 // Tell the Functions to cache values, so when the function values are
258 // being requested for output, the functions do not get calculated again
259 // in a context that might have changed, but instead use the values that
260 // have already been calculated for this frame.
261 (*f)->cacheValue(true);
262 vMomentsMRC(axis_ctr+1) += (*f)->GetValue();
263 }
264 }
265
266 // Transform moments to bodyXYZ if the moments are specified in stability or
267 // wind axes
268 vMomentsMRCBodyXYZ.InitMatrix();
269 switch (momentAxisType) {
270 case atBodyXYZ:
271 vMomentsMRCBodyXYZ = vMomentsMRC;
272 break;
273 case atStability:
274 vMomentsMRCBodyXYZ = Ts2b*vMomentsMRC;
275 break;
276 case atWind:
277 vMomentsMRCBodyXYZ = in.Tw2b*vMomentsMRC;
278 break;
279 default:
280 {
281 LogException err;
282 err << "\n A proper axis type has NOT been selected. Check "
283 << "your aerodynamics definition.\n";
284 throw err;
285 }
286 }
287
288 vMoments = vMomentsMRCBodyXYZ + vDXYZcg*vForces; // M = r X F
289
290 // Now add the "at CG" values to base forces - after the moments have been
291 // transferred.
292 vForces += vForcesAtCG;
293
294 // Note that we still need to convert to wind axes here, because it is used in
295 // the L/D calculation, and we still may want to look at Lift and Drag.
296 //
297 // JSB 4/27/12 - After use, convert wind axes to produce normal lift and drag
298 // values - not negative ones!
299 //
300 // As a clarification, JSBSim assumes that drag and lift values are defined in
301 // wind axes - BUT with a 180 rotation about the Y axis. That is, lift and
302 // drag will be positive up and aft, respectively, so that they are reported
303 // as positive numbers. However, the wind axes themselves assume that the X
304 // and Z forces are positive forward and down. Same applies to the stability
305 // axes.
306 vFw = in.Tb2w * vForces;
307 vFw(eDrag) *= -1; vFw(eLift) *= -1;
308
309 // Calculate Lift over Drag
310 if ( fabs(vFw(eDrag)) > 0.0)
311 lod = fabs( vFw(eLift) / vFw(eDrag));
312
313 RunPostFunctions();
314
315 return false;
316}
317
318//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
319
321{
322 FGColumnVector3 vFs = Tb2s*vForces;
323 // Need sign flips since drag is positive and lift is positive in stability axes
324 vFs(eDrag) *= -1; vFs(eLift) *= -1;
325
326 return vFs;
327}
328
329//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330
332{
333 string axis;
334 string scratch_unit="";
335 Element *temp_element, *axis_element, *function_element;
336
337 Name = "Aerodynamics Model: " + document->GetAttributeValue("name");
338
339 // Perform base class Pre-Load
340 if (!FGModel::Upload(document, true))
341 return false;
342
343 DetermineAxisSystem(document); // Determine if Lift/Side/Drag, etc. is used.
344
345 Debug(2);
346
347 if ((temp_element = document->FindElement("alphalimits"))) {
348 scratch_unit = temp_element->GetAttributeValue("unit");
349 if (scratch_unit.empty()) scratch_unit = "RAD";
350 alphaclmin0 = temp_element->FindElementValueAsNumberConvertFromTo("min", scratch_unit, "RAD");
351 alphaclmax0 = temp_element->FindElementValueAsNumberConvertFromTo("max", scratch_unit, "RAD");
352 alphaclmin = alphaclmin0;
353 alphaclmax = alphaclmax0;
354 }
355
356 if ((temp_element = document->FindElement("hysteresis_limits"))) {
357 scratch_unit = temp_element->GetAttributeValue("unit");
358 if (scratch_unit.empty()) scratch_unit = "RAD";
359 alphahystmin = temp_element->FindElementValueAsNumberConvertFromTo("min", scratch_unit, "RAD");
360 alphahystmax = temp_element->FindElementValueAsNumberConvertFromTo("max", scratch_unit, "RAD");
361 }
362
363 if ((temp_element = document->FindElement("aero_ref_pt_shift_x"))) {
364 function_element = temp_element->FindElement("function");
365 AeroRPShift = new FGFunction(FDMExec, function_element);
366 }
367
368 axis_element = document->FindElement("axis");
369 while (axis_element) {
370 AeroFunctionArray ca;
371 AeroFunctionArray ca_atCG;
372 axis = axis_element->GetAttributeValue("name");
373 function_element = axis_element->FindElement("function");
374 while (function_element) {
375 try {
376 if (function_element->HasAttribute("apply_at_cg") &&
377 function_element->GetAttributeValue("apply_at_cg") == "true")
378 ca_atCG.push_back(new FGFunction(FDMExec, function_element));
379 else
380 ca.push_back(new FGFunction(FDMExec, function_element));
381 } catch (BaseException& e) {
382 string current_func_name = function_element->GetAttributeValue("name");
383 FGXMLLogging log(axis_element, LogLevel::ERROR);
384 log << LogFormat::RED << "\nError loading aerodynamic function in "
385 << current_func_name << ":" << e.what() << " Aborting.\n" << LogFormat::RESET;
386 return false;
387 }
388 function_element = axis_element->FindNextElement("function");
389 }
390 AeroFunctions[AxisIdx[axis]] = ca;
391 AeroFunctionsAtCG[AxisIdx[axis]] = ca_atCG;
392 axis_element = document->FindNextElement("axis");
393 }
394
395 PostLoad(document, FDMExec); // Perform base class Post-Load
396
397 return true;
398}
399
400//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
401//
402// This private class function checks to verify consistency in the choice of
403// aerodynamic axes used in the config file. One set of LIFT|DRAG|SIDE, or
404// X|Y|Z, or AXIAL|NORMAL|SIDE must be chosen; mixed system axes are not allowed.
405// Note that if the "SIDE" axis specifier is entered first in a config file,
406// a warning message will be given IF the AXIAL|NORMAL specifiers are also given.
407// This is OK, and the warning is due to the SIDE specifier used for both
408// the Lift/Drag and Axial/Normal axis systems.
409// Alternatively the axis name 'X|Y|Z or ROLL|PITCH|YAW' can be specified in
410// conjunction with a frame 'BODY|STABILITY|WIND', for example:
411// <axis name="X" frame="STABILITY"/>
412//
413// In summary, possible combinations:
414//
415// FORCES
416// Body
417// <axis name="AXIAL|SIDE|NORMAL" />
418// <axis name="X|Y|Z" />
419// <axis name="X|Y|Z" frame="BODY" />
420//
421// Wind
422// <axis name="DRAG|SIDE|LIFT" / >
423// <axis name="X|Y|Z" frame="WIND" / >
424//
425// Stability
426// <axis name="X|Y|Z" frame="STABILITY" />
427//
428// MOMENTS
429// Body
430// <axis name="ROLL|PITCH|YAW" />
431// <axis name="ROLL|PITCH|YAW" frame="BODY" / >
432//
433// Wind
434// <axis name="ROLL|PITCH|YAW" frame="WIND" />
435//
436// Stability
437// <axis name="ROLL|PITCH|YAW" frame="STABILITY" />
438//
439
440void FGAerodynamics::DetermineAxisSystem(Element* document)
441{
442 Element* axis_element = document->FindElement("axis");
443 string axis;
444 while (axis_element) {
445 axis = axis_element->GetAttributeValue("name");
446 string frame = axis_element->GetAttributeValue("frame");
447 if (axis == "X" || axis == "Y" || axis == "Z") {
448 ProcessAxesNameAndFrame(forceAxisType, axis, frame, axis_element,
449 "(X Y Z)");
450 } else if (axis == "ROLL" || axis == "PITCH" || axis == "YAW") {
451 ProcessAxesNameAndFrame(momentAxisType, axis, frame, axis_element,
452 "(ROLL PITCH YAW)");
453 } else if (axis == "LIFT" || axis == "DRAG") {
454 if (forceAxisType == atNone) forceAxisType = atWind;
455 else if (forceAxisType != atWind) {
456 FGXMLLogging log(axis_element, LogLevel::WARN);
457 log << "\n Mixed aerodynamic axis systems have been used in the"
458 << " aircraft config file. (LIFT DRAG)\n";
459 }
460 } else if (axis == "SIDE") {
461 if (forceAxisType != atNone && forceAxisType != atWind && forceAxisType != atBodyAxialNormal) {
462 FGXMLLogging log(axis_element, LogLevel::WARN);
463 log << "\n Mixed aerodynamic axis systems have been used in the"
464 << " aircraft config file. (SIDE)\n";
465 }
466 } else if (axis == "AXIAL" || axis == "NORMAL") {
467 if (forceAxisType == atNone) forceAxisType = atBodyAxialNormal;
468 else if (forceAxisType != atBodyAxialNormal) {
469 FGXMLLogging log(axis_element, LogLevel::WARN);
470 log << "\n Mixed aerodynamic axis systems have been used in the"
471 << " aircraft config file. (NORMAL AXIAL)\n";
472 }
473 } else { // error
474 XMLLogException err(axis_element);
475 err << "\n An unknown axis type, " << axis << " has been specified"
476 << " in the aircraft configuration file.\n";
477 throw err;
478 }
479 axis_element = document->FindNextElement("axis");
480 }
481
482 if (forceAxisType == atNone) {
483 forceAxisType = atWind;
484 FGLogging log(LogLevel::INFO);
485 log << "\n The aerodynamic axis system has been set by default"
486 << " to the Lift/Side/Drag system.\n";
487 }
488 if (momentAxisType == atNone) {
489 momentAxisType = atBodyXYZ;
490 FGLogging log(LogLevel::INFO);
491 log << "\n The aerodynamic moment axis system has been set by default"
492 << " to the bodyXYZ system.\n";
493 }
494}
495
496//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
497
498void FGAerodynamics::ProcessAxesNameAndFrame(eAxisType& axisType, const string& name,
499 const string& frame, Element* el,
500 const string& validNames)
501{
502 if (frame == "BODY" || frame.empty()) {
503 if (axisType == atNone) axisType = atBodyXYZ;
504 else if (axisType != atBodyXYZ) {
505 FGXMLLogging log(el, LogLevel::WARN);
506 log << "\n Mixed aerodynamic axis systems have been used in the "
507 << " aircraft config file." << validNames << " - BODY\n";
508 }
509 }
510 else if (frame == "STABILITY") {
511 if (axisType == atNone) axisType = atStability;
512 else if (axisType != atStability) {
513 FGXMLLogging log(el, LogLevel::WARN);
514 log << "\n Mixed aerodynamic axis systems have been used in the "
515 << " aircraft config file." << validNames << " - STABILITY\n";
516 }
517 }
518 else if (frame == "WIND") {
519 if (axisType == atNone) axisType = atWind;
520 else if (axisType != atWind){
521 FGXMLLogging log(el, LogLevel::WARN);
522 log << "\n Mixed aerodynamic axis systems have been used in the "
523 << " aircraft config file." << validNames << " - WIND\n";
524 }
525 }
526 else {
527 XMLLogException err(el);
528 err << "\n Unknown axis frame type of - " << frame << "\n";
529 throw err;
530 }
531}
532
533//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534
535string FGAerodynamics::GetAeroFunctionStrings(const string& delimeter) const
536{
537 string AeroFunctionStrings = "";
538 bool firstime = true;
539 unsigned int axis, sd;
540
541 for (axis = 0; axis < 6; axis++) {
542 for (sd = 0; sd < AeroFunctions[axis].size(); sd++) {
543 if (firstime) {
544 firstime = false;
545 } else {
546 AeroFunctionStrings += delimeter;
547 }
548 AeroFunctionStrings += AeroFunctions[axis][sd]->GetName();
549 }
550 }
551
552 string FunctionStrings = FGModelFunctions::GetFunctionStrings(delimeter);
553
554 if (!FunctionStrings.empty()) {
555 if (!AeroFunctionStrings.empty()) {
556 AeroFunctionStrings += delimeter + FunctionStrings;
557 } else {
558 AeroFunctionStrings = FunctionStrings;
559 }
560 }
561
562 return AeroFunctionStrings;
563}
564
565//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566
567string FGAerodynamics::GetAeroFunctionValues(const string& delimeter) const
568{
569 ostringstream buf;
570
571 for (unsigned int axis = 0; axis < 6; axis++) {
572 for (unsigned int sd = 0; sd < AeroFunctions[axis].size(); sd++) {
573 if (buf.tellp() > 0) buf << delimeter;
574 buf << AeroFunctions[axis][sd]->GetValue();
575 }
576 }
577
578 string FunctionValues = FGModelFunctions::GetFunctionValues(delimeter);
579
580 if (!FunctionValues.empty()) {
581 if (!buf.str().empty()) {
582 buf << delimeter << FunctionValues;
583 } else {
584 buf << FunctionValues;
585 }
586 }
587
588 return buf.str();
589}
590
591//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
592
593void FGAerodynamics::bind(void)
594{
595 PropertyManager->Tie("forces/fbx-aero-lbs", this, eX, &FGAerodynamics::GetForces);
596 PropertyManager->Tie("forces/fby-aero-lbs", this, eY, &FGAerodynamics::GetForces);
597 PropertyManager->Tie("forces/fbz-aero-lbs", this, eZ, &FGAerodynamics::GetForces);
598 PropertyManager->Tie("moments/l-aero-lbsft", this, eL, &FGAerodynamics::GetMoments);
599 PropertyManager->Tie("moments/m-aero-lbsft", this, eM, &FGAerodynamics::GetMoments);
600 PropertyManager->Tie("moments/n-aero-lbsft", this, eN, &FGAerodynamics::GetMoments);
601 PropertyManager->Tie("forces/fwx-aero-lbs", this, eDrag, &FGAerodynamics::GetvFw);
602 PropertyManager->Tie("forces/fwy-aero-lbs", this, eSide, &FGAerodynamics::GetvFw);
603 PropertyManager->Tie("forces/fwz-aero-lbs", this, eLift, &FGAerodynamics::GetvFw);
604 PropertyManager->Tie("forces/fsx-aero-lbs", this, eX, &FGAerodynamics::GetForcesInStabilityAxes);
605 PropertyManager->Tie("forces/fsy-aero-lbs", this, eY, &FGAerodynamics::GetForcesInStabilityAxes);
606 PropertyManager->Tie("forces/fsz-aero-lbs", this, eZ, &FGAerodynamics::GetForcesInStabilityAxes);
607 PropertyManager->Tie("moments/roll-stab-aero-lbsft", this, eRoll, &FGAerodynamics::GetMomentsInStabilityAxes);
608 PropertyManager->Tie("moments/pitch-stab-aero-lbsft", this, ePitch, &FGAerodynamics::GetMomentsInStabilityAxes);
609 PropertyManager->Tie("moments/yaw-stab-aero-lbsft", this, eYaw, &FGAerodynamics::GetMomentsInStabilityAxes);
610 PropertyManager->Tie("moments/roll-wind-aero-lbsft", this, eRoll, &FGAerodynamics::GetMomentsInWindAxes);
611 PropertyManager->Tie("moments/pitch-wind-aero-lbsft", this, ePitch, &FGAerodynamics::GetMomentsInWindAxes);
612 PropertyManager->Tie("moments/yaw-wind-aero-lbsft", this, eYaw, &FGAerodynamics::GetMomentsInWindAxes);
613 PropertyManager->Tie("forces/lod-norm", this, &FGAerodynamics::GetLoD);
614 PropertyManager->Tie("aero/cl-squared", this, &FGAerodynamics::GetClSquared);
615 PropertyManager->Tie("aero/qbar-area", &qbar_area);
616 PropertyManager->Tie("aero/alpha-max-rad", this, &FGAerodynamics::GetAlphaCLMax, &FGAerodynamics::SetAlphaCLMax);
617 PropertyManager->Tie("aero/alpha-min-rad", this, &FGAerodynamics::GetAlphaCLMin, &FGAerodynamics::SetAlphaCLMin);
618 PropertyManager->Tie("aero/bi2vel", this, &FGAerodynamics::GetBI2Vel);
619 PropertyManager->Tie("aero/ci2vel", this, &FGAerodynamics::GetCI2Vel);
620 PropertyManager->Tie("aero/alpha-wing-rad", this, &FGAerodynamics::GetAlphaW);
621 PropertyManager->Tie("systems/stall-warn-norm", this, &FGAerodynamics::GetStallWarn);
622 PropertyManager->Tie("aero/stall-hyst-norm", this, &FGAerodynamics::GetHysteresisParm);
623}
624
625//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626//
627// Build transformation matrices for transforming from stability axes to
628// body axes and to wind axes. Where "a" is alpha and "B" is beta:
629//
630// The transform from body to stability axes is:
631//
632// cos(a) 0 sin(a)
633// 0 1 0
634// -sin(a) 0 cos(a)
635//
636// The transform from stability to body axes is:
637//
638// cos(a) 0 -sin(a)
639// 0 1 0
640// sin(a) 0 cos(a)
641//
642//
643
644void FGAerodynamics::BuildStabilityTransformMatrices(void)
645{
646 double ca = cos(in.Alpha);
647 double sa = sin(in.Alpha);
648
649 // Stability-to-body
650 Ts2b(1, 1) = ca;
651 Ts2b(1, 2) = 0.0;
652 Ts2b(1, 3) = -sa;
653 Ts2b(2, 1) = 0.0;
654 Ts2b(2, 2) = 1.0;
655 Ts2b(2, 3) = 0.0;
656 Ts2b(3, 1) = sa;
657 Ts2b(3, 2) = 0.0;
658 Ts2b(3, 3) = ca;
659
660 Tb2s = Ts2b.Transposed();
661}
662
663//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
664// The bitmasked value choices are as follows:
665// unset: In this case (the default) JSBSim would only print
666// out the normally expected messages, essentially echoing
667// the config files as they are read. If the environment
668// variable is not set, debug_lvl is set to 1 internally
669// 0: This requests JSBSim not to output any messages
670// whatsoever.
671// 1: This value explicity requests the normal JSBSim
672// startup messages
673// 2: This value asks for a message to be printed out when
674// a class is instantiated
675// 4: When this value is set, a message is displayed when a
676// FGModel object executes its Run() method
677// 8: When this value is set, various runtime state variables
678// are printed out periodically
679// 16: When set various parameters are sanity checked and
680// a message is printed out when they go out of bounds
681
682void FGAerodynamics::Debug(int from)
683{
684 if (debug_lvl <= 0) return;
685
686 if (debug_lvl & 1) { // Standard console startup message output
687 if (from == 2) { // Loader
688 FGLogging log(LogLevel::DEBUG);
689 switch (forceAxisType) {
690 case (atWind):
691 log << "\n Aerodynamics (Lift|Side|Drag axes):\n\n";
692 break;
693 case (atBodyAxialNormal):
694 log << "\n Aerodynamics (Axial|Side|Normal axes):\n\n";
695 break;
696 case (atBodyXYZ):
697 log << "\n Aerodynamics (Body X|Y|Z axes):\n\n";
698 break;
699 case (atStability):
700 log << "\n Aerodynamics (Stability X|Y|Z axes):\n\n";
701 break;
702 case (atNone):
703 log << "\n Aerodynamics (undefined axes):\n\n";
704 break;
705 }
706 }
707 }
708 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
709 FGLogging log(LogLevel::DEBUG);
710 if (from == 0) log << "Instantiated: FGAerodynamics\n";
711 if (from == 1) log << "Destroyed: FGAerodynamics\n";
712 }
713 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
714 }
715 if (debug_lvl & 8 ) { // Runtime state variables
716 }
717 if (debug_lvl & 16) { // Sanity checking
718 }
719 if (debug_lvl & 64) {
720 if (from == 0) { // Constructor
721 }
722 }
723}
724
725} // namespace JSBSim
Element * FindElement(const std::string &el="")
Searches for a specified element.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
double FindElementValueAsNumberConvertFromTo(const std::string &el, const std::string &supplied_units, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
std::string GetAeroFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of aero functions.
const FGColumnVector3 & GetMoments(void) const
Gets the total aerodynamic moment vector about the CG.
bool Load(Element *element) override
Loads the Aerodynamics model.
FGColumnVector3 GetForcesInStabilityAxes(void) const
Retrieves the aerodynamic forces in the stability axes.
FGColumnVector3 GetMomentsInWindAxes(void) const
Gets the total aerodynamic moment vector about the CG in the wind axes.
~FGAerodynamics() override
Destructor.
FGColumnVector3 GetMomentsInStabilityAxes(void) const
Gets the total aerodynamic moment vector about the CG in the stability axes.
double GetLoD(void) const
Retrieves the lift over drag ratio.
FGAerodynamics(FGFDMExec *Executive)
Constructor.
const FGColumnVector3 & GetvFw(void) const
Retrieves the aerodynamic forces in the wind axes.
bool Run(bool Holding) override
Runs the Aerodynamics model; called by the Executive Can pass in a value indicating if the executive ...
std::string GetAeroFunctionValues(const std::string &delimeter) const
Gets the aero function values.
double GetClSquared(void) const
Retrieves the square of the lift coefficient.
const FGColumnVector3 & GetForces(void) const
Gets the total aerodynamic force vector.
This class implements a 3 element column vector.
Encapsulates the JSBSim simulation executive.
Definition FGFDMExec.h:185
Represents a mathematical function.
Definition FGFunction.h:765
double GetValue(void) const override
Retrieves the value of the function object.
FGMatrix33 Transposed(void) const
Transposed matrix.
Definition FGMatrix33.h:221
std::string GetFunctionStrings(const std::string &delimeter) const
Gets the strings for the current set of functions.
std::string GetFunctionValues(const std::string &delimeter) const
Gets the function values.
Base class for all scheduled JSBSim models.
Definition FGModel.h:70
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
Definition FGModel.cpp:90
bool Upload(Element *el, bool preLoad)
Uploads this model in memory.
Definition FGModel.cpp:111
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71