Test controller, functions

TestController supports expressions in a lot of places, for most users it will be the math page and the calculator that is most interesting. All places uses the same evaluator, that support a wide variety of functions. This list will show the most relevant functions, but not all supported functions.

Contents
    System
        Typecast
        Failsafe reading of a variable
        Testing for invalid value
        Trapping any error or fault
        Creating arrays
    Mathematic
        Simple
        Complex mathematic
        Solve
        Poly
        Quadratic equation
    Electronic functions
        Find a standard value
        Calculate parallel resistors
        Combine two standard values to get an a good match
        Voltage divider with standard values
        Decibel dB
    Complex values for electronic calculation
    Sensor functions
        Temperature sensors thermistor/NTC
        Temperature sensors RTD
        Temperature sensors thermocouples
    Date & time functions
    Getting a date/time variable
    Test controller device functions
        getDevice
        get..., set..., read... name...
        deviceWrite & deviceRead
    String functions
        unQuote
        listIndex
        inList
        getElement
        substring
        match
        getMatch & getMatchGroup
        trim
        equals & equalsi
        replace & replacei
        indexOf & lastIndexOf
        formatLeft & formatRight
        formatDouble
        formatSI
        formatInt
        strlen
    Notes
Main page


System

Typecast

Generally the evaluator will do automatic typecasting, but sometimes it can be necessary to force a specific type.

Code:
double(value)
complex(value)
int(value)
string(value)

The above function will return the specified type, but the automatic type casting can change the type to another. This automatic typecast is most obvious between numbers and strings containing numbers.

Code:
type(value)
To check the type of a variable use type(v), the normal types will be long/double/string, but there are also complex/dateTime/array/struct/...



Failsafe reading of a variable

In some cases it is not known if the variable exist before using it, it can be handled in two ways:
Code:
getVar("name");
This will always return a value, when the name do not exist it will be an empty value, if name exist it will the the value of name.

Code:
varExists("name");
Returns true or false depending on if a variable with name exist.


Testing for invalid value

A invalid value may fail the expression with a error return or just return a invalid result, but there are ways to trap these conditions:
Code:
isNan(value{,value{,value...}});
Will return true i if any of the listed values is a NaN (Not a Number). It will not detect infinite values.


Code:
isInfinite(value{,value{,value...}});
A infinite value will usually mean the result is infinite, but with this test it can return true instead.

Code:
invalidValue(value);
It is possible to generate a invalid value, depending on value this function will return:
value>0 -> Positive infinite
value<0 -> Negative infinite
value==0 -> NaN (Not a number).



Trapping any error or fault

It is possible to trap any error or fault and return a user defined value. This will trap anything, including syntax and external errors.
Code:
noFault()+any expression
noFault();any statement
noFault(returnValue)+any expression
noFault(returnValue);any statement
The function noFault will always return 0 and enable a trap for any sequent errors. The calls without a returnValue will use a 0 return value on faults/errors.



Creating arrays

Arrays is used to hold many values and some functions will accept an array argument, instead of a list of values.
Arrays are always starting from element 0 and will automatic increase to contain the specified number of elements.

Below I create a array with on element and then increase it to four element:
Code:
var a[0]=1;
a[3]=4;

Another way to create an array and initialize it at the same time is:
Code:
array(listOfValues)

var a=array(1,2,3,4)

A slight variation of the above, here each value of a array parameters will be added separately, i.e. the final array will contain 5 elements (If I had used array() the final array would be two elements with first first element a 4 element array):
Code:
arrayCombine(listOfValues)
var a=arrayCombine(array(1,2,3,4),5)



Mathematic

Simple

All the standard mathematic functions are supported by TestController.

Numeric handling:
Code:
abs(v)// Strip sign
floor(v) // Round toward zero
round(v) // Standard 0.5 rounding
sign(v)// Return sign, i.e. -1, 0 or 1
limit(value,min,max) // Returns value while between min/max else either min or max.
max(v1,v2,...)// Return the highest number from the parameters
min(v1,v2,...)// Return the lowest number from the parameters

Power and exponential functions:
Code:
sqr(v)// Returns square of v, i.e. v*v
sqrt(v)// Returns square root of v
hypot(y,x) // Returns sqrt(sqr(y)+sqr(x))
ln(v)// Return natural logarithmic of v
exp(v)// Returns e to the v power.
log(v)// Return log 10 of v
pow(base,exp) // Returns base to exp power.

