File wfm_synth_core.h¶
FileList > inc > wfm_synth > wfm_synth_core.h
Go to the source code of this file
Synth component API. More...
#include "clib_common.h"#include "dp_state.h"#include "jm_perf.h"#include "fir/fir_core.h"#include "lo/lo_core.h"#include "awgn/awgn_core.h"#include "pn/pn_core.h"#include <math.h>
Classes¶
| Type | Name |
|---|---|
| struct | wfm_synth_state_t Synth state. |
Public Types¶
| Type | Name |
|---|---|
| enum | wfm__synth__core_8h_1adc29c2ff13d900c2f185ee95427fb06c |
Public Functions¶
| Type | Name |
|---|---|
| wfm_synth_state_t * | wfm_synth_create (int type, double fs, double freq, double snr, int snr_mode, uint32_t seed, int sps, int pn_length, uint64_t pn_poly, int lfsr, double f_end) Allocate and configure a waveform synthesiser. The synthesiser combines a local oscillator (LO), optional AWGN, and an optional PN LFSR into a single streaming source. One call to wfm_synth_step() orwfm_synth_steps() advances all sub-components in lock-step. SNR >= WFM_SYNTH_SNR_CLEAN (100 dB) skips AWGN entirely — clean waveforms pay no noise overhead. When snr_mode is "auto" the library picks the natural reference: Es/No for modulated types (BPSK, QPSK), fs-band SNR for tone/noise/PN. |
| void | wfm_synth_destroy (wfm_synth_state_t * state) Destroy a synth instance and release all memory. Recursively frees the LO, AWGN, and PN sub-objects, then the struct itself. Safe to call with NULL (no-op). |
| float | wfm_synth_get_cur_im (const wfm_synth_state_t * state) Return the imaginary part of the current held symbol. For QPSK this is the Q component (±1/√2); for BPSK/PN it is always 0; for tone/noise it is 0. |
| float | wfm_synth_get_cur_re (const wfm_synth_state_t * state) Return the real part of the current held symbol. For modulated types this is the I component latched at the last symbol boundary (±1 for BPSK/PN, ±1/√2 for QPSK). For tone the synthesiser initialises cur_re to 1.0 so that the held symbol is a clean unit-power carrier; for noise it is 0.0 (noise has no held symbol). |
| int | wfm_synth_get_nsps (const wfm_synth_state_t * state) Return the samples-per-symbol count. For modulated types (BPSK, QPSK, PN) each symbol is held for nsps consecutive output samples. For tone/noise this field is present but unused by the synthesis path. |
| void | wfm_synth_get_state (const wfm_synth_state_t * state, void * blob) |
| int | wfm_synth_get_sym_pos (const wfm_synth_state_t * state) Return the current position within the current symbol (0..nsps-1). Reaches nsps and wraps to 0 each time a new symbol is consumed from the PN LFSR. Useful for frame alignment: sym_pos==0 on a step boundary means the very next sample begins a fresh symbol. |
| int | wfm_synth_get_wtype (const wfm_synth_state_t * state) Return the active waveform type discriminant. Maps to the WFM_SYNTH_* enum: 0=tone, 1=noise, 2=pn, 3=bpsk, 4=qpsk. Use this to inspect which synthesis path is active at runtime. |
| JM_FORCEINLINE uint64_t | wfm_synth_mls_poly (uint32_t n) Maximal-length-sequence (MLS) primitive polynomial for an LFSR of the given register length n, in pn_core's right-shift Galois convention. Returns 0 for lengths outside 2..64 (caller errors). Generated from verified primitive polynomials (period 2^n-1); the n=2..16 values are unchanged. |
| void | wfm_synth_reseed_noise (wfm_synth_state_t * state, uint32_t seed) Reseed only the additive-noise (AWGN) generator, leaving the signal (LO / PN code / data / pulse shaping) untouched. A no-op for a synth with no noise. Used by the composer to give each repeat a fresh noise realization while the underlying waveform stays bit-identical. |
| void | wfm_synth_reset (wfm_synth_state_t * state) Reset Synth to its post-create state. Resets the LO phase accumulator, AWGN internal state, and PN LFSR register to their initial values so the output sequence is perfectly reproducible from sample 0. |
| int | wfm_synth_set_bits (wfm_synth_state_t * state, const uint8_t * bits, size_t n, int modulation) Attach a user bit pattern to a type=bits synth (no-op otherwise). |
| void | wfm_synth_set_chirp_span (wfm_synth_state_t * state, size_t span) Pin a chirp's sweep span to span samples (no-op for non-chirp). |
| void | wfm_synth_set_cur_im (wfm_synth_state_t * state, float val) Override the held-symbol imaginary (Q) component in-place. Takes effect on the next wfm_synth_step() within the current symbol hold. |
| void | wfm_synth_set_cur_re (wfm_synth_state_t * state, float val) Override the held-symbol real (I) component in-place. Takes effect on the next wfm_synth_step() within the current symbol hold. |
| void | wfm_synth_set_nsps (wfm_synth_state_t * state, int val) Override the samples-per-symbol count in-place. Does not flush the symbol-position counter (sym_pos); set sym_pos=0 as well when changing sps mid-stream. |
| int | wfm_synth_set_rrc (wfm_synth_state_t * state, const float * taps, size_t ntaps) Enable RRC pulse shaping on a symbol synth (pn/bpsk/qpsk/bits). |
| int | wfm_synth_set_state (wfm_synth_state_t * state, const void * blob) |
| void | wfm_synth_set_sym_pos (wfm_synth_state_t * state, int val) Override the symbol-position counter in-place. Injecting 0 forces the next wfm_synth_step() to latch a new PN chip; any other value fast-forwards into the middle of the current symbol hold. |
| int | wfm_synth_set_symbols (wfm_synth_state_t * state, const float _Complex * symbols, size_t n) Attach a complex-symbol stream to a type=symbols synth (no-op else). |
| void | wfm_synth_set_wtype (wfm_synth_state_t * state, int val) Override the waveform type discriminant in-place. Changing wtype does not reinitialise sub-objects; use with care. |
| size_t | wfm_synth_state_bytes (const wfm_synth_state_t * state) |
| JM_FORCEINLINE JM_HOT float complex | wfm_synth_step (wfm_synth_state_t * state) Generate one output sample from internal state. Advances the PN LFSR (modulated types only, on symbol boundaries), the LO phase accumulator, and the AWGN engine, then returns the mixed result: sym * carrier + noise . Inlined and hot-path annotated so tight per-sample loops pay no call overhead. |
| void | wfm_synth_steps (wfm_synth_state_t * state, float complex * output, size_t n) Generate a block of output samples. Calls wfm_synth_step() in a tight loop, writing each cf32 sample into output . The Python binding returns a freshly allocated NumPy complex64 array; ownership is transferred to the caller. |
Macros¶
| Type | Name |
|---|---|
| define | WFM_SYNTH_SNR_CLEAN 100.0 |
| define | WFM_SYNTH_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('W','F','M','S') |
| define | WFM_SYNTH_STATE_VERSION 1u |
Detailed Description¶
Lifecycle: create -> [step / steps / reset]* -> destroy
Example:
wfm_synth_state_t *obj = wfm_synth_create(0, 1000000.0, 0.0, 100.0, 0, 1, 8, 7, 0);
float complex y = wfm_synth_step(obj);
wfm_synth_destroy(obj);
Public Types Documentation¶
enum wfm__synth__core_8h_1adc29c2ff13d900c2f185ee95427fb06c¶
enum wfm__synth__core_8h_1adc29c2ff13d900c2f185ee95427fb06c {
WFM_SYNTH_TONE = 0,
WFM_SYNTH_NOISE = 1,
WFM_SYNTH_PN = 2,
WFM_SYNTH_BPSK = 3,
WFM_SYNTH_QPSK = 4,
WFM_SYNTH_CHIRP = 5,
WFM_SYNTH_BITS = 6,
WFM_SYNTH_SYMBOLS = 7
};
Waveform type discriminant (the type create argument / type choice).
Public Functions Documentation¶
function wfm_synth_create¶
Allocate and configure a waveform synthesiser. The synthesiser combines a local oscillator (LO), optional AWGN, and an optional PN LFSR into a single streaming source. One call to wfm_synth_step() orwfm_synth_steps() advances all sub-components in lock-step. SNR >= WFM_SYNTH_SNR_CLEAN (100 dB) skips AWGN entirely — clean waveforms pay no noise overhead. Whensnr_mode is "auto" the library picks the natural reference: Es/No for modulated types (BPSK, QPSK), fs-band SNR for tone/noise/PN.
wfm_synth_state_t * wfm_synth_create (
int type,
double fs,
double freq,
double snr,
int snr_mode,
uint32_t seed,
int sps,
int pn_length,
uint64_t pn_poly,
int lfsr,
double f_end
)
Parameters:
typeWaveform type: 0=tone, 1=noise, 2=pn, 3=bpsk, 4=qpsk, 5=chirp, 6=bits, 7=symbols. The Python binding accepts strings "tone"|"noise"|"pn"|"bpsk"|"qpsk"|"chirp"|"bits"|"symbols". For "bits" attach the pattern with wfm_synth_set_bits(); for "symbols" attach the complex stream with wfm_synth_set_symbols() after create().fsSample rate in Hz. Sets the carrier frequency normalisation and the noise bandwidth. Default 1 000 000.0.freqCarrier frequency offset in Hz (−fs/2 … fs/2). A complex LO is created only when freq != 0. For a chirp this is the start frequency f_start (the instantaneous frequency at t=0). Default 0.0.snrTarget SNR in dB, interpreted persnr_mode. Values >= WFM_SYNTH_SNR_CLEAN (100) disable AWGN. Default 100.0.snr_modeSNR reference: 0=auto, 1=fs (full-band), 2=ebno, 3=esno. The Python binding accepts strings "auto"|"fs"|"ebno"|"esno". Default 0.seedPRNG seed shared by AWGN and the PN LFSR. Default 1.spsSamples per symbol for modulated types (BPSK, QPSK, PN). Ignored for tone/noise. Default 8.pn_lengthLFSR register length (1..64); period = 2^pn_length - 1. Default 7 (period 127).pn_polyGalois tap polynomial for the LFSR. 0 means "look up the canonical MLS polynomial for pn_length" from the wfm_synth_mls_poly table. Default 0.lfsrLFSR realization: PN_GALOIS (0) or PN_FIBONACCI (1).f_endChirp end frequency in Hz (type=chirp only; ignored otherwise). Withfreqas the start, the instantaneous frequency sweeps linearly fromfreqtof_endover the span (set by wfm_synth_set_chirp_span() or the first wfm_synth_steps() call), then holds atf_end.f_end < freqis a down-chirp. Default 0.0.
Returns:
Heap-allocated state, or NULL on allocation failure.
Note:
Caller must call wfm_synth_destroy() when done.
>>> from doppler.wfm import _SynthEngine
>>> import numpy as np
>>> s = _SynthEngine(type="tone", fs=1.0, freq=0.0, snr=100.0)
>>> x = s.steps(4)
>>> x.dtype
dtype('complex64')
>>> x.tolist()
[(1+0j), (1+0j), (1+0j), (1+0j)]
function wfm_synth_destroy¶
Destroy a synth instance and release all memory. Recursively frees the LO, AWGN, and PN sub-objects, then the struct itself. Safe to call with NULL (no-op).
Parameters:
statePointer to heap-allocated state; may be NULL.
function wfm_synth_get_cur_im¶
Return the imaginary part of the current held symbol. For QPSK this is the Q component (±1/√2); for BPSK/PN it is always 0; for tone/noise it is 0.
Parameters:
stateMust be non-NULL.
Returns:
Current symbol imaginary (Q) component.
function wfm_synth_get_cur_re¶
Return the real part of the current held symbol. For modulated types this is the I component latched at the last symbol boundary (±1 for BPSK/PN, ±1/√2 for QPSK). For tone the synthesiser initialises cur_re to 1.0 so that the held symbol is a clean unit-power carrier; for noise it is 0.0 (noise has no held symbol).
Parameters:
stateMust be non-NULL.
Returns:
Current symbol real (I) component.
function wfm_synth_get_nsps¶
Return the samples-per-symbol count. For modulated types (BPSK, QPSK, PN) each symbol is held for nsps consecutive output samples. For tone/noise this field is present but unused by the synthesis path.
Parameters:
stateMust be non-NULL.
Returns:
Samples per symbol (nsps >= 1).
function wfm_synth_get_state¶
function wfm_synth_get_sym_pos¶
Return the current position within the current symbol (0..nsps-1). Reaches nsps and wraps to 0 each time a new symbol is consumed from the PN LFSR. Useful for frame alignment: sym_pos==0 on a step boundary means the very next sample begins a fresh symbol.
Parameters:
stateMust be non-NULL.
Returns:
Symbol position counter (0 <= sym_pos < nsps).
function wfm_synth_get_wtype¶
Return the active waveform type discriminant. Maps to the WFM_SYNTH_* enum: 0=tone, 1=noise, 2=pn, 3=bpsk, 4=qpsk. Use this to inspect which synthesis path is active at runtime.
Parameters:
stateMust be non-NULL.
Returns:
Integer waveform type index (WFM_SYNTH_TONE .. WFM_SYNTH_QPSK).
function wfm_synth_mls_poly¶
Maximal-length-sequence (MLS) primitive polynomial for an LFSR of the given register length n, in pn_core's right-shift Galois convention. Returns 0 for lengths outside 2..64 (caller errors). Generated from verified primitive polynomials (period 2^n-1); the n=2..16 values are unchanged.
function wfm_synth_reseed_noise¶
Reseed only the additive-noise (AWGN) generator, leaving the signal (LO / PN code / data / pulse shaping) untouched. A no-op for a synth with no noise. Used by the composer to give each repeat a fresh noise realization while the underlying waveform stays bit-identical.
Parameters:
stateSynth state (may be NULL).seedNew noise RNG seed.
function wfm_synth_reset¶
Reset Synth to its post-create state. Resets the LO phase accumulator, AWGN internal state, and PN LFSR register to their initial values so the output sequence is perfectly reproducible from sample 0.
Parameters:
stateMust be non-NULL.
function wfm_synth_set_bits¶
Attach a user bit pattern to a type=bits synth (no-op otherwise).
int wfm_synth_set_bits (
wfm_synth_state_t * state,
const uint8_t * bits,
size_t n,
int modulation
)
Copies n bits (each 0/1) into the synth; modulation maps them to symbols (0=none → 0/1 amplitude, 1=bpsk → ±1, 2=qpsk → Gray-coded ±1/√2, two bits per symbol). The pattern is oversampled by the create-time sps and cycled to fill whatever length wfm_synth_steps() requests, so one pass is n * sps samples (2*ceil... — n/2 * sps for qpsk). Replaces any previous pattern; resets the read position. Safe to call repeatedly.
Parameters:
stateMust be non-NULL.bitsArray ofnbytes, each 0 or 1.nNumber of bits (> 0).modulation0=none, 1=bpsk, 2=qpsk.
Returns:
0 on success; -1 on bad args or allocation failure.
function wfm_synth_set_chirp_span¶
Pin a chirp's sweep span to span samples (no-op for non-chirp).
A linear chirp's slope is (f_end − f_start) / span, so the span — the number of samples the sweep occupies — must be known before generation. The composer/CLI call this with the segment length; a standalone synth that is never pinned locks its span to the first wfm_synth_steps() call instead. Only the first pin (while the span is still 0) takes effect, so it is safe to call unconditionally after wfm_synth_create().
Parameters:
stateMust be non-NULL.spanSweep length in samples (> 0).
function wfm_synth_set_cur_im¶
Override the held-symbol imaginary (Q) component in-place. Takes effect on the next wfm_synth_step() within the current symbol hold.
Parameters:
stateMust be non-NULL.valNew cur_im value.
function wfm_synth_set_cur_re¶
Override the held-symbol real (I) component in-place. Takes effect on the next wfm_synth_step() within the current symbol hold.
Parameters:
stateMust be non-NULL.valNew cur_re value.
function wfm_synth_set_nsps¶
Override the samples-per-symbol count in-place. Does not flush the symbol-position counter (sym_pos); set sym_pos=0 as well when changing sps mid-stream.
Parameters:
stateMust be non-NULL.valNew nsps value (>= 1).
function wfm_synth_set_rrc¶
Enable RRC pulse shaping on a symbol synth (pn/bpsk/qpsk/bits).
Replaces the default rectangular sample-and-hold with a root-raised-cosine pulse: the symbol-rate impulse train is filtered by taps (a real FIR of ntaps coefficients, typically wfm_rrc_taps(beta, sps, span)). The taps are scaled by sqrt(sps) internally for unit transmit power, so every caller passes the raw taps and gets byte-identical shaping. No-op for types with no symbol stream (tone/noise/chirp). Replaces any existing shaper and clears its delay line.
Parameters:
stateMust be non-NULL.tapsReal FIR taps (copied).ntapsNumber of taps (> 0).
Returns:
0 on success; -1 on bad args / allocation failure.
function wfm_synth_set_state¶
function wfm_synth_set_sym_pos¶
Override the symbol-position counter in-place. Injecting 0 forces the next wfm_synth_step() to latch a new PN chip; any other value fast-forwards into the middle of the current symbol hold.
Parameters:
stateMust be non-NULL.valNew sym_pos value (0 <= val < nsps).
function wfm_synth_set_symbols¶
Attach a complex-symbol stream to a type=symbols synth (no-op else).
Copies n complex symbols into the synth. Each symbol is the constellation point — there is no bit→symbol mapping, so this generalises every modulation (pi/4-QPSK, QAM, custom shaping) into "compute the symbols,
pass them in". The stream is oversampled by the create-time sps and cycled to fill whatever length wfm_synth_steps() requests (one pass is n * sps samples), and is RRC-shaped when wfm_synth_set_rrc() is active. Replaces any previous stream; resets the read position. Safe to call repeatedly.
Parameters:
stateMust be non-NULL.symbolsArray ofncomplex symbols (copied).nNumber of symbols (> 0).
Returns:
0 on success; -1 on bad args or allocation failure.
>>> import numpy as np
>>> from doppler.wfm import _SynthEngine, rrc_taps
>>> s = _SynthEngine(type="symbols", fs=1.0, freq=0.0, snr=100.0, sps=4)
>>> s.set_symbols(np.array([1+0j, 1j, -1+0j, -1j], np.complex64))
>>> s.steps(4)[::4].tolist() # symbol centres (rect hold)
[(1+0j), (1+0j), (1+0j), (1+0j)]
function wfm_synth_set_wtype¶
Override the waveform type discriminant in-place. Changing wtype does not reinitialise sub-objects; use with care.
Parameters:
stateMust be non-NULL.valNew wtype value (WFM_SYNTH_TONE .. WFM_SYNTH_QPSK).
function wfm_synth_state_bytes¶
function wfm_synth_step¶
Generate one output sample from internal state. Advances the PN LFSR (modulated types only, on symbol boundaries), the LO phase accumulator, and the AWGN engine, then returns the mixed result: sym * carrier + noise . Inlined and hot-path annotated so tight per-sample loops pay no call overhead.
Parameters:
stateMust be non-NULL.
Returns:
Next output sample (float complex).
>>> from doppler.wfm import _SynthEngine
>>> s = _SynthEngine(type="tone", fs=1.0, freq=0.0, snr=100.0)
>>> s.step()
(1+0j)
function wfm_synth_steps¶
Generate a block of output samples. Calls wfm_synth_step() in a tight loop, writing each cf32 sample intooutput . The Python binding returns a freshly allocated NumPy complex64 array; ownership is transferred to the caller.
Parameters:
stateInitialised Synth state returned bywfm_synth_create.outputOutput buffer of at leastncf32 elements.nNumber of samples to generate.
Macro Definition Documentation¶
define WFM_SYNTH_SNR_CLEAN¶
define WFM_SYNTH_STATE_MAGIC¶
define WFM_SYNTH_STATE_VERSION¶
The documentation for this class was generated from the following file native/inc/wfm_synth/wfm_synth_core.h