Calculated Inputs
Note: These features can only be accessed by Client Admin users. Your Wildeye devices may be managed by a third party as a service.
Accessing
Calculated Inputs can be added to a Site from the "Other" section of the Inputs settings page.
Overview
A Calculated Input is a function of one or more Inputs from the same or different Sites.
Calculated Inputs are an advanced feature that require some level of programming knowledge to create.
Calculated Inputs are written in VB.NET. After creation they are complied and will be triggered to run automatically when new data from all referenced source Inputs is uploaded into the cloud.
You can still benefit form the power of Calculated Inputs without writing code. Calculated Inputs are automatically created and compiled when required with the Add Sensor templating feature.
You can jump to some example functions below to get a better understanding.
Input Class
TriggerType Enum
Basic Usage
Reference an Input on the current site:
Inputs.Input Name
e.g. Inputs.Rain Fall
LogicalInputs.Input Logical Name
e.g. LogicalInputs.C1
Reference an Input on another site:
Outposts.Outpost Name.Inputs.Input Name
e.g. Outposts.Station A.Inputs.Temperature
Loggers.HumanId.Inputs.InputName
e.g. Loggers.OP12345.Inputs.Flow
Reference Previous Value of the Calculated Input Itself:
PreviousValue
e.g. Return PreviousValue + Inputs.Input A.Value
Similar to Input.PreviousValue property, the PreviousValue property references the previous value of the calculated input itself.
The Input.PreviousValue property from all source inputs and the PreviousValue property will always have the same datetime.
Please note that the Input.PreviousValue property may not be the actual previous value of the source input. It is the last known value that generated a valid data for the calculated input.
Log time of PreviousValue: This is not directly available; Get the PreviousLogTime of one of the inputs to the CI, e.g. Inputs.Flow.PreviousLogTime
Reference Site Custom Field Value (return type: string):
CustomFields.Field Name
e.g. CustomFields.Field 1
Outposts.Site Name.CustomFields.Field Name
e.g. Outpost.Station A.CustomFields.Field 1
Reference Input Custom Field Value (return type: string):
Inputs.Input Name.CustomFields.Field Name
e.g. Inputs.Flow.CustomFields.Field 1
Outposts.Site Name.Inputs.Input Name.CustomFields.Field Name
e.g. Outposts.Station A.Inputs.Flow.Field 1
Set Default Value Used in Calculation When Input Has No Data
##Inputs.InputName.NoDataDefault (line must start with ##)
e.g. ##Inputs.Flow.NoDataDefault = 0
Set Calculation to Always Trigger
##Inputs.InputName.Trigger (line must start with ##)
e.g. ##Inputs.Flow.Trigger = TriggerType.Always
Data Aggregation
Input.PreviousValue and Input.PreviousLogTime refer to the previous data point that is used in the calculation. They do not necessarily represent the actual previous data point of the input.
For example, when one of the source inputs used in the calculation has a single data gap, that data point is skipped for all source inputs used in the calculation. This means Input.PreviousValue for the subsequent calculation will reference the value before the gap for all source inputs. Not the actual previous value.
Another example is when the source inputs used in the calculation have different logging frequencies. The lowest frequency will be used in the calculation and inputs with higher logging frequency will have their data aggregated to the lowest frequency. Aggregation will be as follows:
Counter inputs: Sum All other inputs: Average Aggregation is done of data point from the current calculated point to the last data point before the next calculated point.
The Input.PreviousLogTime for all source inputs will always have the same value.
Example:
Input A and B are referenced by a CI; Input A is logging at a higher frequency than input B; The CI doing a calculation at the frequency of the referenced input with the lowest frequency, i.e. input B in this case; The values of the higher frequency input are aggregated to give a single value to use in the CI;
The reason it is done this way is to align the input data in time when a CI is referencing multiple inputs. This is also applied when a CI references only one input, but is not necessary.
Advanced Settings
The advanced settings can be access by clicking on the advanced settings link on the top right of the screen.
Replace source input data gaps with value
The "Replace source input data gaps with value" feature will replace any gaps in the source data with a specified value. This is useful when multiple source inputs are used in the script and the user does not want data gap caused by gaps in one of the source inputs.
Please note that the fill gap feature does not prevent calculated input from stop calculating due to no data in one of the source input. A gap must have data on both sides.
Group source input data with function
The "Group source input data with function" feature applies an aggregated function such as daily total on all source input data before passing them into the user script.
Calculate data after
The "Calculate data after" feature is useful for removing unwanted test data as well as speed up the initial calculation process.
After a CI is created or triggered to recalculate it will process historical input data as follows:
CI's will
never start calculating earlier than the 1st available data points of the inputs referenced by the CI,
not process data older than 3 years (to optimise performance), except
if an earlier start date is specified in the CI's advanced setting "Calculate data after". If a start date is specified the CI will start calculating from that date.
Start date example:
Examples
Rates
Create Flow Rate (unit / s)
If Inputs.Flow.PreviousValue.HasValue Then
Return (Inputs.Flow.Value - Inputs.Flow.PreviousValue) / Inputs.Flow.LogTime.Subtract(Inputs.Flow.PreviousLogTime).TotalSeconds
End If
Return 0
Create Flow Rate (unit / min.)
If Inputs.Flow.PreviousValue.HasValue Then
Return (Inputs.Flow.Value - Inputs.Flow.PreviousValue) / Inputs.Flow.LogTime.Subtract(Inputs.Flow.PreviousLogTime).TotalMinutes
End If
Return 0
Convert an amount per log interval to a rate, e.g. convert a pulse input to litre/min or mph
'Convert an amount per log interval to a rate, e.g. convert a pulse input to litre/min or mph
Dim PulseIn As Input = Inputs.WindSensor 'or Inputs.FlowSensor, etc.
If PulseIn.PreviousValue.HasValue Then
Return PulseIn.Value / PulseIn.LogTime.Subtract(PulseIn.PreviousLogTime).TotalHours ' or TotalSeconds or TotalMinutes
End If
Return 0
Filters
Return DateTime Filtered Data
'return data between 9 AM to 9 PM only
If Inputs.C1.LogTime.Hour >= 9 AndAlso Inputs.C1.LogTime.Hour < 21 Then
Return Inputs.C1.Value
End If
Return 0
'return data for year 2017 only
If Inputs.C1.LogTime >= New System.DateTime(2017, 1, 1) AndAlso Inputs.C1.LogTime < New System.DateTime(2018, 1, 1) Then
Return Inputs.C1.Value
End If
Return 0
'return data for week days only
If(Not ((Inputs.C1.LogTime.DayOfWeek <> System.DayOfWeek.Saturday) AndAlso (Inputs.C1.LogTime.DayOfWeek <> System.DayOfWeek.Sunday))) Then
Return Inputs.C1.Value
End If
Return 0
Remove steps and spikes from a series
'Remove spikes and step jumps that are bigger than a specified fraction of the input.
'For detail see https://outpost.atlassian.net/wiki/x/5YB-/
Dim RAW As Input = Inputs.Air Pressure
'Linear scaling of RAW input
Dim Scale As Double = 10
'Fraction of RAW input at which step would be removed
Dim StepSizeCutoff As Double = 0.9
Dim RAW_v As Double = RAW.Value * Scale
If PreviousValue.HasValue AndAlso RAW.Previousvalue.HasValue Then
Dim RAW_pv As Double = RAW.Previousvalue * Scale
'Extract previous adjusted RAW value from CI result
Dim pav As Double = PreviousValue
If (RAW_v < pav * StepSizeCutoff) Or (RAW_v > pav * (2 - StepSizeCutoff)) Then
Dim diff As Double = System.Math.Abs(Decimal.Parse(RAW_v - RAW_pv))
If diff < RAW_pv * (1 - StepSizeCutoff) Then
pav = pav + (RAW_v - RAW_pv)
End If
RAW_v = pav
End If
End If
Return RAW_v
Limit input values to be between min and max values
'Also enable advanced settings if needed, e.g. sum over one day to get output as the sum of
'the input samples during one day with the sum limited to the min & max values given.
Dim InputData As Input = Inputs.Precipitation #0 mm
Dim CutoffMax As Double = 90
Dim CutoffMin As Double = 0
Dim ResetValue As Double = 0
'Scale inputs as needed
Dim InputDataScaled As Double = InputData.Value * 1
InputDataScaled = if(InputDataScaled > CutoffMax, ResetValue, InputDataScaled)
InputDataScaled = if(InputDataScaled < CutoffMin, ResetValue, InputDataScaled)
Return InputDataScaled
Level measurement
Calculate the height of a liquid using two external pressure sensor inputs (air & liquid)
'Linear offset in mm
Const Offset_mm As Double = 0
'Inputs for air and water pressures; set next constants to scale these to the correct unit of measures
Dim airPressureRaw = Inputs.MAC - BaroTROLL (0-30psi)
Dim liquidPressureRaw = Inputs.MAS - LevelTROLL 400 (0-30psi)
'Factors to convert raw pressure values to Pascal (Pa/mmHg = 133.322; Pa/H2O = 9.80665; Pa/PSI = 6894.76; Pa/Atm = 101325)
Const airPressureRawToPa As Double = 133.322
Const liquidPressureRawToPa As Double = 6894.76
'The pressure offset of the sensors in Pascal; if the reported value is lower than it should be then the offset is negative
Const airPressureOffset_Pa As Double = -101325
Const liquidPressureOffset_Pa As Double = 0
'Factor to convert height in mm to the desired unit (ft/mm = 0.00328084; in/mm = 0.0393701)
Const outputScaling_mmToUnit As Double = 0.00328084
'Density of liquid being measured as kg/m^3 (water = 998)
Const density As Double = 998
'Convert everything to Pascal
Dim airPressure_Pa as Double = airPressureRaw.Value * airPressureRawToPa
Dim liquidPressure_Pa as Double = liquidPressureRaw.Value * liquidPressureRawToPa
'Liquid height = (pressure of liquid - pressure of air) / (density * acceleration of gravity) ; [Pa / (kg/m^3 * m/s^2) = m]; Pa = kg/m.s^2
Dim liquidHeight_mm As Double = ((((liquidPressure_Pa - liquidPressureOffset_Pa) - (airPressure_Pa - airPressureOffset_Pa)) / (density * 9.80665))) * 1000 + Offset_mm
Return liquidHeight_mm * outputScaling_mmToUnit
Calculate the height of a liquid using one air pressure sensor and raw ADC value of liquid sensor
'Linear offset in mm
Const Offset_mm As Double = 0
'Inputs for air and water pressures; set next constants to scale these to the correct unit of measures
Dim airPressureRaw = Inputs.MAC - BaroTROLL (0-30psi)
Dim liquidPressureRaw = Inputs.MAS - LevelTROLL 400 (0-30psi)
'Factors to convert raw pressure values to Pascal (Pa/mmHg = 133.322; Pa/H2O = 9.80665; Pa/PSI = 6894.76; Pa/Atm = 101325)
Const airPressureRawToPa As Double = 133.322
Const liquidPressureRawToPa As Double = 6894.76
'The pressure offset of the sensors in Pascal; if the reported value is lower than it should be then the offset is negative
Const airPressureOffset_Pa As Double = -101325
Const liquidPressureOffset_Pa As Double = 0
'Factor to convert height in mm to the desired unit (ft/mm = 0.00328084; in/mm = 0.0393701)
Const outputScaling_mmToUnit As Double = 0.00328084
'Density of liquid being measured as kg/m^3 (water = 998)
Const density As Double = 998
'Convert everything to Pascal
Dim airPressure_Pa as Double = airPressureRaw.Value * airPressureRawToPa
Dim liquidPressure_Pa as Double = liquidPressureRaw.Value * liquidPressureRawToPa
'Liquid height = (pressure of liquid - pressure of air) / (density * acceleration of gravity) ; [Pa / (kg/m^3 * m/s^2) = m]; Pa = kg/m.s^2
Dim liquidHeight_mm As Double = ((((liquidPressure_Pa - liquidPressureOffset_Pa) - (airPressure_Pa - airPressureOffset_Pa)) / (density * 9.80665))) * 1000 + Offset_mm
Return liquidHeight_mm * outputScaling_mmToUnit
Water depth to volume calculation (reservoir, dam, pond)
Make your height to volume table
Add new CI to the logger by clicking "Add Sensor" and searching for "Calculations & Agronomic Derivations", then click "Use"
Select Input Type = "Piecewise function", select your level/depth input, edit the name of the new input (eg Water Volume), then click Add Sensor To Site.
The new CI will be added to your site.
Edit the CI:
Enter your depth (x) to volume (y) as a list of {x,y} (Blue highlight)
Add any depth offsets as needed (yellow highlight)
Set the output unit to the one corresponding to the y values in the {x,y} list
Save the CI
Make your height to volume table
Add new CI to the logger by clicking "Add Sensor" and searching for "Calculations & Agronomic Derivations", then click "Use"
Select Input Type = "Piecewise function", select your level/depth input, edit the name of the new input (eg Water Volume), then click Add Sensor To Site.
The new CI will be added to your site.
Edit the CI:
Enter your depth (x) to volume (y) as a list of {x,y} (Blue highlight)
Add any depth offsets as needed (yellow highlight)
Set the output unit to the one corresponding to the y values in the {x,y} list
Save the CI
Weather
Vapour Pressure from RH and T
'Returns vapour pressure as kPa
'For more detail see
' https://outpost.atlassian.net/wiki/x/SID_Ag
' https://outpost.atlassian.net/wiki/x/5YB-/
Dim TemperatureIn As Input = Inputs.#a *CI Temperature °C
Dim HumidityIn As Input = Inputs.#a *CI Relative Humidity %
'Convert humidity to a percentage value; * 1 if already a %
Dim RH As Double = HumidityIn.Value * 1
'Specify the UOM of the input temperature
' C = °C; F = °F; K = Kelvin
Const UOM_In As String = "C"
'--------------------------------------------------------------------
Dim T As Double = TemperatureIn.Value
'Convert input temperature to °C
Select Case UOM_In
Case "C" ' no conversion needed
Case "F"
T = (T - 32) * (5 / 9)
Case "K"
T = T - 273.15
Case Else
Return -273 ' Invalid UOM; please specify a valid UOM
End Select
Dim VapourPressure As Double = (RH / 100) * (0.6108 * System.math.exp((17.27 * T) / (T + 237.3)))
Return VapourPressure
Dew point temperature
'Derived dew point temperature
'https://www.ajdesigner.com/phphumidity/dewpoint_equation_dewpoint_temperature.php
Dim T_Input As Input = Inputs.#a *CI Air Temperature
Dim RH_Input As Input = Inputs.#a *CI Relative Humidity
' multiplier to convert RH to a decimal value; 0.01 if RH is a percentage
Const RhScaling As Double = 0.01
'C, F, or K
Const InputUom As String = "F"
Const OutputUom As String = "F"
'-----------------------------------------------------------
Dim T As Double = T_Input.Value
' Convert to C
Select Case InputUom
Case "C"
T = T
Case "F"
T = (T - 32) * 5 / 9
Case "K"
T = T - 273.15
Case Else
T = -1000 ' Invalid UOM; please specify a valid UOM
End Select
'RH must be a decimal; divide by 100 if it is a percentage
Dim RH As Double = RH_Input.Value * RhScaling
Dim T_Dp = (RH^(1/8)) * (112 + (0.9 * T)) + (0.1 * T) - 112
'convert from C to specified UOM
Select Case OutputUom
Case "C"
T_Dp = T_Dp
Case "F"
T_Dp = T_Dp * 9 / 5 + 32
Case "K"
T_Dp = T_Dp + 273.15
End Select
return T_Dp
Wind chill temperature
' Output is in the same unit as the input temperature
Dim WindTemperatureInput As Input = Inputs.Temperature
Dim WindSpeedInput As Input = Inputs.WindSpeed
'For more detail see:
' - https://en.wikipedia.org/wiki/Wind_chill
' - https://www.weather.gov/epz/wxcalc_windchill
' - https://www.weather.gov/media/epz/wxcalc/windChill.pdf
'Specify the UOM of the input temperature
' C = °C; F = °F; K = Kelvin
Const UOM_In As String = "C"
' Factor to convert input wind speed to mile/h
' m/s = 2.23694
' mile/h = 1
Const SpeedScalingFactor As Double = 2.23694
'-------------------------------------------------------------
Dim T As Double = WindTemperatureInput.Value
Dim V As Double = WindSpeedInput.Value * SpeedScalingFactor
'Convert input temperature to °F
Select Case UOM_In
Case "C" ' no conversion needed
T = T * 9 / 5 + 32
Case "F"
Case "K"
T = (T - 273.15) * 9 / 5 + 32
Case Else
Return -273 ' Invalid UOM; please specify a valid UOM
End Select
Dim TChill As Double = T
' The wind chill formula is only valid <= 50°F (10°C) and wind speeds >= 3 mph (1.34 m/s).
If ((V >= 3) And (T <= 50))
TChill = 35.74 + (0.6215 * T) - (35.75 * (V^0.16)) + (0.4275 * T * (V^0.16))
End If
'Convert output to same unit as input
Select Case UOM_In
Case "C"
TChill = (TChill - 32) * 5 / 9
Case "F"
Case "K"
TChill = (TChill - 32) * 5 / 9 + 273.15
End Select
Return TChill
Filter for step, min & max limits, e.g. rain & wind
'Remove single point steps and samples outside min & max limits.
'Invalid samples are replace with the previous value
Dim dataIn As Input = Inputs.Wind Speed in m/s
Const SCALING As Double = 1 ' m/s to mph : 2.23694
Const MAX_VALUE As Double = 115 ' max wind speed ever = 103 m/s; maximum wind gust = 113 m/s
Const MIN_VALUE As Double = 0 ' wind & rain cannot be negative
Const MAX_STEP As Double = 115 ' this would depend on the sampling rate, maximum rate of change, etc.; Todo: consider sampling rate in calculations
'------------------------------
Dim dataOutVal As Double = dataIn.Value * SCALING
If PreviousValue.HasValue AndAlso dataIn.Previousvalue.HasValue Then
Dim dataOutPrevVal As Double? = PreviousValue
Dim dataPrevVal As Double? = dataIn.Previousvalue * SCALING
Dim change = System.Math.Abs(Decimal.Parse(dataPrevVal - dataOutVal))
If (dataOutVal > MAX_VALUE) Or (dataOutVal < MIN_VALUE) Or (change > MAX_STEP) Then
dataOutVal = dataOutPrevVal
End If
End If
Return dataOutVal
Common Mistakes
Calculated Input with formula that returns Nothing in the code path.
The CI will stop calculating if more than a certain number (10000) of consecutive calculations return Nothing. E.g.:
If <condition> Then Return <value> Else Return Nothing End If 'This CI will stop calculating when Nothing is returned 10000 times in a row
No null check when referencing a previous values.
The previous value is always null during calculation of the first data point. This need to be handled explicitly in the code if the formula needs to references the previous value. E.g.:
If Inputs.Input A.PreviousValue.HasValue Then Return <somevalue> Else Return <othervalue>
Source input not logging
Calculated input uses the logging frequency of the inputs it references to align the data points. Currently it will not work if one of the referenced inputs is set to not logging.