JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGFunction.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3 Module: FGFunction.cpp
4 Author: Jon Berndt
5 Date started: 8/25/2004
6 Purpose: 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 Software
12  Foundation; either version 2 of the License, or (at your option) any later
13  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 with
21  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
22  Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24  Further information about the GNU Lesser General Public License can also be found on
25  the world wide web at http://www.gnu.org.
26 
27 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
28 INCLUDES
29 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
30 
31 #include <sstream>
32 #include <iomanip>
33 #include <cstdlib>
34 #include <cmath>
35 
36 #include "FGFunction.h"
37 #include "FGTable.h"
38 #include "FGPropertyValue.h"
39 #include "FGRealValue.h"
40 #include "input_output/FGXMLElement.h"
41 
42 using namespace std;
43 
44 namespace JSBSim {
45 
46 IDENT(IdSrc,"$Id: FGFunction.cpp,v 1.58 2015/07/12 19:34:08 bcoconni Exp $");
47 IDENT(IdHdr,ID_FUNCTION);
48 
49 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50 CLASS IMPLEMENTATION
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
52 
53 const std::string FGFunction::property_string = "property";
54 const std::string FGFunction::value_string = "value";
55 const std::string FGFunction::table_string = "table";
56 const std::string FGFunction::p_string = "p";
57 const std::string FGFunction::v_string = "v";
58 const std::string FGFunction::t_string = "t";
59 
60 const std::string FGFunction::function_string = "function";
61 const std::string FGFunction::description_string = "description";
62 const std::string FGFunction::sum_string = "sum";
63 const std::string FGFunction::difference_string = "difference";
64 const std::string FGFunction::product_string = "product";
65 const std::string FGFunction::quotient_string = "quotient";
66 const std::string FGFunction::pow_string = "pow";
67 const std::string FGFunction::sqrt_string = "sqrt";
68 const std::string FGFunction::toradians_string = "toradians";
69 const std::string FGFunction::todegrees_string = "todegrees";
70 const std::string FGFunction::exp_string = "exp";
71 const std::string FGFunction::log2_string = "log2";
72 const std::string FGFunction::ln_string = "ln";
73 const std::string FGFunction::log10_string = "log10";
74 const std::string FGFunction::abs_string = "abs";
75 const std::string FGFunction::sign_string = "sign";
76 const std::string FGFunction::sin_string = "sin";
77 const std::string FGFunction::cos_string = "cos";
78 const std::string FGFunction::tan_string = "tan";
79 const std::string FGFunction::asin_string = "asin";
80 const std::string FGFunction::acos_string = "acos";
81 const std::string FGFunction::atan_string = "atan";
82 const std::string FGFunction::atan2_string = "atan2";
83 const std::string FGFunction::min_string = "min";
84 const std::string FGFunction::max_string = "max";
85 const std::string FGFunction::avg_string = "avg";
86 const std::string FGFunction::fraction_string = "fraction";
87 const std::string FGFunction::mod_string = "mod";
88 const std::string FGFunction::random_string = "random";
89 const std::string FGFunction::urandom_string = "urandom";
90 const std::string FGFunction::pi_string = "pi";
91 const std::string FGFunction::integer_string = "integer";
92 const std::string FGFunction::rotation_alpha_local_string = "rotation_alpha_local";
93 const std::string FGFunction::rotation_beta_local_string = "rotation_beta_local";
94 const std::string FGFunction::rotation_gamma_local_string = "rotation_gamma_local";
95 const std::string FGFunction::rotation_bf_to_wf_string = "rotation_bf_to_wf";
96 const std::string FGFunction::rotation_wf_to_bf_string = "rotation_wf_to_bf";
97 
98 const std::string FGFunction::lessthan_string = "lt";
99 const std::string FGFunction::lessequal_string = "le";
100 const std::string FGFunction::greatthan_string = "gt";
101 const std::string FGFunction::greatequal_string = "ge";
102 const std::string FGFunction::equal_string = "eq";
103 const std::string FGFunction::notequal_string = "nq";
104 const std::string FGFunction::and_string = "and";
105 const std::string FGFunction::or_string = "or";
106 const std::string FGFunction::not_string = "not";
107 const std::string FGFunction::ifthen_string = "ifthen";
108 const std::string FGFunction::switch_string = "switch";
109 const std::string FGFunction::interpolate1d_string = "interpolate1d";
110 
111 FGFunction::FGFunction(FGPropertyManager* propMan, Element* el, const string& prefix)
112  : PropertyManager(propMan), Prefix(prefix)
113 {
114  Element* element;
115  string operation, property_name;
116  cached = false;
117  cachedValue = -HUGE_VAL;
118  invlog2val = 1.0/log10(2.0);
119  pCopyTo = 0L;
120 
121  Name = el->GetAttributeValue("name");
122  operation = el->GetName();
123 
124  if (operation == function_string) {
125  sCopyTo = el->GetAttributeValue("copyto");
126  if (!sCopyTo.empty()) {
127 
128  if (sCopyTo.find("#") != string::npos) {
129  if (is_number(Prefix)) sCopyTo = replace(sCopyTo,"#",Prefix);
130  }
131 
132  pCopyTo = PropertyManager->GetNode(sCopyTo);
133  if (pCopyTo == 0L) cerr << "Property \"" << sCopyTo << "\" must be previously defined in function "
134  << Name << endl;
135  }
136  Type = eTopLevel;
137  } else if (operation == product_string) {
138  Type = eProduct;
139  } else if (operation == difference_string) {
140  Type = eDifference;
141  } else if (operation == sum_string) {
142  Type = eSum;
143  } else if (operation == quotient_string) {
144  Type = eQuotient;
145  } else if (operation == pow_string) {
146  Type = ePow;
147  } else if (operation == sqrt_string) {
148  Type = eSqrt;
149  } else if (operation == toradians_string) {
150  Type = eToRadians;
151  } else if (operation == todegrees_string) {
152  Type = eToDegrees;
153  } else if (operation == log2_string) {
154  Type = eLog2;
155  } else if (operation == ln_string) {
156  Type = eLn;
157  } else if (operation == log10_string) {
158  Type = eLog10;
159  } else if (operation == abs_string) {
160  Type = eAbs;
161  } else if (operation == sign_string) {
162  Type = eSign;
163  } else if (operation == sin_string) {
164  Type = eSin;
165  } else if (operation == exp_string) {
166  Type = eExp;
167  } else if (operation == cos_string) {
168  Type = eCos;
169  } else if (operation == tan_string) {
170  Type = eTan;
171  } else if (operation == asin_string) {
172  Type = eASin;
173  } else if (operation == acos_string) {
174  Type = eACos;
175  } else if (operation == atan_string) {
176  Type = eATan;
177  } else if (operation == atan2_string) {
178  Type = eATan2;
179  } else if (operation == min_string) {
180  Type = eMin;
181  } else if (operation == max_string) {
182  Type = eMax;
183  } else if (operation == avg_string) {
184  Type = eAvg;
185  } else if (operation == fraction_string) {
186  Type = eFrac;
187  } else if (operation == integer_string) {
188  Type = eInteger;
189  } else if (operation == mod_string) {
190  Type = eMod;
191  } else if (operation == random_string) {
192  Type = eRandom;
193  } else if (operation == urandom_string) {
194  Type = eUrandom;
195  } else if (operation == pi_string) {
196  Type = ePi;
197  } else if (operation == rotation_alpha_local_string) {
198  Type = eRotation_alpha_local;
199  } else if (operation == rotation_beta_local_string) {
200  Type = eRotation_beta_local;
201  } else if (operation == rotation_gamma_local_string) {
202  Type = eRotation_gamma_local;
203  } else if (operation == rotation_bf_to_wf_string) {
204  Type = eRotation_bf_to_wf;
205  } else if (operation == rotation_wf_to_bf_string) {
206  Type = eRotation_wf_to_bf;
207  } else if (operation == lessthan_string) {
208  Type = eLT;
209  } else if (operation == lessequal_string) {
210  Type = eLE;
211  } else if (operation == greatthan_string) {
212  Type = eGT;
213  } else if (operation == greatequal_string) {
214  Type = eGE;
215  } else if (operation == equal_string) {
216  Type = eEQ;
217  } else if (operation == notequal_string) {
218  Type = eNE;
219  } else if (operation == and_string) {
220  Type = eAND;
221  } else if (operation == or_string) {
222  Type = eOR;
223  } else if (operation == not_string) {
224  Type = eNOT;
225  } else if (operation == ifthen_string) {
226  Type = eIfThen;
227  } else if (operation == switch_string) {
228  Type = eSwitch;
229  } else if (operation == interpolate1d_string) {
230  Type = eInterpolate1D;
231  } else if (operation != description_string) {
232  cerr << "Bad operation " << operation << " detected in configuration file" << endl;
233  }
234 
235  element = el->GetElement();
236  if (!element && Type != eRandom && Type != eUrandom && Type != ePi) {
237  cerr << fgred << highint << endl;
238  cerr << " No element was specified as an argument to the \"" << operation << "\" operation" << endl;
239  cerr << " This can happen when, for instance, a cos operation is specified and a " << endl;
240  cerr << " property name is given explicitly, but is not placed within a" << endl;
241  cerr << " <property></property> element tag pair." << endl;
242  cerr << reset;
243  exit(-2);
244  }
245 
246  while (element) {
247  operation = element->GetName();
248 
249  // data types
250  if (operation == property_string || operation == p_string) {
251  property_name = element->GetDataLine();
252  if (property_name.find("#") != string::npos) {
253  if (is_number(Prefix)) {
254  property_name = replace(property_name,"#",Prefix);
255  }
256  }
257  if (PropertyManager->HasNode(property_name)) {
258  FGPropertyNode* newNode = PropertyManager->GetNode(property_name);
259  Parameters.push_back(new FGPropertyValue( newNode ));
260  } else {
261  // cerr << fgcyan << "Warning: The property " + property_name + " is initially undefined."
262  // << reset << endl;
263  Parameters.push_back(new FGPropertyValue( property_name,
264  PropertyManager ));
265  }
266  } else if (operation == value_string || operation == v_string) {
267  Parameters.push_back(new FGRealValue(element->GetDataAsNumber()));
268  } else if (operation == table_string || operation == t_string) {
269  Parameters.push_back(new FGTable(PropertyManager, element));
270  // operations
271  } else if (operation == product_string ||
272  operation == difference_string ||
273  operation == sum_string ||
274  operation == quotient_string ||
275  operation == pow_string ||
276  operation == sqrt_string ||
277  operation == toradians_string ||
278  operation == todegrees_string ||
279  operation == exp_string ||
280  operation == log2_string ||
281  operation == ln_string ||
282  operation == log10_string ||
283  operation == abs_string ||
284  operation == sign_string ||
285  operation == sin_string ||
286  operation == cos_string ||
287  operation == tan_string ||
288  operation == asin_string ||
289  operation == acos_string ||
290  operation == atan_string ||
291  operation == atan2_string ||
292  operation == min_string ||
293  operation == max_string ||
294  operation == fraction_string ||
295  operation == integer_string ||
296  operation == mod_string ||
297  operation == random_string ||
298  operation == urandom_string ||
299  operation == pi_string ||
300  operation == avg_string ||
301  operation == rotation_alpha_local_string||
302  operation == rotation_beta_local_string||
303  operation == rotation_gamma_local_string||
304  operation == rotation_bf_to_wf_string||
305  operation == rotation_wf_to_bf_string ||
306  operation == lessthan_string ||
307  operation == lessequal_string ||
308  operation == greatthan_string ||
309  operation == greatequal_string ||
310  operation == equal_string ||
311  operation == notequal_string ||
312  operation == and_string ||
313  operation == or_string ||
314  operation == not_string ||
315  operation == ifthen_string ||
316  operation == switch_string ||
317  operation == interpolate1d_string)
318  {
319  Parameters.push_back(new FGFunction(PropertyManager, element, Prefix));
320  } else if (operation != description_string) {
321  cerr << "Bad operation " << operation << " detected in configuration file" << endl;
322  }
323  element = el->GetNextElement();
324  }
325 
326  bind(); // Allow any function to save its value
327 
328  Debug(0);
329 }
330 
331 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332 
334 {
335  for (unsigned int i=0; i<Parameters.size(); i++) delete Parameters[i];
336 }
337 
338 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 
340 void FGFunction::cacheValue(bool cache)
341 {
342  cached = false; // Must set cached to false prior to calling GetValue(), else
343  // it will _never_ calculate the value;
344  if (cache) {
345  cachedValue = GetValue();
346  cached = true;
347  }
348 }
349 
350 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351 
352 unsigned int FGFunction::GetBinary(double val) const
353 {
354  val = fabs(val);
355  if (val < 1E-9) return 0;
356  else if (val-1 < 1E-9) return 1;
357  else {
358  throw("Malformed conditional check in function definition.");
359  }
360 }
361 
362 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 
364 double FGFunction::GetValue(void) const
365 {
366  unsigned int i;
367  double scratch;
368  double temp=0;
369 
370  if (cached) return cachedValue;
371 
372  if ( Type != eRandom
373  && Type != eUrandom
374  && Type != ePi ) temp = Parameters[0]->GetValue();
375 
376  switch (Type) {
377  case eTopLevel:
378  if (pCopyTo) pCopyTo->setDoubleValue(temp);
379  break;
380  case eProduct:
381  for (i=1;i<Parameters.size();i++) {
382  temp *= Parameters[i]->GetValue();
383  }
384  break;
385  case eDifference:
386  for (i=1;i<Parameters.size();i++) {
387  temp -= Parameters[i]->GetValue();
388  }
389  break;
390  case eSum:
391  for (i=1;i<Parameters.size();i++) {
392  temp += Parameters[i]->GetValue();
393  }
394  break;
395  case eQuotient:
396  if (Parameters[1]->GetValue() != 0.0)
397  temp /= Parameters[1]->GetValue();
398  else
399  temp = HUGE_VAL;
400  break;
401  case ePow:
402  temp = pow(temp,Parameters[1]->GetValue());
403  break;
404  case eSqrt:
405  temp = sqrt(temp);
406  break;
407  case eToRadians:
408  temp *= M_PI/180.0;
409  break;
410  case eToDegrees:
411  temp *= 180.0/M_PI;
412  break;
413  case eExp:
414  temp = exp(temp);
415  break;
416  case eLog2:
417  if (temp > 0.00) temp = log10(temp)*invlog2val;
418  else temp = -HUGE_VAL;
419  break;
420  case eLn:
421  if (temp > 0.00) temp = log(temp);
422  else temp = -HUGE_VAL;
423  break;
424  case eLog10:
425  if (temp > 0.00) temp = log10(temp);
426  else temp = -HUGE_VAL;
427  break;
428  case eAbs:
429  temp = fabs(temp);
430  break;
431  case eSign:
432  temp = temp < 0 ? -1:1; // 0.0 counts as positive.
433  break;
434  case eSin:
435  temp = sin(temp);
436  break;
437  case eCos:
438  temp = cos(temp);
439  break;
440  case eTan:
441  temp = tan(temp);
442  break;
443  case eACos:
444  temp = acos(temp);
445  break;
446  case eASin:
447  temp = asin(temp);
448  break;
449  case eATan:
450  temp = atan(temp);
451  break;
452  case eATan2:
453  temp = atan2(temp, Parameters[1]->GetValue());
454  break;
455  case eMod:
456  temp = ((int)temp) % ((int) Parameters[1]->GetValue());
457  break;
458  case eMin:
459  for (i=1;i<Parameters.size();i++) {
460  if (Parameters[i]->GetValue() < temp) temp = Parameters[i]->GetValue();
461  }
462  break;
463  case eMax:
464  for (i=1;i<Parameters.size();i++) {
465  if (Parameters[i]->GetValue() > temp) temp = Parameters[i]->GetValue();
466  }
467  break;
468  case eAvg:
469  for (i=1;i<Parameters.size();i++) {
470  temp += Parameters[i]->GetValue();
471  }
472  temp /= Parameters.size();
473  break;
474  case eFrac:
475  temp = modf(temp, &scratch);
476  break;
477  case eInteger:
478  modf(temp, &scratch);
479  temp = scratch;
480  break;
481  case eRandom:
482  temp = GaussianRandomNumber();
483  break;
484  case eUrandom:
485  temp = -1.0 + (((double)rand()/double(RAND_MAX))*2.0);
486  break;
487  case ePi:
488  temp = M_PI;
489  break;
490  case eLT:
491  temp = (temp < Parameters[1]->GetValue())?1:0;
492  break;
493  case eLE:
494  temp = (temp <= Parameters[1]->GetValue())?1:0;
495  break;
496  case eGT:
497  temp = (temp > Parameters[1]->GetValue())?1:0;
498  break;
499  case eGE:
500  temp = (temp >= Parameters[1]->GetValue())?1:0;
501  break;
502  case eEQ:
503  temp = (temp == Parameters[1]->GetValue())?1:0;
504  break;
505  case eNE:
506  temp = (temp != Parameters[1]->GetValue())?1:0;
507  break;
508  case eAND:
509  {
510  bool flag = (GetBinary(temp) != 0u);
511  for (i=1; i<Parameters.size() && flag; i++) {
512  flag = (GetBinary(Parameters[i]->GetValue()) != 0);
513  }
514  temp = flag ? 1 : 0;
515  }
516  break;
517  case eOR:
518  {
519  bool flag = (GetBinary(temp) != 0);
520  for (i=1; i<Parameters.size() && !flag; i++) {
521  flag = (GetBinary(Parameters[i]->GetValue()) != 0);
522  }
523  temp = flag ? 1 : 0;
524  }
525  break;
526  case eNOT:
527  temp = (GetBinary(temp) != 0) ? 0 : 1;
528  break;
529  case eIfThen:
530  {
531  i = Parameters.size();
532  if (i == 3) {
533  if (GetBinary(temp) == 1) {
534  temp = Parameters[1]->GetValue();
535  } else {
536  temp = Parameters[2]->GetValue();
537  }
538  } else {
539  throw("Malformed if/then function statement");
540  }
541  }
542  break;
543  case eSwitch:
544  {
545  size_t n = Parameters.size()-1;
546  i = int(temp+0.5);
547  if (i < n) {
548  temp = Parameters[i+1]->GetValue();
549  } else {
550  throw(string("The switch function index selected a value above the range of supplied values"
551  " - not enough values were supplied."));
552  }
553  }
554  break;
555  case eInterpolate1D:
556  {
557  size_t sz = Parameters.size();
558  if (temp <= Parameters[1]->GetValue()) {
559  temp = Parameters[2]->GetValue();
560  } else if (temp >= Parameters[sz-2]->GetValue()) {
561  temp = Parameters[sz-1]->GetValue();
562  } else {
563  for (unsigned int i=1; i<=sz-4; i+=2) {
564  if (temp < Parameters[i+2]->GetValue()) {
565  double factor = (temp - Parameters[i]->GetValue()) /
566  (Parameters[i+2]->GetValue() - Parameters[i]->GetValue());
567  double span = Parameters[i+3]->GetValue() - Parameters[i+1]->GetValue();
568  double val = factor*span;
569  temp = Parameters[i+1]->GetValue() + val;
570  break;
571  }
572  }
573  }
574  }
575  break;
576  case eRotation_alpha_local:
577  if (Parameters.size()==6) // calculates local angle of attack for skydiver body component
578  //Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
579  {
580  double alpha = Parameters[0]->GetValue()*degtorad;
581  double beta = Parameters[1]->GetValue()*degtorad;
582  double gamma = Parameters[2]->GetValue()*degtorad;
583  double phi = Parameters[3]->GetValue()*degtorad;
584  double theta = Parameters[4]->GetValue()*degtorad;
585  double psi = Parameters[5]->GetValue()*degtorad;
586  double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
587  double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
588  double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
589  double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
590  double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
591  //calculate local body frame to the intermediate body frame rotation quaternion
592  double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
593  double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
594  double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
595  double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
596  //calculate the intermediate body frame to global wind frame rotation quaternion
597  double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
598  double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
599  double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
600  double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
601  //multiply quaternions
602  double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
603  double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
604  double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
605  double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
606  //calculate alpha_local
607  temp = -atan2(2*(Cy*Ct-Cx*Cz),(Ct*Ct+Cx*Cx-Cy*Cy-Cz*Cz));
608  temp *= radtodeg;
609  } else {
610  temp = 1;
611  }
612  break;
613  case eRotation_beta_local:
614  if (Parameters.size()==6) // calculates local angle of sideslip for skydiver body component
615  //Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
616  {
617  double alpha = Parameters[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
618  double beta = Parameters[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
619  double gamma = Parameters[2]->GetValue()*degtorad; //roll angle of intermediate body frame
620  double phi = Parameters[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
621  double theta = Parameters[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
622  double psi = Parameters[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
623  double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
624  double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
625  double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
626  double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
627  double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
628  //calculate local body frame to the intermediate body frame rotation quaternion
629  double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
630  double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
631  double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
632  double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
633  //calculate the intermediate body frame to global wind frame rotation quaternion
634  double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
635  double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
636  double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
637  double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
638  //multiply quaternions
639  double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
640  double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
641  double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
642  double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
643  //calculate beta_local
644  temp = asin(2*(Cx*Cy+Cz*Ct));
645  temp *= radtodeg;
646  }
647  else //
648  {temp = 1;}
649  break;
650  case eRotation_gamma_local:
651  if (Parameters.size()==6) // calculates local angle of attack for skydiver body component
652  //Euler angles from the intermediate body frame to the local body frame must be from a z-y-x axis rotation order
653  {
654  double alpha = Parameters[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
655  double beta = Parameters[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
656  double gamma = Parameters[2]->GetValue()*degtorad; //roll angle of intermediate body frame
657  double phi = Parameters[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
658  double theta = Parameters[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
659  double psi = Parameters[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
660  double cphi2 = cos(-phi/2), ctht2 = cos(-theta/2), cpsi2 = cos(-psi/2);
661  double sphi2 = sin(-phi/2), stht2 = sin(-theta/2), spsi2 = sin(-psi/2);
662  double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
663  double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
664  double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
665  //calculate local body frame to the intermediate body frame rotation quaternion
666  double At = cphi2*ctht2*cpsi2 - sphi2*stht2*spsi2;
667  double Ax = cphi2*stht2*spsi2 + sphi2*ctht2*cpsi2;
668  double Ay = cphi2*stht2*cpsi2 - sphi2*ctht2*spsi2;
669  double Az = cphi2*ctht2*spsi2 + sphi2*stht2*cpsi2;
670  //calculate the intermediate body frame to global wind frame rotation quaternion
671  double Bt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
672  double Bx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
673  double By = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
674  double Bz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
675  //multiply quaternions
676  double Ct = At*Bt - Ax*Bx - Ay*By - Az*Bz;
677  double Cx = At*Bx + Ax*Bt + Ay*Bz - Az*By;
678  double Cy = At*By - Ax*Bz + Ay*Bt + Az*Bx;
679  double Cz = At*Bz + Ax*By - Ay*Bx + Az*Bt;
680  //calculate local roll anlge
681  temp = -atan2(2*(Cx*Ct-Cz*Cy),(Ct*Ct-Cx*Cx+Cy*Cy-Cz*Cz));
682  temp *= radtodeg;
683  }
684  else //
685  {temp = 1;}
686  break;
687  case eRotation_bf_to_wf:
688  if (Parameters.size()==7) // transforms the input vector from a body frame to a wind frame. The origin of the vector remains the same.
689  {
690  double rx = Parameters[0]->GetValue(); //x component of input vector
691  double ry = Parameters[1]->GetValue(); //y component of input vector
692  double rz = Parameters[2]->GetValue(); //z component of input vector
693  double alpha = Parameters[3]->GetValue()*degtorad; //angle of attack of the body frame
694  double beta = Parameters[4]->GetValue()*degtorad; //sideslip angle of the body frame
695  double gamma = Parameters[5]->GetValue()*degtorad; //roll angle of the body frame
696  double index = Parameters[6]->GetValue();
697  double calpha2 = cos(-alpha/2), salpha2 = sin(-alpha/2);
698  double cbeta2 = cos(beta/2), sbeta2 = sin(beta/2);
699  double cgamma2 = cos(-gamma/2), sgamma2 = sin(-gamma/2);
700  //calculate the body frame to wind frame quaternion
701  double qt = calpha2*cbeta2*cgamma2 - salpha2*sbeta2*sgamma2;
702  double qx = calpha2*cbeta2*sgamma2 + salpha2*sbeta2*cgamma2;
703  double qy = calpha2*sbeta2*sgamma2 + salpha2*cbeta2*cgamma2;
704  double qz = calpha2*sbeta2*cgamma2 - salpha2*cbeta2*sgamma2;
705  //calculate the quaternion conjugate
706  double qstart = qt;
707  double qstarx = -qx;
708  double qstary = -qy;
709  double qstarz = -qz;
710  //multiply quaternions v*q
711  double vqt = -rx*qx - ry*qy - rz*qz;
712  double vqx = rx*qt + ry*qz - rz*qy;
713  double vqy = -rx*qz + ry*qt + rz*qx;
714  double vqz = rx*qy - ry*qx + rz*qt;
715  //multiply quaternions qstar*vq
716  double Cx = qstart*vqx + qstarx*vqt + qstary*vqz - qstarz*vqy;
717  double Cy = qstart*vqy - qstarx*vqz + qstary*vqt + qstarz*vqx;
718  double Cz = qstart*vqz + qstarx*vqy - qstary*vqx + qstarz*vqt;
719 
720  if (index == 1) temp = Cx;
721  else if (index ==2) temp = Cy;
722  else temp = Cz;
723  }
724  else //
725  {temp = 1;}
726  break;
727  case eRotation_wf_to_bf:
728  if (Parameters.size()==7) // transforms the input vector from q wind frame to a body frame. The origin of the vector remains the same.
729  {
730  double rx = Parameters[0]->GetValue(); //x component of input vector
731  double ry = Parameters[1]->GetValue(); //y component of input vector
732  double rz = Parameters[2]->GetValue(); //z component of input vector
733  double alpha = Parameters[3]->GetValue()*degtorad; //angle of attack of the body frame
734  double beta = Parameters[4]->GetValue()*degtorad; //sideslip angle of the body frame
735  double gamma = Parameters[5]->GetValue()*degtorad; //roll angle of the body frame
736  double index = Parameters[6]->GetValue();
737  double calpha2 = cos(alpha/2), salpha2 = sin(alpha/2);
738  double cbeta2 = cos(-beta/2), sbeta2 = sin(-beta/2);
739  double cgamma2 = cos(gamma/2), sgamma2 = sin(gamma/2);
740  //calculate the wind frame to body frame quaternion
741  double qt = cgamma2*cbeta2*calpha2 + sgamma2*sbeta2*salpha2;
742  double qx = -cgamma2*sbeta2*salpha2 + sgamma2*cbeta2*calpha2;
743  double qy = cgamma2*cbeta2*salpha2 - sgamma2*sbeta2*calpha2;
744  double qz = cgamma2*sbeta2*calpha2 + sgamma2*cbeta2*salpha2;
745  //calculate the quaternion conjugate
746  double qstart = qt;
747  double qstarx = -qx;
748  double qstary = -qy;
749  double qstarz = -qz;
750  //multiply quaternions v*q
751  double vqt = -rx*qx - ry*qy - rz*qz;
752  double vqx = rx*qt + ry*qz - rz*qy;
753  double vqy = -rx*qz + ry*qt + rz*qx;
754  double vqz = rx*qy - ry*qx + rz*qt;
755  //multiply quaternions qstar*vq
756  double Cx = qstart*vqx + qstarx*vqt + qstary*vqz - qstarz*vqy;
757  double Cy = qstart*vqy - qstarx*vqz + qstary*vqt + qstarz*vqx;
758  double Cz = qstart*vqz + qstarx*vqy - qstary*vqx + qstarz*vqt;
759 
760  if (index == 1) temp = Cx;
761  else if (index ==2) temp = Cy;
762  else temp = Cz;
763  }
764  else //
765  {temp = 1;}
766  break;
767  default:
768  cerr << "Unknown function operation type" << endl;
769  break;
770  }
771 
772  return temp;
773 }
774 
775 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
776 
778 {
779  ostringstream buffer;
780 
781  buffer << setw(9) << setprecision(6) << GetValue();
782  return buffer.str();
783 }
784 
785 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
786 
787 void FGFunction::bind(void)
788 {
789  if ( !Name.empty() ) {
790  string tmp;
791  if (Prefix.empty())
792  tmp = PropertyManager->mkPropertyName(Name, false);
793  else {
794  if (is_number(Prefix)) {
795  if (Name.find("#") != string::npos) { // if "#" is found
796  Name = replace(Name,"#",Prefix);
797  tmp = PropertyManager->mkPropertyName(Name, false);
798  } else {
799  cerr << "Malformed function name with number: " << Prefix
800  << " and property name: " << Name
801  << " but no \"#\" sign for substitution." << endl;
802  }
803  } else {
804  tmp = PropertyManager->mkPropertyName(Prefix + "/" + Name, false);
805  }
806  }
807 
808  if (PropertyManager->HasNode(tmp)) {
809  FGPropertyNode* _property = PropertyManager->GetNode(tmp);
810  if (_property->isTied()) {
811  cout << "Property " << tmp << " has already been successfully bound (late)." << endl;
812  throw("Failed to bind the property to an existing already tied node.");
813  }
814  }
815  PropertyManager->Tie( tmp, this, &FGFunction::GetValue);
816  }
817 }
818 
819 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820 // The bitmasked value choices are as follows:
821 // unset: In this case (the default) JSBSim would only print
822 // out the normally expected messages, essentially echoing
823 // the config files as they are read. If the environment
824 // variable is not set, debug_lvl is set to 1 internally
825 // 0: This requests JSBSim not to output any messages
826 // whatsoever.
827 // 1: This value explicity requests the normal JSBSim
828 // startup messages
829 // 2: This value asks for a message to be printed out when
830 // a class is instantiated
831 // 4: When this value is set, a message is displayed when a
832 // FGModel object executes its Run() method
833 // 8: When this value is set, various runtime state variables
834 // are printed out periodically
835 // 16: When set various parameters are sanity checked and
836 // a message is printed out when they go out of bounds
837 
838 void FGFunction::Debug(int from)
839 {
840  if (debug_lvl <= 0) return;
841 
842  if (debug_lvl & 1) { // Standard console startup message output
843  if (from == 0) { // Constructor
844  if (Type == eTopLevel)
845  cout << " Function: " << Name << endl;
846  }
847  }
848  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
849  if (from == 0) cout << "Instantiated: FGFunction" << endl;
850  if (from == 1) cout << "Destroyed: FGFunction" << endl;
851  }
852  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
853  }
854  if (debug_lvl & 8 ) { // Runtime state variables
855  }
856  if (debug_lvl & 16) { // Sanity checking
857  }
858  if (debug_lvl & 64) {
859  if (from == 0) { // Constructor
860  cout << IdSrc << endl;
861  cout << IdHdr << endl;
862  }
863  }
864 }
865 
866 }
virtual ~FGFunction()
Destructor.
Definition: FGFunction.cpp:333
std::string GetValueAsString(void) const
The value that the function evaluates to, as a string.
Definition: FGFunction.cpp:777
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
std::string mkPropertyName(std::string name, bool lowercase)
Property-ify a name replaces spaces with &#39;-&#39; and, optionally, makes name all lower case...
Represents a property value which can use late binding.
Class wrapper for property handling.
static char reset[5]
resets text properties
Definition: FGJSBBase.h:131
STL namespace.
Represents a real value.
Definition: FGRealValue.h:63
double GetValue(void) const
Retrieves the value of the function object.
Definition: FGFunction.cpp:364
void cacheValue(bool shouldCache)
Specifies whether to cache the value of the function, so it is calculated only once per frame...
Definition: FGFunction.cpp:340
static char fgred[6]
red text
Definition: FGJSBBase.h:141
void Tie(const std::string &name, bool *pointer, bool useDefault=true)
Tie a property to an external bool variable.
double GetDataAsNumber(void)
Converts the element data to a number.
const std::string & GetName(void) const
Retrieves the element name.
Definition: FGXMLElement.h:186
std::string GetDataLine(unsigned int i=0)
Gets a line of data belonging to an element.
static char highint[5]
highlights text
Definition: FGJSBBase.h:125
Element * GetElement(unsigned int el=0)
Returns a pointer to the element requested by index.
FGFunction(FGPropertyManager *PropertyManager, Element *element, const std::string &prefix="")
Constructor.
Definition: FGFunction.cpp:111
Lookup table class.
Definition: FGTable.h:243
Element * GetNextElement(void)
Returns a pointer to the next element in the list.