Skip to main content

Decoders

A protocol decoder turns logic transitions into packets: addresses, data bytes, ACKs, frame errors. Decoders are the JavaScript files in the server's script library: the same ones ScanaStudio uses, and the same ones you can write yourself.

Reached through workspace.decoders.

Finding the available scripts​

The script library belongs to the server, not to a workspace, so it is reached through the client rather than the workspace.

Read the script list after creating a workspace

The server sends its script list shortly after connect() returns, so reading it immediately can give an empty list, and scripts.find("i2c.js") then reports the decoder missing when it is installed.

Two reliable orders:

# 1. Create the workspace first. By then the list has arrived.
workspace = server.create("se254")
if server.scripts.find("i2c.js") is None:
...

# 2. Or wait for the list, if you must check before opening anything.
import time

deadline = time.monotonic() + 10.0
while not server.scripts.all() and time.monotonic() < deadline:
time.sleep(0.05)

decoders.add() itself is unaffected: it names the script on the server, which knows its own library. Only the client-side listing is affected.

for script in server.scripts.decoders():
script.file_name # "i2c.js" — what add() takes
script.name # "I2C"
script.version
script.author
script.description

server.scripts.find("i2c.js") # a ScriptDesc, or None
server.scripts.all() # every script, decoders and generators
server.scripts.generators() # pattern-generator scripts
server.scripts.library_dir # where they live on the server

Example output. A stock library holds 44 scripts, 42 decoders and 6 generators (a few do both):

1-wire.js — 1-Wire
ADC.js — ADC
FDX-B.js — FDX-B
IR_NEC.js — IR_NEC
LPC (Low Pin count).js — LPC (Low Pin Count)
NMEA_2000.js — NMEA_2000
...
Installing scripts is not covered here

The SDKs can also install, create, rename and delete scripts, but this documentation leaves that out (see Feature coverage). If a decoder is missing from a bench, install it in ScanaStudio once.

options​

The options a script takes. A decoder is a script, so the SDK cannot know its settings in advance; ask before you configure.

for option in workspace.decoders.options("i2c.js"):
print(option)
option.id # "ch_scl" — what add() keys on
option.caption # "SCL channel" — also accepted as a key
option.type # "channel", "combo", "number"...
option.default
option.choices # for a combo
option.unit

Example output. i2c.js on an SE254. Each line is id [tab]: caption (type) choices unit = default:

ch_sda: SDA Channel (ch_selector) = None
ch_scl: SCL Channel (ch_selector) = None
AUTO0: Advanced options (tab) = False
address_opt [Advanced options]: Address convention (combo) one of ['7 bit address', '8 bit address (inlcuding R/W flag)'] = '7 bit address'
address_format [Advanced options]: Address display format (combo) one of ['HEX', 'Binary', 'Decimal'] = 'HEX'
data_format [Advanced options]: Data display format (combo) one of ['HEX', 'Binary', 'Decimal', 'ASCII'] = 'HEX'
en_noise_flter [Advanced options]: Ignore high-frequency noise on data and clock lines (checkbox) = False

Only ch_scl and ch_sda have no default, so they are the two you must supply. Everything under [Advanced options] can be left alone.

add​

Attaches a decoder instance and starts it decoding.

i2c = workspace.decoders.add(
"i2c.js",
{
"ch_scl": workspace.channel("I2C SCL"),
"ch_sda": workspace.channel("I2C SDA"),
},
wait=True, # block until decoding is finished
)
i2c.instance_id
i2c.name

Parameters:

  • the script's file name, e.g. i2c.js;
  • the option values, keyed by id or caption. Anything you leave out keeps the script's own default;
  • wait — block until decoding finishes, rather than returning immediately.

Returns: the decoder instance.

Errors: ScriptError carries the script's own log lines when it rejects its configuration or fails; they say what the script did not like. An option name the script does not know raises UnknownOption, which lists the known ones.

Pass channels by name

workspace.channel("I2C SCL") keeps one test script working across every device that has those channels. Hard-coded channel numbers break when the bench changes.

Reading a decoder's results​

for packet in i2c.packets():
print(packet.start, packet.title, packet.content)

for entry in i2c.hex_bytes(): # this decoder's bytes
print(hex(entry.byte))

page = i2c.hex(offset=0, count=5000) # a page of them
page.total
page.bytes

Packet fields are described under Reading captured data, and both packets() and items() accept the same filter.

Example output. The first packets of a decoded capture:

496 I2C CH4
433 START
1138 RE-START
1837 STOP

wait and progress​

Decoding runs on the server, asynchronously. progress is 0–100.

i2c.wait(timeout=300.0) # this decoder
workspace.decoders.wait() # every attached decoder
i2c.progress # 0-100

Managing instances​

workspace.decoders.all() # every attached instance
workspace.decoders.get(instance_id) # one, or None
len(workspace.decoders)

i2c.update({"ch_scl": 5}) # reconfigure and restart decoding
i2c.relaunch() # decode again from scratch
i2c.pause() # stop decoding; results produced so far stay
i2c.remove() # detach it

workspace.decoders.clear() # detach every decoder
A demo device decodes noise

Running a decoder on a demo device finds structure by chance, because the signal is pseudo-random rather than protocol traffic. Assert that decoding happened, not that a particular frame came out.