Trigonometry functions (Angles are in radians):
Code:
acos(v)// arc cosine, returns result in radians
asin(v)// arc sine, returns result in radians
atan(v)// arc tangent, returns result in radians
atan2(y,x)// Angle in radians from rectangular coordinates
cos(v)// cosine, input is in radians
sin(v)// sine, input is in radians
tan(v)// tangent, input is in radians
toRad(deg)// Convert degree to radians
toDeg(rad)// Convert radians to degree

Hyperbolic trigonometry functions:
Code:
asinh(v)
acosh(v)
atanh(v)
sinh(v)
cosh(v)
tanh(v)

A few constants are included:
Code:
PI
pi
e
There is no i or j constant, but they can easily be defined with "var i=1i;const("i");" making it possible to write 3+i instead of 3+1i



Complex mathematic

Most mathematic functions support complex arguments, but will not use complex arithmetic except one of the arguments are complex.
This means sqrt(-1) is invalid, but sqrt(complex(-1)) or sqrt(-1+0i) is valid. A complex number stays complex, even if the imaginary part is zero.

There are a few functions specific for complex numbers:
Code:
cpxPolar(vector,angle)// Create a complex value from polar coordinates (Angle is in radians)
conj(v)// Calculate the conjugate of a complex value, for sqrt & qeq use this function to show the other complex root.
complex(v)// Cast argument to complex



Solve

This is a function to numerically solve an expression for minimum, maximum or zero result.
It will first calculate the result at the two endpoints, if they have different signs there will be a zero in the range and it will solve for that.
If they have the same sign it will calculate the result midways between start and end. If this value is higher than either start or end it will solve for a maximum, if the result is lower than either start or end it will solve for minimum.
If there are multiple zeros, minimums or maximums inside the range it is unspecific which one is found.

The expression must use the variable x and be in a string type. Being numerically solved the result will not be exact, but the calculated x will have 10 significant digits.
The result will contain x, the value of the expression and what was solved for: min/max/zero

Code:
solve(expression,start,end)

solve("x-2",-10,10)// This will solve for a zero
solve("sqr(x-2)",-10,10)// This will solve for a minimum
solve("sqr(x-2)-5",-10,10)// This will solve for a minimum
solve("sqr(x-2)-5",0,10)// This will solve for a zero



Poly

Calculate a polynomial expression, that is one like this:  a0 + a1*x + a2*x*x + a3*x*x*x...

Code:
poly(x,a0,a1,a2,a3,...)

var a=array(a0,a1,a2,a3,...)
poly(x,a)



Quadratic equation

Solver for quadratic equation, it can both handle real and complex arguments.
Code:
qeq(a,b,c)
It will return a fail, one or two values depending on the input. When called with real arguments only real answers will be returned, to get complex answers at least one of the arguments must be complex.



Electronic functions


Find a standard value

There is a couple of function to map a value to the nearest E series value.
series: 0=E3, 1=E6, 2=E12, 3=E24, 4=E48, 5=E96, 6=E192, 12=E12, 24=E24, 48=E48, 96=E96, 125=R125, 192=E192, any other value will return E192

Code:
findE(series,resistor)
findE6(resistor)
findE12(resistor)
findE24(resistor)
findE48(resistor)
findE96(resistor)
findE192(resistor)


Calculate parallel resistors

This function can calculate the parallel value of any number of resistors or inductors or the serial value of capacitors.

Code:
par(r1,r2,...)



Combine two standard values to get an a good match

These functions will do series or parallel coupling of components to get as close to the specified value as possible.
series: 0=E3, 1=E6, 2=E12, 3=E24, 4=E48, 5=E96, 6=E192, 12=E12, 24=E24, 48=E48, 96=E96, 125=R125, 192=E192, any other value will return E192

Code:
findRser(series,resistance)
findRpar(series,resistance)
findCser(series,capacitance)
findCpar(series,capacitance)
findLser(series,inductance)
findLpar(series,inductance)

findR(series,resistance)
findC(series,capacitance)
findL(series,inductance)
findXser & findXpar will only find the best serial or parallel combinations or components in the specified series.
The findX function will locate the optimal combination of parts. This means it will check single part, series parts and parallel parts and return the best match for the requested value.
With E12 the combination will be below 0.8% excluding component tolerance for findX.
With E24 the combination will be below 0.3% excluding component tolerance for findX.
With E48 the combination will be below 0.06% excluding component tolerance for findX.



Voltage divider with standard values

Voltage dividers are easy to calculate, but getting the best combination of resistors from standard values can be a bit tedious work. I have implemented some functions to make it easier:

Code:
vdiv(vin,r1,r2)
vdiv(series,inputVoltage,outputVoltage,dividerCurrent)
vdiv2(series,inputVoltage,outputVoltage,dividerCurrent)

