FlightGear next
FGFunction.cpp
Go to the documentation of this file.
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3Module: FGFunction.cpp
4Author: Jon Berndt
5Date started: 8/25/2004
6Purpose: Stores various parameter types for functions
7
8 ------------- Copyright (C) 2004 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
27%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28INCLUDES
29%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
30
31#include <iomanip>
32#include <random>
33#include <chrono>
34#include <memory>
35
36#include "simgear/misc/strutils.hxx"
37#include "FGFDMExec.h"
38#include "FGFunction.h"
39#include "FGTable.h"
40#include "FGRealValue.h"
43
44
45using namespace std;
46
47namespace JSBSim {
48
49/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50CLASS IMPLEMENTATION
51%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52
53const double invlog2val = 1.0/log10(2.0);
54constexpr unsigned int MaxArgs = 9999;
55
56//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57
58class WrongNumberOfArguments : public runtime_error
59{
60public:
61 WrongNumberOfArguments(const string &msg, const vector<FGParameter_ptr> &p,
62 Element* el)
63 : runtime_error(msg), Parameters(p), element(el) {}
64 size_t NumberOfArguments(void) const { return Parameters.size(); }
65 FGParameter* FirstParameter(void) const { return *(Parameters.cbegin()); }
66 const Element* GetElement(void) const { return element; }
67
68private:
69 const vector<FGParameter_ptr> Parameters;
70 const Element* element;
71};
72
73//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74
75template<typename func_t, unsigned int Nmin>
76class aFunc: public FGFunction
77{
78public:
79 aFunc(const func_t& _f, FGFDMExec* fdmex, Element* el,
80 const string& prefix, FGPropertyValue* v, unsigned int Nmax=Nmin,
82 : FGFunction(fdmex->GetPropertyManager()), f(_f)
83 {
84 Load(el, v, fdmex, prefix);
85 CheckMinArguments(el, Nmin);
86 CheckMaxArguments(el, Nmax);
87 CheckOddOrEvenArguments(el, odd_even);
88 }
89
90 double GetValue(void) const override {
91 return cached ? cachedValue : f(Parameters);
92 }
93
94protected:
95 void bind(Element* el, const string& Prefix) override {
96 string nName = CreateOutputNode(el, Prefix);
97 if (!nName.empty())
99 }
100
101private:
102 const func_t f;
103};
104
105//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106// Template specialization for functions without parameters.
107
108template<typename func_t>
109class aFunc<func_t, 0>: public FGFunction
110{
111public:
112 aFunc(const func_t& _f, FGPropertyManager* pm, Element* el,
113 const string& Prefix)
114 : FGFunction(pm), f(_f)
115 {
116 if (el->GetNumElements() != 0) {
117 ostringstream buffer;
118 buffer << el->ReadFrom() << fgred << highint
119 << "<" << el->GetName() << "> should have no arguments." << reset
120 << endl;
121 throw WrongNumberOfArguments(buffer.str(), Parameters, el);
122 }
123
124 bind(el, Prefix);
125 }
126
127 double GetValue(void) const override {
128 double result = cached ? cachedValue : f();
129 if (pNode) pNode->setDoubleValue(result);
130 return result;
131 }
132
133 // Functions without parameters are assumed to be non-const
134 bool IsConstant(void) const override {
135 return false;
136 }
137
138protected:
139 // The method GetValue() is not bound for functions without parameters because
140 // we do not want the property to return a different value each time it is
141 // read.
142 void bind(Element* el, const string& Prefix) override {
143 CreateOutputNode(el, Prefix);
144 // Initialize the node to a sensible value.
145 if (pNode) pNode->setDoubleValue(f());
146 }
147
148private:
149 const func_t f;
150};
151
152//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153
154bool GetBinary(double val, const string &ctxMsg)
155{
156 val = fabs(val);
157 if (val < 1E-9) return false;
158 else if (val-1 < 1E-9) return true;
159 else {
160 cerr << ctxMsg << FGJSBBase::fgred << FGJSBBase::highint
161 << "Malformed conditional check in function definition."
162 << FGJSBBase::reset << endl;
163 throw("Fatal Error.");
164 }
165}
166
167//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168// Hides the machinery to create a class for functions from <math.h> such as
169// sin, cos, exp, etc.
170
171FGFunction* make_MathFn(double(*math_fn)(double), FGFDMExec* fdmex, Element* el,
172 const string& prefix, FGPropertyValue* v)
173{
174 auto f = [math_fn](const std::vector<FGParameter_ptr> &p)->double {
175 return math_fn(p[0]->GetValue());
176 };
177 return new aFunc<decltype(f), 1>(f, fdmex, el, prefix, v);
178}
179
180//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181// Manage the functions with a variable number of arguments.
182// It handles the special case where a single argument is provided to the
183// function: in that case the function is ignored and replaced by its argument.
184
185template<typename func_t>
186FGParameter_ptr VarArgsFn(const func_t& _f, FGFDMExec* fdmex, Element* el,
187 const string& prefix, FGPropertyValue* v)
188{
189 try {
190 return new aFunc<func_t, 2>(_f, fdmex, el, prefix, v, MaxArgs);
191 }
192 catch(WrongNumberOfArguments& e) {
193 if ((e.GetElement() == el) && (e.NumberOfArguments() == 1)) {
194 cerr << el->ReadFrom() << FGJSBBase::fgred
195 << "<" << el->GetName()
196 << "> only has one argument which makes it a no-op." << endl
197 << "Its argument will be evaluated but <" << el->GetName()
198 << "> will not be applied to the result." << FGJSBBase::reset << endl;
199 return e.FirstParameter();
200 }
201 else
202 throw e.what();
203 }
204}
205
206//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
207
208FGFunction::FGFunction(FGFDMExec* fdmex, Element* el, const string& prefix,
209 FGPropertyValue* var)
210 : FGFunction(fdmex->GetPropertyManager())
211{
212 Load(el, var, fdmex, prefix);
213 CheckMinArguments(el, 1);
214 CheckMaxArguments(el, 1);
215
216 string sCopyTo = el->GetAttributeValue("copyto");
217
218 if (!sCopyTo.empty()) {
219 if (sCopyTo.find("#") != string::npos) {
220 if (is_number(prefix))
221 sCopyTo = replace(sCopyTo,"#",prefix);
222 else {
223 cerr << el->ReadFrom() << fgred
224 << "Illegal use of the special character '#'" << reset << endl
225 << "The 'copyto' argument in function " << Name << " is ignored."
226 << endl;
227 return;
228 }
229 }
230
231 pCopyTo = PropertyManager->GetNode(sCopyTo);
232 if (!pCopyTo)
233 cerr << el->ReadFrom() << fgred
234 << "Property \"" << sCopyTo
235 << "\" must be previously defined in function " << Name << reset
236 << "The 'copyto' argument is ignored." << endl;
237 }
238}
239
240//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241
242void FGFunction::CheckMinArguments(Element* el, unsigned int _min)
243{
244 if (Parameters.size() < _min) {
245 ostringstream buffer;
246 buffer << el->ReadFrom() << fgred << highint
247 << "<" << el->GetName() << "> should have at least " << _min
248 << " argument(s)." << reset << endl;
249 throw WrongNumberOfArguments(buffer.str(), Parameters, el);
250 }
251}
252
253//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254
255void FGFunction::CheckMaxArguments(Element* el, unsigned int _max)
256{
257 if (Parameters.size() > _max) {
258 ostringstream buffer;
259 buffer << el->ReadFrom() << fgred << highint
260 << "<" << el->GetName() << "> should have no more than " << _max
261 << " argument(s)." << reset << endl;
262 throw WrongNumberOfArguments(buffer.str(), Parameters, el);
263 }
264}
265
266//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267
269{
270
271 switch(odd_even) {
272 case OddEven::Even:
273 if (Parameters.size() % 2 == 1) {
274 cerr << el->ReadFrom() << fgred << highint
275 << "<" << el->GetName() << "> must have an even number of arguments."
276 << reset << endl;
277 throw("Fatal Error");
278 }
279 break;
280 case OddEven::Odd:
281 if (Parameters.size() % 2 == 0) {
282 cerr << el->ReadFrom() << fgred << highint
283 << "<" << el->GetName() << "> must have an odd number of arguments."
284 << reset << endl;
285 throw("Fatal Error");
286 }
287 break;
288 default:
289 break;
290 }
291}
292
293//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294
295shared_ptr<default_random_engine> makeRandomEngine(Element *el, FGFDMExec* fdmex)
296{
297 string seed_attr = el->GetAttributeValue("seed");
298 unsigned int seed;
299 if (seed_attr.empty())
300 return fdmex->GetRandomEngine();
301 else if (seed_attr == "time_now")
302 seed = chrono::system_clock::now().time_since_epoch().count();
303 else
304 seed = atoi(seed_attr.c_str());
305 return make_shared<default_random_engine>(seed);
306}
307
308//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
309
311 const string& Prefix)
312{
313 Name = el->GetAttributeValue("name");
314 Element* element = el->GetElement();
315
316 auto sum = [](const decltype(Parameters)& Parameters)->double {
317 double temp = 0.0;
318
319 for (auto p: Parameters)
320 temp += p->GetValue();
321
322 return temp;
323 };
324
325 while (element) {
326 string operation = element->GetName();
327
328 // data types
329 if (operation == "property" || operation == "p") {
330 string property_name = element->GetDataLine();
331
332 if (var && simgear::strutils::strip(property_name) == "#")
333 Parameters.push_back(var);
334 else {
335 if (property_name.find("#") != string::npos) {
336 if (is_number(Prefix)) {
337 property_name = replace(property_name,"#",Prefix);
338 }
339 else {
340 cerr << element->ReadFrom()
341 << fgred << "Illegal use of the special character '#'"
342 << reset << endl;
343 throw("Fatal Error.");
344 }
345 }
346
347 if (element->HasAttribute("apply")) {
348 string function_str = element->GetAttributeValue("apply");
349 FGTemplateFunc* f = fdmex->GetTemplateFunc(function_str);
350 if (f)
351 Parameters.push_back(new FGFunctionValue(property_name,
352 PropertyManager, f));
353 else {
354 cerr << element->ReadFrom()
355 << fgred << highint << " No function by the name "
356 << function_str << " has been defined. This property will "
357 << "not be logged. You should check your configuration file."
358 << reset << endl;
359 }
360 }
361 else
362 Parameters.push_back(new FGPropertyValue(property_name,
364 }
365 } else if (operation == "value" || operation == "v") {
366 Parameters.push_back(new FGRealValue(element->GetDataAsNumber()));
367 } else if (operation == "pi") {
368 Parameters.push_back(new FGRealValue(M_PI));
369 } else if (operation == "table" || operation == "t") {
370 Parameters.push_back(new FGTable(PropertyManager, element, Prefix));
371 // operations
372 } else if (operation == "product") {
373 auto f = [](const decltype(Parameters)& Parameters)->double {
374 double temp = 1.0;
375
376 for (auto p: Parameters)
377 temp *= p->GetValue();
378
379 return temp;
380 };
381 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
382 } else if (operation == "sum") {
383 Parameters.push_back(VarArgsFn<decltype(sum)>(sum, fdmex, element, Prefix, var));
384 } else if (operation == "avg") {
385 auto avg = [&](const decltype(Parameters)& p)->double {
386 return sum(p) / p.size();
387 };
388 Parameters.push_back(VarArgsFn<decltype(avg)>(avg, fdmex, element, Prefix, var));
389 } else if (operation == "difference") {
390 auto f = [](const decltype(Parameters)& Parameters)->double {
391 double temp = Parameters[0]->GetValue();
392
393 for (auto p = Parameters.begin()+1; p != Parameters.end(); ++p)
394 temp -= (*p)->GetValue();
395
396 return temp;
397 };
398 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
399 } else if (operation == "min") {
400 auto f = [](const decltype(Parameters)& Parameters)->double {
401 double _min = HUGE_VAL;
402
403 for (auto p : Parameters) {
404 double x = p->GetValue();
405 if (x < _min)
406 _min = x;
407 }
408
409 return _min;
410 };
411 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
412 } else if (operation == "max") {
413 auto f = [](const decltype(Parameters)& Parameters)->double {
414 double _max = -HUGE_VAL;
415
416 for (auto p : Parameters) {
417 double x = p->GetValue();
418 if (x > _max)
419 _max = x;
420 }
421
422 return _max;
423 };
424 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
425 } else if (operation == "and") {
426 string ctxMsg = element->ReadFrom();
427 auto f = [ctxMsg](const decltype(Parameters)& Parameters)->double {
428 for (auto p : Parameters) {
429 if (!GetBinary(p->GetValue(), ctxMsg)) // As soon as one parameter is false, the expression is guaranteed to be false.
430 return 0.0;
431 }
432
433 return 1.0;
434 };
435 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
436 var, MaxArgs));
437 } else if (operation == "or") {
438 string ctxMsg = element->ReadFrom();
439 auto f = [ctxMsg](const decltype(Parameters)& Parameters)->double {
440 for (auto p : Parameters) {
441 if (GetBinary(p->GetValue(), ctxMsg)) // As soon as one parameter is true, the expression is guaranteed to be true.
442 return 1.0;
443 }
444
445 return 0.0;
446 };
447 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
448 var, MaxArgs));
449 } else if (operation == "quotient") {
450 auto f = [](const decltype(Parameters)& p)->double {
451 double y = p[1]->GetValue();
452 return y != 0.0 ? p[0]->GetValue()/y : HUGE_VAL;
453 };
454 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
455 } else if (operation == "pow") {
456 auto f = [](const decltype(Parameters)& p)->double {
457 return pow(p[0]->GetValue(), p[1]->GetValue());
458 };
459 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
460 } else if (operation == "toradians") {
461 auto f = [](const decltype(Parameters)& p)->double {
462 return p[0]->GetValue()*M_PI/180.;
463 };
464 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
465 } else if (operation == "todegrees") {
466 auto f = [](const decltype(Parameters)& p)->double {
467 return p[0]->GetValue()*180./M_PI;
468 };
469 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
470 } else if (operation == "sqrt") {
471 auto f = [](const decltype(Parameters)& p)->double {
472 double x = p[0]->GetValue();
473 return x >= 0.0 ? sqrt(x) : -HUGE_VAL;
474 };
475 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
476 } else if (operation == "log2") {
477 auto f = [](const decltype(Parameters)& p)->double {
478 double x = p[0]->GetValue();
479 return x > 0.0 ? log10(x)*invlog2val : -HUGE_VAL;
480 };
481 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
482 } else if (operation == "ln") {
483 auto f = [](const decltype(Parameters)& p)->double {
484 double x = p[0]->GetValue();
485 return x > 0.0 ? log(x) : -HUGE_VAL;
486 };
487 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
488 } else if (operation == "log10") {
489 auto f = [](const decltype(Parameters)& p)->double {
490 double x = p[0]->GetValue();
491 return x > 0.0 ? log10(x) : -HUGE_VAL;
492 };
493 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
494 } else if (operation == "sign") {
495 auto f = [](const decltype(Parameters)& p)->double {
496 return p[0]->GetValue() < 0.0 ? -1 : 1; // 0.0 counts as positive.
497 };
498 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
499 } else if (operation == "exp") {
500 Parameters.push_back(make_MathFn(exp, fdmex, element, Prefix, var));
501 } else if (operation == "abs") {
502 Parameters.push_back(make_MathFn(fabs, fdmex, element, Prefix, var));
503 } else if (operation == "sin") {
504 Parameters.push_back(make_MathFn(sin, fdmex, element, Prefix, var));
505 } else if (operation == "cos") {
506 Parameters.push_back(make_MathFn(cos, fdmex, element, Prefix, var));
507 } else if (operation == "tan") {
508 Parameters.push_back(make_MathFn(tan, fdmex, element, Prefix, var));
509 } else if (operation == "asin") {
510 Parameters.push_back(make_MathFn(asin, fdmex, element, Prefix, var));
511 } else if (operation == "acos") {
512 Parameters.push_back(make_MathFn(acos, fdmex, element, Prefix, var));
513 } else if (operation == "atan") {
514 Parameters.push_back(make_MathFn(atan, fdmex, element, Prefix, var));
515 } else if (operation == "floor") {
516 Parameters.push_back(make_MathFn(floor, fdmex, element, Prefix, var));
517 } else if (operation == "ceil") {
518 Parameters.push_back(make_MathFn(ceil, fdmex, element, Prefix, var));
519 } else if (operation == "fmod") {
520 auto f = [](const decltype(Parameters)& p)->double {
521 double y = p[1]->GetValue();
522 return y != 0.0 ? fmod(p[0]->GetValue(), y) : HUGE_VAL;
523 };
524 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
525 } else if (operation == "atan2") {
526 auto f = [](const decltype(Parameters)& p)->double {
527 return atan2(p[0]->GetValue(), p[1]->GetValue());
528 };
529 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
530 } else if (operation == "mod") {
531 auto f = [](const decltype(Parameters)& p)->double {
532 return static_cast<int>(p[0]->GetValue()) % static_cast<int>(p[1]->GetValue());
533 };
534 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
535 } else if (operation == "fraction") {
536 auto f = [](const decltype(Parameters)& p)->double {
537 double scratch;
538 return modf(p[0]->GetValue(), &scratch);
539 };
540 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
541 } else if (operation == "integer") {
542 auto f = [](const decltype(Parameters)& p)->double {
543 double result;
544 modf(p[0]->GetValue(), &result);
545 return result;
546 };
547 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
548 } else if (operation == "lt") {
549 auto f = [](const decltype(Parameters)& p)->double {
550 return p[0]->GetValue() < p[1]->GetValue() ? 1.0 : 0.0;
551 };
552 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
553 } else if (operation == "le") {
554 auto f = [](const decltype(Parameters)& p)->double {
555 return p[0]->GetValue() <= p[1]->GetValue() ? 1.0 : 0.0;
556 };
557 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
558 } else if (operation == "gt") {
559 auto f = [](const decltype(Parameters)& p)->double {
560 return p[0]->GetValue() > p[1]->GetValue() ? 1.0 : 0.0;
561 };
562 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
563 } else if (operation == "ge") {
564 auto f = [](const decltype(Parameters)& p)->double {
565 return p[0]->GetValue() >= p[1]->GetValue() ? 1.0 : 0.0;
566 };
567 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
568 } else if (operation == "eq") {
569 auto f = [](const decltype(Parameters)& p)->double {
570 return p[0]->GetValue() == p[1]->GetValue() ? 1.0 : 0.0;
571 };
572 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
573 } else if (operation == "nq") {
574 auto f = [](const decltype(Parameters)& p)->double {
575 return p[0]->GetValue() != p[1]->GetValue() ? 1.0 : 0.0;
576 };
577 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
578 } else if (operation == "not") {
579 string ctxMsg = element->ReadFrom();
580 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
581 return GetBinary(p[0]->GetValue(), ctxMsg) ? 0.0 : 1.0;
582 };
583 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
584 } else if (operation == "ifthen") {
585 string ctxMsg = element->ReadFrom();
586 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
587 if (GetBinary(p[0]->GetValue(), ctxMsg))
588 return p[1]->GetValue();
589 else
590 return p[2]->GetValue();
591 };
592 Parameters.push_back(new aFunc<decltype(f), 3>(f, fdmex, element, Prefix, var));
593 } else if (operation == "random") {
594 double mean = 0.0;
595 double stddev = 1.0;
596 string mean_attr = element->GetAttributeValue("mean");
597 string stddev_attr = element->GetAttributeValue("stddev");
598 if (!mean_attr.empty())
599 mean = atof(mean_attr.c_str());
600 if (!stddev_attr.empty())
601 stddev = atof(stddev_attr.c_str());
602 auto distribution = make_shared<normal_distribution<double>>(mean, stddev);
603 auto generator(makeRandomEngine(element, fdmex));
604 auto f = [generator, distribution]()->double {
605 return (*distribution.get())(*generator);
606 };
607 Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
608 Prefix));
609 } else if (operation == "urandom") {
610 double lower = -1.0;
611 double upper = 1.0;
612 string lower_attr = element->GetAttributeValue("lower");
613 string upper_attr = element->GetAttributeValue("upper");
614 if (!lower_attr.empty())
615 lower = atof(lower_attr.c_str());
616 if (!upper_attr.empty())
617 upper = atof(upper_attr.c_str());
618 auto distribution = make_shared<uniform_real_distribution<double>>(lower, upper);
619 auto generator(makeRandomEngine(element, fdmex));
620 auto f = [generator, distribution]()->double {
621 return (*distribution.get())(*generator);
622 };
623 Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
624 Prefix));
625 } else if (operation == "switch") {
626 string ctxMsg = element->ReadFrom();
627 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
628 double temp = p[0]->GetValue();
629 if (temp < 0.0) {
630 cerr << ctxMsg << fgred << highint
631 << "The switch function index (" << temp
632 << ") is negative." << reset << endl;
633 throw("Fatal error");
634 }
635 size_t n = p.size()-1;
636 size_t i = static_cast<size_t>(temp+0.5);
637
638 if (i < n)
639 return p[i+1]->GetValue();
640 else {
641 cerr << ctxMsg << fgred << highint
642 << "The switch function index (" << temp
643 << ") selected a value above the range of supplied values"
644 << "[0:" << n-1 << "]"
645 << " - not enough values were supplied." << reset << endl;
646 throw("Fatal error");
647 }
648 };
649 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
650 var, MaxArgs));
651 } else if (operation == "interpolate1d") {
652 auto f = [](const decltype(Parameters)& p)->double {
653 // This is using the bisection algorithm. Special care has been
654 // taken to evaluate each parameter only once.
655 size_t n = p.size();
656 double x = p[0]->GetValue();
657 double xmin = p[1]->GetValue();
658 double ymin = p[2]->GetValue();
659 if (x <= xmin) return ymin;
660
661 double xmax = p[n-2]->GetValue();
662 double ymax = p[n-1]->GetValue();
663 if (x >= xmax) return ymax;
664
665 size_t nmin = 0;
666 size_t nmax = (n-3)/2;
667 while (nmax-nmin > 1) {
668 size_t m = (nmax-nmin)/2+nmin;
669 double xm = p[2*m+1]->GetValue();
670 double ym = p[2*m+2]->GetValue();
671 if (x < xm) {
672 xmax = xm;
673 ymax = ym;
674 nmax= m;
675 } else if (x > xm) {
676 xmin = xm;
677 ymin = ym;
678 nmin = m;
679 }
680 else
681 return ym;
682 }
683
684 return ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin);
685 };
686 Parameters.push_back(new aFunc<decltype(f), 5>(f, fdmex, element, Prefix,
687 var, MaxArgs, OddEven::Odd));
688 } else if (operation == "rotation_alpha_local") {
689 // Calculates local angle of attack for skydiver body component.
690 // Euler angles from the intermediate body frame to the local body frame
691 // must be from a z-y-x axis rotation order
692 auto f = [](const decltype(Parameters)& p)->double {
693 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
694 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
695 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
696 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
697 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
698
699 FGQuaternion qTb2l(phi, theta, psi);
700 double cos_beta = cos(beta);
701 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
702 sin(alpha)*cos_beta);
703 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
704
705 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
706 return 0.0;
707 else
708 return atan2(wind_local(eZ), wind_local(eX))*radtodeg;
709 };
710 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
711 } else if (operation == "rotation_beta_local") {
712 // Calculates local angle of sideslip for skydiver body component.
713 // Euler angles from the intermediate body frame to the local body frame
714 // must be from a z-y-x axis rotation order
715 auto f = [](const decltype(Parameters)& p)->double {
716 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
717 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
718 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
719 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
720 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
721 FGQuaternion qTb2l(phi, theta, psi);
722 double cos_beta = cos(beta);
723 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
724 sin(alpha)*cos_beta);
725 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
726
727 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
728 return wind_local(eY) > 0.0 ? 0.5*M_PI : -0.5*M_PI;
729
730 double alpha_local = atan2(wind_local(eZ), wind_local(eX));
731 double cosa = cos(alpha_local);
732 double sina = sin(alpha_local);
733 double cosb;
734
735 if (fabs(cosa) > fabs(sina))
736 cosb = wind_local(eX) / cosa;
737 else
738 cosb = wind_local(eZ) / sina;
739
740 return atan2(wind_local(eY), cosb)*radtodeg;
741 };
742 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
743 } else if (operation == "rotation_gamma_local") {
744 // Calculates local roll angle for skydiver body component.
745 // Euler angles from the intermediate body frame to the local body frame
746 // must be from a z-y-x axis rotation order
747 auto f = [](const decltype(Parameters)& p)->double {
748 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
749 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
750 double gamma = p[2]->GetValue()*degtorad; //roll angle of intermediate body frame
751 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
752 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
753 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
754 double cos_alpha = cos(alpha), sin_alpha = sin(alpha);
755 double cos_beta = cos(beta), sin_beta = sin(beta);
756 double cos_gamma = cos(gamma), sin_gamma = sin(gamma);
757 FGQuaternion qTb2l(phi, theta, psi);
758 FGColumnVector3 wind_body_X(cos_alpha*cos_beta, sin_beta,
759 sin_alpha*cos_beta);
760 FGColumnVector3 wind_body_Y(-sin_alpha*sin_gamma-sin_beta*cos_alpha*cos_gamma,
761 cos_beta*cos_gamma,
762 -sin_alpha*sin_beta*cos_gamma+sin_gamma*cos_alpha);
763 FGColumnVector3 wind_local_X = qTb2l.GetT()*wind_body_X;
764 FGColumnVector3 wind_local_Y = qTb2l.GetT()*wind_body_Y;
765 double cosacosb = wind_local_X(eX);
766 double sinb = wind_local_X(eY);
767 double sinacosb = wind_local_X(eZ);
768 double sinc, cosc;
769
770 if (fabs(sinb) < 1E-9) { // cos(beta_local) == 1.0
771 cosc = wind_local_Y(eY);
772
773 if (fabs(cosacosb) > fabs(sinacosb))
774 sinc = wind_local_Y(eZ) / cosacosb;
775 else
776 sinc = -wind_local_Y(eX) / sinacosb;
777 }
778 else if (fabs(fabs(sinb)-1.0) < 1E-9) { // cos(beta_local) == 0.0
779 sinc = wind_local_Y(eZ);
780 cosc = -wind_local_Y(eX);
781 }
782 else {
783 sinc = cosacosb*wind_local_Y(eZ)-sinacosb*wind_local_Y(eX);
784 cosc = (-sinacosb*wind_local_Y(eZ)-cosacosb*wind_local_Y(eX))/sinb;
785 }
786
787 return atan2(sinc, cosc)*radtodeg;
788 };
789 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
790 } else if (operation == "rotation_bf_to_wf") {
791 // Transforms the input vector from a body frame to a wind frame. The
792 // origin of the vector remains the same.
793 string ctxMsg = element->ReadFrom();
794 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
795 double rx = p[0]->GetValue(); //x component of input vector
796 double ry = p[1]->GetValue(); //y component of input vector
797 double rz = p[2]->GetValue(); //z component of input vector
798 double alpha = p[3]->GetValue()*degtorad; //angle of attack of the body frame
799 double beta = p[4]->GetValue()*degtorad; //sideslip angle of the body frame
800 double gamma = p[5]->GetValue()*degtorad; //roll angle of the body frame
801 int idx = static_cast<int>(p[6]->GetValue());
802
803 if ((idx < 1) || (idx > 3)) {
804 cerr << ctxMsg << fgred << highint
805 << "The index must be one of the integer value 1, 2 or 3."
806 << reset << endl;
807 throw("Fatal error");
808 }
809
810 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
811 FGMatrix33 mT = (qa*qb*qc).GetT();
812 FGColumnVector3 r0(rx, ry, rz);
813 FGColumnVector3 r = mT*r0;
814
815 return r(idx);
816 };
817 Parameters.push_back(new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
818 } else if (operation == "rotation_wf_to_bf") {
819 // Transforms the input vector from q wind frame to a body frame. The
820 // origin of the vector remains the same.
821 string ctxMsg = element->ReadFrom();
822 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
823 double rx = p[0]->GetValue(); //x component of input vector
824 double ry = p[1]->GetValue(); //y component of input vector
825 double rz = p[2]->GetValue(); //z component of input vector
826 double alpha = p[3]->GetValue()*degtorad; //angle of attack of the body frame
827 double beta = p[4]->GetValue()*degtorad; //sideslip angle of the body frame
828 double gamma = p[5]->GetValue()*degtorad; //roll angle of the body frame
829 int idx = static_cast<int>(p[6]->GetValue());
830
831 if ((idx < 1) || (idx > 3)) {
832 cerr << ctxMsg << fgred << highint
833 << "The index must be one of the integer value 1, 2 or 3."
834 << reset << endl;
835 throw("Fatal error");
836 }
837
838 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
839 FGMatrix33 mT = (qa*qb*qc).GetT();
840 FGColumnVector3 r0(rx, ry, rz);
841 mT.T();
842 FGColumnVector3 r = mT*r0;
843
844 return r(idx);
845 };
846 Parameters.push_back(new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
847 } else if (operation != "description") {
848 cerr << element->ReadFrom() << fgred << highint
849 << "Bad operation <" << operation
850 << "> detected in configuration file" << reset << endl;
851 }
852
853 // Optimize functions applied on constant parameters by replacing them by
854 // their constant result.
855 if (!Parameters.empty()){
856 FGFunction* p = dynamic_cast<FGFunction*>(Parameters.back().ptr());
857
858 if (p && p->IsConstant()) {
859 double constant = p->GetValue();
860 FGPropertyNode_ptr node = p->pNode;
861 string pName = p->GetName();
862
863 Parameters.pop_back();
864 Parameters.push_back(new FGRealValue(constant));
865 if (debug_lvl > 0)
866 cout << element->ReadFrom() << fggreen << highint
867 << "<" << operation << "> is applied on constant parameters."
868 << endl << "It will be replaced by its result ("
869 << constant << ")";
870
871 if (node) {
872 node->setDoubleValue(constant);
873 node->setAttribute(SGPropertyNode::WRITE, false);
874 if (debug_lvl > 0)
875 cout << " and the property " << pName
876 << " will be unbound and made read only.";
877 }
878 cout << reset << endl << endl;
879 }
880 }
881 element = el->GetNextElement();
882 }
883
884 bind(el, Prefix); // Allow any function to save its value
885
886 Debug(0);
887}
888
889//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
890
892{
893 if (pNode && pNode->isTied())
894 PropertyManager->Untie(pNode);
895
896 Debug(1);
897}
898
899//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900
902{
903 for (auto p: Parameters) {
904 if (!p->IsConstant())
905 return false;
906 }
907
908 return true;
909}
910
911//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
912
914{
915 cached = false; // Must set cached to false prior to calling GetValue(), else
916 // it will _never_ calculate the value;
917 if (cache) {
919 cached = true;
920 }
921}
922
923//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
924
925double FGFunction::GetValue(void) const
926{
927 if (cached) return cachedValue;
928
929 double val = Parameters[0]->GetValue();
930
931 if (pCopyTo) pCopyTo->setDoubleValue(val);
932
933 return val;
934}
935
936//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
937
939{
940 ostringstream buffer;
941
942 buffer << setw(9) << setprecision(6) << GetValue();
943 return buffer.str();
944}
945
946//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947
948string FGFunction::CreateOutputNode(Element* el, const string& Prefix)
949{
950 string nName;
951
952 if ( !Name.empty() ) {
953 if (Prefix.empty())
954 nName = PropertyManager->mkPropertyName(Name, false);
955 else {
956 if (is_number(Prefix)) {
957 if (Name.find("#") != string::npos) { // if "#" is found
958 Name = replace(Name,"#",Prefix);
959 nName = PropertyManager->mkPropertyName(Name, false);
960 } else {
961 cerr << el->ReadFrom()
962 << "Malformed function name with number: " << Prefix
963 << " and property name: " << Name
964 << " but no \"#\" sign for substitution." << endl;
965 }
966 } else {
967 nName = PropertyManager->mkPropertyName(Prefix + "/" + Name, false);
968 }
969 }
970
971 pNode = PropertyManager->GetNode(nName, true);
972 if (pNode->isTied()) {
973 cerr << el->ReadFrom()
974 << "Property " << nName << " has already been successfully bound (late)." << endl;
975 throw("Failed to bind the property to an existing already tied node.");
976 }
977 }
978
979 return nName;
980}
981
982//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
983
984void FGFunction::bind(Element* el, const string& Prefix)
985{
986 string nName = CreateOutputNode(el, Prefix);
987
988 if (!nName.empty())
989 PropertyManager->Tie(nName, this, &FGFunction::GetValue);
990}
991
992//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
993// The bitmasked value choices are as follows:
994// unset: In this case (the default) JSBSim would only print
995// out the normally expected messages, essentially echoing
996// the config files as they are read. If the environment
997// variable is not set, debug_lvl is set to 1 internally
998// 0: This requests JSBSim not to output any messages
999// whatsoever.
1000// 1: This value explicity requests the normal JSBSim
1001// startup messages
1002// 2: This value asks for a message to be printed out when
1003// a class is instantiated
1004// 4: When this value is set, a message is displayed when a
1005// FGModel object executes its Run() method
1006// 8: When this value is set, various runtime state variables
1007// are printed out periodically
1008// 16: When set various parameters are sanity checked and
1009// a message is printed out when they go out of bounds
1010
1011void FGFunction::Debug(int from)
1012{
1013 if (debug_lvl <= 0) return;
1014
1015 if (debug_lvl & 1) { // Standard console startup message output
1016 if (from == 0) { // Constructor
1017 if (!Name.empty())
1018 cout << " Function: " << Name << endl;
1019 }
1020 }
1021 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1022 if (from == 0) cout << "Instantiated: FGFunction" << endl;
1023 if (from == 1) cout << "Destroyed: FGFunction" << endl;
1024 }
1025 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1026 }
1027 if (debug_lvl & 8 ) { // Runtime state variables
1028 }
1029 if (debug_lvl & 16) { // Sanity checking
1030 }
1031 if (debug_lvl & 64) {
1032 if (from == 0) { // Constructor
1033 }
1034 }
1035}
1036
1037}
#define p(x)
#define M_PI
Definition FGJSBBase.h:50
#define i(x)
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
const std::string & GetName(void) const
Retrieves the element name.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
Element * GetNextElement(void)
Returns a pointer to the next element in the list.
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
bool HasAttribute(const std::string &key)
Determines if an element has the supplied attribute.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
Element * GetElement(unsigned int el=0)
Returns a pointer to the element requested by index.
double GetDataAsNumber(void)
Converts the element data to a number.
This class implements a 3 element column vector.
const std::shared_ptr< std::default_random_engine > & GetRandomEngine(void) const
Definition FGFDMExec.h:619
FGTemplateFunc * GetTemplateFunc(const std::string &name)
Definition FGFDMExec.h:611
Represents a property value on which a function is applied.
Represents a mathematical function.
Definition FGFunction.h:753
FGPropertyNode_ptr pNode
Definition FGFunction.h:821
~FGFunction(void) override
Destructor Make sure the function is untied before destruction.
void CheckMaxArguments(Element *el, unsigned int _max)
FGFunction()
Default constructor.
Definition FGFunction.h:756
void CheckOddOrEvenArguments(Element *el, OddEven odd_even)
double GetValue(void) const override
Retrieves the value of the function object.
std::string GetValueAsString(void) const
The value that the function evaluates to, as a string.
std::string CreateOutputNode(Element *el, const std::string &Prefix)
virtual void bind(Element *, const std::string &)
std::vector< FGParameter_ptr > Parameters
Definition FGFunction.h:819
FGPropertyManager * PropertyManager
Definition FGFunction.h:820
bool IsConstant(void) const override
Does the function always return the same result (i.e.
void CheckMinArguments(Element *el, unsigned int _min)
void cacheValue(bool shouldCache)
Specifies whether to cache the value of the function, so it is calculated only once per frame.
void Load(Element *element, FGPropertyValue *var, FGFDMExec *fdmex, const std::string &prefix="")
static constexpr double radtodeg
Definition FGJSBBase.h:348
static char highint[5]
highlights text
Definition FGJSBBase.h:123
static char reset[5]
resets text properties
Definition FGJSBBase.h:129
static char fgred[6]
red text
Definition FGJSBBase.h:139
static char fggreen[6]
green text
Definition FGJSBBase.h:141
static constexpr double degtorad
Definition FGJSBBase.h:349
static short debug_lvl
Definition FGJSBBase.h:190
Handles matrix math operations.
Definition FGMatrix33.h:70
void T(void)
Transposes this matrix.
Represents various types of parameters.
Definition FGParameter.h:59
Represents a property value which can use late binding.
Models the Quaternion representation of rotations.
const FGMatrix33 & GetT(void) const
Transformation matrix.
Represents a real value.
Definition FGRealValue.h:58
Lookup table class.
Definition FGTable.h:234
size_t NumberOfArguments(void) const
FGParameter * FirstParameter(void) const
const Element * GetElement(void) const
WrongNumberOfArguments(const string &msg, const vector< FGParameter_ptr > &p, Element *el)
double GetValue(void) const override
bool IsConstant(void) const override
void bind(Element *el, const string &Prefix) override
aFunc(const func_t &_f, FGPropertyManager *pm, Element *el, const string &Prefix)
void bind(Element *el, const string &Prefix) override
aFunc(const func_t &_f, FGFDMExec *fdmex, Element *el, const string &prefix, FGPropertyValue *v, unsigned int Nmax=Nmin, FGFunction::OddEven odd_even=FGFunction::OddEven::Either)
double GetValue(void) const override
static mt seed
Definition fgclouds.cxx:49
FGParameter_ptr VarArgsFn(const func_t &_f, FGFDMExec *fdmex, Element *el, const string &prefix, FGPropertyValue *v)
FGFunction * make_MathFn(double(*math_fn)(double), FGFDMExec *fdmex, Element *el, const string &prefix, FGPropertyValue *v)
short debug_lvl
SGSharedPtr< FGPropertyNode > FGPropertyNode_ptr
const double invlog2val
shared_ptr< default_random_engine > makeRandomEngine(Element *el, FGFDMExec *fdmex)
SGSharedPtr< FGParameter > FGParameter_ptr
Definition FGParameter.h:70
constexpr unsigned int MaxArgs
bool GetBinary(double val, const string &ctxMsg)
static double atof(const string &str)
Definition options.cxx:107
static int atoi(const string &str)
Definition options.cxx:113
bool is_number(const std::string &str)
std::string replace(std::string str, const std::string &old, const std::string &newstr)