AckIrqDTO#
bitsrequiredinteger · int32
ActiveDRO#
idrequiredstring
campaignrequiredstring
cursorrequiredinteger
totalrequiredinteger
pausingrequiredboolean
ActiveProfileDRO#
The active profile, AND whether it still describes the board.
name alone was a claim that outlived its truth: moving the frequency, a
gain or a register by hand leaves the profile reported as applied while the
board no longer matches it. Same idea the RF readiness already applies to
the digital tuning, generalised — a status that asserts something no longer
true is worse than no status, because it is believed.
namestring · nullable
dirtyrequiredboolean
A manual change landed AFTER the profile was applied.
dirty_reason string · nullable
What made it dirty — the route or register. "dirty" alone sends the reader looking through everything.
ApiErrorDRO#
Error payload returned to clients.
coderequiredstring
Machine-readable error code (not_found, validation_failed, …).
messagerequiredstring
Human-readable message.
ApplyResultDRO#
Result of POST /profiles/{id}/apply.
profile_name requiredstring
cfg_epoch requiredinteger · int32
tuningrequiredstring
What the apply did to the RADIO, in one sentence — tuned, or why not.
Applying a profile used to leave the AD9361 exactly where it was while the reply said nothing about it (STE-994), so a UHF profile could be "applied" onto a part listening in S-band and every field in this struct stayed green. A tuning that did not happen has to be visible where the apply is.
AssertionResultDRO#
One assertion's outcome, surfaced in the report detail endpoint.
namerequiredstring
passedrequiredboolean
verifiedrequiredboolean
Whether this assertion read the LINK or only the scenario's own timeline.
It was missing from this DTO while scenario_passed required
passed && verified on every assertion — so the field that decides the
verdict was the one field a caller could not see, and a green run was
indistinguishable from a run that had checked the script against
itself. That distinction is the entire reason the flag exists.
messagerequiredstring
AttemptDRO#
steprequiredstring
outcomerequiredstring
run_id string · nullable
detailrequiredstring
at_uptime_ms requiredinteger · int64
MILLISECONDS SINCE THE DAEMON STARTED, not since the epoch. The board has no RTC; a client subtracting this from its own wall clock would be out by decades, which has already cost this project a UI feature.
AuxDRO#
availablerequiredboolean
reasonstring · nullable
Set when nothing could be mapped — typically satlinkd not running as root, or /dev/mem absent.
regsrequiredarray of AuxRegDRO
AuxRegDRO · 5 fields
namerequiredstring
addrrequiredstring
valueinteger · int32 · nullable
docrequiredstring
What the value means; present even when the read failed, so a client can show the contract without the number.
errorstring · nullable
Why value is absent. Never left implicit: a missing number that does
not say why is indistinguishable from a zero.
AuxRegDRO#
namerequiredstring
addrrequiredstring
valueinteger · int32 · nullable
docrequiredstring
What the value means; present even when the read failed, so a client can show the contract without the number.
errorstring · nullable
Why value is absent. Never left implicit: a missing number that does
not say why is indistinguishable from a zero.
BlockMatchDRO#
indexrequiredinteger
matchedrequiredinteger
totalrequiredinteger
rotationrequiredinteger
ratiorequirednumber · double
CampaignActiveDRO#
activeActiveDRO · nullable
ActiveDRO · 5 fields
idrequiredstring
campaignrequiredstring
cursorrequiredinteger
totalrequiredinteger
pausingrequiredboolean
board_provider requiredstring
Who would take a board action, in words. "the orchestrator power-cycled it" and "nobody did, the step was refused" must not read the same afterwards.
CampaignStateDRO#
idrequiredstring
campaignrequiredstring
statusrequiredstring
cursorrequiredinteger
total_steps requiredinteger
verdictboolean · nullable
true / false / null — and null is the common case.
A CAMPAIGN THAT DID NOT FINISH HAS NO VERDICT. One paused at step 3 of
15 must not report a pass on twelve steps nobody ran, and a client that
rendered false there would be accusing the board of failures it was
never asked to produce.
pause_requested requiredboolean
A pause is asked for and has not yet taken effect.
attemptsrequiredarray of AttemptDRO
AttemptDRO · 5 fields
steprequiredstring
outcomerequiredstring
run_id string · nullable
detailrequiredstring
at_uptime_ms requiredinteger · int64
MILLISECONDS SINCE THE DAEMON STARTED, not since the epoch. The board has no RTC; a client subtracting this from its own wall clock would be out by decades, which has already cost this project a UI feature.
started_at_uptime_ms requiredinteger · int64
CampaignSummaryDRO#
namerequiredstring
display_name string · nullable
descriptionrequiredstring
stepsrequiredinteger
cronstring · nullable
The cron expression, when this campaign schedules itself.
tagsrequiredarray of string
CapabilitiesDRO#
modulationsrequiredarray of string
Supported modulations (strings matching the profile enum).
rx_channels requiredinteger · int32
Number of RX channels exposed by the platform.
tx_channels requiredinteger · int32
Number of TX channels exposed by the platform.
mockrequiredboolean
true when the PL driver is a mock (no real HW attached).
symbol_rates requiredarray of SymbolRateDRO
THE SYMBOL RATES THIS MODEM CAN PRODUCE, and the two it cannot.
Not a menu someone chose: the rate is ad9361_rate / (sps * interp)
with sps fixed at 4 and interp decoded only over 1..32, so the set is
fixed by the hardware. Clients build their forms from this rather than
offering a free number — phy.symbol_rate_baud accepted anything and
reached no register at all, so a profile declaring 9600 baud transmitted
at about 2 MBaud (measured 2026-09-13).
The unreachable entries are PRESENT, with the bound they hit, rather than silently absent: a list that quietly drops what it cannot do reads as a complete list of what exists.
SymbolRateDRO · 6 fields
baudrequirednumber · double
The exact rate in baud. Fractional at the bottom of the ladder: the hardware produces 39 062.5, and rounding it would publish a rate nobody can select.
labelrequiredstring
As an operator writes it, e.g. "39.063 ksym/s".
availablerequiredboolean
False when this rate cannot be produced at all.
ad9361_rate_sps integer · int64 · nullable
What the AD9361 must be clocked at to realise it. null when unavailable.
interpinteger · int32 · nullable
The DUC interpolation that realises it. null when unavailable.
unavailable_reason string · nullable
Which bound it hits. Present exactly when available is false.
CaptureRequest#
blocksrequiredinteger
Number of RAW DMA blocks to collect. Obtaining them in a row is the proof of a sustained regime; a short capture is refused, not truncated.
warmupinteger
Blocks to obtain and DISCARD before the measured window starts.
Every capture re-arms the chain, so the leading blocks carry the acquisition transient. Skipping them is legitimate; skipping them SILENTLY, or guessing which ones look like transient, would throw away real head-of-capture errors and call the result clean. Hence explicit, and zero by default.
timeout_ms integer · int64
Give up after this many milliseconds.
releaseboolean
Release the IIO devices afterwards so bring-up tools can open them.
ChainStateDRO#
targetrequiredstring
The target these conditions were computed for.
current_target string · nullable
What the board is wired for right now; null when the two loopback
switches could not be read.
reaches_the_connector requiredboolean
Does anything leave the board in this target? False for both loopbacks — the AD9361's internal one returns TX to RX BEFORE the mixers, so no LO, gain or attenuation setting can explain an empty analyser.
stagerequiredstring
readyrequiredboolean
nextstring · nullable
The first unmet condition and the command to run. null when ready.
conditionsrequiredarray of ConditionDRO
Every condition, in the order the bring-up needs them.
ConditionDRO · 8 fields
keyrequiredstring
Stable key — dac-source, tx-egress, …
stagerequiredstring
The stage this must be satisfied to leave.
sourcerequiredstring
board when the value was re-read from a register or an IIO attribute,
session when it is this daemon's memory and a restart forgets it.
requiredrequiredstring
observedrequiredstring
What it is, or could not be read.
verdictrequiredstring
met / unmet / unknown. An unknown BLOCKS: a register we failed to
read is not a register holding the right value.
breaksrequiredstring
What goes wrong when it is unmet, as the symptom the operator will see.
fixrequiredstring
The command that clears it.
ClockDRO#
namerequiredstring
enablerequiredinteger · int32
Authoritative: non-zero means the clock is enabled.
preparerequiredinteger · int32
Authoritative: non-zero means it is prepared.
rate_hz requiredinteger · int64
NOT authoritative. Computed by the clock framework from the parent and the dividers, and printed unchanged with the MMCM off (STE-943). Present for display; never judge on it.
ClocksDRO#
availablerequiredboolean
None when debugfs is not mounted — distinct from "no clocks", which
would read as a healthy empty set.
sourcerequiredstring
clocksrequiredarray of ClockDRO
ClockDRO · 4 fields
namerequiredstring
enablerequiredinteger · int32
Authoritative: non-zero means the clock is enabled.
preparerequiredinteger · int32
Authoritative: non-zero means it is prepared.
rate_hz requiredinteger · int64
NOT authoritative. Computed by the clock framework from the parent and the dividers, and printed unchanged with the MMCM off (STE-943). Present for display; never judge on it.
ConditionDRO#
keyrequiredstring
Stable key — dac-source, tx-egress, …
stagerequiredstring
The stage this must be satisfied to leave.
sourcerequiredstring
board when the value was re-read from a register or an IIO attribute,
session when it is this daemon's memory and a restart forgets it.
requiredrequiredstring
observedrequiredstring
What it is, or could not be read.
verdictrequiredstring
met / unmet / unknown. An unknown BLOCKS: a register we failed to
read is not a register holding the right value.
breaksrequiredstring
What goes wrong when it is unmet, as the symptom the operator will see.
fixrequiredstring
The command that clears it.
ConditionRequest#
targetstring · nullable
Where the samples should go. Defaults to air — the only target of the
three that puts anything on a connector, and the one an operator at a
spectrum analyser is asking for.
profilestring · nullable
Which profile to apply. Defaults to the one currently applied; with none applied the request is refused rather than guessing, because the profile decides the frame length, the coding and the frequency.
rate_sps integer · int64
AD9361 rate for the tuning step, when one is needed.
ConditionResultDRO#
stepsrequiredarray of string
The steps actually run, in order.
detailrequiredarray of string
What each step reported, keyed in the same order as steps.
staterequiredChainStateDRO · 7 fields
targetrequiredstring
The target these conditions were computed for.
current_target string · nullable
What the board is wired for right now; null when the two loopback
switches could not be read.
reaches_the_connector requiredboolean
Does anything leave the board in this target? False for both loopbacks — the AD9361's internal one returns TX to RX BEFORE the mixers, so no LO, gain or attenuation setting can explain an empty analyser.
stagerequiredstring
readyrequiredboolean
nextstring · nullable
The first unmet condition and the command to run. null when ready.
conditionsrequiredarray of ConditionDRO
Every condition, in the order the bring-up needs them.
ConditionDRO · 8 fields
keyrequiredstring
Stable key — dac-source, tx-egress, …
stagerequiredstring
The stage this must be satisfied to leave.
sourcerequiredstring
board when the value was re-read from a register or an IIO attribute,
session when it is this daemon's memory and a restart forgets it.
requiredrequiredstring
observedrequiredstring
What it is, or could not be read.
verdictrequiredstring
met / unmet / unknown. An unknown BLOCKS: a register we failed to
read is not a register holding the right value.
breaksrequiredstring
What goes wrong when it is unmet, as the symptom the operator will see.
fixrequiredstring
The command that clears it.
DatapathStateDRO#
enabledrequiredboolean
DatapathStatusDRO#
acquiredrequiredboolean
True while the daemon holds the modem's IIO devices.
EnableLoopbackDTO#
enabledrequiredboolean
EventDRO#
Event record returned by /events.
idinteger · int64 · nullable
kindrequiredstring
sourcerequiredstring
severityrequiredstring
messagerequiredstring
timestamprequiredstring · date-time
ExecutionEventDRO#
One entry in a run's execution timeline. Mirrors
satlink_scenario_engine::report::ExecutionEvent with a stringified
kind for wire stability (the engine enum is free to add variants).
elapsed_ms requiredinteger · int64
event_id string · nullable
kindrequiredstring
descriptionrequiredstring
FftCaptureDRO#
sizerequiredinteger · int32
sourcerequiredinteger · int32
monitor.fft.SRC_SEL — which tap the core is pointed at.
bins_db requiredarray of number · double
Frequency-domain magnitude samples.
syntheticrequiredboolean
TRUE while these bins are MANUFACTURED HERE and do not come from the PL.
The field exists because nothing in the response distinguished a
measurement from an invention: a spectrum served by /api/v1/... reads
as signal, and the UI displayed it as such. The client now labels itself
from this value rather than from a hard-coded string, which would have
gone stale the day the real path landed — and a stale note is this
project's dominant failure mode.
Set it to false IN THE SAME COMMIT as the real sample read, not
before: a premature false is worse than today's true, because it
makes the invention undetectable.
channelrequiredstring
Which end of the link this spectrum describes ("rx" / "tx").
enabledrequiredboolean
Whether analysis is switched on for that channel. Off by default: a frame costs a DMA block plus a transform, and nothing should pay that because a dashboard tab happens to be open.
sample_rate_hz requiredinteger · int64
Sample rate the block was taken at, 0 when there is no block.
unavailableUnavailable · nullable
FlushedDRO#
useful_bytes requiredinteger
Useful bytes HANDED TO THE DMA, padding included. Zero means nothing was pending — a block of pure padding would put silence on the air.
Handed to the DMA is not "on the air". This field used to say it was,
and it was wrong: with the TX egress closed the modem discards the
block and this number is exactly the same. tx_chain_running is the
only field here that tells the two apart.
tx_chain_running boolean · nullable
Whether the modem's TX egress is open. null when there is no PL
driver to ask, which is not the same as false.
FlushRequest#
blocksinteger
How many blocks to emit. Use 2 whenever you intend to see the result come back. An RX block is returned only when ENTIRELY full, and the receive side is always short of what was sent — the first frame of a freshly-armed chain is lost to acquisition, so 4096 useful bytes out come back as 4080. One block therefore stops just before the line and waits for traffic a one-shot sender never sends.
Measured on hardware 2026-09-12: one flushed block never surfaced in
60 s (with tx.framer.frame_cnt confirming all 256 frames had
crossed); two came back in 2 s.
FrameDRO#
idrequiredstring · uuid
timestamprequiredstring · date-time
profilestring · nullable
protocolrequiredstring
fec_status requiredstring
crc_ok boolean · nullable
payload_hex requiredstring
FramesPageDRO#
One page of the ring, plus what the caller cannot see from the rows.
[] used to be the whole answer, and it conflated two very different
facts: "the link carried nothing" and "nothing is reading the modem". The
first is information about the RADIO, the second about the DAEMON, and an
operator reads the empty array as the first. Same shape as /pl/aux, which
reports available: false WITH its reason.
granularityrequiredstring
What ONE entry is. "dma_block" today: the producer pushes one entry
per DMA block, so an entry is 4096 useful bytes cut at a boundary that
has no relation to any protocol frame. Calling that a frame is the
defect STE-989 is about; naming it is the honest half of the fix.
For packet granularity use /ws/frames?framing=slot&slot_len=N.
clockrequiredstring
How to read timestamp. "boot_relative": the board has no RTC and no
NTP, so times start at the epoch on every boot. A control centre would
otherwise take them for wall-clock.
empty_reason string · nullable
null when the page is non-empty. Otherwise WHY it is empty.
framesrequiredarray of FrameDRO
FrameDRO · 7 fields
idrequiredstring · uuid
timestamprequiredstring · date-time
profilestring · nullable
protocolrequiredstring
fec_status requiredstring
crc_ok boolean · nullable
payload_hex requiredstring
HealthDRO#
statusrequiredstring
InterruptsDRO#
statusrequiredinteger · int32
enablerequiredinteger · int32
bitsrequiredarray of string
IqAnnotationDTO#
offset_samples requiredinteger · int64
labelrequiredstring
notestring · nullable
IqCaptureDRO#
idrequiredstring · uuid
created_at requiredstring · date-time
samplesrequiredinteger · int64
sample_rate_hz requiredinteger · int64
center_frequency_hz requiredinteger · int64
pathrequiredstring
size_bytes requiredinteger · int64
statusrequiredstring
tagsrequiredarray of string
annotationsrequiredarray of IqAnnotationDTO
IqAnnotationDTO · 3 fields
offset_samples requiredinteger · int64
labelrequiredstring
notestring · nullable
profile_ref string · nullable
syntheticrequiredboolean
TRUE when the samples were MANUFACTURED rather than captured. Without it a fabricated capture is indistinguishable from a measurement: it carries the true centre frequency and the active profile, and opens in any viewer showing a clean carrier.
JobDRO#
A job descriptor returned when kicking off an async operation (apply profile, run scenario, capture IQ, …).
job_id requiredstring · uuid
kindrequiredstring
statusrequiredcreated_at requiredstring · date-time
updated_at requiredstring · date-time
progressnumber · double · nullable
messagestring · nullable
MeasureReportDRO#
verdictrequiredstring
BytePerfect | Degraded | NoCorrelation.
rotation_slip requiredboolean
A byte was lost or inserted at a block boundary. NOT a demodulation error — reporting it separately is what stops it being read as one.
blocksrequiredarray of BlockMatchDRO
Read this before overall_ratio: an aggregate is exactly what hid the
2026-09-08 defect, where the whole capture scored 49.99 % while every
block taken separately scored 99.98 %.
BlockMatchDRO · 5 fields
indexrequiredinteger
matchedrequiredinteger
totalrequiredinteger
rotationrequiredinteger
ratiorequirednumber · double
overall_ratio requirednumber · double
worst_block_ratio requirednumber · double
capture_bytes requiredinteger
warmuprequiredinteger
Blocks obtained and discarded before the measured window. Never hidden: a window that was moved must say so, or the number stops meaning what the reader thinks it means.
PlStatusDRO#
global_ctrl requiredinteger · int32
versionrequiredinteger · int32
build_id requiredinteger · int32
cfg_epoch requiredinteger · int32
irq_status requiredinteger · int32
irq_enable requiredinteger · int32
PowerDRO#
taprequiredinteger · int32
power_dbfs requirednumber · double
ProfileDRO#
Full profile payload — serialized as the opaque satlink.profile/v1 shape.
object
ProfileRevisionDRO#
One row of a profile's revision history.
versionrequiredinteger · int32
cfg_epoch requiredinteger · int32
applied_at requiredstring · date-time
applied_by string · nullable
messagestring · nullable
ProfileSequencerDRO#
staterequiredinteger · int32
statusrequiredinteger · int32
err_code requiredinteger · int32
profile_id requiredinteger · int32
state_name requiredstring
ProfileSummaryDRO#
Summary of a profile for list endpoints.
namerequiredstring
display_name string · nullable
modulationrequiredstring
symbol_rate_baud requirednumber · double
center_frequency_hz requiredinteger · int64
PurgeQueryDTO#
older_than_hours requiredinteger · int64
Purge events older than this many hours.
PurgeResultDRO#
purgedrequiredinteger
RadioStatusDRO#
rx_state requiredstring
tx_state requiredstring
center_frequency_hz requiredinteger · int64
rx_gain_db requiredinteger · int32
tx_gain_db requiredinteger · int32
loopbackrequiredboolean
timing_lock requiredboolean
carrier_lock requiredboolean
rx_lo_hz integer · int64 · nullable
rx_nco_offset_hz requiredinteger · int64
The DDC's digital offset from the RX LO, signed, in Hz.
tx_nco_offset_hz requiredinteger · int64
The DUC's digital offset from the TX LO, signed, in Hz.
profile_center_frequency_hz requiredinteger · int64
What the applied PROFILE declares as its centre, read from the inert
chan_cfg shadow. It describes the configuration, not the radio, and
the two diverge the moment anyone tunes by hand.
tx_lo_hz integer · int64 · nullable
rx_rf_gain_db number · double · nullable
The part's own gain, in dB at the connector. TX is an ATTENUATION.
tx_rf_gain_db number · double · nullable
rx_bandwidth_hz integer · int64 · nullable
The part's ANALOG filter width, per direction, read from IIO.
NOT a PL register — the AD9361 has none in this address space, which is
exactly why the datapath diagram could not show it. None means the
attribute could not be read, never a default: this is the value that sat
at 18 MHz for a 53 kHz signal (STE-998), and a plausible-looking number
here would be worse than an absence.
The part QUANTISES what is written, so this read-back legitimately differs from any requested width. It is the truth; the request is not.
SERIALISED EVEN WHEN NULL, unlike its neighbours above, and the difference
is deliberate. An ABSENT field cannot be told apart from an older daemon
that never had it; an explicit null says "this daemon knows about the
bandwidth and could not read it". For a field whose whole point is that
nobody could see it before, that distinction is the feature.
tx_bandwidth_hz integer · int64 · nullable
sample_rate_sps integer · int64 · nullable
The part's sample rate. Also on /radio/rf-state; carried here so that
one call answers "how is the front end configured" instead of two that
have to be reconciled.
RegisterDRO#
addrrequiredinteger · int32
valuerequiredinteger · int32
RegisterResultDRO#
namerequiredstring
replacedrequiredboolean
True when a profile of this name already existed and was replaced.
persistedrequiredboolean
Always false today, and present so a client never has to assume.
noterequiredstring
ReportDetailDRO#
Detailed view returned by GET /reports/{run_id}. Embeds the parsed
satlink.report/v1 payload (events + assertion results).
run_id requiredstring · uuid
scenario_name requiredstring
profile_ref string · nullable
Profile referenced by the scenario at the time the run finished, if still known to the daemon's in-memory scenarios map.
statusrequiredstring
started_at requiredstring · date-time
ended_at string · date-time · nullable
duration_ms integer · int64 · nullable
steps_executed integer · int32 · nullable
assertions_passed integer · int32 · nullable
assertions_failed integer · int32 · nullable
has_report requiredboolean
true if the run had a persisted report (completed/failed/cancelled).
verifiedrequiredboolean
Whether ANY assertion in this run read the link rather than the scenario's own script. A report that observed nothing is not a pass, and the reader has to be able to tell the two apart.
abortedrequiredboolean
The run did not reach the end of its timeline. Its assertions were written for a whole one, so neither verdict applies.
eventsrequiredarray of ExecutionEventDRO
ExecutionEventDRO · 4 fields
elapsed_ms requiredinteger · int64
event_id string · nullable
kindrequiredstring
descriptionrequiredstring
assertionsrequiredarray of AssertionResultDRO
AssertionResultDRO · 4 fields
namerequiredstring
passedrequiredboolean
verifiedrequiredboolean
Whether this assertion read the LINK or only the scenario's own timeline.
It was missing from this DTO while scenario_passed required
passed && verified on every assertion — so the field that decides the
verdict was the one field a caller could not see, and a green run was
indistinguishable from a run that had checked the script against
itself. That distinction is the entire reason the flag exists.
messagerequiredstring
metric_samples requiredarray of MetricSampleDRO
The board's own trace across the run. EMPTY IS NOT "NOTHING HAPPENED":
it means no sampler was attached, which sample_interval_ms == None
is how you tell apart.
sample_interval_ms integer · int64 · nullable
The sampler's nominal tick, beside the samples — a series without its cadence cannot distinguish a slow poll from a stalled board.
sourcestring
Where this run's numbers came from: "pl", "stub", "mixed", "none".
SERVED, not kept internal. The point of the field is that a report declares its own provenance to whoever reads it — and the reader of a qualification document is usually not the person who ran it.
ReportSummaryDRO#
One row of the reports list.
run_id requiredstring · uuid
scenario_name requiredstring
profile_ref string · nullable
Profile referenced by the scenario at the time the run finished, if still known to the daemon's in-memory scenarios map.
statusrequiredstring
started_at requiredstring · date-time
ended_at string · date-time · nullable
duration_ms integer · int64 · nullable
steps_executed integer · int32 · nullable
assertions_passed integer · int32 · nullable
assertions_failed integer · int32 · nullable
ResetDTO#
scoperequiredstring
RfKillDRO#
ensm_mode requiredstring
What the part's ENSM reports AFTER the stop. alert is RF off.
stoppedrequiredarray of string
Steps that took effect, in the order they were applied.
failedrequiredarray of string
Steps that did NOT. Present even when RF is off, because a stop that half-worked and says nothing is the failure mode this exists against.
rf_off requiredboolean
Is RF actually off at the connector?
recoverrequiredstring
How to come back.
RssiDRO#
average_dbm requirednumber · double
instant_dbm requirednumber · double
threshold_dbm requirednumber · double
ScenarioDRO#
object
ScenarioRunDRO#
Public view of a scenario execution (summary).
run_id requiredstring · uuid
scenario_name requiredstring
statusrequiredstring
pending, running, passed, failed, cancelled.
started_at requiredstring · date-time
ended_at string · date-time · nullable
duration_ms integer · int64 · nullable
steps_executed integer · int32 · nullable
assertions_passed integer · int32 · nullable
assertions_failed integer · int32 · nullable
ScenarioSummaryDRO#
namerequiredstring
display_name string · nullable
descriptionstring · nullable
profile_ref string · nullable
tagsrequiredarray of string
ServiceStatusDRO#
namerequiredstring
healthyrequiredboolean
SetFrequencyDTO#
center_frequency_hz requiredinteger · int64
channelstring
Which side to tune: "rx", "tx" or "both" (default).
RX AND TX ARE INDEPENDENT LOs. Measured on this bench 2026-09-12: the RX LO sat at 2.400 GHz while the TX LO was still at its 2.450 GHz default, so an analyser on TX1A at 2.4 GHz saw nothing and the API reported one "center_frequency_hz" that described neither.
tune_lo boolean
true (default) also drives the AD9361's analogue LO. false writes
only the PL's digital NCO (chan_cfg::FREQ_HZ).
The distinction is not pedantry: until today this route wrote ONLY the
NCO, so /radio/status reported a centre frequency the transmitter had
never been told about.
SetGainDTO#
StartCaptureDTO#
samplesrequiredinteger · int64
sample_rate_hz integer · int64 · nullable
center_frequency_hz integer · int64 · nullable
tagsarray of string
annotationsarray of IqAnnotationDTO
IqAnnotationDTO · 3 fields
offset_samples requiredinteger · int64
labelrequiredstring
notestring · nullable
profile_ref string · nullable
SymbolRateDRO#
baudrequirednumber · double
The exact rate in baud. Fractional at the bottom of the ladder: the hardware produces 39 062.5, and rounding it would publish a rate nobody can select.
labelrequiredstring
As an operator writes it, e.g. "39.063 ksym/s".
availablerequiredboolean
False when this rate cannot be produced at all.
ad9361_rate_sps integer · int64 · nullable
What the AD9361 must be clocked at to realise it. null when unavailable.
interpinteger · int32 · nullable
The DUC interpolation that realises it. null when unavailable.
unavailable_reason string · nullable
Which bound it hits. Present exactly when available is false.
SystemStatusDRO#
uptime_seconds requiredinteger · int64
pl_driver requiredstring
mock or uio.
servicesrequiredarray of ServiceStatusDRO
ServiceStatusDRO · 2 fields
namerequiredstring
healthyrequiredboolean
TransmitAcceptedDRO#
packetsrequiredinteger
useful_bytes requiredinteger
Useful bytes queued (before the le:u8/32 lane expansion).
wire_bytes requiredinteger
Bytes actually written to the DMA block.
boundaries_preserved requiredboolean
True when packet boundaries survive the crossing. False in stream mode — stated rather than left for the caller to discover from a receiver that returns bytes at the wrong offsets.
tx_chain_running boolean · nullable
Whether the modem's TX egress is open.
false means these bytes reach the DMA and the modem DISCARDS them —
nothing goes on the air, and no other number in this reply changes.
null when there is no PL driver to ask.
block_useful_bytes requiredinteger
Useful bytes that fill one DMA block.
Not a measurement of what is buffered — the assembler's fill level
lives in the service and is not queried here, and reporting a guessed
one would be the snr_db mistake of STE-977. It is the quantum that
matters to a caller: a packet does NOT go on the air until its block
is complete, so this is the latency granularity.
chainChainStateDRO · nullable
TransmitRequest#
packetsrequiredarray of object
Packets to transmit, tagged by protocol:
{"protocol":"raw","payload_hex":".."} for bytes forged elsewhere,
{"protocol":"csp","header":{..},"payload_hex":".."} to have SatLink
build the packet, likewise "ccsds".
framingobject
How packets are laid out in the byte stream.
{"mode":"stream"} packs them end to end: maximum throughput, and NO
packet boundary survives — the receiver gets a byte stream cut at the
PL's FRAME_LEN, which has no relation to what was pushed.
{"mode":"slot","slot_len":256,"pad":0} gives one packet per slot, so
the framer's cut lands on the boundary. slot_len must equal the PL's
FRAME_LEN and divide the block's useful bytes, or the request is
refused.
forceboolean
Transmit even though the chain cannot carry it (STE-995).
The refusal is a guard, not a wall: probing a closed-egress branch or an
unconditioned front-end is legitimate bench work, and
dsl/scenarios/bringup/tx_egress_probe.yaml exists to do exactly that.
It is deliberately not the default, and the reply carries the chain state
either way so no report can claim the chain was healthy.
ValidationDRO#
Validation result — DRO returned by POST /profiles/validate
(and POST /scenarios/validate in the scenarios module).
okrequiredboolean
errorsrequiredarray of string
VersionDRO#
firmwarerequiredstring
satlinkd crate version.
apirequiredstring
API contract version (v1).
fpgastring · nullable
PL bitstream identifier read from the shell — None in mock mode.
This is the shell's BUILD_ID register, read live over /dev/uio0. It
says which bitstream is LOADED, which no host-side manifest can.
releaserequiredstring
Release bundle this daemon was built for, or "unreleased".
Compare it with the manifest of the bundle you deployed: they must be equal. On a ramdisk rootfs, deploying and running are two different things, and this field is what separates them (STE-985).