File wfm_compose.h¶
FileList > inc > wfm > wfm_compose.h
Go to the source code of this file
Multi-segment waveform composer (Phase B). More...
#include "clib_common.h"#include "wfm_synth/wfm_synth_core.h"#include "wfm/wfm_frame.h"#include "doppler_channel/doppler_channel_core.h"
Classes¶
| Type | Name |
|---|---|
| struct | wfm_draw_t One rendered source instance: its timing AND the values it was actually rendered with. |
| struct | wfm_segment_t One composer segment: one or more sources summed over the same span, then a trailing off-time gap. |
| struct | wfm_source_t One additive source within a segment: a synth config + its level. |
| struct | wfm_span_t One rendered segment instance's exact timing: where it lands in the composed stream and how its delay | on | off spans divide it. |
Public Types¶
| Type | Name |
|---|---|
| enum | wfm__compose_8h_1ab04a0655cd1e3bcac5e8f48c18df1a57 Per-field "draw uniformly each repeat" flags ( ranged bitmask). |
| enum | wfm_bitmod_t How a WFM_SYNTH_BITS source maps its payload to symbols. |
| typedef struct wfm_compose_state | wfm_compose_state_t |
| enum | wfm_doppler_lifetime_t When a source's Doppler channel restarts. |
| typedef struct wfm_render | wfm_render_t One source's renderer: its synth, plus its Doppler channel. |
| enum | wfm_seed_advance_t Per-repeat seed policy for a looped/continuous stream. |
| enum | wfm_snr_mode_t What a source's snr is measured against. |
Public Functions¶
| Type | Name |
|---|---|
| wfm_render_t * | wfm_compose_build_render (const wfm_source_t * src, double fs, size_t on_len, double freq, double snr, double f_end, double doppler, double doppler_rate, unsigned epoch, int seed_advance, size_t instance, doppler_channel_state_t * borrow) Build a source's renderer — wfm_compose_build_synth plus the clock-Doppler channel the source declares, if it declares one. |
| wfm_synth_state_t * | wfm_compose_build_synth (const wfm_source_t * src, double fs, size_t on_len, double freq, double snr, double f_end, unsigned epoch, int seed_advance, size_t instance) Construct + configure the synth for one resolved source. |
| wfm_compose_state_t * | wfm_compose_create (const wfm_segment_t * segs, size_t n_segs, int repeat, int continuous) Build a composer over a copy of segs . |
| void | wfm_compose_destroy (wfm_compose_state_t * state) Destroy a composer and its active synth. |
| size_t | wfm_compose_draws (const wfm_segment_t * segs, size_t n_segs, wfm_draw_t * out, size_t cap) Replay the (epoch 0) instance timeline AND its drawn source values. |
| size_t | wfm_compose_execute (wfm_compose_state_t * state, float _Complex * out, size_t max) Emit up to max samples of the composed stream. |
| wfm_compose_state_t * | wfm_compose_from_file (const char * path) Build a composer from a JSON spec file. |
| wfm_compose_state_t * | wfm_compose_from_json (const char * json) Build a composer from a JSON spec string (for from-file). |
| wfm_compose_state_t * | wfm_compose_from_json_why (const char * json, const char ** why) The same, but able to say why a FRAME was refused. |
| int | wfm_compose_seed_advance (const wfm_compose_state_t * state) The composer's current seed-advance mode (a wfm_seed_advance_t ). |
| const wfm_segment_t * | wfm_compose_segments (const wfm_compose_state_t * state, size_t * n_out, int * repeat, int * continuous) Borrow the composer's stored segment list (for record / SigMF). |
| void | wfm_compose_set_seed_advance (wfm_compose_state_t * state, int mode) Choose how the seed advances on each repeat of a looped/continuous stream (a wfm_seed_advance_t ): |
| size_t | wfm_compose_spans (const wfm_segment_t * segs, size_t n_segs, wfm_span_t * out, size_t cap) Replay the (epoch 0) instance timeline of a resolved segment list. |
| char * | wfm_draws_json (const wfm_segment_t * segs, size_t n_segs) The same rows wfm_compose_draws() reports, as a JSON array. |
| void | wfm_render_destroy (wfm_render_t * r) Free a renderer and everything it owns. NULL-safe. |
| void | wfm_render_noise_steps (wfm_render_t * r, float _Complex * dst, size_t n) Pull n samples of the source's NOISE FLOOR only, through the same channel. |
| void | wfm_render_steps (wfm_render_t * r, float _Complex * dst, size_t n) Pull exactly n samples fromr , through its channel if any. |
| int | wfm_resolve_noise (wfm_segment_t * segs, size_t n) Resolve a segment list's noise model in place (Phase 4b). |
| double | wfm_snr_over_fs (int snr_mode, int type, int sps, size_t sf, double sym_span, double snr) SNR (dB) referred to fs, from a source's snr/snr_mode/sps/type. |
| int | wfm_source_attach_dsss (wfm_synth_state_t * syn, const wfm_source_t * src, double fs) Attach a dsss source's data to a freshly-created synth. |
| int | wfm_source_attach_frame (wfm_synth_state_t * syn, const wfm_source_t * src) Attach an unspread source's bit pattern, framed or not. |
| double | wfm_source_create_snr (const wfm_source_t * src, double fs, double snr, int * snr_mode) Resolve a source's (snr, snr_mode) into the pair to hand to wfm_synth_create() . |
| int | wfm_source_describe_frame (const wfm_source_t * src, wfm_frame_desc_t * d) Describe a source's frame: the fields, the stages, and their covers. |
| size_t | wfm_source_dsss_nchips (const wfm_source_t * src) Chips one DSSS BURST from this source occupies, description and all. |
| const char * | wfm_source_frame_error (const wfm_source_t * src) NULL when this source's frame fields can be honoured; else why not. |
| int | wfm_source_has_frame (const wfm_source_t * src) Non-zero when this source describes a FRAME. |
| double | wfm_spec_headroom (const char * json) The top-level headroom (dB) from a spec JSON, or 0 if absent. |
| char * | wfm_spec_template_json (void) A ready-to-edit example spec in the canonical from-file schema. |
| char * | wfm_spec_to_json (const wfm_segment_t * segs, size_t n_segs, int repeat, int continuous, int seed_advance, double headroom) Serialise a spec to a JSON string (for record). |
Detailed Description¶
Sequences a list of segments — each one a synth configuration plus an on-time and a trailing off-time gap — into a single IQ stream, optionally repeating the whole sequence or running forever. The composer owns one synth at a time (the active segment) and reuses the Phase-A engine verbatim, so every waveform type / SNR mode / MLS behaviour is identical to the single-waveform path; a one-segment spec is byte-identical to calling synth directly.
Lifecycle: wfm_compose_create -> wfm_compose_execute* -> wfm_compose_destroy
wfm_source_t tone = {.type = 0, .freq = 1e5, .snr = 100.0};
wfm_source_t qpsk = {.type = 4, .sps = 8, .snr = 9.0};
wfm_segment_t segs[2] = {
{.sources = &tone, .n_sources = 1, .fs = 1e6,
.num_samples = 1000, .off_samples = 500}, // tone, then a gap
{.sources = &qpsk, .n_sources = 1, .fs = 1e6,
.num_samples = 4096, .off_samples = 0}, // qpsk
};
wfm_compose_state_t *c = wfm_compose_create(segs, 2, 0, 0);
float _Complex buf[4096];
size_t n;
while ((n = wfm_compose_execute(c, buf, 4096)) > 0) { ... }
wfm_compose_destroy(c);
Public Types Documentation¶
enum wfm__compose_8h_1ab04a0655cd1e3bcac5e8f48c18df1a57¶
Per-field "draw uniformly each repeat" flags ( ranged bitmask).
enum wfm__compose_8h_1ab04a0655cd1e3bcac5e8f48c18df1a57 {
WFM_RANGE_FREQ = 1u << 0,
WFM_RANGE_SNR = 1u << 1,
WFM_RANGE_LEVEL = 1u << 2,
WFM_RANGE_FEND = 1u << 3,
WFM_RANGE_NUM_SAMPLES = 1u << 4,
WFM_RANGE_OFF_SAMPLES = 1u << 5,
WFM_RANGE_DELAY_SAMPLES = 1u << 6,
WFM_RANGE_DOPPLER = 1u << 7,
WFM_RANGE_DOPPLER_RATE = 1u << 8
};
A scalar field is a constant; a ranged field carries a [lo, hi] span (the scalar holds lo, a companion *_hi holds hi) and is redrawn uniformly in [lo, hi] at the start of every repeat (composer epoch) — so a looped / continuous stream can vary Doppler (freq), arrival jitter (off_samples), etc. burst-to-burst while staying reproducible: the draw is a deterministic hash of the source seed, the epoch, the segment/source index, and the field, so --record stores the span (not a drawn value) and --from-file replays the same sequence byte-for-byte. Bits 0–3 and 7–8 live on wfm_source_t.ranged; bits 4–6 on wfm_segment_t.ranged.
enum wfm_bitmod_t¶
How a WFM_SYNTH_BITS source maps its payload to symbols.
Order IS the wire value; BITMOD_NAMES[] and the [[enum]] bitmod manifest are held to this by make lint-wfm-enum-tables.
typedef wfm_compose_state_t¶
Opaque composer state.
enum wfm_doppler_lifetime_t¶
When a source's Doppler channel restarts.
Neither is a superset of the other, so it is declared rather than defaulted into an argument:
PER_INSTANCE(default) restarts the geometry for every burst instance, which is the repeated-trial shape — every burst sees the same pass, and it composes with the per-instance re-draw of a rangeddoppler.PERSISTcarries one emitter's motion across every REPEAT INSTANCE of its segment, and across the gaps between them, so burst k sees where the pass has got to. It is the only lifetime under whichdoppler_ratemeans anything over a multi-burst scene.
The channel is keyed by (segment, source), because that is the only source identity the composer has — a position. So a PERSIST source persists over its own segment's instances; two DIFFERENT segments each get their own pass, even where a reader might call them the same emitter. Sharing one across segments needs a declared source id, which nothing in the scene format carries yet; gh-942 says as much ("no per-source identity that survives it ... the repeats/epoch machinery is where one would hang").
typedef wfm_render_t¶
One source's renderer: its synth, plus its Doppler channel.
enum wfm_seed_advance_t¶
Per-repeat seed policy for a looped/continuous stream.
enum wfm_seed_advance_t {
WFM_SEED_ADVANCE_NONE = 0,
WFM_SEED_ADVANCE_NOISE = 1,
WFM_SEED_ADVANCE_ALL = 2
};
A source's single seed feeds two RNGs: the PN LFSR (spreading code and data bits — one register) and the AWGN generator. The clean cut is therefore signal (code+data) vs. noise, exposed as an ordered, cumulative level.
enum wfm_snr_mode_t¶
What a source's snr is measured against.
The scale a number in dB is quoted on is not a detail a caller can infer, and it changes the noise by 10log10(sps) between fs and esno. Naming the modes is what lets a downstream write the mode it means instead of a literal whose meaning lives in a comment. Order IS the wire value — the [[enum]] snr_mode manifest and MODE_NAMES[] in wfm_names.h are held to this by make lint-wfm-enum-tables.
Public Functions Documentation¶
function wfm_compose_build_render¶
Build a source's renderer — wfm_compose_build_synth plus the clock-Doppler channel the source declares, if it declares one.
wfm_render_t * wfm_compose_build_render (
const wfm_source_t * src,
double fs,
size_t on_len,
double freq,
double snr,
double f_end,
double doppler,
double doppler_rate,
unsigned epoch,
int seed_advance,
size_t instance,
doppler_channel_state_t * borrow
)
THE pull path. Both faces go through wfm_render_steps() rather than calling wfm_synth_steps() themselves, because a Doppler channel is a RESAMPLER: it consumes about n*(1+d) inputs per n outputs, so "pull
k, get k" only holds if something keeps the remainder. Two implementations that agreed today would drift the moment either grew a holdover the other did not.
A source with doppler == 0 && doppler_rate == 0 gets no channel and wfm_render_steps() is then literally wfm_synth_steps(), so every scene that does not ask for Doppler renders through exactly the path it always did — byte-identical, not merely equivalent.
doppler/doppler_rate arrive ranged-resolved, like freq/snr/f_end.
borrow is the channel a WFM_DOPPLER_PERSIST source keeps ACROSS segments: the composer owns it for the life of the scene and passes it in here, so the renderer uses it without adopting it and the geometry does not restart when the synth is torn down at a segment boundary. NULL means the ordinary case — the renderer creates and owns a channel if the source declares Doppler, and destroys it with itself.
Returns:
A heap renderer (caller wfm_render_destroy()s it), or NULL.
function wfm_compose_build_synth¶
Construct + configure the synth for one resolved source.
wfm_synth_state_t * wfm_compose_build_synth (
const wfm_source_t * src,
double fs,
size_t on_len,
double freq,
double snr,
double f_end,
unsigned epoch,
int seed_advance,
size_t instance
)
THE single synth-construction path (create + chirp-span pin + bits/symbols/RRC attach + per-repeat NOISE reseed) shared by the streaming composer and the Plan stimulus cache, so a cached per-source render is byte-identical to the composed one. freq/snr/f_end are passed already ranged-resolved by the caller; on_len pins a chirp's sweep to the on-time; epoch/seed_advance (a wfm_seed_advance_t) drive the per-repeat seed policy — epoch == 0 yields the unmodified seed. instance is the segment's repeats counter (0-based): a non-zero instance always reseeds the AWGN (fresh noise per burst instance, signal fixed, regardless of seed_advance); instance 0 is byte-identical to the pre-repeats behaviour.
Returns:
A heap synth (caller wfm_synth_destroy()s it), or NULL on failure.
function wfm_compose_create¶
Build a composer over a copy of segs .
wfm_compose_state_t * wfm_compose_create (
const wfm_segment_t * segs,
size_t n_segs,
int repeat,
int continuous
)
Parameters:
segsSegment list (copied; caller keeps ownership).n_segsNumber of segments (>= 1).repeatNon-zero: loop the whole sequence after the last segment.continuousNon-zero: never finish (implies repeat); execute always returnsmax.
Returns:
Heap state, or NULL on bad args / allocation / synth failure.
Note:
Caller must wfm_compose_destroy() when done.
function wfm_compose_destroy¶
Destroy a composer and its active synth.
Parameters:
stateMay be NULL.
function wfm_compose_draws¶
Replay the (epoch 0) instance timeline AND its drawn source values.
size_t wfm_compose_draws (
const wfm_segment_t * segs,
size_t n_segs,
wfm_draw_t * out,
size_t cap
)
Same size-then-fill protocol as wfm_compose_spans(): call once with cap 0 to size, then again with a buffer. Emits one row per SOURCE per instance, in stream order, because that is the granularity a per-source annotation or a scoring pipeline needs. Pass the RESOLVED segments (wfm_compose_segments() on a live composer) so intrinsic on-times are already folded in.
The rows are produced by the same wfm_draw_segment()/wfm_draw_source() calls the renderer resolves through, so a field added to the draw reaches both by construction rather than by a reviewer noticing.
Parameters:
segsResolved segment array.n_segsSegment count.outRow buffer (may be NULL when cap is 0).capCapacity of out in rows.
Returns:
Total rows in one pass of the spec (sum of n_sources over instances), regardless of cap.
function wfm_compose_execute¶
Emit up to max samples of the composed stream.
Returns:
Number of samples written: < max (or 0) signals the sequence finished (never, when continuous).
function wfm_compose_from_file¶
Build a composer from a JSON spec file.
Returns:
Composer state, or NULL on read/parse error.
function wfm_compose_from_json¶
Build a composer from a JSON spec string (for from-file).
Returns:
Composer state, or NULL on parse error / bad type / no segments.
function wfm_compose_from_json_why¶
The same, but able to say why a FRAME was refused.
A spec is the interface most likely to be hand-written, and a NULL return is the one answer that cannot teach anything. This runs wfm_source_frame_error over every parsed source before handing them to the composer — which asks the same question and would refuse either way — so the reason survives the boundary as a sentence instead of a pointer.
Only the frame rule reports this way. A parse error or a bad type is still a bare NULL, because those are cJSON's to describe and duplicating its diagnostics here would be a second opinion about the same text.
Parameters:
jsonthe spec.whyoptional; receives a STATIC message when a source's frame is refused, or NULL in every other case (including success). Passing NULL makes this exactly wfm_compose_from_json.
Returns:
Composer state, or NULL on parse error / bad type / no segments / a refused frame.
function wfm_compose_seed_advance¶
The composer's current seed-advance mode (a wfm_seed_advance_t ).
The composer is the SSOT for it: --from-file sets it from the spec and the flag path sets it from --seed-advance, so a serialiser must read it back from here rather than from whichever half happened to supply it.
Parameters:
stateCompose state (may be NULL →WFM_SEED_ADVANCE_NONE).
function wfm_compose_segments¶
Borrow the composer's stored segment list (for record / SigMF).
const wfm_segment_t * wfm_compose_segments (
const wfm_compose_state_t * state,
size_t * n_out,
int * repeat,
int * continuous
)
Parameters:
statethe composer.n_outreceives the segment count.repeatreceives the repeat flag (may be NULL).continuousreceives the continuous flag (may be NULL).
Returns:
Pointer to the internal segments (owned by the composer; valid until wfm_compose_destroy).
function wfm_compose_set_seed_advance¶
Choose how the seed advances on each repeat of a looped/continuous stream (a wfm_seed_advance_t ):
WFM_SEED_ADVANCE_NONE(default): byte-identical repeats.WFM_SEED_ADVANCE_NOISE: advance only the AWGN seed → a fresh noise realization each pass while the signal (LO / PN code / data / pulse) stays bit-identical (so a fixed preamble/code re-acquires every burst).WFM_SEED_ADVANCE_ALL: advance the whole seed → code, data, and noise all change (a fully stochastic stream).
Set before the first execute(); the first pass is always unchanged. An out-of-range mode is ignored.
Parameters:
stateCompose state (may be NULL).modeA wfm_seed_advance_t value.
function wfm_compose_spans¶
Replay the (epoch 0) instance timeline of a resolved segment list.
size_t wfm_compose_spans (
const wfm_segment_t * segs,
size_t n_segs,
wfm_span_t * out,
size_t cap
)
Walks every segment's repeats instances, re-deriving each instance's drawn delay/on/off exactly as the streaming composer will (identical draw hash), and fills out with up to cap spans in stream order. Returns the TOTAL instance count regardless of cap — call once with cap 0 to size, then again with a buffer. Pass the RESOLVED segments (wfm_compose_segments() on a live composer) so intrinsic on-times (dsss) are already folded in.
Assumes every segment builds: a segment that fails at render time (invalid burst geometry) degrades to its gaps only, so positions after it would shift relative to this replay.
Parameters:
segsResolved segment array.n_segsSegment count.outSpan buffer (may be NULL when cap is 0).capCapacity of out in spans.
Returns:
Total number of instances in one pass of the spec.
function wfm_draws_json¶
The same rows wfm_compose_draws() reports, as a JSON array.
One object per source per instance, in stream order, with the keys named after the wfm_draw_t fields. Exists so a binding can hand a caller its GROUND TRUTH without marshalling a struct array itself: a ranged field is only usable if what it drew can be read back, and scoring a receiver against a scene whose freq re-draws per instance means scoring against a number the caller does not otherwise have (doppler#1112).
Reads through wfm_compose_draws(), so it cannot disagree with the SigMF annotations, which read through it too.
Parameters:
segsResolved segment array (wfm_compose_segments()).n_segsSegment count.
Returns:
Heap JSON string the caller free()s; never NULL — the allocations go through the abort-on-OOM helpers. A spec with no rows yields [].
size_t n; int rp, ct;
const wfm_segment_t *segs = wfm_compose_segments(c, &n, &rp, &ct);
char *js = wfm_draws_json(segs, n);
puts(js);
free(js);
function wfm_render_destroy¶
Free a renderer and everything it owns. NULL-safe.
function wfm_render_noise_steps¶
Pull n samples of the source's NOISE FLOOR only, through the same channel.
What a gap renders (gh-409). The channel runs here too, and deliberately: an emitter does not stop moving because its burst ended, so a pass is continuous and during a gap the thing propagating is the noise floor. Skip the channel over gaps and doppler_rate across a multi-burst scene quietly means "rate per unit of ON time" instead of per second.
function wfm_render_steps¶
Pull exactly n samples fromr , through its channel if any.
function wfm_resolve_noise¶
Resolve a segment list's noise model in place (Phase 4b).
No-op for 1-source segments (keeps the bundled-synth path byte-identical). For a multi-source segment it sets one shared noise floor (from an explicit WFM_SYNTH_NOISE source, else the first snr-bearing source), cleans the signal sources, and appends a WFM_SYNTH_NOISE source at the floor — so the composer's accumulator just sums. May realloc each segment's sources. Idempotent.
wfm_compose_create() calls this on its private copy, so every face (CLI, JSON, Python) resolves identically.
Returns:
0 on success; -1 if a non-anchor source over-specifies (snr + level) or on allocation failure.
function wfm_snr_over_fs¶
SNR (dB) referred to fs, from a source's snr/snr_mode/sps/type.
The single source of truth for the Es/No, Eb/No, and over-fs conventions (snr_mode 0 auto / 1 fs / 2 ebno / 3 esno). wfm_resolve_noise() uses it to place the shared noise floor at level(anchor) − wfm_snr_over_fs(anchor), and the Plan stimulus engine reuses it to recompute the floor at an arbitrary swept SNR — so both agree to the bit.
For type=dsss the symbol is the outer data symbol. For a BURST that spans sf * sps samples (sf chips, sps samples per chip). For a CONTINUOUS async stream the data clock is independent of the code, so the span is fs / symbol_rate samples — passed as sym_span (non-integer), which OVERRIDES the sf·sps reconstruction when non-zero. auto picks esno, and esno/ebno convert as snr − 10·log10(span) (BPSK payload, so the two coincide). Every other type ignores sf and sym_span.
Parameters:
snr_mode0 auto, 1 fs, 2 ebno, 3 esno.typeA WFM_SYNTH_* waveform type (selects the auto convention).spsSamples per symbol/chip (≥1; <1 treated as 1).sfSpreading factor — chips per data symbol (burst dsss; ≥1, <1 treated as 1).sym_spanContinuous-dsss symbol span in samples (fs/symbol_rate); 0 = burst/non-dsss, derive fromsf·sps.snrThe declared SNR in dB.
Returns:
SNR over fs in dB.
function wfm_source_attach_dsss¶
Attach a dsss source's data to a freshly-created synth.
The single dsss-attach path, called by BOTH synth-construction faces (wfm_compose_build_synth and the standalone wfm_source_to_synth), so the two cannot drift on how a dsss stream is configured. Selects on symbol_rate: 0 → the burst form (wfm_synth_set_dsss); > 0 → the continuous form (wfm_synth_set_dsss_cont) with chips_per_symbol = (fs/sps)/symbol_rate, taking the data from the payload when one is supplied (bits) and otherwise from the seeded PN. A no-op for a non-dsss source.
Parameters:
synA synth from wfm_synth_create() withwtype == WFM_SYNTH_DSSS.srcThe source (codes, payload, symbol_rate, pn config).fsSegment sample rate (Hz) — the continuous chip rate is fs/sps.
Returns:
0 on success (or non-dsss no-op); -1 on invalid geometry.
function wfm_source_attach_frame¶
Attach an unspread source's bit pattern, framed or not.
The type=bits counterpart of wfm_source_attach_dsss(), and called from the same two places for the same reason. When the source carries a frame, the pattern handed to wfm_synth_set_bits() is wfm_frame_bits() of [preamble x reps | sync | payload | crc] rather than the payload alone — so the layout, the CRC's position and its bit order come from the one descriptor that the DSSS path and the receiver already read.
The frame CYCLES, exactly as an unframed pattern does: one descriptor fills whatever length is asked for, which is what turns a one-frame description into a multi-frame record.
Parameters:
synA synth from wfm_synth_create() withwtype == WFM_SYNTH_BITS.srcThe source (pattern, modulation, and any frame fields).
Returns:
0 on success (or a non-bits/no-pattern no-op); -1 on failure.
function wfm_source_create_snr¶
Resolve a source's (snr, snr_mode) into the pair to hand to wfm_synth_create() .
wfm_synth_create() runs before a dsss source's codes are attached, so it cannot know the spreading factor its own esno would need. This helper — the one create-time entry point shared by the composer (wfm_compose_build_synth) and the standalone-Synth bridge (wfm_source_to_synth), so every face agrees to the bit — converts a dsss source's SNR to the over-fs reference (via wfm_snr_over_fs; the burst span is sf = n_data_code, a continuous stream uses fs/symbol_rate) and returns snr_mode=fs; every other type passes through unchanged.
Parameters:
srcThe source (supplies type/sps/snr_mode/n_data_code/ symbol_rate).fsSegment sample rate (Hz) — needed for a continuous dsss source'sfs/symbol_ratespan; ignored otherwise.snrThe declared SNR in dB, already ranged-resolved.snr_modeReceives the snr_mode for create.
Returns:
The SNR in dB for create.
function wfm_source_describe_frame¶
Describe a source's frame: the fields, the stages, and their covers.
The ONE place a wfm_source_t's framing flags become a description, read by the type=bits assembler and the DSSS spreader alike, so the two cannot disagree about which stage covers what. For a DSSS burst the acquisition preamble is deliberately NOT a field: it is unmodulated and unspread, so it sits outside everything a stage can cover.
Parameters:
srcthe source.dreceives the description.
Returns:
0, or non-zero if the source cannot be described.
function wfm_source_dsss_nchips¶
Chips one DSSS BURST from this source occupies, description and all.
What sizes a lone dsss segment's intrinsic on-time. It reads the same description the burst is assembled from, so a stage that lengthens the frame a rate-1/2 inner code doubles it lengthens the segment by the same arithmetic instead of by a second copy of it.
Parameters:
srcthe source.
Returns:
burst chips, or 0 for a non-dsss source, a CONTINUOUS dsss source (which has no intrinsic length), or an empty/refused geometry.
function wfm_source_frame_error¶
NULL when this source's frame fields can be honoured; else why not.
ONE rule, asked by all three faces — the wfmgen CLI before it generates, the standalone Synth through wfm_source_to_synth, and the composer through wfm_compose_create — because the alternative is what shipped: the flags were accepted, stored and readable back on every face, and applied on none of them, so a caller who asked for a framed waveform silently got an unframed one.
A frame needs a payload, and the unspread types that source their symbols from the PN LFSR (bpsk/qpsk/pn) have no length to bound one. So the frame is honoured where the payload is EXPLICIT — type=bits with a pattern, which modulation already maps to BPSK or QPSK — and refused with a reason everywhere else.
Parameters:
srcThe source.
Returns:
NULL if there is nothing wrong, else a static message.
function wfm_source_has_frame¶
Non-zero when this source describes a FRAME.
A preamble or a sync word is what says "framed". Deliberately not crc: it defaults to crc16 on every source ([[module.wfm_compose.source.fields]] and wfmgen alike), so reading it as intent would silently append a trailer to every unframed bit pattern anyone has ever generated. With neither a preamble nor a sync word, crc stays inert exactly as it always was.
Parameters:
srcThe source; NULL reads as unframed.
function wfm_spec_headroom¶
The top-level headroom (dB) from a spec JSON, or 0 if absent.
Lets --from-file reproduce a recorded --headroom; the value is a writer gain, so it lives outside the composer state.
function wfm_spec_template_json¶
A ready-to-edit example spec in the canonical from-file schema.
Returns a representative multi-segment template — an inline tone, an RRC-shaped QPSK-from-bits burst with a trailing gap, and a two-source additive sum mix — serialised with wfm_spec_to_json(), so it is valid by construction and round-trips through wfm_compose_from_json() unchanged. It therefore doubles as a working starting point for wfmgen --from-file, not just documentation: dump it, edit the fields, feed it back.
Returns:
malloc'd JSON (caller frees), or NULL on allocation failure.
function wfm_spec_to_json¶
Serialise a spec to a JSON string (for record).
char * wfm_spec_to_json (
const wfm_segment_t * segs,
size_t n_segs,
int repeat,
int continuous,
int seed_advance,
double headroom
)
seed_advance (a wfm_seed_advance_t) and headroom (dB of output backoff applied at the writer, not the composer) are each emitted as a top-level field only when non-default, so an unrecorded run and any older spec stay byte-identical. Read headroom back with wfm_spec_headroom(); the parser reads seed_advance straight onto the composer.
seed_advance is a parameter rather than something read from segs because it is a property of the whole stream, like repeat/continuous. Omitting it is what made a recorded run replay a DIFFERENT waveform (doppler#978): the key was parsed and never written, so the round-trip silently fell back to NONE and every loop after the first came out identical.
Returns:
malloc'd JSON (caller frees), or NULL on allocation failure.
The documentation for this class was generated from the following file native/inc/wfm/wfm_compose.h