36 #include "simgear/misc/strutils.hxx"
37 #include "FGFDMExec.h"
38 #include "FGFunction.h"
40 #include "FGRealValue.h"
41 #include "input_output/FGXMLElement.h"
42 #include "math/FGFunctionValue.h"
53 const double invlog2val = 1.0/log10(2.0);
54 constexpr
unsigned int MaxArgs = 9999;
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; }
69 const vector<FGParameter_ptr> Parameters;
75 template<
typename func_t,
unsigned int Nmin>
81 FGFunction::OddEven odd_even=FGFunction::OddEven::Either)
84 Load(el, v, fdmex, prefix);
85 CheckMinArguments(el, Nmin);
86 CheckMaxArguments(el, Nmax);
87 CheckOddOrEvenArguments(el, odd_even);
90 double GetValue(
void)
const override {
91 return cached ? cachedValue : f(Parameters);
95 void bind(
Element* el,
const string& Prefix)
override {
96 string nName = CreateOutputNode(el, Prefix);
108 template<
typename func_t>
113 const string& Prefix)
117 ostringstream buffer;
118 buffer << el->
ReadFrom() << fgred << highint
119 <<
"<" << el->
GetName() <<
"> should have no arguments." << reset
127 double GetValue(
void)
const override {
128 double result = cached ? cachedValue : f();
129 if (pNode) pNode->setDoubleValue(result);
134 bool IsConstant(
void)
const override {
142 void bind(
Element* el,
const string& Prefix)
override {
143 CreateOutputNode(el, Prefix);
145 if (pNode) pNode->setDoubleValue(f());
154 bool GetBinary(
double val,
const string &ctxMsg)
157 if (val < 1E-9)
return false;
158 else if (val-1 < 1E-9)
return true;
160 cerr << ctxMsg << FGJSBBase::fgred << FGJSBBase::highint
161 <<
"Malformed conditional check in function definition."
162 << FGJSBBase::reset << endl;
163 throw(
"Fatal Error.");
171 FGFunction* 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);
185 template<
typename func_t>
186 FGParameter_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 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();
212 Load(el, var, fdmex, prefix);
213 CheckMinArguments(el, 1);
214 CheckMaxArguments(el, 1);
218 if (!sCopyTo.empty()) {
219 if (sCopyTo.find(
"#") != string::npos) {
220 if (is_number(prefix))
221 sCopyTo = replace(sCopyTo,
"#",prefix);
224 <<
"Illegal use of the special character '#'" <<
reset << endl
225 <<
"The 'copyto' argument in function " << Name <<
" is ignored."
231 pCopyTo = PropertyManager->GetNode(sCopyTo);
234 <<
"Property \"" << sCopyTo
235 <<
"\" must be previously defined in function " << Name <<
reset
236 <<
"The 'copyto' argument is ignored." << endl;
242 void FGFunction::CheckMinArguments(
Element* el,
unsigned int _min)
244 if (Parameters.size() < _min) {
245 ostringstream buffer;
247 <<
"<" << el->
GetName() <<
"> should have at least " << _min
248 <<
" argument(s)." <<
reset << endl;
255 void FGFunction::CheckMaxArguments(Element* el,
unsigned int _max)
257 if (Parameters.size() > _max) {
258 ostringstream buffer;
260 <<
"<" << el->GetName() <<
"> should have no more than " << _max
261 <<
" argument(s)." <<
reset << endl;
262 throw WrongNumberOfArguments(buffer.str(), Parameters, el);
268 void FGFunction::CheckOddOrEvenArguments(Element* el, OddEven odd_even)
273 if (Parameters.size() % 2 == 1) {
275 <<
"<" << el->GetName() <<
"> must have an even number of arguments."
277 throw(
"Fatal Error");
281 if (Parameters.size() % 2 == 0) {
283 <<
"<" << el->GetName() <<
"> must have an odd number of arguments."
285 throw(
"Fatal Error");
295 shared_ptr<default_random_engine> makeRandomEngine(Element *el, FGFDMExec* fdmex)
297 string seed_attr = el->GetAttributeValue(
"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();
304 seed = atoi(seed_attr.c_str());
305 return make_shared<default_random_engine>(seed);
310 void FGFunction::Load(Element* el, FGPropertyValue* var, FGFDMExec* fdmex,
311 const string& Prefix)
313 Name = el->GetAttributeValue(
"name");
314 Element* element = el->GetElement();
316 auto sum = [](
const decltype(Parameters)& Parameters)->
double {
319 for (
auto p: Parameters)
320 temp += p->GetValue();
326 string operation = element->GetName();
329 if (operation ==
"property" || operation ==
"p") {
330 string property_name = element->GetDataLine();
332 if (var && simgear::strutils::strip(property_name) ==
"#")
333 Parameters.push_back(var);
335 if (property_name.find(
"#") != string::npos) {
336 if (is_number(Prefix)) {
337 property_name = replace(property_name,
"#",Prefix);
340 cerr << element->ReadFrom()
341 <<
fgred <<
"Illegal use of the special character '#'"
343 throw(
"Fatal Error.");
347 if (element->HasAttribute(
"apply")) {
348 string function_str = element->GetAttributeValue(
"apply");
349 FGTemplateFunc* f = fdmex->GetTemplateFunc(function_str);
351 Parameters.push_back(
new FGFunctionValue(property_name,
352 PropertyManager, f));
354 cerr << element->ReadFrom()
356 << function_str <<
" has been defined. This property will "
357 <<
"not be logged. You should check your configuration file."
362 Parameters.push_back(
new FGPropertyValue(property_name,
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));
372 }
else if (operation ==
"product") {
373 auto f = [](
const decltype(Parameters)& Parameters)->
double {
376 for (
auto p: Parameters)
377 temp *= p->GetValue();
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();
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();
393 for (
auto p = Parameters.begin()+1; p != Parameters.end(); ++p)
394 temp -= (*p)->GetValue();
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;
403 for (
auto p : Parameters) {
404 double x = p->GetValue();
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;
416 for (
auto p : Parameters) {
417 double x = p->GetValue();
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))
435 Parameters.push_back(
new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
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))
447 Parameters.push_back(
new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
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;
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 {
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.;
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;
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;
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;
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;
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;
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;
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;
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 {
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());
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 {
538 return modf(p[0]->
GetValue(), &scratch);
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 {
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;
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;
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;
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;
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;
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;
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;
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))
590 return p[2]->GetValue();
592 Parameters.push_back(
new aFunc<decltype(f), 3>(f, fdmex, element, Prefix, var));
593 }
else if (operation ==
"random") {
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);
607 Parameters.push_back(
new aFunc<decltype(f), 0>(f, PropertyManager, element,
609 }
else if (operation ==
"urandom") {
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);
623 Parameters.push_back(
new aFunc<decltype(f), 0>(f, PropertyManager, element,
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();
631 <<
"The switch function index (" << temp
632 <<
") is negative." <<
reset << endl;
633 throw(
"Fatal error");
635 size_t n = p.size()-1;
636 size_t i =
static_cast<size_t>(temp+0.5);
639 return p[i+1]->GetValue();
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");
649 Parameters.push_back(
new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
651 }
else if (operation ==
"interpolate1d") {
652 auto f = [](
const decltype(Parameters)& p)->
double {
656 double x = p[0]->GetValue();
657 double xmin = p[1]->GetValue();
658 double ymin = p[2]->GetValue();
659 if (x <= xmin)
return ymin;
661 double xmax = p[n-2]->GetValue();
662 double ymax = p[n-1]->GetValue();
663 if (x >= xmax)
return ymax;
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();
684 return ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin);
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") {
692 auto f = [](
const decltype(Parameters)& p)->
double {
693 double alpha = p[0]->GetValue()*degtorad;
694 double beta = p[1]->GetValue()*degtorad;
695 double phi = p[3]->GetValue()*degtorad;
696 double theta = p[4]->GetValue()*degtorad;
697 double psi = p[5]->GetValue()*degtorad;
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;
705 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
708 return atan2(wind_local(eZ), wind_local(eX))*radtodeg;
710 Parameters.push_back(
new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
711 }
else if (operation ==
"rotation_beta_local") {
715 auto f = [](
const decltype(Parameters)& p)->
double {
716 double alpha = p[0]->GetValue()*degtorad;
717 double beta = p[1]->GetValue()*degtorad;
718 double phi = p[3]->GetValue()*degtorad;
719 double theta = p[4]->GetValue()*degtorad;
720 double psi = p[5]->GetValue()*degtorad;
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;
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;
730 double alpha_local = atan2(wind_local(eZ), wind_local(eX));
731 double cosa = cos(alpha_local);
732 double sina = sin(alpha_local);
735 if (fabs(cosa) > fabs(sina))
736 cosb = wind_local(eX) / cosa;
738 cosb = wind_local(eZ) / sina;
740 return atan2(wind_local(eY), cosb)*radtodeg;
742 Parameters.push_back(
new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
743 }
else if (operation ==
"rotation_gamma_local") {
747 auto f = [](
const decltype(Parameters)& p)->
double {
748 double alpha = p[0]->GetValue()*degtorad;
749 double beta = p[1]->GetValue()*degtorad;
750 double gamma = p[2]->GetValue()*degtorad;
751 double phi = p[3]->GetValue()*degtorad;
752 double theta = p[4]->GetValue()*degtorad;
753 double psi = p[5]->GetValue()*degtorad;
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,
760 FGColumnVector3 wind_body_Y(-sin_alpha*sin_gamma-sin_beta*cos_alpha*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);
770 if (fabs(sinb) < 1E-9) {
771 cosc = wind_local_Y(eY);
773 if (fabs(cosacosb) > fabs(sinacosb))
774 sinc = wind_local_Y(eZ) / cosacosb;
776 sinc = -wind_local_Y(eX) / sinacosb;
778 else if (fabs(fabs(sinb)-1.0) < 1E-9) {
779 sinc = wind_local_Y(eZ);
780 cosc = -wind_local_Y(eX);
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;
787 return atan2(sinc, cosc)*radtodeg;
789 Parameters.push_back(
new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
790 }
else if (operation ==
"rotation_bf_to_wf") {
793 string ctxMsg = element->ReadFrom();
794 auto f = [ctxMsg](
const decltype(Parameters)& p)->
double {
795 double rx = p[0]->GetValue();
796 double ry = p[1]->GetValue();
797 double rz = p[2]->GetValue();
798 double alpha = p[3]->GetValue()*degtorad;
799 double beta = p[4]->GetValue()*degtorad;
800 double gamma = p[5]->GetValue()*degtorad;
801 int idx =
static_cast<int>(p[6]->GetValue());
803 if ((idx < 1) || (idx > 3)) {
805 <<
"The index must be one of the integer value 1, 2 or 3."
807 throw(
"Fatal error");
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;
817 Parameters.push_back(
new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
818 }
else if (operation ==
"rotation_wf_to_bf") {
821 string ctxMsg = element->ReadFrom();
822 auto f = [ctxMsg](
const decltype(Parameters)& p)->
double {
823 double rx = p[0]->GetValue();
824 double ry = p[1]->GetValue();
825 double rz = p[2]->GetValue();
826 double alpha = p[3]->GetValue()*degtorad;
827 double beta = p[4]->GetValue()*degtorad;
828 double gamma = p[5]->GetValue()*degtorad;
829 int idx =
static_cast<int>(p[6]->GetValue());
831 if ((idx < 1) || (idx > 3)) {
833 <<
"The index must be one of the integer value 1, 2 or 3."
835 throw(
"Fatal error");
838 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
839 FGMatrix33 mT = (qa*qb*qc).GetT();
840 FGColumnVector3 r0(rx, ry, rz);
842 FGColumnVector3 r = mT*r0;
846 Parameters.push_back(
new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
847 }
else if (operation !=
"description") {
849 <<
"Bad operation <" << operation
850 <<
"> detected in configuration file" <<
reset << endl;
855 if (!Parameters.empty()){
858 if (p && p->IsConstant()) {
859 double constant = p->GetValue();
860 FGPropertyNode_ptr node = p->pNode;
861 string pName = p->GetName();
863 Parameters.pop_back();
864 Parameters.push_back(
new FGRealValue(constant));
867 <<
"<" << operation <<
"> is applied on constant parameters."
868 << endl <<
"It will be replaced by its result ("
872 node->setDoubleValue(constant);
873 node->setAttribute(SGPropertyNode::WRITE,
false);
875 cout <<
" and the property " << pName
876 <<
" will be unbound and made read only.";
878 cout <<
reset << endl << endl;
881 element = el->GetNextElement();
893 if (pNode && pNode->isTied())
894 PropertyManager->
Untie(pNode);
903 for (
auto p: Parameters) {
904 if (!p->IsConstant())
927 if (cached)
return cachedValue;
929 double val = Parameters[0]->GetValue();
931 if (pCopyTo) pCopyTo->setDoubleValue(val);
940 ostringstream buffer;
942 buffer << setw(9) << setprecision(6) <<
GetValue();
948 string FGFunction::CreateOutputNode(
Element* el,
const string& Prefix)
952 if ( !Name.empty() ) {
956 if (is_number(Prefix)) {
957 if (Name.find(
"#") != string::npos) {
958 Name = replace(Name,
"#",Prefix);
962 <<
"Malformed function name with number: " << Prefix
963 <<
" and property name: " << Name
964 <<
" but no \"#\" sign for substitution." << endl;
967 nName = PropertyManager->
mkPropertyName(Prefix +
"/" + Name,
false);
971 pNode = PropertyManager->GetNode(nName,
true);
972 if (pNode->isTied()) {
974 <<
"Property " << nName <<
" has already been successfully bound (late)." << endl;
975 throw(
"Failed to bind the property to an existing already tied node.");
984 void FGFunction::bind(Element* el,
const string& Prefix)
986 string nName = CreateOutputNode(el, Prefix);
1011 void FGFunction::Debug(
int from)
1013 if (debug_lvl <= 0)
return;
1015 if (debug_lvl & 1) {
1018 cout <<
" Function: " << Name << endl;
1021 if (debug_lvl & 2 ) {
1022 if (from == 0) cout <<
"Instantiated: FGFunction" << endl;
1023 if (from == 1) cout <<
"Destroyed: FGFunction" << endl;
1025 if (debug_lvl & 4 ) {
1027 if (debug_lvl & 8 ) {
1029 if (debug_lvl & 16) {
1031 if (debug_lvl & 64) {