Skip to main content

Reading captured data

Everything the capture produced: raw transitions, decoded packets, and the hex view. Reached through workspace.data.

Bulk data is pulled, never pushed. Nothing crosses the wire until you ask, and reading slowly never slows the acquisition down.

transitions​

The edges on one channel, oldest first. The SDK pages the request for you, so you can walk a very long capture without holding it in memory.

for edge in workspace.data.transitions(channel=0):
edge.sample # where it happened
edge.level # the level it changed TO (0 or 1)

# A window, and a count:
edges = list(workspace.data.transitions(channel=0, start=0, end=1_000_000))
print(f"{len(edges)} transitions")

Example output. The first few edges of channel 0, printed as sample -> level:

0 -> 0
0 -> 1
172 -> 0
1043 -> 1
1371 -> 0

The first record is the level in force at sample 0, which is why two records share sample 0 here.

The first edge is not necessarily inside your window

The first record you get back is the level in force where you started reading, so its sample index may be earlier than start. This way a window of transitions tells you what the channel was doing when the window opened, without a second query.

Parameters: channel, start (default 0), end (default: the end of the capture), and the page size, which you rarely need to touch.

level_at, next_edge, previous_edge​

Point queries, for when you want one answer rather than a stream.

workspace.data.level_at(channel=0, sample=1_000) # 0, 1 or None
workspace.data.next_edge(channel=0, after=1_000) # sample index or None
workspace.data.previous_edge(channel=0, before=1_000) # sample index or None

Returns: the level, or the edge's sample index. Nothing (None / null / Option::None) when there is no such edge.

packets​

The decoded packets, merged across every decoder attached to the workspace, in time order. This is the Packet View you see in ScanaStudio.

for packet in workspace.data.packets():
packet.instance_id # which decoder produced it
packet.root # True for a top-level packet, False for a child
packet.channel
packet.start # sample index
packet.end
packet.title # "Address", "Data", "Start"...
packet.content # "0x4E", "ACK"...

Example output. An I²C decoder over a demo capture, printed as time title: content:

0.000006s I2C: CH4
0.000006s START:
0.000020s RE-START:
0.000032s STOP:
0.000038s I2C: CH4

The I2C rows are the root packets, and START / STOP / RE-START their children. packet.root tells them apart. On real bus traffic you would also see Address and Data rows carrying values like 0x4E.

Filtering​

A filter narrows the read on the server, so a big capture does not have to cross the wire to be searched.

FieldKeeps packets…
titlewhose title contains this text
contentwhose content contains this text
sourcesproduced by these decoder instance ids
channelson these channels
from, toinside this sample range
dur_min, dur_maxof at least / at most this duration
from ikalogic_scanastudio import RowFilter

errors = list(workspace.data.packets(filter=RowFilter(title="Error")))
mine = list(workspace.data.packets(filter=RowFilter(sources=[i2c.instance_id])))
Read one decoder's packets directly

A decoder instance can hand you its own packets without a filter, with decoder.packets(). See Decoders.

hex and find_hex​

The byte-level view of everything the decoders produced, and a search in it. This is what you want for payload and firmware work: the bytes on the bus, without the packet structure around them.

for entry in workspace.data.hex():
entry.byte # 0-255
entry.channel
entry.start # sample index
entry.end

# Find a byte pattern; returns the offset in the hex view, or None.
offset = workspace.data.find_hex(b"\xDE\xAD\xBE\xEF")
backwards = workspace.data.find_hex([0x55, 0xAA], start=10_000, backwards=True)

find_hex returns the offset of the match in the hex view, or nothing when the pattern is not there.

detect_baud​

Estimates a channel's baud rate from the signal itself, using the shortest interval between transitions. Useful for an unlabelled UART.

measured, standard = workspace.data.detect_baud(channel=4)
print(f"{measured:.0f} Bd, nearest standard rate {standard:.0f}")

Example output.

235849 Bd, nearest standard rate 230400

Returns: two numbers: the rate measured, and the nearest standard rate. Feed the standard one into a UART decoder's baud option.

A channel with too little traffic measures 0

Detection needs transitions to work from. A quiet channel comes back as 0 for both numbers rather than as an error, so check the result before using it.