Repository navigation
Simulators
This page explains the simulated devices of openSciLab: what they are for, the profiles and their limits, what their channels carry, wires, the emulated USB link, faults, real and virtual time, and how to write a profile of your own.
Every function of openSciLab works without hardware. A simulator behaves like the real device - with its channels and pins, its limits (rates, memory depth, stream throughput, latency, reserved pins), its wiring and its faults - so you can
- learn the application and try the templates (all of them run with simulators),
- build and test a flow or a panel before the hardware is on the desk, then change one address,
- run a flow as a repeatable test, in virtual time and with the same result every time,
- practise synchronization, latency and error handling that are hard to provoke on a real board.
A simulator is not a circuit simulator: each channel carries a signal that is a function of time (a clock, a UART text, a sine, a bouncing button …), and wires copy a pin to another one, through an RC low pass or a threshold if wanted.
- Start page → Try a simulator, then a profile. Its device card opens.
- Devices sidebar → Available → Simulators (n): a click opens the group, a double-click on an entry connects it. In the group the names are shown without the prefix Simulation:.
-
Devices → Connect... (
Ctrl+Shift+K), group Simulators. -
In a flow or project, a device with the address
sim:<profile>, e.g.sim:uno. -
On the command line and in Python:
openscilab-cli capture --device sim:free …,api.open("sim:free"),api.instrument("sim:uno"); see Scripting.
Every simulator can be connected several times; each copy is a device of its own with its own
pins, signals and state. Choosing a simulator that is connected already connects the next one
(sim:uno#2, sim:uno#3). Settings → Devices → List the simulators with the devices hides them
from the device list.
| Address | Name in the application | What it simulates |
|---|---|---|
sim:free |
Simulation: free | a 16 channel logic analyzer with test signals: counter on D0-D7 (1 MHz, clock on D8), UART on D9, SPI on D10-D12, I²C on D13-D14, 1 kHz on D15; a pattern generator on D0-D7 (looped back); trigger input TRIG IN on D15. 100 MHz, 4 M samples |
sim:uno |
Simulation: Arduino Uno | an Arduino Uno with the openSciLab firmware: GPIO, PWM, A0-A5 (10 bit, 5 V). Captures of 448 samples (8 channels) up to 100 kHz, pins only, at most 2 s after the trigger; streams of 11.5 kB/s up to 20 kHz. Wired: D7 → D3, PWM of D9 → RC low pass (10 kΩ, 10 µF) → A0, a bouncing push button on D2 |
sim:uno_r4 |
Simulation: Uno R4 | an Arduino Uno R4: like the Uno with D0/D1 free, a 14 bit ADC and a 12 bit DAC on A0. Captures of 12288 samples up to 1 MHz, streams of 500 kB/s up to 100 kHz. Wired: DAC (A0) → A1, D7 → D3, PWM of D9 → RC → A2 |
sim:pico |
Simulation: Pico | a Raspberry Pi Pico with the firmware 8: 24 channels (GP2-GP22, GP26-GP28), 100 MHz, GPIO and PWM on every channel pin, ADC on GP26-GP28, pattern generator, streams of 800 kB/s; its blocks arrive as over USB |
sim:daq |
Simulation: USB DAQ | a measuring box in the class of an NI USB-6001: AI0-AI7 (±10 V, 14 bit, 20 kS/s for all inputs together), AO0/AO1, digital lines P0.0-P0.7 and P1.0-P1.3; blocks arrive as over USB, its sample clock runs 30 ppm fast. Wired: P0.0 → P0.1, AO0 → AI2; P0.7 free for a sync signal |
sim:dho924s |
Simulation: DHO924S | a Rigol DHO924S: 4 analog channels (12 bit) and 16 logic channels; large captures arrive piece by piece over a simulated network; generator output on CH1 with its sync on D15 |
openscilab sim lists the profiles, openscilab sim <profile> shows one with its limits and
circuit:
$ openscilab sim uno
sim:uno Simulation: Arduino Uno
...
Maximum rate 100,000 Hz
Memory depth 448 samples
Stream 11,520 bytes/s
Circuit:
D2 button
D3 wired to D7
A0 RC low pass of D9 (τ = 100 ms)
The numbers are provisional. The limits of uno, uno_r4, pico and dho924s are those of
the firmware and the instrument as far as they are known; they will be corrected once they are
measured on the real devices. The DAQ is a device of its class, not a model of one measured box.
| Address | Name in the application | What it is |
|---|---|---|
sim:pico*2 (up to *5; also sim:free*2 …) |
Simulated multi device... | 2 to 5 boards captured together as one device, as a multi device set of real boards. The dialog asks for the Board (any profile with logic channels) and the number of Boards. Channels 1-24 are board 1, 25-48 board 2, the pins are called B1.GP2, B2.GP2, …. A set only captures: no GPIO, analog inputs, generator or stream |
arduino-sim:uno, arduino-sim:uno_r4
|
Simulation: Arduino Uno (firmware protocol), … | the same Arduino, but behind the real serial protocol and driver of the openSciLab Arduino firmware |
rigol-sim:bridge, rigol-sim:direct
|
Simulation: DHO924S with bridge app, … without bridge app | the oscilloscope behind the real SCPI driver, with or without the bridge app |
remote-sim:climate, audio, echo, daq
|
Simulation: remote climate station, … | devices on another computer behind an emulated network; see Remote devices |
By default the channels carry the test signals of the profile. The Signals tab of a simulator's device card puts something else on them - at once, also while a capture runs. Choose a scenario in Simulates, fill in its fields and press Apply:
| Scenario | Signals |
|---|---|
| Test signals of the device | what the profile connects |
| UART | 8N1 frames of a text on one channel, repeated (text, baud rate, channel) |
| SPI | clock, MOSI and chip select on three channels in a row |
| I²C | SCL and SDA on two channels in a row |
| UART, SPI and I²C | UART on channel 1, SPI on 2-4, I²C on 5-6 |
| Counter | an 8 bit counter on the first channels, its clock on the next one |
| C64 bus | a Commodore 64 at its expansion port running a small program; needs 48 channels, i.e. sim:pico*2
|
| Capture file | the channels of a .lac file, played at its rate and repeated |
| Nothing connected | every channel low |
A scenario a device cannot use is shown greyed out with the reason. Name the channels after their
signals (TX, SCL, A0, …) for the next capture names the channels in the data view. A
double-click on a channel in the table (or select it and Set signal of the selected channel...) gives that channel a signal
of its own: Square wave, Pulses, UART, SPI line, I²C line, Counter bit, Random levels,
Constant level, C64 bus line, Channel of a capture file, Sine (analog) or Triangle
(analog), with settings typed as key: value, e.g. frequency: 2 kHz, duty: 0.25.
What the device drives itself - its outputs, a running generator, the wiring of its profile - stays. The signals are kept per device address and come back with the next connection. The Events tab lists what the simulated device did: pin changes, pulses, PWM, generators, the signals, wires and faults.
In a flow, the device node says what to simulate (parameter signals):
nodes:
uart: {type: device.instrument, address: "sim:free",
signals: {scenario: uart, text: "Hello\n", baud: 9600, channel: 1}}
c64: {type: device.instrument, address: "sim:pico*2", signals: {scenario: c64}}
own: {type: device.instrument, address: "sim:uno#2",
signals: {channels: {D5: {type: square, frequency: 1 kHz}}}}To see the C64 at work: connect Simulated multi device... with Simulation: Pico and 2 boards, choose C64 bus on its Signals tab, load the profile C64 expansion port - board B (master) + board A (slave) in the data view (Profiles → Standard profiles) and capture; see C64 bus.
Wires connect pins of a simulator: an output to an input (D7 → D3), through an RC low pass
(D9 → A0), or an analog net read as logic with a threshold. Outputs (GPIO, DAC, generator)
drive their net while they are on, so whatever is wired to them follows.
- The profiles bring their wiring (see the table above).
- Signals tab → Wires → Add wire... adds one (From a pin, To a channel); Remove takes it away. Wires are kept per device address, like the signals.
- The Timing tab has Wire them (simulator): it adds the loopback that measures the latency in one click; see Time and synchronization.
- A project adds wires per device without touching the profile, also between two simulated devices of the project:
# project.yaml
devices: {uno: "sim:uno", scope: "sim:dho924s"}
simulation:
wiring:
scope: [{from: "uno:D9", to: CH1}] # the probe of CH1 on D9 of the UnoWires that lead back to themselves (D0 → D1 → D0) are refused.
A real device on USB delivers its samples late, in frames and unevenly, and its crystal runs a little fast or slow. Simulators can do the same, so that flows have to find the time of the samples as with the real device. The group USB link and clock on the Signals tab:
- Emulate a USB link with Latency, Jitter and Frame (milliseconds),
- Clock drift in ppm (how much faster the device's sample clock runs),
- Apply.
sim:pico emulates USB by default (1 ms frames, 1.2 ms latency, 0.4 ms jitter, a 128 KiB buffer
that overflows when the computer reads too late), sim:daq also runs 30 ppm fast. The settings
are kept per device address. What to do about latency and drift is explained on
Time and synchronization.
Devices → Simulate faults has a sub-menu for every connected simulator; its entries inject a fault:
| Entry | Effect |
|---|---|
| Disconnect | the device does not answer: captures and GPIO fail, it is shown as disconnected |
| Reconnect | it answers again |
| Delay answers by 100 ms / Answer without delay | answers come late, or on time again |
| Overflow the next stream | the next stream overflows and ends early |
| Restart (outputs released) | the device restarted: pins are inputs again, generators stopped, analog outputs at 0 V |
Scripts do the same with device.inject("disconnect") (for api.open) or
instrument.simulated_driver.inject(...).
- Real time (default): the simulators run on the clock of the computer. All simulators connected in the window share one clock, so wired simulators agree on the time. A capture records what the signals and outputs do until it ends, as the device does.
-
Virtual time: tick Fast (virtual time) in the tool bar of a flow, or add
--fasttoopenscilab run. The simulators run on the clock of the flow, as fast as the computer can, and with the same seed (flow settingseed,--seed N) a run gives the same samples every time. A capture lets the flow's time run on while it waits for its trigger, so a stimulus the flow starts after arming it is in the capture. The status bar shows Virtual time.
Virtual time is for simulators only. To run a flow written for real hardware without it, tick
Simulate devices in the flow's tool bar (--sim on the command line): every device becomes a
simulator - a Pico (also over the network or as a set) sim:pico, an Arduino sim:uno, a Rigol
sim:dho924s, anything else sim:free. Remote devices (remote:…) have no stand-in; use a
remote-sim:… address instead.
A profile is a JSON file. Put your own into the folder sim of the settings directory (see
Installation); the file name is the profile name,
so bench.json is sim:bench. Give it a name of its own: a file named like a profile that comes
with openSciLab is not used. The shipped profiles in
examples/sim/ are good
templates.
{
"title": "Simulation: test bench",
"description": "8 logic channels and 2 analog inputs; D7 wired to D6.",
"digital": ["D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7"],
"analog": ["A0", "A1"],
"pins": [
{"name": "D6", "caps": ["DIN"]},
{"name": "D7", "caps": ["DIN", "DOUT", "PWM"]},
{"name": "A0", "caps": ["ADC"], "analog_channel": 0},
{"name": "A1", "caps": ["ADC"], "analog_channel": 1}
],
"max_rate": 10000000,
"memory_depth": 65536,
"stream_bandwidth": 1000000,
"capabilities": ["IMMEDIATE_TRIGGER", "GPIO", "MONITOR"],
"circuit": {
"sources": {
"D0": {"type": "clock", "frequency": "1 kHz"},
"D1": {"type": "uart", "text": "Hello\n", "baud": 9600},
"D2": {"type": "pulse", "period": "1 ms", "width": "100 us"},
"A0": {"type": "sum", "parts": [
{"type": "sine", "frequency": "50 Hz", "amplitude": "1 V", "offset": "1.65 V"},
{"type": "analog_noise", "rms": "10 mV"}]}
},
"wiring": [{"from": "D7", "to": "D6"}]
}
}The most important keys:
| Key | Meaning |
|---|---|
title, description
|
the name in the application, the text |
digital, analog
|
the names of the logic channels and analog inputs, in channel order |
pins |
pins with their capabilities (DIN DOUT PULLUP PULLDOWN PWM ADC DAC CLOCK CLOCK_SW); analog_channel links a pin to an analog input; reserved makes a pin unusable and says why |
max_rate, memory_depth
|
highest sample rate (Hz), samples per channel of a buffer capture |
stream_bandwidth |
bytes per second a stream may use (0: no streams) |
capabilities |
what the device reports, e.g. IMMEDIATE_TRIGGER (capture without a trigger), GPIO, PWM, MONITOR, DAC, STATE_MODE
|
logic_level |
volts of a logic 1 |
usb, clock_drift
|
the emulated USB link and the drift of the sample clock |
circuit |
sources per net and wiring
|
Signal sources (type): constant, square, clock, pulse, counter, uart, spi, i2c,
noise, sine, triangle, ramp, analog_noise, sum, button, c64, file. Every key and
every parameter is listed in
docs/simulator.md.
openSciLab reads the folder when the device list or openscilab sim asks; a changed file is read
again. Check a new profile with openscilab sim bench.
openSciLab · 0.1 beta
Instruments
Logic analyzer
The lab
More