Skip to main content

Capture

Arming, running and following an acquisition. Reached through workspace.capture.

run​

Start a capture and wait for it to finish. The one call most scripts need.

from ikalogic_scanastudio import Trigger

last = workspace.capture.run(
samples=1_000_000,
sample_rate=25_000_000,
trigger=Trigger.rising(channel=0, position=0.1),
timeout=300.0,
)
print(f"captured up to sample {last}")

Returns: the index of the last sample captured.

Choosing a sample rate​

A device does not sample at any rate you like: it has a fixed list, and server.capabilities(device).sample_rates is that list, descending.

A rate that is not on the list is not refused. The server picks one for you:

The rule

The server rounds down to the nearest offered rate, never up. Sampling faster than requested would fill the acquisition sooner than planned. The one exception is a request below the slowest rate the device has, which rounds up to that rate.

Measured on an SP259, whose rates are [250M, 125M, 25M, 12.5M, 6.25M, 2.5M]:

You ask forYou getWhy
250 000 000250 000 000exactly on the list
999 000 000250 000 000above the fastest → the fastest
100 000 00025 000 000rounds down past 125 M, which is faster than asked
26 000 00025 000 000rounds down
1 0002 500 000below the slowest → the slowest

That third row is the one that surprises people: 100 MS/s lands on 25 MS/s, not 125 MS/s, because 125 MS/s is faster than you asked for.

Always read the rate back. capture.sample_rate is what the device settled on, and it is always a member of capabilities().sample_rates:

workspace.capture.run(samples=1_000_000, sample_rate=100_000_000)
print(workspace.capture.sample_rate) # 25000000 on an SP259

# Or pick from the list up front, and get exactly what you chose.
caps = server.capabilities("sp259")
rate = next(r for r in caps.sample_rates if r <= 100_000_000)
workspace.capture.run(samples=1_000_000, sample_rate=rate)
Timing computed from the requested rate will be wrong

Dividing by the rate you asked for, rather than the one in capture.sample_rate, gives results that are consistently off. On the 100 MS/s example above, that is a factor of four.

start and stop​

start arms the capture and returns immediately. Use it when the script should not wait, as in Headless datalogger.

workspace.capture.start(
samples=2_000_000_000,
sample_rate=25_000_000,
trigger=Trigger.immediate(),
)
# ... the script can exit here; the capture keeps running on the server

workspace.capture.stop()

Options. Anything left out keeps the device's default:

OptionMeaning
samplesHow many samples to capture.
sample_rateSamples per second. The device rounds to a rate it offers; read sample_rate back to learn what it settled on.
triggerWhat arms the capture. See Triggers. Absent starts at once.
modetiming samples on the device's internal clock (the default); state samples on an external one.
state_clock_channel, state_edgeWhich channel clocks state mode, and on which edge.
logic_levelsInput thresholds in volts, per channel group.
pullsPer-channel pull: up, down or none.
probesPer-port probe model, on families with removable probes.
industrialPer-channel industrial receiver settings.

stop ends the acquisition; whatever was already captured stays and stays readable.

wait​

Blocks until the capture reaches a terminal state.

last = workspace.capture.wait(timeout=300.0)

Errors: a timeout is raised as Timeout (Error::Timeout in Rust). The capture is not stopped by a timeout; decide whether to stop() or keep waiting.

wait() on a finished capture times out

wait() waits for the capture to reach a terminal state; it does not return early if it is already in one. Attaching to a workspace that has already finished and calling wait() blocks until the timeout. Check running, or capturing in the workspaces() listing, before waiting on a session you did not start yourself.

state and running​

workspace.capture.state # "sampling"
workspace.capture.running # True while the capture is live

The states, in the order they occur:

StateMeaning
idleNothing has been started.
configuringThe device is being set up.
purgingOld data is being cleared out.
pretriggerFilling the pre-trigger buffer, so the trigger has history behind it.
waiting_triggerArmed, waiting for the trigger condition.
samplingCapturing.
doneFinished normally.
errorFinished badly; the log says why.

idle, done and error are terminal; the rest mean the capture is running.

last_sample, sample_rate, seconds​

last = workspace.capture.last_sample # -1 before anything is captured
rate = workspace.capture.sample_rate # what the DEVICE settled on
print(f"{workspace.capture.seconds(last):.6f} s of signal")
Always convert with the rate you got back

The device rounds the rate you asked for to one it can produce. Timing a signal with the rate you requested rather than the one in sample_rate gives results that are consistently off.

trigger_count​

How many times the trigger has fired. It only moves in normal mode, where the capture re-arms after each trigger.

workspace.capture.trigger_count

config​

The capture configuration the server actually applied: the settled sample rate, the trigger definition, the thresholds. Useful for logging what a test bench ran with.

config = workspace.capture.config # None before the first capture
When a capture is refused

If the device cannot honour the configuration, the capture is refused rather than started, with a reason you can show to an operator. The SDKs raise it as Refused (Error::Refused in Rust). A generator script that produces nothing also refuses the capture this way.