Skip to content

File wfm_compose.h

FileList > doppler > wfm > wfm_compose.h

Go to the source code of this file

Multi-segment waveform composer (Phase B). More...

  • #include "doppler/clib_common.h"
  • #include "doppler/wfm_synth/wfm_synth_core.h"
  • #include "doppler/wfm/wfm_frame.h"
  • #include "doppler/wfm/wfm_data.h"
  • #include "doppler/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
typedef struct wfm_compose_state dp_wfm_compose_state_t
enum wfm__compose_8h_1abc5c98fcc1211af2b80116dd6e0a035d
Per-field "draw uniformly each repeat" flags ( ranged bitmask).
enum wfm_bitmod_t
How a WFM_SYNTH_BITS source maps its payload to symbols.
enum wfm_doppler_lifetime_t
When a source's Doppler channel restarts.
typedef void(* wfm_feed_fn
Where a channel renderer's INPUT comes from, when it is not a synth.
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 Attributes

Type Name
const char dp_wfm_why_count_derived
The reason dp_wfm_scene_error() gives a count beside a segment whose sources set its length (dp_wfm_segment_sets_length() ) exported so the wfmgen CLI can name its flag beside it.
const char dp_wfm_why_dsss_cont_no_data_code
The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.
const char dp_wfm_why_dsss_cont_rate
The reason dp_wfm_scene_error() gives a continuous dsss source whose chip rate is below its symbol rate exported so the wfmgen CLI can name the values beside it, by identity.
const char dp_wfm_why_dsss_empty
The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.
const char dp_wfm_why_dsss_frame_no_data_code
The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.
const char dp_wfm_why_pn_poly
The reason dp_wfm_source_error() gives for apn_poly wider than its register exported so a face that knows the values (the wfmgen CLI) can name them beside it, by identity rather than by matching text.
const char dp_wfm_why_retired_bits
The reason dp_wfm_source_error() gives forretired_bits set: Python's retiredbits= (andpayload= ,pattern= ), named once. The CLI's and a scene's RETIRED tables say the same in their own spelling (doppler#1718).
const char dp_wfm_why_retired_crc
const char dp_wfm_why_retired_sync
The reasons dp_wfm_source_error() gives forretired_sync andretired_crc set.

Public Functions

Type Name
wfm_render_t * dp_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, dp_doppler_channel_state_t * borrow)
Build a source's renderer — dp_wfm_compose_build_synth plus the clock-Doppler channel the source declares, if it declares one.
dp_wfm_synth_state_t * dp_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.
dp_wfm_compose_state_t * dp_wfm_compose_create (const wfm_segment_t * segs, size_t n_segs, int repeat, int continuous)
Build a composer over a copy of segs .
dp_wfm_compose_state_t * dp_wfm_compose_create_why (const wfm_segment_t * segs, size_t n_segs, int repeat, int continuous, const char ** why)
dp_wfm_compose_create() , able to say why the scene was refused.
void dp_wfm_compose_destroy (dp_wfm_compose_state_t * state)
Destroy a composer and its active synth.
size_t dp_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 dp_wfm_compose_execute (dp_wfm_compose_state_t * state, float _Complex * out, size_t max)
Emit up to max samples of the composed stream.
size_t dp_wfm_compose_execute_rate (dp_wfm_compose_state_t * state, float _Complex * out, size_t max, double * fs)
Emit up to max samples, all at ONE sample rate, and say which.
dp_wfm_compose_state_t * dp_wfm_compose_from_file (const char * path)
dp_wfm_compose_state_t * dp_wfm_compose_from_file_why (const char * path, const char ** why)
dp_wfm_compose_from_file , able to say why a scene was refused.
dp_wfm_compose_state_t * dp_wfm_compose_from_json (const char * json)
Build a composer from a JSON spec string (for from-file).
dp_wfm_compose_state_t * dp_wfm_compose_from_json_at (const char * json, const char * base, const char ** why)
Build a composer from a JSON spec file.
dp_wfm_compose_state_t * dp_wfm_compose_from_json_data (const char * json, const char * base, const char * data_file, const char ** why)
dp_wfm_compose_from_json_at() , replaying a record by its data.
dp_wfm_compose_state_t * dp_wfm_compose_from_json_why (const char * json, const char ** why)
The same, but able to say why a FRAME was refused.
dp_doppler_channel_state_t * dp_wfm_compose_persist_channel (const wfm_source_t * src, double fs, double doppler, double doppler_rate, dp_doppler_channel_state_t ** slot)
The PERSIST channel for one (segment, source) slot, created on first use, or NULL when the source does not persist.
int dp_wfm_compose_seed_advance (const dp_wfm_compose_state_t * state)
The composer's current seed-advance mode (a wfm_seed_advance_t ).
const wfm_segment_t * dp_wfm_compose_segments (const dp_wfm_compose_state_t * state, size_t * n_out, int * repeat, int * continuous)
Borrow the composer's stored segment list (for record / SigMF).
void dp_wfm_compose_set_data_pacing (dp_wfm_compose_state_t * state, wfm_data_pacing_t pacing)
Pace a composer's data sources: WFM_DATA_PACED under --realtime .
void dp_wfm_compose_set_seed_advance (dp_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 dp_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 * dp_wfm_draws_json (const wfm_segment_t * segs, size_t n_segs)
The same rows dp_wfm_compose_draws() reports, as a JSON array.
wfm_frame_desc_t * dp_wfm_frame_copy (const wfm_frame_desc_t * d)
Deep-copy a description: the struct and each literal field's bits.
void dp_wfm_frame_free (wfm_frame_desc_t * d)
Free a description returned by dp_wfm_frame_from_json() , bits and all. NULL is a no-op.
wfm_frame_desc_t * dp_wfm_frame_from_json (const char * json, const char ** why)
Read a frame description from its JSON form.
wfm_frame_desc_t * dp_wfm_frame_refuse_text (const char * text, const char ** why)
Refuse text for a source's frame= : it takes a description.
char * dp_wfm_frame_to_json (const wfm_frame_desc_t * d)
Write a description as its JSON frame object, the text dp_wfm_frame_from_json() reads back.
const char * dp_wfm_framing_flags_error (const wfm_source_t * src)
Why --sync /--crc /--acq-code cannot frame this source, or NULL.
void dp_wfm_render_destroy (wfm_render_t * r)
Free a renderer and everything it owns. NULL-safe.
wfm_render_t * dp_wfm_render_from_feed (const wfm_source_t * src, double fs, double doppler, double doppler_rate, dp_doppler_channel_state_t * borrow, size_t delay, size_t on, wfm_feed_fn feed, void * feed_ctx)
A renderer whose input is a caller-supplied feed, not a live synth.
void dp_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 dp_wfm_render_set_input_timeline (wfm_render_t * r, size_t delay, size_t on)
Tell a Doppler renderer where its INPUT's phases fall.
void dp_wfm_render_steps (wfm_render_t * r, float _Complex * dst, size_t n)
Pull exactly n samples fromr , through its channel if any.
int dp_wfm_resolve_noise (wfm_segment_t * segs, size_t n)
Resolve a segment list's noise model in place (Phase 4b).
const char * dp_wfm_scene_error (const wfm_segment_t * segs, size_t n_segs, int repeat, int continuous)
Why a scene cannot be composed, or NULL: the one validator.
int dp_wfm_segment_sets_length (const wfm_segment_t * seg)
Whether a segment's sources SET its on-time, so its num_samples is derived and a count given beside them refused.
double dp_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 dp_wfm_source_attach_dsss (dp_wfm_synth_state_t * syn, const wfm_source_t * src, double fs)
Attach a dsss source's data to a freshly-created synth.
int dp_wfm_source_attach_frame (dp_wfm_synth_state_t * syn, const wfm_source_t * src)
Attach an unspread source's bit pattern, framed or not.
size_t dp_wfm_source_bits_refuse_text (const char * text, uint8_t * out, size_t max_out, const char ** why)
Refuse text for a source's bit field: an object takes bits.
int dp_wfm_source_common_frame (const wfm_source_t * src, const wfm_seq_t * sync, int crc, wfm_frame_desc_t * d)
The common frame as a description: [preamble x reps | sync | data | crc] , built by the ONE function that builds it.
size_t dp_wfm_source_crc_refuse_text (const char * text, uint8_t * out, size_t max_out, const char ** why)
Refuse text for the retired crc= : a CRC is a stage of the frame description.
double dp_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 dp_wfm_synth_create() .
uint64_t dp_wfm_source_data_frames (const wfm_source_t * src)
Frames a FINITE data source makes, ceil(bits / LEN) ; 0 for a stream or none. On continuous dsss, which has no frame, its bits.
int dp_wfm_source_data_is_stream (const wfm_source_t * src)
Whether a source's data is a stream: data_from_file is- .
uint64_t dp_wfm_source_data_samples (const wfm_source_t * src, double fs, uint64_t frames)
Samples the first frames frames of a source's data occupy; 0 with no data.
double dp_wfm_source_dsss_cps (const wfm_source_t * src, double fs)
Chips per data symbol of a CONTINUOUS dsss source at fs .
size_t dp_wfm_source_dsss_nchips (const wfm_source_t * src)
Chips one DSSS BURST from this source occupies, description and all.
const char * dp_wfm_source_error (const wfm_source_t * src)
NULL when this source can be built; else why not, as a sentence.
const char * dp_wfm_source_frame_error (const wfm_source_t * src)
NULL when this source's frame fields can be honoured; else why not.
int dp_wfm_source_has_frame (const wfm_source_t * src)
Non-zero when this source describes a FRAME.
size_t dp_wfm_source_sync_refuse_text (const char * text, uint8_t * out, size_t max_out, const char ** why)
Refuse text for the retired sync= : a sync word is a field of the frame description.
int dp_wfm_source_synth_type (const wfm_source_t * src)
The synth type to create this source with.
const char * dp_wfm_source_to_synth_error (const wfm_source_t * src, double fs)
Why dp_wfm_source_to_synth() refused this source, or NULL.
double dp_wfm_spec_headroom (const char * json)
The top-level headroom (dB) from a spec JSON, or 0 if absent.
char * dp_wfm_spec_template_json (void)
A ready-to-edit example spec in the canonical from-file schema.
char * dp_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).
int dp_wfm_synth_attach_data (dp_wfm_synth_state_t * syn, const wfm_frame_desc_t * d, const wfm_frame_ops_t * ops, wfm_data_src_t * src, wfm_data_pacing_t pacing, int modulation)
Drive a type=bits synth from a frame whose payload is a data source.
const wfm_data_src_t * dp_wfm_synth_data_source (const dp_wfm_synth_state_t * syn)
The data source a synth pulls from (dp_wfm_synth_attach_data), or NULL: its stats are the run's truth for scoring.
void dp_wfm_synth_set_data_pacing (dp_wfm_synth_state_t * syn, wfm_data_pacing_t pacing)
Pace a synth's data source: WFM_DATA_PACED for --realtime .

