This tutorial shows you how to write a Simulation.
- Step 1: Create a class
- Step 2: Override run
- Step 3: Add input data to the input map
- Step 4: Use output data to simulate input data
A Simulation is simply a MATLAB class that subclasses from symphonyui.core.Simulation.
Create a new class in your personal Symphony package by navigating to the package in MATLAB's Current Folder, right-clicking on the "+simulations" directory, and selecting New File > Class.
Name the class file "Demo.m" and open it in the MATLAB Editor.
classdef Demo
%DEMO Summary of this class goes here
% Detailed explanation goes here
properties
end
methods
end
endRemove the comments and properties block and edit the classdef line to subclass from the symphonyui.core.Simulation class.
classdef Demo < symphonyui.core.Simulation
methods
end
endYou now have an empty Simulation.
When a simulated DAQ controller needs to "acquire" data, it calls the run() method of a simulation and passes it the data its "outputting" and the duration of the input data its expecting the simulation to generate. The simulation then decides how to generate input data based on these parameters.
The simulation run() method is generally called many times over the course of a complete run. The simulated DAQ controller use the simulation to generate only small chunks of data at a time. The duration of these chunks is determined by the simulation time step. The simulation time step is generally 0.5 seconds but may vary.
Override the run() method in the "Demo" simulation so you can define what it does when a DAQ controller needs to simulate input data for a simulation time step.
classdef Demo < symphonyui.core.Simulation
methods
function inputMap = run(obj, daq, outputMap, timeStep)
end
end
endThe run() method take three parameters:
- "daq" - The current
DaqController. - "outputMap" - A map from output stream names (i.e. channels) to output data for the current simulation time step.
- "timeStep" - The duration of the current simulation time step.
Given these parameters, the run() method must return a single value:
- "inputMap" - A map from input stream names (i.e. channels) to input data for the current simulation time step.
Create an "inputMap" variable by instantiating an empty map.
classdef Demo < symphonyui.core.Simulation
methods
function inputMap = run(obj, daq, outputMap, timeStep)
inputMap = containers.Map();
end
end
endAdd a key to the "inputMap" with the name of each active input streams in the current DaqController.
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Add a key for the active input stream with an empty value.
inputMap(inStream.name) = [];
endYou now have a Simulation that returns a map with no input data.
classdef Demo < symphonyui.core.Simulation
methods
function inputMap = run(obj, daq, outputMap, timeStep)
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Add a key for the active input stream with an empty value.
inputMap(inStream.name) = [];
end
end
end
endIf you ran this simulation as is, epochs would never complete because you are not yet producing any input data. You must associate each key in the input map with simulated "acquired" data for the simulation time step.
Simulate a vector of noise data for each input stream by using rand() for analog input streams and randi() for digital input streams.
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Simulate input data.
rate = inStream.sampleRate;
nsamples = seconds(timeStep) * rate.quantityInBaseUnits;
if strncmp(inStream.name, 'diport', 6)
% Simulate digital noise.
quantities = randi(2^16-1, 1, nsamples);
else
% Simulate analog noise.
quantities = rand(1, nsamples) - 0.5;
end
inputMap(inStream.name) = [];
endUse the simulated "quantities" vector to instantiate an InputData object for each key in the map.
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Simulate input data.
rate = inStream.sampleRate;
nsamples = seconds(timeStep) * rate.quantityInBaseUnits;
if strncmp(inStream.name, 'diport', 6)
% Simulate digital noise.
quantities = randi(2^16-1, 1, nsamples);
else
% Simulate analog noise.
quantities = rand(1, nsamples) - 0.5;
end
units = inStream.measurementConversionTarget;
inputMap(inStream.name) = symphonyui.core.InputData(quantities, units, rate);
endYou now have a fully functioning Simulation that simulates noise on each input stream.
classdef Demo < symphonyui.core.Simulation
methods
function inputMap = run(obj, daq, outputMap, timeStep)
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Simulate input data.
rate = inStream.sampleRate;
nsamples = seconds(timeStep) * rate.quantityInBaseUnits;
if strncmp(inStream.name, 'diport', 6)
% Simulate digital noise.
quantities = randi(2^16-1, 1, nsamples);
else
% Simulate analog noise.
quantities = rand(1, nsamples) - 0.5;
end
units = inStream.measurementConversionTarget;
inputMap(inStream.name) = symphonyui.core.InputData(quantities, units, rate);
end
end
end
endThe "outputMap" parameter passed into the simulation function allows you to access the output data for each active output stream in the current simulation time step. You can use this output data to determine how you want your simulation function to simulate input data.
Add an additional if statement to the "Simulate input data" if block to determine if the current stream in the loop is the analog input 0 stream (i.e. if it has the stream name "ai0"). If so, get a vector of the quantities for the output data on the analog output 0 stream from the "outputMap" and use it (with some added noise) as input data for the analog input 0 stream.
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Simulate input data.
rate = inStream.sampleRate;
nsamples = seconds(timeStep) * rate.quantityInBaseUnits;
if strcmp(inStream.name, 'ai0')
% Simulate the output signal + noise.
outData = outputMap('ao0');
[outQuantities, outUnits] = outData.getData();
quantities = outQuantities + rand(1, nsamples) - 0.5;
elseif strncmp(inStream.name, 'diport', 6)
% Simulate digital noise.
quantities = randi(2^16-1, 1, nsamples);
else
% Simulate analog noise.
quantities = rand(1, nsamples) - 0.5;
end
units = inStream.measurementConversionTarget;
inputMap(inStream.name) = symphonyui.core.InputData(quantities, units, rate);
endYou now have a fully functioning Simulation that simulates a loopback of the analog output 0 signal on the analog input 0 stream and noise on all other input streams.
classdef Demo < symphonyui.core.Simulation
methods
function inputMap = run(obj, daq, outputMap, timeStep)
inputMap = containers.Map();
inputStreams = daq.getInputStreams();
for i = 1:numel(inputStreams)
inStream = inputStreams{i};
if ~inStream.active
% We don't care to process inactive input streams (i.e. channels without devices).
continue;
end
% Simulate input data.
rate = inStream.sampleRate;
nsamples = seconds(timeStep) * rate.quantityInBaseUnits;
if strcmp(inStream.name, 'ai0')
% Simulate the output signal + noise.
outData = outputMap('ao0');
[outQuantities, outUnits] = outData.getData();
quantities = outQuantities + rand(1, nsamples) - 0.5;
elseif strncmp(inStream.name, 'diport', 6)
% Simulate digital noise.
quantities = randi(2^16-1, 1, nsamples);
else
% Simulate analog noise.
quantities = rand(1, nsamples) - 0.5;
end
units = inStream.measurementConversionTarget;
inputMap(inStream.name) = symphonyui.core.InputData(quantities, units, rate);
end
end
end
endTo use a Simulation, refer to the "Set the Simulation for a DAQ Controller" tutorial.