vdiv(12,10,3,1m)

The vdiv with 3 parameters will calculated the output voltage and the divider current.
The vdiv function with 4 parameters will search around the optimal resistor and pick the best combination within a couple of steps. This means there is a fairly large tolerance on the divider current.
The vdiv2 function will pick the best top resistor in the series and they pick a parallel combination for the bottom resistor. This will give more precise divider current and more precise output voltage at the cost of one extra resistor. This function will skip the paralleling if a single resistor is better.
R1 is top resistor, R2 is bottom resistor.



Decibel dB

Decibel is a logarithmic function that is used for both absolute levels and for amplification/attenuation. The implemented functions are for absolute values, this can used on measured input and output levels. Subtracting them from each other will give the amplification/attenuation.

Code:
dBV(voltage)// dB above/below 1V
dBm(power)// dB above/below 1mW
dBm(voltage,resistance)// dB above/below 1mW in resistance
dBm(voltage,600)// dB above/below 0.775 volt, this is very common in audio and is called dBu



Complex values for electronic calculation

Converting capacitors and inductors to complex values makes it possible to calculated with them the same way as with resistors. Note that all calculations are done at a specific frequency.

The functions to turn components into complex values are:

cpxR cpxR(R)
cpxC cpxC(freq,C)
cpxCRs cpxC(freq,C,Rs)
cpxL cpxL(freq,L)
cpxLRs cpxL(freq,L,Rs)
cpxCLsRs cpxCLRs(freq,C,L,Rs)
cpxCLsRsRp cpxCLRs(freq,C,L,Rs,Rp)
cpxCLpRs cpxCLRp(freq,C,L,Rs)
cpxCLpRsRp cpxCLRp(freq,C,L,Rs,Rp)

Note: The cpxR is only a typecast to complex and it not needed (The typecase would be done automatic).

Converting back from complex (The complex value is the complex impedance):
Code:
cpxToQ(cpxValue)
cpxToZ(cpxValue)
cpxToPhase(cpxValue)
cpxToCLRs(freq,cpxValue)



Sensor functions


Temperature sensors thermistor/NTC

Thermistors can be described two ways, either with a beta or with A B & C coefficients (Steinhart–Hart), these last method is more precise. There are functions for both methods.
Code:
ntcB(r0,t0,beta,resistance);
ntcSH(a,b,c,resistance);

ntcBT(r0,t0,beta,temperature);
ntcSHT(a,b,c,temperature);

Sometimes it is easier pack the coefficients into a single variable or to calculate the coefficients from a resistance table. Placing the ntcCoef() function in .../Documents/TestController/Settings/autorun.txt means it will be automatic run each time TestController is started.
Code:
globalvar ntc1=ntcCoef(r,beta)
globalvar ntc1=ntcCoef(a,b,c)
globalvar ntc1=ntcCoef(r1,t1,r2,t2)
globalvar ntc1=ntcCoef(r1,t1,r2,t2,r3,t3)

ntc(ntc1,resistance)
ntcT(ntc1,temperature)



Temperature sensors RTD

These is support for a couple of different RTD sensors: With PT & ITS-90 sensors the Callendar-Van Dusen equations are used, with CU & NI it is the simple equation.

The sensor type is specified in the function call:
Code:
rtd(type,resistance)// Shortcut, for types PT100 & PT1000
rtdT(type,temperature)// Shortcut, for types PT100 & PT1000
rtd(type,r0,resistance)
rtdT(type,r0,temperature)

rtd("PT100",105.3);
rtdT("PT",100,300);



Temperature sensors thermocouples

There is support for eight types: J B E N R S T K, I uses ITS90 data from NIST for the conversion, this means all conversion between volt and temperature are better than 0.1°C in precision.
To use it requires a meter with 10uV resolution, i.e. mV with two decimals.
The function do not include cold junction compensation, this must be added/subtracted externally.

Code:
thermocouple(type,voltage)
thermocoupleT(type,temperature)

thermocoupleInfo(type)

thermocouple("j",3m)+25

                                


Date & time functions

There is generally not much use for these in TestController, but one application is a time based trigger when logging using the date() function


Getting a date/time variable

Code:
date();
date(year,month,date);
date(hour,minute,seconds,millis);// Will assume the current date
date(year,month,date,hour,minute,seconds);
date(year,month,date,hour,minute,seconds,millis);

This function will return current date and time or encode a date time value from discrete date and time elements.
The date is stored as a millisecond value and can be compared and when read as string it will return a yyyymmddhhmmss format.



Test controller device functions

These functions are for controlling devices in TestController.


getDevice