Public Static Functions

Type Name
double dp_wfm_scene_fs (const wfm_segment_t * segs, size_t n_segs)
The ONE answer to "what is this stream's sample rate": the fs every segment shares, or 0.0 when they differ.

Macros

Type Name
define WFM_NUM_SAMPLES_PLAIN ((size\_t)1024)
A plain segment's on-time when its num_samples is 0 and no source sets one: what--count , a scene andSegment default to.

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: dp_wfm_compose_create -> dp_wfm_compose_execute* -> dp_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
};
dp_wfm_compose_state_t *c = dp_wfm_compose_create(segs, 2, 0, 0);
float _Complex buf[4096];
size_t n;
while ((n = dp_wfm_compose_execute(c, buf, 4096)) > 0) { ... }
dp_wfm_compose_destroy(c);

Public Types Documentation

typedef dp_wfm_compose_state_t

typedef struct wfm_compose_state dp_wfm_compose_state_t;

Opaque composer state.


enum wfm__compose_8h_1abc5c98fcc1211af2b80116dd6e0a035d

Per-field "draw uniformly each repeat" flags ( ranged bitmask).

enum wfm__compose_8h_1abc5c98fcc1211af2b80116dd6e0a035d {
    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.

enum wfm_bitmod_t {
    WFM_BITMOD_NONE = 0,
    WFM_BITMOD_BPSK = 1,
    WFM_BITMOD_QPSK = 2
};

Order IS the wire value; BITMOD_NAMES[] and the [[enum]] bitmod manifest are held to this by make lint-wfm-enum-tables.


enum wfm_doppler_lifetime_t

When a source's Doppler channel restarts.

enum wfm_doppler_lifetime_t {
    WFM_DOPPLER_PER_INSTANCE = 0,
    WFM_DOPPLER_PERSIST = 1
};

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 ranged doppler.
  • PERSIST carries 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 which doppler_rate means 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_feed_fn

Where a channel renderer's INPUT comes from, when it is not a synth.

typedef void(* wfm_feed_fn) (void *ctx, float _Complex *dst, size_t n, int signal);

Called with the next n input samples wanted, signal non-zero for ON samples and zero for noise-only ones (the delay and the gap), in INPUT order: the ON calls together cover the burst exactly once, front to back.

Parameters:

  • ctx The pointer given to dp_wfm_render_from_feed().
  • dst Write n samples here.
  • n Samples wanted.
  • signal Non-zero: ON samples. Zero: noise-only.

typedef wfm_render_t

One source's renderer: its synth, plus its Doppler channel.

typedef struct wfm_render wfm_render_t;


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.

enum wfm_snr_mode_t {
    WFM_SNR_AUTO = 0,
    WFM_SNR_FS = 1,
    WFM_SNR_EBNO = 2,
    WFM_SNR_ESNO = 3
};

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 Attributes Documentation

variable dp_wfm_why_count_derived

The reason dp_wfm_scene_error() gives a count beside a segment whose sources set its length (dp_wfm_segment_sets_length() ) exported so the wfmgen CLI can name its flag beside it.

const char dp_wfm_why_count_derived[];


variable dp_wfm_why_dsss_cont_no_data_code

The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.

const char dp_wfm_why_dsss_cont_no_data_code[];


variable dp_wfm_why_dsss_cont_rate

The reason dp_wfm_scene_error() gives a continuous dsss source whose chip rate is below its symbol rate exported so the wfmgen CLI can name the values beside it, by identity.

const char dp_wfm_why_dsss_cont_rate[];


variable dp_wfm_why_dsss_empty

The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.

const char dp_wfm_why_dsss_empty[];


variable dp_wfm_why_dsss_frame_no_data_code

The reasons dp_wfm_source_error() gives a dsss source missing a code (doppler#1696): a burst whose frame has no data_code to spread it, a burst with neither a preamble nor a frame, and a continuous stream with no data_code. Exported so a test or a face can hold a refusal to its reason by identity.

const char dp_wfm_why_dsss_frame_no_data_code[];


variable dp_wfm_why_pn_poly

The reason dp_wfm_source_error() gives for apn_poly wider than its register exported so a face that knows the values (the wfmgen CLI) can name them beside it, by identity rather than by matching text.

const char dp_wfm_why_pn_poly[];


variable dp_wfm_why_retired_bits

The reason dp_wfm_source_error() gives forretired_bits set: Python's retiredbits= (andpayload= ,pattern= ), named once. The CLI's and a scene's RETIRED tables say the same in their own spelling (doppler#1718).

const char dp_wfm_why_retired_bits[];


variable dp_wfm_why_retired_crc

const char dp_wfm_why_retired_crc[];

variable dp_wfm_why_retired_sync

The reasons dp_wfm_source_error() gives forretired_sync andretired_crc set.

const char dp_wfm_why_retired_sync[];

One sentence each, naming frame=; the CLI and a scene say the same in their own spelling. Exposed so a test can pin the wording once.


Public Functions Documentation

function dp_wfm_compose_build_render

Build a source's renderer — dp_wfm_compose_build_synth plus the clock-Doppler channel the source declares, if it declares one.

wfm_render_t * dp_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,
    dp_doppler_channel_state_t * borrow
) 

THE pull path. Both faces go through dp_wfm_render_steps() rather than calling dp_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 dp_wfm_render_steps() is then literally dp_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 dp_wfm_render_destroy()s it), or NULL.


