# Formula Interpreter 3 - Creating Script Formulas¶

Most formulas can be defined in the formula table or using the formula dialog in the data browser. Script formulas can be created for formulas which cannot defined in the table because they are too long or too complex.

Script formulas can use all UniScript elements, such as if-else conditions and for-loops.

## Example Formula¶

The following script formula computes power from speed and torque.

```
// Power P in kW
// T*2*PI*rpm
// Speed rpm in 1/min
// Trq T in Nm
def _fi_P(bInfo)
{
ssChannels = "rpm, Trq";
if (bInfo) {
return ["P"; ..
"kW"; ..
"Power"; ..
ssChannels; ..
""; ..
"%.2lf"];
}
rpm = ch("rpm");
Trq = ch("Trq");
P = (rpm .* Trq) ./ 9549.3;
set_ch("P", P, ssChannels);
}
```

In script formulas, the operators `.*`

, `./`

, `.^`

should be used. The
period before the operator means that the operation of vectors is performed on an
element by element basis.

`rpm`

and `Trq`

are vectors containing all data points. The result vector
`P`

contains as many elements as `rpm`

and `Trq`

. If `rpm`

and `Trq`

contain a different number of data points, the function would be terminated with
an error message.

For MDF-, Famos- or ASAM-ODS-Data the formula can contain channels with different number of data points. In this case the formula interpreter will interpolate all used channels to the time base with the highest resolution.

Example:

In the following example the channels a is multiplied with b. a is measured with 10 Hz and b with 100 Hz. The formula result c will have a resolution of 100 Hz.

```
def _fi_c(bInfo)
{
ssChannels = "a, b";
if (bInfo) {
return ["c"; ..
""; ..
""; ..
ssChannels];
}
_fich_a = ch("a", "c"); // a 10 Hz -> interpolate to 10Hz
_fich_b = ch("b", "c"); // b 100 Hz -> interpolate to 10Hz
_fich_c = _fich_a .* _fich_b;
set_ch("c", _fich_c, ssChannels);
}
```

## Elements of Script Formulas¶

In order for the formulas to be evaluated by UniPlot, the function must fulfill the following regulations:

A UniScript function specified for each formula.

Each formula produces one channel.

The name of a function begins with

`_fi_`

. The function has one parameter`bInfo`

.If the function is called with

`bInfo == TRUE`

, the function must return a string vector with at least 4 elements. The meaning of the elements is described in Query Function Information.The function can access the data of a NC file with the function ch Calculated data is written to the NC file using the set_ch function.

All UniScript functions can be called within the function.

## Query Function Information¶

If a formula function is called with the value `bInfo == TRUE`

, the function
must return a string vector with at least 4 elements (see example
Example Formula):

- Element 1
- The first item is the name of the formula. The name must adhere to the rules for channel names (see nc_vardef).
- Element 2
- Short description of the formula.
- Element 3
- Physical unit.
- Element 4
- A list of the channel names that are accessed in the formula separated by a comma. Before the function is called, the formula interpreter checks whether the formula can be calculated. In addition, the interpreter checks whether the dependent channels were already calculated or whether the measuring channels are available in the file.
- Element 5
- Name of a condition, e.g. Diesel. If a condition is specified, a function
must be available, which returns the value TRUE (1) or FALSE (0). If the
formula can be calculated, the function returns the value TRUE (1), otherwise
the value FALSE (0). The function begins with the prefix
`_fiis_`

followed by the name of the condition in lowercase letters, e.g.`_fiis_diesel`

. - Element 6
- Number format definition, e.g.
`%g`

or`%.2lf`

. You’ll find a detailed description of the format definition in the documentation for the printf function.

## Definition of a Condition¶

The following program listing shows the UniScript function for the condition “Diesel”:

```
def _fiis_diesel()
{
ncid = get_ncid();
if (ncid == -1) {
error();
return FALSE;
}
if (nc_attinq_datatype(ncid, NC_GLOBAL, "PEtrol___DIesel") == NC_CHAR) {
ssEngineType = nc_attget(ncid, NC_GLOBAL, "PEtrol___DIesel");
if (ssEngineType == "DI") {
return TRUE;
}
}
return FALSE;
}
```

The function get_ncid returns the handle of the active NC file. Use the handle to retrieve information from the file.

In the example, we checked whether the file contains the global attribute “PEtrol___DIesel “. If the attribute value is “DI”, the function returns TRUE (1). In all other cases the return value is FALSE (0). This means that all formulas containing the value “Diesel” in the info text will only be applied on NC files where the attribute “PEtrol___DIesel” is “DI”.

Condition functions begin with the character sequence `_fiis _`

followed by the
function name, which must correspond to the name rules for UniScript functions.
Only lowercase letters should be used for the name, e.g. `_fiis_diesel`

.

Condition functions can be stored in an ic file in the directory
`UniPlotFormula`

.

## User Defined Functions¶

If the program which executes the conversion of table formulas into UniScript
functions finds any function calls, e.g. `sqrt()`

, it replaces the name with
`_fif_sqrt()`

. Instead of sqrt, the function `_fif_sqrt()`

is called.

The reason for this is that the UniScript function sqrt ignores “Missing Values” (see Channels with Missing Values) and will calculate complex numbers for negative values.

The function `_fif_sqrt()`

is defined as following:

```
def _fif_sqrt(rvData)
{
r = rvData;
idx = find(rvData < 0 || rvData == MISSING_VALUE);
if (idx[1] != 0) {
r[idx] = MISSING_VALUE;
}
r = sqrt(r);
if (idx[1] != 0) {
r[idx] = MISSING_VALUE;
}
return r;
}
```

Accordingly, you can write your own functions for the formula table.

## OnFormulaStartEval¶

The function OnFormulaStartEval is called if the user starts the formula
evaluation, e.g. over the `F9` key.

The function has one parameter: The netCDF file handle (NC handle).

This function can check whether the formulas are to be applied to the active NC file or not. If the function does not exist, the formulas are calculated with all NC-files.

The function has read access to the NC-file.

In the following program listing the function checks whether the NC file contains a global attribute “Creator” that contains the string “Pasy”.

```
def OnFormulaStartEval(ncid)
{
if (nc_attinq_datatype(ncid, NC_GLOBAL, "Creator") == NC_CHAR) {
ssCreator = nc_attget(ncid, NC_GLOBAL, "Creator");
if (strmatch("*Pasy*", ssCreator)) {
return TRUE;
}
}
return FALSE;
}
```

`OnFormulaStartEval(ncid)`

functions can also be stored in an IC file in the
directory `UniPlot\Formula`

.

The `OnFormulaStartEval`

can be used to set a different formula directory.

If a function with the name OnFormulaStartEval is defined, it will be invoked by UniPlot to finish the calculation. OnFormulaStartEval can be used to set the original formula directory.

```
def OnFormulaStartEval(ncid)
{
svOldDir = FE_SetFormulaDirectory("C:/formula");
WriteProfileString("Formula", "olddir", strcat(svOldDir, ";"));
return TRUE;
}
def OnFormulaFinishEval()
{
ssOldDir = GetProfileString("Formula", "olddir");
FE_SetFormulaDirectory(ssOldDir);
}
```

## Register Formula¶

If a new formla file has been added to the formula directory, the library
`formula.icl9`

must be updated. When the dialog
Tools=>Formula Configuration is closed with OK the library is updated.
To manually update the library invoke `_FE_UpdateFormulaList()`

.

_{id-71732}