Architecture

This page explains how gr-satellites works internally: how the gr_satellites command turns a SatYAML file into a running GNU Radio flowgraph, how frames flow from the antenna to the data sinks, and how options and metadata are passed along. It is meant for contributors and for users who want to understand why the tool behaves as it does. The core of it is python/core/gr_satellites_flowgraph.py and apps/gr_satellites.

Layers of the Project

Layer Where Role
Command line tool apps/gr_satellites Argument parsing, input sources, top block.
Core flowgraph python/core/gr_satellites_flowgraph.py Builds the decoder from a SatYAML file.
SatYAML files python/satyaml/*.yml One description per satellite.
Components python/components/ Demodulators, deframers, transports, data sinks, data sources.
Hierarchical helpers python/hier/ Reusable sub-flowgraphs: syncword-to-PDU, CCSDS descrambler and Viterbi, PN9 scramblers, RMS AGC.
Low-level blocks lib/ (C++), python/*.py FEC decoders (Reed-Solomon, Viterbi, Golay, RA), HDLC, KISS, NRZ-I, CRCs, submitters...
Telemetry and file formats python/telemetry/, python/filereceiver/ construct definitions and file reassembly.
GRC definitions grc/ YAML block definitions for GNU Radio Companion.

From Command Line to Flowgraph

When you run gr_satellites FUNcube-1 --wavfile ao73.wav:

  1. Early exits. If the first argument is --version or --list_satellites, it is handled and the program exits. If there are no arguments or the first starts with -, the usage is printed (status 1).
  2. Satellite resolution. sys.argv[1] becomes {'file': ...}, {'norad': ...} or {'name': ...} and yamlfiles.open_satyaml() loads the file (by path, or by scanning all bundled files).
  3. Option registration. gr_satellites_flowgraph.add_options() creates the demodulation, deframing and data sink argument groups and calls the class method add_options() of every component referenced by the SatYAML file: one per data entry type, and one demodulator and deframer per transmitter. The parser uses conflict_handler='resolve', so a repeated option replaces the earlier one.
  4. Parsing. If --satcfg is present, the NORAD ID's line from sat.cfg is prepended. Arguments are parsed strictly, or with parse_known_args() under --ignore_unknown_args. check_options() enforces --samp_rate where needed.
  5. Configuration. open_config() loads ~/.gr_satellites/config.ini, creating it if missing.
  6. Input. setup_input() creates the source blocks for the chosen input (see below).
  7. Sample rate. --samp_rate if given, else the WAV header rate.
  8. Decoder. A gr_satellites_flowgraph is created with the options namespace and the config.
  9. Connection. For sample inputs: source → (Throttle if --throttle) → Multiply Const (--input_gain) → decoder. For --kiss_in: KISS File Source message port → decoder in port.
  10. Run. Signal handlers for SIGINT/SIGTERM are installed; tb.start() and tb.wait().

Input source construction

Option Blocks
--wavfile WAV File Source; with --iq, Float To Complex from channels 0 and 1.
--rawfile File Source of float or complex.
--rawint16 File Source of short → Short To Float (scale 32767) → with --iq, Deinterleave + Float To Complex.
--audio Audio Source (with --iq, two channels to Float To Complex).
--udp Network UDP Source (payload 1472 bytes) of short (then as --rawint16) or, with --udp_raw, float/complex.
--sigmf Metadata checks, then the --rawint16 path with IQ forced.
--kiss_in kiss_file_source (File Source → KISS to PDU).

Inside gr_satellites_flowgraph

The decoder is a hierarchical block. Its constructor:

  1. Parses string options (GRC case) with a parser built by add_options().
  2. Loads the SatYAML file.
  3. In GRC mode (grc_block=True), registers the out message port and skips all data sinks.
  4. Otherwise builds the data sinks:
    • one per data entry (_init_datasink), except telemetry and unknown entries when --hexdump is set;
    • the additional data sinks (_init_additional_datasinks): KISS file, KISS server, ZMQ PUB, telemetry submitters (SatNOGS plus telemetry_servers, if enabled by GR_SATELLITES_SUBMIT_TLM or submit_tlm), and the hex dump sink for --hexdump;
    • one transport per transports entry (_init_transport), connected to its data sinks unless --hexdump.
  5. For PDU input, connects the in port to all data sinks and additional data sinks. Transports are not used.
  6. For sample input, for each transmitter (_init_demodulator_deframer):
    • picks the demodulator class from _demodulator_hooks[modulation], passing baudrate, samp_rate, iq, dump_path, the options, and any deviation, fm_deviation, af_carrier from the SatYAML entry;
    • picks the deframer class from _deframer_hooks[framing], passing the options and any frame size, precoding, RS basis, RS interleaving, convolutional, scrambler, callsign (translated to Python argument names);
    • connects input → demodulator → deframer;
    • adds a pdu_add_meta tagger that inserts transmitter: <name> into each PDU's metadata;
    • connects the tagger to the out port (GRC mode), or to all additional data sinks, the transmitter's data sinks and its transports;
    • for each additional_data entry, connects that deframer port through its own tagger to the named data sink only.

The hooks are dictionaries of classes. Variants are created with set_options(), which derives a class with some constructor arguments fixed (for example 'AX.25 G3RUH' is ax25_deframer with g3ruh_scrambler=True, and 'DBPSK' is bpsk_demodulator with differential=True).

Frame flow for a satellite with a transport

                        ┌─► KISS file / KISS server / ZMQ / submitters / hexdump
samples ─► demod ─► deframer ─► tagger ─┤
                        ├─► data sinks listed in transmitter 'data'
                        └─► transport ─► data sinks listed in transport 'data'

Two consequences follow, both visible to users:

  • KISS output, ZMQ, hex dump and submission always see deframer output, never transport output.
  • With --kiss_in, frames reach the data sinks without passing through transports.

Options Flow

All components share one argparse namespace in the command line tool. Each component accesses self.options.<name>, and explicit constructor arguments (from SatYAML fields or from GRC parameters) take precedence over the namespace. Components inherit options_block, which, when given None or a string instead of a namespace, builds its own parser from its add_options() and parses the string. That is what makes the Command line options parameter of every GRC block work.

Deframer Building Blocks

Most deframers are built from a few shared pieces:

Piece Purpose
digital.binary_slicer_fb Soft symbols to bits.
sync_to_pdu, sync_to_pdu_packed, sync_to_pdu_soft (python/hier/) Correlate a syncword with a bit-error threshold and cut fixed-length packets into PDUs.
distributed_syncframe_soft Distributed syncwords (AO-40 FEC).
hdlc_deframer HDLC flags, bit de-stuffing and FCS check (AX.25 family).
nrzi_decode, G3RUH descrambler_bb, ccsds_descrambler, PN9 scramblers Line coding and scrambling.
decode_rs, ccsds_viterbi, fec.cc_decoder, Golay, RA and BCH decoders Forward error correction.
crc16_arc, crc_check and others in crcs.py Integrity checks.

New deframers are usually a short hierarchical block chaining these, plus an add_options() class method for their syncword threshold and verbosity flags.

See Also