Skip to content

Field reference

Every field of a wfmgen source or segment, on each face that exposes it: the wfmgen flag, the scene-file key, and the Python keyword. The description is the field's header comment, the same text as wfmgen --help (its first sentence) and the Python docstring. A ranged field also takes a LO:HI range on the command line, or a [lo, hi] pair in a scene, drawn uniformly per repeat.

On the command line an integer is decimal or 0x hex, and a leading 0 is decimal, never octal. Every number must be the whole value: 4x, 0.1abc and an empty value are refused, naming the flag. A sample count (--count, --off, --delay, each side of a range) is a whole, non-negative number: 1e3 is 1000, and 1.5 or -1 is refused.

Framing, payload and coding keys are described in Waveforms and Scenes.

Segment fields

Python CLI JSON Type Default Description
fs --fs HZ fs number 1.0 Sample rate in Hz, one per segment and shared by all its sources. At the default 1.0 every frequency is normalised (cycles per sample); state it whenever a scene is in real Hz.
num_samples --count N num_samples integer, or a range 0 Segment on-time in samples, before the trailing gap: 0 derives it from the sources, or 1024 when they set none. A finite data source sets its frames, a lone dsss burst one burst, and a stream runs to its end. A count beside a finite data source or a lone dsss burst is refused, since they set the length; give repeats for more.
off_samples --off N off_samples integer, or a range 0 Trailing gap after the on-time, in samples. It carries the noise floor or hard zeros, per gap_noise.
repeats --repeats N repeats integer 1 Play the segment this many times back-to-back (each instance = delay + on-time + trailing gap) before advancing. Ranged fields re-draw and the AWGN is fresh per instance; the signal (codes, payload, PN phase) stays fixed. 0 and 1 both mean one instance.
delay_samples --delay N delay_samples integer, or a range 0 Leading gap before the on-time, in samples: the burst arrives after this delay, and the gap carries the noise floor like off_samples. Ranged like off_samples and re-drawn per repeats instance, so a (lo, hi) delay is per-burst arrival jitter. Use off_samples for inter-burst spacing, delay_samples for arrival jitter.
gap_noise --gap-noise M gap_noise one of auto, off auto Gap policy for this segment's delay and trailing gap. auto: gaps carry the segment's noise floor -- the sources' AWGN keeps running while the signal stops (clean scenes still get exact-zero gaps). off: gaps are hard zeros.

Source fields