This function is used to get a device of a specific kind, it is independent of brand or specifications for the device, but will return the first, second, etc. device loaded.
When specifying the interfaceType it is possible to add a ":index" to it, this will return 2, 3, 4, ... etc. device. It can either be next unit in a multichannel device or next device.

Code:
getDevice(interfaceType);

var ps=getDevice("PS")



get..., set..., read... name...

These functions are the used with the generic interface, they will usually be named the same and have the same parameters across brands.



deviceWrite & deviceRead

These two functions are basically the same, except only deviceWrite will return a result. They are used to send SCPI commands to device when in calculator mode.

Code:
deviceWrite(device,scpi_commands)
deviceRead(device,scpi_commands)



String functions

These functions are mostly for use in definitions, especially in the :readmath: function or in brackets.

unQuote

Will return the string with one set of quotes removed, they must be either " or ' and be present at both the start and the end of the string. If no valid quotes are found the string is returned unmodified.

Code:
unQuote(string)



listIndex

Will return the index of value in the list. The list is a couple of items with a delimiter between, the default delimiter is "[|,; ]+"
The delimiter is in regular expression format. The easiest way to specify one is to always use brackets around the delimiter character, i.e. "[ ]" for one space or "[ ]+" for one or more spaces.
If no match is found -1 is returned.

Code:
listIndex(value,listString);
listIndex(value,listString,delimiter);



inList

Very similar to listIndex, but only test for presence of the value in the list.

The list is a couple of items with a delimiter between, the default delimiter is "[|,; ]+"
The delimiter is in regular expression format. The easiest way to specify one is to always use brackets around the delimiter character, i.e. "[ ]" for one space or "[ ]+" for one or more spaces.
If no match is found false is returned.

Code:
inList(value,listString);
inList(value,listString,delimiter);



getElement

Returns element at index. This is used to convert a index (From 0 and up to about 10 or 20) to a text.
The list is split with "[ ]+", i.e. a regular expression string, this string can be changed by specifying another delimiter string. The easiest way to specify one is to always use brackets around the delimiter character, i.e. "[ ]" for one space or "[ ]+" for one or more spaces.

Code:
getElement(listString,index);
getElement(listString,index,delimiter);



substring

Returns part of a string, starting at index from, but not including index to, leaving out to or using a large value for to will return the rest of the string.

Code:
substring(string,from);
substring(string,from,to);



match

Returns true if the string matches the regEx

Code:
match(string,regEx)
Note regEx is a string, i.e. it must be in quotes.




getMatch & getMatchGroup

Returns the part if the string that matches the regular expression. With the group only the regEx match group with that number is returned.

Code:
getMatch(string,regEx)
getMatchGroup(string,regEx,number)
Note regEx is a string, i.e. it must be in quotes.



trim

Returns the string with spaces removed from both ends.

Code:
trim(string)



equals & equalsi

Return true if the two strings are equal, the i version ignores upper/lower case. Equal is very similar to ==, but will always force the values to strings before comparing them, this means integer and float numbers will never match (Float always has at least one decimal, integer never has decimals).

Code:
equals(string,string)
equalsi(string,string)



replace & replacei

Replace a text with another text. This will replace all occurrences of the text.
The version ending with i will ignore upper/lower case.

Code:
replace(string,from,to)
replacei(string,from,to)



indexOf & lastIndexOf

Return position of specified string, either first occurrence or last occurrence.
Will return -1 if string is not present.

Code:
indexOf(string,search)
lastIndexOf(string,search)





formatLeft & formatRight

Fill the string to the specified length, the original is string is placed left or right as the name implies. The filler is a space of the specified string. The result will always be limited to the specified length.

Code:
formatLeft(string,length)
formatRight(string,length)

formatLeft(string,length,filler)
formatRight(string,length,filler)


formatDouble

Format a floating point number.

The minIntDigits will typically be 1 and maxIntDigits is the maximum number of digits before the decimal point.
The minFracDigits will usually be 0 or same value as maxFracDigits that specify maximum number of digits after the decimal point.

Code:
formatDouble(number,minIntDigits,maxIntDigits,minFracDigits,maxFracDigits)



formatSI

Format a floating point number using SI prefix.

Code:
formatSI(number)



formatInt

Format a integer number.

Code:
formatInt(number,minIntDigits,maxIntDigits);



strlen

Length of a value as string. This will convert any variable to string and return the length of the string.

Code:
strlen(value);

strlen("hello") -> 5
strlen(15) -> 2
strlen(8.0) -> 3



Notes

The evaluator is a common library I uses in multiple projects, this means it contains functions for a lot of other stuff, not related to TestController or electronic.