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. |