JSBSim Flight Dynamics Model 1.3.3 (16 Oct 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGFunction Class Reference

Detailed Description

Represents a mathematical function.

The FGFunction class is a powerful and versatile resource that allows algebraic functions to be defined in a JSBSim configuration file. It is similar in concept to MathML (Mathematical Markup Language, www.w3.org/Math/), but simpler and more terse. A function definition consists of an operation, a value, a table, or a property (which evaluates to a value). The currently supported operations are:

  • sum (takes n args)
  • difference (takes n args)
  • product (takes n args)
  • quotient (takes 2 args)
  • pow (takes 2 args)
  • sqrt (takes one argument)
  • toradians (takes one argument)
  • todegrees (takes one argument)
  • exp (takes 2 args)
  • log2 (takes 1 arg)
  • ln (takes 1 arg)
  • log10 (takes 1 arg)
  • abs (takes 1 arg)
  • sin (takes 1 arg)
  • cos (takes 1 arg)
  • tan (takes 1 arg)
  • asin (takes 1 arg)
  • acos (takes 1 arg)
  • atan (takes 1 arg)
  • atan2 (takes 2 args)
  • min (takes n args)
  • max (takes n args)
  • avg (takes n args)
  • fraction
  • mod
  • floor (takes 1 arg)
  • ceil (takes 1 arg)
  • fmod (takes 2 args)
  • roundmultiple (takes 2 args)
  • lt (less than, takes 2 args)
  • le (less equal, takes 2 args)
  • gt (greater than, takes 2 args)
  • ge (greater than, takes 2 args)
  • eq (equal, takes 2 args)
  • nq (not equal, takes 2 args)
  • and (takes n args)
  • or (takes n args)
  • not (takes 1 args)
  • if-then (takes 2-3 args)
  • switch (takes 2 or more args)
  • random (Gaussian distribution random number)
  • urandom (Uniform random number between -1 and +1)
  • pi
  • integer
  • interpolate 1-dimensional (takes a minimum of five arguments, odd number)

An operation is defined in the configuration file as in the following example:

<sum>
<value> 3.14159 </value>
<property> velocities/qbar </property>
<product>
<value> 0.125 </value>
<property> metrics/wingarea </property>
</product>
</sum>

A full function definition, such as is used in the aerodynamics section of a configuration file includes the function element, and other elements. It should be noted that there can be only one non-optional (non-documentation) element - that is, one operation element or one table element - in the top-level function definition. Multiple value and/or property elements cannot be immediate child members of the function element. Almost always, the first operation within the function element will be a product or sum. For example:

<function name="aero/moment/Clr">
<description>Roll moment due to yaw rate</description>
<product>
<property>aero/qbar-area</property>
<property>metrics/bw-ft</property>
<property>aero/bi2vel</property>
<property>velocities/r-aero-rad_sec</property>
<table>
<independentVar>aero/alpha-rad</independentVar>
<tableData>
0.000 0.08
0.094 0.19
</tableData>
</table>
</product>
</function>

The "lowest level" in a function is always a value or a property, which cannot itself contain another element. As shown, operations can contain values, properties, tables, or other operations. In the first above example, the sum element contains all three. What is evaluated is written algebraically as:

3.14159 + qbar + (0.125 * wingarea)

Some operations can take only a single argument. That argument, however, can be an operation (such as sum) which can contain other items. The point to keep in mind is that it evaluates to a single value - which is just what the trigonometric functions require (except atan2, which takes two arguments).

Specific Function Definitions

Note: In the definitions below, a "property" refers to a single property specified within either the <property></property> tag or the shortcut tag, <p></p>. The keyword "value" refers to a single numeric value specified either within the <value></value> tag or the shortcut <v></v> tag. The keyword "table" refers to a single table specified either within the <table></table> tag or the shortcut <t></t> tag. The plural form of any of the three words refers to one or more instances of a property, value, or table.

  • sum, sums the values of all immediate child elements:
    <sum>
    {properties, values, tables, or other function elements}
    </sum>
    Example: Mach + 0.01
    <sum>
    <p> velocities/mach </p>
    <v> 0.01 </v>
    </sum>
  • difference, subtracts the values of all immediate child elements from the value of the first child element:
    <difference>
    {properties, values, tables, or other function elements}
    </difference>
    Example: Mach - 0.01
    <difference>
    <p> velocities/mach </p>
    <v> 0.01 </v>
    </difference>
  • product multiplies together the values of all immediate child elements:
    <product>
    {properties, values, tables, or other function elements}
    </product>
    Example: qbar*S*beta*CY_beta
    <product>
    <property> aero/qbar-psf </property>
    <property> metrics/Sw-sqft </property>
    <property> aero/beta-rad </property>
    <property> aero/coefficient/CY_beta </property>
    </product>
  • quotient, divides the value of the first immediate child element by the second immediate child element:
    <quotient>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </quotient>
    Example: (2*GM)/R
    <quotient>
    <product>
    <v> 2.0 </v>
    <p> guidance/executive/gm </p>
    </product>
    <p> position/radius-to-vehicle-ft </p>
    </quotient>
  • pow, raises the value of the first immediate child element to the power of the value of the second immediate child element:
    <pow>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </pow>
    Example: Mach^2
    <pow>
    <p> velocities/mach </p>
    <v> 2.0 </v>
    </pow>
  • sqrt, takes the square root of the value of the immediate child element:
    <sqrt>
    {property, value, table, or other function element}
    </sqrt>
    Example: square root of 25
    <sqrt> <v> 25.0 </v> </sqrt>
  • toradians, converts a presumed argument in degrees to radians by multiplying the value of the immediate child element by pi/180:
    <toradians>
    {property, value, table, or other function element}
    </toradians>
    Example: convert 45 degrees to radians
    <toradians> <v> 45 </v> </toradians>
  • todegrees, converts a presumed argument in radians to degrees by multiplying the value of the immediate child element by 180/pi:
    <todegrees>
    {property, value, table, or other function element}
    </todegrees>
    Example: convert 0.5*pi radians to degrees
    <todegrees>
    <product> <v> 0.5 </v> <pi/> </product>
    </todegrees>
  • exp, raises "e" to the power of the immediate child element:
    <exp>
    {property, value, table, or other function element}
    </exp>
    Example: raise "e" to the 1.5 power, e^1.5
    <exp> <v> 1.5 </v> </exp>
  • log2, calculates the log base 2 value of the immediate child element:
    <log2>
    {property, value, table, or other function element}
    </log2>
    Example:
    <log2> <v> 128 </v> </log2>
  • ln, calculates the natural logarithm of the value of the immediate child element:
    <ln>
    {property, value, table, or other function element}
    </ln>
    Example: ln(128)
    <ln> <v> 200 </v> </ln>
  • log10, calculates the base 10 logarithm of the value of the immediate child element
    <log10>
    {property, value, table, or other function element}
    </log10>
    Example log(Mach)
    <log10> <p> velocities/mach </p> </log10>
  • abs calculates the absolute value of the immediate child element
    <abs>
    {property, value, table, or other function element}
    </abs>
    Example:
    <abs> <p> flight-path/gamma-rad </p> </abs>
  • sin, calculates the sine of the value of the immediate child element (the argument is expected to be in radians)
    <sin>
    {property, value, table, or other function element}
    </sin>
    Example:
    <sin> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </sin>
  • cos, calculates the cosine of the value of the immediate child element (the argument is expected to be in radians)
    <cos>
    {property, value, table, or other function element}
    </cos>
    Example:
    <cos> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </cos>
  • tan, calculates the tangent of the value of the immediate child element (the argument is expected to be in radians)
    <tan>
    {property, value, table, or other function element}
    </tan>
    Example:
    <tan> <toradians> <p> fcs/heading-true-degrees </p> </toradians> </tan>
  • asin, calculates the arcsine (inverse sine) of the value of the immediate child element. The value provided should be in the range from -1 to +1. The value returned will be expressed in radians, and will be in the range from -pi/2 to +pi/2.
    <asin>
    {property, value, table, or other function element}
    </asin>
    Example:
    <asin> <v> 0.5 </v> </asin>
  • acos, calculates the arccosine (inverse cosine) of the value of the immediate child element. The value provided should be in the range from -1 to +1. The value returned will be expressed in radians, and will be in the range from 0 to pi.
    <acos>
    {property, value, table, or other function element}
    </acos>
    Example:
    <acos> <v> 0.5 </v> </acos>
  • atan, calculates the inverse tangent of the value of the immediate child element. The value returned will be expressed in radians, and will be in the range from -pi/2 to +pi/2.
    <atan>
    {property, value, table, or other function element}
    </atan>
    Example:
    <atan> <v> 0.5 </v> </atan>
  • atan2 calculates the inverse tangent of the value of the immediate child elements, Y/X (in that order). It even works for X values near zero. The value returned will be expressed in radians, and in the range -pi to +pi.
    <atan2>
    {property, value, table, or other function element} {property, value, table, or other function element}
    </atan2>
    Example: inverse tangent of 0.5/0.25, evaluates to: 1.107 radians
    <atan2> <v> 0.5 </<v> <v> 0.25 </v> </atan2>
  • min returns the smallest value from all the immediate child elements
    <min>
    {properties, values, tables, or other function elements}
    </min>
    Example: returns the lesser of velocity and 2500
    <min>
    <p> velocities/eci-velocity-mag-fps </p>
    <v> 2500.0 </v>
    </min>
  • max returns the largest value from all the immediate child elements
    <max>
    {properties, values, tables, or other function elements}
    </max>
    Example: returns the greater of velocity and 15000
    <max>
    <p> velocities/eci-velocity-mag-fps </p>
    <v> 15000.0 </v>
    </max>
  • avg returns the average value of all the immediate child elements
    <avg>
    {properties, values, tables, or other function elements}
    </avg>
    Example: returns the average of the four numbers below, evaluates to 0.50.
    <avg>
    <v> 0.25 </v>
    <v> 0.50 </v>
    <v> 0.75 </v>
    <v> 0.50 </v>
    </avg>
  • fraction, returns the fractional part of the value of the immediate child element
    <fraction>
    {property, value, table, or other function element}
    </fraction>
    Example: returns the fractional part of pi - or, roughly, 0.1415926...
    <fraction> <pi/> </fraction>
  • integer, returns the integer portion of the value of the immediate child element
    <integer>
    {property, value, table, or other function element}
    </integer>
  • mod returns the remainder from the integer division of the value of the first immediate child element by the second immediate child element, X/Y (X modulo Y). The value returned is the value X-I*Y, for the largest integer I such that if Y is nonzero, the result has the same sign as X and magnitude less than the magnitude of Y. For instance, the expression "5 mod 2" would evaluate to 1 because 5 divided by 2 leaves a quotient of 2 and a remainder of 1, while "9 mod 3" would evaluate to 0 because the division of 9 by 3 has a quotient of 3 and leaves a remainder of 0.
    <mod>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </mod>
    Example: 5 mod 2, evaluates to 1
    <mod> <v> 5 </v> <v> 2 </v> </mod>
  • floor returns the largest integral value that is not greater than X.
    <floor>
    {property, value, table, or other function element}
    </floor>
    Examples: floor(2.3) evaluates to 2.0 while floor(-2.3) evaluates to -3.0
  • ceil returns the smallest integral value that is not less than X.
    <ceil>
    {property, value, table, or other function element}
    </ceil>
    Examples: ceil(2.3) evaluates to 3.0 while ceil(-2.3) evaluates to -2.0
  • fmod returns the floating-point remainder of X/Y (rounded towards zero)
    <fmod>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </fmod>
    Example: fmod(18.5, 4.2) evaluates to 1.7
  • roundmultiple returns the floating-point rounding of X to a multiple of M. round(X/M) * M
    <roundmultiple>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </roundmultiple>
    Example: roundmultiple(93.43, 5.0) evaluates to 95.0
  • lt returns a 1 if the value of the first immediate child element is less than the value of the second immediate child element, returns 0 otherwise
    <lt>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </lt>
    Example: returns 1 if thrust is less than 10,000, returns 0 otherwise
    <lt>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 10000.0 </v>
    </lt>
  • le, returns a 1 if the value of the first immediate child element is less than or equal to the value of the second immediate child element, returns 0 otherwise
    <le>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </le>
    Example: returns 1 if thrust is less than or equal to 10,000, returns 0 otherwise
    <le>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 10000.0 </v>
    </le>
  • gt returns a 1 if the value of the first immediate child element is greater than the value of the second immediate child element, returns 0 otherwise
    <gt>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </gt>
    Example: returns 1 if thrust is greater than 10,000, returns 0 otherwise
    <gt>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 10000.0 </v>
    </gt>
  • ge, returns a 1 if the value of the first immediate child element is greater than or equal to the value of the second immediate child element, returns 0 otherwise
    <ge>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </ge>
    Example: returns 1 if thrust is greater than or equal to 10,000, returns 0
    otherwise
    <ge>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 10000.0 </v>
    </ge>
  • eq returns a 1 if the value of the first immediate child element is equal to the second immediate child element, returns 0 otherwise
    <eq>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </eq>
    Example: returns 1 if thrust is equal to 10,000, returns 0 otherwise
    <eq>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 10000.0 </v>
    </eq>
  • nq returns a 1 if the value of the first immediate child element is not equal to the value of the second immediate child element, returns 0 otherwise
    <nq>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </nq>
    Example: returns 1 if thrust is not 0, returns 0 otherwise
    <nq>
    <p> propulsion/engine[2]/thrust-lbs </p>
    <v> 0.0 </v>
    </nq>
  • and returns a 1 if the values of the immediate child elements are all 1, returns 0 otherwise. Values provided are expected to be either 1 or 0 within machine precision.
    <and>
    {properties, values, tables, or other function elements}
    </and>
    Example: returns 1 if the specified flags are all 1
    <and>
    <p> guidance/first-stage-flight-flag </p>
    <p> control/engines-running-flag </p>
    </and>
  • or returns a 1 if the values of any of the immediate child elements 1, returns 0 otherwise. Values provided are expected to be either 1 or 0 within machine precision.
    <or>
    {properties, values, tables, or other function elements}
    </or>
    Example: returns 1 if any of the specified flags are 1
    <or>
    <p> guidance/first-stage-flight-flag </p>
    <p> control/engines-running-flag </p>
    </or>
  • not, returns the inverse of the value of the supplied immediate child element (e.g., returns 1 if supplied a 0)
    <not>
    {property, value, table, or other function element}
    </not>
    Example: returns 0 if the value of the supplied flag is 1
    <not> <p> guidance/first-stage-flight-flag </p> </not>
  • ifthen if the value of the first immediate child element is 1, then the value of the second immediate child element is returned, otherwise the value of the third child element is returned
    <ifthen>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    </ifthen>
    Example: if flight-mode is greater than 2, then a value of 0.00 is
    returned, otherwise the value of the property control/pitch-lag is
    returned.
    <ifthen>
    <gt> <p> executive/flight-mode </p> <v> 2 </v> </gt>
    <v> 0.00 </v>
    <p> control/pitch-lag </p>
    </ifthen>
  • switch uses the integer value of the first immediate child element as an index to select one of the subsequent immediate child elements to return the value of
    <switch>
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    {property, value, table, or other function element}
    ...
    </switch>
    Example: if flight-mode is 2, the switch function returns 0.50
    <switch>
    <p> executive/flight-mode </p>
    <v> 0.25 </v>
    <v> 0.50 </v>
    <v> 0.75 </v>
    <v> 1.00 </v>
    </switch>
  • random Returns a normal distributed random number. The function, without parameters, returns a normal distributed random value with a distribution defined by the parameters mean = 0.0 and standard deviation (stddev) = 1.0 The Mean of the distribution (its expected value, μ). Which coincides with the location of its peak. Standard deviation (σ): The square root of variance, representing the dispersion of values from the distribution mean. This shall be a positive value (σ>0).
    <random/>
    <random seed="1234"/>
    <random seed="time_now"/>
    <random seed="time_now" mean="0.0" stddev="1.0"/>
  • urandom Returns a uniformly distributed random number. The function, without parameters, returns a random value between the minimum value -1.0 and the maximum value of 1.0 The two maximum and minimum values can be modified using the lower and upper parameters.
    <urandom/>
    <random seed="1234"/>
    <random seed="time_now"/>
    <random seed="time_now" lower="-1.0" upper="1.0"/>
  • pi Takes no argument and returns the value of Pi
    <pi/>
  • interpolate1d returns the result from a 1-dimensional interpolation of the supplied values, with the value of the first immediate child element representing the lookup value into the table, and the following pairs of values representing the independent and dependent values. The first provided child element is expected to be a property. The interpolation does not extrapolate, but holds the highest value if the provided lookup value goes outside of the provided range.
    <interpolate1d>
    {property, value, table, or other function element}
    {property, value, table, or other function element} {property, value, table, or other function element}
    ...
    </interpolate1d>
    Example: If mach is 0.4, the interpolation will return 0.375. If mach is
    1.5, the interpolation will return 0.60.
    <interpolate1d>
    <p> velocities/mach </p>
    <v> 0.00 </v> <v> 0.25 </v>
    <v> 0.80 </v> <v> 0.50 </v>
    <v> 0.90 </v> <v> 0.60 </v>
    </interpolate1d>
    Author
    Jon Berndt

Definition at line 764 of file FGFunction.h.

#include <FGFunction.h>

+ Inheritance diagram for FGFunction:
+ Collaboration diagram for FGFunction:

Public Types

enum class  OddEven { Either , Odd , Even }
 
- Public Types inherited from FGJSBBase
enum  { eL = 1 , eM , eN }
 Moments L, M, N. More...
 
enum  { eP = 1 , eQ , eR }
 Rates P, Q, R. More...
 
enum  { eU = 1 , eV , eW }
 Velocities U, V, W. More...
 
enum  { eX = 1 , eY , eZ }
 Positions X, Y, Z. More...
 
enum  { ePhi = 1 , eTht , ePsi }
 Euler angles Phi, Theta, Psi. More...
 
enum  { eDrag = 1 , eSide , eLift }
 Stability axis forces, Drag, Side force, Lift. More...
 
enum  { eRoll = 1 , ePitch , eYaw }
 Local frame orientation Roll, Pitch, Yaw. More...
 
enum  { eNorth = 1 , eEast , eDown }
 Local frame position North, East, Down. More...
 
enum  { eLat = 1 , eLong , eRad }
 Locations Radius, Latitude, Longitude. More...
 
enum  {
  inNone = 0 , inDegrees , inRadians , inMeters ,
  inFeet
}
 Conversion specifiers. More...
 

Public Member Functions

 FGFunction ()
 Default constructor.
 
 FGFunction (FGFDMExec *fdmex, Element *element, const std::string &prefix="", FGPropertyValue *var=0L)
 Constructor.
 
 FGFunction (std::shared_ptr< FGPropertyManager > pm)
 
 ~FGFunction (void) override
 Destructor Make sure the function is untied before destruction.
 
void cacheValue (bool shouldCache)
 Specifies whether to cache the value of the function, so it is calculated only once per frame.
 
std::string GetName (void) const override
 Retrieves the name of the function.
 
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.
 
bool IsConstant (void) const override
 Does the function always return the same result (i.e.
 
- Public Member Functions inherited from FGParameter
double getDoubleValue (void) const
 
- Public Member Functions inherited from FGJSBBase
 FGJSBBase ()
 Constructor for FGJSBBase.
 
virtual ~FGJSBBase ()
 Destructor for FGJSBBase.
 

Protected Member Functions

virtual void bind (Element *, const std::string &)
 
void CheckMaxArguments (Element *el, unsigned int _max)
 
void CheckMinArguments (Element *el, unsigned int _min)
 
void CheckOddOrEvenArguments (Element *el, OddEven odd_even)
 
std::string CreateOutputNode (Element *el, const std::string &Prefix)
 
void Load (Element *element, FGPropertyValue *var, FGFDMExec *fdmex, const std::string &prefix="")
 

Protected Attributes

bool cached
 
double cachedValue
 
std::vector< FGParameter_ptr > Parameters
 
SGPropertyNode_ptr pNode
 
std::shared_ptr< FGPropertyManager > PropertyManager
 

Additional Inherited Members

- Static Public Member Functions inherited from FGJSBBase
static constexpr double CelsiusToFahrenheit (double celsius)
 Converts from degrees Celsius to degrees Fahrenheit.
 
static constexpr double CelsiusToKelvin (double celsius)
 Converts from degrees Celsius to degrees Kelvin.
 
static constexpr double CelsiusToRankine (double celsius)
 Converts from degrees Celsius to degrees Rankine.
 
static constexpr double Constrain (double min, double value, double max)
 Constrain a value between a minimum and a maximum value.
 
static bool EqualToRoundoff (double a, double b)
 Finite precision comparison.
 
static bool EqualToRoundoff (double a, float b)
 Finite precision comparison.
 
static bool EqualToRoundoff (float a, double b)
 Finite precision comparison.
 
static bool EqualToRoundoff (float a, float b)
 Finite precision comparison.
 
static constexpr double FahrenheitToCelsius (double fahrenheit)
 Converts from degrees Fahrenheit to degrees Celsius.
 
static constexpr double FeetToMeters (double measure)
 Converts from feet to meters.
 
static const std::string & GetVersion (void)
 Returns the version number of JSBSim.
 
static constexpr double KelvinToCelsius (double kelvin)
 Converts from degrees Kelvin to degrees Celsius.
 
static constexpr double KelvinToFahrenheit (double kelvin)
 Converts from degrees Kelvin to degrees Fahrenheit.
 
static constexpr double KelvinToRankine (double kelvin)
 Converts from degrees Kelvin to degrees Rankine.
 
static constexpr double RankineToCelsius (double rankine)
 Converts from degrees Rankine to degrees Celsius.
 
static constexpr double RankineToKelvin (double rankine)
 Converts from degrees Rankine to degrees Kelvin.
 
static constexpr double sign (double num)
 
- Static Public Attributes inherited from FGJSBBase
static short debug_lvl = 1
 
- Static Protected Member Functions inherited from FGJSBBase
static std::string CreateIndexedPropertyName (const std::string &Property, int index)
 
- Static Protected Attributes inherited from FGJSBBase
static constexpr double degtorad = M_PI / 180.
 
static constexpr double fpstokts = 1.0 / ktstofps
 
static constexpr double fttom = 0.3048
 
static constexpr double hptoftlbssec = 550.0
 
static constexpr double in3tom3 = inchtoft*inchtoft*inchtoft/m3toft3
 
static constexpr double inchtoft = 1.0/12.0
 
static constexpr double inhgtopa = 3386.38
 
static const std::string JSBSim_version = JSBSIM_VERSION " " __DATE__ " " __TIME__
 
static constexpr double kgtolb = 2.20462
 
static constexpr double kgtoslug = 0.06852168
 
static constexpr double ktstofps = 1852./(3600*fttom)
 
static constexpr double lbtoslug = 1.0/slugtolb
 
static constexpr double m3toft3 = 1.0/(fttom*fttom*fttom)
 
static const std::string needed_cfg_version = "2.0"
 
static constexpr double psftoinhg = 0.014138
 
static constexpr double psftopa = 47.88
 
static constexpr double radtodeg = 180. / M_PI
 
static constexpr double slugtolb = 32.174049
 Note that definition of lbtoslug by the inverse of slugtolb and not to a different constant you can also get from some tables will make lbtoslug*slugtolb == 1 up to the magnitude of roundoff.
 

Member Enumeration Documentation

◆ OddEven

enum class OddEven
strong

Definition at line 825 of file FGFunction.h.

825{Either, Odd, Even};

Constructor & Destructor Documentation

◆ FGFunction() [1/3]

FGFunction ( )
inline

Default constructor.

Definition at line 768 of file FGFunction.h.

769 : cached(false), cachedValue(-HUGE_VAL), pNode(nullptr), pCopyTo(nullptr) {}

◆ FGFunction() [2/3]

FGFunction ( std::shared_ptr< FGPropertyManager >  pm)
inlineexplicit

Definition at line 771 of file FGFunction.h.

772 : FGFunction()
773 { PropertyManager = pm; }
FGFunction()
Default constructor.
Definition FGFunction.h:768

◆ FGFunction() [3/3]

FGFunction ( FGFDMExec *  fdmex,
Element *  element,
const std::string &  prefix = "",
FGPropertyValue *  var = 0L 
)

Constructor.

When this constructor is called, the XML element pointed to in memory by the element argument is traversed. If other FGParameter-derived objects (values, functions, properties, or tables) are encountered, this instance of the FGFunction object will store a pointer to the found object and pass the relevant Element pointer to the constructor for the new object. In other words, each FGFunction object maintains a list of "child" FGParameter-derived objects which in turn may each contain its own list, and so on. At runtime, each object evaluates its child parameters, which each may have its own child parameters to evaluate.

Parameters
PropertyManagera pointer to the property manager instance.
elementa pointer to the Element object containing the function definition.
prefixan optional prefix to prepend to the name given to the property that represents this function (if given).

Definition at line 207 of file FGFunction.cpp.

209 : FGFunction(fdmex->GetPropertyManager())
210{
211 Load(el, var, fdmex, prefix);
212 CheckMinArguments(el, 1);
213 CheckMaxArguments(el, 1);
214
215 string sCopyTo = el->GetAttributeValue("copyto");
216
217 if (!sCopyTo.empty()) {
218 if (sCopyTo.find("#") != string::npos) {
219 if (is_number(prefix))
220 sCopyTo = replace(sCopyTo,"#",prefix);
221 else {
222 FGXMLLogging log(el, LogLevel::ERROR);
223 log << LogFormat::RED
224 << "Illegal use of the special character '#'\n" << LogFormat::RESET
225 << "The 'copyto' argument in function " << Name << " is ignored.\n";
226 return;
227 }
228 }
229
230 pCopyTo = PropertyManager->GetNode(sCopyTo);
231 if (!pCopyTo) {
232 FGXMLLogging log(el, LogLevel::ERROR);
233 log << LogFormat::RED
234 << "Property \"" << sCopyTo
235 << "\" must be previously defined in function " << Name << LogFormat::RESET
236 << "The 'copyto' argument is ignored.\n";
237 }
238 }
239}
+ Here is the call graph for this function:

◆ ~FGFunction()

~FGFunction ( void  )
override

Destructor Make sure the function is untied before destruction.

Definition at line 950 of file FGFunction.cpp.

951{
952 if (pNode && pNode->isTied())
953 PropertyManager->Untie(pNode);
954
955 Debug(1);
956}

Member Function Documentation

◆ bind()

void bind ( Element *  el,
const std::string &  Prefix 
)
protectedvirtual

Definition at line 1043 of file FGFunction.cpp.

1044{
1045 string nName = CreateOutputNode(el, Prefix);
1046
1047 if (!nName.empty())
1048 PropertyManager->Tie(nName, this, &FGFunction::GetValue);
1049}
double GetValue(void) const override
Retrieves the value of the function object.

◆ cacheValue()

void cacheValue ( bool  shouldCache)

Specifies whether to cache the value of the function, so it is calculated only once per frame.

If shouldCache is true, then the value of the function is calculated, and a flag is set so further calculations done this frame will use the cached value. In order to turn off caching, cacheValue must be called with a false argument.

Parameters
shouldCachespecifies whether the function should cache the computed value.

Definition at line 972 of file FGFunction.cpp.

973{
974 cached = false; // Must set cached to false prior to calling GetValue(), else
975 // it will _never_ calculate the value;
976 if (cache) {
977 cachedValue = GetValue();
978 cached = true;
979 }
980}
+ Here is the call graph for this function:

◆ CheckMaxArguments()

void CheckMaxArguments ( Element *  el,
unsigned int  _max 
)
protected

Definition at line 256 of file FGFunction.cpp.

257{
258 if (Parameters.size() > _max) {
259 WrongNumberOfArguments err(Parameters, el);
260 err << LogFormat::RED << LogFormat::BOLD
261 << "<" << el->GetName() << "> should have no more than " << _max
262 << " argument(s).\n" << LogFormat::RESET;
263 throw err;
264 }
265}

◆ CheckMinArguments()

void CheckMinArguments ( Element *  el,
unsigned int  _min 
)
protected

Definition at line 243 of file FGFunction.cpp.

244{
245 if (Parameters.size() < _min) {
246 WrongNumberOfArguments err(Parameters, el);
247 err << LogFormat::RED << LogFormat::BOLD
248 << "<" << el->GetName() << "> should have at least " << _min
249 << " argument(s).\n" << LogFormat::RESET;
250 throw err;
251 }
252}

◆ CheckOddOrEvenArguments()

void CheckOddOrEvenArguments ( Element *  el,
OddEven  odd_even 
)
protected

Definition at line 269 of file FGFunction.cpp.

270{
271
272 switch(odd_even) {
273 case OddEven::Even:
274 if (Parameters.size() % 2 == 1) {
275 XMLLogException err(el);
276 err << LogFormat::RED << LogFormat::BOLD
277 << "<" << el->GetName() << "> must have an even number of arguments.\n"
278 << LogFormat::RESET;
279 throw err;
280 }
281 break;
282 case OddEven::Odd:
283 if (Parameters.size() % 2 == 0) {
284 XMLLogException err(el);
285 err << LogFormat::RED << LogFormat::BOLD
286 << "<" << el->GetName() << "> must have an odd number of arguments.\n"
287 << LogFormat::RESET;
288 throw err;
289 }
290 break;
291 default:
292 break;
293 }
294}

◆ CreateOutputNode()

string CreateOutputNode ( Element *  el,
const std::string &  Prefix 
)
protected

Definition at line 1007 of file FGFunction.cpp.

1008{
1009 string nName;
1010
1011 if ( !Name.empty() ) {
1012 if (Prefix.empty())
1013 nName = PropertyManager->mkPropertyName(Name, false);
1014 else {
1015 if (is_number(Prefix)) {
1016 if (Name.find("#") != string::npos) { // if "#" is found
1017 Name = replace(Name,"#",Prefix);
1018 nName = PropertyManager->mkPropertyName(Name, false);
1019 } else {
1020 FGXMLLogging log(el, LogLevel::ERROR);
1021 log << "Malformed function name with number: " << Prefix
1022 << " and property name: " << Name
1023 << " but no \"#\" sign for substitution.\n";
1024 }
1025 } else {
1026 nName = PropertyManager->mkPropertyName(Prefix + "/" + Name, false);
1027 }
1028 }
1029
1030 pNode = PropertyManager->GetNode(nName, true);
1031 if (pNode->isTied()) {
1032 XMLLogException err(el);
1033 err << "Property " << nName << " has already been successfully bound (late).\n";
1034 throw err;
1035 }
1036 }
1037
1038 return nName;
1039}

◆ GetName()

std::string GetName ( void  ) const
inlineoverridevirtual

Retrieves the name of the function.

Implements FGParameter.

Definition at line 809 of file FGFunction.h.

809{return Name;}

◆ GetValue()

double GetValue ( void  ) const
overridevirtual

Retrieves the value of the function object.

Returns
the total value of the function.

Implements FGParameter.

Definition at line 984 of file FGFunction.cpp.

985{
986 if (cached) return cachedValue;
987
988 double val = Parameters[0]->GetValue();
989
990 if (pCopyTo) pCopyTo->setDoubleValue(val);
991
992 return val;
993}
+ Here is the caller graph for this function:

◆ GetValueAsString()

string GetValueAsString ( void  ) const

The value that the function evaluates to, as a string.

Returns
the value of the function as a string.

Definition at line 997 of file FGFunction.cpp.

998{
999 ostringstream buffer;
1000
1001 buffer << setw(9) << setprecision(6) << GetValue();
1002 return buffer.str();
1003}
+ Here is the call graph for this function:

◆ IsConstant()

bool IsConstant ( void  ) const
overridevirtual

Does the function always return the same result (i.e.

does it apply to constant parameters) ?

Reimplemented from FGParameter.

Definition at line 960 of file FGFunction.cpp.

961{
962 for (auto p: Parameters) {
963 if (!p->IsConstant())
964 return false;
965 }
966
967 return true;
968}

◆ Load()

void Load ( Element *  element,
FGPropertyValue *  var,
FGFDMExec *  fdmex,
const std::string &  prefix = "" 
)
protected

Definition at line 313 of file FGFunction.cpp.

315{
316 Name = el->GetAttributeValue("name");
317 Element* element = el->GetElement();
318
319 auto sum = [](const decltype(Parameters)& Parameters)->double {
320 double temp = 0.0;
321
322 for (auto p: Parameters)
323 temp += p->GetValue();
324
325 return temp;
326 };
327
328 while (element) {
329 string operation = element->GetName();
330
331 // data types
332 if (operation == "property" || operation == "p") {
333 string property_name = element->GetDataLine();
334
335 if (var && simgear::strutils::strip(property_name) == "#")
336 Parameters.push_back(var);
337 else {
338 if (property_name.find("#") != string::npos) {
339 if (is_number(Prefix)) {
340 property_name = replace(property_name,"#",Prefix);
341 }
342 else {
343 XMLLogException err(element);
344 err << LogFormat::RED << "Illegal use of the special character '#'\n"
345 << LogFormat::RESET;
346 throw err;
347 }
348 }
349
350 if (element->HasAttribute("apply")) {
351 string function_str = element->GetAttributeValue("apply");
352 auto f = fdmex->GetTemplateFunc(function_str);
353 if (f)
354 Parameters.push_back(new FGFunctionValue(property_name,
355 PropertyManager, f, element));
356 else {
357 FGXMLLogging log(element, LogLevel::ERROR);
358 log << LogFormat::RED << LogFormat::BOLD
359 << " No function by the name " << function_str
360 << " has been defined. This property will "
361 << "not be logged. You should check your configuration file.\n"
362 << LogFormat::RESET;
363 }
364 }
365 else
366 Parameters.push_back(new FGPropertyValue(property_name,
367 PropertyManager, element));
368 }
369 } else if (operation == "value" || operation == "v") {
370 Parameters.push_back(new FGRealValue(element->GetDataAsNumber()));
371 } else if (operation == "pi") {
372 Parameters.push_back(new FGRealValue(M_PI));
373 } else if (operation == "table" || operation == "t") {
374 string call_type = element->GetAttributeValue("type");
375 if (call_type == "internal") {
376 XMLLogException err(el);
377 err << "An internal table cannot be nested within a function.\n";
378 throw err;
379 }
380 Parameters.push_back(new FGTable(PropertyManager, element, Prefix));
381 // operations
382 } else if (operation == "product") {
383 auto f = [](const decltype(Parameters)& Parameters)->double {
384 double temp = 1.0;
385
386 for (auto p: Parameters)
387 temp *= p->GetValue();
388
389 return temp;
390 };
391 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
392 } else if (operation == "sum") {
393 Parameters.push_back(VarArgsFn<decltype(sum)>(sum, fdmex, element, Prefix, var));
394 } else if (operation == "avg") {
395 auto avg = [&](const decltype(Parameters)& p)->double {
396 return sum(p) / p.size();
397 };
398 Parameters.push_back(VarArgsFn<decltype(avg)>(avg, fdmex, element, Prefix, var));
399 } else if (operation == "difference") {
400 auto f = [](const decltype(Parameters)& Parameters)->double {
401 double temp = Parameters[0]->GetValue();
402
403 for (auto p = Parameters.begin()+1; p != Parameters.end(); ++p)
404 temp -= (*p)->GetValue();
405
406 return temp;
407 };
408 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
409 } else if (operation == "min") {
410 auto f = [](const decltype(Parameters)& Parameters)->double {
411 double _min = HUGE_VAL;
412
413 for (auto p : Parameters) {
414 double x = p->GetValue();
415 if (x < _min)
416 _min = x;
417 }
418
419 return _min;
420 };
421 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
422 } else if (operation == "max") {
423 auto f = [](const decltype(Parameters)& Parameters)->double {
424 double _max = -HUGE_VAL;
425
426 for (auto p : Parameters) {
427 double x = p->GetValue();
428 if (x > _max)
429 _max = x;
430 }
431
432 return _max;
433 };
434 Parameters.push_back(VarArgsFn<decltype(f)>(f, fdmex, element, Prefix, var));
435 } else if (operation == "and") {
436 string ctxMsg = element->ReadFrom();
437 auto f = [ctxMsg](const decltype(Parameters)& Parameters)->double {
438 for (auto p : Parameters) {
439 if (!GetBinary(p->GetValue(), ctxMsg)) // As soon as one parameter is false, the expression is guaranteed to be false.
440 return 0.0;
441 }
442
443 return 1.0;
444 };
445 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
446 var, MaxArgs));
447 } else if (operation == "or") {
448 string ctxMsg = element->ReadFrom();
449 auto f = [ctxMsg](const decltype(Parameters)& Parameters)->double {
450 for (auto p : Parameters) {
451 if (GetBinary(p->GetValue(), ctxMsg)) // As soon as one parameter is true, the expression is guaranteed to be true.
452 return 1.0;
453 }
454
455 return 0.0;
456 };
457 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
458 var, MaxArgs));
459 } else if (operation == "quotient") {
460 auto f = [](const decltype(Parameters)& p)->double {
461 double y = p[1]->GetValue();
462 return y != 0.0 ? p[0]->GetValue()/y : HUGE_VAL;
463 };
464 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
465 } else if (operation == "pow") {
466 auto f = [](const decltype(Parameters)& p)->double {
467 return pow(p[0]->GetValue(), p[1]->GetValue());
468 };
469 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
470 } else if (operation == "toradians") {
471 auto f = [](const decltype(Parameters)& p)->double {
472 return p[0]->GetValue()*M_PI/180.;
473 };
474 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
475 } else if (operation == "todegrees") {
476 auto f = [](const decltype(Parameters)& p)->double {
477 return p[0]->GetValue()*180./M_PI;
478 };
479 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
480 } else if (operation == "sqrt") {
481 auto f = [](const decltype(Parameters)& p)->double {
482 double x = p[0]->GetValue();
483 return x >= 0.0 ? sqrt(x) : -HUGE_VAL;
484 };
485 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
486 } else if (operation == "log2") {
487 auto f = [](const decltype(Parameters)& p)->double {
488 double x = p[0]->GetValue();
489 return x > 0.0 ? log10(x)*invlog2val : -HUGE_VAL;
490 };
491 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
492 } else if (operation == "ln") {
493 auto f = [](const decltype(Parameters)& p)->double {
494 double x = p[0]->GetValue();
495 return x > 0.0 ? log(x) : -HUGE_VAL;
496 };
497 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
498 } else if (operation == "log10") {
499 auto f = [](const decltype(Parameters)& p)->double {
500 double x = p[0]->GetValue();
501 return x > 0.0 ? log10(x) : -HUGE_VAL;
502 };
503 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
504 } else if (operation == "sign") {
505 auto f = [](const decltype(Parameters)& p)->double {
506 return p[0]->GetValue() < 0.0 ? -1 : 1; // 0.0 counts as positive.
507 };
508 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
509 } else if (operation == "exp") {
510 Parameters.push_back(make_MathFn(exp, fdmex, element, Prefix, var));
511 } else if (operation == "abs") {
512 Parameters.push_back(make_MathFn(fabs, fdmex, element, Prefix, var));
513 } else if (operation == "sin") {
514 Parameters.push_back(make_MathFn(sin, fdmex, element, Prefix, var));
515 } else if (operation == "cos") {
516 Parameters.push_back(make_MathFn(cos, fdmex, element, Prefix, var));
517 } else if (operation == "tan") {
518 Parameters.push_back(make_MathFn(tan, fdmex, element, Prefix, var));
519 } else if (operation == "asin") {
520 Parameters.push_back(make_MathFn(asin, fdmex, element, Prefix, var));
521 } else if (operation == "acos") {
522 Parameters.push_back(make_MathFn(acos, fdmex, element, Prefix, var));
523 } else if (operation == "atan") {
524 Parameters.push_back(make_MathFn(atan, fdmex, element, Prefix, var));
525 } else if (operation == "floor") {
526 Parameters.push_back(make_MathFn(floor, fdmex, element, Prefix, var));
527 } else if (operation == "ceil") {
528 Parameters.push_back(make_MathFn(ceil, fdmex, element, Prefix, var));
529 } else if (operation == "fmod") {
530 auto f = [](const decltype(Parameters)& p)->double {
531 double y = p[1]->GetValue();
532 return y != 0.0 ? fmod(p[0]->GetValue(), y) : HUGE_VAL;
533 };
534 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
535 } else if (operation == "roundmultiple") {
536 if (element->GetNumElements() == 1)
537 Parameters.push_back(make_MathFn(round, fdmex, element, Prefix, var));
538 else {
539 auto f = [](const decltype(Parameters)& p)->double {
540 double multiple = p[1]->GetValue();
541 return round((p[0]->GetValue() / multiple)) * multiple;
542 };
543 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var, 2));
544 }
545 } else if (operation == "atan2") {
546 auto f = [](const decltype(Parameters)& p)->double {
547 return atan2(p[0]->GetValue(), p[1]->GetValue());
548 };
549 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
550 } else if (operation == "mod") {
551 auto f = [](const decltype(Parameters)& p)->double {
552 return static_cast<int>(p[0]->GetValue()) % static_cast<int>(p[1]->GetValue());
553 };
554 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
555 } else if (operation == "fraction") {
556 auto f = [](const decltype(Parameters)& p)->double {
557 double scratch;
558 return modf(p[0]->GetValue(), &scratch);
559 };
560 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
561 } else if (operation == "integer") {
562 auto f = [](const decltype(Parameters)& p)->double {
563 double result;
564 modf(p[0]->GetValue(), &result);
565 return result;
566 };
567 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
568 } else if (operation == "lt") {
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 == "le") {
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 == "gt") {
579 auto f = [](const decltype(Parameters)& p)->double {
580 return p[0]->GetValue() > p[1]->GetValue() ? 1.0 : 0.0;
581 };
582 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
583 } else if (operation == "ge") {
584 auto f = [](const decltype(Parameters)& p)->double {
585 return p[0]->GetValue() >= p[1]->GetValue() ? 1.0 : 0.0;
586 };
587 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
588 } else if (operation == "eq") {
589 auto f = [](const decltype(Parameters)& p)->double {
590 return p[0]->GetValue() == p[1]->GetValue() ? 1.0 : 0.0;
591 };
592 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
593 } else if (operation == "nq") {
594 auto f = [](const decltype(Parameters)& p)->double {
595 return p[0]->GetValue() != p[1]->GetValue() ? 1.0 : 0.0;
596 };
597 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix, var));
598 } else if (operation == "not") {
599 string ctxMsg = element->ReadFrom();
600 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
601 return GetBinary(p[0]->GetValue(), ctxMsg) ? 0.0 : 1.0;
602 };
603 Parameters.push_back(new aFunc<decltype(f), 1>(f, fdmex, element, Prefix, var));
604 } else if (operation == "ifthen") {
605 string ctxMsg = element->ReadFrom();
606 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
607 if (GetBinary(p[0]->GetValue(), ctxMsg))
608 return p[1]->GetValue();
609 else
610 return p[2]->GetValue();
611 };
612 Parameters.push_back(new aFunc<decltype(f), 3>(f, fdmex, element, Prefix, var));
613 } else if (operation == "random") {
614 double mean = 0.0;
615 double stddev = 1.0;
616 string mean_attr = element->GetAttributeValue("mean");
617 string stddev_attr = element->GetAttributeValue("stddev");
618 if (!mean_attr.empty()) {
619 try {
620 mean = atof_locale_c(mean_attr);
621 } catch (InvalidNumber& e) {
622 XMLLogException err(element);
623 err << e.what() << "\n";
624 throw err;
625 }
626 }
627 if (!stddev_attr.empty()) {
628 try {
629 stddev = atof_locale_c(stddev_attr);
630 } catch (InvalidNumber& e) {
631 XMLLogException err(element);
632 err << e.what() << "\n";
633 throw err;
634 }
635 }
636 auto generator(makeRandomGenerator(element, fdmex));
637 auto f = [generator, mean, stddev]()->double {
638 double value = generator->GetNormalRandomNumber();
639 return value*stddev + mean;
640 };
641 Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
642 Prefix));
643 } else if (operation == "urandom") {
644 double lower = -1.0;
645 double upper = 1.0;
646 string lower_attr = element->GetAttributeValue("lower");
647 string upper_attr = element->GetAttributeValue("upper");
648 if (!lower_attr.empty()) {
649 try {
650 lower = atof_locale_c(lower_attr);
651 } catch (InvalidNumber &e) {
652 XMLLogException err(element);
653 err << e.what() << "\n";
654 throw err;
655 }
656 }
657 if (!upper_attr.empty()) {
658 try {
659 upper = atof_locale_c(upper_attr);
660 } catch (InvalidNumber &e) {
661 XMLLogException err(element);
662 err << e.what() << "\n";
663 throw err;
664 }
665 }
666 auto generator(makeRandomGenerator(element, fdmex));
667 double a = 0.5*(upper-lower);
668 double b = 0.5*(upper+lower);
669 auto f = [generator, a, b]()->double {
670 double value = generator->GetUniformRandomNumber();
671 return value*a + b;
672 };
673 Parameters.push_back(new aFunc<decltype(f), 0>(f, PropertyManager, element,
674 Prefix));
675 } else if (operation == "switch") {
676 string ctxMsg = element->ReadFrom();
677 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
678 double temp = p[0]->GetValue();
679 if (temp < 0.0) {
680 LogException err;
681 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
682 << "The switch function index (" << temp
683 << ") is negative.\n" << LogFormat::RESET;
684 throw err;
685 }
686 size_t n = p.size()-1;
687 size_t i = static_cast<size_t>(temp+0.5);
688
689 if (i < n)
690 return p[i+1]->GetValue();
691 else {
692 LogException err;
693 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
694 << "The switch function index (" << temp
695 << ") selected a value above the range of supplied values"
696 << "[0:" << n-1 << "]"
697 << " - not enough values were supplied.\n" << LogFormat::RESET;
698 throw err;
699 }
700 };
701 Parameters.push_back(new aFunc<decltype(f), 2>(f, fdmex, element, Prefix,
702 var, MaxArgs));
703 } else if (operation == "interpolate1d") {
704 auto f = [](const decltype(Parameters)& p)->double {
705 // This is using the bisection algorithm. Special care has been
706 // taken to evaluate each parameter only once.
707 size_t n = p.size();
708 double x = p[0]->GetValue();
709 double xmin = p[1]->GetValue();
710 double ymin = p[2]->GetValue();
711 if (x <= xmin) return ymin;
712
713 double xmax = p[n-2]->GetValue();
714 double ymax = p[n-1]->GetValue();
715 if (x >= xmax) return ymax;
716
717 size_t nmin = 0;
718 size_t nmax = (n-3)/2;
719 while (nmax-nmin > 1) {
720 size_t m = (nmax-nmin)/2+nmin;
721 double xm = p[2*m+1]->GetValue();
722 double ym = p[2*m+2]->GetValue();
723 if (x < xm) {
724 xmax = xm;
725 ymax = ym;
726 nmax= m;
727 } else if (x > xm) {
728 xmin = xm;
729 ymin = ym;
730 nmin = m;
731 }
732 else
733 return ym;
734 }
735
736 return ymin + (x-xmin)*(ymax-ymin)/(xmax-xmin);
737 };
738 Parameters.push_back(new aFunc<decltype(f), 5>(f, fdmex, element, Prefix,
739 var, MaxArgs, OddEven::Odd));
740 } else if (operation == "rotation_alpha_local") {
741 // Calculates local angle of attack for skydiver body component.
742 // Euler angles from the intermediate body frame to the local body frame
743 // must be from a z-y-x axis rotation order
744 auto f = [](const decltype(Parameters)& p)->double {
745 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
746 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
747 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
748 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
749 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
750
751 FGQuaternion qTb2l(phi, theta, psi);
752 double cos_beta = cos(beta);
753 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
754 sin(alpha)*cos_beta);
755 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
756
757 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
758 return 0.0;
759 else
760 return atan2(wind_local(eZ), wind_local(eX))*radtodeg;
761 };
762 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
763 } else if (operation == "rotation_beta_local") {
764 // Calculates local angle of sideslip for skydiver body component.
765 // Euler angles from the intermediate body frame to the local body frame
766 // must be from a z-y-x axis rotation order
767 auto f = [](const decltype(Parameters)& p)->double {
768 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
769 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
770 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
771 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
772 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
773 FGQuaternion qTb2l(phi, theta, psi);
774 double cos_beta = cos(beta);
775 FGColumnVector3 wind_body(cos(alpha)*cos_beta, sin(beta),
776 sin(alpha)*cos_beta);
777 FGColumnVector3 wind_local = qTb2l.GetT()*wind_body;
778
779 if (fabs(fabs(wind_local(eY)) - 1.0) < 1E-9)
780 return wind_local(eY) > 0.0 ? 0.5*M_PI : -0.5*M_PI;
781
782 double alpha_local = atan2(wind_local(eZ), wind_local(eX));
783 double cosa = cos(alpha_local);
784 double sina = sin(alpha_local);
785 double cosb;
786
787 if (fabs(cosa) > fabs(sina))
788 cosb = wind_local(eX) / cosa;
789 else
790 cosb = wind_local(eZ) / sina;
791
792 return atan2(wind_local(eY), cosb)*radtodeg;
793 };
794 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
795 } else if (operation == "rotation_gamma_local") {
796 // Calculates local roll angle for skydiver body component.
797 // Euler angles from the intermediate body frame to the local body frame
798 // must be from a z-y-x axis rotation order
799 auto f = [](const decltype(Parameters)& p)->double {
800 double alpha = p[0]->GetValue()*degtorad; //angle of attack of intermediate body frame
801 double beta = p[1]->GetValue()*degtorad; //sideslip angle of intermediate body frame
802 double gamma = p[2]->GetValue()*degtorad; //roll angle of intermediate body frame
803 double phi = p[3]->GetValue()*degtorad; //x-axis Euler angle from the intermediate body frame to the local body frame
804 double theta = p[4]->GetValue()*degtorad; //y-axis Euler angle from the intermediate body frame to the local body frame
805 double psi = p[5]->GetValue()*degtorad; //z-axis Euler angle from the intermediate body frame to the local body frame
806 double cos_alpha = cos(alpha), sin_alpha = sin(alpha);
807 double cos_beta = cos(beta), sin_beta = sin(beta);
808 double cos_gamma = cos(gamma), sin_gamma = sin(gamma);
809 FGQuaternion qTb2l(phi, theta, psi);
810 FGColumnVector3 wind_body_X(cos_alpha*cos_beta, sin_beta,
811 sin_alpha*cos_beta);
812 FGColumnVector3 wind_body_Y(-sin_alpha*sin_gamma-sin_beta*cos_alpha*cos_gamma,
813 cos_beta*cos_gamma,
814 -sin_alpha*sin_beta*cos_gamma+sin_gamma*cos_alpha);
815 FGColumnVector3 wind_local_X = qTb2l.GetT()*wind_body_X;
816 FGColumnVector3 wind_local_Y = qTb2l.GetT()*wind_body_Y;
817 double cosacosb = wind_local_X(eX);
818 double sinb = wind_local_X(eY);
819 double sinacosb = wind_local_X(eZ);
820 double sinc, cosc;
821
822 if (fabs(sinb) < 1E-9) { // cos(beta_local) == 1.0
823 cosc = wind_local_Y(eY);
824
825 if (fabs(cosacosb) > fabs(sinacosb))
826 sinc = wind_local_Y(eZ) / cosacosb;
827 else
828 sinc = -wind_local_Y(eX) / sinacosb;
829 }
830 else if (fabs(fabs(sinb)-1.0) < 1E-9) { // cos(beta_local) == 0.0
831 sinc = wind_local_Y(eZ);
832 cosc = -wind_local_Y(eX);
833 }
834 else {
835 sinc = cosacosb*wind_local_Y(eZ)-sinacosb*wind_local_Y(eX);
836 cosc = (-sinacosb*wind_local_Y(eZ)-cosacosb*wind_local_Y(eX))/sinb;
837 }
838
839 return atan2(sinc, cosc)*radtodeg;
840 };
841 Parameters.push_back(new aFunc<decltype(f), 6>(f, fdmex, element, Prefix, var));
842 } else if (operation == "rotation_bf_to_wf") {
843 // Transforms the input vector from a body frame to a wind frame. The
844 // origin of the vector remains the same.
845 string ctxMsg = element->ReadFrom();
846 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
847 double rx = p[0]->GetValue(); //x component of input vector
848 double ry = p[1]->GetValue(); //y component of input vector
849 double rz = p[2]->GetValue(); //z component of input vector
850 double alpha = p[3]->GetValue()*degtorad; //angle of attack of the body frame
851 double beta = p[4]->GetValue()*degtorad; //sideslip angle of the body frame
852 double gamma = p[5]->GetValue()*degtorad; //roll angle of the body frame
853 int idx = static_cast<int>(p[6]->GetValue());
854
855 if ((idx < 1) || (idx > 3)) {
856 LogException err;
857 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
858 << "The index must be one of the integer value 1, 2 or 3.\n"
859 << LogFormat::RESET;
860 throw err;
861 }
862
863 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
864 FGMatrix33 mT = (qa*qb*qc).GetT();
865 FGColumnVector3 r0(rx, ry, rz);
866 FGColumnVector3 r = mT*r0;
867
868 return r(idx);
869 };
870 Parameters.push_back(new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
871 } else if (operation == "rotation_wf_to_bf") {
872 // Transforms the input vector from q wind frame to a body frame. The
873 // origin of the vector remains the same.
874 string ctxMsg = element->ReadFrom();
875 auto f = [ctxMsg](const decltype(Parameters)& p)->double {
876 double rx = p[0]->GetValue(); //x component of input vector
877 double ry = p[1]->GetValue(); //y component of input vector
878 double rz = p[2]->GetValue(); //z component of input vector
879 double alpha = p[3]->GetValue()*degtorad; //angle of attack of the body frame
880 double beta = p[4]->GetValue()*degtorad; //sideslip angle of the body frame
881 double gamma = p[5]->GetValue()*degtorad; //roll angle of the body frame
882 int idx = static_cast<int>(p[6]->GetValue());
883
884 if ((idx < 1) || (idx > 3)) {
885 LogException err;
886 err << ctxMsg << LogFormat::RED << LogFormat::BOLD
887 << "The index must be one of the integer value 1, 2 or 3.\n"
888 << LogFormat::RESET;
889 throw err;
890 }
891
892 FGQuaternion qa(eY, -alpha), qb(eZ, beta), qc(eX, -gamma);
893 FGMatrix33 mT = (qa*qb*qc).GetT();
894 FGColumnVector3 r0(rx, ry, rz);
895 mT.T();
896 FGColumnVector3 r = mT*r0;
897
898 return r(idx);
899 };
900 Parameters.push_back(new aFunc<decltype(f), 7>(f, fdmex, element, Prefix, var));
901 } else if (operation != "description") {
902 FGXMLLogging log(element, LogLevel::ERROR);
903 log << LogFormat::RED << LogFormat::BOLD
904 << "Bad operation <" << operation
905 << "> detected in configuration file\n" << LogFormat::RESET;
906 }
907
908 // Optimize functions applied on constant parameters by replacing them by
909 // their constant result.
910 if (!Parameters.empty()){
911 FGFunction* p = dynamic_cast<FGFunction*>(Parameters.back().ptr());
912
913 if (p && p->IsConstant()) {
914 double constant = p->GetValue();
915 SGPropertyNode_ptr node = p->pNode;
916 string pName = p->GetName();
917 unique_ptr<FGXMLLogging> log;
918
919 Parameters.pop_back();
920 Parameters.push_back(new FGRealValue(constant));
921
922 if (debug_lvl > 0) {
923 log.reset(new FGXMLLogging(element, LogLevel::DEBUG));
924 *log << LogFormat::GREEN << LogFormat::BOLD
925 << "<" << operation << "> is applied on constant parameters.\n"
926 << "It will be replaced by its result (" << constant << ")";
927 }
928
929 if (node) {
930 node->setDoubleValue(constant);
931 node->setAttribute(SGPropertyNode::WRITE, false);
932 if (debug_lvl > 0)
933 *log << " and the property " << pName
934 << " will be unbound and made read only.";
935 }
936
937 if (debug_lvl > 0) *log << LogFormat::RESET << "\n\n";
938 }
939 }
940 element = el->GetNextElement();
941 }
942
943 bind(el, Prefix); // Allow any function to save its value
944
945 Debug(0);
946}

Member Data Documentation

◆ cached

bool cached
protected

Definition at line 828 of file FGFunction.h.

◆ cachedValue

double cachedValue
protected

Definition at line 829 of file FGFunction.h.

◆ Parameters

std::vector<FGParameter_ptr> Parameters
protected

Definition at line 830 of file FGFunction.h.

◆ pNode

SGPropertyNode_ptr pNode
protected

Definition at line 832 of file FGFunction.h.

◆ PropertyManager

std::shared_ptr<FGPropertyManager> PropertyManager
protected

Definition at line 831 of file FGFunction.h.


The documentation for this class was generated from the following files: