SignalTables.SignalTableType
sigTable = SignalTable(args...)

Returns a new SignalTable dictionary.

Arguments args... are dictionary pairs where values must be Var(...) and/or Par(...) and/or Map(...). Example:

The first argument must define the independent signal, that is, Var(values=..., independent=true), ... and values must be an AbstractVector. Further added signals with a :values key, must have the same first dimension as the independent signal.

Most dictionary operations can be applied on sigTable, as well as all functions of Overview of Functions.

Examples

using SignalTables
using Unitful

t = 0.0:0.1:0.5
sigTable = SignalTable(
  "time"         => Var(values= t, unit="s", independent=true),
  "load.r"       => Var(values= [sin.(t) cos.(t) sin.(t)], unit="m"),
  "motor.angle"  => Var(values= sin.(t), unit="rad", state=true, der="motor.w"),
  "motor.w"      => Var(values= cos.(t), unit="rad/s"),
  "motor.w_ref"  => Var(values= 0.9*[sin.(t) cos.(t)], unit = ["rad", "1/s"],
                                info="Reference angle and speed"),
  "wm"           => Var(alias = "motor.w"),
  "ref.clock"    => Var(values= [true, missing, missing, true, missing, missing],
                                 variability="clock"),
  "motor.w_c"    => Var(values= [0.8, missing, missing, 1.5, missing, missing],
                                variability="clocked", clock="ref.clock"),
  "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"),
  "motor.data"   => Par(value = "resources/motorMap.json"),
  "attributes"   => Map(experiment=Map(stoptime=0.5, interval=0.01))
)

signalInfo(sigTable)

This results in the following output:

name          unit          size  eltypeOrType            kind attributes
─────────────────────────────────────────────────────────────────────────────────────────────────────────
time          "s"            [6]   Float64                Var  independent=true
load.r        "m"            [6,3] Float64                Var
motor.angle   "rad"          [6]   Float64                Var  state=true, der="motor.w"
motor.w       "rad/s"        [6]   Float64                Var
motor.w_ref   ["rad", "1/s"] [6,2] Float64                Var  info="Reference angle and speed"
wm            "rad/s"        [6]   Float64                Var  alias="motor.w"
ref.clock                    [6]   Union{Missing,Bool}    Var  variability="clock"
motor.w_c                    [6]   Union{Missing,Float64} Var  variability="clocked", clock="ref.clock"
motor.inertia "kg*m/s^2"           Float32                Par
motor.data                         String                 Par
attributes                                                Map  experiment=Map(stoptime=0.5, interval=0.01)

The command show(IOContext(stdout, :compact => true), sigTable) results in the following output:

SignalTable(
  "time" => Var(values=0.0:0.1:0.5, unit="s", independent=true),
  "load.r" => Var(values=[0.0 1.0 0.0; 0.0998334 0.995004 0.0998334; 0.198669 0.980067 0.198669; 0.29552 0.955336 0.29552; 0.389418 0.921061 0.389418; 0.479426 0.877583 0.479426], unit="m"),
  "motor.angle" => Var(values=[0.0, 0.0998334, 0.198669, 0.29552, 0.389418, 0.479426], unit="rad", state=true. der="motor.w"),
  "motor.w" => Var(values=[1.0, 0.995004, 0.980067, 0.955336, 0.921061, 0.877583], unit="rad/s"),
  "motor.w_ref" => Var(values=[0.0 0.9; 0.0898501 0.895504; 0.178802 0.88206; 0.265968 0.859803; 0.350477 0.828955; 0.431483 0.789824], unit=["rad", "1/s"], info="Reference angle and speed"),
  "wm" => Var(values=[1.0, 0.995004, 0.980067, 0.955336, 0.921061, 0.877583], unit="rad/s", alias="motor.w"),
  "ref.clock" => Var(values=Union{Missing, Bool}[true, missing, missing, true, missing, missing], variability="clock"),
  "ref.trigger" => Var(values=Union{Missing, Bool}[missing, missing, true, missing, true, true], variability="trigger"),
  "motor.w_c" => Var(values=Union{Missing, Float64}[0.8, missing, missing, 1.5, missing, missing], variability="clocked", clock="ref.clock"),
  "motor.inertia" => Par(value=0.02, unit="kg*m/s^2"),
  "motor.data" => Par(value="resources/motorMap.json"),
  "attributes" => Map(experiment=Map(stoptime=0.5, interval=0.01)),
  )
SignalTables.StringDictTypeType
const StringDictType = OrderedDict{String,Any}

Predefined dictionary type used for the signal dictionary.

SignalTables.MapMethod
signal = Map(; kwargs...)::OrderedDict{Symbol,Any}

Returns a set of key/value pairs in form of a dictionary. A Map is used to associate a set of attributes to a Variable, Parameter or Signal Table.

The following keys are recognized (all are optional):

keyvalue
:infoShort description.

Additionally, any other signal attributes can be stored in signal with a desired key,

Example

using SignalTables

sigTable = SignalTable(
   J = Par(value = 0.02),
   attributes = Map(author = "xyz")
)
SignalTables.ParMethod
signal = Par(; kwargs...)::OrderedDict{Symbol,Any}

Returns a parameter signal definition in form of a dictionary. A parameter is a variable that is constant and is not a function of the independent variables. kwargs... are key/value pairs of parameter attributes.

The value of a parameter variable is stored with key :value in signal and is an instance of any Julia type (number, string, array, tuple, dictionary, ...).

The following keys are recognized (all are optional):

keyvalue (of type String, if not obvious from context)
:valuesignal[:value] is a constant value that holds for all values of the independent signals.
:unitString: Unit of all signal elements (parseable with Unitful.uparse), e.g., "kg*m*s^2". Array{String,N}: signal[:unit][j1,j2,...] is unit of variable element [j1,j2,...].
:infoShort description of signal (= description of FMI 3.0 and of Modelica).
:aliasString: signal[:value] is a reference to getSignal(signalTable, signal[:alias])[:value]. The reference is set and attributes are merged when the Par-signal is added to the signal table.

Additionally, any other signal attributes can be stored in signal with a desired key, such as Variable Types of FMI 3.0.

Example

using SignalTables

J         = Par(value = 0.02, unit=u"kg*m/s^2", info="Motor inertia")
fileNames = Par(value = ["data1.json", "data2.json"])
J_alias   = Par(alias = "J")
SignalTables.VarMethod
signal = Var(; kwargs...)::OrderedDict{Symbol,Any}

Returns a variable signal definition in form of a dictionary. kwargs... are key/value pairs of variable attributes.

The :values key represents a signal array of any element type as function of the independent signal(s) (or is the k-th independent variable). A signal array has indices [i1,i2,...,j1,j2,...] to hold variable elements [j1,j2,...] at the [i1,i2,...] independent signal(s). If an element of a signal array is not defined it has a value of missing. Furthermore, additional attributes can be stored.

The following keys are recognized (all are optional with exception of :values that must be either directly defined or via :alias):