Python CLI JSON Type Default Description
type --type TYPE type one of tone, noise, pn, bpsk, qpsk, chirp, bits, symbols, dsss tone Waveform type. tone: complex sinusoid. noise: AWGN. pn: PN sequence (LFSR). bpsk/qpsk: PN-driven modulation. chirp: linear-FM sweep. bits: a caller's bit pattern, with selectable modulation. symbols: a caller's complex constellation stream. dsss: spread spectrum -- a two-code burst (repeated preamble + data-code-spread frame) by default, or a continuous asynchronous stream when symbol_rate is set.
freq --freq HZ freq number, or a range 0.0 Carrier or offset frequency in Hz; for chirp, the sweep start. With fs = 1 it is in normalised cycles per sample.
snr --snr DB snr number, or a range 100.0 Signal-to-noise ratio in dB, interpreted per snr_mode. 100 or more is clean: no AWGN is added.
snr_mode --snr-mode MODE snr_mode one of auto, fs, ebno, esno auto How snr is interpreted. auto: Es/N0 for bpsk/qpsk/dsss, and relative to full scale for tone/noise/pn/chirp/bits/symbols -- bits included, because a bits frame has no symbol rate the engine can infer. fs: dB relative to full scale. ebno: Eb/N0, per bit. esno: Es/N0, per symbol; for a dsss burst the outer data symbol of len(data_code) chips x sps samples, for a continuous dsss stream the fs/symbol_rate samples the async symbol spans.
seed --seed N seed integer 0 PRNG and LFSR seed for the noise and PN streams. Deterministic: vary it for run-to-run change. For a PN-sourced type it starts the pn_length-bit register, masked: a seed whose low pn_length bits are zero starts the register at 1, as seed 0 does.
sps --sps N sps integer 1 Samples per symbol (PSK) or per chip (PN): the oversampling factor. Unused by noise, which records it as 0.
pn_length --pn-length N pn_length integer 15 PN LFSR register length in bits, 2 to 64 for the PN-bearing types; the sequence period is 2^pn_length - 1. Unused by noise, which records it as 0.
pn_poly --pn-poly N pn_poly integer 0 PN generator polynomial, in the Galois bit-vector convention; 0 selects a maximal-length (MLS) polynomial for pn_length. It must fit the pn_length-bit register: a bit at or above it is refused, not masked. A polynomial above 2^53 does not survive a JSON number, so a scene file needs 0 (auto) for such a register.
lfsr --lfsr TYPE lfsr one of galois, fibonacci galois PN LFSR realisation. Both give the same period; fibonacci's chips are galois's in reverse order.
level --level DB level number, or a range 0.0 Source power in dBFS (<= 0; 0 is unit power). Applies when summed in a Segment or Composer, as a gain of 10^(level/20); a standalone Synth.steps() ignores it.
background — background bool 0 Mark this source as part of the static background field (0/1). Plan.prepare() folds a contiguous leading run of background sources into ONE pre-summed cache entry instead of caching each separately, so a scene of many fixed emitters costs one buffer rather than hundreds. The composite is overridable as a unit: it takes a single slot in gains/phases/enable and counts as one in n_sources(), so scaling it trims the whole field while its members keep their relative levels. Background sources must come first in the segment (a non-prefix ordering is rejected by prepare, since the fold would no longer reproduce compose bit-for-bit). Ignored by compose() and by standalone Synth.steps().
f_end --f-end HZ f_end number, or a range 0.0 Chirp end frequency in Hz; ignored by other types.
span — span integer 0 Chirp sweep length in samples: the frequency ramps from freq to f_end over this many samples, then holds at f_end. 0 means the enclosing Segment's num_samples. A standalone chirp (f_end != freq) must declare it, so step(), steps(N) and any chunking of reads produce the same waveform; generating one without it raises. Ignored by non-chirp types.
doppler --doppler PPM doppler number, or a range 0.0 Clock Doppler in ppm: the received time base is rescaled by 1 + doppler*1e-6, so the symbol and chip rates move with the carrier and a timing loop sees the error a carrier-only freq offset hides. Accepts a (lo, hi) tuple drawn uniformly per repeat, like freq/snr. Zero doppler AND zero doppler_rate means no channel is built at all, so a source that does not ask for Doppler renders exactly as it always did.
doppler_rate --doppler-rate PPM_S doppler_rate number, or a range 0.0 Linear ramp on doppler, in ppm per second of elapsed stream time. The channel runs through a segment's gaps as well as its on-time -- an emitter does not stop moving because its burst ended -- so this is per second, not per second of on-time. Accepts a (lo, hi) tuple drawn uniformly per repeat.
carrier_hz --carrier-hz HZ carrier_hz number 0.0 RF carrier in Hz that the ppm figures are referred to. It gives the coherent carrier rotation that accompanies the time-base warp; 0 warps the clock alone, with no carrier rotation -- a legitimate scene, not an unset field. Independent of doppler/doppler_rate.
doppler_lifetime --doppler-lifetime L doppler_lifetime one of per_instance, persist per_instance How long this source's Doppler channel lives. per_instance: the channel dies with each repeats instance, so the geometry restarts -- the repeated-trial shape, which composes with a ranged doppler re-drawn per instance. persist: one continuous pass carries across the segment's gaps and repeat instances, keyed by (segment, source) position -- the only lifetime under which doppler_rate accumulates across a multi-burst scene. compose(), stream() and Plan.prepare() all honour both.
modulation --modulation M modulation one of none, bpsk, qpsk bpsk Symbol mapping of a bits pattern. none: the pattern shaped and output as-is (NRZ). bpsk: +/-1 symbols. qpsk: Gray-coded symbols from pairs of bits.
pulse --pulse SHAPE pulse one of rect, rrc rect Pulse shape per symbol or chip, for pn/bpsk/qpsk/bits/symbols/dsss. rect: rectangular, no ISI filtering. rrc: root-raised cosine; see rrc_beta and rrc_span.
rrc_beta --rrc-beta R rrc_beta number 0.35 RRC roll-off factor, in (0, 1], when pulse=rrc.
rrc_span --rrc-span N rrc_span integer 8 RRC filter support in symbols when pulse=rrc, ONE-SIDED: the filter has 2rrc_spansps + 1 taps, unit energy (sum of h^2 = 1).
symbols --symbols-file F — complex stream — For type=symbols: a complex constellation stream. Each element is the output point itself, oversampled by sps, cycled, and RRC-shaped with pulse=rrc, which generalises any modulation (pi/4-QPSK, QAM, ...).
acq_code --acq-code FIELD acq_code Field — The preamble code, sent acq_reps times at the head of the frame (a Field's *REPS on the command line and in a scene) -- the coherent pull-in target BurstDespreader.set_acq and BurstDemod.set_preamble lock to. For type=dsss it is unmodulated chips ahead of the spread frame; for type=bits it is the head of the bit pattern. Setting it is what makes a source FRAMED.
data_code --data-code FIELD data_code Field — For type=dsss: the payload spreading code, a second code distinct from acq_code. Every frame bit (sync, payload, crc) is XOR-spread across its full length, so len(data_code) is the spreading factor.
symbol_rate --symbol-rate HZ symbol_rate number 0.0 For type=dsss: > 0 selects CONTINUOUS asynchronous mode. The spreading code repeats endlessly and data rides on it at this symbol rate (Hz), independent of the code-epoch rate (chips/symbol = fs/sps/symbol_rate, non-integer). No preamble/sync/CRC frame; data comes from the payload when supplied, else a seeded PN a receiver regenerates. Absent/0 = burst.
dsss_code_only --code-only code_only bool 0 Continuous dsss data source: 1 = code-only (the pure spreading code, no data modulation); 0 = data-modulated (the payload when supplied, else the seeded PN). Ignored for burst dsss and non-dsss types.
frame --frame FILE frame frame description — A frame DESCRIPTION, the whole frame: fields in wire order, and stages that each name the span they cover (crc16, rs, randomise, interleave, conv, or a kind of your own). It is the only way a source says anything but the common frame: a coding stage, a field of the caller's own bits at a position of their choosing, a stage covering a span they name. wfmgen --frame FILE, a scene's frame key and Python's frame= (a FrameDesc or a Frame) all land here. When it is set it IS the frame, and an unspread acq_code beside it is refused. NULL means the common frame, [preamble x reps \| data], which dp_wfm_frame_fixed() builds from acq_code and the data source; a sync word or a CRC is a field or a stage of a description, never a flat field. A C caller's description is borrowed, exactly as wfm_seq_t is borrowed elsewhere here, so it must outlive the source. The composer and a Python source hold their own copy (dp_wfm_frame_copy()), so a later change to the FrameDesc does not reach them. On the Python face frame= is an input: read it back from the composer's JSON (its getter is jm's, pending removal: doppler#1694). KERNELS stay in C by design. A description names a stage's KIND; the code that runs it is a wfm_frame_ops_t entry, and a caller adding a genuinely new transform (convolutional interleaving, say) writes that kernel in C and hands it to dp_wfm_frame_assemble directly.
data_from_file --data-from-file PATH data_from_file path — A data source read from a file of packed octets, MSB first, or - for stdin, instead of data. Not on the Python face: there a file is cvt.bytes_to_bin of its bytes, passed as data (payload-data-source.md section 4.9). Borrowed, so it must outlive the source. Not with data: a payload has one data source.
data --data FIELD data Field — A frame's payload drawn from a data source: a Field on the command line and in a scene, a bit array in Python. The source is split into data_len-bit frames, one chunk per frame, and its last chunk is padded from fill. For type=bits, bpsk/qpsk/pn framed, a dsss burst (one burst per frame), and continuous dsss (one bit per data symbol, no frame); not with data_from_file. Not with data_from_file: a payload has one data source.
data_len --data-len BITS data_len integer 0 Bits of the data source per frame: the data:LEN of the common frame [preamble x reps | sync | data:LEN | crc]. 0 takes a finite source whole, as one frame. A carried frame names its own data field, and this is then 0 or that field's LEN. Continuous dsss has no frame, and refuses it.
fill --fill FIELD fill Field — The bits that pad a data source's last frame when it does not divide into data_len-bit frames, tiled from their first bit; stdin on a framed source always needs them. Without them such a source is refused before the first sample. A Field on the command line and in a scene, a bit array in Python. Continuous dsss has no frame to pad, and refuses it.
code_only_symbols --code-only-symbols N code_only_symbols integer 0 Continuous dsss: pure-code symbols opening each frame of frame_symbols symbols. The data pauses through them and runs on across frames, so a searcher can integrate coherently over the window; once a payload ends, no window is sent. At most frame_symbols, and fewer when a data source is set; 0 with frame_symbols 0 = no window.
frame_symbols --frame-symbols N frame_symbols integer 0 Continuous dsss: the frame, in symbols, that code_only_symbols opens; give it with code_only_symbols, or both 0 for no window. The frame runs on the stream's own symbol clock, so its edges land at any chip phase.