function dp_wfm_compose_build_synth

Construct + configure the synth for one resolved source.

dp_wfm_synth_state_t * dp_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 dp_wfm_synth_destroy()s it), or NULL on failure.


function dp_wfm_compose_create

Build a composer over a copy of segs .

dp_wfm_compose_state_t * dp_wfm_compose_create (
    const wfm_segment_t * segs,
    size_t n_segs,
    int repeat,
    int continuous
) 

Parameters:

  • segs Segment list (copied; caller keeps ownership).
  • n_segs Number of segments (>= 1).
  • repeat Non-zero: loop the whole sequence after the last segment.
  • continuous Non-zero: never finish (implies repeat); execute always returns max.

Returns:

Heap state, or NULL on bad args / allocation / synth failure.

Note:

Caller must dp_wfm_compose_destroy() when done.


function dp_wfm_compose_create_why

dp_wfm_compose_create() , able to say why the scene was refused.

dp_wfm_compose_state_t * dp_wfm_compose_create_why (
    const wfm_segment_t * segs,
    size_t n_segs,
    int repeat,
    int continuous,
    const char ** why
) 

The scene is asked dp_wfm_scene_error() before anything is built, and its sentence is what why receives the same sentence the wfmgen CLI, a scene read by dp_wfm_compose_from_json_why() and the standalone Synth report, because it is the same validator. It is the create the generated Composer([...]) calls (just-makeit's create_why), so a refused composer raises ValueError(<the reason>).

Parameters:

Returns:

Heap state, or NULL as for dp_wfm_compose_create().

wfm_source_t  src = { .type = WFM_SYNTH_DSSS, .sps = 2 };   // no codes
wfm_segment_t seg = { .sources = &src, .n_sources = 1, .fs = 1e6,
                      .num_samples = 64 };
const char   *why = NULL;
dp_wfm_compose_state_t *c = dp_wfm_compose_create_why (&seg, 1, 0, 0, &why);
if (c != NULL || why != dp_wfm_why_dsss_empty)   // refused, and says why
  return 1;

function dp_wfm_compose_destroy

Destroy a composer and its active synth.

void dp_wfm_compose_destroy (
    dp_wfm_compose_state_t * state
) 

Parameters:

  • state May be NULL.

function dp_wfm_compose_draws

Replay the (epoch 0) instance timeline AND its drawn source values.

size_t dp_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 dp_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 (dp_wfm_compose_segments() on a live composer) so intrinsic on-times are already folded in.

The rows are produced by the same dp_wfm_draw_segment()/dp_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:

  • segs Resolved segment array.
  • n_segs Segment count.
  • out Row buffer (may be NULL when cap is 0).
  • cap Capacity of out in rows.

Returns:

Total rows in one pass of the spec (sum of n_sources over instances), regardless of cap.


function dp_wfm_compose_execute

Emit up to max samples of the composed stream.

size_t dp_wfm_compose_execute (
    dp_wfm_compose_state_t * state,
    float _Complex * out,
    size_t max
) 

Returns:

Number of samples written: < max (or 0) signals the sequence finished (never, when continuous).


function dp_wfm_compose_execute_rate

Emit up to max samples, all at ONE sample rate, and say which.

size_t dp_wfm_compose_execute_rate (
    dp_wfm_compose_state_t * state,
    float _Complex * out,
    size_t max,
    double * fs
) 

dp_wfm_compose_execute() for an output that states a rate per block: it stops early where the next segment's fs differs from the samples already written, so every block it returns has one rate. A scene whose segments share an fs never stops early, and its samples are the same, byte for byte, as dp_wfm_compose_execute()'s. A short return therefore does NOT mean the scene finished; 0 does.

wfm_source_t  src     = { .type = WFM_SYNTH_TONE, .snr = 100.0 };
wfm_segment_t segs[2] = {
  { .sources = &src, .n_sources = 1, .fs = 6e6, .num_samples = 8 },
  { .sources = &src, .n_sources = 1, .fs = 2e6, .num_samples = 8 },
};
dp_wfm_compose_state_t *c = dp_wfm_compose_create (segs, 2, 0, 0);
float _Complex buf[64];
double         fs = 0.0;
size_t a = dp_wfm_compose_execute_rate (c, buf, 64, &fs); // 8 at 6e6
int    ok = a == 8 && fs == 6e6;
size_t b = dp_wfm_compose_execute_rate (c, buf, 64, &fs); // 8 at 2e6
ok = ok && b == 8 && fs == 2e6;
ok = ok && dp_wfm_compose_execute_rate (c, buf, 64, &fs) == 0;
dp_wfm_compose_destroy (c);
return ok ? 0 : 1;

Parameters:

  • state the composer.
  • out destination, max samples.
  • max capacity of out.
  • fs receives the rate of the samples written (left untouched when none are).

Returns:

samples written; 0 when the scene has finished.


function dp_wfm_compose_from_file

dp_wfm_compose_state_t * dp_wfm_compose_from_file (
    const char * path
) 

function dp_wfm_compose_from_file_why

dp_wfm_compose_from_file , able to say why a scene was refused.

dp_wfm_compose_state_t * dp_wfm_compose_from_file_why (
    const char * path,
    const char ** why
) 

Reads path and hands its text to dp_wfm_compose_from_json_why, so the reason is that function's: a retired key or a refused frame, named. A file that cannot be read gives NULL and leaves why NULL.

Parameters:

Returns:

Composer state, or NULL on read/parse error / a refused scene.


function dp_wfm_compose_from_json

Build a composer from a JSON spec string (for from-file).

dp_wfm_compose_state_t * dp_wfm_compose_from_json (
    const char * json
) 

Returns:

Composer state, or NULL on parse error / bad type / no segments.


function dp_wfm_compose_from_json_at

Build a composer from a JSON spec file.

dp_wfm_compose_state_t * dp_wfm_compose_from_json_at (
    const char * json,
    const char * base,
    const char ** why
) 

Returns:

Composer state, or NULL on read/parse error.

dp_wfm_compose_from_json_why(), reading a scene that lives in base.

A source's relative "data_from_file" is the scene's: it resolves against base, the directory the scene was read from, so a scene and its data move together and replay from anywhere. NULL (as from_json_why passes) leaves a relative path relative to the caller's working directory.

Parameters:

  • json the scene's text.
  • base the scene's directory, or NULL.
  • why optional; receives a static reason for a refusal.

Returns:

the composer, or NULL.

const char *why = NULL;
dp_wfm_compose_state_t *c = dp_wfm_compose_from_json_at (
    "{\"segments\":[{\"type\":\"tone\",\"num_samples\":8}]}", ".",
    &why);
if (!c)
  return 1;
dp_wfm_compose_destroy (c);

function dp_wfm_compose_from_json_data

dp_wfm_compose_from_json_at() , replaying a record by its data.

dp_wfm_compose_state_t * dp_wfm_compose_from_json_data (
    const char * json,
    const char * base,
    const char * data_file,
    const char ** why
) 

A --record stores each data source's truth as "data_sent": the frames, the fill bits padding the last, the idle frames, and for a file or stdin the bits read and their dp_hash64 (payload-data-source.md §4.8). A replay identifies a file by that content, not by its name:

  • a "data_from_file" whose "data_sent" carries a hash is refused unless the file's length in bits and hash are the record's. The reason names the file and both hashes;
  • a "data_from_file": "-" is a run read from stdin, whose octets are gone. It is refused unless data_file names the file that held them, which is then checked the same way. - again is refused: a pipe could only be checked after it had been sent;
  • a data_file that no "-" source takes is refused: a scene carries its own data.

Every check is made before anything is built. A scene with no hash (one written by hand) replays its files unchecked.

Parameters:

  • json the scene's text.
  • base the scene's directory, or NULL.
  • data_file the file a record's stdin is replayed from (--data-from-file given again), as typed: relative to the working directory, not to base. NULL for none.
  • why optional; receives the reason for a refusal. A mismatch's names the numbers, so it is formatted into a thread-local buffer, valid until this thread reads another scene; never freed by the caller.

Returns:

the composer, or NULL.

const char *why = NULL;
dp_wfm_compose_state_t *c = dp_wfm_compose_from_json_data (
    "{\"segments\":[{\"type\":\"bits\",\"data_from_file\":\"-\"}]}",
    NULL, NULL, &why);
if (c || !why) // stdin with no file given again: refused, with a reason
  return 1;

function dp_wfm_compose_from_json_why

The same, but able to say why a FRAME was refused.

dp_wfm_compose_state_t * dp_wfm_compose_from_json_why (
    const char * json,
    const char ** why
) 

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 dp_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:

  • json the spec.
  • why optional; receives a STATIC message when a source's frame is refused, or NULL in every other case (including success). Passing NULL makes this exactly dp_wfm_compose_from_json.

Returns:

Composer state, or NULL on parse error / bad type / no segments / a refused frame.


function dp_wfm_compose_persist_channel

The PERSIST channel for one (segment, source) slot, created on first use, or NULL when the source does not persist.

dp_doppler_channel_state_t * dp_wfm_compose_persist_channel (
    const wfm_source_t * src,
    double fs,
    double doppler,
    double doppler_rate,
    dp_doppler_channel_state_t ** slot
) 

The one place that decides "does this instance borrow a scene-owned channel": the composer and the Plan both call it, so they cannot disagree. NULL for a per_instance source, for a NULL slot, and for an instance that drew no Doppler at all (the channel then does not exist for it).

Parameters:

  • src The source.
  • fs Sample rate, Hz.
  • doppler This instance's drawn Doppler, ppm.
  • doppler_rate This instance's drawn Doppler rate, ppm/s.
  • slot The scene-owned slot for this (segment, source).

function dp_wfm_compose_seed_advance

The composer's current seed-advance mode (a wfm_seed_advance_t ).

int dp_wfm_compose_seed_advance (
    const dp_wfm_compose_state_t * state
) 

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:

  • state Compose state (may be NULL → WFM_SEED_ADVANCE_NONE).

function dp_wfm_compose_segments

Borrow the composer's stored segment list (for record / SigMF).

const wfm_segment_t * dp_wfm_compose_segments (
    const dp_wfm_compose_state_t * state,
    size_t * n_out,
    int * repeat,
    int * continuous
) 

Each source's data_sent holds what its data source has sent so far in the latest instance, brought up to date by this call, so a record or a SigMF sidecar written after a run carries the run's truth.

Parameters:

  • state the composer.
  • n_out receives the segment count.
  • repeat receives the repeat flag (may be NULL).
  • continuous receives the continuous flag (may be NULL).

Returns:

Pointer to the internal segments (owned by the composer; valid until dp_wfm_compose_destroy).


function dp_wfm_compose_set_data_pacing

Pace a composer's data sources: WFM_DATA_PACED under --realtime .

void dp_wfm_compose_set_data_pacing (
    dp_wfm_compose_state_t * state,
    wfm_data_pacing_t pacing
) 

Applied to the synths already built create builds the first segment's, before this can be called and to every synth built from here on, so a data stream with nothing yet sends an idle frame of fill rather than waiting (dp_wfm_data_frame, the one rule). The default, WFM_DATA_UNPACED, waits.


function dp_wfm_compose_set_seed_advance

Choose how the seed advances on each repeat of a looped/continuous stream (a wfm_seed_advance_t ):

void dp_wfm_compose_set_seed_advance (
    dp_wfm_compose_state_t * state,
    int mode
) 

  • 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:

  • state Compose state (may be NULL).
  • mode A wfm_seed_advance_t value.

function dp_wfm_compose_spans

Replay the (epoch 0) instance timeline of a resolved segment list.

size_t dp_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 (dp_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:

  • segs Resolved segment array.
  • n_segs Segment count.
  • out Span buffer (may be NULL when cap is 0).
  • cap Capacity of out in spans.

Returns:

Total number of instances in one pass of the spec.


function dp_wfm_draws_json

The same rows dp_wfm_compose_draws() reports, as a JSON array.

char * dp_wfm_draws_json (
    const wfm_segment_t * segs,
    size_t n_segs
) 

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 dp_wfm_compose_draws(), so it cannot disagree with the SigMF annotations, which read through it too.

Parameters:

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 = dp_wfm_compose_segments(c, &n, &rp, &ct);
char *js = dp_wfm_draws_json(segs, n);
puts(js);
free(js);

function dp_wfm_frame_copy

Deep-copy a description: the struct and each literal field's bits.

wfm_frame_desc_t * dp_wfm_frame_copy (
    const wfm_frame_desc_t * d
) 

A source borrows its description; a holder that must outlive the caller's (the composer, a Python Synth) takes a copy instead, and releases it with dp_wfm_frame_free(). A later change to the original, or freeing it, does not reach the copy.

Parameters:

  • d the description to copy; NULL gives NULL.

Returns:

the owned copy.

wfm_frame_desc_t *a
    = dp_wfm_frame_from_json ("{\"fields\": [{\"spec\": \"1010\"}]}", NULL);
wfm_frame_desc_t *b = dp_wfm_frame_copy (a);
dp_wfm_frame_free (a);   // b is unaffected
// b->field[0].seq.len == 4
dp_wfm_frame_free (b);

function dp_wfm_frame_free

Free a description returned by dp_wfm_frame_from_json() , bits and all. NULL is a no-op.

void dp_wfm_frame_free (
    wfm_frame_desc_t * d
) 

dp_wfm_frame_free (dp_wfm_frame_from_json ("{\"fields\": []}", NULL));
dp_wfm_frame_free (NULL);   // nothing to free

function dp_wfm_frame_from_json

Read a frame description from its JSON form.

wfm_frame_desc_t * dp_wfm_frame_from_json (
    const char * json,
    const char ** why
) 

The form a scene's "frame" key holds, and what wfmgen --frame FILE reads — one reader for both: {"fields": [...], "stages": [...]}. A field with bits is its Field text, "spec" ("0x1ACFFC1D", "pn:31:5*4"); a derived field is its "bits" and the "derived_by" stage (index plus one). A stage names its "kind" ("crc16", "rs", "randomise", "conv", "interleave", or a number from WFM_STAGE_USER up) and its cover as "first_field"/"n_fields", plus "depth", "unit_bits" and "emit_num"/"emit_den" where the kind uses them.

A malformed description is REFUSED, never salvaged: a frame read wrong builds a waveform that looks fine and is not the one described. Whether it lays out is a separate question, asked by dp_wfm_source_frame_error() once a source carries it.

Parameters:

  • json the frame object, NUL-terminated.
  • why receives a static reason on failure; may be NULL.

Returns:

the description, owning its literal bits (free it with dp_wfm_frame_free()), or NULL if the text is not a frame object.

const char       *why;
wfm_frame_desc_t *d = dp_wfm_frame_from_json (
    "{\"fields\": [{\"name\": \"sync\", \"spec\": \"0x1ACFFC1D\"},"
    "              {\"name\": \"data\", \"spec\": \"pn:31:5*4\"}]}",
    &why);
if (!d)
  fprintf (stderr, "error: %s\n", why);
// d->n_fields == 2; d->field[1].reps == 4
dp_wfm_frame_free (d);

function dp_wfm_frame_refuse_text

Refuse text for a source's frame= : it takes a description.

wfm_frame_desc_t * dp_wfm_frame_refuse_text (
    const char * text,
    const char ** why
) 

The frame face of dp_wfm_source_bits_refuse_text(). A source's frame= takes a FrameDesc or a Frame on the Python face, and a str is refused rather than read as JSON; a description written as JSON is a scene's "frame" key, read by dp_wfm_frame_from_json(). It has the shape of that reader because the binding calls it where the reader would be called (just-makeit's owned-pointer parse_fn with parse_why).

Parameters:

  • text ignored.
  • why receives the static reason; may be NULL.

Returns:

NULL, always.

const char *why;
wfm_frame_desc_t *d = dp_wfm_frame_refuse_text ("{\"fields\": []}", &why);
// d == NULL; why names FrameDesc

function dp_wfm_frame_to_json

Write a description as its JSON frame object, the text dp_wfm_frame_from_json() reads back.

char * dp_wfm_frame_to_json (
    const wfm_frame_desc_t * d
) 

The same writer a scene's "frame" key uses, so the two cannot spell a description differently: a literal field is its Field text, a derived field its bits and derived_by, a stage its kind and cover.

Parameters:

  • d the description; NULL gives NULL.

Returns:

a NUL-terminated string the caller releases with free().

wfm_frame_desc_t *d
    = dp_wfm_frame_from_json ("{\"fields\": [{\"spec\": \"1010\"}]}", NULL);
char *text = dp_wfm_frame_to_json (d);
// text: {"fields":[{"spec":"0xa"}],"stages":[]}
free (text);
dp_wfm_frame_free (d);

function dp_wfm_framing_flags_error

Why --sync /--crc /--acq-code cannot frame this source, or NULL.

const char * dp_wfm_framing_flags_error (
    const wfm_source_t * src
) 

The CLI's flags spell the common frame, which needs a waveform that carries a bit stream and, off dsss, a data source to fill it. Said once here and used by the same refusal inside dp_wfm_source_frame_error, so the flags and a bare source give one answer. A dsss burst needs neither.

Parameters:

  • src the source.

Returns:

a static sentence naming the fix, or NULL.


function dp_wfm_render_destroy

Free a renderer and everything it owns. NULL-safe.

void dp_wfm_render_destroy (
    wfm_render_t * r
) 


function dp_wfm_render_from_feed

A renderer whose input is a caller-supplied feed, not a live synth.

wfm_render_t * dp_wfm_render_from_feed (
    const wfm_source_t * src,
    double fs,
    double doppler,
    double doppler_rate,
    dp_doppler_channel_state_t * borrow,
    size_t delay,
    size_t on,
    wfm_feed_fn feed,
    void * feed_ctx
) 

This is what lets the Plan run a Doppler channel over its CACHED clean on-time through the one renderer the composer uses, so the dilation, the holdover and the input timeline are the same code rather than a second implementation of them. dp_wfm_render_steps() then yields the channel's output exactly as it does for a synth-backed renderer.

Parameters:

  • src Source whose carrier_hz the channel uses.
  • fs Sample rate, Hz.
  • doppler Drawn Doppler, ppm.
  • doppler_rate Drawn Doppler rate, ppm/s.
  • borrow A PERSIST channel the caller owns, or NULL to make one that dies with the renderer.
  • delay Leading noise-only INPUT samples (0 if the gaps are never pulled: see dp_wfm_render_set_input_timeline()).
  • on ON input samples.
  • feed Input source.
  • feed_ctx Passed to feed.

Returns:

A renderer (dp_wfm_render_destroy() it), or NULL if the source has no Doppler to apply (doppler and doppler_rate both zero and no borrow) or the channel could not be built.


function dp_wfm_render_noise_steps

Pull n samples of the source's NOISE FLOOR only, through the same channel.

void dp_wfm_render_noise_steps (
    wfm_render_t * r,
    float _Complex * dst,
    size_t n
) 

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 dp_wfm_render_set_input_timeline

Tell a Doppler renderer where its INPUT's phases fall.

void dp_wfm_render_set_input_timeline (
    wfm_render_t * r,
    size_t delay,
    size_t on
) 

A renderer with a channel pulls the synth in refills sized to the channel's input, not to what the caller asked for, so it cannot learn the phase boundaries from the pull calls: the channel's input and output clocks are decoupled. This states them, in INPUT samples delay noise-only, then on signal, then noise-only for ever and the renderer follows that timeline however the output is drained (doppler#1858).

Without it a channel renderer falls back to choosing per refill from the phase of the output being drained, which is wrong at every phase boundary. No effect on a renderer with no channel.

Parameters:


function dp_wfm_render_steps

Pull exactly n samples fromr , through its channel if any.

void dp_wfm_render_steps (
    wfm_render_t * r,
    float _Complex * dst,
    size_t n
) 


function dp_wfm_resolve_noise

Resolve a segment list's noise model in place (Phase 4b).

int dp_wfm_resolve_noise (
    wfm_segment_t * segs,
    size_t n
) 

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.

dp_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 dp_wfm_scene_error

Why a scene cannot be composed, or NULL: the one validator.

const char * dp_wfm_scene_error (
    const wfm_segment_t * segs,
    size_t n_segs,
    int repeat,
    int continuous
) 

Every source's dp_wfm_source_error(), then what only the scene can say:

  • A continuous dsss source's chip rate fs / sps, at its segment's fs, is at least its symbol_rate one chip per data symbol, the synth's own floor (dp_wfm_source_dsss_cps()). The default fs = 1.0 with a symbol_rate in Hz is the case that finds it (doppler#1706); the reason is dp_wfm_why_dsss_cont_rate.
  • A count (num_samples, non-zero or ranged) beside sources that set the segment's length (dp_wfm_segment_sets_length()) is refused with dp_wfm_why_count_derived: the length is theirs, so it would be dropped (doppler#1729).
  • A data STREAM (--data-from-file -) has no end to repeat, so repeat, continuous and a segment's repeats > 1 are refused, and stdin feeds at most one source.

dp_wfm_compose_create() refuses exactly these, and dp_wfm_compose_create_why() says which; a face calls this to say why.

Returns:

a static sentence naming the fault and its fix, or NULL.


function dp_wfm_segment_sets_length

Whether a segment's sources SET its on-time, so its num_samples is derived and a count given beside them refused.

int dp_wfm_segment_sets_length (
    const wfm_segment_t * seg
) 

Two kinds of source set a length (payload-data-source.md 4.6):

  • A finite data source data, a finite file, or a carried frame of fixed bits is its frames (dp_wfm_source_data_frames()); the longest of a segment's sets it.
  • A lone dsss burst one dsss source, no symbol_rate, no data source is one burst (dp_wfm_source_dsss_nchips() times sps).

A stream sets none: it runs to its end, and a count may bound it. A non-zero num_samples (or a ranged one) beside a segment this answers 1 for is refused by dp_wfm_scene_error() with dp_wfm_why_count_derived, on every face.

static const uint8_t bits[16] = { 1 };
wfm_source_t  src  = { .type       = WFM_SYNTH_BITS,
                       .modulation = 1, // bpsk
                       .sps        = 1,
                       .pn_length  = 7 };
src.data = (wfm_seq_t){ .kind = WFM_SEQ_LITERAL, .bits = bits,
                        .len = 16 };
wfm_segment_t seg = { .sources = &src, .n_sources = 1, .fs = 1e6 };
if (!dp_wfm_segment_sets_length (&seg)) // its frames are the run
  return 1;
seg.num_samples = 1000;                 // so a count is refused
if (dp_wfm_scene_error (&seg, 1, 0, 0) != dp_wfm_why_count_derived)
  return 1;

Parameters:

  • seg the segment, as the caller gave it.

Returns:

1 when its sources set its on-time, else 0.


function dp_wfm_snr_over_fs

SNR (dB) referred to fs, from a source's snr/snr_mode/sps/type.

double dp_wfm_snr_over_fs (
    int snr_mode,
    int type,
    int sps,
    size_t sf,
    double sym_span,
    double snr
) 

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). dp_wfm_resolve_noise() uses it to place the shared noise floor at level(anchor) − dp_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_mode 0 auto, 1 fs, 2 ebno, 3 esno.
  • type A WFM_SYNTH_* waveform type (selects the auto convention).
  • sps Samples per symbol/chip (≥1; <1 treated as 1).
  • sf Spreading factor — chips per data symbol (burst dsss; ≥1, <1 treated as 1).
  • sym_span Continuous-dsss symbol span in samples (fs/symbol_rate); 0 = burst/non-dsss, derive from sf·sps.
  • snr The declared SNR in dB.

Returns:

SNR over fs in dB.


function dp_wfm_source_attach_dsss

Attach a dsss source's data to a freshly-created synth.

int dp_wfm_source_attach_dsss (
    dp_wfm_synth_state_t * syn,
    const wfm_source_t * src,
    double fs
) 

The single dsss-attach path, called by BOTH synth-construction faces (dp_wfm_compose_build_synth and the standalone dp_wfm_source_to_synth), so the two cannot drift on how a dsss stream is configured. Selects on symbol_rate: 0 → the burst form (dp_wfm_synth_set_dsss_chips); > 0 → the continuous form (dp_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, and framing it with the per-frame code-only window (code_only_symbols of every frame_symbols, dp_wfm_synth_set_dsss_window) when one is set. A no-op for a non-dsss source.

Parameters:

  • syn A synth from dp_wfm_synth_create() with wtype == WFM_SYNTH_DSSS.
  • src The source (codes, payload, symbol_rate, pn config).
  • fs Segment sample rate (Hz) — the continuous chip rate is fs/sps.

Returns:

0 on success (or non-dsss no-op); -1 on invalid geometry.


function dp_wfm_source_attach_frame

Attach an unspread source's bit pattern, framed or not.

int dp_wfm_source_attach_frame (
    dp_wfm_synth_state_t * syn,
    const wfm_source_t * src
) 

The type=bits counterpart of dp_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 dp_wfm_synth_set_bits() is dp_wfm_frame_assemble() 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:

  • syn A synth from dp_wfm_synth_create() with wtype == WFM_SYNTH_BITS.
  • src The source (pattern, modulation, and any frame fields).

Returns:

0 on success (or a non-bits/no-pattern no-op); -1 on failure.


function dp_wfm_source_bits_refuse_text

Refuse text for a source's bit field: an object takes bits.

size_t dp_wfm_source_bits_refuse_text (
    const char * text,
    uint8_t * out,
    size_t max_out,
    const char ** why
) 

A composer source's bit fields (payload, sync, acq_code, data_code) take BITS on the Python face a uint8 array, bytes or a sequence of 0/1 and module helpers make them from other forms: field_bits(text) for the Field grammar, cvt.hex_to_bin, cvt.bytes_to_bin. A str is refused rather than read, so the object has one shape and the Field grammar one door. The text faces (the CLI, a scene) keep reading a Field through dp_wfm_field_parse().

It has the shape of dp_wfm_field_bits() because the binding calls it where that would be called (just-makeit's coerce_str_fn), and it always refuses: it returns 0 and sets why to its one static reason.

Parameters:

  • text ignored.
  • out never written.
  • max_out ignored.
  • why receives the static reason; may be NULL.

Returns:

0, always.

const char *why;
size_t      n = dp_wfm_source_bits_refuse_text ("0101", NULL, 0, &why);
// n == 0; why names field_bits()

function dp_wfm_source_common_frame

The common frame as a description: [preamble x reps | sync | data | crc] , built by the ONE function that builds it.

int dp_wfm_source_common_frame (
    const wfm_source_t * src,
    const wfm_seq_t * sync,
    int crc,
    wfm_frame_desc_t * d
) 

Used by the bridge (a scene's or Python's acq_code + data, with no sync word and no CRC) and by wfmgen for --sync and --crc, which are sugar for these fields. The preamble is a field for an unspread source and is NOT one for a spread burst: a DSSS preamble is sent unspread outside the description. The description borrows src's and sync's sequences.

Parameters:

  • src the source (its acq_code, acq_reps, data, data_from_file and data_len are read).
  • sync the sync word, or NULL for none.
  • crc non-zero appends a CRC-16 over the payload.
  • d receives the description.

Returns:

0, or -1 when it does not lay out.


function dp_wfm_source_crc_refuse_text

Refuse text for the retired crc= : a CRC is a stage of the frame description.

size_t dp_wfm_source_crc_refuse_text (
    const char * text,
    uint8_t * out,
    size_t max_out,
    const char ** why
) 

As dp_wfm_source_sync_refuse_text, for crc=. crc="none" is refused too: it would otherwise be a spelling that works forever with no way to retire it.

Parameters:

  • text the str (unused: every str is refused).
  • out unused.
  • max_out unused.
  • why receives the sentence.

Returns:


function dp_wfm_source_create_snr

Resolve a source's (snr, snr_mode) into the pair to hand to dp_wfm_synth_create() .

double dp_wfm_source_create_snr (
    const wfm_source_t * src,
    double fs,
    double snr,
    int * snr_mode
) 

dp_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 (dp_wfm_compose_build_synth) and the standalone-Synth bridge (dp_wfm_source_to_synth), so every face agrees to the bit — converts a dsss source's SNR to the over-fs reference (via dp_wfm_snr_over_fs; the burst span is sf = n_data_code, a continuous stream uses fs/symbol_rate) and returns snr_mode=fs. A framed bpsk/qpsk/pn is referred the same way, because its synth is created as BITS (dp_wfm_source_synth_type()). Every other source passes through unchanged.

Parameters:

  • src The source (supplies type/sps/snr_mode/n_data_code/ symbol_rate).
  • fs Segment sample rate (Hz) — needed for a continuous dsss source's fs/symbol_rate span; ignored otherwise.
  • snr The declared SNR in dB, already ranged-resolved.
  • snr_mode Receives the snr_mode for create.

Returns:

The SNR in dB for create.


function dp_wfm_source_data_frames

Frames a FINITE data source makes, ceil(bits / LEN) ; 0 for a stream or none. On continuous dsss, which has no frame, its bits.

uint64_t dp_wfm_source_data_frames (
    const wfm_source_t * src
) 

Known before the first sample (a file's length from fstat), which is what lets a finite run's length be derived rather than given (§4.6).

static const uint8_t bits[40] = { 1 };
wfm_source_t src = { .type = WFM_SYNTH_BPSK, .sps = 4 };
src.data      = (wfm_seq_t){ .kind = WFM_SEQ_LITERAL, .bits = bits,
                             .len = 40 };
src.data_len  = 16;
src.fill      = (wfm_seq_t){ .kind = WFM_SEQ_DOTTED, .len = 2 };
if (dp_wfm_source_data_frames (&src) != 3) // 40 bits in 16-bit frames
  return 1;
if (dp_wfm_source_data_samples (&src, 1e6, 3) != 3 * 16 * 4) // bpsk
  return 1;

function dp_wfm_source_data_is_stream

Whether a source's data is a stream: data_from_file is- .

int dp_wfm_source_data_is_stream (
    const wfm_source_t * src
) 

A stream has no length up front and no end to repeat, so a scene refuses it with repeat, continuous or repeats > 1, Plan refuses it, and a segment carrying one runs until it ends (dp_wfm_scene_error()).


function dp_wfm_source_data_samples

Samples the first frames frames of a source's data occupy; 0 with no data.

uint64_t dp_wfm_source_data_samples (
    const wfm_source_t * src,
    double fs,
    uint64_t frames
) 

The one length a run with a data source is measured in: a finite run is this over dp_wfm_source_data_frames(), and a stream ends at this over the frames it sent.

  • A frame (bits, bpsk/qpsk/pn): its output bits at the mapping's bits per symbol (rounded up), times sps, per frame.
  • A dsss burst: its chips times sps (samples per chip), per burst.
  • Continuous dsss has no frame: a "frame" is one data bit, one per data symbol, and the run is every chip up to the symbol after the frames-th data symbol, at fs / sps / symbol_rate chips per symbol not a whole number, so not a product times sps. Every symbol carries data unless a code-only window is set; then the data pauses through each frame's first code_only_symbols, and the run counts every window before the last data symbol (not one after it).

Parameters:

  • src the source.
  • fs the segment's sample rate (only continuous dsss reads it).
  • frames frames (data bits, for continuous dsss) sent.

function dp_wfm_source_dsss_cps

Chips per data symbol of a CONTINUOUS dsss source at fs .

double dp_wfm_source_dsss_cps (
    const wfm_source_t * src,
    double fs
) 

sps is samples per CHIP for dsss, so the chip rate is fs / sps, and the data clock is symbol_rate: their ratio, non-integer in general, which is the asynchronicity. It is the number the builder hands dp_wfm_synth_set_dsss_cont(), and the one dp_wfm_scene_error() holds to >= 1 so the rule and the synth read the same value.

Parameters:

  • src the source.
  • fs its segment's sample rate, in Hz.

Returns:

chips per data symbol, or 0 for a source that is not continuous dsss (or has no sps).

wfm_source_t s = { .type = WFM_SYNTH_DSSS, .sps = 2,
                   .symbol_rate = 1000.0 };
if (dp_wfm_source_dsss_cps (&s, 1e6) != 500.0)   // 500 chips per symbol
  return 1;

function dp_wfm_source_dsss_nchips

Chips one DSSS BURST from this source occupies, description and all.

size_t dp_wfm_source_dsss_nchips (
    const wfm_source_t * src
) 

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:

  • src the 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 dp_wfm_source_error

NULL when this source can be built; else why not, as a sentence.

const char * dp_wfm_source_error (
    const wfm_source_t * src
) 

The question every face asks before it builds the wfmgen CLI, a scene read by dp_wfm_compose_from_json_why(), the standalone Synth through dp_wfm_source_to_synth() and the composer through dp_wfm_compose_create() so all four refuse the same sources for the same reason. It checks the source's own parameters, then asks dp_wfm_source_frame_error() about its frame:

  • pn_poly must fit the pn_length-bit register (pn_fits_register()). The generator masks a wider one, silently, so pn_poly = 0x40 on a 5-bit register is a register with no feedback: the seed, then zeros, a constant waveform that still looks like a PN source (doppler#1636). 0 selects the maximal-length polynomial and always fits.
  • No two members the surface table declares exclusive may both be set (WFM_SURFACE_EXCLUSIVE, wfm_surface.h): a carried frame is the whole frame, so a payload beside it would be dropped (doppler#1683). The CLI and a scene refuse the same pair first, naming their own spelling.
  • A dsss source has the codes it needs (doppler#1696): a burst whose frame has bits to spread needs data_code (dp_wfm_why_dsss_frame_no_data_code), a burst needs a preamble or a frame (dp_wfm_why_dsss_empty), and a continuous stream needs data_code (dp_wfm_why_dsss_cont_no_data_code). A preamble alone is a valid burst: an acquisition stimulus.

A rule that needs the segment's sample rate is not here a source does not carry it but in dp_wfm_scene_error(), which asks this first.

Parameters:

  • src The source.

Returns:

NULL if there is nothing wrong, else a static message.

wfm_source_t s = { .type = WFM_SYNTH_PN, .sps = 1, .pn_length = 5,
                   .pn_poly = 0x40 };
dp_wfm_source_error (&s);   // "pn_poly has a bit above ..."
s.pn_poly = 0x12;
dp_wfm_source_error (&s);   // NULL: x^5 + x^2 + 1 fits

function dp_wfm_source_frame_error

NULL when this source's frame fields can be honoured; else why not.

const char * dp_wfm_source_frame_error (
    const wfm_source_t * src
) 

ONE rule, asked by all three faces — the wfmgen CLI before it generates, the standalone Synth through dp_wfm_source_to_synth, and the composer through dp_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. type=bits and the PN-sourced bpsk/qpsk/pn carry one when a payload is given (literal or generated, gh-762); the latter are then built as a BITS synth (dp_wfm_source_synth_type()). Types with no bit stream (tone, noise, chirp, symbols) are refused with a reason.

Parameters:

  • src The source.

Returns:

NULL if there is nothing wrong, else a static message.


function dp_wfm_source_has_frame

Non-zero when this source describes a FRAME.

int dp_wfm_source_has_frame (
    const wfm_source_t * src
) 

A carried description, a preamble or a data source is what says "framed".

Parameters:

  • src The source; NULL reads as unframed.

function dp_wfm_source_sync_refuse_text

Refuse text for the retired sync= : a sync word is a field of the frame description.

size_t dp_wfm_source_sync_refuse_text (
    const char * text,
    uint8_t * out,
    size_t max_out,
    const char ** why
) 

The coercion hook of the sync tombstone: ANY str reaching it is refused, and a non-empty array is refused by dp_wfm_source_error, so sync= fails whatever it is given and the sentence names frame=.

Parameters:

  • text the str (unused: every str is refused).
  • out unused.
  • max_out unused.
  • why receives the sentence.

Returns:


function dp_wfm_source_synth_type

The synth type to create this source with.

int dp_wfm_source_synth_type (
    const wfm_source_t * src
) 

The source's own type, except for a FRAMED bpsk/qpsk/pn: that one transmits its frame, and the only synth that plays a bit pattern is a WFM_SYNTH_BITS one (dp_wfm_synth_set_bits() is a no-op on any other), so it is created as BITS and dp_wfm_source_attach_frame() hands it the frame with the mapping the type names (bpsk for bpsk/pn, Gray QPSK for qpsk). Created with its own type it played the LFSR stream and dropped the frame (doppler#1616).

Asked by both construction faces (dp_wfm_compose_build_synth and the standalone dp_wfm_source_to_synth) and by dp_wfm_source_create_snr(), which refers such a source's SNR to fs so its noise stays in the reference its type names.

wfm_source_t s = { .type = WFM_SYNTH_BPSK };
int t = dp_wfm_source_synth_type (&s); // unframed: WFM_SYNTH_BPSK

Parameters:

  • src The source.

Returns:

A WFM_SYNTH_* type.


function dp_wfm_source_to_synth_error

Why dp_wfm_source_to_synth() refused this source, or NULL.

const char * dp_wfm_source_to_synth_error (
    const wfm_source_t * src,
    double fs
) 

The standalone Synth's reason channel (just-makeit's bridge_error_fn, which takes the bridge's own arguments): dp_wfm_scene_error() of a scene of one segment at fs, so a refused Synth raises the same sentence as every other face, including a rule that needs the rate. NULL leaves the binding's generic error, for a refusal that is not the source's.

Parameters:

  • src The source.
  • fs The sample rate the bridge was given.

Returns:

A static sentence, or NULL.

wfm_source_t s = { .type = WFM_SYNTH_PN, .sps = 1, .pn_length = 5,
                   .pn_poly = 0x40 };
dp_wfm_source_to_synth_error (&s, 1e6);   // dp_wfm_why_pn_poly

function dp_wfm_spec_headroom

The top-level headroom (dB) from a spec JSON, or 0 if absent.

double dp_wfm_spec_headroom (
    const char * json
) 

Lets --from-file reproduce a recorded --headroom; the value is a writer gain, so it lives outside the composer state.


function dp_wfm_spec_template_json

A ready-to-edit example spec in the canonical from-file schema.

char * dp_wfm_spec_template_json (
    void
) 

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 dp_wfm_spec_to_json(), so it is valid by construction and round-trips through dp_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 dp_wfm_spec_to_json

Serialise a spec to a JSON string (for record).

char * dp_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 dp_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.


function dp_wfm_synth_attach_data

Drive a type=bits synth from a frame whose payload is a data source.

int dp_wfm_synth_attach_data (
    dp_wfm_synth_state_t * syn,
    const wfm_frame_desc_t * d,
    const wfm_frame_ops_t * ops,
    wfm_data_src_t * src,
    wfm_data_pacing_t pacing,
    int modulation
) 

The pull that replaces the cycle (docs/design/payload-data-source.md §7). Each frame is d assembled over the next chunk of src (dp_wfm_frame_assemble_data): the first now, and each one after at the previous frame's last bit, so every stage over the payload covers its own frame's chunk. Under pacing, a source with nothing yet sends an idle frame (dp_wfm_data_frame, the one rule). When src ends, the synth goes silent and dp_wfm_synth_data_ended() says so.

Parameters:

  • syn a synth created with type=bits.
  • d the frame; exactly one field is data:LEN, and LEN is the source's.
  • ops stage kernels, as dp_wfm_frame_assemble(); may be NULL.
  • src the data source; the synth OWNS it from here, success or not, and frees it with the synth.
  • pacing WFM_DATA_PACED for --realtime, else WFM_DATA_UNPACED.
  • modulation as dp_wfm_synth_set_bits().

Returns:

0, or -1: not a bits synth, no data field in d, a frame that does not assemble, or a source that failed its first read.

wfm_frame_desc_t d;
const wfm_seq_t  data = { .kind = WFM_SEQ_DATA, .len = 16 };
dp_wfm_frame_fixed (&d, NULL, 0, NULL, &data, 1);        // [data:16 | crc]
wfm_data_src_t *src = dp_wfm_data_create ("0x0123456789AB", NULL, 16, NULL,
                                          NULL);
dp_wfm_synth_state_t *s
    = dp_wfm_synth_create (WFM_SYNTH_BITS, 1e6, 0.0, 100.0, 0, 1, 8, 7, 0,
                           0, 0.0);
dp_wfm_synth_attach_data (s, &d, NULL, src, WFM_DATA_UNPACED, 1); // bpsk
// three 32-bit frames, each over its own 16-bit chunk, then silence
dp_wfm_synth_destroy (s);                 // frees src too

function dp_wfm_synth_data_source

The data source a synth pulls from (dp_wfm_synth_attach_data), or NULL: its stats are the run's truth for scoring.

const wfm_data_src_t * dp_wfm_synth_data_source (
    const dp_wfm_synth_state_t * syn
) 


function dp_wfm_synth_set_data_pacing

Pace a synth's data source: WFM_DATA_PACED for --realtime .

void dp_wfm_synth_set_data_pacing (
    dp_wfm_synth_state_t * syn,
    wfm_data_pacing_t pacing
) 

A synth attached by dp_wfm_source_attach_frame() pulls UNPACED it waits for its data, as cat does. A paced caller (the composer under --realtime) sets this after the build, so a source with nothing yet sends an idle frame instead (dp_wfm_data_frame, the one rule). A synth with no data source ignores it.

dp_wfm_synth_state_t *s = dp_wfm_synth_create (
    WFM_SYNTH_BITS, 1e6, 0.0, 100.0, 0, 1, 8, 7, 0, 0, 0.0);
dp_wfm_synth_set_data_pacing (s, WFM_DATA_PACED); // no source: no-op
dp_wfm_synth_destroy (s);

Public Static Functions Documentation

function dp_wfm_scene_fs

The ONE answer to "what is this stream's sample rate": the fs every segment shares, or 0.0 when they differ.

static inline double dp_wfm_scene_fs (
    const wfm_segment_t * segs,
    size_t n_segs
) 

fs is per segment, and a scene whose segments differ is legal: no single rate is true of it. 0.0 is the library's "not stated" (a Writer opened at fs=0.0, a SigMF document without core:sample_rate), so an output asks this and either states the rate it returns or, given 0.0, says nothing or refuses, if its format cannot say nothing (a BLUE header has one xdelta). Every output asks here rather than reading segs[0].fs, which is a rate only when they agree (doppler#1733).

wfm_segment_t s[2] = { { .fs = 6e6 }, { .fs = 6e6 } };
int ok = dp_wfm_scene_fs (s, 2) == 6e6;
s[1].fs = 2e6;
ok = ok && dp_wfm_scene_fs (s, 2) == 0.0 && dp_wfm_scene_fs (s, 0) == 0.0;
return ok ? 0 : 1;

Parameters:

  • segs the segments; may be NULL when n_segs is 0.
  • n_segs their count.

Returns:

the shared fs, or 0.0 when the segments differ or there are none.


Macro Definition Documentation

define WFM_NUM_SAMPLES_PLAIN

A plain segment's on-time when its num_samples is 0 and no source sets one: what--count , a scene andSegment default to.

#define WFM_NUM_SAMPLES_PLAIN `((size_t)1024)`



The documentation for this class was generated from the following file native/inc/doppler/wfm/wfm_compose.h