keyvalue (of type String, if not obvious from context)
:valuesArray{T,N}: signal[:values][i1,i2,...j1,j2,...] is value [j1,j2,...] at the [i1,i2,...] independent signal(s), or signal[:values][i_k] is value [i_k] of the k-th independent variable.
:unitString: Unit of all signal elements (parseable with Unitful.uparse), e.g., "kg*m*s^2". Array{String,N}: signal[:unit][j1,j2,...] is unit of variable element [j1,j2,...].
:infoShort description of signal (= description of FMI 3.0 and of Modelica).
:independent= true, if independent variable (k-th independent variable is k-th Var insignal table)
:variability"continuous", "clocked", "clock", "discrete", or "tunable" (parameter).
:state= true, if signal is a (continuous, clocked, or discrete) state.
:derString: getSignal(signalTable, signal[:der])[:values] is the derivative of signal[:values].
:clockString: getSignal(signalTable, signal[:clock])[:values] is the clock associated with signal[:values] (is only defined at clock ticks and otherwise is missing). If Vector{String}, a set of clocks is associated with the signal.
:aliasString: signal[:values] is a reference to getSignal(signalTable, signal[:alias])[:values]. The reference is set and attributes are merged when the Var-signal is added to the signal table.
:interpolationInterpolation of signal points ("linear", "none"). If not provided, interpolation is deduced from :variability and otherwise interpolation is `"linear".
:extrapolationExtrapolation outside the values of the independent signal ("none").

Additionally, any other signal attributes can be stored in signal with a desired key, such as Variable Types of FMI 3.0.

Example

using SignalTables

t = (0.0:0.1:0.5)
t_sig = Var(values = t, unit=u"s",  independent=true)
w_sig = Var(values = sin.(t), unit="rad/s", info="Motor angular velocity")
c_sig = Var(values = [1.0, missing, missing, 4.0, missing, missing],
            variability="clocked")
b_sig = Var(values = [false, true, true, false, false, true])
a_sig = Var(alias = "w_sig")
SignalTables.compactPathsMethod
compactPaths(str::String)

Returns a compacted string, where all paths with dots are reduced to their leaf name. Example: compactPaths("MonteCarloMeasurements.Particles") is reduced to "Particles".

SignalTables.currentPlotPackageMethod
currentPlotPackage()

Return the name of the plot package as a string that was defined with usePlotPackage. For example, the function may return "GLMakie", "PyPlot" or "NoPlot" or or "", if no PlotPackage is defined.

SignalTables.eltypeOrTypeMethod
eltypeOrType(obj)

Returns eltype(obj), if obj is an AbstractArray and otherwise returns typeof(obj).

SignalTables.encodeSignalTableMethod
jsigDict = encodeSignalTable(signalTable; signalNames = nothing)

Encodes a SignalTable suitable to convert to JSON format.

If a keyword signalNames with a vector of strings is provided, then only the corresponding signals are encoded.

SignalTables.encodeSignalTableElementMethod
jsigDict = encodeSignalTableElement(path, signalTableElement; log=false)

Encodes a signal table element suitable to convert to JSON format.

SignalTables.getFlattenedSignalMethod
signal = getFlattenedSignal(signalTable, name;
                            missingToNaN = true,
                            targetInt    = Int,
                            targetFloat  = Float64)

Returns a copy of a signal where the flattened and converted values (e.g.: missing -> NaN) are stored as signal[:flattenedValues] and the legend as signal[:legend]. A flattened signal can be, for example, used for traditional plot functions or for traditional tables.

signal[:flattenedValues] is a reshape of values into a vector or a matrix with optionally the following transformations:

  • name can be a signal name with or without array range indices (for example name = "a.b.c[2,3:5]").
  • If missingToNaN=true, then missing values are replaced by NaN values. If NaN does not exist in the corresponding type, the values are first converted to targetFloat.
  • If targetInt is not nothing, Int-types are converted to targetInt
  • If targetFloat is not nothing, Float-types are converted to targetFloat
  • collect(..) is performed on the result.

flattenedSignal[:legend] is a vector of strings that provides a description for every array column (e.g. if "name=a.b.c[2,3:5]", unit="m/s", then legend = ["a.b.c[2,3] [m/s]", "a.b.c[2,3] [m/s]", "a.b.c[2,5] [m/s]"].

If the required transformation is not possible, a warning message is printed and nothing is returned.

As a special case, if signal[:values] is a vector or signal[:value] is a scalar and an element of values or value is of type Measurements{targetFloat} or MonteCarloMeasurements{targetFloat}, then the signal is not transformed, so signal[:flattenedValues] = signal[:values].

SignalTables.getHeadingMethod
getHeading(signalTable, heading)

Return heading if no empty string. Otherwise, return defaultHeading(signalTable).

SignalTables.getIndependentSignalsSizeMethod
getIndependentSignalsSize(signalTable)::Dims

Returns the lengths of the independent signals as Dims. E.g. for one independent signal of length 5 return (5,), or for two independent signals of length 5 and 7 return (5,7).

SignalTables.getSignalMethod
getSignal(signalTable, name::String)

Returns signal name from signalTable (that is a Var, a Par or a Map). If name does not exist, an error is raised.

SignalTables.getSignalInfoMethod
getSignalInfo(signalTable, name::String)

Returns signal, but Var or Par) without :values or :value but instead with :eltypeOrType (eltype of the values if AbstractArray, otherwise typeof the values) and :size (if defined on the values)

If name does not exist, an error is raised.

This function is useful if only the attributes of a signal are needed, but not their values (returning the attributes might be a cheap operation, whereas returning the values might be an expensive operation).

SignalTables.getSignalNamesMethod
getSignalNames(signalTable; getVar=true, getPar=true, getMap=true)::Vector{String}

Returns a string vector of the signal names that are present in signalTable (including independent signal names).

  • If getVar=true, Var(..) variables are included.
  • If getPar=true, Par(..) variables are included.
  • If getMap=true, Map(..) variables are included.
SignalTables.hasSignalMethod
hasSignal(signalTable, name::String)

Returns true if signal name is present in signalTable.

SignalTables.new_signal_tableMethod
new_signal_table(args...)::OrderedDict{String,Any}

Returns a new signal table, that is OrderedDict{String,Any}("_class" => :SignalTable, args...)

SignalTables.plotMethod
plot(signalTable, names;
     heading = "", grid = true, xAxis = nothing,
     figure = 1, prefix = "", reuse = false, maxLegend = 10,
     minXaxisTickLabels = false,
     MonteCarloAsArea = true)

Generate plots of selected signals of a signal table using the plot package defined with [@usePlotPackage]@ref(xxx). Possible values for xxx: "GLMakie", "WGLMakie", "CairoMakie", "PyPlot", "SilentNoPlot").

signalTable is an instance of a type that supports the Abstract Signal Table Interface.

Argument names defines the diagrams to be drawn and the signals to be included in the respective diagram:

  • If names is a String, generate one diagram with one time series of the variable with key names.

  • If names is a Tuple of Strings, generate one diagram with the time series of the variables with the keys given in the tuple.

  • If names is a Vector or a Matrix of Strings and/or Tuples, generate a vector or matrix of diagrams.

Note, the names (and their units, if available in the signals) are automatically used as legends in the respective diagram.

A signal variable identified by a String key can be a scalar of type <:Number or an array of element type <:Number. A signal is defined by a vector of time values, a corresponding vector of signal values, and the signal type (continuous or clocked).

Note, before passing data to the plot package, it is converted to Float64. This allows to, for example, also plot rational numbers, even if not supported by the plot package. Measurements.Measurement{xxx} and MonteCarloMeasurements is specially handled.

Optional Arguments

  • heading::AbstractString: Optional heading above the diagram.

  • grid::Bool: = true, to display a grid.

  • xAxis::Union{AbstractString,Nothing}: Name of x-axis. If xAxis=nothing, the independent variable of the signal table (usually "time" is used as x-axis.

  • figure::Int: Integer identifier of the window in which the diagrams shall be drawn.

  • prefix::AbstractString: String that is appended in front of every legend label (useful especially if reuse=true).

  • reuse::Bool: If figure already exists and reuse=false, clear the figure before adding the plot. Otherwise, include the plot in the existing figure without removing the curves present in the figure. reuse = true is ignored for "WGLMakie" (because not supported).

  • maxLegend::Int: If the number of legend entries in one plot command > maxLegend, the legend is suppressed. All curves have still their names as labels. In PyPlot, the curves can be inspected by their names by clicking in the toolbar of the plot on button Edit axis, curve .. and then on Curves.

  • minXaxisTickLabels::Bool: = true, if xaxis tick labels shall be removed in a vector or array of plots, if not the last row (useful when including plots in a document). = false, x axis tick labels are always shown (useful when interactively zooming into a plot).

  • MonteCarloAsArea::Bool: = true, if MonteCarloMeasurements values are shown with the mean value and the area between the minimum and the maximum value of all particles. = false, if all particles of MonteCarloMeasurements values are shown (e.g. if a value has 2000 particles, then 2000 curves are shown in the diagram).

Examples

using SignalTables
using Unitful

# Generate "using xxx" statement
# (where "xxx" is from a previous SignalTables.usePlotPackage("xxx"))
@usingPlotPackage

# Construct result data
t = range(0.0, stop=10.0, length=100);
result = Dict{String,Any}();
result["time"] = t*u"s";
result["phi"]  = sin.(t)*u"rad";
result["w"]    = cos.(t)*u"rad/s";
result["a"]    = 1.2*sin.(t)*u"rad/s^2";
result["r"]    = hcat(0.4 * cos.(t), 0.5 * sin.(t), 0.3*cos.(t))*u"m";

# 1 signal in one diagram (legend = "phi [rad]")
plot(result, "phi")

# 3 signals in one diagram
plot(result, ("phi", "w", "a"), figure=2)

# 3 diagrams in form of a vector (every diagram has one signal)
plot(result, ["phi", "w", "r"], figure=3)

# 4 diagrams in form of a matrix (every diagram has one signal)
plot(result, ["phi" "w";
              "a"   "r[2]" ], figure=4)

# 2 diagrams in form of a vector
plot(result, [ ("phi", "w"), ("a") ], figure=5)

# 4 diagrams in form of a matrix
plot(result, [ ("phi",)           ("phi", "w");
               ("phi", "w", "a")  ("r[2:3]",)     ],figure=6)

# Plot w=f(phi) in one diagram
plot(result, "w", xAxis="phi", figure=7)

# Append signal of the next simulation run to figure=1
# (legend = "Sim 2: phi [rad]")
result["phi"] = 0.5*result["phi"];
plot(result, "phi", prefix="Sim 2: ", reuse=true)

Example of a matrix of plots:

Matrix of plots

SignalTables.prepend!Method
prepend!(prefix, signalLegend)

Add prefix string in front of every element of the signalLegend string-Vector.

SignalTables.quantityMethod
quantityType = quantity(numberType, numberUnit::Unitful.FreeUnits)

Returns Quantity from numberType and numberUnit, e.g. quantity(Float64,u"m/s")

Example

using SignalTables
using Unitful

mutable struct Data{FloatType <: AbstractFloat}
    velocity::quantity(FloatType, u"m/s")
end

v = Data{Float64}(2.0u"mm/s")
@show v  # v = Data{Float64}(0.002 m s^-1)

sig = Vector{Union{Missing,quantity(Float64,u"m/s")}}(missing,3)
append!(sig, [1.0, 2.0, 3.0]u"m/s")
append!(sig, fill(missing, 2))
@show sig    # [missing, missing, missing, 1.0u"m/s", 2.0u"m/s", 3.0u"m/s", missing, missing]
SignalTables.saveFigureFunction
saveFigure(figure, file; kwargs...)

Save figure on file. The file extension defines the image format (for example *.png).

Plot packageSupported file extensions
GLMakiepng, jpg, bmp
WGLMakiepng
CairoMakiepng, pdf, svg, eps
PyPlotdepends on backend (usually: png, pdf, jpg, tiff, svg, ps, eps)
SilentNoPlotCall is ignored

Keyword arguments

  • resolution: (width::Int, height::Int) of the scene in dimensionless units (equivalent to px for GLMakie and WGLMakie).

Example

using SignalTables
@usingPlotPackage
...

plot(..., figure=1)
plot(..., figure=2)

saveFigure(1, "plot.png")   # save in png-format
saveFigure(2, "plot.svg")   # save in svg-format
SignalTables.showFigureFunction
showFigure(figure)
Plot packageEffect
GLMakieShow figure in the single window.
WGLMakieShow figure in the single window.
CairoMakieCall is ignored
PyPlotCall is ignored
SilentNoPlotCall is ignored

Example

using SignalTables
@usingPlotPackage
...
plot(..., figure=1)
plot(..., figure=2)
plot(..., figure=3)

showFigure(2)
showFigure(1)
SignalTables.showInfoMethod
showInfo([io::IO=stdout,] signalTable;
         sorted=false, showVar=true, showPar=true, showMap=true, showAttributes=true)

Writes info about a signal table to the output stream. The keyword arguments define what information shall be printed or whether the names shall be sorted or presented in definition order.

Example

using SignalTables
using Unitful

t = 0.0:0.1:0.5
sigTable = SignalTable(
  "time"         => Var(values= t, unit="s", independent=true),
  "load.r"       => Var(values= [sin.(t) cos.(t) sin.(t)], unit="m"),
  "motor.angle"  => Var(values= sin.(t), unit="rad", state=true, der="motor.w"),
  "motor.w"      => Var(values= cos.(t), unit="rad/s"),
  "motor.w_ref"  => Var(values= 0.9*[sin.(t) cos.(t)], unit = ["rad", "1/s"],
                                info="Reference angle and speed"),
  "wm"           => Var(alias = "motor.w"),
  "ref.clock"    => Var(values= [true, missing, missing, true, missing, missing],
                                 variability="clock"),
  "motor.w_c"    => Var(values= [0.8, missing, missing, 1.5, missing, missing],
                                variability="clocked", clock="ref.clock"),
  "motor.inertia"=> Par(value = 0.02f0, unit="kg*m/s^2"),
  "motor.data"   => Par(value = "resources/motorMap.json"),
  "attributes"   => Map(experiment=Map(stoptime=0.5, interval=0.01))
)

signalInfo(sigTable)

results in the following output

name          unit           size  eltypeOrType           kind attributes
───────────────────────────────────────────────────────────────────────────────────────────────────────
time          "s"            [6]   Float64                Var independent=true
load.r        "m"            [6,3] Float64                Var
motor.angle   "rad"          [6]   Float64                Var state=true, der="motor.w"
motor.w       "rad/s"        [6]   Float64                Var
motor.w_ref   ["rad", "1/s"] [6,2] Float64                Var info="Reference angle and speed"
wm            "rad/s"        [6]   Float64                Var alias="motor.w"
ref.clock                    [6]   Union{Missing,Bool}    Var variability="clock"
motor.w_c                    [6]   Union{Missing,Float64} Var variability="clocked", clock="ref.clock"
motor.inertia "kg*m/s^2"           Float32                Par
motor.data                         String                 Par
attributes                                                Map experiment=Map(stoptime=0.5, interval=0.01)
SignalTables.signalTableToJSONMethod
json = signalTableToJSON(signalTable; signalNames = nothing)

Returns a JSON string representation of signalTable

If keyword signalNames with a Vector of strings is provided, then a signal table with the corresponding signals are returned as JSON string.

SignalTables.unitAsParseableStringMethod
v_unit = unitAsParseableString(v::[Number|AbstractArray])::String

Returns the unit of v as a string that can be parsed with Unitful.uparse.

This allows, for example, to store a quantity with units into a JSON File and recover it when reading the file. This is not (easily) possible with current Unitful functionality, because string(unit(v)) returns a string that cannot be parse with uparse. In Julia this is an unusual behavior because string(something) typically returns a string representation of something that can be again parsed by Julia. For more details, see Unitful issue 412.

Most likely, unitAsParseableString(..) cannot handle all occuring cases.

Examples

using SignalTables
using Unitful

s = 2.1u"m/s"
v = [1.0, 2.0, 3.0]u"m/s"

s_unit = unitAsParseableString(s)  # ::String
v_unit = unitAsParseableString(v)  # ::String

s_unit2 = uparse(s_unit)  # :: Unitful.FreeUnits{(m, s^-1), ..., nothing}
v_unit2 = uparse(v_unit)  # :: Unitful.FreeUnits{(m, s^-1), ..., nothing}

@show s_unit   # = "m*s^-1"
@show v_unit   # = "m*s^-1"

@show s_unit2  # = "m s^-1"
@show v_unit2  # = "m s^-1"
SignalTables.usePlotPackageMethod
usePlotPackage(plotPackage::String)

Define the plot package that shall be used by command @usingPlotPackage. If a PlotPackage package is already defined, save it on an internal stack (can be reactivated with usePreviousPlotPackage().

Possible values for plotPackage:

  • "PyPlot"
  • "GLMakie"
  • "WGLMakie"
  • "CairoMakie"
  • "SilentNoPlot"

Example

using SignalTables
usePlotPackage("GLMakie")

module MyTest
    using SignalTables
    @usingPlotPackage

    t = range(0.0, stop=10.0, length=100)
    result = Dict{String,Any}("time" => t, "phi" => sin.(t))

    plot(result, "phi")  # use GLMakie for the rendering
end
SignalTables.usePreviousPlotPackageMethod
usePreviousPlotPackage()

Pop the last saved PlotPackage package from an internal stack and call usePlotPackage(<popped PlotPackage package>).

SignalTables.writeSignalTableMethod
writeSignalTable(filename::String, signalTable; signalNames=nothing, indent=nothing, log=false)

Write signalTable in JSON format on file filename.

If keyword signalNames with a Vector of strings is provided, then a signal table with the corresponding signals are stored on file.

If indent=<number> is given, then <number> indentation is used (otherwise, compact representation)

SignalTables.@usingPlotPackageMacro
@usingPlotPackage()

Execute using XXX, where XXX is the Plot package that was activated with usePlotPackage(plotPackage).