CLI
CLI
simtx-cli is the headless companion to SimTX Transmitter. It can transmit
audio files, generated white noise, or GNU Radio IQ from one of your persistent
stations, serve the simulated world over rtl-tcp, manage your stations and
satellites, predict satellite passes, sign in to a SimTX account, query a
server, and check for updates. It speaks SimTX client protocol v25.
This document covers every command, option, environment variable, preset key, output format, and exit condition.
Contents
- Quick start
- Command summary
- Output conventions
- Global options
- Environment variables
- Transmitter commands
- Receiver command:
rx - Station commands:
stations,passes - Account command:
login - Query commands
- Version and update commands
- Multiplexed Serve Mode:
serve - Presets
- Exit status and signals
- Troubleshooting
Quick start
# Sign in once (stores credentials in the OS keyring by default)
simtx-cli login
# Create a ground station (or use the app's station editor)
simtx-cli --name Home --callsign N0CALL --lat 52.37 --lon 4.90 \
--antenna-type efhw stations create
# List your stations (ids, antennas, rigs)
simtx-cli stations
# Transmit a WAV file on 40 m USB from your station "Home"
simtx-cli --server-id myserver --station Home audio argon.simtx.net 5556 \
7100000 message.wav USB
# Transmit generated white noise on 80 m AM
simtx-cli --server-id myserver --station Home white argon.simtx.net 5556 3700000 AM_DSB_FC
# Feed IQ from GNU Radio into SimTX on a local loopback port
simtx-cli --server-id myserver --station Home iq argon.simtx.net 5556 7100000 4567
# Serve the simulated world to SDR++ over rtl-tcp (ad-hoc receiver)
simtx-cli --server-id myserver rx argon.simtx.net 5556 1234 N0CALL 7100000 1024000
# ... or receive at a persistent station (yours or a PUBLIC one)
simtx-cli --server-id myserver --station Home rx argon.simtx.net 5556 1234
# Launch a satellite and predict its passes over your station
simtx-cli --name Cube --callsign CUBE1 --altitude-km 550 --inclination 97.6 stations launch
simtx-cli --sat Cube --station Home passes
# One-shot queries
simtx-cli stations argon.simtx.net 5556
simtx-cli health argon.simtx.net 5556
simtx-cli catalog argon.simtx.net 5556
Command summary
| Command | Purpose |
|---|---|
version
|
Print the local build version. No network access. |
update
|
Print update metadata and whether an update is available. |
login
|
Sign in with the device authorization flow and store credentials. |
audio
|
Stream a local audio file from one of your stations. |
white, whitenoise
|
Transmit a generated white-noise signal from one of your stations. |
iq, gnuradio
|
Stream IQ from a local GNU Radio TCP sink via one of your stations. |
rx
|
Serve the simulated world over rtl-tcp (ad-hoc or station-bound). |
stations
|
List/create/launch/delete/deorbit your stations, or (with <host> <port>) fetch a server's live station list.
|
passes
|
Predict passes of a satellite over a ground station. |
health
|
Fetch a server health report as JSON. |
catalog, catalogue
|
Fetch the catalog listing as JSON. |
serve
|
Run a multiplexed NDJSON session engine for game integration. |
All commands except version and update require an exact version match with
the update service. If the CLI is outdated or the update service is
unreachable, the command refuses to run. Upgrade the CLI, then retry.
Unknown commands fail before any network access.
Output conventions
- Human-readable logs and progress messages always go to stderr.
- Machine-readable results go to stdout.
- Query results and update metadata are printed as one compact line beginning
with J::
simtx-cli stations argon.simtx.net 5556 | grep '^J:' | cut -c3- | jq .
versionprints plain text, not JSON.loginprints the verification URL and code as plain text.- Transmitter commands (
audio,white,iq) print nothing to stdout unless
--telemetry-json is enabled.
Global options
These options work before or after the subcommand. Explicit flags win over environment variables, which win over preset values, which win over built-in defaults.
| Option | Default | Meaning |
|---|---|---|
--lat DEG
|
52.3676
|
Station latitude, -90..90. Receiver default is 47.50; see rx.
|
--lon DEG
|
4.9041
|
Station longitude, -180..180. Receiver default is 19.04; see rx.
|
--power W
|
100
|
Transmit power in watts. |
--sample-rate HZ
|
auto (0)
|
Transmission sample rate (0 = automatic wire rate based on mode/bandwidth).
|
--rate HZ
|
unset | Alias for --sample-rate when greater than zero.
|
--gain GAIN
|
0.9
|
Transmission audio/carrier gain (or from preset source.gain).
|
--duty-cycle DC
|
0.5
|
PA duty cycle 0.0..1.0 (or from preset source.dutyCycle).
|
--radio-preset P
|
empty | Radio model preset override (GENERIC_HF, CLEAN_LAB, etc.).
|
--debug
|
off | Verbose logging. Also logs every telemetry frame instead of only the first. |
--telemetry-json
|
off | Stream every transmitter telemetry frame to stdout as JSON. |
--plain-tcp
|
off | Connect using unencrypted TCP instead of TLS on custom server ports. |
--account-url URL
|
official service | Account service to use for login and tickets. |
--server-id ID
|
empty | Game server ID used to mint an authenticated ticket. Requires login.
|
--ticket T
|
empty | Use a literal game ticket instead of account authentication. |
--auth-file PATH
|
empty | Store credentials in a plaintext JSON file instead of the OS keyring. |
--preset FILE
|
empty | Load defaults from a preset file. |
--save-preset OUT
|
empty | Write the effective settings to a preset file and continue. |
Station options
| Option | Default | Meaning |
|---|---|---|
--station REF
|
SIMTX_STATION
|
Persistent station id or name (or callsign). Required for TX; makes rx station-bound.
|
--antenna REF
|
first antenna | Station antenna id or name. |
--rig REF
|
none | Station rig id or name. Without a rig the generic HF defaults and the account PA limit apply. |
--track ID
|
none | Point the antenna at and Doppler-correct toward this station id (usually a satellite). |
--name N
|
— | stations create / stations launch: station name.
|
--callsign C
|
— | stations create / stations launch: station callsign.
|
--visibility V
|
PUBLIC
|
stations create / stations launch: PUBLIC or UNLISTED.
|
--altitude-km KM
|
550
|
stations launch: circular orbit altitude above the 6371 km sphere.
|
--inclination DEG
|
97.6
|
stations launch: orbit inclination.
|
--raan DEG
|
0
|
stations launch: right ascension of the ascending node.
|
--sat REF
|
— | passes: satellite id or name (yours or the public catalog).
|
--hours H
|
24
|
passes: prediction window (max 168).
|
--min-el DEG
|
0
|
passes: minimum elevation.
|
--yes
|
off | stations deorbit: fire the burn without the confirmation question.
|
Boolean options accept --flag or --flag=true / --flag=false.
Flags can appear before or after the subcommand. Use -- to stop flag parsing
before positional values that begin with -.
Antenna options
Antenna options describe the inline antenna of ad-hoc receivers (rx
without --station) and the first antenna of stations create. Transmissions
always use the antenna stored on the station, so these options do not apply to
audio, white or iq.
| Option | Default | Meaning |
|---|---|---|
--antenna-type TYPE
|
default EFHW | Antenna template. See the list below. |
--antenna-height M
|
10
|
Antenna height in metres. |
--wire-length M
|
0
|
Wire or element length in metres. 0 selects a resonant length.
|
--azimuth DEG
|
0
|
Azimuth in degrees clockwise from north. |
--feedline-loss DB
|
1.0
|
Feedline loss in dB. |
--nec-deck PATH
|
empty | Path to a custom NEC-2 deck file (.nec). Alias: --nec.
|
Antenna types (case-insensitive, - and _ are interchangeable):
dipole, inverted-v, vertical, yagi-3el, yagi-5el, loop,
beverage, longwire, efhw.
Leaving --antenna-type empty uses the default shortwave EFHW antenna.
Built-in antenna templates automatically synthesize a resonant NEC-2 deck at the
transmission frequency so servers requiring NEC models radiate properly without
manual deck configuration. Supplying --nec-deck (or --nec) overrides the
built-in template with a custom NEC-2 deck.
Identity and authentication
- With no
--server-idand no--ticket, sessions are anonymous. Anonymous
sessions are intended for local or test servers and may be rejected by official servers. Anonymous sessions are receive-only: transmitting needs a station-bound ticket.
- Transmitter tickets are minted bound to
--station(and the resolved
antenna and rig); every reconnect mints a fresh bound ticket. A literal
--ticket must already be bound to the same station, and then --station,
--antenna and --rig must be ids.
- Station management (
stations create/launch/delete/deorbit) needs the
station.manage scope. Sessions signed in before stations existed lack it;
run simtx-cli login again (after simtx-cli has been updated) when the
account service answers scope_required.
--server-idmints a fresh game ticket through the signed-in account. Run
simtx-cli login first.
--ticketpasses a literal ticket. For long-lived sessions such asrx,
a reused single-use ticket may stop working after the first reconnect.
--auth-file PATH(orSIMTX_AUTH_FILE) stores the account session in a
plaintext JSON file with owner-only permissions. This is intended for headless servers without an OS keyring. Anyone who can read that file can act as the signed-in device.
Preset precedence and positional filling
Precedence is:
- Explicit command-line flags and positional arguments.
SIMTX_*environment variables.- Values loaded with
--preset. - Built-in defaults.
Missing trailing positional arguments are filled left-to-right from preset
keys. Leading positionals cannot be skipped. For example, if a preset provides
cli.audioFile and rf.modulation, you can supply only the connection and
station positionals:
simtx-cli --preset station.stxpreset audio argon.simtx.net 5556 7100000
The audio file, modulation, bandwidth, and low cut then come from the preset. A preset containing every required positional also allows a bare command:
simtx-cli --preset station.stxpreset audio
Environment variables
| Variable | Equivalent option |
|---|---|
SIMTX_TX_LAT
|
--lat
|
SIMTX_TX_LON
|
--lon
|
SIMTX_TX_POWER
|
--power
|
SIMTX_SAMPLE_RATE
|
--sample-rate
|
SIMTX_PLAIN_TCP
|
--plain-tcp
|
SIMTX_FORCE_PLAIN_TCP
|
--plain-tcp
|
SIMTX_ACCOUNT_URL
|
--account-url
|
SIMTX_AUTH_FILE
|
--auth-file
|
SIMTX_SERVICE_CREDENTIAL
|
--service-credential (receiver only)
|
SIMTX_STATION
|
--station
|
Environment variables apply to every command. The receiver location
(47.50, 19.04) only applies when none of --lat, --lon,
SIMTX_TX_LAT, SIMTX_TX_LON, or the preset latitude/longitude keys are set.
Transmitter commands
All transmitter commands run on one of your persistent stations:
--station is required (else the command fails with "transmitting needs
--station"), together with --server-id (or a station-bound --ticket). The
station dictates callsign, location, antenna and pointing, the PA cap and the
rig's realism; the session chooses modulation, frequency, power (up to the
cap), source, sample rate, gain, bandwidth, low cut, tone and duty. Only the PA
knobs you give (a radio preset, positional overrides or rf.* preset keys)
are sent; with a rig the server takes final drive, audio compression,
frequency offset and AM index from them and everything else from the rig.
Ad-hoc transmission (inline callsign/location/antenna) no longer exists.
Transmitter commands use the global power and sample-rate options and run
until interrupted with Ctrl-C (SIGINT) or SIGTERM or until the session ends.
The server confirms the station with a session status log line (BOUND,
station name, callsign, PA cap); rejections carry the server's reason and code
(for example FORBIDDEN for a station that is not yours or a ticket bound to
another station).
Ports must be in 1..65535. Frequency must be positive and power must be
non-negative. Sample rates at or below zero fall back to 48000. Bandwidth values below the modulation minimum
are raised to the minimum; values above 10,000,000 Hz are capped.
A session can be queued. The CLI logs queue position, acceptance, live update acknowledgements, kicks, and telemetry.
audio
simtx-cli [global flags] --station S audio <host> <port> <freqHz> <file> \
[MOD] [BW] [LOWCUT] [PRESET|DRIVE ...]
Streams a local audio file.
<host> <port>— SimTX server address.<freqHz>— transmit frequency in Hz; must be positive.<file>— local WAV, AIFF, or MP3 file.[MOD]— modulation; defaults toAM_DSB_FC.[BW]— occupied bandwidth in Hz; defaults to the modulation's typical
bandwidth.
[LOWCUT]— audio low cut in Hz; defaults to the modulation's typical
low cut.
[PRESET|DRIVE ...]— optional radio preset and PA knobs; see
The file is streamed once. The session ends when the file ends, when the server disconnects, or when you interrupt the command. For WFM, only the first channel is used.
Examples:
simtx-cli --server-id myserver --station Home audio argon.simtx.net 5556 7100000 \
message.wav USB
simtx-cli --server-id myserver --station Home --antenna Yagi --rig IC-7300 \
audio argon.simtx.net 5556 145500000 fm.wav NFM 12500 200
simtx-cli --server-id myserver --station Home audio argon.simtx.net 5556 7100000 \
message.wav LSB 2700 350 ICOM_IC_7300
white / whitenoise
simtx-cli [global flags] --station S white <host> <port> <freqHz> \
[MOD] [BW] [LOWCUT] [PRESET|DRIVE ...]
Transmits a generated white-noise signal. No audio samples are sent by the
client; the signal is generated for the session. Positional arguments are the
same as audio without the file.
simtx-cli --server-id myserver --station Home white argon.simtx.net 5556 3700000 AM_DSB_FC 10000 80
iq / gnuradio
simtx-cli [global flags] --station S iq <host> <port> <freqHz> <localIqPort> \
[sampleRate] [BW] [PRESET|DRIVE ...]
Streams IQ from a local GNU Radio TCP sink into a transmitter session.
<localIqPort>— TCP port on127.0.0.1where GNU Radio connects.[sampleRate]— IQ sample rate. Overrides the global sample rate.[BW]— transmit bandwidth in Hz; defaults to the sample rate.[PRESET|DRIVE ...]— optional radio preset and PA knobs.
The input stream must be interleaved little-endian 32-bit float I/Q pairs
(gr_complex). The CLI listens only on loopback. One producer can stream at a
time; a restarted flowgraph can reconnect and take over.
Examples:
simtx-cli --server-id myserver --station Home iq argon.simtx.net 5556 7100000 4567
simtx-cli --server-id myserver --station Home iq argon.simtx.net 5556 7100000 4567 \
96000 96000 GENERIC_HF
In GNU Radio, use a TCP sink pointed at 127.0.0.1:4567 with the matching
sample rate and complex float output.
Modulations
Modulation names are case-insensitive. The default is AM_DSB_FC.
| Name | Common name | Typical bandwidth | Typical low cut |
|---|---|---|---|
AM_DSB_FC
|
AM double sideband, full carrier | 10000 Hz | 80 Hz |
AM_DSB_SC
|
AM double sideband, suppressed carrier | 6000 Hz | 100 Hz |
USB
|
Upper sideband | 2700 Hz | 350 Hz |
LSB
|
Lower sideband | 2700 Hz | 350 Hz |
CW
|
Continuous wave | 150 Hz | 300 Hz |
NFM
|
Narrow FM | 12500 Hz | 200 Hz |
WFM
|
Wide FM | 180000 Hz | 30 Hz |
IQ
|
External complex IQ | 48000 Hz | 0 Hz |
WSPR
|
Weak Signal Propagation Reporter | 6 Hz | 0.5 Hz |
FT8
|
Franke-Taylor FT8 | 50 Hz | 20 Hz |
Radio presets and PA knobs
The optional trailing arguments start with a radio preset name, followed by
up to 15 numeric overrides in the order shown below. The preset name is
case-insensitive and -/_ are interchangeable. Missing or unparseable
values use the preset's defaults.
Which knobs are sent: naming a radio preset (positionally, with
--radio-preset or rf.radioPreset) sends all 15; otherwise only the
positional values and rf.* preset keys you give are sent and the rest come
from the station's rig (or the generic HF preset when the session has no rig).
| Preset | Character |
|---|---|
GENERIC_HF
|
Generic HF transceiver |
CLEAN_LAB
|
Clean lab exciter |
ICOM_IC_7300
|
Icom IC-7300 class HF rig |
ICOM_IC_705
|
Icom IC-705 class QRP SDR |
ICOM_IC_7610
|
Icom IC-7610 class base SDR |
YAESU_FTDX10
|
Yaesu FTDX10 class HF SDR |
ELECRAFT_KX3
|
Elecraft KX2/KX3 class QRP |
YAESU_FT_817
|
Yaesu FT-817/818 class QRP |
KENWOOD_TS_590
|
Kenwood TS-590 class HF rig |
XIEGU_G90
|
Xiegu G90 class portable HF |
USDX_QRP
|
(tr)uSDX / uSDX QRP Class-E |
MIL_MANPACK
|
Rugged HF manpack |
VINTAGE_TUBE
|
Vintage tube SSB |
COLLINS_KWM2
|
Collins KWM-2 class tube SSB |
BOAT_ANCHOR_AM
|
Vintage AM boatanchor |
CB_EXPORT_AM
|
Overdriven export AM/SSB |
VHF_HANDHELD_FM
|
Budget VHF/UHF handheld FM |
HOME_BREW_CLASS_E_AM
|
Homebrew Class-E AM |
DIRTY_DRIVER
|
Overdriven driver / poor ALC |
Numeric override order:
| # | Knob | Unit |
|---|---|---|
| 1 | Final drive | dB |
| 2 | PA headroom | dB |
| 3 | Audio compression | dB |
| 4 | PA softness | — |
| 5 | PA AM/PM | degrees |
| 6 | Carrier leak | dB |
| 7 | Opposite-sideband suppression | dB |
| 8 | Frequency offset | Hz |
| 9 | Mains hum level | dB |
| 10 | Mains hum frequency | Hz |
| 11 | AM modulation index | — |
| 12 | PA supply sag | dB |
| 13 | TX noise floor | dB |
| 14 | Oscillator phase noise | dB |
| 15 | CW key rise time | ms |
Example: ... USB 2700 350 ICOM_IC_7300 1.5 9 selects the IC-7300 preset
and overrides final drive to 1.5 dB and PA headroom to 9 dB.
Telemetry
Transmitter sessions receive server-reported telemetry. By default the first
frame is logged at info level and later frames at debug level. Use --debug to
log every frame.
With --telemetry-json, every frame is also written to stdout as:
J:{"type":"telemetry","telemetry":{...}}
The full telemetry object:
| JSON field | Meaning |
|---|---|
Sequence
|
Frame sequence number. |
ServerTimeMillis
|
Server timestamp in milliseconds. |
ActualFrequencyHz
|
Actual transmit frequency. |
RequestedPowerWatts
|
Requested power. |
ForwardPowerWatts
|
Forward power at the finals. |
DeliveredPowerWatts
|
Delivered power. |
ReflectedPowerWatts
|
Reflected power. |
VSWR
|
Standing-wave ratio. |
MismatchLossDb
|
Mismatch loss in dB. |
TransformerLossDb
|
Transformer loss in dB. |
FoldbackLossDb
|
Foldback loss in dB. |
AlcDb
|
Automatic level control, in dB. |
BufferSeconds
|
Server-side buffer depth in seconds. |
FinalsTemperatureC
|
Finals temperature in °C. |
ThermalFoldbackDb
|
Thermal foldback in dB. |
PaCompressionDb
|
PA compression in dB. |
PaHeatWatts
|
PA heat dissipation in watts. |
PaEfficiencyPercent
|
PA efficiency in percent. |
PaThermalFaultActive
|
True when the PA thermal fault is active. |
Human-readable log lines use these short field names: seq,
serverTimeMillis, freqHz, requestedW, forwardW, deliveredW,
reflectedW, vswr, mismatchLossDb, transformerLossDb, foldbackLossDb,
alcDb, bufferS, tempC, thermalFoldbackDb, paCompressionDb,
paHeatW, paEfficiencyPct, paThermalFault.
Example:
simtx-cli --telemetry-json --server-id myserver --station Home audio argon.simtx.net 5556 \
7100000 message.wav USB \
| grep '^J:' | cut -c3- | jq '.telemetry.FinalsTemperatureC'
Receiver command: rx
simtx-cli [global flags] rx <simtxHost> <simtxPort> [rtlTcpPort] [callsign] \
[centerHz] [bandwidthHz]
Serves the simulated world over rtl-tcp so an SDR application such as SDR++ can tune around the requested center frequency and bandwidth. Point the SDR client at the local rtl-tcp endpoint.
Defaults:
| Positional | Default |
|---|---|
rtlTcpPort
|
1234
|
callsign
|
RTLTCP
|
centerHz
|
7100000
|
bandwidthHz
|
1024000
|
| location | Budapest (47.50, 19.04)
|
Without --station the receiver is ad-hoc: it uses the antenna options, the
callsign positional and --lat/--lon, and works anonymously. With
--station (your own station by id or name, or someone else's PUBLIC station
by id) every upstream session mints a fresh receiver ticket bound to that
station (needs --server-id, or a station-bound --ticket): the station's
location, antenna and rig apply and the callsign positional and antenna
options are ignored. --antenna, --rig and --track work as for TX.
--timestamped sets the v25 timestamped flag of the receiver session (there
is no separate timestamped role any more). The upstream
connection is to the SimTX server given by <simtxHost> <simtxPort>.
The callsign follows the same rules as transmitter sessions. The center
frequency must be zero or greater, the bandwidth must be positive, and both
ports must be in 1..65535.
simtx-cli --server-id myserver rx argon.simtx.net 5556 1234 N0CALL 7100000 1024000
Then connect SDR++ to 127.0.0.1:1234.
Receiver options
| Option | Default | Meaning |
|---|---|---|
--hardware-profile PATH
|
generic R820T | RTL tuner/oscillator profile file. |
--service-credential VALUE
|
empty | Service credential for authenticated receiver access. Requires --server-id.
|
--service-credential-file PATH
|
empty | Read the service credential from an owner-only (0600) file.
|
--timestamped
|
off | Use the timestamped receiver mode. |
--rx-read-timeout-ms N
|
5000
|
Upstream read timeout, 1..60000. 0 uses the default.
|
--rx-max-retries N
|
3
|
Upstream attempts before failing. -1 retries forever. 0 uses the default.
|
--rx-retry-base-ms N
|
250
|
Base retry delay, 1..5000. Retries back off with jitter. 0 uses the default.
|
--rx-jitter-ms N
|
300
|
Jitter buffer, 1..2000. Negative disables prefetch. 0 uses the default.
|
--rx-auto-stepdown
|
off | Narrow the upstream request after repeated stalls. |
--rx-no-lossy-link
|
off | Disable all lossy-link tuning at once: synchronous reads, fixed backoff, and resets on reconnect. |
--rx-listen-host IP
|
127.0.0.1
|
rtl-tcp bind address. Must be an IP literal. |
WARNING: --rx-listen-host 0.0.0.0 exposes the bridge to the LAN. The
rtl-tcp bridge is unauthenticated, so anyone who can reach the port can
retune it.
Service credential notes:
- A service credential requires
--server-id. --service-credentialcan also come fromSIMTX_SERVICE_CREDENTIAL.--service-credential-filemust be readable only by its owner (chmod 600)
on Unix-like systems.
- Service credentials are never written to preset files.
- With a credential or account identity, the receiver asks for a fresh ticket
on each upstream reconnect.
Press Ctrl-C to stop the bridge.
Station commands: stations, passes
simtx-cli [global flags] stations [list]
simtx-cli [global flags] stations show <id|name>
simtx-cli [global flags] --name N --callsign C --lat L --lon L [antenna options] \
[--visibility PUBLIC|UNLISTED] stations create
simtx-cli [global flags] --name N --callsign C [--altitude-km KM] [--inclination DEG] \
[--raan DEG] [--visibility PUBLIC|UNLISTED] stations launch
simtx-cli [global flags] stations delete <id|name>
simtx-cli [global flags] [--yes] stations deorbit <id|name>
simtx-cli [global flags] --sat <id|name> --station <id|name> [--hours H] [--min-el DEG] passes
These commands talk to the account service through your signed-in session
(run login first) and print one J: line:
| Command | type
|
Payload |
|---|---|---|
stations, list
|
my-stations
|
count, stations: [StationView]
|
show
|
station
|
station: StationView
|
create
|
station.created
|
station: StationView
|
launch
|
station.launched
|
station: StationView (orbit epoch = launch)
|
delete
|
station.deleted
|
stationId
|
deorbit
|
station.deorbiting
|
station: StationView (state: DEORBITING, deorbit)
|
passes
|
passes
|
satellite, station, from, to, reentryAt, passes
|
StationView is the account service's JSON (camelCase): stationId, kind
(GROUND/SATELLITE), state, name, callsign, visibility, revision,
limits, location (ground) or orbit + attitude (satellite), antennas
(antennaId, name, ordinal, type, heightMeters, … mount, pointing)
and rigs (rigId, name, paMaxWatts, noiseFigureDb, tx).
createmakes a ground station with one antenna built from the antenna
options (default EFHW) at --lat/--lon. Add more antennas and rigs, or
edit them, in the app's station editor.
launchputs a satellite on a circularsimtx-j2-v1orbit (--altitude-km
above the 6371 km sphere, --inclination, --raan; the mean anomaly is 0
at the launch instant), nadir-pointing with one turnstile antenna. The
perigee must be at least 200 km and the apogee at most 40,000 km.
Satellites are sealed at launch: only name, callsign, visibility and
limits can change afterwards; deorbit and relaunch to change anything else.
Use the app's mission designer for custom orbits, attitudes and antennas.
deorbitfires a real retrograde burn. It first fetches a preview and
prints the burn (delta-v) and when and where the satellite will reenter,
then asks Fire the deorbit burn? This cannot be undone. [y/N] on stderr;
only y/yes fires it. --yes skips the question (needed when stdin is
not a terminal). The satellite becomes DEORBITING on its falling orbit,
stays fully usable (tickets, sessions, passes) and burns up at
deorbit.reentryAt (80 km), after which it is DEORBITED. StationView
carries `deorbit: {burnAt, reentryAt, reentryLocation: {latitudeDeg,
longitudeDeg}, deltaVMps}. A second burn fails with already_deorbiting`.
deleteremoves ground stations (satellites must be deorbited and reenter
first).
passespredicts locally with the Go port ofsimtx-j2-v1(the server is
authoritative). Each pass has aos, tca, los (RFC 3339 UTC),
maxElevationDeg, aosAzimuthDeg, losAzimuthDeg and
maxDopplerHzPerMHz (multiply by the carrier in MHz). For a deorbiting
satellite the window ends at reentryAt (also printed), a pass in progress
at reentry ends there, and after reentry the list is empty.
Without arguments stations lists your stations; stations <host> <port>
is the live server query described under Query commands.
Account command: login
simtx-cli [--account-url URL] [--auth-file PATH] login
Starts a device authorization flow:
- The CLI prints a verification URL and a short code.
- Open the URL and approve the code in a browser.
- The CLI stores the account session and exits.
If a usable session is already stored, login reports that you are signed in
and exits successfully.
By default the session is stored in the OS keyring. With --auth-file PATH,
it is stored in a plaintext JSON file with owner-only permissions. The CLI
logs a warning when the plaintext store is used.
If the code expires, run login again.
Query commands
simtx-cli [global flags] stations <host> <port>
simtx-cli [global flags] health <host> <port>
simtx-cli [global flags] catalog <host> <port>
Queries are one-shot. Each opens a session to the server, requests the data,
prints a single J: line, and exits. The request waits up to 10 seconds for a
response.
catalogue is an alias for catalog.
Queries accept the identity options: anonymous by default, --ticket for a
literal ticket, or --server-id to mint a ticket through the signed-in
account. If the server bans the session, the command fails with the server's
message.
stations output
stations <host> <port> lists what the server sees: persistent stations with
their live sessions, every active PUBLIC satellite (even with nobody attached;
latDeg/lonDeg is the sub-satellite point), and ad-hoc sessions (kind
ADHOC, no stationId, one session). UNLISTED stations appear as ad-hoc
sessions with the callsign only.
{
"type": "stations",
"host": "argon.simtx.net",
"port": 5556,
"capturedAtMs": 1792843466892,
"count": 1,
"stations": [
{
"stationId": "…",
"kind": "GROUND",
"callsign": "N0CALL",
"name": "Home",
"ownerHandle": "n0call",
"visibility": "PUBLIC",
"latDeg": 52.37,
"lonDeg": 4.9,
"altitudeMeters": 0,
"revision": 3,
"sessions": [
{
"sessionId": "…",
"role": "TX",
"antennaId": "…",
"callsign": "N0CALL",
"frequencyHz": 7100000,
"modulation": "LSB",
"powerWatts": 100,
"bandwidthHz": 2700,
"latDeg": 52.37,
"lonDeg": 4.9
}
]
}
]
}
address appears on sessions only for moderation sessions.
health output
{
"type": "health",
"host": "argon.simtx.net",
"port": 5556,
"health": {
"capturedAtEpochMillis": 0,
"status": "OK",
"uptimeMillis": 0,
"connectedClients": 0,
"connectedStations": 0,
"activeTransmitters": 0,
"receiverGroups": 0,
"acceptorAlive": true,
"worldTick": 0,
"processCpuLoad": 0.0,
"systemCpuLoad": 0.0,
"heapUsedBytes": 0,
"heapMaxBytes": 0,
"transmitters": [
{
"stationUuid": "…",
"callsign": "N0CALL",
"address": "…",
"sourceType": "AUDIO_STREAM",
"frequencyHz": 7100000,
"modulation": "LSB",
"sampleRate": 48000
}
],
"receivers": [
{
"stationUuid": "…",
"callsign": "RTLTCP",
"address": "…",
"centerFrequencyHz": 7100000,
"sampleRate": 1024000,
"wideband": false,
"framesWritten": 0,
"framesDropped": 0
}
]
}
}
catalog output
{
"type": "catalog",
"host": "argon.simtx.net",
"port": 5556,
"count": 1,
"entries": [
{
"id": 1,
"kind": "…",
"name": "…",
"description": "…",
"author": "…",
"sizeBytes": 0,
"fileCount": 0,
"types": [],
"categories": [],
"packageId": "…",
"packageVersion": "…",
"dependencies": [
{
"id": "…",
"versionRange": "…"
}
],
"revision": 0,
"uploadedAtEpochMillis": 0
}
]
}
Empty station, transmitter, receiver, catalog, type, category, and dependency
lists are always encoded as [], never null.
Version and update commands
version
simtx-cli version
Prints simtx-cli <version> using the local build version, including build
metadata. No network access and no version gate.
update
simtx-cli update
Prints one J: line:
{
"current": "…",
"latest": "…",
"updateAvailable": true,
"appimageUrl": "…",
"zsyncUrl": "…",
"sha256": "…",
"builtAt": "…"
}
Behaviour:
- Exits successfully and logs that the build is up to date when no update is
available.
- Exits with a non-zero status when an update is available.
- Exits with a non-zero status when update metadata cannot be fetched.
- Bypasses the strict version gate.
- The CLI never modifies its own installation. AppImage users can use the
printed URLs or a standard AppImage delta update tool.
Presets
Preset files are plain-text Java-properties-style files. The recommended
extensions are .stxpreset for transmit sessions and .srxpreset for
receiver sessions.
Format:
# Comments start with # or !
station.id=Home
rf.frequencyHz=7100000
rf.modulation=LSB
rf.bandwidthHz=2700
rf.lowCutHz=350
rf.powerWatts=100
rf.radioPreset=ICOM_IC_7300
cli.host=argon.simtx.net
cli.port=5556
Rules:
- Lines are
key=value.key:valueis also accepted. - Lines beginning with
#or!are comments. - Blank lines are ignored.
\n,\r,\t,\\, and\uXXXXescapes are supported.- Unknown keys are ignored.
- Preset values are overridden by environment variables and explicit flags.
Load and save:
simtx-cli --preset station.stxpreset audio argon.simtx.net 5556 \
7100000 message.wav USB
simtx-cli --station Home --save-preset station.stxpreset audio argon.simtx.net 5556 \
7100000 message.wav USB
--save-preset writes the effective settings and continues running the
command. Files are written with owner-only permissions and sorted by key.
The literal --ticket value and service credentials are never written to a
preset; the CLI warns when a ticket was supplied but not saved.
rx can write an .srxpreset:
simtx-cli --save-preset receiver.srxpreset rx argon.simtx.net 5556 1234
Transmit preset keys
Common keys:
| Key | Meaning |
|---|---|
station.id
|
Persistent station id or name (--station).
|
station.callsign
|
Receiver callsign (ad-hoc rx); ignored for TX.
|
station.lat, station.latitude
|
Latitude (ad-hoc rx, stations create); ignored for TX.
|
station.lon, station.longitude
|
Longitude (ad-hoc rx, stations create); ignored for TX.
|
rf.frequencyHz, rf.frequency, rf.freq
|
Frequency in Hz. |
rf.modulation
|
Modulation name. |
rf.bandwidthHz, rf.bandwidth
|
Bandwidth in Hz. |
rf.lowCutHz
|
Audio low cut in Hz. |
rf.powerWatts, rf.power
|
Power in watts. |
rf.radioPreset
|
Radio preset name. |
antenna.type
|
Antenna type. |
antenna.height
|
Antenna height in metres. |
antenna.length
|
Wire or element length in metres. |
antenna.azimuth
|
Azimuth in degrees clockwise from north. |
antenna.feedlineLossDb
|
Feedline loss in dB. |
antenna.necDeck
|
Custom NEC-2 antenna deck text. |
antenna.necFile
|
Path to custom NEC-2 antenna deck file (.nec). |
source.tone
|
Reserved for tone-controlled sources; accepted but currently unused by the built-in commands. |
cli.host
|
Server host. |
cli.port
|
Server port. |
cli.accountUrl
|
Account service URL. |
cli.serverId
|
Game server ID. |
cli.ticket
|
Literal ticket. Loaded, but never saved. |
cli.authFile
|
Plaintext credential file path. |
cli.audioFile
|
Audio file path. |
cli.iqLocalPort
|
Local IQ loopback port. |
cli.iqSampleRate
|
IQ sample rate. |
cli.sampleRate, source.sampleRate, rf.sampleRate
|
General sample rate. |
cli.debug
|
Enable verbose logging. |
cli.telemetryJson
|
Enable telemetry JSON output. |
PA knob keys, in the same order as the positional overrides:
| Key | Knob |
|---|---|
rf.finalDriveDb
|
Final drive, dB |
rf.paHeadroomDb
|
PA headroom, dB |
rf.audioCompressionDb
|
Audio compression, dB |
rf.paSoftness
|
PA softness |
rf.paAmPmDeg
|
PA AM/PM, degrees |
rf.carrierLeakDb
|
Carrier leak, dB |
rf.sidebandSuppressionDb
|
Opposite-sideband suppression, dB |
rf.frequencyOffsetHz
|
Frequency offset, Hz |
rf.mainsHumDb
|
Mains hum level, dB |
rf.mainsHumHz
|
Mains hum frequency, Hz |
rf.amModulationIndex
|
AM modulation index |
rf.paSupplySagDb
|
PA supply sag, dB |
rf.txNoiseFloorDb
|
TX noise floor suppression below peak carrier, dBc (0–160, higher = cleaner) |
rf.oscillatorPhaseNoiseDb
|
Oscillator phase noise suppression at 10 kHz offset, dBc/Hz (0–160, higher = cleaner) |
rf.cwKeyRiseMs
|
CW key rise time, ms |
Receiver preset keys
Receiver presets accept the shared station.*, rf.frequencyHz,
rf.bandwidthHz, antenna., and cli. keys, plus:
| Key | Equivalent option |
|---|---|
rx.rtlPort
|
Local rtl-tcp port. |
rx.hardwareProfile
|
--hardware-profile
|
rx.serviceCredential
|
--service-credential
|
rx.serviceCredentialFile
|
--service-credential-file
|
rx.timestamped
|
--timestamped
|
rx.readTimeoutMs
|
--rx-read-timeout-ms
|
rx.maxRetries
|
--rx-max-retries
|
rx.retryBaseMs
|
--rx-retry-base-ms
|
rx.jitterMs
|
--rx-jitter-ms
|
rx.autoStepdown
|
--rx-auto-stepdown
|
rx.noLossyLink
|
--rx-no-lossy-link
|
rx.listenHost
|
--rx-listen-host
|
Receiver location defaults to Budapest (47.50, 19.04) unless overridden.
Service credentials are never saved to receiver presets.
Exit status and signals
- Successful commands exit with status
0. - Invalid arguments, network failures, authentication failures, server kicks,
disconnects, query timeouts, and update availability exit with status 1.
versionnever contacts the network and always exits successfully.- Ctrl-C (SIGINT) and SIGTERM stop transmitter and receiver sessions cleanly.
The CLI exits successfully (status 0) after an interrupt.
- When a transmitter audio file stream finishes playing to completion (EOF), the CLI
exits cleanly with status 0.
- An abnormal session termination (such as an upstream server kick or network drop)
exits with status 1.
- Query commands print their JSON result before exiting.
Troubleshooting
| Symptom | Likely cause |
|---|---|
update required
|
CLI version does not exactly match the update service. Upgrade, then retry. |
update check failed
|
Update service unreachable. Check network access. |
run 'simtx-cli login' first
|
--server-id was used without a signed-in account.
|
transmitting needs --station
|
TX always runs on a persistent station; pass --station (see stations).
|
--server-id is required to mint a station-bound ticket
|
Station-bound sessions need an account ticket. |
scope_required / sign in again
|
The stored session predates station management; run login again.
|
The account service does not support station-bound sessions yet
|
The account service predates v25 stations. |
rejected: … (FORBIDDEN)
|
Not your station (TX), an UNLISTED station of someone else (RX), or a mismatched ticket. |
server did not answer with a protocol v25 hello
|
The server runs an older protocol; it needs the v25 update. |
--server-id is required with a service credential
|
Receiver credential supplied without a game server ID. |
service credential file must be owner-only
|
Run chmod 600 on the credential file.
|
| Connection refused on rtl-tcp | The SDR client is pointed at the wrong host/port, or rx is not running.
|
| IQ connection refused | GNU Radio is not connected to 127.0.0.1:<localIqPort>, or the sample rate does not match.
|
Multiplexed Serve Mode (serve)
simtx-cli serve runs a single long-lived multiplexing process designed for game engines and integration layers (such as C#/.NET on Windows and Linux). Rather than spawning a separate process per radio or session, serve manages multiple concurrent receivers, scope panadapters, and persistent control channels over standard input/output using newline-delimited JSON (NDJSON), accompanied by local loopback binary TCP streaming sockets for high-rate float32 IQ data.
Standard I/O Protocol (NDJSON)
Communication occurs over two streams:
stdin: Receives single-line JSON commands from the controlling game or application.stdout: Emits single-line JSON responses, events, and push notifications. Lines are newline-terminated and thread-safe.
Command Format (Application -> CLI)
{
"id": "req-101",
"cmd": "rx.open",
"session": "radio-1",
"params": { ... }
}
| Field | Type | Description |
|---|---|---|
id
|
string (optional) | Correlates responses to requests. Echoed back in the corresponding event. |
cmd
|
string (required) | Command identifier (e.g. rx.open, control.subscribe_health, serve.ping).
|
session
|
string (optional) | Session identifier chosen by the application (e.g. radio-1, panadapter, ctl-main).
|
params
|
object (optional) | Command-specific arguments. |
Event & Response Format (CLI -> Application)
{
"event": "rx.opened",
"session": "radio-1",
"id": "req-101",
"data": { ... }
}
| Field | Type | Description |
|---|---|---|
event
|
string (required) | Event type (e.g. serve.ready (its data.version is "v25"), rx.opened, control.health, error).
|
session
|
string (optional) | Associated session identifier. |
id
|
string (optional) | Corresponds to the request id for direct command replies. Omitted for asynchronous pushes.
|
data
|
object (optional) | Event payload data. |
error
|
string (optional) | Error description when event is "error" or a command fails.
|
Command Reference
1. System Lifecycle
serve.ready:
Emitted immediately upon startup to advertise protocol and engine capabilities:
{
"event": "serve.ready",
"data": {
"serve_version": 2,
"protocol_version": 24,
"app_version": "0.1.0-alpha-14"
}
}
serve.ping:
Checks serve process responsiveness and fetches system clock times.
- Request:
{"id": "p1", "cmd": "serve.ping"} - Response:
- Request:
{
"event": "serve.pong",
"id": "p1",
"data": {
"time_unix_nanos": 1792686378727327947,
"time_utc": "2026-10-22T16:26:18.727342195Z"
}
}
serve.stop/exit/quit:
Closes all active receiver, transmitter, and control sessions, frees resources, and terminates the serve process with exit code 0.
- Request:
{"id": "p2", "cmd": "serve.stop"} - Response:
{"event": "serve.stopped", "id": "p2", "data": {"ok": true}}
- Request:
2. Receiver Sessions (rx.*)
rx.open:
Opens an upstream receiver session (v25 ReceiverStart, timestamped flag set by default) to the SimTX server and starts a local loopback TCP listener on 127.0.0.1:<port> for zero-copy binary IQ streaming. The receiver is ad-hoc unless station_id is given.
- Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
host
|
string | argon.simtx.net
|
SimTX upstream server host. |
port
|
int | 5556
|
SimTX upstream server port. |
ticket
|
string | ""
|
Single-use session ticket (anonymous if blank). |
callsign
|
string | "HAMSIMTEST"
|
Receiver station callsign. |
lat, lon
|
float | 0.0, 0.0
|
Receiver geographic latitude and longitude. |
altitude
|
float | 0.0
|
Receiver antenna altitude in meters. |
noise_figure_db
|
float | 0.0
|
Front-end noise figure in dB. |
frequency_hz
|
float | 7093000
|
Center tuned frequency in Hz. |
sample_rate
|
int | 24000
|
IQ sample rate in Hz (e.g. 24000 for voice radio, 64000 for panadapter, 2400000 for SDR view). |
bandwidth_hz
|
int | 0
|
Receiver filter bandwidth in Hz (0 defaults to sample_rate).
|
chunk_samples
|
int | 0
|
Samples per chunk (0 defaults to 512).
|
plain_tcp
|
bool | false
|
Force plain TCP connection instead of TLS. |
hardware_profile
|
string | ""
|
Optional hardware calibration profile path. |
timestamped
|
bool | true
|
Enables nanosecond server timestamping on IQ blocks. |
antenna_type
|
string | "DIPOLE"
|
Template type (DIPOLE, INVERTED_V, VERTICAL, YAGI_3EL, YAGI_5EL, LOOP, BEVERAGE, LONGWIRE, EFHW_32M_49_1).
|
antenna_height
|
float | 10.0
|
Height above ground in meters. |
antenna_length
|
float | 0.0
|
Wire length in meters (0 = resonant). |
azimuth
|
float | 0.0
|
Antenna azimuth heading in degrees. |
feedline_loss_db
|
float | 1.0
|
Feedline transmission loss in dB. |
ground
|
string | "AVERAGE"
|
Ground model (AVERAGE, FREESPACE, PERFECT).
|
nec_deck
|
string | ""
|
Raw NEC-2 deck card text (GW/GE/EX cards). Overrides template parameters. |
nec_file
|
string | ""
|
Path to a local .nec antenna file.
|
station_id
|
string | ""
|
Bind the receiver to this persistent station (UUID). ticket must then be a receiver ticket minted for the same station (and antenna/rig); callsign, location and antenna parameters are ignored.
|
antenna_id, rig_id
|
string | ""
|
Station antenna (default: first) and rig (default: none) for station_id.
|
- Response:
{
"event": "rx.opened",
"session": "radio-1",
"id": "req-101",
"data": {
"stream_addr": "127.0.0.1:41235",
"sample_rate": 24000,
"chunk_samples": 512,
"antenna_accepted": {
"source": "synthesized",
"type": "DIPOLE",
"wire_count": 1,
"segment_count": 21,
"deck_bytes": 191
}
}
}
- Rejection / Error:
If the server limits or refuses the receiver connection, rx.rejected is emitted with the upstream reason:
{
"event": "rx.rejected",
"session": "radio-1",
"id": "req-101",
"error": "upstream receiver session refused: limit exceeded"
}
rx.retune:
Retunes an active receiver to a new frequency without reconnecting or tearing down the loopback streaming socket.
- Request:
{"id": "r2", "cmd": "rx.retune", "session": "radio-1", "params": {"frequency_hz": 7095000}} - Response:
{"event": "rx.retuned", "session": "radio-1", "id": "r2", "data": {"frequency_hz": 7095000}}
- Request:
rx.close:
Closes the local loopback TCP listener and disconnects the upstream receiver session.
- Request:
{"id": "r3", "cmd": "rx.close", "session": "radio-1"} - Response:
{"event": "rx.closed", "session": "radio-1", "id": "r3", "data": {"ok": true}}
- Request:
3. Control Sessions & Subscriptions (control.*)
control.open:
Establishes a persistent control connection (role CONTROL) to the server. The CLI pings the server every 15 s to keep the link alive.
- Parameters:
host,port,ticket,plain_tcp. - Response:
{"event": "control.opened", "session": "ctl-1", "id": "c1", "data": {"host": "argon.simtx.net", "port": 5556}}
- Parameters:
control.subscribe_stations:
Starts periodic polling of the server station list (v25: persistent stations with live sessions, public satellites, ad-hoc sessions).
- Parameters:
interval_sec(default5),on_change_only(bool, defaultfalse). - Response:
{"event": "control.subscribed_stations", "session": "ctl-1", "id": "c2", "data": {"interval_sec": 5}} - Push Event (
control.stations):
- Parameters:
{
"event": "control.stations",
"session": "ctl-1",
"data": {
"count": 1,
"stations": [
{
"kind": "ADHOC",
"callsign": "HAMSIMTEST",
"visibility": "UNLISTED",
"latDeg": 39.53,
"lonDeg": -119.81,
"altitudeMeters": 0,
"sessions": [
{
"sessionId": "a4b1...",
"role": "TX",
"callsign": "HAMSIMTEST",
"frequencyHz": 7093000,
"modulation": "USB",
"powerWatts": 100.0,
"bandwidthHz": 2700,
"latDeg": 39.53,
"lonDeg": -119.81
}
]
}
]
}
}
control.subscribe_health:
Starts periodic polling of server health, computing the real-time simulation clock offset $\Delta_{\text{sim}} = T_{\text{sim}} - T_{\text{local}}$.
- Parameters:
interval_sec(default30). - Push Event (
control.health):
- Parameters:
{
"event": "control.health",
"session": "ctl-1",
"data": {
"health": {
"SimulationTimeEpochMillis": 1729600000123,
"CapturedAtEpochMillis": 1729600000120,
"WorldTicks": 45129,
"ActiveTransmitters": 1,
"ActiveReceivers": 3
},
"time_sync": {
"simulation_time_millis": 1729600000123,
"server_captured_millis": 1729600000120,
"local_time_millis": 1729600000000,
"offset_millis": 123
}
}
}
control.weather:
Requests a regional weather map raster from the server.
- Parameters:
center_lat,center_lon: Center geographic coordinates.lat_span,lon_span: Extent in degrees (default5.0).width,height: Grid dimensions (e.g.64$\times$32).type: Raster layer type:"RAIN_RATE","DUCT_STRENGTH","CLOUD_WATER","SURFACE_TEMPERATURE","LIGHTNING_RATE".
- Push Event (
control.weather):
- Parameters:
Carries grid bounds (SouthLatDeg, NorthLatDeg, WestLonDeg, EastLonDeg), surface parameters, and raw raster values array (Values).
control.catalog/control.catalog_fetch:
Browses community presets and downloads specific items by entry_id.
control.close:
Closes the control connection and cancels all subscription polling.
4. Transmitter Sessions (tx.*)
tx.open:
Opens an upstream transmitter session (RoleTransmitter) using protocol v24 and starts a local loopback TCP listener on 127.0.0.1:<port> for zero-copy streaming of live microphone audio or complex IQ baseband.
- Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
host
|
string | argon.simtx.net
|
Upstream server hostname. |
port
|
int | 5556
|
Upstream server port. |
plain_tcp
|
bool | false
|
Force plain TCP connection instead of TLS. |
ticket
|
string | ""
|
Single-use session ticket (anonymous if blank). |
callsign
|
string | "HAMSIMTEST"
|
Transmitter station callsign. |
lat, lon
|
float | 0.0, 0.0
|
Geographic coordinates. |
altitude
|
float | 0.0
|
Antenna altitude in meters. |
frequency_hz
|
float | 7093000
|
Center transmission carrier frequency in Hz. |
power_watts
|
float | 100.0
|
Transmit power in watts. |
sample_rate
|
int | 48000
|
Declared input sample rate (e.g. 22050 for voice or up to 10000000 for IQ). |
bandwidth_hz
|
int | 2700
|
Occupied RF bandwidth in Hz. |
low_cut_hz
|
int | 350
|
Audio low-frequency cutoff in Hz. |
modulation
|
string | "USB"
|
Modulation (USB, LSB, AM_DSB_FC, CW, NFM, WFM, IQ).
|
audio_gain
|
float | 1.0
|
Input audio gain factor. |
tone_hz
|
float | 0.0
|
Test tone frequency in Hz (0 to disable). |
duty_cycle
|
float | 1.0
|
PA duty cycle factor (0.0 to 1.0). |
clean_lab
|
bool | false
|
Enable clean lab mode (disables driver distortion). |
antenna_type
|
string | "DIPOLE"
|
Template type (DIPOLE, INVERTED_V, VERTICAL, YAGI_3EL, YAGI_5EL, LOOP, BEVERAGE, LONGWIRE, EFHW_32M_49_1).
|
antenna_height
|
float | 10.0
|
Height in meters. |
antenna_length
|
float | 0.0
|
Wire length in meters (0 = resonant). |
azimuth
|
float | 0.0
|
Azimuth heading in degrees. |
feedline_loss_db
|
float | 1.0
|
Feedline loss in dB. |
ground
|
string | "AVERAGE"
|
Ground model (AVERAGE, FREESPACE, PERFECT).
|
nec_deck
|
string | ""
|
Raw NEC-2 deck card text (GW/GE/EX cards). Overrides template parameters. |
nec_file
|
string | ""
|
Path to a local .nec antenna file.
|
mode_name
|
string | ""
|
Custom mode info identifier. |
mode_description
|
string | ""
|
Custom mode info human-readable description. |
mode_bandwidth_hz
|
int | 0
|
Custom mode bandwidth limit. |
| Impairment fields: | |||
final_drive_db
|
float | 0.0
|
Final driver stage level in dB. |
pa_headroom_db
|
float | 6.0
|
PA headroom in dB. |
audio_compression_db
|
float | 0.0
|
Mic audio speech compressor level in dB. |
pa_softness
|
float | 0.0
|
PA saturation softness curve factor. |
pa_am_pm_deg
|
float | 0.0
|
PA AM-to-PM phase conversion in degrees. |
carrier_leak_db
|
float | -50.0
|
Residual carrier leak level in dB. |
opposite_sideband_suppression_db
|
float | 50.0
|
Opposite sideband suppression in dB. |
freq_offset_hz
|
float | 0.0
|
Local oscillator frequency offset in Hz. |
mains_hum_level_db
|
float | -60.0
|
Power supply mains hum level in dB. |
mains_hum_freq_hz
|
float | 50.0
|
Mains hum base frequency in Hz (e.g. 50 or 60). |
am_modulation_index
|
float | 0.85
|
AM modulation index. |
pa_supply_sag_db
|
float | 0.0
|
PA power supply voltage sag in dB. |
tx_noise_floor_db
|
float | 120.0
|
Broadband noise floor suppression in dBc. |
oscillator_phase_noise_db
|
float | 120.0
|
Oscillator phase noise suppression at 10 kHz in dBc/Hz. |
cw_key_rise_ms
|
float | 5.0
|
CW keying rise/fall time envelope in ms. |
- Responses / Events:
tx.opened: Session accepted and on-air or ready.
- Responses / Events:
{
"event": "tx.opened",
"session": "tx-1",
"id": "t1",
"data": {
"stream_addr": "127.0.0.1:43521",
"sample_rate": 22050,
"wire_rate": 48000,
"audio_in": true,
"iq_in": false,
"antenna_accepted": { ... }
}
}
tx.queued: Session queued by server if channel or server capacity is full.
{"event": "tx.queued", "session": "tx-1", "id": "t1", "data": {"queue_position": 2}}
tx.rejected: Connection refused by server.
{"event": "tx.rejected", "session": "tx-1", "id": "t1", "error": "callsign already in use"}
tx.kicked: Disconnected during session.
{"event": "tx.kicked", "session": "tx-1", "data": {"reason": "time limit expired"}}
tx.key:
Keys the transmitter (keyed: true). Incoming audio or IQ on the loopback socket is actively processed, modulated, and pushed to the upstream server.
- Request:
{"id": "t2", "cmd": "tx.key", "session": "tx-1"} - Response:
{"event": "tx.keyed", "session": "tx-1", "id": "t2", "data": {"keyed": true}}
- Request:
tx.unkey:
Unkeys the transmitter (keyed: false). The session remains warm and active:
- Loopback audio/IQ ingestion is muted, and the CLI automatically pumps warm silence heartbeats (20ms frames) to keep server lease renewed.
- Server buffer behavior: Any already-buffered audio or IQ in the server finishes draining through playout without being cut off mid-packet or triggering timeouts.
- Request:
{"id": "t3", "cmd": "tx.unkey", "session": "tx-1"} - Response:
{"event": "tx.unkeyed", "session": "tx-1", "id": "t3", "data": {"keyed": false}}
tx.set:
Applies live mid-session parameter modifications without interrupting the connection or closing sockets.
- Parameters:
frequency_hz,power_watts,bandwidth_hz,low_cut_hz, and any of the 15 impairment fields. - Request:
- Parameters:
{
"id": "t4",
"cmd": "tx.set",
"session": "tx-1",
"params": {
"frequency_hz": 7094000,
"power_watts": 80.0,
"bandwidth_hz": 2400
}
}
- Response:
{"event": "tx.set_applied", "session": "tx-1", "id": "t4", "data": {"ok": true}}
- Response:
tx.close:
Stops loopback streaming and closes the upstream transmitter session.
- Request:
{"id": "t5", "cmd": "tx.close", "session": "tx-1"} - Response:
{"event": "tx.closed", "session": "tx-1", "id": "t5", "data": {"ok": true}}
- Request:
tx.telemetry:
Asynchronous push event carrying full RF and PA telemetry from the server on every frame:
{
"event": "tx.telemetry",
"session": "tx-1",
"data": {
"sequence": 104,
"server_time_millis": 1792686379220,
"actual_frequency_hz": 7093000,
"requested_power_watts": 100.0,
"forward_power_watts": 78.4,
"delivered_power_watts": 76.1,
"reflected_power_watts": 2.3,
"vswr": 1.25,
"mismatch_loss_db": 0.12,
"transformer_loss_db": 0.08,
"foldback_loss_db": 0.0,
"alc_db": -0.5,
"buffer_seconds": 0.42,
"finals_temperature_c": 46.5,
"thermal_foldback_db": 0.0,
"pa_compression_db": 0.4,
"pa_heat_watts": 21.6,
"pa_efficiency_percent": 78.0,
"pa_thermal_fault_active": false
}
}
Loopback Streaming Protocols
Receiver Output Protocol (CLI -> Application)
For receiver sessions, high-bandwidth complex samples are transmitted over a dedicated loopback TCP socket (stream_addr) rather than base64-encoded on standard I/O. This avoids JSON parser CPU overhead, memory allocations, and garbage collection stalls when streaming at high sample rates (e.g. 2.4 Msps $\approx$ 153 Mbit/s).
Packet Header & Payload Layout
Each chunk sent across the local TCP socket consists of a 12-byte header followed by raw interleaved 32-bit floating-point samples:
+-------------------------------------------------------------+
| Field | Size | Byte Order |
+-------------------------------------------------------------+
| ServerTimestampUnixNanos | 8 bytes | Big-Endian (BE) |
| FloatCount (samples * 2) | 4 bytes | Big-Endian (BE) |
| Interleaved float32[] I/Q | N bytes | Little-Endian(LE) |
+-------------------------------------------------------------+
ServerTimestampUnixNanos(int64, 8 bytes, Big-Endian):
The authoritative simulation clock timestamp of the first sample in the block. Used for FT8/FT4 synchronization and synchronized voice audio playout.
FloatCount(uint32, 4 bytes, Big-Endian):
The number of float32 values in the payload ($= \text{ChunkSamples} \times 2$).
- **
I/Q Samples(float32[],FloatCount * 4bytes, Little-Endian)**:
Alternating real and imaginary components: $[I_0, Q_0, I_1, Q_1, I_2, Q_2, \dots]$.
Transmitter Ingest Protocol (Application -> CLI)
For transmitter sessions (tx.open), the application connects to the local TCP listener (stream_addr):
- Audio Streams (Microphone / Voice Modes):
- When opened with voice/analog modulations (
USB,LSB,AM_DSB_FC,CW,NFM,WFM), the socket accepts continuous 32-bit Little-Endian IEEE-754 float mono PCM audio at the declaredsample_rate(e.g. 22,050 Hz or 48,000 Hz). - Audio is resampled internally in real time to the RF pipeline wire rate via
RatePump, where microphone compression, frequency response, and exciter impairments are applied before modulation.
- When opened with voice/analog modulations (
- IQ Streams (GNU Radio / Digital Modes):
- When opened with
modulation: "IQ", the socket accepts complex baseband samples (interleaved float32 real and imaginary pairs). - Ingest supports two modes:
- Framed packets with 12-byte header: Same format as receiver packets (
[ServerTimestampUnixNanos (int64 BE)][FloatCount (uint32 BE)][samples float32 LE]). This allows precise scheduling of FT8/JS8 blocks at specific server time slots ($T_{\text{server}}$). Block sizes up to 65,536 values are supported. - Raw stream: Continuous stream of interleaved float32 pairs sent directly into the RF chain.
- Framed packets with 12-byte header: Same format as receiver packets (
- When opened with
Server Buffer Drain at Unkey
When a transmitter is unkeyed via tx.unkey:
- The client CLI stops reading audio/IQ from the loopback socket and immediately switches to emitting 20 ms frames of silence (
holdSilence) upstream. - This serves two functions:
- It acts as an active keep-alive heartbeat, preventing session timeouts and lease expiration between overs.
- The server's internal audio buffer drains gracefully to the simulated air. The trailing 0.5–1.0 s of voice audio is never truncated or abruptly silenced mid-sentence.
- When
tx.keyis reissued, normal audio/IQ ingest resumes instantly with no need to renegotiate tickets or reconnect.
Serve Protocol Changelog
Version 2 (CLI alpha-14 / Protocol v24)
- Added full transmitter session control:
tx.open,tx.key,tx.unkey,tx.set,tx.close. - Added real-time audio loopback streaming with automatic resampling from game rates (e.g. 22,050 Hz) to internal wire rates.
- Added IQ loopback streaming supporting sample rates up to 10 MS/s and server timestamped scheduling blocks.
- Added live mid-session knob adjustment via
tx.set. - Added complete 18-parameter telemetry event emissions (
tx.telemetry). - Added
serve.readybanner containingserve_version: 2,protocol_version: 24, andapp_version. - Added receiver options to
rx.open:altitude,noise_figure_db,bandwidth_hz,chunk_samples,plain_tcp. - Added
rx.rejectedevent when upstream receiver sessions are refused by server quotas or limits. - Added
--plain-tcp/SIMTX_PLAIN_TCP/SIMTX_FORCE_PLAIN_TCPfor environments operating over unencrypted TCP. - Fixed informational server health banners (
BannerInfo) causing query commands to fail.
C# (.NET) Zero-Copy Read Example
using System;
using System.Buffers.Binary;
using System.Net.Sockets;
using System.Runtime.InteropServices;
public async Task ReadIqStreamAsync(string host, int port, CancellationToken ct)
{
using var client = new TcpClient();
await client.ConnectAsync(host, port, ct);
using var stream = client.GetStream();
byte[] header = new byte[12];
byte[] payload = new byte[65536];
while (!ct.IsCancellationRequested)
{
// 1. Read 12-byte header
await stream.ReadExactlyAsync(header, 0, 12, ct);
long timestampNanos = BinaryPrimitives.ReadInt64BigEndian(header.AsSpan(0, 8));
uint floatCount = BinaryPrimitives.ReadUInt32BigEndian(header.AsSpan(8, 4));
int byteCount = (int)floatCount * 4;
if (payload.Length < byteCount)
Array.Resize(ref payload, byteCount);
// 2. Read float32 payload
await stream.ReadExactlyAsync(payload, 0, byteCount, ct);
// 3. Zero-copy cast into float span (x86_64 and arm64 are Little-Endian)
ReadOnlySpan<float> iqSpan = MemoryMarshal.Cast<byte, float>(payload.AsSpan(0, byteCount));
// iqSpan[0] = I0, iqSpan[1] = Q0, iqSpan[2] = I1, iqSpan[3] = Q1...
ProcessIqBlock(timestampNanos, iqSpan);
}
}
RF Impairment Knob Conventions
Fields 14 through 28 define the transmitter simulation impairments. In particular:
- Field 26 (
rf.txNoiseFloorDb): Broadband exciter noise floor suppression below peak carrier power, expressed in dBc (positive values, typically 0–160 dB, default 120 dB). Higher values mean greater suppression (quieter, cleaner background). - Field 27 (
rf.oscillatorPhaseNoiseDb): Close-in oscillator phase noise suppression at 10 kHz offset, expressed in dBc/Hz (positive values, typically 0–160 dB, default 120 dB). Higher values mean greater spectral purity (less skirt broadening).
Both fields follow the suppression convention across both audio and GNU Radio IQ transmitter sessions in protocol v25 (TxImperfections.tx_noise_floor_db / oscillator_phase_noise_db).
Precedence Order
When resolving transmitter knobs:
- Explicit command-line positional floats have highest priority.
- Explicit keys in
--presetfiles (rf.*) override template base defaults. - Radio preset template defaults (e.g.
CLEAN_LAB,ICOM_IC_7300,GENERIC_HF) provide base defaults for any unspecified knobs.