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