Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
SimTX Wiki
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
SimTX API/Schema
Page
Discussion
English
Read
Edit
Edit source
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
Edit source
View history
General
What links here
Related changes
Special pages
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
{{SimTX API navigation}} The wire format of the [[SimTX API]] game protocol, version 25, is defined by four protobuf 3 files in the package <code>simtx.v25</code>. They follow below in a shortened form: comments are abridged, and language options, reserved field names and an enum that clients never see are left out. Where any other page of this reference and these files disagree, the files win. == Generating bindings == Save the files under a directory <code>simtx/v25/</code>, because they import each other by that path, and run <code>protoc</code> from the directory above it. For example, for Python: <syntaxhighlight lang="bash"> protoc -I . --python_out=. simtx/v25/common.proto simtx/v25/session.proto simtx/v25/control.proto simtx/v25/frames.proto </syntaxhighlight> Any protobuf 3 toolchain works. Read and write frames with the library's length-delimited helpers, after the 8-byte [[SimTX API/Framing#Preface|preface]]. A minimal hand-written codec is shown on [[SimTX API/Quick start]]. {| class="wikitable" ! File !! Contents |- | [[#frames.proto|frames.proto]] || The frames: <code>ClientFrame</code>, <code>ServerFrame</code>, the hello messages, <code>Error</code>, ping and pong. Also the protocol revisions and the feature registry. |- | [[#session.proto|session.proto]] || Receiver and transmitter sessions, <code>SessionStatus</code> and <code>BoundStation</code>. |- | [[#control.proto|control.proto]] || Control and moderation requests: station list, maps, health, catalog, ephemerides, passes, monitor, banners. |- | [[#common.proto|common.proto]] || Shared types: positions, antennas, stations, orbits, attitude, imperfections, <code>ErrorCode</code>, and the string enumerations. |} == frames.proto == <syntaxhighlight lang="protobuf"> // SimTX wire protocol v25: connection framing. // // Byte stream: the client sends the 8-byte preface i32be 0x53494D54 ("SIMT"), i32be 25. // From then on, each direction is a sequence of frames: varint length + one ClientFrame / // ServerFrame message (protobuf "delimited" encoding). Max frame 16 MiB; the client hello frame // at most 64 KiB. Receivers also bound a frame's shape (nested messages, list elements and // unknown fields; never the group encoding). Unknown oneof members are skipped by the receiver. // The first client frame must be `hello`; the first server frame is `hello_result`. // // Protocol revisions (ClientHello / ServerHello `protocol_revision`): each side sends the highest // revision it implements and relies on revision-N behaviour only when the peer's revision is >= N. // Revision 1 is the current protocol; clients send 1. A server keeps a station-bound session // running across a station edit (SessionStatus UPDATED with update_reason and a station snapshot) // only when the client's revision is >= 1; other clients get ENDED. // // Feature registry (ServerHello `features`): optional server capabilities that depend on the // deployment rather than on the schema. Clients ignore names they do not know; names are never // reused or redefined (a changed meaning gets a new name, e.g. stations.v2). // stations.v1 persistent stations: StationBinding in session starts, StationList entries // pointing AntennaPointingUpdate and Tracking are accepted // ephemeris EphemerisRequest is answered // passes PassPredictionRequest is answered // kinematics satellite motion (orbit, attitude, Doppler) is simulated // ClientHello `features` uses the same names for what the client understands (stations.v1: it // handles BoundStation and station-bound sessions); servers ignore names they do not know. // Prefer protocol_revision for schema-level knowledge. syntax = "proto3"; package simtx.v25; import "simtx/v25/common.proto"; import "simtx/v25/session.proto"; import "simtx/v25/control.proto"; // Required: servers reject a hello with UNSPECIFIED (or an unknown role). enum Role { ROLE_UNSPECIFIED = 0; ROLE_RECEIVER = 1; ROLE_TX_AUDIO = 2; ROLE_TX_GENERATED = 3; ROLE_CONTROL = 4; ROLE_MODERATION = 5; } // 0 = ANONYMOUS. An anonymous hello that carries a credential is refused. enum AuthKind { AUTH_KIND_ANONYMOUS = 0; AUTH_KIND_GAME_TICKET = 1; } message ClientHello { Role role = 1; AuthKind auth = 2; string credential = 3; // stx_gt1_β¦ game ticket; empty when anonymous string client_name = 4; // e.g. "SimTX Transmitter" string client_version = 5; repeated string features = 6; // client feature registry (see the top of this file) int32 protocol_revision = 7; // highest protocol revision this client implements } // 0 = UNSPECIFIED: clients treat it (and unknown values) as SERVER_UNAVAILABLE. enum HelloStatus { HELLO_STATUS_UNSPECIFIED = 0; HELLO_STATUS_ACCEPTED = 1; HELLO_STATUS_VERSION_UNSUPPORTED = 2; HELLO_STATUS_AUTHENTICATION_REQUIRED = 3; HELLO_STATUS_CREDENTIAL_INVALID = 4; HELLO_STATUS_ACCOUNT_RESTRICTED = 5; HELLO_STATUS_ROLE_FORBIDDEN = 6; HELLO_STATUS_SERVER_UNAVAILABLE = 7; HELLO_STATUS_IP_BANNED = 8; } message Account { bytes account_id = 1; string handle = 2; string display_name = 3; repeated string permissions = 4; int64 authorization_revision = 5; int64 lease_expires_at_ms = 6; } message ServerHello { HelloStatus status = 1; string message = 2; Account account = 3; // unset for anonymous sessions repeated string features = 4; // server feature registry (see the top of this file) string server_version = 5; int64 server_time_ms = 6; // Unix ms int64 simulation_time_ms = 7; // Unix ms of the simulated world clock int32 protocol_revision = 8; // highest protocol revision this server implements } message Ping { int64 nonce = 1; int64 client_time_ms = 2; } message Pong { int64 nonce = 1; int64 client_time_ms = 2; int64 server_time_ms = 3; } message ForwardedClientAddress { string address = 1; } // trusted proxies only, before ReceiverStart // The one error frame. request_id echoes the failed request (control and moderation requests, // TxUpdate, AntennaPointingUpdate, ReceiverRetune, Start/StopMonitor); 0 = the connection as a // whole (a protocol violation: the connection closes after it). Session refusals and ends use // SessionStatus REJECTED / ENDED instead. message Error { ErrorCode code = 1; string message = 2; int64 request_id = 3; } // Frame member numbers are allocated by block, in both ClientFrame and ServerFrame: // 1-9 connection (hello, ping, errors), 10-29 RX/TX sessions, 30-59 control, 60-79 moderation, // 80-99 reserved for the next area. Numbers 1-15 encode the tag in one byte. message ClientFrame { oneof msg { ClientHello hello = 1; Ping ping = 2; ForwardedClientAddress forwarded_address = 3; ReceiverStart receiver_start = 10; ReceiverRetune receiver_retune = 11; TransmitterStart transmitter_start = 12; SampleBlock samples = 13; TxUpdate tx_update = 14; TxModeInfo tx_mode_info = 15; AntennaPointingUpdate pointing = 16; ListStations list_stations = 30; StartMonitor start_monitor = 31; StopMonitor stop_monitor = 32; WeatherMapRequest weather_map = 33; ServerHealthRequest health = 34; CoverageMapRequest coverage_map = 35; CatalogListRequest catalog_list = 36; CatalogFetch catalog_fetch = 37; CatalogUpload catalog_upload = 38; CatalogDelete catalog_delete = 39; EphemerisRequest ephemeris = 40; PassPredictionRequest passes = 41; KickRequest kick = 60; IpModerationRequest ip_moderation = 61; BroadcastBanner broadcast_banner = 62; } } message ServerFrame { reserved 15, 16, 62; oneof msg { ServerHello hello_result = 1; Pong pong = 2; Error error = 3; Banner banner = 4; SessionStatus session_status = 10; IqStreamDescriptor iq_descriptor = 11; IqBlock iq = 12; TxQueue tx_queue = 13; TxAccept tx_accept = 14; TxTelemetry tx_telemetry = 17; TxUpdateAck tx_update_ack = 18; StationList station_list = 30; MonitorBlock monitor_block = 31; MonitorEnd monitor_end = 32; WeatherMapResponse weather_map = 33; ServerHealthReport health = 34; CoverageMapResponse coverage_map = 35; CatalogList catalog_list = 36; CatalogEntry catalog_entry = 37; CatalogUploadResult catalog_upload_result = 38; CatalogDeleteResult catalog_delete_result = 39; EphemerisResponse ephemeris = 40; PassPredictionResponse passes = 41; MonitorAck monitor_ack = 42; KickResult kick_result = 60; IpModerationResult ip_moderation_result = 61; StationImpactList station_impacts = 63; } } </syntaxhighlight> == session.proto == <syntaxhighlight lang="protobuf"> // SimTX wire protocol v25: receiver and transmitter sessions. syntax = "proto3"; package simtx.v25; import "simtx/v25/common.proto"; // 0 = INT16: the default sample format. enum SampleFormat { SAMPLE_FORMAT_INT16 = 0; SAMPLE_FORMAT_INT8 = 1; } // First message after a RECEIVER hello. message ReceiverStart { reserved 2; SessionSite site = 1; double center_frequency_hz = 3; float bandwidth_hz = 4; int32 sample_rate = 5; int32 chunk_samples = 6; optional float noise_figure_db = 7; // default: rig value, else 6 dB bool timestamped = 8; // IqBlocks carry first-sample timestamps string solo_transmitter_id = 9; // moderator monitor feature Tracking tracking = 10; SampleFormat sample_format = 11; AntennaAim aim = 12; // start pointing; absent = the antenna's stored heading } // Nonzero request_id: once the stream runs on the new frequency the server sends SessionStatus // UPDATED (UPDATE_REASON_RETUNE) echoing it. A retune superseded by a later one before it took // effect gets no acknowledgement of its own. message ReceiverRetune { double center_frequency_hz = 1; int64 request_id = 2; } message IqStreamDescriptor { int32 sample_rate = 1; // complex samples per second double center_frequency_hz = 2; int32 chunk_samples = 3; // complex samples per IqBlock SampleFormat sample_format = 4; bool timestamped = 5; } // samples: chunk_samples Γ (I, Q), big-endian int16 (or int8). x = q Γ scale / 32767 (or / 127). // Full scale (|x| = 1) is β12 dBm at the receiver input. // sequence numbers the blocks of one stream: 1 for the first block, +1 for each block after it, // across retunes. A step larger than 1 means the server dropped blocks for this stream; 0 = the // sender does not number blocks. message IqBlock { float scale = 1; bytes samples = 2; int64 first_sample_epoch_seconds = 3; // timestamped streams only int32 first_sample_nanos = 4; fixed64 sequence = 5; } // First message after a TX_AUDIO / TX_GENERATED hello. An ad-hoc site is only accepted for // principals holding tx.adhoc (service integrations) or when the server policy allows it. message TransmitterStart { reserved 2; SessionSite site = 1; string modulation = 3; // string enum (common.proto) double frequency_hz = 4; float power_watts = 5; string source_type = 6; // string enum (common.proto) int32 sample_rate = 7; float audio_gain = 8; float bandwidth_hz = 9; float audio_low_cut_hz = 10; float tone_hz = 11; float duty_cycle = 12; // Session knobs. With a rig, only final_drive_db, audio_compression_db, frequency_offset_hz and // am_modulation_index are taken from here; the rest come from the rig. TxImperfections imperfections = 13; Tracking tracking = 14; AntennaAim aim = 15; // start pointing; absent = the antenna's stored heading } // Transmit samples. Audio sources: mono float samples. GNU_RADIO_IQ: interleaved I, Q floats, or // compact big-endian int16 pairs (value = q Γ scale / 32767). message SampleBlock { message CompactIq { float scale = 1; bytes samples = 2; } oneof data { PackedFloats audio = 1; PackedFloats iq = 2; CompactIq compact_iq = 3; } } message PackedFloats { repeated float values = 1; } message TxModeInfo { string mode_name = 1; string description = 2; float bandwidth_hz = 3; } // 0 = UNSPECIFIED. A TxUpdateAck result whose field is UNSPECIFIED or unknown names a field this // client does not know; ignore it. enum TxLiveField { TX_LIVE_FIELD_UNSPECIFIED = 0; TX_LIVE_FIELD_FREQUENCY = 1; TX_LIVE_FIELD_POWER = 2; TX_LIVE_FIELD_BANDWIDTH = 3; TX_LIVE_FIELD_LOW_CUT = 4; TX_LIVE_FIELD_FINAL_DRIVE = 5; TX_LIVE_FIELD_PA_HEADROOM = 6; TX_LIVE_FIELD_AUDIO_COMPRESSION = 7; TX_LIVE_FIELD_AUDIO_GAIN = 8; } message TxUpdate { int64 request_id = 1; optional double frequency_hz = 2; optional float power_watts = 3; optional float bandwidth_hz = 4; optional float audio_low_cut_hz = 5; optional float final_drive_db = 6; optional float pa_headroom_db = 7; optional float audio_compression_db = 8; optional float audio_gain = 9; } message TxUpdateAck { message Result { TxLiveField field = 1; bool accepted = 2; double applied_value = 3; } int64 request_id = 1; repeated Result results = 2; } message TxQueue { int32 ahead = 1; int32 total = 2; } // On air: start streaming. Refusals and kicks arrive as SessionStatus REJECTED / ENDED. message TxAccept {} message TxTelemetry { int64 sequence = 1; int64 server_time_ms = 2; double actual_frequency_hz = 3; float requested_power_watts = 4; float forward_power_watts = 5; float delivered_power_watts = 6; float reflected_power_watts = 7; float vswr = 8; float mismatch_loss_db = 9; float transformer_loss_db = 10; float foldback_loss_db = 11; float alc_db = 12; float buffer_seconds = 13; float finals_temperature_c = 14; float thermal_foldback_db = 15; float pa_compression_db = 16; float pa_heat_watts = 17; float pa_efficiency_percent = 18; bool pa_thermal_fault_active = 19; } // Steer a station-bound session's antenna / Doppler tracking without reconnecting. message AntennaPointingUpdate { int64 request_id = 1; PointingMode mode = 2; float azimuth_deg = 3; float elevation_deg = 4; Tracking tracking = 5; } // The station a session is attached to, as the server resolved it. core.kind is GROUND or // SATELLITE; core.location is the site (ground) or the sub-satellite point when the status was sent. message BoundStation { StationCore core = 1; bytes antenna_id = 2; int64 antenna_revision = 3; bytes rig_id = 4; // empty = rig-less (the station has no rig) int64 rig_revision = 5; float pa_cap_watts = 6; // effective PA cap; 0 = uncapped AntennaConfig antenna = 7; // pointing = the antenna's default pointing } // Required: decoders reject UNSPECIFIED (and values they do not know). enum SessionState { SESSION_STATE_UNSPECIFIED = 0; SESSION_STATE_BOUND = 1; // request accepted; TxQueue/TxAccept or IqStreamDescriptor follow SESSION_STATE_REJECTED = 2; // the connection closes after this SESSION_STATE_ENDED = 3; // ended by the server (kick, station deleted, deorbit); connection closes SESSION_STATE_UPDATED = 4; // the session keeps running with a changed state; see update_reason } // Why a session was UPDATED. A status carries the most significant cause; the snapshot fields // carry everything that changed. 0 = UNSPECIFIED: not an UPDATED status. On an UPDATED status // UNSPECIFIED or a reason the client does not know means "apply the snapshot fields". enum UpdateReason { UPDATE_REASON_UNSPECIFIED = 0; UPDATE_REASON_POINTING = 1; // this session's AntennaPointingUpdate was applied (request_id) UPDATE_REASON_TRACKING = 2; // the server changed tracking (target gone or deorbited) UPDATE_REASON_STATION_EDIT = 3; // the station, its antenna or its rig was edited (revisions) UPDATE_REASON_ORBIT_CHANGE = 4; // the bound satellite's elements or attitude changed UPDATE_REASON_DEORBIT_SCHEDULED = 5; // the bound satellite will deorbit (station.core.deorbit) UPDATE_REASON_RETUNE = 6; // this session's ReceiverRetune took effect (request_id) } // Sent to every RX and TX session: once before the stream starts (BOUND), on every change the // session survives (UPDATED, protocol revision >= 1 for station changes) and when it stops // (REJECTED, ENDED). REJECTED and ENDED are the only refusal / kick signal, for RX and TX alike; // they carry `code` and `reason` and the connection closes after them. BOUND and UPDATED describe // the session as the server now runs it: // - station: always set on BOUND of a station-bound session. On UPDATED it is set whenever the // station changed (STATION_EDIT, ORBIT_CHANGE, DEORBIT_SCHEDULED) and replaces the client's copy // as a whole; unset on UPDATED means unchanged. Statuses arrive in order on one connection. // - frequency_hz, tracking, pointing: the current values; unset tracking / pointing = none. // Edits a session cannot follow (station deleted, its antenna or rig removed, access revoked) // still end it with ENDED. message SessionStatus { SessionState state = 1; string reason = 2; ErrorCode code = 3; bytes session_id = 4; // per-connection id (the transmission id for TX) BoundStation station = 5; // unset for ad-hoc sessions double frequency_hz = 6; Tracking tracking = 7; AntennaPointing pointing = 8; int64 request_id = 9; // echoes AntennaPointingUpdate / ReceiverRetune request_id UpdateReason update_reason = 10; // UPDATED only } </syntaxhighlight> == control.proto == <syntaxhighlight lang="protobuf"> // SimTX wire protocol v25: control channel (station lists, maps, catalog, satellites). // Every request carries a nonzero request_id that its response echoes. A request that fails gets // `Error {code, message, request_id}` (frames.proto) instead of its response; responses describe // success only. syntax = "proto3"; package simtx.v25; import "simtx/v25/common.proto"; message ListStations { int64 request_id = 1; bool include_sessions = 2; } // 0 = UNSPECIFIED: a session whose role the client does not know; show it without a role. enum SessionRole { SESSION_ROLE_UNSPECIFIED = 0; SESSION_ROLE_TX = 1; SESSION_ROLE_RX = 2; } message LiveSession { bytes session_id = 1; SessionRole role = 2; bytes antenna_id = 3; string callsign = 4; double frequency_hz = 5; string modulation = 6; // string enum (common.proto); empty for receivers float power_watts = 7; float bandwidth_hz = 8; string address = 9; // moderation sessions only GeoPoint location = 10; // where the session's antenna is right now } // A persistent station (with any live sessions), a satellite with nobody attached, or an ad-hoc // session (core.kind ADHOC, core.station_id empty, exactly one session). For satellites // core.location is the sub-satellite point at captured_at_ms. message Station { StationCore core = 1; repeated LiveSession sessions = 2; } message StationList { int64 request_id = 1; int64 captured_at_ms = 2; repeated Station stations = 3; } // Own-TX monitor. Success: MonitorAck (active = true), then MonitorBlocks. Failure: Error // (NOT_FOUND: no live transmitter with that id; FORBIDDEN: not yours). message StartMonitor { bytes session_id = 1; int64 request_id = 2; } // Answered by MonitorAck (active = false), also when that monitor was not running. message StopMonitor { bytes session_id = 1; int64 request_id = 2; } message MonitorAck { int64 request_id = 1; bytes session_id = 2; bool active = 3; // true: the monitor runs; false: it is stopped } message MonitorBlock { bytes session_id = 1; int32 sample_rate = 2; repeated float iq = 3; // interleaved I, Q } // The server ended a running monitor (the transmitter went off the air). message MonitorEnd { bytes session_id = 1; string reason = 2; } message WeatherMapRequest { int64 request_id = 1; double center_latitude_deg = 2; double center_longitude_deg = 3; double latitude_span_deg = 4; double longitude_span_deg = 5; int32 width = 6; int32 height = 7; string type = 8; // weather type (string enum, common.proto) } message WeatherMapResponse { int64 request_id = 1; int64 valid_time_ms = 2; string type = 3; // weather type (string enum, common.proto) double south_latitude_deg = 4; double north_latitude_deg = 5; double west_longitude_deg = 6; double east_longitude_deg = 7; int32 width = 8; int32 height = 9; repeated float values = 10; float surface_temperature_celsius = 11; float surface_pressure_hpa = 12; float surface_relative_humidity_percent = 13; float surface_wind_meters_per_second = 14; float maximum_rain_rate_millimeters_per_hour = 15; float maximum_cloud_water_grams_per_cubic_meter = 16; float cape_joules_per_kilogram = 17; float lightning_flashes_per_minute = 18; string duct_type = 19; // duct type (string enum, common.proto) float duct_base_meters = 20; float duct_top_meters = 21; float duct_strength_m_units = 22; float duct_gradient_m_units_per_kilometer = 23; } message ServerHealthRequest { int64 request_id = 1; } message TransmitterHealth { bytes session_id = 1; string callsign = 2; string address = 3; string source_type = 4; // string enum (common.proto) double frequency_hz = 5; string modulation = 6; // string enum (common.proto) int32 sample_rate = 7; int32 buffer_capacity_samples = 8; int32 buffer_retained_samples = 9; int64 buffer_sequence = 10; int64 wire_components_received = 11; int64 processed_blocks = 12; int64 total_processing_nanos = 13; int64 connected_duration_nanos = 14; int64 last_progress_age_nanos = 15; int64 recent_processing_nanos = 16; int64 max_processing_nanos = 17; int64 current_write_stall_nanos = 18; int64 recent_write_nanos = 19; int64 max_write_nanos = 20; int64 total_write_nanos = 21; } message ReceiverHealth { bytes session_id = 1; string callsign = 2; string address = 3; double center_frequency_hz = 4; int32 sample_rate = 5; int32 chunk_samples = 6; bool wideband = 7; int32 shared_group_members = 8; int32 queued_frames = 9; int32 max_queued_frames = 10; int32 queue_capacity = 11; int64 frames_written = 12; int64 frames_dropped = 13; int64 group_processed_chunks = 14; int64 total_group_processing_nanos = 15; int64 total_group_dsp_nanos = 16; int64 connected_duration_nanos = 17; int64 group_progress_age_nanos = 18; int64 recent_group_processing_nanos = 19; int64 max_group_processing_nanos = 20; int64 recent_group_dsp_nanos = 21; int64 max_group_dsp_nanos = 22; int64 current_write_stall_nanos = 23; int64 recent_write_nanos = 24; int64 max_write_nanos = 25; int64 total_write_nanos = 26; } message ServerHealthReport { int64 request_id = 1; int64 captured_at_ms = 2; string status = 3; int64 uptime_ms = 4; int32 available_processors = 5; float process_cpu_load = 6; float system_cpu_load = 7; double system_load_average = 8; int64 process_cpu_time_nanos = 9; int64 heap_used_bytes = 10; int64 heap_committed_bytes = 11; int64 heap_max_bytes = 12; int64 non_heap_used_bytes = 13; int64 physical_memory_used_bytes = 14; int64 physical_memory_total_bytes = 15; int64 open_file_descriptors = 16; int64 max_file_descriptors = 17; int32 live_threads = 18; int32 peak_threads = 19; int32 connected_clients = 20; int32 connected_stations = 21; int32 active_transmitters = 22; int32 receiver_groups = 23; bool acceptor_alive = 24; int64 world_tick = 25; int64 world_tick_duration_nanos = 26; int64 world_tick_interval_nanos = 27; int64 simulation_time_ms = 28; int32 session_tasks_active = 29; int32 session_tasks_queued = 30; int32 receiver_dsp_active = 31; int32 receiver_dsp_queued = 32; int32 propagation_jobs_active = 33; int32 propagation_jobs_queued = 34; int64 streamed_receiver_chunks = 35; int64 receiver_overrun_chunks = 36; int32 stalled_transmitter_writes = 37; int32 stalled_receiver_groups = 38; repeated TransmitterHealth transmitters = 39; repeated ReceiverHealth receivers = 40; } // Failures: Error (UNAVAILABLE: disabled or busy; CANCELLED: superseded by a newer coverage // request on this connection). message CoverageMapRequest { reserved 3; int64 request_id = 1; SessionSite site = 2; // ad-hoc: the callsign is ignored string modulation = 4; // string enum (common.proto) double frequency_hz = 5; float power_watts = 6; float bandwidth_hz = 7; int32 width = 8; int32 height = 9; } message CoverageMapResponse { reserved 2, 3; int64 request_id = 1; int64 sim_time_ms = 4; int32 width = 5; int32 height = 6; repeated float received_power_dbm = 7; } message CatalogFile { string path = 1; string content = 2; } message CatalogDependency { string id = 1; string version_range = 2; } message CatalogEntrySummary { int32 id = 1; string kind = 2; string name = 3; string description = 4; string author = 5; int32 size_bytes = 6; int32 file_count = 7; repeated string types = 8; // catalog types (string enum, common.proto) repeated string categories = 9; string package_id = 10; string package_version = 11; repeated CatalogDependency dependencies = 12; int32 revision = 13; int64 uploaded_at_ms = 14; } message CatalogListRequest { int64 request_id = 1; } message CatalogList { int64 request_id = 1; repeated CatalogEntrySummary entries = 2; } // Unknown entry: Error NOT_FOUND. message CatalogFetch { int64 request_id = 1; int32 entry_id = 2; } message CatalogEntry { reserved 3; int64 request_id = 1; int32 entry_id = 2; string kind = 4; string name = 5; int32 revision = 6; repeated CatalogFile files = 7; } message CatalogUpload { int64 request_id = 1; string kind = 2; string name = 3; string description = 4; repeated string categories = 5; repeated CatalogFile files = 6; } // Refused uploads and deletes: Error (FORBIDDEN, INVALID_REQUEST, NOT_FOUND, LIMIT_REACHED, // UNAVAILABLE, INTERNAL) with the reason in message. message CatalogUploadResult { reserved 2, 3; int64 request_id = 1; int32 entry_id = 4; bool updated = 5; // true: replaced your existing publication of that name } message CatalogDelete { int64 request_id = 1; int32 entry_id = 2; } message CatalogDeleteResult { reserved 2, 3; int64 request_id = 1; int32 entry_id = 4; } // 0 = INFO: the safe default; an unknown level is shown as INFO. enum BannerLevel { BANNER_LEVEL_INFO = 0; BANNER_LEVEL_BAN_NOTICE = 1; } message Banner { BannerLevel level = 1; string text = 2; int64 expires_at_ms = 3; // 0 = no expiry } // Failures (unknown or invisible satellite, bad window, busy): Error with this request_id. message EphemerisRequest { int64 request_id = 1; bytes station_id = 2; int64 from_ms = 3; int64 to_ms = 4; int32 step_seconds = 5; // >= 1; at most 3600 samples } message EphemerisSample { int64 t_ms = 1; double latitude_deg = 2; double longitude_deg = 3; double altitude_meters = 4; double x = 5; // ECEF metres (6371 km sphere) double y = 6; double z = 7; } message EphemerisResponse { reserved 4; int64 request_id = 1; bytes station_id = 2; repeated EphemerisSample samples = 3; } // Failures: Error with this request_id, as for EphemerisRequest. message PassPredictionRequest { int64 request_id = 1; bytes satellite_id = 2; oneof observer_site { bytes observer_station_id = 3; GeoPoint observer = 4; } int64 from_ms = 5; int64 to_ms = 6; // at most 7 days after from_ms double min_elevation_deg = 7; double frequency_hz = 8; // for max Doppler; 0 = skip } message Pass { int64 aos_ms = 1; int64 tca_ms = 2; int64 los_ms = 3; double max_elevation_deg = 4; double aos_azimuth_deg = 5; double los_azimuth_deg = 6; double max_doppler_hz = 7; } message PassPredictionResponse { reserved 4; int64 request_id = 1; bytes satellite_id = 2; repeated Pass passes = 3; } // Moderation (role MODERATION). Failures: Error with the request's request_id. message KickRequest { int64 request_id = 1; string target = 2; string reason = 3; bool by_callsign = 4; } message KickResult { int64 request_id = 1; int32 sessions_kicked = 2; } // Required: decoders reject UNSPECIFIED, so an omitted action never bans. enum IpModerationAction { IP_MODERATION_ACTION_UNSPECIFIED = 0; IP_MODERATION_ACTION_UNBAN = 1; IP_MODERATION_ACTION_BAN = 2; } message IpModerationRequest { int64 request_id = 1; IpModerationAction action = 2; string target = 3; string reason = 4; int64 duration_seconds = 5; } message IpModerationResult { int64 request_id = 1; bool banned = 2; repeated string addresses = 3; int32 sessions_disconnected = 4; int64 expires_at_ms = 5; } message BroadcastBanner { int64 request_id = 1; string text = 2; int64 duration_seconds = 3; } message TransmitterImpact { bytes session_id = 1; string callsign = 2; string address = 3; double frequency_hz = 4; string modulation = 5; // string enum (common.proto) float power_watts = 6; float occupied_bandwidth_hz = 7; float impact_score = 8; int32 affected_receivers = 9; float nearest_receiver_km = 10; string reasons = 11; } message StationImpactList { int64 request_id = 1; repeated TransmitterImpact impacts = 2; } </syntaxhighlight> == common.proto == <syntaxhighlight lang="protobuf"> // SimTX wire protocol v25: shared types. // // Conventions // - UUIDs are 16-byte big-endian `bytes` (most significant half first); empty = none. // - Frequencies are double Hz. `*_ms` fields are Unix milliseconds. // // Enum zero values. An omitted enum field reads as 0, so 0 never grants or asserts anything: // - Most enums start with `*_UNSPECIFIED = 0`. Where the field is required, decoders reject // UNSPECIFIED (IpModerationAction, SessionState, Role, and StationKind wherever a persistent // station is meant); elsewhere each enum says how 0 is read. // - A few have a real value at 0, the default, documented on each: // AuthKind ANONYMOUS, AntennaMount FIXED, PointingMode FIXED, SpinAxis ORBIT_NORMAL, // StationKind ADHOC, StationVisibility UNLISTED, SampleFormat INT16, BannerLevel INFO. // // String-valued enums travel as upper-case ASCII names, compared exactly. A peer that does not // know a name refuses it (Error INVALID_REQUEST naming the value). This list is the reference; // other comments point here. // modulation AM_DSB_FC AM_DSB_SC USB LSB CW NFM WFM IQ WSPR FT8 // (TransmitterStart, CoverageMapRequest, LiveSession, TransmitterHealth, // TransmitterImpact; empty in LiveSession for receivers) // source type AUDIO_STREAM MICROPHONE WHITE_NOISE PIPEWIRE GNU_RADIO_IQ // (TransmitterStart.source_type, TransmitterHealth.source_type) // antenna type DIPOLE VERTICAL YAGI_3EL YAGI_5EL LOOP BEVERAGE LONGWIRE INVERTED_V // EFHW_32M_49_1 MONOPOLE TURNSTILE CROSSED_YAGI HELIX (AntennaConfig.type) // weather type DUCT_STRENGTH RAIN_RATE CLOUD_WATER SURFACE_TEMPERATURE LIGHTNING_RATE // (WeatherMapRequest.type, WeatherMapResponse.type) // duct type NONE SURFACE ELEVATED (WeatherMapResponse.duct_type) // catalog type MODULATION AUDIO_ADDON THEME ACTION LIBRARY PRESET (CatalogEntrySummary.types) syntax = "proto3"; package simtx.v25; message GeoPoint { double latitude_deg = 1; double longitude_deg = 2; double altitude_meters = 3; } // 0 = FIXED: the safe default, an antenna without a rotator. enum AntennaMount { ANTENNA_MOUNT_FIXED = 0; ANTENNA_MOUNT_AZ = 1; ANTENNA_MOUNT_AZEL = 2; } // 0 = FIXED: hold the given azimuth / elevation; nothing is tracked unless asked for. enum PointingMode { POINTING_MODE_FIXED = 0; POINTING_MODE_TRACK = 1; } message AntennaPointing { AntennaMount mount = 1; PointingMode mode = 2; float azimuth_deg = 3; float elevation_deg = 4; bytes target_station_id = 5; float azimuth_rate_deg_per_sec = 6; float elevation_rate_deg_per_sec = 7; } message AntennaConfig { string type = 1; // antenna type (string enum, see the top of this file) float height_meters = 2; float azimuth_deg = 3; float feedline_loss_db = 4; float wire_length_meters = 5; // NEC-2 card deck (UTF-8, <= 262144 bytes). Empty = built-in template for `type`. string nec_deck = 6; AntennaPointing pointing = 7; } message OrbitalElements { string model = 1; // "simtx-j2-v1" int64 epoch_seconds = 2; // Unix seconds of the element epoch int32 epoch_nanos = 3; double semi_major_axis_km = 4; double eccentricity = 5; double inclination_deg = 6; double raan_deg = 7; double argument_of_perigee_deg = 8; double mean_anomaly_deg = 9; } // 0 = ORBIT_NORMAL: the usual spin-stabilised axis and the account service's default. enum SpinAxis { SPIN_AXIS_ORBIT_NORMAL = 0; SPIN_AXIS_NADIR = 1; SPIN_AXIS_VELOCITY = 2; } // No mode set = nadir pointing with yaw 0. message Attitude { message Nadir { double yaw_deg = 1; } message Inertial { double right_ascension_deg = 1; double declination_deg = 2; double roll_deg = 3; } message Spin { SpinAxis axis = 1; double rate_rpm = 2; double phase_deg = 3; } oneof mode { Nadir nadir = 1; Inertial inertial = 2; Spin spin = 3; } } // 0 = ADHOC: no persistent station. Only a StationList entry may be ADHOC; decoders reject it // wherever a persistent station is meant (BoundStation). enum StationKind { STATION_KIND_ADHOC = 0; STATION_KIND_GROUND = 1; STATION_KIND_SATELLITE = 2; } // 0 = UNLISTED: the private default; a station is public only when it says so. enum StationVisibility { STATION_VISIBILITY_UNLISTED = 0; STATION_VISIBILITY_PUBLIC = 1; } // The fields every view of a persistent station shares. Embedded as `core` by Station (control // list) and BoundStation (session); each adds only its own fields. message StationCore { bytes station_id = 1; // empty only for ad-hoc StationList entries int64 revision = 2; // bumped on every edit of the station StationKind kind = 3; string name = 4; string callsign = 5; string owner_handle = 6; StationVisibility visibility = 7; GeoPoint location = 8; // ground: the site; satellite: the sub-satellite point (see the embedding message) OrbitalElements orbit = 9; // satellites Attitude attitude = 10; // satellites Deorbit deorbit = 11; // satellites with a deorbit burn scheduled or done } // Which persistent station (and which of its antennas / rigs) a session uses. message StationBinding { bytes station_id = 1; bytes antenna_id = 2; // empty = the station's first antenna bytes rig_id = 3; // empty = the station's first rig (by ordinal); rig-less only when it has no rig } // Inline station for anonymous / ad-hoc sessions (no account-owned station). message AdhocStation { string callsign = 1; GeoPoint location = 2; AntennaConfig antenna = 3; } // Where a session (or a coverage prediction) sits. Exactly one member; neither is refused. message SessionSite { oneof site { StationBinding station = 1; // one of your persistent stations; needs a game ticket AdhocStation adhoc = 2; // inline site; anonymous OK (TX: see the ad-hoc policy) } } message Tracking { bytes target_station_id = 1; bool point_antenna = 2; // drive the antenna's rotator toward the target bool doppler_correct = 3; // RX: shift tuning by the target's Doppler; TX: pre-correct so it arrives on frequency // Simulation time at which the rotator began slewing toward this target (point_antenna only; both 0 = // not known). Servers set it in SessionStatus; with the antenna's slew rate it gives the rotator position. // Ignored in a client's start or AntennaPointingUpdate. int64 since_epoch_seconds = 4; int32 since_nanos = 5; } // Where a session's antenna points when it starts (ReceiverStart / TransmitterStart `aim`), e.g. to // reconnect already pointed at a manual heading. // - Absent: the antenna's stored heading. // - FIXED mount: ignored; the BOUND status's `pointing` reports the effective pointing. // - AZ mount: elevation_deg is ignored. // - With tracking.point_antenna: the rotator's position when the session starts; it slews from there // toward the target. // Checked like AntennaPointingUpdate: finite numbers, elevation -90..90, azimuth normalised to [0, 360). message AntennaAim { float azimuth_deg = 1; float elevation_deg = 2; } message Deorbit { int64 burn_at_ms = 1; int64 reentry_at_ms = 2; GeoPoint reentry_point = 3; double delta_v_mps = 4; } message StationLimits { int32 max_sessions = 1; int32 max_transmitters = 2; int32 max_receivers = 3; } // The 15 transmitter imperfection knobs. Unset = default (rig value, or the generic HF preset). message TxImperfections { optional float final_drive_db = 1; optional float pa_headroom_db = 2; optional float audio_compression_db = 3; optional float pa_softness = 4; optional float pa_am_pm_deg = 5; optional float carrier_leak_db = 6; optional float opposite_sideband_suppression_db = 7; optional float frequency_offset_hz = 8; optional float mains_hum_db = 9; optional float mains_hum_hz = 10; optional float am_modulation_index = 11; optional float pa_supply_sag_db = 12; optional float tx_noise_floor_db = 13; optional float oscillator_phase_noise_db = 14; optional float cw_key_rise_ms = 15; } message RigProfile { bytes rig_id = 1; int64 revision = 2; string name = 3; float pa_max_watts = 4; float noise_figure_db = 5; TxImperfections tx = 6; } // The one error model: every failure is `Error {code, message, request_id}` (frames.proto), a // SessionStatus REJECTED / ENDED with `code` and `reason`, or a non-ACCEPTED ServerHello. 0 = // UNSPECIFIED: no specific code (an ENDED session that simply ended); clients treat unknown codes // the same way. enum ErrorCode { ERROR_CODE_UNSPECIFIED = 0; ERROR_CODE_INVALID_REQUEST = 1; ERROR_CODE_UNAUTHENTICATED = 2; ERROR_CODE_FORBIDDEN = 3; ERROR_CODE_NOT_FOUND = 4; ERROR_CODE_LIMIT_REACHED = 5; ERROR_CODE_RATE_LIMITED = 6; ERROR_CODE_UNAVAILABLE = 7; ERROR_CODE_STATION_INACTIVE = 8; ERROR_CODE_STATION_REQUIRED = 9; ERROR_CODE_INTERNAL = 10; ERROR_CODE_CANCELLED = 11; // superseded by a newer request of the same kind (coverage maps) } </syntaxhighlight> {{SimTX API navbox}} [[Category:SimTX API]]
Summary:
Please note that all contributions to SimTX Wiki may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
SimTX Wiki:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Templates used on this page:
Template:SimTX API navbox
(
edit
)
Template:SimTX API navigation
(
edit
)