34#include "simgear/misc/strutils.hxx"
36#include "FGFunction.h"
38#include "FGRealValue.h"
39#include "input_output/FGXMLElement.h"
40#include "math/FGFunctionValue.h"
41#include "input_output/string_utilities.h"
52const double invlog2val = 1.0/log10(2.0);
53constexpr unsigned int MaxArgs = 9999;
63 size_t NumberOfArguments(
void)
const {
return Parameters.size(); }
64 FGParameter* FirstParameter(
void)
const {
return *(Parameters.cbegin()); }
65 const Element* GetElement(
void)
const {
return element; }
68 const vector<FGParameter_ptr> Parameters;
69 const Element_ptr element;
74template<
typename func_t,
unsigned int Nmin>
80 FGFunction::OddEven odd_even=FGFunction::OddEven::Either)
83 Load(el, v, fdmex, prefix);
84 CheckMinArguments(el, Nmin);
85 CheckMaxArguments(el, Nmax);
86 CheckOddOrEvenArguments(el, odd_even);
89 double GetValue(
void)
const override {
90 return cached ? cachedValue : f(Parameters);
94 void bind(
Element* el,
const string& Prefix)
override {
95 string nName = CreateOutputNode(el, Prefix);
107template<
typename func_t>
111 aFunc(
const func_t& _f, std::shared_ptr<FGPropertyManager> pm,
Element* el,
112 const string& Prefix)
116 ostringstream buffer;
118 <<
"<" << el->
GetName() <<
"> should have no arguments." <<
reset
126 double GetValue(
void)
const override {
127 double result = cached ? cachedValue : f();
128 if (pNode) pNode->setDoubleValue(result);
141 void bind(
Element* el,
const string& Prefix)
override {
142 CreateOutputNode(el, Prefix);
144 if (pNode) pNode->setDoubleValue(f());
153bool GetBinary(
double val,
const string &ctxMsg)
156 if (val < 1E-9)
return false;
157 else if (val-1 < 1E-9)
return true;
160 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
161 <<
"Malformed conditional check in function definition.\n"
171FGFunction* make_MathFn(
double(*math_fn)(
double), FGFDMExec* fdmex, Element* el,
172 const string& prefix, FGPropertyValue* v)
174 auto f = [math_fn](
const std::vector<FGParameter_ptr> &p)->
double {
175 return math_fn(p[0]->GetValue());
177 return new aFunc<decltype(f), 1>(f, fdmex, el, prefix, v);
185template<
typename func_t>
186FGParameter_ptr VarArgsFn(
const func_t& _f, FGFDMExec* fdmex, Element* el,
187 const string& prefix, FGPropertyValue* v)
190 return new aFunc<func_t, 2>(_f, fdmex, el, prefix, v, MaxArgs);
192 catch(WrongNumberOfArguments& e) {
193 if ((e.GetElement() == el) && (e.NumberOfArguments() == 1)) {
194 FGXMLLogging log(el, LogLevel::WARN);
195 log << LogFormat::RED
196 <<
"<" << el->GetName()
197 <<
"> only has one argument which makes it a no-op.\n"
198 <<
"Its argument will be evaluated but <" << el->GetName()
199 <<
"> will not be applied to the result.\n" << LogFormat::RESET;
200 return e.FirstParameter();
213 Load(el, var, fdmex, prefix);
214 CheckMinArguments(el, 1);
215 CheckMaxArguments(el, 1);
219 if (!sCopyTo.empty()) {
220 if (sCopyTo.find(
"#") != string::npos) {
221 if (is_number(prefix))
222 sCopyTo = replace(sCopyTo,
"#",prefix);
225 log << LogFormat::RED
226 <<
"Illegal use of the special character '#'\n" << LogFormat::RESET
227 <<
"The 'copyto' argument in function " << Name <<
" is ignored.\n";
232 pCopyTo = PropertyManager->GetNode(sCopyTo);
235 log << LogFormat::RED
236 <<
"Property \"" << sCopyTo
237 <<
"\" must be previously defined in function " << Name << LogFormat::RESET
238 <<
"The 'copyto' argument is ignored.\n";
245void FGFunction::CheckMinArguments(
Element* el,
unsigned int _min)
247 if (Parameters.size() < _min) {
248 ostringstream buffer;
250 <<
"<" << el->
GetName() <<
"> should have at least " << _min
251 <<
" argument(s)." <<
reset << endl;
258void FGFunction::CheckMaxArguments(Element* el,
unsigned int _max)
260 if (Parameters.size() > _max) {
261 ostringstream buffer;
263 <<
"<" << el->GetName() <<
"> should have no more than " << _max
264 <<
" argument(s)." <<
reset << endl;
265 throw WrongNumberOfArguments(buffer.str(), Parameters, el);
271void FGFunction::CheckOddOrEvenArguments(Element* el, OddEven odd_even)
276 if (Parameters.size() % 2 == 1) {
277 XMLLogException err(el);
278 err << LogFormat::RED << LogFormat::BOLD
279 <<
"<" << el->GetName() <<
"> must have an even number of arguments.\n"
285 if (Parameters.size() % 2 == 0) {
286 XMLLogException err(el);
287 err << LogFormat::RED << LogFormat::BOLD
288 <<
"<" << el->GetName() <<
"> must have an odd number of arguments.\n"
300shared_ptr<RandomNumberGenerator> makeRandomGenerator(Element *el, FGFDMExec* fdmex)
302 string seed_attr = el->GetAttributeValue(
"seed");
303 if (seed_attr.empty())
304 return fdmex->GetRandomGenerator();
305 else if (seed_attr ==
"time_now")
306 return make_shared<RandomNumberGenerator>();
308 unsigned int seed = atoi(seed_attr.c_str());
309 return make_shared<RandomNumberGenerator>(seed);
315void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
316 const string& Prefix)
318 Name = el->GetAttributeValue(
"name");
319 Element* element = el->GetElement();
321 auto sum = [](
const decltype(Parameters)& Parameters)->double {
324 for (
auto p: Parameters)
331 string operation = element->GetName();
334 if (operation ==
"property" || operation ==
"p") {
335 string property_name = element->GetDataLine();
337 if (var && simgear::strutils::strip(property_name) ==
"#")
338 Parameters.push_back(var);
340 if (property_name.find(
"#") != string::npos) {
341 if (is_number(Prefix)) {
342 property_name = replace(property_name,
"#",Prefix);
345 XMLLogException err(element);
346 err << LogFormat::RED <<
"Illegal use of the special character '#'\n"
352 if (element->HasAttribute(
"apply")) {
353 string function_str = element->GetAttributeValue(
"apply");
354 auto f = fdmex->GetTemplateFunc(function_str);
356 Parameters.push_back(
new FGFunctionValue(property_name,
357 PropertyManager, f, element));
359 FGXMLLogging log(element, LogLevel::ERROR);
360 log << LogFormat::RED << LogFormat::BOLD
361 <<
" No function by the name " << function_str
362 <<
" has been defined. This property will "
363 <<
"not be logged. You should check your configuration file.\n"
368 Parameters.push_back(
new FGPropertyValue(property_name,
369 PropertyManager, element));
371 }
else if (operation ==
"value" || operation ==
"v") {
372 Parameters.push_back(
new FGRealValue(element->GetDataAsNumber()));
373 }
else if (operation ==
"pi") {
374 Parameters.push_back(
new FGRealValue(M_PI));
375 }
else if (operation ==
"table" || operation ==
"t") {
376 string call_type = element->GetAttributeValue(
"type");
377 if (call_type ==
"internal") {
378 XMLLogException err(el);
379 err <<
"An internal table cannot be nested within a function.\n";
382 Parameters.push_back(
new FGTable(PropertyManager, element, Prefix));
384 }
else if (operation ==
"product") {
385 auto f = [](
const decltype(Parameters)& Parameters)->double {
388 for (
auto p: Parameters)
393 Parameters.push_back(VarArgsFn<
decltype(f)>(f, fdmex, element, Prefix, var));
394 }
else if (operation ==
"sum") {
395 Parameters.push_back(VarArgsFn<
decltype(sum)>(sum, fdmex, element, Prefix, var));
396 }
else if (operation ==
"avg") {
397 auto avg = [&](
const decltype(Parameters)& p)->double {
398 return sum(p) / p.size();
400 Parameters.push_back(VarArgsFn<
decltype(avg)>(avg, fdmex, element, Prefix, var));
401 }
else if (operation ==
"difference") {
402 auto f = [](
const decltype(Parameters)& Parameters)->double {
403 double temp = Parameters[0]->GetValue();
405 for (
auto p = Parameters.begin()+1; p != Parameters.end(); ++p)
406 temp -= (*p)->GetValue();
410 Parameters.push_back(VarArgsFn<
decltype(f)>(f, fdmex, element, Prefix, var));
411 }
else if (operation ==
"min") {
412 auto f = [](
const decltype(Parameters)& Parameters)->double {
413 double _min = HUGE_VAL;
415 for (
auto p : Parameters) {
416 double x = p->GetValue();
423 Parameters.push_back(VarArgsFn<
decltype(f)>(f, fdmex, element, Prefix, var));
424 }
else if (operation ==
"max") {
425 auto f = [](
const decltype(Parameters)& Parameters)->double {
426 double _max = -HUGE_VAL;
428 for (
auto p : Parameters) {
429 double x = p->GetValue();
436 Parameters.push_back(VarArgsFn<
decltype(f)>(f, fdmex, element, Prefix, var));
437 }
else if (operation ==
"and") {
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))
447 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix,
449 }
else if (operation ==
"or") {
450 string ctxMsg = element->ReadFrom();
451 auto f = [ctxMsg](
const decltype(Parameters)& Parameters)->double {
452 for (
auto p : Parameters) {
453 if (GetBinary(p->GetValue(), ctxMsg))
459 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix,
461 }
else if (operation ==
"quotient") {
462 auto f = [](
const decltype(Parameters)& p)->double {
463 double y = p[1]->GetValue();
464 return y != 0.0 ? p[0]->GetValue()/y : HUGE_VAL;
466 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
467 }
else if (operation ==
"pow") {
468 auto f = [](
const decltype(Parameters)& p)->double {
471 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
472 }
else if (operation ==
"toradians") {
473 auto f = [](
const decltype(Parameters)& p)->double {
474 return p[0]->GetValue()*M_PI/180.;
476 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
477 }
else if (operation ==
"todegrees") {
478 auto f = [](
const decltype(Parameters)& p)->double {
479 return p[0]->GetValue()*180./M_PI;
481 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
482 }
else if (operation ==
"sqrt") {
483 auto f = [](
const decltype(Parameters)& p)->double {
484 double x = p[0]->GetValue();
485 return x >= 0.0 ? sqrt(x) : -HUGE_VAL;
487 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
488 }
else if (operation ==
"log2") {
489 auto f = [](
const decltype(Parameters)& p)->double {
490 double x = p[0]->GetValue();
491 return x > 0.0 ? log10(x)*invlog2val : -HUGE_VAL;
493 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
494 }
else if (operation ==
"ln") {
495 auto f = [](
const decltype(Parameters)& p)->double {
496 double x = p[0]->GetValue();
497 return x > 0.0 ? log(x) : -HUGE_VAL;
499 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
500 }
else if (operation ==
"log10") {
501 auto f = [](
const decltype(Parameters)& p)->double {
502 double x = p[0]->GetValue();
503 return x > 0.0 ? log10(x) : -HUGE_VAL;
505 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
506 }
else if (operation ==
"sign") {
507 auto f = [](
const decltype(Parameters)& p)->double {
508 return p[0]->GetValue() < 0.0 ? -1 : 1;
510 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
511 }
else if (operation ==
"exp") {
512 Parameters.push_back(make_MathFn(exp, fdmex, element, Prefix, var));
513 }
else if (operation ==
"abs") {
514 Parameters.push_back(make_MathFn(fabs, fdmex, element, Prefix, var));
515 }
else if (operation ==
"sin") {
516 Parameters.push_back(make_MathFn(sin, fdmex, element, Prefix, var));
517 }
else if (operation ==
"cos") {
518 Parameters.push_back(make_MathFn(cos, fdmex, element, Prefix, var));
519 }
else if (operation ==
"tan") {
520 Parameters.push_back(make_MathFn(tan, fdmex, element, Prefix, var));
521 }
else if (operation ==
"asin") {
522 Parameters.push_back(make_MathFn(asin, fdmex, element, Prefix, var));
523 }
else if (operation ==
"acos") {
524 Parameters.push_back(make_MathFn(acos, fdmex, element, Prefix, var));
525 }
else if (operation ==
"atan") {
526 Parameters.push_back(make_MathFn(atan, fdmex, element, Prefix, var));
527 }
else if (operation ==
"floor") {
528 Parameters.push_back(make_MathFn(floor, fdmex, element, Prefix, var));
529 }
else if (operation ==
"ceil") {
530 Parameters.push_back(make_MathFn(ceil, fdmex, element, Prefix, var));
531 }
else if (operation ==
"fmod") {
532 auto f = [](
const decltype(Parameters)& p)->double {
533 double y = p[1]->GetValue();
534 return y != 0.0 ? fmod(p[0]->
GetValue(), y) : HUGE_VAL;
536 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
537 }
else if (operation ==
"roundmultiple") {
538 if (element->GetNumElements() == 1)
539 Parameters.push_back(make_MathFn(round, fdmex, element, Prefix, var));
541 auto f = [](
const decltype(Parameters)& p)->double {
542 double multiple = p[1]->GetValue();
543 return round((p[0]->
GetValue() / multiple)) * multiple;
545 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var, 2));
547 }
else if (operation ==
"atan2") {
548 auto f = [](
const decltype(Parameters)& p)->double {
551 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
552 }
else if (operation ==
"mod") {
553 auto f = [](
const decltype(Parameters)& p)->double {
554 return static_cast<int>(p[0]->GetValue()) %
static_cast<int>(p[1]->
GetValue());
556 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
557 }
else if (operation ==
"fraction") {
558 auto f = [](
const decltype(Parameters)& p)->double {
560 return modf(p[0]->
GetValue(), &scratch);
562 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
563 }
else if (operation ==
"integer") {
564 auto f = [](
const decltype(Parameters)& p)->double {
569 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
570 }
else if (operation ==
"lt") {
571 auto f = [](
const decltype(Parameters)& p)->double {
572 return p[0]->GetValue() < p[1]->GetValue() ? 1.0 : 0.0;
574 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
575 }
else if (operation ==
"le") {
576 auto f = [](
const decltype(Parameters)& p)->double {
577 return p[0]->GetValue() <= p[1]->GetValue() ? 1.0 : 0.0;
579 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
580 }
else if (operation ==
"gt") {
581 auto f = [](
const decltype(Parameters)& p)->double {
582 return p[0]->GetValue() > p[1]->GetValue() ? 1.0 : 0.0;
584 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
585 }
else if (operation ==
"ge") {
586 auto f = [](
const decltype(Parameters)& p)->double {
587 return p[0]->GetValue() >= p[1]->GetValue() ? 1.0 : 0.0;
589 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
590 }
else if (operation ==
"eq") {
591 auto f = [](
const decltype(Parameters)& p)->double {
592 return p[0]->GetValue() == p[1]->GetValue() ? 1.0 : 0.0;
594 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
595 }
else if (operation ==
"nq") {
596 auto f = [](
const decltype(Parameters)& p)->double {
597 return p[0]->GetValue() != p[1]->GetValue() ? 1.0 : 0.0;
599 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix, var));
600 }
else if (operation ==
"not") {
601 string ctxMsg = element->ReadFrom();
602 auto f = [ctxMsg](
const decltype(Parameters)& p)->double {
603 return GetBinary(p[0]->
GetValue(), ctxMsg) ? 0.0 : 1.0;
605 Parameters.push_back(
new aFunc<
decltype(f), 1>(f, fdmex, element, Prefix, var));
606 }
else if (operation ==
"ifthen") {
607 string ctxMsg = element->ReadFrom();
608 auto f = [ctxMsg](
const decltype(Parameters)& p)->double {
609 if (GetBinary(p[0]->
GetValue(), ctxMsg))
610 return p[1]->GetValue();
612 return p[2]->GetValue();
614 Parameters.push_back(
new aFunc<
decltype(f), 3>(f, fdmex, element, Prefix, var));
615 }
else if (operation ==
"random") {
618 string mean_attr = element->GetAttributeValue(
"mean");
619 string stddev_attr = element->GetAttributeValue(
"stddev");
620 if (!mean_attr.empty()) {
622 mean = atof_locale_c(mean_attr);
623 }
catch (InvalidNumber& e) {
624 XMLLogException err(element);
625 err << e.what() <<
"\n";
629 if (!stddev_attr.empty()) {
631 stddev = atof_locale_c(stddev_attr);
632 }
catch (InvalidNumber& e) {
633 XMLLogException err(element);
634 err << e.what() <<
"\n";
638 auto generator(makeRandomGenerator(element, fdmex));
639 auto f = [generator, mean, stddev]()->
double {
640 double value = generator->GetNormalRandomNumber();
641 return value*stddev + mean;
643 Parameters.push_back(
new aFunc<
decltype(f), 0>(f, PropertyManager, element,
645 }
else if (operation ==
"urandom") {
648 string lower_attr = element->GetAttributeValue(
"lower");
649 string upper_attr = element->GetAttributeValue(
"upper");
650 if (!lower_attr.empty()) {
652 lower = atof_locale_c(lower_attr);
653 }
catch (InvalidNumber &e) {
654 XMLLogException err(element);
655 err << e.what() <<
"\n";
659 if (!upper_attr.empty()) {
661 upper = atof_locale_c(upper_attr);
662 }
catch (InvalidNumber &e) {
663 XMLLogException err(element);
664 err << e.what() <<
"\n";
668 auto generator(makeRandomGenerator(element, fdmex));
669 double a = 0.5*(upper-lower);
670 double b = 0.5*(upper+lower);
671 auto f = [generator, a, b]()->
double {
672 double value = generator->GetUniformRandomNumber();
675 Parameters.push_back(
new aFunc<
decltype(f), 0>(f, PropertyManager, element,
677 }
else if (operation ==
"switch") {
678 string ctxMsg = element->ReadFrom();
679 auto f = [ctxMsg](
const decltype(Parameters)& p)->double {
680 double temp = p[0]->GetValue();
683 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
684 <<
"The switch function index (" << temp
685 <<
") is negative.\n" << LogFormat::RESET;
688 size_t n = p.size()-1;
689 size_t i =
static_cast<size_t>(temp+0.5);
692 return p[i+1]->GetValue();
695 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
696 <<
"The switch function index (" << temp
697 <<
") selected a value above the range of supplied values"
698 <<
"[0:" << n-1 <<
"]"
699 <<
" - not enough values were supplied.\n" << LogFormat::RESET;
703 Parameters.push_back(
new aFunc<
decltype(f), 2>(f, fdmex, element, Prefix,
705 }
else if (operation ==
"interpolate1d") {
706 auto f = [](
const decltype(Parameters)& p)->double {
710 double x = p[0]->GetValue();
711 double xmin = p[1]->GetValue();
712 double ymin = p[2]->GetValue();
713 if (x <= xmin)
return ymin;
715 double xmax = p[n-2]->GetValue();
716 double ymax = p[n-1]->GetValue();
717 if (x >= xmax)
return ymax;
720 size_t nmax = (n-3)/2;
721 while (nmax-nmin > 1) {
722 size_t m = (nmax-nmin)/2+nmin;
723 double xm = p[2*m+1]->GetValue();
724 double ym = p[2*m+2]->GetValue();
738 return ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin);
740 Parameters.push_back(
new aFunc<
decltype(f), 5>(f, fdmex, element, Prefix,
741 var, MaxArgs, OddEven::Odd));
742 }
else if (operation ==
"rotation_alpha_local") {
746 auto f = [](
const decltype(Parameters)& p)->double {
747 double alpha = p[0]->GetValue()*degtorad;
748 double beta = p[1]->GetValue()*degtorad;
749 double phi = p[3]->GetValue()*degtorad;
750 double theta = p[4]->GetValue()*degtorad;
751 double psi = p[5]->GetValue()*degtorad;
753 FGQuaternion qTb2l(phi, theta, psi);
754 double cos_beta = cos(beta);
755 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
756 sin(alpha)*cos_beta);
757 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
759 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
762 return atan2(wind_local(eZ), wind_local(eX))*radtodeg;
764 Parameters.push_back(
new aFunc<
decltype(f), 6>(f, fdmex, element, Prefix, var));
765 }
else if (operation ==
"rotation_beta_local") {
769 auto f = [](
const decltype(Parameters)& p)->double {
770 double alpha = p[0]->GetValue()*degtorad;
771 double beta = p[1]->GetValue()*degtorad;
772 double phi = p[3]->GetValue()*degtorad;
773 double theta = p[4]->GetValue()*degtorad;
774 double psi = p[5]->GetValue()*degtorad;
775 FGQuaternion qTb2l(phi, theta, psi);
776 double cos_beta = cos(beta);
777 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
778 sin(alpha)*cos_beta);
779 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
781 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
782 return wind_local(eY) > 0.0 ? 0.5*M_PI : -0.5*M_PI;
784 double alpha_local = atan2(wind_local(eZ), wind_local(eX));
785 double cosa = cos(alpha_local);
786 double sina = sin(alpha_local);
789 if (fabs(cosa) > fabs(sina))
790 cosb = wind_local(eX) / cosa;
792 cosb = wind_local(eZ) / sina;
794 return atan2(wind_local(eY), cosb)*radtodeg;
796 Parameters.push_back(
new aFunc<
decltype(f), 6>(f, fdmex, element, Prefix, var));
797 }
else if (operation ==
"rotation_gamma_local") {
801 auto f = [](
const decltype(Parameters)& p)->double {
802 double alpha = p[0]->GetValue()*degtorad;
803 double beta = p[1]->GetValue()*degtorad;
804 double gamma = p[2]->GetValue()*degtorad;
805 double phi = p[3]->GetValue()*degtorad;
806 double theta = p[4]->GetValue()*degtorad;
807 double psi = p[5]->GetValue()*degtorad;
808 double cos_alpha = cos(alpha), sin_alpha = sin(alpha);
809 double cos_beta = cos(beta), sin_beta = sin(beta);
810 double cos_gamma = cos(gamma), sin_gamma = sin(gamma);
811 FGQuaternion qTb2l(phi, theta, psi);
812 FGColumnVector3 wind_body_X(cos_alpha*cos_beta, sin_beta,
814 FGColumnVector3 wind_body_Y(-sin_alpha*sin_gamma-sin_beta*cos_alpha*cos_gamma,
816 -sin_alpha*sin_beta*cos_gamma+sin_gamma*cos_alpha);
817 FGColumnVector3 wind_local_X = qTb2l.GetT()*wind_body_X;
818 FGColumnVector3 wind_local_Y = qTb2l.GetT()*wind_body_Y;
819 double cosacosb = wind_local_X(eX);
820 double sinb = wind_local_X(eY);
821 double sinacosb = wind_local_X(eZ);
824 if (fabs(sinb) < 1E-9) {
825 cosc = wind_local_Y(eY);
827 if (fabs(cosacosb) > fabs(sinacosb))
828 sinc = wind_local_Y(eZ) / cosacosb;
830 sinc = -wind_local_Y(eX) / sinacosb;
832 else if (fabs(fabs(sinb)-1.0) < 1E-9) {
833 sinc = wind_local_Y(eZ);
834 cosc = -wind_local_Y(eX);
837 sinc = cosacosb*wind_local_Y(eZ)-sinacosb*wind_local_Y(eX);
838 cosc = (-sinacosb*wind_local_Y(eZ)-cosacosb*wind_local_Y(eX))/sinb;
841 return atan2(sinc, cosc)*radtodeg;
843 Parameters.push_back(
new aFunc<
decltype(f), 6>(f, fdmex, element, Prefix, var));
844 }
else if (operation ==
"rotation_bf_to_wf") {
847 string ctxMsg = element->ReadFrom();
848 auto f = [ctxMsg](
const decltype(Parameters)& p)->double {
849 double rx = p[0]->GetValue();
850 double ry = p[1]->GetValue();
851 double rz = p[2]->GetValue();
852 double alpha = p[3]->GetValue()*degtorad;
853 double beta = p[4]->GetValue()*degtorad;
854 double gamma = p[5]->GetValue()*degtorad;
855 int idx =
static_cast<int>(p[6]->GetValue());
857 if ((idx < 1) || (idx > 3)) {
859 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
860 <<
"The index must be one of the integer value 1, 2 or 3.\n"
865 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
866 FGMatrix33 mT = (qa*qb*qc).GetT();
867 FGColumnVector3 r0(rx, ry, rz);
868 FGColumnVector3 r = mT*r0;
872 Parameters.push_back(
new aFunc<
decltype(f), 7>(f, fdmex, element, Prefix, var));
873 }
else if (operation ==
"rotation_wf_to_bf") {
876 string ctxMsg = element->ReadFrom();
877 auto f = [ctxMsg](
const decltype(Parameters)& p)->double {
878 double rx = p[0]->GetValue();
879 double ry = p[1]->GetValue();
880 double rz = p[2]->GetValue();
881 double alpha = p[3]->GetValue()*degtorad;
882 double beta = p[4]->GetValue()*degtorad;
883 double gamma = p[5]->GetValue()*degtorad;
884 int idx =
static_cast<int>(p[6]->GetValue());
886 if ((idx < 1) || (idx > 3)) {
888 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
889 <<
"The index must be one of the integer value 1, 2 or 3.\n"
894 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
895 FGMatrix33 mT = (qa*qb*qc).GetT();
896 FGColumnVector3 r0(rx, ry, rz);
898 FGColumnVector3 r = mT*r0;
902 Parameters.push_back(
new aFunc<
decltype(f), 7>(f, fdmex, element, Prefix, var));
903 }
else if (operation !=
"description") {
904 FGXMLLogging log(element, LogLevel::ERROR);
905 log << LogFormat::RED << LogFormat::BOLD
906 <<
"Bad operation <" << operation
907 <<
"> detected in configuration file\n" << LogFormat::RESET;
912 if (!Parameters.empty()){
915 if (p && p->IsConstant()) {
916 double constant = p->GetValue();
917 SGPropertyNode_ptr node = p->pNode;
918 string pName = p->GetName();
919 unique_ptr<FGXMLLogging> log;
921 Parameters.pop_back();
922 Parameters.push_back(
new FGRealValue(constant));
925 log.reset(
new FGXMLLogging(element, LogLevel::DEBUG));
926 *log << LogFormat::GREEN << LogFormat::BOLD
927 <<
"<" << operation <<
"> is applied on constant parameters.\n"
928 <<
"It will be replaced by its result (" << constant <<
")";
932 node->setDoubleValue(constant);
933 node->setAttribute(SGPropertyNode::WRITE,
false);
935 *log <<
" and the property " << pName
936 <<
" will be unbound and made read only.";
939 if (debug_lvl > 0) *log << LogFormat::RESET <<
"\n\n";
942 element = el->GetNextElement();
954 if (pNode && pNode->isTied())
955 PropertyManager->Untie(pNode);
964 for (
auto p: Parameters) {
965 if (!p->IsConstant())
988 if (cached)
return cachedValue;
990 double val = Parameters[0]->GetValue();
992 if (pCopyTo) pCopyTo->setDoubleValue(val);
1001 ostringstream buffer;
1003 buffer << setw(9) << setprecision(6) <<
GetValue();
1004 return buffer.str();
1009string FGFunction::CreateOutputNode(
Element* el,
const string& Prefix)
1013 if ( !Name.empty() ) {
1015 nName = PropertyManager->mkPropertyName(Name,
false);
1017 if (is_number(Prefix)) {
1018 if (Name.find(
"#") != string::npos) {
1019 Name = replace(Name,
"#",Prefix);
1020 nName = PropertyManager->mkPropertyName(Name,
false);
1022 FGXMLLogging log(el, LogLevel::ERROR);
1023 log <<
"Malformed function name with number: " << Prefix
1024 <<
" and property name: " << Name
1025 <<
" but no \"#\" sign for substitution.\n";
1028 nName = PropertyManager->mkPropertyName(Prefix +
"/" + Name,
false);
1032 pNode = PropertyManager->GetNode(nName,
true);
1033 if (pNode->isTied()) {
1034 XMLLogException err(el);
1035 err <<
"Property " << nName <<
" has already been successfully bound (late).\n";
1045void FGFunction::bind(Element* el,
const string& Prefix)
1047 string nName = CreateOutputNode(el, Prefix);
1072void FGFunction::Debug(
int from)
1074 if (debug_lvl <= 0)
return;
1076 if (debug_lvl & 1) {
1078 if (!Name.empty()) {
1079 FGLogging log(LogLevel::DEBUG);
1080 log <<
" Function: " << Name << endl;
1084 if (debug_lvl & 2 ) {
1085 FGLogging log(LogLevel::DEBUG);
1086 if (from == 0) log <<
"Instantiated: FGFunction\n";
1087 if (from == 1) log <<
"Destroyed: FGFunction\n";
1089 if (debug_lvl & 4 ) {
1091 if (debug_lvl & 8 ) {
1093 if (debug_lvl & 16) {
1095 if (debug_lvl & 64) {
const std::string & GetName(void) const
Retrieves the element name.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
unsigned int GetNumElements(void)
Returns the number of child elements for this element.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
Encapsulates the JSBSim simulation executive.
std::shared_ptr< FGPropertyManager > GetPropertyManager(void) const
Returns a pointer to the property manager object.
Represents a mathematical function.
FGFunction()
Default constructor.
double GetValue(void) const override
Retrieves the value of the function object.
bool IsConstant(void) const override
Does the function always return the same result (i.e.
void cacheValue(bool shouldCache)
Specifies whether to cache the value of the function, so it is calculated only once per frame.
std::string GetValueAsString(void) const
The value that the function evaluates to, as a string.
~FGFunction(void) override
Destructor Make sure the function is untied before destruction.
static char fgred[6]
red text
static char reset[5]
resets text properties
static char highint[5]
highlights text
Represents various types of parameters.
Represents a property value which can use late binding.
Main namespace for the JSBSim Flight Dynamics Model.