================================================================================ SeRo Systems GRX Receiver API - LLM Context Document ================================================================================ This document provides all context needed to generate client code for the SeRo Systems GRX receiver API. It contains the full Protocol Buffer service definitions, connection details, language-specific setup instructions, working code examples, and important domain-specific notes. Paste this entire document into a conversation with an LLM (such as Claude or ChatGPT) and then ask it to generate client code for your use case. When generating code, always include: - The language and project layout being used. - The exact working directory for each command. - Where the .proto files should be placed. - The command that generates gRPC/protobuf bindings. - The command that runs the generated program. At the end of every generated answer, tell the user exactly what to do next. This final checklist should include: - Which files to create or edit. - Which directory to run each command from. - The dependency installation command. - The proto binding generation command. - The run command with YOUR_GRX_IP replaced by the receiver address. - What successful output should roughly look like. For Python, generated *_pb2.py and *_pb2_grpc.py files must be importable by the script. The simplest layout is to generate them into the same directory as the script and run the script from that directory. For Java/Maven, place the .proto files in src/main/proto/. For C++/CMake, place the .proto files in the directory referenced by CMakeLists.txt, typically proto/ or protos/. ================================================================================ 1. ARCHITECTURE OVERVIEW ================================================================================ The GRX receiver exposes its functionality through a gRPC API defined with Protocol Buffers (proto3). The API is split into 5 services, each listening on a separate TCP port: Service Port Purpose ------------------ ----- --------------------------------------------------- Receiverd 5303 Signal data (Mode S, UAT, Mode 1/2/3/A/C frames), aircraft state vectors/tracking, radio front-end hardware status, signal statistics Monitord 5305 System health monitoring: CPU, memory, swap, disk, network, GNSS timing/position, systemd units, logs Spectrumd 5306 FFT spectrum analysis, waterfall JPEG images, channel power measurements Samplestreamingd 5308 Continuous raw I/Q sample streaming (SDR-like) TunableChanneld 5309 SDR channel configuration: center frequency, sample rate, bandwidth, gain, RX port selection Connection model: - All connections use INSECURE channels (no TLS). - Each service runs on its own port; connect separately to each. - The receiver IP is typically on a local network (e.g. 10.x.x.x). Obtaining the proto files: The .proto files are available for download from the GRX's built-in documentation at http:///doc/ (API Reference section). Proto file dependencies: - Common.proto defines shared types (Band enum, RadioIdentification message). - Receiverd.proto, Spectrumd.proto, Samplestreamingd.proto, and TunableChanneld.proto all import Common.proto. - Monitord.proto is self-contained (only imports google/protobuf/empty.proto). - Receiverd.proto also imports google/protobuf/timestamp.proto. ================================================================================ 2. PROTOCOL BUFFER DEFINITIONS ================================================================================ Below are the COMPLETE contents of all 6 public proto files. These are the authoritative API definitions from which client stubs are generated. -------------------------------------------------------------------------------- 2.1 Common.proto -------------------------------------------------------------------------------- syntax = "proto3"; package serosystems.proto.v3.grx; option java_package = "de.serosystems.proto.v3.grx"; option java_outer_classname = "CommonProto"; /* * Band/Frequency a front end operates on. * Note that this is currently also used by other protofiles that need to communicate bands. */ enum Band { /* Unspecified band, used when uninitialized or no other enum member matches */ BAND_UNSPECIFIED = 0; /* 978 MHz band. */ BAND_978_MHZ = 1; /* 1030 MHz band. */ BAND_1030_MHZ = 2; /* 1090 MHz band. */ BAND_1090_MHZ = 3; /* Special "band" to identify a channel that is fixed to a band/frequency that is not included here as enum member */ BAND_FIXED = 127; /* Special "band" to identify a channel that can be tuned to different bands/frequencies */ BAND_TUNABLE = 128; /* Special enum member that does not describe a band, but can be used to indicated a "match all" when matching bands */ BAND_MATCH_ANY = 254; } /* * Message used to clearly identify a radio. */ message RadioIdentification { /* The band this radio operates on */ Band band = 1; /* The per-band-index, useful if there is more than a single radio per band */ int32 per_band_index = 2; } -------------------------------------------------------------------------------- 2.2 Receiverd.proto -------------------------------------------------------------------------------- syntax = "proto3"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; package serosystems.proto.v3.grx.receiverd; option java_package = "de.serosystems.proto.v3.grx.receiverd"; option java_outer_classname = "ReceiverDProto"; // Go package is relative to the web-ui package // This will result in "golang.sero-systems.de/grx/web-ui/proto/.../v3 option go_package = "proto/receiverd/v3"; /* 2D Position. */ message Position2D { /* Latitude [degrees], WGS-84. */ double latitude = 1; /* Longitude [degrees], WGS-84. */ double longitude = 2; } /* List of state vectors. */ message StateVectorList { /* * State vector. * Note: all fields having a timestamp (i.e. a ..._last_updated) field should * be checked for their validity: if the timestamp is 0, the value of the * field is not valid. */ message StateVector { /* * Qualified address: an address with a qualifier (e.g. ICAO24). */ message QualifiedAddress { /* Address qualifiers. */ enum AddressQualifier { /* * ICAO24, directly or rebroadcast, all map to the same qualified * address. * Consult the data_sources for a list of actual sources. */ ICAO24 = 0; /* * The following are all non-ICAO24 addresses received via UAT. * They all map to qualified addresses that describe them further. */ /* UAT ADS-B with temporary address. */ UAT_ADS_B_WITH_TEMPORARY = 1; /* UAT TIS-B target with track file identifier. */ UAT_TIS_B_TARGET_WITH_TRACK_FILE_IDENTIFIER = 2; /* UAT surface vehicle. */ UAT_SURFACE_VEHICLE = 3; /* UAT fixed ADS-B beacon. */ UAT_FIXED_ADS_B_BEACON = 4; /* UAT ADS-R target with non-icao address. */ UAT_ADS_R_TARGET_WITH_NON_ICAO = 5; /* UAT reserved. */ UAT_RESERVED = 6; /* * The following are all non-ICAO24 addresses received via Mode-S. * They map to distinct qualified addresses; including their sources. */ /* Mode-S / ADS-B anonymous or ground vehicle or fixed obstacle. */ MODE_S_ADS_B_ANONYMOUS_OR_GROUND_VEHICLE_OR_FIXED_OBSTACLE = 7; /* Mode-S / ADS-R anonymous or ground vehicle or fixed obstacle. */ MODE_S_ADS_R_ANONYMOUS_OR_GROUND_VEHICLE_OR_FIXED_OBSTACLE = 8; /* Mode-S / TIS-B Mode-A and track file identifier. */ MODE_S_TIS_B_MODE_AC_CODE_AND_TRACK_FILE_IDENTIFIER = 9; /* Mode-S / TIS-B non-icao address. */ MODE_S_TIS_B_NON_ICAO24 = 10; /* * Mode S message with reserved content or in general: * when the address cannot be determined from the message. */ MODE_S_INVALID = 11; } /* Address qualifier. */ AddressQualifier aq = 1; /* Address part of the qualified address, 24 bits. */ uint32 address = 2; } /* Qualified address. */ QualifiedAddress qualified_address = 1; /* * Data source. */ enum DataSource { /* * Mode S Downlink message, including DF 17 (ADS-B), excluding certain * DF 18 message that do not carry data originating from the transmitter * themselves. */ MODE_S_DL = 0; /* * Mode S Uplink message. Set when * 1. ACAS broadcast or resolutionmessage (UF 16) is received * 2. target is selectively interrogated by another transmitter */ MODE_S_UL = 1; /* * Mode S Downlink message having DF 18, with CF 2, 3, 5 or 6 (see * DO-260B, Table 2-13). * Those are all messages where the transmitter itself does not carry the * address, but re-broadcasts the message in some way. * Note: there is no distinction between TIS-B and ADS-R. * If the address is not an ICAO 24-bit address, the address qualifier * may contain further information to distinguish between TIS-B and * ADS-R. */ MODE_S_DL_ADS_R_TIS_B = 2; /* * UAT ADS-B message with address qualifier 0, 1, 4 or 5. * Those contain data originating from the transmitter themselves. */ UAT_ADS_B = 3; /* * UAT ADS-B message with address qualifier 2, 3 or 6. * Note: there is no distinction between TIS-B and ADS-R. * If the address is not an ICAO 24-bit address, the address qualifier * may contain further information to distinguish between TIS-B and * ADS-R. */ UAT_ADS_R_TIS_B = 4; } /* * List of data sources from which the state vector has been composed. */ repeated DataSource data_sources = 3; /* Average rate of directly received (i.e. non-rebroadcasted) messages * [1/s]. */ float average_message_rate_directly_received = 4; /* * Average signal level of directly received (i.e. non-rebroadcasted) * messages [dBm]. */ float average_signal_level_directly_received = 5; /* Time [s since epoch UTC] of last received arbitrary message. */ uint64 last_seen = 6; /* * Decoded identification of the target, aka. call sign. Empty if not valid. */ string identification = 7; /* * Time [s since epoch UTC] of last updating the identification. * Is 0 if too old/not received yet. */ uint64 identification_last_update = 8; /* Whether target is on ground. */ bool ground_flag = 9; /* * Time [s since epoch UTC] of last updating the ground flag. * Is 0 if too old/not received yet. */ uint64 ground_flag_last_update = 10; /* * Position of the target. * Only available if position is recent. */ Position2D position = 11; /* * Time [s since epoch UTC] of last updating the position. * Is 0 if too old/not received yet. */ uint64 position_last_update = 12; /* Track angle [degrees] of the target. */ uint32 track_angle = 13; /* * Time [s since epoch UTC] of last updating the track angle. * Is 0 if too old/not received yet. */ uint64 track_angle_last_update = 14; /* Speed over ground [knots] of the target. */ uint32 speed_over_ground = 15; /* * Time [s since epoch UTC] of last updating the speed over ground. * Is 0 if too old/not received yet. */ uint64 speed_over_ground_last_update = 16; /* Barometric altitude [feet] of the target. */ int32 barometric_altitude = 17; /* * Time [s since epoch UTC] of last updating the barometric altitude. * Is 0 if too old/not received yet. */ uint64 barometric_altitude_last_update = 18; /* Geometric height [feet] of the target. */ int32 geometric_height = 19; /* * Time [s since epoch UTC] of last updating the geometric height. * Is 0 if too old/not received yet. */ uint64 geometric_height_last_update = 20; /* Vertical rate [feet/min] of the target. */ int32 vertical_rate = 21; /* * Time [s since epoch UTC] of last updating the vertical rate. * Is 0 if too old/not received yet. */ uint64 vertical_rate_last_update = 22; /* * Country, as determined by the ICAO address. * Is either the full country name (e.g. "Germany") or "unknown". */ string country = 23; /* The "4096 identification code", aka. squawk. */ uint32 identification_code = 24; /* * Time [s since epoch UTC] of last updating the identification code. * Is 0 if too old/not received yet. */ uint64 identification_code_last_update = 25; } /* * Current timestamp [s since epoch UTC], can be used to check if fields * are up-to-date. */ uint64 current_timestamp = 1; /* List of state vectors. */ repeated StateVector state_vectors = 2; } /* History (time series) of StateVectors for a single target. */ message StateVectorHistory { /* * Single data point in the time-series. * Overlaps with StateVectorList.StateVector message, but is shorter. * Note: all fields having a valid flag (i.e. a ..._valid field) should * be checked for their validity: if the valid flag is false, the value of * the field should not be interpreted. */ message DataPoint { /* Time [s since epoch UTC] of this data point. */ uint64 timestamp = 1; /* Average rate of directly received (i.e. non-rebroadcasted) messages * [1/s]. */ float average_message_rate_directly_received = 2; /* * Average signal level of directly received (i.e. non-rebroadcasted) * messages [dBm]. */ float average_signal_level_directly_received = 3; /* Whether target is on ground. */ bool on_ground = 4; /* * Whether the on_ground flag is considered valid. */ bool on_ground_valid = 5; /* * Decoded identification, aka. call sign. Empty if not valid (i.e. if * identification_valid is false). */ string identification = 6; /* * Whether the identification is considered valid. */ bool identification_valid = 7; /* * Track angle [degrees] of the target. */ uint32 track_angle = 8; /* * Whether the track angle is considered valid. */ bool track_angle_valid = 9; /* Speed over ground [knots] of the target. */ uint32 speed_over_ground = 10; /* Whether speed over ground is considered valid. */ bool speed_over_ground_valid = 11; /* Barometric altitude [feet] of the target. */ int32 barometric_altitude = 12; /* Whether barometric altitude is considered valid. */ bool barometric_altitude_valid = 13; /* Geometric height [feet] of the target. */ int32 geometric_height = 14; /* Whether geometric height is considered valid. */ bool geometric_height_valid = 15; /* * Position of the target. * Only available if position is considered valid. */ Position2D position = 16; /* Vertical rate [feet/min] of the target. */ int32 vertical_rate = 17; /* Whether vertical rate is considered valid. */ bool vertical_rate_valid = 18; /* The "4096 identification code", a.k.a. squawk. */ uint32 identification_code = 19; /* Whether identification_code is considered valid. */ bool identification_code_valid = 20; } /* * List of data points describing different points of time. * Empty if there are no such data points, e.g. if target is not tracked. */ repeated DataPoint data_point = 1; /* * Current time [s since epoch UTC]. Can be used to represent the * DataPoints' timestamps relative to the current time. */ uint64 current_timestamp = 2; } /* Range chart, computed by directly received (non-rebroadcasted) position * messages. */ message RangeChart { /* * Center of the chart, i.e. sensor position. * Note: not included if position is not available. */ Position2D center = 1; /* * Reception range [m] from the center for each degree. * Exactly 360 items. */ repeated float ranges = 2; } /* Reception capabilities */ message ReceptionCapabilities { /* Capabilities. */ enum Capability { /* Mode-S frame on downlink. */ DOWNLINK_MODE_S_FRAME = 0; /* Mode-AC reply on downlink. */ DOWNLINK_MODE_AC_REPLY = 1; /* Mode-4 reply on downlink. */ DOWNLINK_MODE_4_REPLY = 2; /* Mode-5 reply on downlink. */ DOWNLINK_MODE_5_REPLY = 3; /* DME/TACAN pulse pair on downlink. */ DOWNLINK_DME_TACAN_PULSE_PAIR = 4; /* Isolated pulse on downlink. */ DOWNLINK_ISOLATED_PULSE = 5; /* Mode-S frame on uplink. */ UPLINK_MODE_S_FRAME = 8; /* Mode-1, Mode-2, Mode-A, Mode-C interrogation on uplink. */ UPLINK_BASIC_INTERROGATION = 9; /* Mode-4 interrogation on uplink. */ UPLINK_MODE_4_INTERROGATION = 10; /* Mode-5 interrogation on uplink. */ UPLINK_MODE_5_INTERROGATION = 11; /* DME/TACAN pulse pair on uplink. */ UPLINK_DME_TACAN_PULSE_PAIR = 12; /* Isolated pulse on uplink. */ UPLINK_ISOLATED_PULSE = 13; /* UAT ADS-B message. */ UAT_ADS_B_MESSAGE = 16; /* UAT ground uplink message. */ UAT_GROUND_UPLINK_MESSAGE = 17; /* DME/TACAN pulse pair on UAT. */ UAT_DME_TACAN_PULSE_PAIR = 18; /* Isolated pulse on UAT. */ UAT_ISOLATED_PULSE = 19; /* DME/TACAN pulse pair on a generic band. */ GENERIC_DME_TACAN_PULSE_PAIR = 24; /* Isolated pulse on a generic band. */ GENERIC_ISOLATED_PULSE = 25; } /* Capabilities. */ repeated Capability capabilities = 1; } /* Received signal statistics. */ message Statistics { /* Absolute message counter. */ message Counters { /* Mode S downlink, split by DF (0-24). */ repeated uint64 dl_mode_s_by_df = 1; /* Mode 1/2/A/C downlink. */ uint64 dl_mode_1_2_a_c_reply = 2; /* Mode 4 downlink. */ uint64 dl_mode_4_reply = 3; /* Mode 5 downlink. */ uint64 dl_mode_5_reply = 4; /* Isolated pulses on 1090MHz. */ uint64 dl_isolated_pulse = 5; /* DME/TACAN pulse pair on 1090MHz. */ uint64 dl_dme_tacan_pulse_pair = 6; /* Mode S uplink, split by UF (0-24). */ repeated uint64 ul_mode_s_by_uf = 7; /* Mode 1 uplink. */ uint64 ul_mode_1_interrogation = 8; /* Mode 2 uplink. */ uint64 ul_mode_2_interrogation = 9; /* Mode 4 uplink. */ uint64 ul_mode_4_interrogation = 10; /* Mode 5 uplink. */ uint64 ul_mode_5_interrogation = 11; /* Mode A uplink. */ uint64 ul_mode_a_interrogation = 12; /* Mode C uplink. */ uint64 ul_mode_c_interrogation = 13; /* Isolated pulses on 1030MHz. */ uint64 ul_isolated_pulse = 14; /* DME/TACAN pulse pair on 1030MHz. */ uint64 ul_dme_tacan_pulse_pair = 15; /* UAT ADS-B by payload type (0-31). */ repeated uint64 uat_adsb_message_by_payload_type = 16; /* UAT ground uplink. */ uint64 uat_ground_uplink_message = 17; /* UAT ground uplink information by frame type (0..15). */ repeated uint64 uat_ground_uplink_information_frame_by_type = 18; /* Isolated pulses on 978 MHz. */ uint64 uat_isolated_pulse = 19; /* DME/TACAN pulse pair on 978MHz. */ uint64 uat_dme_tacan_pulse_pair = 20; // Signals on other bands, that is (currently) neither on Uplink (1030 MHz), Downlink (1090 MHz) or UAT (978 MHz) // These are only relevant if the receiver has a radio frontend for other bands. /* Isolated pulse on other bands */ uint64 other_isolated_pulse = 21; /* DME/TACAN pulse pair on other bands */ uint64 other_dme_tacan_pulse_pair = 22; } /* Message rates. */ message Rates { /* Mode S downlink, split by DF (0-24). */ repeated float dl_mode_s_by_df = 1; /* Mode 1/2/A/C downlink. */ float dl_mode_1_2_a_c_reply = 2; /* Mode 4 downlink. */ float dl_mode_4_reply = 3; /* Mode 5 downlink. */ float dl_mode_5_reply = 4; /* Isolated pulses on 1090MHz. */ float dl_isolated_pulse = 5; /* DME/TACAN pulse pair on 1090MHz. */ float dl_dme_tacan_pulse_pair = 6; /* Mode S uplink, split by UF (0-24). */ repeated float ul_mode_s_by_uf = 7; /* Mode 1 uplink. */ float ul_mode_1_interrogation = 8; /* Mode 2 uplink. */ float ul_mode_2_interrogation = 9; /* Mode 4 uplink. */ float ul_mode_4_interrogation = 10; /* Mode 5 uplink. */ float ul_mode_5_interrogation = 11; /* Mode A uplink. */ float ul_mode_a_interrogation = 12; /* Mode C uplink. */ float ul_mode_c_interrogation = 13; /* Isolated pulses on 1030MHz.. */ float ul_isolated_pulse = 14; /* DME/TACAN pulse pair on 1030MHz. */ float ul_dme_tacan_pulse_pair = 15; /* UAT ADS-B by payload type (0-31). */ repeated float uat_adsb_message_by_payload_type = 16; /* UAT ground uplink. */ float uat_ground_uplink_message = 17; /* UAT ground uplink information by frame type (0..15). */ repeated float uat_ground_uplink_information_frame_by_type = 18; /* Isolated pulses on 978 MHz. */ float uat_isolated_pulse = 19; /* DME/TACAN pulse pair on 978MHz. */ float uat_dme_tacan_pulse_pair = 20; // Signals on other bands, that is (currently) neither on Uplink (1030 MHz), Downlink (1090 MHz) or UAT (978 MHz) // These are only relevant if the receiver has a radio frontend for other bands. /* Isolated pulse on other bands */ float other_isolated_pulse = 21; /* DME/TACAN pulse pair on other bands */ float other_dme_tacan_pulse_pair = 22; } /* Message counters. */ Counters counters = 1; /* Message rate [1/s] over last 10 seconds. */ Rates rates_10s = 2; /* Message rate [1/s] over last 60 seconds. */ Rates rates_60s = 3; /* Device reception capabilities. */ ReceptionCapabilities reception_capabilities = 4; /* Timestamp of the counters field in system time at time of assembly of the message. * Can be used to derive more fine-grained statistics */ google.protobuf.Timestamp timestamp_counters = 5; } /* Samples statistics, may indicate system overloads. */ message SamplesStatistics { /* * Number of sample read attempts. * Subtract the errors below to get successful attempts. */ uint64 read_attempts = 1; /* * Number of attempts which failed due to deadline passed. * Signifies too high load. */ uint64 stale = 2; /* * Number of other errors. * Might signify hardware/software incompatibilities. */ uint64 other_error = 3; } /* Band/Frequency a front end operates on. */ enum Band { /* Unspecified band, used when uninitialized or no other enum member matches */ BAND_UNSPECIFIED = 0; /* 978 MHz band. */ BAND_978_MHZ = 1; /* 1030 MHz band. */ BAND_1030_MHZ = 2; /* 1090 MHz band. */ BAND_1090_MHZ = 3; /* Special "band" to identify a channel that is fixed to a band/frequency that is not included here as enum member */ BAND_FIXED = 127; /* Special "band" to identify a channel that can be tuned to different bands/frequencies */ BAND_TUNABLE = 128; /* Special enum member that does not describe a band, but can be used to indicated a "match all" when matching bands */ BAND_MATCH_ANY = 254; } /* * Antenna status. */ message AntennaStatus { /* Unique identification of the antenna. */ string id = 1; /* Human-readable label describing the antenna. */ string label = 2; /* Whether power supply of the antenna can be enabled/disabled. */ bool has_supply_enable = 3; /* Whether power supply should be enabled. */ bool supply_enable = 4; /* Power supply faults. */ enum SupplyFault { /* No power supply fault detection supported. */ NOT_SUPPORTED = 0; /* No power supply fault detected. */ NO_FAULT_DETECTED = 1; /* Power supply fault detected. */ FAULT_DETECTED = 2; } /* Power fault. Note: power faults can only be detected if * power_supply_enabled is true. */ SupplyFault supply_fault = 5; } /* * Antenna switch status. */ message AntennaSwitchStatus { /* Unique identification of the antenna switch. */ string id = 1; /* Human-readable label describing the antenna switch. */ string label = 2; /* * Map from state id (e.g. "combined") to label (e.g. "Combined GNSS and * Mode-S") */ map state_id_to_label = 3; /* Current state id, referenced an id in state_id_to_label. */ string state_id = 4; } /* * Radio frontend status. * This includes mostly low-level data, such as current gain and noise level. * Different frontends can be distinguished via fields 'band' and * 'per_band_index'. */ message RadioFrontEndStatus { /* * Fixed external gain (before entering the receiver) [dB]. * This can be used to represent the losses in the cable or the gain of * the (active) antenna. */ float external_fixed_gain = 1; /* * Fixed gain of the RX chain (beginning at the antenna input) [dB]. * This excludes the rx_chain_variable_gain. */ float rx_chain_fixed_gain = 2; /* * Variable gain of the RX chain [dB]. * Can be used as a measure to see how the receiver did adapt to the current * situation. */ float rx_chain_variable_gain = 3; /* * Noise level [dBm]. * Estimated by analyzing "zero power" positions in the preamble of Mode S * frames. * Note that this only gets updated if valid frames are received. */ float noise_level = 4; /* DC offset applied on-chip for channel I. * Deprecated, use dc_offset_correction_on_board */ int32 dc_offset_on_chip_i = 5 [ deprecated = true ]; /* DC offset applied on-chip for channel Q. * Deprecated, use dc_offset_correction_on_board */ int32 dc_offset_on_chip_q = 6 [ deprecated = true ]; /* DC offset applied in PL for channel I. * Deprecated, use dc_offset_correction_pl */ int32 dc_offset_pl_i = 7 [ deprecated = true ]; /* DC offset applied in PL for channel Q. * Deprecated, use dc_offset_correction_pl */ int32 dc_offset_pl_q = 8 [ deprecated = true ]; /* * Most recent averaged, DC corrected residual value for channel I. * Deprecated, use dc_offset_residual_measurement */ int32 dc_offset_last_i_residual_avg = 9 [ deprecated = true ]; /* * Most recent averaged, DC corrected residual value for channel Q. * Deprecated, use dc_offset_residual_measurement */ int32 dc_offset_last_q_residual_avg = 10 [ deprecated = true ]; /* Whether a DC offset calibration is performed right now. * Deprecated, use dc_offset_manually_triggered_calibration. */ bool dc_offset_calibration_ongoing = 11 [ deprecated = true ]; /* * Whether a DC offset calibration has been performed. * Note: if neither this nor dc_offset_calibration_ongoing is set, * no calibration has been done since the device is running. * Deprecated, use dc_offset_manually_triggered_calibration. */ bool dc_offset_calibration_done = 12 [ deprecated = true ]; /* Whether DC offset calibration failed: did not converge. * Deprecated, use dc_offset_manually_triggered_calibration. */ bool dc_offset_calibration_no_convergence = 13 [ deprecated = true ]; /* Whether DC offset recalibration should be done. * Deprecated, use dc_offset_manually_triggered_calibration. */ bool dc_offset_calibration_recalibration_advised = 14 [ deprecated = true ]; /* Whether DC offset recalibration must be done. * Deprecated, use dc_offset_manually_triggered_calibration. */ bool dc_offset_calibration_recalibration_required = 15 [ deprecated = true ]; /* Band this radio front end operates on. */ Band band = 16; /* Distinguishes front ends operating on same band/frequency. */ int32 per_band_index = 17; /* Antenna id. May be empty for "unknown". */ string antenna_id = 18; /* * Antenna status. May be unavailable. * Note: response GetRadioFrontEndStatusReply contains map antenna_status. * The antenna_id above maps to the same info as this field, this is just a * shortcut. */ AntennaStatus antenna_status = 19; /* * The minimum observed level [dBm] of valid/correctly decoded signals. * Gives some hint at the currently achievable sensitivity. * * This value has usually been processed using some rudimentary statistics. * * In case the value is not available or cannot be computed * (e.g. due to missing received signals), * this field will be NAN. */ float minimum_observed_signal_level = 20; /* * The estimated gain [dB] of the radio frontend. * Negative gains hint at losses in the signal chain. * * In case the value is not available or cannot be computed * (e.g. due to missing received signals), * this field will be NAN. */ float estimated_gain = 21; message Occupancy { /* The threshold [dBm] that is currently used to calculate the occupancy * A value of NAN indicates that the value is not available. */ float threshold = 1; /* The occupancy [0..1] of the channel. Value is 0 if no samples are above the threshold and 1 if all are above the threshold. * A value of NAN indicates that the value is not available. */ float value = 2; } /* Occupancy of the channel. * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ Occupancy occupancy = 22; message AveragePower { /* The average power on the channel [dBm] * A value of NAN indicates that the value is not available. */ float power = 1; } /* Average power of the channel. * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ AveragePower average_power = 23; message DCOffsetResidualMeasurement { /* true iff the measurements (and thus avg_i and avg_q) are valid */ bool available = 1; /** The most recent averaged, DC corrected residual value for channel I. Should be close to zero if DC offset calibration is good. */ int32 avg_i = 2; /** The most recent averaged, DC corrected residual value for channel Q. Should be close to zero if DC offset calibration is good. */ int32 avg_q = 3; } /* Measured residual DC offset values. * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ DCOffsetResidualMeasurement dc_offset_residual_measurement = 24; message DCOffsetCorrectionOnBoard { bool available = 1; /* The DC offset applied on-board for channel I */ int32 offset_i = 2; /* The DC offset applied on-board for channel Q */ int32 offset_q = 3; } /* Correction applied on "board", which may comprise several entities that apply DC offsets. * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ DCOffsetCorrectionOnBoard dc_offset_correction_on_board = 25; message DCOffsetCorrectionPL { bool available = 1; /* The DC offset applied in PL for channel I */ int32 offset_i = 2; /* The DC offset applied in PL for channel Q */ int32 offset_q = 3; } /* Correction applied in programmable logic (PL) * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ DCOffsetCorrectionPL dc_offset_correction_pl = 26; message DCOffsetAutomaticCalibration { bool available = 1; enum Status { /* calibration status not known or unsupported */ UNKNOWN = 0; /* indicates good calibration status (e.g. offsets within bounds and no other problems) */ GOOD = 1; /* the following indicate a bad calibration status with some more details */ /* at least one of the possible compensation values (on-board) has reached its bounds, offsets cannot be completely eliminated, probably something wrong */ BAD_ON_BOARD_COMPENSATION_BOUNDS_REACHED = 2; /* at least one of the possible compensation values (in PL) has reached its bounds, offsets cannot be completely eliminated, probably something wrong */ BAD_PL_COMPENSATION_BOUNDS_REACHED = 3; } Status status = 2; } /* Automatic DC Offset calibration * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ DCOffsetAutomaticCalibration dc_offset_automatic_calibration = 27; message DCOffsetManuallyTriggeredCalibration { bool available = 1; enum Status { /* calibration status not known or unsupported */ UNKNOWN = 0; /* indicates that a (possibly manually triggered or initially triggered) calibration is currently ongoing */ ONGOING = 1; /* calibration algorithm done, but did not converge, probably something wrong */ DONE_NO_CONVERGENCE = 2; /* calibration algorithm done and good. */ DONE_GOOD = 3; /* indicates that manually triggering a calibration is advised */ CALIBRATION_ADVISED = 4; /* indicates that manually triggering a calibration is required */ CALIBRATION_REQUIRED = 5; } Status status = 2; } /* Manually triggered DC Offset calibration * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ DCOffsetManuallyTriggeredCalibration dc_offset_manually_triggered_calibration = 28; message TunableState { bool available = 1; /* The center frequency this channel is tuned to. [Hz] */ uint32 center_frequency = 2; /* The sample rate [Sps] */ uint32 sample_rate = 3; /* The analog bandwith [Hz] */ uint32 bandwidth = 4; } /* If this radio frontend is tunable, contains more information about the state. * Only present if that feature is built-in, i.e. if the values can ever be populated by that receiver. */ TunableState tunable_state = 29; } /* Parameter for GetStateVectorRequest request. */ message GetStateVectorHistoryRequest { /* The qualified address of the requested target. */ StateVectorList.StateVector.QualifiedAddress address = 1; } /* * The Mode S specific address qualifier. * Mainly for use with DF 18. * See DO-260B, Table 2-11. */ message QualifiedAddressModeS { /* Address qualifier. */ enum AddressQualifier { /* Mode S (or ADS-B) Target with ICAO 24 bit address. */ ICAO24 = 0; /* TIS-B target with ICAO 24 bit address. */ TIS_B_ICAO24 = 1; /* ADS-R target with ICAO 24 bit address. */ ADS_R_ICAO24 = 2; /* ADS-B target with anonymous, ground vehicle or fixed obstacle address. */ ADS_B_ANONYMOUS_OR_GROUND_VEHICLE_OR_FIXED_OBSTACLE = 3; /* ADS-R target with anonymous, ground vehicle or fixed obstacle address. */ ADS_R_ANONYMOUS_OR_GROUND_VEHICLE_OR_FIXED_OBSTACLE = 4; /* TIS-B target with Mode A code and track file identifier. */ TIS_B_MODE_A_CODE_AND_TRACK_FILE_IDENTIFIER = 5; /* TIS-B target with non-ICAO 24 bit address. */ TIS_B_NON_ICAO24 = 6; /* * Invalid or reserved address; will also be used to indicated that the * address could not be determined */ INVALID = 7; /* * Begin Special address qualifiers that may be used to when matching * addresses/qualifiers. */ /* * Matches addresses with any qualifier (including invalid ones) that * additionally have a matching address part. * Use this if you want to match an address, but do not care about the * exact qualifier. */ MATCH_ANY_QUALIFIER = 254; /* * Matches any addresses with any qualifier (including invalid ones). * Use this to disable any filtering. */ MATCH_ANY_ADDRESS = 255; } /* Address qualifier. */ AddressQualifier aq = 1; /* Address part of the qualified address, 24 bits */ uint32 address = 2; } /* * The UAT specific address qualifier. * See See DO-282B, 2.2.4.5.1.2 */ message QualifiedAddressUAT { /* Address qualifier. */ enum AddressQualifier { /** "ADS-B target with ICAO 24-bit address" */ ADSB_TARGET_WITH_ICAO24 = 0; /** "ADS-B target with self-assigned temporary address" */ ADSB_TARGET_WITH_SELF_ASSIGNED_TEMPORARY = 1; /** "TIS-B or ADS-R target with ICAO 24-bit address" */ TISB_OR_ADSR_TARGET_WITH_ICAO24 = 2; /** "TIS-B target with track file identifier" */ TISB_TARGET_WITH_TRACK_FILE_IDENTIFIER = 3; /** "Surface Vehicle" */ SURFACE_VEHICLE = 4; /** "Fixed ADS-B Beacon" */ FIXED_ADSB_BEACON = 5; /** "ADS-R target with non-ICAO address" */ ADSR_TARGET_WITH_NON_ICAO = 6; /** Reserved */ RESERVED = 7; /* * Begin Special address qualifiers that may be used to when matching * addresses/qualifiers. */ /* * Matches addresses with any qualifier (including invalid ones) that * additionally have a matching address part. * Use this if you want to match an address, but do not care about the * exact qualifier. */ MATCH_ANY_QUALIFIER = 254; /* * Matches any addresses with any qualifier (including invalid ones). * Use this to disable any filtering. */ MATCH_ANY_ADDRESS = 255; } /* Address qualifier. */ AddressQualifier aq = 1; /* Address part of the qualified address, 24 bits */ uint32 address = 2; } /* Parameter for GetModeSDownlinkFrames request. */ message GetModeSDownlinkFramesRequest { /* * Single rule that enables samples for decoded frames. * Both, Address and DF, must match in order for the rule to apply * (logic AND). */ message SampleEnableRule { /* Qualified address to be matched */ QualifiedAddressModeS address = 1; /* * Set of downlink formats to be matched. * If e.g. only 1 and 17 are put into this array, only samples of a * frame with downlink format 1 and 17 from the specified AA will be * relayed. * If this array is empty, this rule is not effective. * It is an error to specify downlink formats > 24. * Note also, that all given downlink formats should also be * specified in the downlink_formats array of the * GetModeSDownlinkFramesRequest, otherwise the frame (and thus the * samples) won't be relayed at all. */ repeated uint32 downlink_formats = 2; } /* * Set of downlink formats to be relayed. * If e.g. only 1 and 17 are put into this array, only frames with df 1 and * 17 will be relayed. * It is an error to leave this array empty. * It is an error to specify downlink formats > 24. */ repeated uint32 downlink_formats = 1; /* * List of rules that describe for which of the decoded frames the samples * should be included. */ repeated SampleEnableRule sample_enable_rules = 2; /* The required confirmation flags for messages to be relayed. * If the message is omitted, requires the message to be valid and the address to be tracked * (previous behavior before the introduction of this field). */ ModeSConfirmationFlags required_confirmation_flags = 3; } /* Parameter for GetModeSUplinkFrames request. */ message GetModeSUplinkFramesRequest { /* * Single rule that enables samples for decoded frames. * Both, Address and UF, must match in order for the rule to apply * (logic AND). */ message SampleEnableRule { /* Qualified address to be matched */ QualifiedAddressModeS address = 1; /* * Set of uplink formats to be matched. * If e.g. only 0 and 16 are put into this array, only samples of a * frame with uplink format 0 and 16 from the specified AA will be * relayed. * If this array is empty, this rule is not effective. * It is an error to specify uplink formats > 24. * Note also, that all given uplink formats should also be * specified in the uplink_formats array of the GetModeSUplinkFramesRequest, * otherwise the frame (and thus the samples) won't be relayed at all. */ repeated uint32 uplink_formats = 2; } /* * Set of uplink formats to be relayed. * If e.g. only 1 and 16 are put into this array, only frames with uf 0 and * 16 will be relayed. * It is an error to leave this array empty. * It is an error to specify uplink formats > 24. */ repeated uint32 uplink_formats = 1; /* * List of rules that describe for which of the decoded frames the samples * should be included. */ repeated SampleEnableRule sample_enable_rules = 2; /* The required confirmation flags for messages to be relayed. * If the message is omitted, requires the message to be valid and the address to be tracked * (previous behavior before the introduction of this field). */ ModeSConfirmationFlags required_confirmation_flags = 3; } /* Parameter for GetUATADSBMessages request. */ message GetUATADSBMessagesRequest { /* * Single rule that enables samples for decoded messages. * Both, Address and payload type code must match in order for the rule to apply * (logic AND). */ message SampleEnableRule { /* Qualified address to be matched */ QualifiedAddressUAT address = 1; /* * Set of payload type codes to be matched. * If e.g. only 0 is put into this array, only samples of a * Basic ADS-B message from the specified AA will be * relayed. * If this array is empty, this rule is not effective. * It is an error to specify payload type codes > 31. * Note also, that all given payload type codes should also be * specified in the payload_type_codes array of the * GetUATADSBMessagesRequest, otherwise the message (and thus the * samples) won't be relayed at all. */ repeated uint32 payload_type_codes = 2; } /* * Set of payload type codes to be relayed. * If e.g. only 0 is put into this array, only basic ADS-B messages * will be relayed. * It is an error to leave this array empty. * It is an error to specify payload type codes > 31. */ repeated uint32 payload_type_codes = 1; /* * List of rules that describe for which of the decoded messages the samples * should be included. */ repeated SampleEnableRule sample_enable_rules = 2; } /* Parameter for GetUATGroundUplinkMessages request. */ message GetUATGroundUplinkMessagesRequest { message SampleEnableRule { /* * Currently, samples can only be enabled for all ground uplink messages, * i.e. no filtering possible. */ bool enable_all = 1; } /* * List of rules that describe for which of the decoded messages the samples * should be included. */ repeated SampleEnableRule sample_enable_rules = 2; } /* Message used to enable radios by band/per_band_index */ message RadioIdentificationEnable { /* the band to enable, note that BAND_MATCH_ANY matches any band */ Band band = 1; /* per-band-index, set to -1 to request all for the specified band */ int32 per_band_index = 2; } /* Parameter for GetDMETACANPulsePairsRequest */ message GetDMETACANPulsePairsRequest { /* List of radio identifications for which the signals are requested */ repeated RadioIdentificationEnable enabled_radios = 1; /* if true, all samples are enabled */ bool enable_all_samples = 2; } /* Parameter for GetIsolatedPulsesRequest */ message GetIsolatedPulsesRequest { /* List of radio identifications for which the signals are requested */ repeated RadioIdentificationEnable enabled_radios = 1; /* if true, all samples are enabled */ bool enable_all_samples = 2; } /* * Time base (from where timestamp was taken). * Also implies epoch for the timestamp. */ enum TimingBase { /* Not based on any time base */ NO_BASE = 0; /* * Clock advances with system clock, and is based on the system's uptime, * i.e. advances monotonic. It only has 51 bit, thus resets each 2^51 ns, * which is about 13 days. */ SYSTEM_TIME = 1; /* Based on GPS Time of Week (ToW) */ GPS_TOW = 2; } /* * Synchronization source. */ enum TimingSyncSource { /* No valid synchronization source or not within allowed bounds */ NO_SOURCE = 0; /* Synchronized to a Global Navigation Satellite System (GNSS) clock */ GNSS = 1; /* Synchronized to a (local) atomic clock */ ATOMIC_CLOCK = 2; } /* Signal samples. */ message SignalSamples { /* Sample formats. */ enum SampleFormat { /* Unspecified sample format, should not happen. */ Unspecified = 0x00; /* I (12 bit signed) and Q (12 bit signed) packed into 32 bits. */ S12I_S12Q = 0x01; /* I (16 bit signed) and Q (16 bit signed) packed into 32 bits */ S16I_S16Q = 0x02; /* I (8 bit signed) and Q (8 bit signed) packed into 16 bits */ S8I_S8Q = 0x03; /* I (8 bit signed) and Q (8 bit signed), squelch compressed. * Not used at the moment. */ S8I_S8Q_SQUELCH_COMPRESSED = 0x04; } /* Samples bytes, must be interpreted as specified by sample_format. */ bytes samples = 1; /* Sample rate [Hz]. */ fixed32 sample_rate = 2; /* Sample format. */ SampleFormat sample_format = 3; /* Start of data index (index into decoded samples). */ uint32 start_of_data = 4; } /* Flags that communicate the validity of Mode S messages (down- and uplink) */ message ModeSConfirmationFlags { /* True iff the address is internally tracked */ bool address_tracked = 1; /* True iff message has a valid structure (depth of checks may vary) */ bool message_valid = 2; } /* Mode S Downlink Frame. */ message ModeSDownlinkFrame { /* * Timestamp [ns] of the frame. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Number of bits that have been corrected. */ uint32 number_corrected_bits = 4; /* Noise level of this frame [dBm]. */ float level_noise = 5; /* Signal level of this frame [dBm]. */ float level_signal = 6; /* Whether received carrier frequency was computed. */ bool carrier_frequency_offset_computed = 7; /* * Offset of received carrier frequency from nominal carrier frequency [Hz]. * Only valid if carrier_frequency_offset_computed is true. */ float carrier_frequency_offset = 8; /* * Error indicator of received carrier frequency offset computation. * Only valid if carrier_frequency_offset_computed is true. * Smaller values signify a better estimation of the offset. * Should be a sufficiently small value (<= 0.16) to assert a meaningful * estimation. */ float carrier_frequency_estimation_error = 9; /* 14 (or 7, in case of a short frame) bytes of payload. */ bytes payload = 10; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 11; /* The confirmation flags, see documentation of message for details. */ ModeSConfirmationFlags confirmation_flags = 12; } /* Mode S Uplink Frame. */ message ModeSUplinkFrame { /* * Timestamp [ns] of the frame. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Number of bits that have been corrected. */ uint32 number_corrected_bits = 4; /* Noise level of this frame [dBm]. */ float level_noise = 5; /* Signal level of this frame [dBm]. */ float level_signal = 6; /* Whether received carrier frequency was computed. */ bool carrier_frequency_offset_computed = 7; /* * Offset of received carrier frequency from nominal carrier frequency [Hz]. * Only valid if carrier_frequency_offset_computed is true. */ float carrier_frequency_offset = 8; /* * Error indicator of received carrier frequency offset computation. * Only valid if carrier_frequency_offset_computed is true. * Smaller values signify a better estimation of the offset. * Should be a sufficiently small value (<= 0.16) to assert a meaningful * estimation. */ float carrier_frequency_estimation_error = 9; /* 14 (or 7, in case of a short frame) bytes of payload. */ bytes payload = 10; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 11; /* The confirmation flags, see documentation of message for details. */ ModeSConfirmationFlags confirmation_flags = 12; } /* Mode S Downlink Frame with stream info. */ message ModeSDownlinkFrameWithStreamInfo { /* Mode S Downlink frame. */ ModeSDownlinkFrame frame = 1; /* * Number of dropped frames for this stream. * Dropped frames indicate high network and/or system utilization. */ fixed32 frames_dropped = 2; } /* Mode S Uplink Frame with stream info. */ message ModeSUplinkFrameWithStreamInfo { /* Mode S Uplink frame. */ ModeSUplinkFrame frame = 1; /* * Number of dropped frames for this stream. * Dropped frames indicate high network and/or system utilization. */ fixed32 frames_dropped = 2; } /* UAT ADS-B Message. */ message UATADSBMessage { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Number of bits that have been corrected. */ uint32 number_corrected_bits = 4; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Whether received carrier frequency was computed. */ bool carrier_frequency_offset_computed = 7; /* * Offset of received carrier frequency from nominal carrier frequency [Hz]. * Only valid if carrier_frequency_offset_computed is true. */ float carrier_frequency_offset = 8; /* * Error indicator of received carrier frequency offset computation. * Only valid if carrier_frequency_offset_computed is true. * Smaller values signify a better estimation of the offset. * Should be a sufficiently small value (<= 0.16) to assert a meaningful * estimation. */ float carrier_frequency_estimation_error = 9; /* 34 (or 18, in case of a short message) bytes of payload. */ bytes payload = 10; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 11; } /* UAT ADS-B Message with stream info. */ message UATADSBMessageWithStreamInfo { /* UAT ADS-B Message. */ UATADSBMessage message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* UAT Ground Uplink Message. */ message UATGroundUplinkMessage { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Number of bits that have been corrected. */ uint32 number_corrected_bits = 4; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Whether received carrier frequency was computed. */ bool carrier_frequency_offset_computed = 7; /* * Offset of received carrier frequency from nominal carrier frequency [Hz]. * Only valid if carrier_frequency_offset_computed is true. */ float carrier_frequency_offset = 8; /* * Error indicator of received carrier frequency offset computation. * Only valid if carrier_frequency_offset_computed is true. * Smaller values signify a better estimation of the offset. * Should be a sufficiently small value (<= 0.16) to assert a meaningful * estimation. */ float carrier_frequency_estimation_error = 9; /* 432 bytes of payload. */ bytes payload = 10; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 11; } /* UAT Ground Uplink Message with stream info. */ message UATGroundUplinkMessageWithStreamInfo { /* UAT Ground Uplink Message. */ UATGroundUplinkMessage message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetModeACDownlinkFrames request. */ message GetModeACDownlinkFramesRequest { /* * Binning mode for Mode AC frames. * As Mode AC frames are sent in bursts (and the amount of frames is very * high), forwarding each frame on its own would generate a high load. * Because of this, Mode AC frames with the same code are grouped into bins * of 100ms and transferred in those bins. * It is not common, that there is only a single reception of a code within * 100ms, so these binning modes act like a filter on those bins. */ enum BinningMode { /* Forward all bins, even those containing only single receptions. */ ALL = 0; /* * Forward only those bins which contain at least two receptions. */ AT_LEAST_2RECEPTIONS = 1; /* * Forward only those bins which contain at least two receptions * or (in case the is only one reception) whose code has been seen * recently (with exponential decay). */ AT_LEAST_2RECEPTIONS_OR_RECENT = 2; } /* Binning mode. */ BinningMode binning_mode = 1; } /* * Mode AC Downlink receptions. * Message contains a burst of receptions of the same code. */ message ModeACDownlinkBinnedFrame { /* * Timestamp [ns] of the primary reception, i.e. the same as the timestamp * of the first reception within this message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base for all receptions within this message. */ TimingBase timing_base = 2; /* Synchronization source for all receptions within this message. */ TimingSyncSource timing_sync_source = 3; /* Mode A/C code without X bit (number between 0 and 4095). */ fixed32 code = 4; /* Single reception of the code. */ message Reception { /* * Timestamp [ns] of the reception. * Also look at timing_base and timing_sync_source of outer message to * know how to interpret the timestamp. */ fixed64 timestamp = 1; /* Signal level of this reception [dBm]. */ float signal_level = 2; } /* Array of receptions, at least one. */ repeated Reception receptions = 5; } /* Mode AC Downlink Binned Frame with stream info. */ message ModeACDownlinkBinnedFrameWithStreamInfo { /* Mode AC Downlink binned frame. */ ModeACDownlinkBinnedFrame frame = 1; /* * Number of dropped frames for this stream. * Dropped frames indicate high network and/or system utilization. */ fixed32 frames_dropped = 2; } /* DME/TACAN Pulse pair */ message DMETACANPulsePair { /* * Timestamp [ns] of the message. Referenced to rising edge of first pulse. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Possible spacing of the pulses [us] */ enum Spacing { INVALID = 0; SPACING_12_US = 12; SPACING_30_US = 30; SPACING_36_US = 36; } /* The spacing of the pulses */ Spacing spacing = 4; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* The band this signal was received on */ Band band = 7; /* Distinguishes front ends operating on same band. */ int32 per_band_index = 8; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 9; } /* DME/TACAN Pulse pair with stream info. */ message DMETACANPulsePairWithStreamInfo { DMETACANPulsePair message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Isolated pulse */ message IsolatedPulse { /* * Timestamp [ns] of the message. Referenced to rising edge of the pulse. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Duration of the pulse [ns] */ float duration = 4; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* The band this signal was received on */ Band band = 7; /* Distinguishes front ends operating on same band. */ int32 per_band_index = 8; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 9; } /* Isolated pulse with stream info. */ message IsolatedPulseWithStreamInfo { IsolatedPulse message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetMode4Interrogations request. */ message GetMode4InterrogationsRequest { /* if true, all samples are enabled */ bool enable_all_samples = 1; } /* Mode 4 interrogation */ message Mode4Interrogation { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* * Level of the SLS (side lobe suppression) pulse, * relative to the reported signal level [dB]. * * If it contains NaN, the SLS signal has not been detected. * * If may contain negative infinity if a signal was detected but the relative level * is so low, that it cannot be represented (probably will not happen in practice). */ float sls_level = 7; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 8; } /* Mode 4 interrogation with stream info. */ message Mode4InterrogationWithStreamInfo { Mode4Interrogation message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetMode4Replies request. */ message GetMode4RepliesRequest { /* if true, all samples are enabled */ bool enable_all_samples = 1; } /* Mode 4 Reply */ message Mode4Reply { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 7; } /* Mode 4 Reply with stream info. */ message Mode4ReplyWithStreamInfo { Mode4Reply message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetMode5Interrogations request. */ message GetMode5InterrogationsRequest { /* if true, all samples are enabled */ bool enable_all_samples = 1; } /* Mode 5 Interrogation */ message Mode5Interrogation { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 7; } /* Mode 5 Interrogation with stream info. */ message Mode5InterrogationWithStreamInfo { Mode5Interrogation message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetMode5Replies request. */ message GetMode5RepliesRequest { /* if true, all samples are enabled */ bool enable_all_samples = 1; } /* Mode 5 Reply */ message Mode5Reply { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 7; } /* Mode 5 Reply with stream info */ message Mode5ReplyWithStreamInfo { Mode5Reply message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* Parameter for GetMode123ACInterrogations request. */ message GetMode123ACInterrogationsRequest { /* if true, all samples are enabled */ bool enable_all_samples = 1; } /* Mode 1, Mode 2 or Mode 3 A/C Interrogation */ message Mode123ACInterrogation { /* * Timestamp [ns] of the message. * Also look at timing_base and timing_sync_source to know how to interpret * the timestamp. */ fixed64 timestamp = 1; /* Time base. */ TimingBase timing_base = 2; /* Synchronization source. */ TimingSyncSource timing_sync_source = 3; /* Enum describing all modes that can be communicated */ enum Mode { UNSPECIFIED = 0; MODE_1 = 1; MODE_2 = 2; MODE_A = 3; MODE_C = 4; }; /* The mode-type of the interrogation */ Mode mode = 4; /* Noise level of this message [dBm]. */ float level_noise = 5; /* Signal level of this message [dBm]. */ float level_signal = 6; /* Whether a Mode A/C interrogation is an all-call interrogation (set iff P4 is present) */ bool all_call = 7; /* Whether a Mode A/C interrogation includes Mode S transponders (set iff P4 is long) */ bool include_mode_s = 8; /* Level of the S1 (whisper-shout suppression) pulse, * relative to the reported signal level [dB]. * * If it contains NaN (i.e. std::isnan of the values is true), the S1 pulse * has not been detected. * * If may contain negative infinity if a pulse was detected but the relative level * is so low, that it cannot be represented (probably will not happen in practice). */ float s1_level = 9; /* * Level of the SLS (side lobe suppression) pulse, * relative to the reported signal level [dB]. * * If it contains NaN, the SLS signal has not been detected. * * If may contain negative infinity if a signal was detected but the relative level * is so low, that it cannot be represented (probably will not happen in practice). */ float sls_level = 10; /* Raw samples of the signal (only set if explicitly enabled). */ SignalSamples samples = 11; } /* Mode123ACInterrogation with stream info. */ message Mode123ACInterrogationWithStreamInfo { Mode123ACInterrogation message = 1; /* * Number of dropped messages for this stream. * Dropped messages indicate high network and/or system utilization. */ fixed32 messages_dropped = 2; } /* * Reply for GetRadioFrontEndStatus() call. */ message GetRadioFrontEndStatusReply { /* Array of radio front end status: one for each front end. */ repeated RadioFrontEndStatus radio_front_end_status = 1; /* Map from antenna id to antenna status. */ map antenna_status = 2; /* Map from antenna switch id to antenna switch status. */ map antenna_switch_status = 3; } /* Receiver daemon service definition. Port 5303. */ service Receiverd { /* * Get a list of state vectors. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetStateVectors (google.protobuf.Empty) returns (StateVectorList); /* * Get a history of state vectors for some target. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetStateVectorHistory (GetStateVectorHistoryRequest) returns (StateVectorHistory); /* * Get a range chart. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetRangeChart (google.protobuf.Empty) returns (RangeChart); /* * Reset range chart. * Returns UNAVAILABLE if receiverd could not be reached. */ rpc ResetRangeChart (google.protobuf.Empty) returns (google.protobuf.Empty); /* * Get received signal counters and rates. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetStatistics (google.protobuf.Empty) returns (Statistics); /* * Get samples statistics. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetSamplesStatistics (google.protobuf.Empty) returns (SamplesStatistics); /* * Get radio frontend status. * Returns UNAVAILABLE if receiverd could not be reached. * Returns DEADLINE_UNAVAILABLE if receiverd did not reply in time. */ rpc GetRadioFrontEndStatus (google.protobuf.Empty) returns (GetRadioFrontEndStatusReply); /* * Recalibrate DC offset. * Should be done if * rf.dc_offset_calibration_recalibration_advised or * rf.dc_offset_calibration_recalibration_required * for some rf as returned by GetRadioFrontEndStatus() is set. * Ongoing calibration can be monitored by * rf.dc_offset_calibration_ongoing, see also * documentation on the fields in RadioFrontEndStatus. * Returns UNAVAILABLE if receiverd could not be reached. */ rpc RecalibrateDCOffset (google.protobuf.Empty) returns (google.protobuf.Empty); /* * Request a stream of Mode S downlink frames (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetModeSDownlinkFramesRequest */ rpc GetModeSDownlinkFrames (GetModeSDownlinkFramesRequest) returns (stream ModeSDownlinkFrameWithStreamInfo); /* * Request a stream of Mode S uplink frames (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetModeSUplinkFramesRequest */ rpc GetModeSUplinkFrames (GetModeSUplinkFramesRequest) returns (stream ModeSUplinkFrameWithStreamInfo); /* * Request a stream of Mode AC downlink frames (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetModeACDownlinkFramesRequest */ rpc GetModeACDownlinkFrames (GetModeACDownlinkFramesRequest) returns (stream ModeACDownlinkBinnedFrameWithStreamInfo); /* * Request a stream of UAT ADS-B messages (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetModeSDownlinkFramesRequest */ rpc GetUATADSBMessages (GetUATADSBMessagesRequest) returns (stream UATADSBMessageWithStreamInfo); /* * Request a stream of UAT Ground Uplink messages (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetModeSDownlinkFramesRequest */ rpc GetUATGroundUplinkMessages (GetUATGroundUplinkMessagesRequest) returns (stream UATGroundUplinkMessageWithStreamInfo); /* * Request a stream of DME/TACAN pulse pairs (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetDMETACANPulsePairsRequest */ rpc GetDMETACANPulsePairs (GetDMETACANPulsePairsRequest) returns (stream DMETACANPulsePairWithStreamInfo); /* * Request a stream of isolated pulses (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetIsolatedPulsesRequest */ rpc GetIsolatedPulses (GetIsolatedPulsesRequest) returns (stream IsolatedPulseWithStreamInfo); /* * Request a stream of Mode 4 Interrogations (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetMode4InterrogationsRequest */ rpc GetMode4Interrogations (GetMode4InterrogationsRequest) returns (stream Mode4InterrogationWithStreamInfo); /* * Request a stream of Mode 4 Replies (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetMode4RepliesRequest */ rpc GetMode4Replies (GetMode4RepliesRequest) returns (stream Mode4ReplyWithStreamInfo); /* * Request a stream of Mode 5 Interrogations (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetMode5InterrogationsRequest */ rpc GetMode5Interrogations (GetMode5InterrogationsRequest) returns (stream Mode5InterrogationWithStreamInfo); /* * Request a stream of Mode 5 Replies (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetMode5RepliesRequest */ rpc GetMode5Replies (GetMode5RepliesRequest) returns (stream Mode5ReplyWithStreamInfo); /* * Request a stream of Mode123ACInterrogations (matching the request) from * the server. * For reconfiguration, the call has to be cancelled. * Returns UNAVAILABLE if the service is shutting down. * Returns INVALID_ARGUMENT if request was invalid, see GetMode123ACInterrogationsRequest */ rpc GetMode123ACInterrogations (GetMode123ACInterrogationsRequest) returns (stream Mode123ACInterrogationWithStreamInfo); } -------------------------------------------------------------------------------- 2.3 Monitord.proto -------------------------------------------------------------------------------- syntax = "proto3"; import "google/protobuf/empty.proto"; package serosystems.proto.v3.grx.monitord; option java_package = "de.serosystems.proto.v3.grx.monitord"; option java_outer_classname = "MonitorDProto"; // Go package is relative to the web-ui package // This will result in "golang.sero-systems.de/grx/web-ui/proto/.../v3 option go_package = "proto/monitord/v3"; /* CPU Usage */ message CPUUsage { /* * CPU Usage information for a single CPU/core (or aggregated over all). * All values are percent of time spent in the given states within the last * measurement period. */ message SingleCPUUsage { float total = 1; float user = 2; float nice = 3; float sys = 4; float idle = 5; float iowait = 6; float irq = 7; float softirq = 8; } /* Total CPU usage (of all CPUs/cores) as average. */ SingleCPUUsage total = 1; /* CPU Usage broken down for each CPU/core. */ repeated SingleCPUUsage per_cpu = 2; } /* Memory usage. All values are in bytes. */ message MemoryUsage { uint64 total = 1; uint64 used = 2; uint64 free = 3; uint64 shared = 4; uint64 buffer = 5; uint64 cached = 6; uint64 user = 7; uint64 locked = 8; } /* Swap memory usage. All values are in bytes. */ message SwapUsage { uint64 total = 1; uint64 used = 2; uint64 free = 3; } /* File system usage. */ message FileSystemUsage { /* Mount point. */ message MountEntry { /* Probably not needed, what is it anyways? */ //uint64 dev = 1; reserved 1; /* Device name. */ string devname = 2; /* Mount point. */ string mountdir = 3; /* Filesystem type. */ string type = 4; } /* Mount point for this usage info. */ MountEntry mount = 1; /* Total number of blocks. */ uint64 blocks = 2; /* Free blocks available to non-superuser. */ uint64 bavail = 3; /* Size of a block [b]. */ uint32 block_size = 4; } /* Process info. */ message ProcessInfo { /* Process ID. */ uint64 pid = 1; /* User name. */ string user = 2; /* Kernel scheduling priority. */ int32 priority = 3; /* Standard unix nice level of process. */ int32 nice = 4; /* The state, as defined by the GLIBTOP_PROCESS_* macros. */ int32 state = 5; /* Command name. */ string command = 6; /* Command Arguments. */ repeated string argument = 7; /* Parent process ID. */ uint64 ppid = 8; /* Total number of pages of virtual memory. */ uint64 mem_virt = 9; /* Number of resident set (non-swapped) pages. */ uint64 mem_res = 10; /* Number of pages of shared memory. */ uint64 mem_shared = 11; /* Start time of the process [s since epoch UTC]. */ uint64 start_time = 12; /* User-mode CPU time [s]. */ float utime = 13; /* Kernel-mode CPU time [s]. */ float stime = 14; /* TODO: possibly missing: %cpu, %mem */ } /* Representation of a Debian Package. */ message SystemPackage { /* Debian package states. */ enum Status { NOTINSTALLED = 0; CONFIGFILES = 1; HALFINSTALLED = 2; UNPACKED = 3; HALFCONFIGURED = 4; TRIGGERSAWAITED = 5; TRIGGERSPENDING = 6; INSTALLED = 7; }; /* Debian package state. */ Status status = 1; /* Package name. */ string name = 2; /* Package version. */ string version = 3; /* Package architecture. */ string architecture = 4; } /* * Representation of of a single log message. * See also SYSTEMD.JOURNAL-FIELDS(7) for details. */ message LogMessage { /* TODO: there can be many, many fields, which are usually organized in * key-value pairs, we only extract some here. */ /* Log message. */ string message = 1; /* Value between 0 (emergency) and 7 (debug). */ uint32 priority = 2; /* File of the code where the messages originates (if known). */ string code_file = 3; /* Line of the code where the messages originates (if known). */ string code_line = 4; /* Function of the code where the messages originates (if known). */ string code_func = 5; /* Value of errno (if available). */ uint32 causing_errno = 6; /* Syslog facility, see RFC 3164. */ uint32 syslog_facility = 7; /* * Systemd SyslogIdentifier, defaulting to process name. * See also systemd.exec(5). */ string syslog_identifier = 8; /* Syslog PID. */ uint32 syslog_pid = 9; /* Time [ms since epoch UTC] this entry was received in the journal. */ uint64 realtime_timestamp = 10; } /* Representation of a systemd unit. */ message Unit { /* ID/name of the unit. */ string id = 1; /* Description of the unit. */ string description = 2; /* Load state, e.g. "loaded", "not-found",etc. */ string load_state = 3; /* Active state, e.g. "active", "inactive", etc. */ string active_state = 4; /* Sub-state, e.g. "running", "exited", etc. */ string sub_state = 5; string following = 6; /* SD-Bus path of the unit. */ string unit_path = 7; uint32 job_id = 8; string job_type = 9; string job_path = 10; } /* * Network device usage: rates for the measurement period, i.e. * packets/s, bytes/s, errors/s and collisions/s. */ message NetworkDeviceUsage { /* Name of the device. */ string device = 1; uint32 packets_in = 2; uint32 packets_out = 3; uint32 packets_total = 4; uint32 bytes_in = 5; uint32 bytes_out = 6; uint32 bytes_total = 7; uint32 errors_in = 8; uint32 errors_out = 9; uint32 errors_total = 10; uint32 collisions = 11; } /* * Network device counters. * Very similar to NetworkDeviceUsage, but gives absolute counter values instead * of rates. * Make sure to take care of overflows. Currently, Linux uses 32 bit counters * for (probably most of) these values. */ message NetworkDeviceCounters { /* Name of the device */ string device = 1; uint32 packets_in = 2; uint32 packets_out = 3; uint32 packets_total = 4; uint32 bytes_in = 5; uint32 bytes_out = 6; uint32 bytes_total = 7; uint32 errors_in = 8; uint32 errors_out = 9; uint32 errors_total = 10; uint32 collisions = 11; } /* System health information. */ message SystemHealth { /* CPU temperature [Deg. Celcius]. */ float temperature_cpu = 1; /* Voltage at the DC input [V]. Only available on devices with voltage sensing hardware. */ float voltage_dc_in = 2; } /* Overall system information. */ message SystemInformation { /* Current system time [s since epoch UTC]. */ uint64 system_time = 1; /* Uptime of the system [s]. */ uint32 uptime = 2; /* Reset reasons. */ enum ResetReason { /* * Power on Reset, i.e. device has been connected to power. Does not * indicate a problem. */ PowerOnReset = 0; /* * System level controller reset. Happens e.g. if the device is * rebooted manually. Usually does not indicate a problem. */ SystemLevelControllerReset = 1; /* * System watchdog reset. Happens if the device is reset by the * internal watchdog timer. Indicates a problem. */ SystemWatchdogReset = 2; /* * External system reset. Can happen on development device which have a * mounted reset switch and if the Zynq is reset by the CPLD. Usually * indicates a problem. */ ExternalSystemReset = 3; /* * Debug reset. Should only happen during debugging with JTAG * hardware. */ DebugReset = 4; /* CPU0 APU watchdog timer reset, should never happen. */ APUWatchdogTimerReset0 = 5; /* CPU1 APU watchdog timer reset, should never happen. */ APUWatchdogTimerReset1 = 6; } /* * The reset reasons. There can be multiple, as they need to be manually * cleared. */ repeated ResetReason reset_reasons = 3; /* The serial number of the device. */ string serial_number = 4; /* System version fields. */ map version_information = 5; } /* * System Load: Number of jobs in run queue or waiting for IO over time. * See /proc/loadavg under proc(5) for details. */ message SystemLoad { /* Load average over last minute. */ float load_avg_1m = 1; /* Load average over last 5 minutes. */ float load_avg_5m = 2; /* Load average over last 15 minutes. */ float load_avg_15m = 3; } /* GNSS Information. */ message GNSSInformation { /* Position information. */ message Position { /* Fix type of GNSS receiver. */ enum FixType { /* No fix established. */ None = 0; /* 2D position fix. */ Pos2D = 2; /* 3D position fix. */ Pos3D = 3; /* * Time only fix. Used in "stationary" mode, when the position is * assumed to be static. */ TimeOnly = 5; } /* Type of fix. */ FixType fix_type = 1; /* Number of satellites used in the solution. Actually uint8. */ uint32 sats_used = 2; /* Latitude [degrees]. */ double latitude = 3; /* Longitude [degrees]. */ double longitude = 4; /* Height above WGS-84 ellipsoid [m]. */ double height = 5; /* Estimated horizontal accuracy [m]. */ double horizontal_accuracy = 6; /* Estimated vertical accuracy [m]. */ double vertical_accuracy = 7; } /* Further timing-related information. */ message Timing { /* UTC Timing information. */ message UTC { /* UTC standard of GNSS receiver. */ enum Standard { /* Unknown. */ Unknown = 0; /* UTC as operated by the U.S. Naval Observatory (USNO). */ USNO = 3; /* UTC as operated by the former Soviet Union. */ SU = 6; /* * UTC as operated by the National Time Service Center, China. */ China = 7; }; /* Whether all UTC fields are valid. */ bool valid = 1; /* Used UTC standard. */ Standard standard = 2; /* UTC year. Actually uint16. */ uint32 year = 3; /* UTC month. Actually uint8. */ uint32 month = 4; /* UTC day. Actually uint8. */ uint32 day = 5; /* UTC hour. Actually uint8. */ uint32 hour = 6; /* UTC minute. actually uint8. */ uint32 min = 7; /* * UTC second (may also be 60, if a leap second is present). * Actually uint8. */ uint32 sec = 8; } /* Disciplining sources of GNSS receiver. */ enum DiscipliningSource { /* Internal oscillator. */ Internal = 0; /* GNSS. */ GNSS = 1; EXTINT0 = 2; EXTINT1 = 3; /* Internal oscillator measured by the host. */ InternalMeasuredByHost = 4; /* External oscillator measured by the host. */ ExternalMeasuredByHost = 5; }; /* UTC timing information. */ UTC utc = 1; /* Whether time pulse is within tolerance limits. */ bool time_pulse_within_tolerance = 2; /* Whether the internal oscillator is within tolerance limits. */ bool internal_oscillator_within_tolerance = 3; /* Disciplining source of the oscillator. */ DiscipliningSource disciplining_source = 4; /* * Whether the Receiver Autonomous Integrity Monitoring Algorithm * (RAIM) system is active. */ bool raim_active = 5; /* Whether coherent pulse generation is in operation. */ bool coherent_pulse_generation = 6; /* Whether the time pulse is locked. * Note: even when locked, it may still be out of tolerance. */ bool time_pulse_locked = 7; /* Offset between preceding pulse and UTC top of second [ns]. */ int32 preceding_pulse_utc_offset = 8; /* Estimated GNSS time accuracy [ns]. If 0, value is invalid. * During holdover, this will usually grow large. * Was previously only named `time_accuracy`, which was inaccurate. */ uint32 gnss_time_accuracy = 9; /* Internal oscillator frequency offset [ppb]. */ float internal_oscillator_frequency_offset = 10; /* Internal oscillator frequency uncertainty [ppb]. */ float internal_oscillator_frequency_uncertainty = 11; /* Estimated overall time accuracy [ns] as reported by the module. If 0, value is invalid. * It is important to note that this may not include external holdover * oscillator uncertainties, but can only represent a module-internal * state. * During non-holdover, this will be similar as the `gnss_time_accuracy`, but * during holdover, this value should NOT be consulted. Instead, use the * field `time_accuracy_estimation` of `holdover_oscillator`. */ uint32 module_utc_accuracy = 12; /* Estimated overall time accuracy [ns]. If 0, value is invalid. * During non-holdover, this will be similar as the `gnss_time_accuracy`, but * during holdover, this value will represent the accuracy of the (external) * holdover oscillator (see also `HoldoverOscillator::time_accuracy_estimation`). */ uint32 overall_time_accuracy = 13; } message HoldoverOscillator { /* Describes what kind of holdover oscillator schema is used. */ enum Schema { /* Only the built-in oscillator (in most cases, this will be the VCTCXO of the GNSS module) is used */ BUILT_IN_ONLY = 0; /* The built-in oscillator is main clock source, but during hold-over it is disciplined by a more stable external oscillator */ BUILT_IN_DISCIPLINED_BY_EXTERNAL = 1; /* The external oscillator is main clock source. During non-holdover, it is disciplined by the built-in GNSS module. */ EXTERNAL_DISCIPLINED_BY_BUILT_IN = 2; /* The external oscillator is main clock source. It is self-contained, i.e. it does not need to be disciplined by the built-in GNSS module. */ EXTERNAL_SELF_CONTAINED = 3; }; Schema schema = 1; /* A string describing the model of the external oscillator, e.g. "CSAC SA.45s" */ string model = 2; /* For how many seconds the holdover state is active. Holdover is active iff value > 0 */ uint32 holdover_active_seconds = 3; /* True iff the external oscillator is considered "ready" to holdover. This usually requires some internal locks to be acquired, device to be sufficiently warmed up and more. */ bool is_ready_for_holdover = 4; /* True iff the external oscillator issues a warning. A warning is considered to describe a non-fatal, but possibly degraded state. This could e.g. be the case if the internal temperature cannot be properly maintained. */ bool warning_present = 5; /* True iff the external oscillator issues a failure. A failure is considered to describe a fatal state where no operation is possible. This could e.g. be hardware failure. */ bool failure_present = 6; /* Estimated accuracy of the time [ns]. If 0, value is invalid. */ uint32 time_accuracy_estimation = 7; /* Detailed health information about external oscillator. As this is highly device-specific, we only provide key-value string-pairs here. */ //message HealthDetails { // string key; // string value; //}; //repeated HealthDetails health_details; }; /* GNSS hardware monitoring related information. */ message HardwareMonitoring { /* Jamming state of GNSS receiver. */ enum JammingState { /* Unknown or feature disabled. */ UnknownOrDisabled = 0; /* OK, no significant jamming. */ OK = 1; /* Warning, interference visible, but fix OK. */ Warning = 2; /* Critical, interference visible and no fix. */ Critical = 3; } /* Noise level (as measured by the GPS core), actually uint8. */ uint32 noise_level = 1; /* AGC Monitor (range 0 to 8191). */ uint32 agc_monitor = 2; /* Output of the interference monitor. */ JammingState jamming_state = 3; /* * Continuous wave (CW) jamming indicator: 0 (no CW jamming) to 255 * (strong CW jamming). */ uint32 jamming_indicator = 4; /* Magnitude of the I-part of the complex signal (0..255). */ uint32 signal_magnitude_i = 5; /* Magnitude of the Q-part of the complex signal (0..255). */ uint32 signal_magnitude_q = 6; /* Imbalance of the I-part of the complex signal (-128..127). */ int32 signal_imbalance_i = 7; /* Imbalance of the Q-part of the complex signal (-128..127). */ int32 signal_imbalance_q = 8; /* Number of 100ms timeslots with parity errors. */ uint32 timeslots_parity_errors = 9; /* Number of 100ms timeslots with framing errors. */ uint32 timeslots_framing_errors = 10; /* Number of 100ms timeslots with overrun errors. */ uint32 timeslots_overrun_errors = 11; /* Number of 100ms timeslots with break conditions. */ uint32 timeslots_break_conditions = 12; /* Maximum usage of transmitter buffer during the last sysmon period for all targets [%]. */ uint32 tx_buffers_usage = 13; /* Maximum usage of transmitter buffer for all targets (overall) [%]. */ uint32 tx_buffers_peak_usage = 14; } /* Version information. */ message Versions { /* Hardware version of the GNSS module. */ string module_hw = 1; /* Software version of the GNSS module. */ string module_sw = 2; } /* GNSS Survey-In (timing mode) related information. */ message SurveyIn { /* Duration of the survey-in process [s]. */ uint32 duration = 1; /* Current mean position in ECEF coordinates (X component) [cm]. */ int32 mean_ecef_x = 2; /* Current mean position in ECEF coordinates (Y component) [cm]. */ int32 mean_ecef_y = 3; /* Current mean position in ECEF coordinates (Z component) [cm]. */ int32 mean_ecef_z = 4; /* Mean variance of the position [mm^2]. */ uint32 mean_variance = 5; /* Number of observations until now. */ uint32 num_observations = 6; /* Whether a valid position has been found. */ bool valid = 7; /* Whether the survey in progress is still active. */ bool active = 8; } /* Details about a satellite */ message SatelliteDetails { /* See 21.6. UBX Satellite Numbering */ enum GNSSIdentifier { GPS = 0; SBAS = 1; Galileo = 2; BeiDou = 3; IMES = 4; QZSS = 5; GLONASS = 6; } /* GNSS identifier */ GNSSIdentifier gnss_id = 1; /* Satellite identifier, actually uint8 */ int32 satellite_id = 2; /* Carrier to noise ratio [dBHz], actually uint8 */ int32 carrier_noise_ratio = 3; /* Elevation -90 deg to +90 deg. Not present if unknown. Actually sint8 */ optional sint32 elevation = 4; /* Azimuth 0 deg to 360 deg. Not present if unknown. Actually sint16 */ optional sint32 azimuth = 5; /* Pseudo range residual [0.1 m], actually sint16 */ sint32 pseudo_range_residual = 6; QualityIndicator quality_indicator = 7; enum QualityIndicator { NO_SIGNAL = 0; SEARCHING_SIGNAL = 1; SIGNAL_AQUIRED = 2; SIGNAL_DETECTED_BUT_UNUSABLE = 3; CODE_LOCKED_AND_TIME_SYNCHRONIZED = 4; CODE_AND_CARRIER_LOCKED_AND_TIME_SYNCHRONIZED_5 = 5; CODE_AND_CARRIER_LOCKED_AND_TIME_SYNCHRONIZED_6 = 6; CODE_AND_CARRIER_LOCKED_AND_TIME_SYNCHRONIZED_7 = 7; } /* currently being used for navigation? */ bool satellite_used = 8; HealthFlag health = 9; enum HealthFlag { UNKNOWN = 0; HEALTHY = 1; UNHEALTHY = 2; } bool differential_correction_data_available = 10; bool carrier_smoothed_pseudorange_used = 11; OrbitSource orbit_source = 12; enum OrbitSource { NO_ORBIT_INFORMATION_AVAILABLE = 0; EPHEMERIS_USED = 1; ALMANAC_USED = 2; ASSIST_NOW_OFFLINE_ORBIT_USED = 3; ASSIST_NOW_AUTONOMOUS_ORBIT_USED = 4; OTHER_ORBIT_INFORMATION_5 = 5; OTHER_ORBIT_INFORMATION_6 = 6; OTHER_ORBIT_INFORMATION_7 = 7; } bool ephemeris_available = 13; bool almanac_available = 14; bool assist_now_offline_data_available = 15; bool assist_now_autonomous_data_available = 16; } /* Message containing a list of satellite details */ message DetailedSatelliteInformation { repeated SatelliteDetails satellite_details = 1; } /* Position information. */ Position position = 1; /* Timing-related information, such as UTC time. */ Timing timing = 2; /* GNSS hardware monitoring related information. */ HardwareMonitoring hardware = 3; /* Version information. */ Versions versions = 4; /* GNSS Survey-In (timing mode) related information. */ SurveyIn survey_in = 5; /* Holdover oscillator related information */ // TODO: this might need to move out of GNSS ... HoldoverOscillator holdover_oscillator = 6; /* Detailed satellite information. If not present, no detailed satellite infomation are communicated. */ optional DetailedSatelliteInformation satellite_information = 7; } /* Reply for GetCPUUsageHistory() call. */ message GetCPUUsageHistoryReply { /* Pair of timestamp [s since epoch UTC] and cpu usage. */ message Pair { uint64 timestamp = 1; CPUUsage cpu_usage = 2; } /* CPU usage history. */ repeated Pair history = 1; } /* Reply for GetMemoryUsageHistory() call. */ message GetMemoryUsageHistoryReply { /* Pair of timestamp [s since epoch UTC] and memory usage. */ message Pair { uint64 timestamp = 1; MemoryUsage memory_usage = 2; } /* Memory usage history. */ repeated Pair history = 1; } /* Reply for GetSwapUsageHistory() call. */ message GetSwapUsageHistoryReply { /* Pair of timestamp [s since epoch UTC] and swap usage. */ message Pair { uint64 timestamp = 1; SwapUsage swap_usage = 2; } /* Swap usage history. */ repeated Pair history = 1; } /* Request for GetLogMessages() call. */ message GetLogMessagesRequest { /* List of matches, same semantics as specified for journalctl(1). */ repeated string match = 1; /* Maximum number of (most recent) entries to fetch. Set to 0 for all. */ uint32 max_lines = 2; /* If set, only show entries after the specified cursor. */ string after_cursor = 3; /* * If set, only show entries since the specified timestamp [ms since epoch * UTC]. */ uint64 since = 4; /* * If set, only show entries until the specified timestamp [ms since epoch] .*/ uint64 until = 5; } /* Reply for GetMountedFilesystemUsage() call. */ message GetMountedFilesystemUsageReply { /* List of mounted file systems with their usage. */ repeated FileSystemUsage file_system = 1; } /* Reply for GetProcessList() call. */ message GetProcessListReply { /* List of processes. */ repeated ProcessInfo processes = 1; } /* Reply for GetSystemPackages() call. */ message GetSystemPackagesReply { /* List of (installed) packages */ repeated SystemPackage package = 1; } /* Reply for GetLogMessages() call. */ message GetLogMessagesReply { /* * Current cursor in the journal, can be used to only fetch new messages in * the next request. */ string cursor = 1; /* Messages. */ repeated LogMessage message = 2; } /* Reply for GetUnitList() call. */ message GetUnitListReply { /* List of systemd units */ repeated Unit unit = 1; } /* Reply for GetNetworkUsage() call. */ message GetNetworkUsageReply { /* List of network devices with their usage. */ repeated NetworkDeviceUsage device_usage = 1; } /* Reply for GetNetworkUsageHistory() call. */ message GetNetworkUsageHistoryReply { /* Pair of timestamp [s since epoch UTC] and network device usage. */ message Pair { uint64 timestamp = 1; repeated NetworkDeviceUsage network_usage = 2; } /* Network device usage history. */ repeated Pair history = 1; } /* Reply for GetNetworkCounters() call. */ message GetNetworkCountersReply { /* List of network devices with their counters. */ repeated NetworkDeviceCounters device_counters = 1; } /* Reply for GetSystemHealthHistory() call. */ message GetSystemHealthHistoryReply { /* Pair of timestamp [s since epoch UTC] and system health values. */ message Pair { uint64 timestamp = 1; SystemHealth values = 2; } /* System health value history. */ repeated Pair history = 1; } /* Reply for GetSystemLoadHistory() call. */ message GetSystemLoadHistoryReply { /* Pair of timestamp [s since epoch UTC] and system load. */ message Pair { uint64 timestamp = 1; SystemLoad load = 2; } /* System load history. */ repeated Pair history = 1; } /* Reply for GetFullSystemStatus() call. **/ message GetFullSystemStatusReply { GNSSInformation gnss_information = 1; SystemLoad system_load = 2; SystemHealth system_health = 3; MemoryUsage memory_usage = 4; repeated FileSystemUsage file_system_usage = 5; repeated NetworkDeviceUsage network_usage = 6; SystemInformation system_information = 7; } /* Monitor daemon service definition. Port 5305. */ service Monitord { /* Get current CPU usage. */ rpc GetCPUUsage (google.protobuf.Empty) returns (CPUUsage); /* Get history of CPU usage. */ rpc GetCPUUsageHistory (google.protobuf.Empty) returns (GetCPUUsageHistoryReply); /* Get current memory usage. */ rpc GetMemoryUsage (google.protobuf.Empty) returns (MemoryUsage); /* Get history of memory usage. */ rpc GetMemoryUsageHistory (google.protobuf.Empty) returns (GetMemoryUsageHistoryReply); /* Get current swap usage. */ rpc GetSwapUsage (google.protobuf.Empty) returns (SwapUsage); /* Get history of swap usage. */ rpc GetSwapUsageHistory (google.protobuf.Empty) returns (GetSwapUsageHistoryReply); /* Get current file system usage. */ rpc GetMountedFilesystemUsage (google.protobuf.Empty) returns (GetMountedFilesystemUsageReply); /* Get a list of processes. */ rpc GetProcessList (google.protobuf.Empty) returns (GetProcessListReply); /* Get a list of (installed) system packages. */ rpc GetSystemPackages (google.protobuf.Empty) returns (GetSystemPackagesReply); /* Get log messages (without streaming/waiting for new ones). */ rpc GetLogMessages (GetLogMessagesRequest) returns (GetLogMessagesReply); /* Get a list (with states) of systemd units. */ rpc GetUnitList (google.protobuf.Empty) returns (GetUnitListReply); /* Get current network usage. */ rpc GetNetworkUsage (google.protobuf.Empty) returns (GetNetworkUsageReply); /* Get history of network usage. */ rpc GetNetworkUsageHistory (google.protobuf.Empty) returns (GetNetworkUsageHistoryReply); /* Get counters (in contrast to the rates) of the network usage. */ rpc GetNetworkCounters (google.protobuf.Empty) returns (GetNetworkCountersReply); /* Get current system health. */ rpc GetSystemHealth (google.protobuf.Empty) returns (SystemHealth); /* Get history of system health. */ rpc GetSystemHealthHistory (google.protobuf.Empty) returns (GetSystemHealthHistoryReply); /* Get current system load. */ rpc GetSystemLoad (google.protobuf.Empty) returns (SystemLoad); /* Get history of system load. */ rpc GetSystemLoadHistory (google.protobuf.Empty) returns (GetSystemLoadHistoryReply); /* * Get (lower level) information about the system, like e.g. uptime, reset * reasons or low-level version information. */ rpc GetSystemInformation (google.protobuf.Empty) returns (SystemInformation); /* * Clear all reset resons (see SystemInformation message). * This can be used to "acknowledge" certain events, such as a watchdog reset * (or also a simple "power on reset"). */ rpc ClearResetReasons (google.protobuf.Empty) returns (google.protobuf.Empty); /* * Get information related to the Global Navigation Satellite System * (GNSS). * Note: if the information is not available or outdated, the gRPC * call will fail with status code ABORTED. */ rpc GetGNSSInformation (google.protobuf.Empty) returns (GNSSInformation); /* Get full system status (single call for 7 most used calls). * Note: submessages may be omitted if the "sub calls" fail, e.g. * if GNSS information are invalid, the `gnss_information` submessage * will not be included. */ rpc GetFullSystemStatus (google.protobuf.Empty) returns (GetFullSystemStatusReply); } -------------------------------------------------------------------------------- 2.4 Spectrumd.proto -------------------------------------------------------------------------------- syntax = "proto3"; import "google/protobuf/empty.proto"; package serosystems.proto.v3.grx.spectrumd; option java_package = "de.serosystems.proto.v3.grx.spectrumd"; option java_outer_classname = "SpectrumDProto"; import "Common.proto"; /* Request for GetAggregatedFFT* calls */ message AggregatedFFTRequest { /* * Index of the chosen RX channel. * Useful if the device has more than a single RX channel. * If that index is not available, the call will fail. * * Only used, if radio_identification is not present. * Deprecated, as radio_identifacation should be used instead. */ uint32 rx_channel_index = 1 [ deprecated = true ]; /* If present, the specified RadioIdentification is used to identify the channel. * In this case, the value of rx_channel_index will be ignored. */ RadioIdentification radio_identification = 2; } /* * This is a service for measuring spectrum occupancy. * For that, (fft_size) samples are taken and transformed into frequency domain. * We call this an FFT block. Then (aggregation_factor) of those blocks are * aggregated (avg and peak=max). We call this an aggregated FFT block. * Regarding those aggregated FFT blocks, different services are derived: * - GetAggregatedFFTProperties() returns the fixed parameters used for * transformation and aggregation, * - GetAggregatedFFTBlockStream() returns a stream of aggregated FFT blocks, * - GetWaterfallJPEG() gathers multiple FFT blocks into an waterfall plot, * returning the plot rendered in a JPEG, * - GetWaterfallJPEGStream() returns a stream of such rendered plots, * - GetChannelPowerStream() re-aggregates aggregated FFT blocks over a longer * period and for a frequency range (we call that a channel). */ /* * Aggregated FFT properties. * Response of GetAggregatedFFTProperties() call. */ message AggregatedFFTProperties { /* Center Frequency [Hz]. */ uint32 center_frequency = 1; /* * Sample rate [Hz]. * Observed band is [-sample_rate/2,+sample_rate/2), * centered at (center_frequency). */ uint32 sample_rate = 2; /* * FFT size, i.e. number of samples used in a single FFT block. * This translates directly into the number of frequency bins of the FFT * result. Each bin has bandwidth (sample_rate / fft_size). * Thinking of a waterfall plot, this is the number of steps of the x axis * (frequency). */ uint32 fft_size = 3; /* * Number of FFT blocks aggregated into an aggregated FFT block. * The duration observed for a single FFT block is * (fft_size / sample_rate). Accordingly, the duration observed for an * aggregated FFT block is (aggregation_factor * fft_size / sample_rate). */ uint32 aggregation_factor = 4; } /* * Aggregated (avg/peak) FFT block, basically one line of a waterfall plot. * Has observation duration (aggregation_factor * fft_size / sample_rate). * Response of GetAggregatedFFTBlock() call. */ message AggregatedFFTBlock { /* * Averaged FFT bins [dBm]. * Each bin has bandwidth (sample_rate / fft_size) and is averaged over * a duration of (aggregation_factor * fft_size / sample_rate). * The FFT bins are centered around center_frequency, i.e. * first element corresponds to (center_frequency - 0.5 * sample_rate), * last element corresponds to * (center_frequency + (0.5 - 1/fft_size) * sample_rate), * and the center frequency is found at index (fft_size/2). * The number of FFT bins is fixed to (fft_size). */ repeated float bins_avg = 1; /* * Aggregated (peak=max) FFT bins [dBm]. * Each bin has bandwidth (sample_rate / fft_size) and is the maximum * observed over a duration of * (aggregation_factor * fft_size / sample_rate). * The FFT bins are centered around center_frequency, i.e. * first element corresponds to center_frequency - 0.5 * sample_rate, * last element corresponds to * (center_frequency + (0.5 - 1/fft_size) * sample_rate), * and the center frequency is found at index (fft_size/2). * The number of FFT bins is fixed to fft_size. * * Important note: intermediate peak results are highly compressed (using a * logarithmic representation), thus you will almost certainly note * "steps" in the data. Small values might be smaller than the average * values or they might even be -inf, due to a low resolution for low * values. * In such cases, the average value represents a "better" peak. * This should not be an issue for strong signals, though. */ repeated float bins_peak = 2; } /* * Request of GetWaterfallJPEG() and GetWaterfallJPEGStream() calls. */ message GetWaterfallJPEGRequest { /* * Number of lines of the rendered image, defining the JPEG height [px]. * The service records num_lines aggregated FFT block messages and renders * them as JPEG. Each line corresponds to one aggregated FFT block. * The duration shown in the waterfall plot corresponds to * (lines * aggregation_factor * fft_size / sample_rate) * which is also the duration needed for the call to return. */ uint32 num_lines = 1; /* Lower limit of the color scale [dBm]. */ float min_level = 2; /* Upper limit of the color scale [dBm]. */ float max_level = 3; /* JPEG quality [%]. */ uint32 jpeg_quality = 4; /* Aggregation type for aggregated FFT blocks. */ enum AggregationType { /* Aggregate blocks by average. */ AVERAGE = 0; /* Aggregate blocks by peak. */ PEAK = 1; } /* Aggregation type for aggregated FFT blocks. */ AggregationType aggregation_type = 5; /* * Index of the chosen RX channel. * Useful if the device has more than a single RX channel. * If that index is not available, the call will fail. * * Only used, if radio_identification is not present. * Deprecated, as radio_identifacation should be used instead. */ uint32 rx_channel_index = 6 [ deprecated = true ]; /* If present, the specified RadioIdentification is used to identify the channel. * In this case, the value of rx_channel_index will be ignored. */ RadioIdentification radio_identification = 7; } /* * Waterfall plot rendered as a JPEG image. * Response of GetWaterfallJPEG() and GetWaterfallJPEGStream() calls. */ message WaterfallJPEGImage { /* * Timestamp [s since epoch UTC] for the bottom line in the JPEG. */ uint64 timestamp = 1; /* * JPEG image for waterfall plot, rendered from top (first block) to bottom * (last block). */ bytes image = 2; } /* Request of GetChannelPowerStream() call. */ message ChannelPowerRequest { /* * Additional aggregation factor for channel power measurements. * The total aggregation factor will be this factor multiplied by * (aggregation_factor). * The response is measured over a period of * (channel_aggregation_factor * aggregation_factor * fft_size * / sample_rate). This is also the time needed for the call to return. */ uint32 channel_aggregation_factor = 1; /* * Lower bin index of the FFT to be included in the result. * See also documentation of AggregatedFFTBlock.bins_avg. */ uint32 lower_bin = 2; /* * Upper bin index of the FFT to be included in the result. * See also documentation of AggregatedFFTBlock.bins_avg. */ uint32 upper_bin = 3; /* * Index of the chosen RX channel. * Useful if the device has more than a single RX channel. * If that index is not available, the call will fail. * * Only used, if radio_identification is not present. * Deprecated, as radio_identifacation should be used instead. */ uint32 rx_channel_index = 4 [ deprecated = true ]; /* If present, the specified RadioIdentification is used to identify the channel. * In this case, the value of rx_channel_index will be ignored. */ RadioIdentification radio_identification = 5; } /* Response of GetChannelPowerStream() call. */ message ChannelPower { /* * Timestamp [s since epoch UTC] when the measurement was finished. */ uint64 timestamp = 1; /* * Average channel power [dBm]. * Average over all * (channel_aggregation_factor * aggregation_factor) FFT blocks, * averaged over the channel. */ float average_channel_power = 2; /* * Peak average channel power [dBm], peak over averages: * Peak over AggregatedFFTBlock.bins_avg averaged over the channel. */ float peak_average_channel_power = 3; /* * Peak power [dBm]. * Peak over all * (channel_aggregation_factor * aggregation_factor) FFTs, * averaged over the channel. * Please observe the notes on AggregatedFFTBlock.bins_peak when * interpreting the data. */ float peak_channel_power = 4; } /* Spectrum daemon service definition. Port 5306. */ service Spectrumd { /* Request the aggregated FFT properties, they do not change over time. * Returns ABORTED if desired RX channel is not available */ rpc GetAggregatedFFTProperties (AggregatedFFTRequest) returns (AggregatedFFTProperties); /* Request a stream of aggregated (avg/peak) FFT blocks. * Returns ABORTED if desired RX channel is not available */ rpc GetAggregatedFFTBlockStream (AggregatedFFTRequest) returns (stream AggregatedFFTBlock); /* Request a single waterfall plot as JPEG image. * Returns ABORTED if desired RX channel is not available */ rpc GetWaterfallJPEG (GetWaterfallJPEGRequest) returns (WaterfallJPEGImage); /* Request a stream of waterfall plots as JPEG images. * Returns ABORTED if desired RX channel is not available */ rpc GetWaterfallJPEGStream (GetWaterfallJPEGRequest) returns (stream WaterfallJPEGImage); /* Request a stream of channel occupancy measurements. * Returns ABORTED if desired RX channel is not available */ rpc GetChannelPowerStream (ChannelPowerRequest) returns (stream ChannelPower); } -------------------------------------------------------------------------------- 2.5 Samplestreamingd.proto -------------------------------------------------------------------------------- syntax = "proto3"; package serosystems.proto.v3.grx.samplestreamingd; option java_package = "de.serosystems.proto.v3.grx.samplestreamingd"; option java_outer_classname = "SamplestreamingDProto"; import "Common.proto"; /** * Message describing the properties of a stream. */ message StreamProperties { /* Center Frequency [Hz]. */ uint32 center_frequency = 1; /* Sample rate [Hz]. */ uint32 sample_rate = 2; /* Calibration value to convert from sample values to signal level in dBm. * Use the following formula to convert the squared magnitude (i.e. I^2 + Q^2) to a signal level in dBm: * signal_level [dBm] = calibration_value + 10 * log10(I^2 + Q^2) */ float calibration_value = 3; } /* Request type for GetStreamProperties call */ message GetStreamPropertiesRequest { /* Radio identification of the chosen RX channel */ RadioIdentification radio_identification = 1; } /* Request type for StartStream call */ message StartStreamRequest { /* Radio identification of the chosen RX channel */ RadioIdentification radio_identification = 1; /* The number of blocks that are requested, set to zero for indefinite streaming */ uint32 requested_blocks = 2; } /* Reply type for StartStream call */ message StartStreamReply { /* data is streamed in blocks, each block has a timestamp */ uint64 block_timestamp = 1; /* the samples */ bytes samples = 2; /* counter for lost blocks, can be used to find out a) if blocks have been lost and b) where exactly */ uint32 lost_blocks = 3; } /* Sample streaming daemon service definition. Port 5308. */ service Samplestreamingd { /* Request the stream properties. They usually do not change over time * (unless the radio front-end allows retuning/changing settings, see TunableChanneld). * Returns INVALID_ARGUMENT if chosen RX channel is not available. */ rpc GetStreamProperties (GetStreamPropertiesRequest) returns (StreamProperties); /* * Request a stream of samples. * Returns INVALID_ARGUMENT if chosen RX channel is not available */ rpc StartStream(StartStreamRequest) returns (stream StartStreamReply) {} } -------------------------------------------------------------------------------- 2.6 TunableChanneld.proto -------------------------------------------------------------------------------- syntax = "proto3"; import "google/protobuf/empty.proto"; package serosystems.proto.v3.grx.tunablechanneld; option java_package = "de.serosystems.proto.v3.grx.tunablechanneld"; option java_outer_classname = "TunableChannelDProto"; import "Common.proto"; /* Reply for the GetChannels call */ message GetChannelsReply { /* list of RadioIdentifications that show all (tunable) channels */ repeated RadioIdentification channels = 1; } /* Reply for the GetRXPorts call */ message GetRXPortsReply { /* Maps the port identifier to a descriptive label */ map rx_port_to_label = 1; } /* Request for the GetRXPorts call */ message GetRXPortsRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Request for the GetRXPort call */ message GetRXPortRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Reply for the GetRXPort call */ message GetRXPortReply { uint32 rx_port = 1; } /* Request for the SetRXPort call */ message SetRXPortRequest { /* identification of the channel */ RadioIdentification channel = 1; /* the requested RX port */ uint32 rx_port = 2; } /* Request for the GetCenterFrequency call */ message GetCenterFrequencyRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Reply for the GetCenterFrequency call */ message GetCenterFrequencyReply { uint32 center_frequency_hz = 1; } /* Request for the SetCenterFrequency call */ message SetCenterFrequencyRequest { /* identification of the channel */ RadioIdentification channel = 1; /* the requested center frequency [Hz] */ uint32 center_frequency_hz = 2; } /* Request for the GetAnalogBandwidth call */ message GetAnalogBandwidthRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Reply for the GetAnalogBandwidth call */ message GetAnalogBandwidthReply { /* the analog bandwidth [Hz] */ uint32 analog_bandwidth_hz = 1; } /* Request for the SetAnalogBandwidth call */ message SetAnalogBandwidthRequest { /* identification of the channel */ RadioIdentification channel = 1; /* requested analog bandwidth [Hz] */ uint32 analog_bandwidth_hz = 2; } /* Request for the GetSampleRate call */ message GetSampleRateRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Reply for the GetSampleRate call */ message GetSampleRateReply { /* the sample rate [Sps] */ uint32 sample_rate_sps = 1; } /* Request for the SetSampleRate call */ message SetSampleRateRequest { /* identification of the channel */ RadioIdentification channel = 1; /* requested sample rate [Sps] */ uint32 sample_rate_sps = 2; } /* Request for the GetHardwareGain call */ message GetHardwareGainRequest { /* identification of the channel */ RadioIdentification channel = 1; } /* Reply for the GetHardwareGain call */ message GetHardwareGainReply { /* the hardware gain [dB] */ int32 hardware_gain_db = 1; } /* Request for the SetHardwareGain call */ message SetHardwareGainRequest { /* identification of the channel */ RadioIdentification channel = 1; /* requested hardware gain [dB] */ uint32 hardware_gain_db = 2; } /* TunableChannel daemon service definition. Port 5309. */ service TunableChanneld { /* Get a list of all (tunable) channels */ rpc GetChannels(google.protobuf.Empty) returns (GetChannelsReply); /* Get a list of RX ports of the specified channel * Returns INVALID_ARGUMENT if request was invalid */ rpc GetRXPorts(GetRXPortsRequest) returns (GetRXPortsReply); /* Get the currently chosen RX port of the specified channel * Returns INVALID_ARGUMENT if request was invalid */ rpc GetRXPort(GetRXPortRequest) returns (GetRXPortReply); /* Choose the RX port of the specified channel * Returns INVALID_ARGUMENT if request was invalid */ rpc SetRXPort(SetRXPortRequest) returns (google.protobuf.Empty); /* Get the center frequency [Hz] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc GetCenterFrequency(GetCenterFrequencyRequest) returns (GetCenterFrequencyReply); /* Set the center frequency [Hz] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc SetCenterFrequency(SetCenterFrequencyRequest) returns (google.protobuf.Empty); /* Get the analog bandwidth [Hz] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc GetAnalogBandwidth(GetAnalogBandwidthRequest) returns (GetAnalogBandwidthReply); /* Set the analog bandwidth [Hz] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc SetAnalogBandwidth(SetAnalogBandwidthRequest) returns (google.protobuf.Empty); /* Get the sample rate [Sps] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc GetSampleRate(GetSampleRateRequest) returns (GetSampleRateReply); /* Set the sample rate [Sps] of the specified channel. * WARNING: changing the sample rate may interrupt the sampling process/data flow for a while * * Returns INVALID_ARGUMENT if request was invalid */ rpc SetSampleRate(SetSampleRateRequest) returns (google.protobuf.Empty); /* Get the hardware gain [dB] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc GetHardwareGain(GetHardwareGainRequest) returns (GetHardwareGainReply); /* Set the hardware gain [dB] of the specified channel. * Returns INVALID_ARGUMENT if request was invalid */ rpc SetHardwareGain(SetHardwareGainRequest) returns (google.protobuf.Empty); } ================================================================================ 3. PYTHON CLIENT SETUP ================================================================================ 3.1 Install dependencies ------------------------- pip install grpcio grpcio-tools Optional (for decoding Mode S payloads): pip install pyModeS Optional (for image display): pip install Pillow 3.2 Generate Python stubs from proto files ------------------------------------------- Download the .proto files from the GRX's built-in documentation at http:///doc/ and place them in a directory (e.g. ./protos/). Then run: python -m grpc_tools.protoc \ -I./protos \ --python_out=. \ --grpc_python_out=. \ ./protos/Common.proto \ ./protos/Receiverd.proto \ ./protos/Monitord.proto \ ./protos/Spectrumd.proto \ ./protos/Samplestreamingd.proto \ ./protos/TunableChanneld.proto This generates *_pb2.py (message classes) and *_pb2_grpc.py (stub classes). Run Python scripts from the same directory, or add the generated output directory to PYTHONPATH. If using the standalone-clients repository examples: cd python pip install -r requirements.txt bash gen_pb.sh python tracked_aircraft.py YOUR_GRX_IP 3.3 Connect and call --------------------- import grpc from google.protobuf.empty_pb2 import Empty # Import generated stubs import Receiverd_pb2_grpc import Receiverd_pb2 # Connect (no TLS) channel = grpc.insecure_channel("RECEIVER_IP:5303") stub = Receiverd_pb2_grpc.ReceiverdStub(channel) # Unary call example state_vectors = stub.GetStateVectors(Empty()) # Server-streaming call example request = Receiverd_pb2.GetModeSDownlinkFramesRequest( downlink_formats=[11, 17] ) for frame_info in stub.GetModeSDownlinkFrames(request): process(frame_info.frame) # Clean up channel.close() ================================================================================ 4. JAVA CLIENT SETUP ================================================================================ 4.1 Maven dependencies (pom.xml) --------------------------------- 1.79.0 4.34.0 io.grpc grpc-netty-shaded ${grpc.version} io.grpc grpc-protobuf ${grpc.version} io.grpc grpc-stub ${grpc.version} com.google.protobuf protobuf-java ${protobuf.version} javax.annotation javax.annotation-api 1.3.2 4.2 Proto files ----------------- Download the .proto files from the GRX's built-in documentation at http:///doc/ and place them in src/main/proto/. If using the standalone-clients repository examples, the java/src/main/proto/ directory already contains symlinks to the public v3 proto files. Build from the repository root with: cd java mvn clean package java -cp target/grx-client-*.jar de.serosystems.grx.example.TrackedAircraft YOUR_GRX_IP 4.3 protobuf-maven-plugin -------------------------- kr.motd.maven os-maven-plugin 1.7.1 org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} ${project.basedir}/src/main/proto compile compile-custom 4.4 Connect and call (Java) ---------------------------- import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import com.google.protobuf.Empty; // Connect (no TLS) ManagedChannel channel = ManagedChannelBuilder .forAddress("RECEIVER_IP", 5303) .usePlaintext() .build(); // Create a blocking stub for synchronous calls ReceiverdGrpc.ReceiverdBlockingStub stub = ReceiverdGrpc.newBlockingStub(channel); // Unary call StateVectorList svl = stub.getStateVectors(Empty.getDefaultInstance()); // Server-streaming call GetModeSDownlinkFramesRequest req = GetModeSDownlinkFramesRequest.newBuilder() .addDownlinkFormats(17) .build(); Iterator stream = stub.getModeSDownlinkFrames(req); while (stream.hasNext()) { ModeSDownlinkFrameWithStreamInfo info = stream.next(); // process info.getFrame() } channel.shutdown(); ================================================================================ 5. C++ CLIENT SETUP ================================================================================ 5.1 Proto files ----------------- Download the .proto files from the GRX's built-in documentation at http:///doc/ and place them in a protos/ directory. 5.2 CMake configuration ------------------------ cmake_minimum_required(VERSION 3.16) project(grx_client) find_package(Protobuf CONFIG REQUIRED) find_package(gRPC CONFIG REQUIRED) # Generate C++ sources from proto files set(PROTO_DIR "${CMAKE_SOURCE_DIR}/protos") set(PROTO_FILES ${PROTO_DIR}/Common.proto ${PROTO_DIR}/Receiverd.proto ${PROTO_DIR}/Monitord.proto ${PROTO_DIR}/Spectrumd.proto ${PROTO_DIR}/Samplestreamingd.proto ${PROTO_DIR}/TunableChanneld.proto ) # Use protobuf_generate and grpc_cpp_plugin add_library(grx_proto ${PROTO_FILES}) target_link_libraries(grx_proto PUBLIC protobuf::libprotobuf gRPC::grpc++ ) protobuf_generate(TARGET grx_proto LANGUAGE cpp) protobuf_generate( TARGET grx_proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=\$" ) add_executable(my_client main.cpp) target_link_libraries(my_client PRIVATE grx_proto) 5.3 Manual protoc invocation (alternative to CMake) ---------------------------------------------------- protoc -I./protos \ --cpp_out=./generated \ --grpc_out=./generated \ --plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) \ ./protos/*.proto 5.4 Connect and call (C++) --------------------------- #include #include "Receiverd.grpc.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::ClientReader; // Connect (no TLS) auto channel = grpc::CreateChannel( "RECEIVER_IP:5303", grpc::InsecureChannelCredentials() ); auto stub = serosystems::proto::v3::grx::receiverd::Receiverd::NewStub(channel); // Unary call ClientContext ctx; google::protobuf::Empty req; serosystems::proto::v3::grx::receiverd::StateVectorList reply; auto status = stub->GetStateVectors(&ctx, req, &reply); // Server-streaming call ClientContext ctx2; serosystems::proto::v3::grx::receiverd::GetModeSDownlinkFramesRequest dl_req; dl_req.add_downlink_formats(17); auto reader = stub->GetModeSDownlinkFrames(&ctx2, dl_req); serosystems::proto::v3::grx::receiverd::ModeSDownlinkFrameWithStreamInfo frame_info; while (reader->Read(&frame_info)) { // process frame_info.frame() } ================================================================================ 6. WORKING PYTHON EXAMPLES ================================================================================ The following are complete, tested example scripts. 6.1 List tracked aircraft (Receiverd - unary call) ---------------------------------------------------- #!/usr/bin/env python3 """ tracked_aircraft.py - List aircraft currently tracked by a GRX receiver. This example retrieves the list of all tracked aircraft (state vectors) from the Receiver API, including their position, altitude, callsign, and signal strength. Usage: python tracked_aircraft.py [HOST] Arguments: HOST IP address or hostname of the GRX (default: YOUR_GRX_IP) Prerequisites: pip install grpcio grpcio-tools Run gen_pb.sh to generate the Python bindings. """ import datetime import sys import grpc from google.protobuf.empty_pb2 import Empty # Import the generated gRPC stubs for the Receiver API import Receiverd_pb2_grpc # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP" RECEIVERD_PORT = 5303 # --------------------------------------------------------------------------- # Connect and retrieve state vectors # --------------------------------------------------------------------------- print(f"Connecting to {host}:{RECEIVERD_PORT}...") channel = grpc.insecure_channel(f"{host}:{RECEIVERD_PORT}") receiverd = Receiverd_pb2_grpc.ReceiverdStub(channel) airspace = receiverd.GetStateVectors(Empty()) # The current_timestamp field is in seconds since epoch (UTC) timestamp = datetime.datetime.fromtimestamp(airspace.current_timestamp) total = len(airspace.state_vectors) print(f"\nTracked {total} aircraft at {timestamp}\n") # --------------------------------------------------------------------------- # Print aircraft information # --------------------------------------------------------------------------- # Print a CSV-like header print(f"{'ICAO':>8s} {'Callsign':>8s} {'Lat':>10s} {'Lon':>11s} " f"{'Alt (ft)':>8s} {'Ground':>6s} {'RSS (dBm)':>9s}") print("-" * 75) for ac in airspace.state_vectors: # Format the ICAO 24-bit address as hex icao = f"{ac.qualified_address.address:06x}" # Callsign (may be empty if not yet received) callsign = ac.identification.strip() if ac.identification else "" # Position (0.0 if not available - check position_last_updated) lat = f"{ac.position.latitude:.4f}" if ac.position_last_updated else "" lon = f"{ac.position.longitude:.4f}" if ac.position_last_updated else "" # Barometric altitude in feet (0 if not available) alt = f"{ac.barometric_altitude:.0f}" if ac.barometric_altitude_last_updated else "" # On-ground flag ground = "yes" if ac.ground_flag else "no" # Average signal level of directly received transmissions rss = f"{ac.average_signal_level_directly_received:.1f}" print(f"{icao:>8s} {callsign:>8s} {lat:>10s} {lon:>11s} " f"{alt:>8s} {ground:>6s} {rss:>9s}") # --------------------------------------------------------------------------- # Clean up # --------------------------------------------------------------------------- channel.close() 6.2 Stream Mode S downlink frames (Receiverd - server streaming) ------------------------------------------------------------------ #!/usr/bin/env python3 """ modes_downlink.py - Stream Mode S downlink frames from a GRX receiver. This example subscribes to Mode S downlink frames (SSR replies and squitters) with optional downlink format filtering. It prints each received frame with its metadata. Usage: python modes_downlink.py [HOST] Arguments: HOST IP address or hostname of the GRX (default: YOUR_GRX_IP) Prerequisites: pip install grpcio grpcio-tools pyModeS Run gen_pb.sh to generate the Python bindings. """ import binascii import sys import grpc import pyModeS as pms # Import the generated gRPC stubs for the Receiver API import Receiverd_pb2 as receiverd_pb2 import Receiverd_pb2_grpc # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP" RECEIVERD_PORT = 5303 # Only subscribe to ADS-B (DF17) and All-Call replies (DF11). # Set to list(range(25)) for all downlink formats. DOWNLINK_FORMATS = [11, 17] # --------------------------------------------------------------------------- # Connect and subscribe # --------------------------------------------------------------------------- print(f"Connecting to {host}:{RECEIVERD_PORT}...") channel = grpc.insecure_channel(f"{host}:{RECEIVERD_PORT}") receiverd = Receiverd_pb2_grpc.ReceiverdStub(channel) # Build the subscription request with the desired downlink format filter. # The downlink_formats field controls which DFs are delivered in the stream. request = receiverd_pb2.GetModeSDownlinkFramesRequest( downlink_formats=DOWNLINK_FORMATS ) # GetModeSDownlinkFrames returns a server-side streaming RPC. # Each element in the stream is a ModeSDownlinkFrameWithStreamInfo message. stream = receiverd.GetModeSDownlinkFrames(request) print(f"Subscribed to Mode S downlink formats {DOWNLINK_FORMATS}. Press Ctrl+C to stop.\n") # --------------------------------------------------------------------------- # Process incoming frames # --------------------------------------------------------------------------- last_dropped = 0 try: for reply in stream: # Check for dropped frames (indicates network overload) if reply.frames_dropped > last_dropped: dropped = reply.frames_dropped - last_dropped print(f" ** WARNING: {dropped} frame(s) dropped (network overload) **") last_dropped = reply.frames_dropped frame = reply.frame # Convert raw payload bytes to hex string for decoding payload_hex = binascii.hexlify(frame.payload).decode("ascii") # Use pyModeS to extract the downlink format and ICAO address df = pms.df(payload_hex) icao = pms.icao(payload_hex) print(f"DF{df:2d} ICAO={icao} " f"Signal={frame.level_signal:.1f} dBm " f"Noise={frame.level_noise:.1f} dBm " f"CFO={frame.carrier_frequency_offset:+.0f} Hz " f"Payload={payload_hex}") except KeyboardInterrupt: print("\nStopped.") # --------------------------------------------------------------------------- # Clean up # --------------------------------------------------------------------------- stream.cancel() channel.close() 6.3 Stream raw I/Q samples (Samplestreamingd) ------------------------------------------------ #!/usr/bin/env python3 """ sample_streaming.py - Stream continuous I/Q samples from a GRX receiver. This example uses the Sample Streaming API to receive a continuous stream of raw I/Q samples from a specific radio channel. This is useful for generic frequency monitoring, recording, or feeding data into custom signal processing pipelines - similar to using an SDR. Usage: python sample_streaming.py [HOST] Arguments: HOST IP address or hostname of the GRX (default: YOUR_GRX_IP) Prerequisites: pip install grpcio grpcio-tools Run gen_pb.sh to generate the Python bindings. Note: Continuous I/Q streaming produces large amounts of data. At 12 MHz sample rate with 16-bit I/Q, the data rate is ~48 MB/s. Make sure your network link and processing pipeline can handle the throughput. Use the requested_blocks parameter to limit the stream duration. """ import struct import sys import grpc # Import the generated gRPC stubs for the Sample Streaming API and Common types import Common_pb2 as common_pb2 import Samplestreamingd_pb2 as samplestreamingd_pb2 import Samplestreamingd_pb2_grpc # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP" # The Sample Streaming API listens on TCP port 5308 SAMPLESTREAMINGD_PORT = 5308 # Identify the radio channel to stream from. # For a GRX1090, use Band=BAND_1090_MHZ, per_band_index=0. # Adjust for your specific receiver configuration. RADIO_ID = common_pb2.RadioIdentification( band=common_pb2.BAND_1090_MHZ, per_band_index=0, ) # Number of sample blocks to receive (0 = indefinite) NUM_BLOCKS = 10 # --------------------------------------------------------------------------- # Connect and query stream properties # --------------------------------------------------------------------------- print(f"Connecting to {host}:{SAMPLESTREAMINGD_PORT}...") channel = grpc.insecure_channel(f"{host}:{SAMPLESTREAMINGD_PORT}") samplestreamingd = Samplestreamingd_pb2_grpc.SamplestreamingdStub(channel) # Query the properties of this radio channel's sample stream props = samplestreamingd.GetStreamProperties( samplestreamingd_pb2.GetStreamPropertiesRequest( radio_identification=RADIO_ID ) ) print(f"\nStream Properties:") print(f" Center frequency: {props.center_frequency / 1e6:.3f} MHz") print(f" Sample rate: {props.sample_rate / 1e6:.3f} MSps") print(f" Calibration value: {props.calibration_value:.2f} dB") print(f" (Use: level_dBm = {props.calibration_value:.2f} + 10*log10(I^2 + Q^2))") # --------------------------------------------------------------------------- # Start streaming I/Q sample blocks # --------------------------------------------------------------------------- request = samplestreamingd_pb2.StartStreamRequest( radio_identification=RADIO_ID, requested_blocks=NUM_BLOCKS, # 0 for indefinite ) stream = samplestreamingd.StartStream(request) print(f"\nStreaming {NUM_BLOCKS} sample blocks...\n") total_samples = 0 total_lost = 0 try: for block in stream: # Each block contains raw I/Q samples as bytes (signed 16-bit interleaved) raw = block.samples num_samples = len(raw) // 4 # 4 bytes per I/Q pair (2x int16) total_samples += num_samples total_lost = block.lost_blocks # Parse the first few samples for display num_preview = min(3, num_samples) if num_preview > 0: pairs = struct.unpack(f"<{num_preview * 2}h", raw[:num_preview * 4]) preview = " ".join(f"({pairs[2*i]:+6d}, {pairs[2*i+1]:+6d})" for i in range(num_preview)) else: preview = "(empty)" print(f"Block: timestamp={block.block_timestamp} " f"samples={num_samples} " f"lost_blocks={block.lost_blocks} " f"preview={preview}") except KeyboardInterrupt: print("\nStopped.") except grpc.RpcError as e: if e.code() != grpc.StatusCode.CANCELLED: print(f"\ngRPC error: {e.details()}") # --------------------------------------------------------------------------- # Summary # --------------------------------------------------------------------------- print(f"\nTotal samples received: {total_samples}") print(f"Lost blocks: {total_lost}") channel.close() 6.4 Capture a waterfall spectrogram (Spectrumd) -------------------------------------------------- #!/usr/bin/env python3 """ spectrum_waterfall.py - Capture a waterfall spectrogram from a GRX receiver. This example retrieves the FFT parameters and then requests a waterfall JPEG image from the Spectrum API. The resulting image shows signal activity over time across the monitored frequency band. Usage: python spectrum_waterfall.py [HOST] [CHANNEL] Arguments: HOST IP address or hostname of the GRX (default: YOUR_GRX_IP) CHANNEL RX channel index, starting from 0 (default: 0) Prerequisites: pip install grpcio grpcio-tools Pillow Run gen_pb.sh to generate the Python bindings. """ import datetime import io import sys import grpc import PIL.Image # Import the generated gRPC stubs for the Spectrum API import Spectrumd_pb2 as spectrumd_pb2 import Spectrumd_pb2_grpc # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP" rx_channel = int(sys.argv[2]) if len(sys.argv) >= 3 else 0 # The Spectrum API listens on TCP port 5306 SPECTRUMD_PORT = 5306 # Duration of the waterfall capture in seconds CAPTURE_DURATION = 10 # --------------------------------------------------------------------------- # Connect to the GRX receiver # --------------------------------------------------------------------------- print(f"Connecting to {host}:{SPECTRUMD_PORT}...") channel = grpc.insecure_channel(f"{host}:{SPECTRUMD_PORT}") spectrumd = Spectrumd_pb2_grpc.SpectrumdStub(channel) # --------------------------------------------------------------------------- # Query FFT parameters # --------------------------------------------------------------------------- # Retrieve the static FFT aggregation parameters for this channel. # These tell us the center frequency, sample rate, FFT size, and how # many FFT blocks are aggregated into a single line of the waterfall. params = spectrumd.GetAggregatedFFTProperties( spectrumd_pb2.AggregatedFFTRequest(rx_channel_index=rx_channel) ) # Duration of a single waterfall line line_duration = params.aggregation_factor * params.fft_size / params.sample_rate num_lines = int(CAPTURE_DURATION / line_duration) print(f"\nFFT Parameters:") print(f" Center frequency: {params.center_frequency / 1e6:.3f} MHz") print(f" Sample rate: {params.sample_rate / 1e6:.3f} MSps") print(f" FFT size: {params.fft_size}") print(f" Aggregation factor: {params.aggregation_factor}") print(f" Line duration: {line_duration * 1000:.1f} ms") print(f"\nCapturing {num_lines} lines ({num_lines * line_duration:.1f} s)...") # --------------------------------------------------------------------------- # Request waterfall JPEG # --------------------------------------------------------------------------- # Build the request with reasonable display settings. # min_level / max_level define the color scale range in dBm. # Adjust these values depending on your signal environment. request = spectrumd_pb2.GetWaterfallJPEGRequest( num_lines=num_lines, min_level=-180.0, max_level=-90.0, jpeg_quality=90, aggregation_type=spectrumd_pb2.GetWaterfallJPEGRequest.AVERAGE, rx_channel_index=rx_channel, ) # This call blocks until all lines have been collected result = spectrumd.GetWaterfallJPEG(request) # --------------------------------------------------------------------------- # Display the image # --------------------------------------------------------------------------- image = PIL.Image.open(io.BytesIO(result.image)) start_time = datetime.datetime.fromtimestamp(result.timestamp - num_lines * line_duration) end_time = datetime.datetime.fromtimestamp(result.timestamp) print(f"\nWaterfall captured: {start_time} to {end_time}") print(f"Image size: {image.size[0]} x {image.size[1]} pixels") # Show the image using the system's default image viewer image.show(title=f"GRX Waterfall - {params.center_frequency / 1e6:.1f} MHz") # --------------------------------------------------------------------------- # Clean up # --------------------------------------------------------------------------- channel.close() 6.5 Query device radio front-end status (Receiverd) ----------------------------------------------------- #!/usr/bin/env python3 """ device_status.py - Retrieve radio front-end status from a GRX receiver. This example connects to a GRX receiver and queries the status of all radio front-ends, including gain settings, noise levels, and antenna information. Usage: python device_status.py [HOST] Arguments: HOST IP address or hostname of the GRX (default: YOUR_GRX_IP) Prerequisites: pip install grpcio grpcio-tools Run gen_pb.sh to generate the Python bindings. """ import sys import grpc from google.protobuf.empty_pb2 import Empty # Import the generated gRPC stubs for the Receiver API import Receiverd_pb2_grpc # --------------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------------- # GRX receiver address (override with command-line argument) host = sys.argv[1] if len(sys.argv) >= 2 else "YOUR_GRX_IP" # The Receiver API listens on TCP port 5303 RECEIVERD_PORT = 5303 # --------------------------------------------------------------------------- # Connect to the GRX receiver # --------------------------------------------------------------------------- print(f"Connecting to {host}:{RECEIVERD_PORT}...") channel = grpc.insecure_channel(f"{host}:{RECEIVERD_PORT}") receiverd = Receiverd_pb2_grpc.ReceiverdStub(channel) # --------------------------------------------------------------------------- # Query radio front-end status # --------------------------------------------------------------------------- status = receiverd.GetRadioFrontEndStatus(Empty()) # Print radio front-end information print(f"\nThe receiver has {len(status.radio_front_end_status)} radio front-end(s).\n") for rf in status.radio_front_end_status: # The 'band' field is a numeric enum value; we print its name for clarity print(f" Radio front-end (band={rf.band}):") print(f" Fixed gain (internal): {rf.rx_chain_fixed_gain} dB") print(f" Fixed gain (external): {rf.external_fixed_gain} dB") print(f" Noise level: {rf.noise_level:.1f} dBm") print() # Print antenna information print(f"The receiver has {len(status.antenna_status)} antenna port(s).\n") for ant_id, ant in status.antenna_status.items(): supply = "not available" if ant.has_supply_enable: supply = "enabled" if ant.supply_enable else "disabled" print(f" Antenna '{ant_id}' ({ant.label}):") print(f" Power supply: {supply}") print() # --------------------------------------------------------------------------- # Clean up # --------------------------------------------------------------------------- channel.close() ================================================================================ 7. KEY PATTERNS AND IDIOMS ================================================================================ 7.1 Unary (request-response) calls ------------------------------------ Most getter methods are simple unary calls. Many take google.protobuf.Empty as the request (no parameters needed): from google.protobuf.empty_pb2 import Empty result = stub.GetStateVectors(Empty()) result = stub.GetStatistics(Empty()) result = stub.GetRadioFrontEndStatus(Empty()) Some take a specific request message: result = stub.GetStateVectorHistory( GetStateVectorHistoryRequest(address=qualified_address) ) 7.2 Server-streaming calls --------------------------- Streaming RPCs return an iterator. Iterate over it to receive messages. Cancel the stream or break out of the loop to stop: stream = stub.GetModeSDownlinkFrames(request) try: for msg in stream: process(msg) except KeyboardInterrupt: stream.cancel() 7.3 RadioIdentification ------------------------ Several services require identifying which radio channel to use. This is done with the RadioIdentification message from Common.proto: import Common_pb2 as common_pb2 radio_id = common_pb2.RadioIdentification( band=common_pb2.BAND_1090_MHZ, # or BAND_978_MHZ, BAND_1030_MHZ, BAND_TUNABLE per_band_index=0, # 0 for the first radio of that band ) Common band values: - BAND_1090_MHZ (3): Mode S downlink / ADS-B - BAND_1030_MHZ (2): Mode S uplink / interrogations - BAND_978_MHZ (1): UAT - BAND_TUNABLE (128): SDR tunable channel - BAND_FIXED (127): Fixed non-standard frequency 7.4 google.protobuf.Empty -------------------------- Many Monitord and Receiverd calls take Empty as input. Import it as: Python: from google.protobuf.empty_pb2 import Empty Java: com.google.protobuf.Empty.getDefaultInstance() C++: google::protobuf::Empty() 7.5 Handling dropped frames/messages ------------------------------------- All streaming messages include a *WithStreamInfo wrapper that contains a frames_dropped or messages_dropped counter. This is a monotonically increasing counter. If it increases between consecutive messages, some messages were dropped due to network or system overload. Monitor this to detect data loss: last_dropped = 0 for msg in stream: if msg.frames_dropped > last_dropped: print(f"Lost {msg.frames_dropped - last_dropped} frames!") last_dropped = msg.frames_dropped ================================================================================ 8. IMPORTANT DOMAIN-SPECIFIC NOTES ================================================================================ 8.1 Timestamps --------------- - Receiverd frame/message timestamps are in NANOSECONDS. - The timing_base field tells you how to interpret the timestamp: - GPS_TOW (2): GPS nanosecond-of-week. This rolls over every week (604,800,000,000,000 ns). NOT a Unix timestamp. - SYSTEM_TIME (1): Monotonic system uptime in ns (51-bit, rolls every ~13 days). - NO_BASE (0): No valid time reference. - The timing_sync_source tells you the quality: - GNSS (1): Synchronized to GNSS (best accuracy, typically < 100 ns). - ATOMIC_CLOCK (2): Synchronized to local atomic clock. - NO_SOURCE (0): Not synchronized. - StateVector timestamps (last_seen, *_last_update) and Monitord timestamps are in SECONDS since Unix epoch UTC. 8.2 I/Q sample format ----------------------- Raw I/Q samples from Samplestreamingd (and optional frame samples from Receiverd) are interleaved signed integers: [I0, Q0, I1, Q1, I2, Q2, ...] The most common format is S16I_S16Q (signed 16-bit): - Each I/Q pair is 4 bytes (2 bytes I + 2 bytes Q), little-endian. - Parse in Python: struct.unpack("<2h", data[offset:offset+4]) -> (I, Q) Other formats (less common): - S12I_S12Q: 12-bit signed, packed into 32 bits - S8I_S8Q: 8-bit signed, 2 bytes per pair 8.3 Signal level calibration ----------------------------- To convert raw I/Q sample magnitudes to absolute power in dBm: level_dBm = calibration_value + 10 * log10(I^2 + Q^2) The calibration_value is obtained from: - Samplestreamingd: StreamProperties.calibration_value - Receiverd frame samples: the level_signal field is already calibrated 8.4 Mode 1/2/3/A/C binning --------------------------- Mode 1/2/3/A/C (SSR secondary radar) generates very high message rates. The API groups receptions of the same code within ~100 ms windows into "bins". Each ModeACDownlinkBinnedFrame contains: - The Mode 1/2/3/A/C code (0-4095) - An array of individual receptions with timestamps and signal levels Use the BinningMode in the request to filter: - ALL: every bin (high volume) - AT_LEAST_2RECEPTIONS: only codes seen 2+ times per bin (reduces noise) - AT_LEAST_2RECEPTIONS_OR_RECENT: 2+ receptions or recently seen code 8.5 State vector field validity -------------------------------- StateVector fields use a companion *_last_update timestamp. If that timestamp is 0, the field value is NOT valid (not yet received or too old). Always check before using: if sv.position_last_update > 0: lat, lon = sv.position.latitude, sv.position.longitude For StateVectorHistory.DataPoint, use the *_valid boolean flags instead. 8.6 Proto package namespaces ----------------------------- Each proto file uses a different package: Common.proto: serosystems.proto.v3.grx Receiverd.proto: serosystems.proto.v3.grx.receiverd Monitord.proto: serosystems.proto.v3.grx.monitord Spectrumd.proto: serosystems.proto.v3.grx.spectrumd Samplestreamingd.proto: serosystems.proto.v3.grx.samplestreamingd TunableChanneld.proto: serosystems.proto.v3.grx.tunablechanneld In C++ and Java, these translate to nested namespaces / packages. In Python, the generated _pb2.py files handle this transparently. 8.7 Receiverd Band enum note ----------------------------- Receiverd.proto defines its OWN Band enum (identical to Common.proto's). This is a legacy artifact. When working with Receiverd messages that contain a Band field (e.g. RadioFrontEndStatus.band), use the enum from Receiverd_pb2, not Common_pb2. For other services (Spectrumd, Samplestreamingd, TunableChanneld), use Common_pb2. 8.8 Python protobuf enum access --------------------------------- In Python, how you access a protobuf enum depends on where it is declared in the .proto file: - TOP-LEVEL enum (declared outside any message): Access it from the generated *_pb2 module directly. # Band is top-level in Receiverd.proto / Common.proto Receiverd_pb2.Band.Name(rf.band) # CORRECT Receiverd_pb2.BAND_1090_MHZ # CORRECT Common_pb2.Band.Name(value) # CORRECT - NESTED enum (declared inside a message): Access it from the message class. # FixType is nested inside GNSSInformation.Position Monitord_pb2.GNSSInformation.Position.FixType.Name(pos.fix_type) WRONG patterns (will fail): type(rf).Band.Name(rf.band) # WRONG for top-level enums RadioFrontEndStatus.Band.Name(rf.band) # WRONG for top-level enums When generating Python code, always check the .proto file to determine whether the enum is declared at top level or nested inside a message before choosing the access pattern. 8.9 TunableChanneld usage -------------------------- TunableChanneld is only relevant for receivers that have a tunable SDR channel (Band = BAND_TUNABLE). Typical workflow: 1. GetChannels() -> list of tunable RadioIdentifications 2. SetCenterFrequency(channel, freq_hz) 3. SetSampleRate(channel, rate_sps) 4. SetAnalogBandwidth(channel, bw_hz) 5. SetHardwareGain(channel, gain_db) 6. Use Samplestreamingd or Spectrumd to read data from this channel WARNING: Changing sample rate may temporarily interrupt data flow. 8.10 gRPC error codes --------------------- Common error codes returned by the services: - UNAVAILABLE: The backend service (e.g. receiverd) could not be reached. - DEADLINE_EXCEEDED: The service did not reply in time. - INVALID_ARGUMENT: The request parameters are invalid (e.g. bad DF > 24). - ABORTED: The requested resource is not available (e.g. no FFT data for the specified RX channel, or GNSS info is outdated). ================================================================================ 9. QUICK REFERENCE: SERVICE METHOD SUMMARY ================================================================================ Receiverd (port 5303): GetStateVectors(Empty) -> StateVectorList [unary] GetStateVectorHistory(GetStateVectorHistoryRequest) -> StateVectorHistory [unary] GetRangeChart(Empty) -> RangeChart [unary] ResetRangeChart(Empty) -> Empty [unary] GetStatistics(Empty) -> Statistics [unary] GetSamplesStatistics(Empty) -> SamplesStatistics [unary] GetRadioFrontEndStatus(Empty) -> GetRadioFrontEndStatusReply [unary] RecalibrateDCOffset(Empty) -> Empty [unary] GetModeSDownlinkFrames(req) -> stream ModeSDownlinkFrameWithStreamInfo GetModeSUplinkFrames(req) -> stream ModeSUplinkFrameWithStreamInfo GetModeACDownlinkFrames(req) -> stream ModeACDownlinkBinnedFrameWithStreamInfo GetUATADSBMessages(req) -> stream UATADSBMessageWithStreamInfo GetUATGroundUplinkMessages(req) -> stream UATGroundUplinkMessageWithStreamInfo GetDMETACANPulsePairs(req) -> stream DMETACANPulsePairWithStreamInfo GetIsolatedPulses(req) -> stream IsolatedPulseWithStreamInfo GetMode4Interrogations(req) -> stream Mode4InterrogationWithStreamInfo GetMode4Replies(req) -> stream Mode4ReplyWithStreamInfo GetMode5Interrogations(req) -> stream Mode5InterrogationWithStreamInfo GetMode5Replies(req) -> stream Mode5ReplyWithStreamInfo GetMode123ACInterrogations(req) -> stream Mode123ACInterrogationWithStreamInfo Monitord (port 5305): GetCPUUsage(Empty) -> CPUUsage [unary] GetCPUUsageHistory(Empty) -> GetCPUUsageHistoryReply [unary] GetMemoryUsage(Empty) -> MemoryUsage [unary] GetMemoryUsageHistory(Empty) -> GetMemoryUsageHistoryReply [unary] GetSwapUsage(Empty) -> SwapUsage [unary] GetSwapUsageHistory(Empty) -> GetSwapUsageHistoryReply [unary] GetMountedFilesystemUsage(Empty) -> GetMountedFilesystemUsageReply [unary] GetProcessList(Empty) -> GetProcessListReply [unary] GetSystemPackages(Empty) -> GetSystemPackagesReply [unary] GetLogMessages(GetLogMessagesRequest) -> GetLogMessagesReply [unary] GetUnitList(Empty) -> GetUnitListReply [unary] GetNetworkUsage(Empty) -> GetNetworkUsageReply [unary] GetNetworkUsageHistory(Empty) -> GetNetworkUsageHistoryReply [unary] GetNetworkCounters(Empty) -> GetNetworkCountersReply [unary] GetSystemHealth(Empty) -> SystemHealth [unary] GetSystemHealthHistory(Empty) -> GetSystemHealthHistoryReply [unary] GetSystemLoad(Empty) -> SystemLoad [unary] GetSystemLoadHistory(Empty) -> GetSystemLoadHistoryReply [unary] GetSystemInformation(Empty) -> SystemInformation [unary] ClearResetReasons(Empty) -> Empty [unary] GetGNSSInformation(Empty) -> GNSSInformation [unary] GetFullSystemStatus(Empty) -> GetFullSystemStatusReply [unary] Spectrumd (port 5306): GetAggregatedFFTProperties(AggregatedFFTRequest) -> AggregatedFFTProperties [unary] GetAggregatedFFTBlockStream(AggregatedFFTRequest) -> stream AggregatedFFTBlock GetWaterfallJPEG(GetWaterfallJPEGRequest) -> WaterfallJPEGImage [unary] GetWaterfallJPEGStream(GetWaterfallJPEGRequest) -> stream WaterfallJPEGImage GetChannelPowerStream(ChannelPowerRequest) -> stream ChannelPower Samplestreamingd (port 5308): GetStreamProperties(GetStreamPropertiesRequest) -> StreamProperties [unary] StartStream(StartStreamRequest) -> stream StartStreamReply TunableChanneld (port 5309): GetChannels(Empty) -> GetChannelsReply [unary] GetRXPorts(GetRXPortsRequest) -> GetRXPortsReply [unary] GetRXPort(GetRXPortRequest) -> GetRXPortReply [unary] SetRXPort(SetRXPortRequest) -> Empty [unary] GetCenterFrequency(GetCenterFrequencyRequest) -> GetCenterFrequencyReply [unary] SetCenterFrequency(SetCenterFrequencyRequest) -> Empty [unary] GetAnalogBandwidth(GetAnalogBandwidthRequest) -> GetAnalogBandwidthReply [unary] SetAnalogBandwidth(SetAnalogBandwidthRequest) -> Empty [unary] GetSampleRate(GetSampleRateRequest) -> GetSampleRateReply [unary] SetSampleRate(SetSampleRateRequest) -> Empty [unary] GetHardwareGain(GetHardwareGainRequest) -> GetHardwareGainReply [unary] SetHardwareGain(SetHardwareGainRequest) -> Empty [unary] ================================================================================ End of document. ================================================================================