Skip to content

Block Reference

Every component in gr-apollo falls into one of two categories: GNU Radio blocks that require the gnuradio runtime, and pure-Python engines that work standalone. The engines power the GR blocks internally, but you can also use them directly for testing, scripting, or integration without GNU Radio.


Module: apollo.usb_signal_gen Type: Pure-Python function (numpy) Purpose: Generate a synthetic Apollo USB downlink complex baseband signal for testing.

from apollo.usb_signal_gen import generate_usb_baseband
signal, frame_bits = generate_usb_baseband(frames=10, snr_db=20.0)
def generate_usb_baseband(
frames: int = 1,
bit_rate: float = 51_200,
sample_rate: float = 5_120_000,
pm_deviation: float = 0.133,
voice_enabled: bool = False,
voice_tone_hz: float = 1000.0,
snr_db: float | None = None,
frame_data: list[bytes] | None = None,
) -> tuple[np.ndarray, list[list[int]]]
ParameterTypeDefaultDescription
framesint1Number of PCM frames to generate
bit_ratefloat51200PCM bit rate in bps. Use 51200 for high rate, 1600 for low rate
sample_ratefloat5120000Output sample rate in Hz
pm_deviationfloat0.133Peak PM deviation in radians (7.6 degrees)
voice_enabledboolFalseInclude 1.25 MHz FM voice subcarrier with test tone
voice_tone_hzfloat1000.0Audio frequency of the voice test tone in Hz
snr_dbfloat | NoneNoneIf set, add AWGN at this signal-to-noise ratio (dB). None means no noise
frame_datalist[bytes] | NoneNoneOptional per-frame payload bytes. None generates random data

A tuple of (signal, frame_bits):

  • signal: np.ndarray of complex64 — the complex baseband IQ samples
  • frame_bits: list[list[int]] — the transmitted bit sequences per frame (for verification)

The module also exposes the individual stages of signal generation:

FunctionSignatureReturns
generate_pcm_frame(frame_id, odd, data, words_per_frame) -> list[int]Bit list (length = words_per_frame * 8)
generate_nrz_waveform(bits, bit_rate, sample_rate) -> np.ndarrayFloat32 NRZ samples (+1.0 / -1.0)
generate_bpsk_subcarrier(nrz_data, subcarrier_freq, sample_rate) -> np.ndarrayFloat32 BPSK subcarrier samples
generate_fm_voice_subcarrier(n_samples, sample_rate, tone_freq, subcarrier_freq, fm_deviation) -> np.ndarrayFloat32 FM subcarrier samples

Module: apollo.pm_demod Type: gr.hier_block2 Purpose: Extract phase modulation from complex baseband using a carrier-tracking PLL.

