SimTX API/Receiver session
A receiver session delivers a continuous stream of complex baseband (IQ) samples, as received by a virtual antenna at a chosen place and frequency. It is what SDR software sees when it is connected to SimTX. Receiver sessions use ROLE_RECEIVER. An ad-hoc receiver works without an account; a receiver bound to a persistent station needs a game ticket for it.
Frames
| Direction | # | Message | When |
|---|---|---|---|
| C2S | 3 | ForwardedClientAddress | Optional, trusted proxies only, before the start. |
| C2S | 10 | ReceiverStart | Once, right after the hello. |
| S2C | 10 | SessionStatus | BOUND or REJECTED after the start; later UPDATED and ENDED.
|
| S2C | 11 | IqStreamDescriptor | Once, after BOUND.
|
| S2C | 12 | IqBlock | Continuously. |
| C2S | 11 | ReceiverRetune | Any time, to change frequency. |
| C2S | 16 | AntennaPointingUpdate | Station-bound sessions, to steer the antenna. |
| C2S / S2C | 2 | Ping / Pong | Any time. |
| S2C | 3 | Error | When a retune or pointing request fails, or on a protocol violation. |
Sequence
client server
| -- preface, [1] hello (ROLE_RECEIVER) --------------> |
| <---------------------------- [1] hello_result |
| -- [10] receiver_start -----------------------------> |
| <---------------------------- [10] session_status | BOUND
| <---------------------------- [11] iq_descriptor |
| <---------------------------- [12] iq | sequence 1
| <---------------------------- [12] iq | sequence 2
| -- [11] receiver_retune (request_id 7) -------------> | (optional, any time)
| <---------------------------- [12] iq |
| <---------------------------- [10] session_status | UPDATED, RETUNE, request_id 7
| <---------------------------- [12] iq |
| ... |
After the start, a client may send only ReceiverRetune, AntennaPointingUpdate and Ping. Any other frame is answered with Error INVALID_REQUEST and the connection closes.
Placing the receiver
ReceiverStart.site is a SessionSite with exactly one member:
- Ad hoc (
adhoc): the client gives a callsign, a position and an antenna. Works anonymously. This is the simplest way to receive. - Station-bound (
station): the receiver uses one of the account's persistent stations, or any PUBLIC station of another account, including satellites. The station dictates the callsign, placement and antenna. The connection must present areceivergame ticket minted for that station, and the StationBinding must match it. See SimTX API/Stations.
A receiver on a satellite hears the uplink: the signals that reach the satellite's antenna in orbit.
Negotiation
The start states what the client would like; the IqStreamDescriptor states what it gets. Always use the values from the descriptor.
Sample rate
- A requested rate up to 2,400,000 samples per second is narrowband. It is clamped to the range 24,000 to 2,400,000.
- A requested rate above 2,400,000 is wideband. The rate becomes the smaller of the request and 32,000,000, and the block size is fixed at 16,384 samples. Wideband capacity is budgeted at 32 million samples per second per client address, plus a server-wide budget. A start over budget is refused with
LIMIT_REACHED.
Block size
Each IQ block carries exactly chunk_samples complex samples. The requested value is clamped to at least 1,024 and at most the smaller of 1,200,000 and the number of samples in a fixed duration at the negotiated rate. That duration is 40 ms on the clustered public deployment and 0.5 s on a stand-alone server. At 48,000 samples per second on the public server, blocks are 1,920 samples long whatever the client asks for.
Bandwidth
The receiver's filter bandwidth is the requested bandwidth_hz, limited to at most the larger of 24,000 Hz and the sample rate, and to at least 50 Hz.
Sample format
sample_format selects 16-bit (SAMPLE_FORMAT_INT16, 0, the default) or 8-bit (SAMPLE_FORMAT_INT8, 1) samples. The descriptor confirms the format.
Noise figure
noise_figure_db is the receiver's front-end noise figure. Unset, it is the bound rig's noise figure, or 6 dB. Real HF receivers are typically 3 to 8 dB; HF reception is usually limited by external noise anyway.
Decoding samples
Each IqBlock has a per-block floating-point scale and a samples payload of interleaved I and Q integers, big-endian:
x = q × scale / 32767 (INT16) or x = q × scale / 127 (INT8)
The server chooses scale as the peak magnitude of the block, so the integers always use the full range and small signals keep their precision. Check that samples is exactly chunk_samples × 2 × bytesPerComponent bytes long and reject anything else.
A sample with magnitude 1.0 corresponds to −12 dBm at the receiver input. The server clips each component to ±1.0 before quantising, so a receiver next to a strong transmitter overloads exactly like a real one. To convert a block's mean power to dBm:
P(dBm) = −12 + 10 × log10( mean(I² + Q²) )
What the samples contain
The IQ stream is the signal at the output of the receiving antenna, after the antenna's pattern, the feed line loss and the noise figure. It includes everything a real receiver would hear: every transmitter in range with its propagation path, fading and Doppler shift, plus atmospheric, galactic and man-made noise. It does not include receiver hardware imperfections such as tuner roll-off, local-oscillator phase noise or image responses. Software that wants to imitate a particular radio adds those itself.
Timestamps
With timestamped set in the start, every block carries the UTC instant of its first sample on the server's simulated RF timeline (first_sample_epoch_seconds and first_sample_nanos). Timestamps of different receivers are directly comparable, which makes them suitable for time-difference-of-arrival (TDOA) direction finding, aligning several receivers, or measuring end-to-end latency. Timestamps work with or without an account. The v24 role TIMESTAMPED_RECEIVER is gone.
Sequence numbers
IqBlock.sequence numbers the blocks of a stream: 1 for the first block, one more for each block after it, across retunes and moves between server nodes. A step larger than 1 means that the server dropped blocks for this stream, because of backpressure, a retune or a move. 0 means the sender does not number blocks.
Retuning
A ReceiverRetune moves the centre frequency without reconnecting. The rate, block size, bandwidth and sample format stay the same, and no new descriptor is sent. The change takes effect after a settling time of about 120 ms; retunes received during that time are coalesced and only the latest is applied. Expect a short gap or transient.
With a nonzero request_id, the server confirms the retune with SessionStatus UPDATED, update_reason = UPDATE_REASON_RETUNE, the same request_id and the new frequency_hz, once the stream runs on the new frequency. A retune superseded by a later one before it took effect gets no confirmation of its own.
To change the sample rate or bandwidth, open a new connection.
Tracking and pointing
A station-bound receiver can follow a target, usually a satellite, from the start (ReceiverStart.tracking) or later (AntennaPointingUpdate). With doppler_correct, the receiver's tuning is shifted by the target's Doppler, so a satellite's signal stays at the same offset in the passband during a pass. See SimTX API/Tracking and pointing.
Flow control
- The server queues up to 8 blocks per connection. When the queue is full, the oldest block is discarded, and the gap shows in
sequence. - If the client does not read for 5 seconds, the server closes the connection.
- Read the socket on a dedicated thread and hand blocks to processing through a queue of your own, so that slow processing does not stall reading.
Through the public gateway, a receiver may be moved from one server node to another. The client sees a short gap, but the connection and the stream continue.
Failure
A start that cannot be accepted is answered with SessionStatus REJECTED, an error code and a reason: invalid values (INVALID_REQUEST), a binding that does not match the ticket (FORBIDDEN), a deleted station or a reentered satellite (STATION_INACTIVE), or a server, per-address, account or station limit or the wideband budget (LIMIT_REACHED). A running session that the server stops gets ENDED. See SimTX API/Session status and SimTX API/Limits and errors.
Checklist
- Connect with TLS to the host and port from the server directory. Set
TCP_NODELAY. - Send the preface
53 49 4D 54 00 00 00 19and ahelloframe:role = ROLE_RECEIVER; for a station-bound receiver alsoauth = AUTH_KIND_GAME_TICKETand the ticket. - Read the
hello_result; requireHELLO_STATUS_ACCEPTED. - Send
receiver_startwith a site, the centre frequency, bandwidth, sample rate and block size. - Read
session_status; stop onREJECTEDand show its code and reason. Then readiq_descriptor. - Loop on
iq: decodesamples(big-endian integer pairs) withscale, checksequencefor gaps, and skip frames you do not handle. - To retune, send
receiver_retune.
A complete working program is on Quick start.