from apollo.pm_demod import pm_demod
blk = pm_demod(carrier_pll_bw=0.02, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputcomplexComplex baseband IQ samples
out0OutputfloatDemodulated composite signal containing all subcarriers
ParameterTypeDefaultDescription
carrier_pll_bwfloat0.02PLL loop bandwidth in rad/sample. Narrower tracks better, wider acquires faster
sample_ratefloat5120000Input sample rate in Hz
MethodSignatureDescription
get_carrier_pll_bw() -> floatRead current PLL loop bandwidth
set_carrier_pll_bw(bw: float) -> NoneUpdate PLL loop bandwidth at runtime

input -> pll_carriertracking_cc -> complex_to_arg -> output


Module: apollo.subcarrier_extract Type: gr.hier_block2 Purpose: Bandpass-filter and frequency-translate a subcarrier to complex baseband.

from apollo.subcarrier_extract import subcarrier_extract
blk = subcarrier_extract(center_freq=1_024_000, bandwidth=150_000)
PortDirectionTypeDescription
in0InputfloatPM demodulator output (composite subcarrier signal)
out0OutputcomplexBaseband subcarrier signal (decimated)
ParameterTypeDefaultDescription
center_freqfloat1024000Subcarrier center frequency in Hz
bandwidthfloat150000Passband bandwidth in Hz. Transition band is 20% of this value
sample_ratefloat5120000Input sample rate in Hz
decimationint1Output decimation factor
PropertyTypeDescription
output_sample_ratefloatEffective output rate: sample_rate / decimation

input -> float_to_complex -> freq_xlating_fir_filter_ccc -> output

The filter taps are designed as a Hamming-windowed lowpass at bandwidth / 2 cutoff with 20% transition width.


Module: apollo.bpsk_demod Type: gr.hier_block2 Purpose: Recover NRZ bits from a BPSK baseband signal using Costas loop carrier recovery and Mueller-Muller symbol sync.

from apollo.bpsk_demod import bpsk_demod
blk = bpsk_demod(symbol_rate=51_200, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputcomplexBaseband BPSK signal (from subcarrier_extract)
out0OutputbyteRecovered NRZ bits (0 or 1), one per symbol period
ParameterTypeDefaultDescription
symbol_ratefloat51200Symbol (bit) rate in Hz
sample_ratefloat5120000Input sample rate in Hz
loop_bwfloat0.045Costas loop bandwidth in rad/sample

input -> costas_loop_cc(order=2) -> symbol_sync_cc(TED_MUELLER_AND_MULLER) -> complex_to_real -> binary_slicer_fb -> output


Module: apollo.bpsk_subcarrier_demod Type: gr.hier_block2 Purpose: Convenience wrapper combining subcarrier_extract and bpsk_demod for the 1.024 MHz PCM subcarrier.

from apollo.bpsk_subcarrier_demod import bpsk_subcarrier_demod
blk = bpsk_subcarrier_demod(sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputfloatPM demodulator output (composite subcarrier signal)
out0OutputbyteRecovered NRZ bits (0 or 1)
ParameterTypeDefaultDescription
subcarrier_freqfloat1024000PCM subcarrier frequency in Hz (PCM_SUBCARRIER_HZ)
bandwidthfloat150000Bandpass filter bandwidth in Hz (PCM_BPF_BW_HZ)
bit_ratefloat51200PCM bit rate in Hz (PCM_HIGH_BIT_RATE)
sample_ratefloat5120000Input sample rate in Hz
decimationint1Subcarrier extraction decimation factor
loop_bwfloat0.045BPSK Costas loop bandwidth

input -> subcarrier_extract -> bpsk_demod -> output


Module: apollo.pcm_frame_sync Type: Pure-Python class Purpose: Acquire and track the 32-bit PCM sync pattern from an NRZ bit stream using a three-state machine (SEARCH, VERIFY, LOCKED).

from apollo.pcm_frame_sync import FrameSyncEngine
engine = FrameSyncEngine(bit_rate=51_200, max_bit_errors=3)
frames = engine.process_bits([1, 0, 1, 1, ...])
ParameterTypeDefaultDescription
bit_rateint51200PCM bit rate: 51200 (high) or 1600 (low). Determines words per frame
max_bit_errorsint3Maximum Hamming distance for a sync pattern match
verify_countint2Consecutive frame-boundary hits to transition VERIFY to LOCKED
miss_limitint3Consecutive frame-boundary misses to drop LOCKED to SEARCH
a_bitsint0b101015-bit patchboard-selectable A field
coreint0b11100110101110015-bit fixed core (even-frame value)
b_bitsint0b1101006-bit patchboard-selectable B field
MethodSignatureDescription
process_bits(bits: list[int]) -> list[dict]Feed bits into the engine. Returns list of completed frame dicts
reset() -> NoneReset to SEARCH state, clear all buffers
PropertyTypeDescription
state_namestrCurrent state: "SEARCH", "VERIFY", or "LOCKED"
stateintNumeric state: 0 (SEARCH), 1 (VERIFY), 2 (LOCKED)
bits_per_frameintTotal bits per frame (1024 for high rate, 1600 for low rate)
words_per_frameintWords per frame (128 or 200)

Each frame returned by process_bits contains:

KeyTypeDescription
frame_idintFrame number within subframe (1-50)
odd_frameboolWhether the frame has a complemented sync core
sync_confidenceintNumber of correct bits in the 32-bit sync word (32 - hamming_distance)
timestampfloattime.time() when the frame was emitted
statestrEngine state name when frame was emitted
frame_bytesbytesComplete frame packed as bytes
frame_bitslist[int]Complete frame as bit list

Module: apollo.pcm_demux Type: Pure-Python class Purpose: Demultiplex PCM frames into individual telemetry words, applying A/D voltage scaling and identifying AGC downlink channels.

from apollo.pcm_demux import DemuxEngine
engine = DemuxEngine(output_format="scaled")
result = engine.process_frame(frame_bytes, meta={"frame_id": 1})
ParameterTypeDefaultDescription
output_formatstr"raw"One of "raw" (8-bit integers), "scaled" (voltage-converted), "engineering" (future: named fields with units)
words_per_frameint128Frame length: 128 (high rate) or 200 (low rate)
MethodSignatureDescription
process_frame(frame_bytes: bytes, meta: dict | None) -> dictDemultiplex a complete frame
extract_word(frame_bytes: bytes, word_position: int) -> dictExtract a single word by 1-indexed position (1-128 or 1-200)
KeyTypeDescription
syncdictParsed sync word fields: a_bits, core, b_bits, frame_id, word
wordslist[dict]Per-word dicts with position (1-indexed), raw_value, and optionally voltage, voltage_low_level
agc_datalist[dict]AGC channel entries with channel, channel_octal, raw_value, word_position, optionally voltage
raw_framebytesOriginal frame bytes
metadictPass-through metadata from frame sync
AGC ChannelOctalWord Position (1-indexed)Description
2803434DNTM1 — telemetry word high byte
2903535DNTM2 — telemetry word low byte
4705757OUTLINK — digital downlink data

Module: apollo.downlink_decoder Type: Pure-Python class Purpose: Reassemble 15-bit AGC words from DNTM1/DNTM2 channel pairs and identify downlink list types.

from apollo.downlink_decoder import DownlinkEngine
engine = DownlinkEngine(buffer_size=400)
snapshot = engine.feed_agc_word(channel=28, raw_value=0x3F)
ParameterTypeDefaultDescription
buffer_sizeint400Number of 15-bit words per downlink buffer before finalization
MethodSignatureDescription
feed_agc_word(channel: int, raw_value: int) -> dict | NoneProcess one AGC channel byte. Returns a snapshot dict when a buffer fills, else None
force_flush() -> dict | NoneForce-finalize the current buffer (for end-of-data). Returns snapshot or None if empty
reset() -> NoneClear all internal state
ChannelDecimalAction
DNTM128Stores as pending high byte, waits for matching DNTM2
DNTM229Combines with pending DNTM1 into a 15-bit word via reassemble_agc_word
OUTLINK47Appended to separate outlink buffer
OtherIgnored, returns None
KeyTypeDescription
list_type_idintDownlink list type ID (lower 4 bits of first word)
list_namestrHuman-readable list name or "Unknown (ID=N)"
word_countintNumber of 15-bit words in this snapshot
wordslist[int]The 15-bit AGC words
outlink_datalist[int]Accumulated OUTLINK channel bytes
IDConstantName
0DL_CM_POWERED_LISTCM Powered Flight
1DL_LM_ORBITAL_MANEUVERSLM Orbital Maneuvers
2DL_CM_COAST_ALIGNCM Coast/Alignment
3DL_LM_COAST_ALIGNLM Coast/Alignment
7DL_LM_DESCENT_ASCENTLM Descent/Ascent
8DL_LM_LUNAR_SURFACE_ALIGNLM Lunar Surface Alignment
9DL_CM_ENTRY_UPDATECM Entry Update
FunctionSignatureDescription
reassemble_agc_word(dntm1_byte: int, dntm2_byte: int) -> intCombine two 8-bit channel bytes into one 15-bit AGC word. DNTM1 provides bits 14-8 (lower 7 bits of byte), DNTM2 provides bits 7-0
identify_list_type(first_word: int) -> tuple[int, str]Extract list type ID from lower 4 bits of first word. Returns (id, name)

Module: apollo.voice_subcarrier_demod Type: gr.hier_block2 Purpose: Extract and demodulate the 1.25 MHz FM voice subcarrier to 300-3000 Hz audio.

from apollo.voice_subcarrier_demod import voice_subcarrier_demod
blk = voice_subcarrier_demod(sample_rate=5_120_000, audio_rate=8000)
PortDirectionTypeDescription
in0InputfloatPM demodulator output (composite subcarrier signal)
out0OutputfloatDemodulated audio at audio_rate Hz
ParameterTypeDefaultDescription
sample_ratefloat5120000Input sample rate in Hz
audio_rateint8000Target output audio sample rate in Hz
PropertyTypeDescription
output_sample_ratefloatActual output sample rate (equals audio_rate)
extracted_ratefloatIntermediate rate after subcarrier extraction
input -> subcarrier_extract(1.25 MHz, BW=58 kHz, decimation=auto)
-> quadrature_demod_cf(gain)
-> band_pass_filter(300-3000 Hz)
-> rational_resampler_fff -> output

The decimation factor is computed automatically: int(sample_rate / (58000 * 2.2)). At the default 5.12 MHz input rate, this yields decimation=40 and an intermediate rate of 128 kHz. The FM discriminator gain is extracted_rate / (2 * pi * 29000).


Module: apollo.sco_demod Type: gr.hier_block2 Purpose: Demodulate one of the 9 Subcarrier Oscillator channels to recover a 0-5V analog sensor reading. Valid in FM downlink mode only.

from apollo.sco_demod import sco_demod
blk = sco_demod(sco_number=5, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputfloatPM demodulator output (composite subcarrier signal)
out0OutputfloatRecovered sensor voltage (0.0 to 5.0 V)
ParameterTypeDefaultDescription
sco_numberint1SCO channel number (1-9). See constants reference for frequencies
sample_ratefloat5120000Input sample rate in Hz
PropertyTypeDescription
center_freqfloatCenter frequency of the selected SCO channel (Hz)
deviation_hzfloatFM deviation in Hz (7.5% of center frequency)
output_sample_ratefloatSample rate of the output stream
input -> subcarrier_extract(sco_freq, BW=15% of center, decimation=auto)
-> quadrature_demod_cf(gain)
-> multiply_const_ff(2.5)
-> add_const_ff(2.5) -> output

The voltage mapping is linear: demod output of -1.0 maps to 0V, 0.0 maps to 2.5V, +1.0 maps to 5V.

SCO NumberCenter FrequencyDeviation (+/-)Bandwidth (15%)
114,500 Hz1,087.5 Hz2,175 Hz
222,000 Hz1,650 Hz3,300 Hz
330,000 Hz2,250 Hz4,500 Hz
440,000 Hz3,000 Hz6,000 Hz
552,500 Hz3,937.5 Hz7,875 Hz
670,000 Hz5,250 Hz10,500 Hz
795,000 Hz7,125 Hz14,250 Hz
8125,000 Hz9,375 Hz18,750 Hz
9165,000 Hz12,375 Hz24,750 Hz

Module: apollo.agc_bridge Type: Pure-Python class (threaded TCP client) Purpose: Connect to a running Virtual AGC (yaAGC) emulator over TCP, receive telemetry packets, and send uplink commands.

from apollo.agc_bridge import AGCBridgeClient
def on_packet(channel, value):
print(f"Ch {channel}: {value}")
client = AGCBridgeClient(host="localhost", port=19697, on_packet=on_packet)
client.start()
# ... later ...
client.send(channel=37, value=0x1234)
client.stop()
ParameterTypeDefaultDescription
hoststr"localhost"yaAGC hostname or IP address
portint19697yaAGC TCP port
channel_filterfrozenset[int] | NoneAGC_TELECOM_CHANNELSSet of channel numbers to pass through. None accepts all channels
on_packetCallable[[int, int], None] | NoneNoneCallback for received packets: on_packet(channel, value). Called from the rx thread
on_statusCallable[[str], None] | NoneNoneCallback for connection state changes: on_status(state_str). States: "disconnected", "connecting", "connected"
MethodSignatureDescription
start() -> NoneLaunch background receive thread. Auto-reconnects on disconnect with exponential backoff (0.5s to 30s)
stop() -> NoneSignal thread to stop, close socket, join thread (5s timeout)
send(channel: int, value: int) -> boolSend a 4-byte I/O packet to yaAGC. Returns True on success, False if not connected
PropertyTypeDescription
statestrCurrent connection state: "disconnected", "connecting", or "connected"
connectedboolTrue if state is "connected"
ParameterValue
Base delay0.5 seconds
Max delay30.0 seconds
Backoff factor2.0x

Module: apollo.uplink_encoder Type: Pure-Python class Purpose: Encode ground-station commands (VERB, NOUN, DATA, PROCEED) into AGC INLINK channel (045 octal) word sequences.

from apollo.uplink_encoder import UplinkEncoder
enc = UplinkEncoder()
pairs = enc.encode_verb_noun(verb=37, noun=1)
# Returns: [(37, 17408), (37, 1024), (37, 7168), ...]
ParameterTypeDefaultDescription
channelint37AGC I/O channel for uplink. Default is channel 045 octal (37 decimal, INLINK)
MethodSignatureDescription
encode_keycode(keycode: int) -> tuple[int, int]Encode a single DSKY keycode. Returns (channel, value) with keycode in bits 14-10
encode_digit(digit: int) -> tuple[int, int]Encode a decimal digit 0-9
encode_verb(verb_number: int) -> list[tuple[int, int]]Encode VERB + 2 digit keys. verb_number range: 0-99
encode_noun(noun_number: int) -> list[tuple[int, int]]Encode NOUN + 2 digit keys. noun_number range: 0-99
encode_data(value: int, signed: bool = True) -> list[tuple[int, int]]Encode a 5-digit data entry with optional sign. Range: -99999 to +99999 (signed) or 0 to 99999 (unsigned)
encode_proceed() -> list[tuple[int, int]]Encode a PROCEED (ENTER) keystroke
encode_verb_noun(verb: int, noun: int) -> list[tuple[int, int]]Convenience: full VERB + NOUN + ENTER sequence
encode_command(command_type: str, data: int | None) -> list[tuple[int, int]]Dispatch by type string: "VERB", "NOUN", "DATA", "PROCEED"
KeyOctalDecimalConstant
VERB02117KEYCODE_VERB
NOUN03731KEYCODE_NOUN
ENTER/PROCEED03428KEYCODE_ENTER
RESET/KEY RELEASE02218KEYCODE_RESET
CLEAR03630KEYCODE_CLEAR
+03226KEYCODE_PLUS
-03327KEYCODE_MINUS
002016KEYCODE_DIGITS[0]
1-7001-0071-7KEYCODE_DIGITS[1-7]
80108KEYCODE_DIGITS[8]
90119KEYCODE_DIGITS[9]

Module: apollo.usb_downlink_receiver Type: gr.hier_block2 Purpose: Complete Apollo USB downlink receiver chain in a single block — complex baseband input to telemetry PDU outputs.

from apollo.usb_downlink_receiver import usb_downlink_receiver
blk = usb_downlink_receiver(sample_rate=5_120_000, output_format="scaled")
PortDirectionTypeDescription
in0Inputcomplex (streaming)Baseband IQ samples at sample_rate
"frames"OutputMessage (PDU)Complete PCM frame PDUs (from frame sync)
"telemetry"OutputMessage (PDU)Individual word PDUs with channel metadata
"agc_data"OutputMessage (PDU)AGC channel data (ch 34/35/57)
"raw_frame"OutputMessage (PDU)Full frame passthrough
ParameterTypeDefaultDescription
sample_ratefloat5120000Input sample rate in Hz
bit_rateint51200PCM bit rate: 51200 (high) or 1600 (low)
carrier_pll_bwfloat0.02PM demodulator PLL bandwidth (rad/sample)
subcarrier_bwfloat150000PCM subcarrier bandpass filter width (Hz)
bpsk_loop_bwfloat0.045BPSK Costas loop bandwidth (rad/sample)
max_bit_errorsint3Frame sync Hamming distance threshold
output_formatstr"raw"Demux output: "raw", "scaled", or "engineering"
complex in -> pm_demod -> subcarrier_extract(1.024 MHz)
-> bpsk_demod -> pcm_frame_sync
|
msg: "frames"
|
pcm_demux
/ | \
"telemetry" "agc_data" "raw_frame"

The internal blocks are exposed as instance attributes for runtime inspection or parameter adjustment:

AttributeTypeBlock
self.pmpm_demodPM demodulator
self.sc_extractsubcarrier_extractSubcarrier extractor
self.bpskbpsk_demodBPSK demodulator
self.frame_syncpcm_frame_syncFrame synchronizer
self.demuxpcm_demuxFrame demultiplexer

Module: apollo.pcm_frame_source Type: Pure-Python class Purpose: Generate PCM telemetry frames as bit lists. Maintains rolling frame counter (1-50), auto-complements sync core on odd frames.

from apollo.pcm_frame_source import FrameSourceEngine
engine = FrameSourceEngine(bit_rate=51200)
bits = engine.next_frame()
ParameterTypeDefaultDescription
bit_rateint51200PCM bit rate: 51200 (high, 128 words/frame) or 1600 (low, 200 words/frame)
MethodSignatureDescription
next_frame(data: bytes | None) -> list[int]Generate next frame as bit list. data=None gives zero-fill. Returns list of 0/1 values, length = words_per_frame * 8
PropertyTypeDescription
bit_rateintPCM bit rate in bps
words_per_frameint128 (high) or 200 (low)
frame_counterintCurrent frame number (1-50)

Module: apollo.nrz_encoder Type: gr.hier_block2 Purpose: Convert PCM bit stream (byte 0/1) to NRZ float waveform (+1.0/-1.0) at the output sample rate.

from apollo.nrz_encoder import nrz_encoder
blk = nrz_encoder(bit_rate=51200, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputbyteBit stream from pcm_frame_source (values 0 or 1)
out0OutputfloatNRZ waveform (+1.0 / -1.0) at sample_rate
ParameterTypeDefaultDescription
bit_rateint51200PCM bit rate in bps
sample_ratefloat5120000Output sample rate in Hz
PropertyTypeDescription
samples_per_bitintSamples per bit period: sample_rate / bit_rate

input -> char_to_float -> multiply_const_ff(2.0) -> add_const_ff(-1.0) -> repeat(samples_per_bit) -> output


Module: apollo.bpsk_subcarrier_mod Type: gr.hier_block2 Purpose: Modulate NRZ float data onto 1.024 MHz cosine subcarrier via multiplication: output(t) = nrz(t) * cos(2*pi*f_sc*t).

from apollo.bpsk_subcarrier_mod import bpsk_subcarrier_mod
blk = bpsk_subcarrier_mod(subcarrier_freq=1_024_000, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputfloatNRZ waveform from nrz_encoder
out0OutputfloatBPSK modulated subcarrier
ParameterTypeDefaultDescription
subcarrier_freqfloat1024000Subcarrier frequency in Hz (PCM_SUBCARRIER_HZ)
sample_ratefloat5120000Sample rate in Hz
PropertyTypeDescription
subcarrier_freqfloatSubcarrier frequency in Hz
sample_ratefloatSample rate in Hz

input (NRZ) -> (mixer, port 0); sig_source_f(cos, subcarrier_freq) -> (mixer, port 1); mixer -> output


Module: apollo.fm_voice_subcarrier_mod Type: gr.hier_block2 Purpose: FM-modulate audio onto 1.25 MHz subcarrier with +/-29 kHz deviation. Two modes: internal test tone (source block) or external audio input.

# Internal test tone (no input)
from apollo.fm_voice_subcarrier_mod import fm_voice_subcarrier_mod
voice = fm_voice_subcarrier_mod(tone_freq=1000.0)
# External audio input
voice = fm_voice_subcarrier_mod(audio_input=True)
PortDirectionTypeDescription
in0Inputfloat (or none)External audio signal when audio_input=True; no input when False
out0OutputfloatFM subcarrier at subcarrier_freq
ParameterTypeDefaultDescription
sample_ratefloat5120000Sample rate in Hz
subcarrier_freqfloat1250000Voice subcarrier center frequency (VOICE_SUBCARRIER_HZ)
fm_deviationfloat29000FM deviation in Hz (VOICE_FM_DEVIATION_HZ)
tone_freqfloat1000.0Internal test tone frequency when audio_input=False
audio_inputboolFalseWhen True, accepts external float audio stream
PropertyTypeDescription
tone_freqfloatTest tone frequency in Hz
subcarrier_freqfloatSubcarrier center frequency in Hz
fm_deviationfloatFM deviation in Hz
audio_inputboolWhether block accepts external audio

[audio source or input] -> frequency_modulator_fc(sensitivity) -> (mixer, 0); sig_source_c(subcarrier_freq) -> (mixer, 1); mixer -> complex_to_real -> output


Module: apollo.pm_mod Type: gr.hier_block2 Purpose: Apply phase modulation at 0.133 rad peak deviation, producing complex baseband exp(j * phi(t)).

from apollo.pm_mod import pm_mod
blk = pm_mod(pm_deviation=0.133, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputfloatComposite modulating signal (sum of subcarriers)
out0OutputcomplexPM complex baseband
ParameterTypeDefaultDescription
pm_deviationfloat0.133Peak PM deviation in radians (PM_PEAK_DEVIATION_RAD)
sample_ratefloat5120000Sample rate in Hz
MethodSignatureDescription
get_pm_deviation() -> floatRead current PM deviation
set_pm_deviation(dev: float) -> NoneUpdate PM deviation at runtime

input -> multiply_const_ff(pm_deviation) -> phase_modulator_fc(1.0) -> output


Module: apollo.sco_mod Type: gr.hier_block2 Purpose: Modulate 0-5V sensor voltage onto an FM subcarrier oscillator tone. Only used in FM downlink mode. 9 channels available (14.5 kHz to 165 kHz).

from apollo.sco_mod import sco_mod
blk = sco_mod(sco_number=5, sample_rate=5_120_000)
PortDirectionTypeDescription
in0InputfloatSensor voltage (0.0 to 5.0 V)
out0OutputfloatFM subcarrier tone
ParameterTypeDefaultDescription
sco_numberint1SCO channel number (1-9). Raises ValueError if invalid
sample_ratefloat5120000Sample rate in Hz
PropertyTypeDescription
center_freqfloatCenter frequency of this SCO channel in Hz
deviation_hzfloatFM deviation in Hz (+/- 7.5% of center)
sco_numberintSCO channel number (1-9)

input -> add_const_ff(-2.5) -> multiply_const_ff(1/2.5) -> frequency_modulator_fc -> (mixer, 0); sig_source_c(center_freq) -> (mixer, 1); mixer -> complex_to_real -> output

SCO NumberCenter FrequencyDeviation (+/-)Bandwidth (15%)
114,500 Hz1,087.5 Hz2,175 Hz
222,000 Hz1,650 Hz3,300 Hz
330,000 Hz2,250 Hz4,500 Hz
440,000 Hz3,000 Hz6,000 Hz
552,500 Hz3,937.5 Hz7,875 Hz
670,000 Hz5,250 Hz10,500 Hz
795,000 Hz7,125 Hz14,250 Hz
8125,000 Hz9,375 Hz18,750 Hz
9165,000 Hz12,375 Hz24,750 Hz

Module: apollo.usb_signal_source Type: gr.hier_block2 Purpose: Complete Apollo USB transmit chain in one block — the TX counterpart to usb_downlink_receiver.

from apollo.usb_signal_source import usb_signal_source
blk = usb_signal_source(voice_enabled=True, snr_db=20.0)
PortDirectionTypeDescription
(none)Input(none)Source block — no streaming input
out0OutputcomplexPM-modulated complex baseband at sample_rate
"frame_data"InputMessageForwarded to pcm_frame_source for dynamic payload
ParameterTypeDefaultDescription
sample_ratefloat5120000Baseband sample rate in Hz
bit_rateint51200PCM bit rate in bps
pm_deviationfloat0.133PM peak deviation in radians
voice_enabledboolFalseInclude 1.25 MHz FM voice subcarrier
voice_tone_hzfloat1000.0Voice test tone frequency when voice_enabled=True
snr_dbfloat | NoneNoneAdd AWGN at this SNR. None means no noise
pcm_frame_source -> nrz_encoder -> bpsk_subcarrier_mod -+-> add_ff -> pm_mod -> [+AWGN] -> output
|
fm_voice_subcarrier_mod (x0.764) --+
(only when voice_enabled=True)

Voice scale factor: 1.68 / 2.2 = 0.764 (per IMPL_SPEC power ratio — PCM at 2.2 Vpp, voice at 1.68 Vpp).

The internal blocks are exposed as instance attributes for runtime inspection or parameter adjustment:

AttributeTypeBlock
self.frame_srcpcm_frame_sourceFrame generator
self.nrznrz_encoderNRZ line encoder
self.bpskbpsk_subcarrier_modBPSK modulator
self.voicefm_voice_subcarrier_modVoice modulator (when voice_enabled)
self.voice_gainmultiply_const_ffVoice level scaling (when voice_enabled)
self.adderadd_ffSubcarrier summer (when voice_enabled)
self.pmpm_modPhase modulator
self.noisenoise_source_cAWGN source (when snr_db is set)