Skip to content

File wfm_core.h

FileList > doppler > wfm > wfm_core.h

Go to the source code of this file

Wfmgen module — public C API.

  • #include "doppler/clib_common.h"
  • #include "doppler/wfm/wfm_frame.h"

Public Functions

Type Name
void dp__sweep_ends (double fs, double f_mid, double rate, size_t n, double center, double * out)
void dp_bpsk_map (const uint8_t * bits, size_t bits_len, float _Complex * out)
Map binary bits {0, 1} to BPSK constellation symbols (cf32). The mapping is: 0 -> +1 + 0j, 1 -> -1 + 0j. Output is unit-power (each symbol has magnitude 1). The imaginary component is always zero. Typically used before a carrier multiply and noise addition to build a BPSK burst without the full Synth engine.
uint16_t dp_crc16 (const uint8_t * bits, size_t bits_len)
CRC-16-CCITT (poly 0x1021, init 0xFFFF) over a bit stream, MSB-first. The one frame CRC doppler's DSSS burst convention uses: the dsss waveform appends it over the payload bits on transmit andBurstDemod validates it asframe_valid on receive (both call the same C kernel,dp_crc16.h ). Each input byte carries one bit (0/1) in its LSB — an unpacked bit array, not a packed byte-stream CRC. Transmit the result MSB-first.
void dp_dsss_spread (const float _Complex * syms, size_t syms_len, const uint8_t * code, size_t code_len, int sf, float _Complex * out)
size_t dp_field_bits (const char * spec, uint8_t * out, const char ** why)
A Field's bits from its text form the one door from text to the bits every frame object takes.
uint64_t dp_mls_poly (uint32_t n)
Maximal-length-sequence primitive polynomial for a length- n LFSR. Returns the tap mask (in the same bit convention the synth/PN engine uses forpn_poly = 0 ) that drives ann-stage Fibonacci LFSR through its full 2^n - 1 state period. Thin public alias over the synth engine's MLS table; valid forn in 2..64 and returns 0 otherwise.
void dp_qpsk_map (const uint8_t * syms, size_t syms_len, float _Complex * out)
Map QPSK symbol indices {0, 1, 2, 3} to Gray-coded symbols (cf32). Gray coding: adjacent indices differ in exactly one bit, minimising BER at low SNR. Bit 0 (LSB) controls I, bit 1 controls Q: I = (1 - 2*b_i) / sqrt(2), Q = (1 - 2*b_q) / sqrt(2). Output is unit-power (|sym| = 1.0 exactly). The four constellation points lie at the cardinal diagonals of the IQ plane.
void dp_rc_h (const double * t, size_t t_len, double * out, double beta)
void dp_rrc_h (const double * t, size_t t_len, double * out, double beta)
void dp_rrc_taps (double beta, int sps, int span, float * out)
float dp_wfm_awgn_amplitude (float snr_db, float signal_power)
Compute the per-component AWGN amplitude for a target SNR. The AWGN engine uses equal-power I and Q noise: complex noise power is 2 * amplitude². This function inverts that relationship for a given signal_power andsnr_db (measured over the full sample-rate bandwidth): amplitude = sqrt(signal_power / (2 * 10^(snr_db / 10))). Pass the result directly todp_awgn_create to get the exact noise level that corresponds to the requested SNR.
float dp_wfm_ebno_to_snr_db (float ebno_db, int bits_per_symbol, float samples_per_symbol)
Convert Eb/No (dB) to SNR (dB) over the full sample-rate band. Digital communication systems are typically specified in Eb/No; doppler uses an fs-band SNR internally. The conversion is: SNR_fs = Eb/No + 10 log10(bits_per_symbol) - 10 log10(samples_per_symbol) For BPSK (bits_per_symbol=1, sps=8) at Eb/No=10 dB this gives ~0.97 dB. For QPSK (bits_per_symbol=2, sps=8) at Eb/No=10 dB this gives ~3.98 dB.

Public Functions Documentation

function dp__sweep_ends

void dp__sweep_ends (
    double fs,
    double f_mid,
    double rate,
    size_t n,
    double center,
    double * out
) 

function dp_bpsk_map

Map binary bits {0, 1} to BPSK constellation symbols (cf32). The mapping is: 0 -> +1 + 0j, 1 -> -1 + 0j. Output is unit-power (each symbol has magnitude 1). The imaginary component is always zero. Typically used before a carrier multiply and noise addition to build a BPSK burst without the full Synth engine.

void dp_bpsk_map (
    const uint8_t * bits,
    size_t bits_len,
    float _Complex * out
) 

Parameters:

  • bits Array of uint8 values; only the LSB of each byte is used.
  • bits_len Number of elements in bits.
  • out Output buffer of at least bits_len cf32 elements.
    >>> from doppler.wfm import bpsk_map
    >>> import numpy as np
    >>> bits = np.array([0, 1, 0, 1], dtype=np.uint8)
    >>> bpsk_map(bits).tolist()
    [(1+0j), (-1+0j), (1+0j), (-1+0j)]
    

function dp_crc16

CRC-16-CCITT (poly 0x1021, init 0xFFFF) over a bit stream, MSB-first. The one frame CRC doppler's DSSS burst convention uses: the dsss waveform appends it over the payload bits on transmit andBurstDemod validates it asframe_valid on receive (both call the same C kernel,dp_crc16.h ). Each input byte carries one bit (0/1) in its LSB — an unpacked bit array, not a packed byte-stream CRC. Transmit the result MSB-first.

uint16_t dp_crc16 (
    const uint8_t * bits,
    size_t bits_len
) 

Parameters:

  • bits Array of 0/1 bit values (one per byte).
  • bits_len Number of bits in bits.

Returns:

The 16-bit CRC.

>>> import numpy as np
>>> from doppler.wfm import crc16
>>> ascii_bits = np.unpackbits(np.frombuffer(b"123456789", np.uint8))
>>> hex(crc16(ascii_bits))   # the standard CCITT check vector
'0x29b1'


function dp_dsss_spread

void dp_dsss_spread (
    const float _Complex * syms,
    size_t syms_len,
    const uint8_t * code,
    size_t code_len,
    int sf,
    float _Complex * out
) 

function dp_field_bits

A Field's bits from its text form the one door from text to the bits every frame object takes.

size_t dp_field_bits (
    const char * spec,
    uint8_t * out,
    const char ** why
) 

The grammar is docs/design/frame-description.md §F.1, read once by dp_wfm_field_parse and rendered once by dp_wfm_field_render, both inside dp_wfm_field_bits; this is that call with the output sized to fit. 0101 and 0x1ACFFC1D are literals, pn:LEN:REG[:SEED [:POLY]][:galois|fibonacci], gold:LEN:REG:TA:SA:TB:SB and dotted:LEN are generated, and any of them takes *REPS. Text outside the grammar is refused, never repaired: pn::10, 12abc and 0102 all raise.

Parameters:

  • spec NUL-terminated Field text.
  • out receives the bits, one per byte, 0 or 1; sized by dp_wfm_field_bits (spec, NULL, 0, NULL).
  • why optional; as dp_wfm_field_bits a STATIC sentence naming the refusal, NULL on success. The binding raises it as the ValueError's message.

Returns:

the bits written, or 0 on refusal (the binding raises).

>>> from doppler.wfm import field_bits
>>> field_bits("0x1A").tolist()          # hex, MSB first
[0, 0, 0, 1, 1, 0, 1, 0]
>>> field_bits("dotted:4*2").tolist()    # a repeat is the same bits again
[1, 0, 1, 0, 1, 0, 1, 0]
>>> len(field_bits("pn:1023:10"))        # an m-sequence, 10-bit register
1023
>>> field_bits("pn::10")
Traceback (most recent call last):
    ...
ValueError: LEN, the output length in bits, must be a number > 0

function dp_mls_poly

Maximal-length-sequence primitive polynomial for a length- n LFSR. Returns the tap mask (in the same bit convention the synth/PN engine uses forpn_poly = 0 ) that drives ann-stage Fibonacci LFSR through its full 2^n - 1 state period. Thin public alias over the synth engine's MLS table; valid forn in 2..64 and returns 0 otherwise.

uint64_t dp_mls_poly (
    uint32_t n
) 

Parameters:

  • n LFSR length in stages (2..64).

Returns:

Primitive-polynomial tap mask, or 0 if n is out of range.

>>> from doppler.wfm import mls_poly
>>> hex(mls_poly(7))
'0x41'


function dp_qpsk_map

Map QPSK symbol indices {0, 1, 2, 3} to Gray-coded symbols (cf32). Gray coding: adjacent indices differ in exactly one bit, minimising BER at low SNR. Bit 0 (LSB) controls I, bit 1 controls Q: I = (1 - 2*b_i) / sqrt(2), Q = (1 - 2*b_q) / sqrt(2). Output is unit-power (|sym| = 1.0 exactly). The four constellation points lie at the cardinal diagonals of the IQ plane.

void dp_qpsk_map (
    const uint8_t * syms,
    size_t syms_len,
    float _Complex * out
) 

Parameters:

  • syms Array of uint8 symbol indices; values must be in {0,1,2,3}. Bits above position 1 are ignored.
  • syms_len Number of elements in syms.
  • out Output buffer of at least syms_len cf32 elements.
    >>> from doppler.wfm import qpsk_map
    >>> import numpy as np
    >>> idx = np.array([0, 1, 2, 3], dtype=np.uint8)
    >>> out = qpsk_map(idx)
    >>> [round(float(v.real), 4) for v in out]
    [0.7071, -0.7071, 0.7071, -0.7071]
    >>> [round(float(v.imag), 4) for v in out]
    [0.7071, 0.7071, -0.7071, -0.7071]
    

function dp_rc_h

void dp_rc_h (
    const double * t,
    size_t t_len,
    double * out,
    double beta
) 

function dp_rrc_h

void dp_rrc_h (
    const double * t,
    size_t t_len,
    double * out,
    double beta
) 

function dp_rrc_taps

void dp_rrc_taps (
    double beta,
    int sps,
    int span,
    float * out
) 

function dp_wfm_awgn_amplitude

Compute the per-component AWGN amplitude for a target SNR. The AWGN engine uses equal-power I and Q noise: complex noise power is 2 * amplitude². This function inverts that relationship for a given signal_power andsnr_db (measured over the full sample-rate bandwidth): amplitude = sqrt(signal_power / (2 * 10^(snr_db / 10))). Pass the result directly todp_awgn_create to get the exact noise level that corresponds to the requested SNR.

float dp_wfm_awgn_amplitude (
    float snr_db,
    float signal_power
) 

Parameters:

  • snr_db Target SNR in dB, referenced to the full sample rate.
  • signal_power RMS power of the signal (e.g. 1.0 for unit-power complex tones or unit-energy BPSK/QPSK symbols).

Returns:

Per-component AWGN amplitude (sigma for one I or Q channel).

>>> from doppler.wfm import wfm_awgn_amplitude
>>> round(float(wfm_awgn_amplitude(10.0, 1.0)), 6)
0.223607
>>> round(float(wfm_awgn_amplitude(0.0, 1.0)), 6)
0.707107


function dp_wfm_ebno_to_snr_db

Convert Eb/No (dB) to SNR (dB) over the full sample-rate band. Digital communication systems are typically specified in Eb/No; doppler uses an fs-band SNR internally. The conversion is: SNR_fs = Eb/No + 10 log10(bits_per_symbol) - 10 log10(samples_per_symbol) For BPSK (bits_per_symbol=1, sps=8) at Eb/No=10 dB this gives ~0.97 dB. For QPSK (bits_per_symbol=2, sps=8) at Eb/No=10 dB this gives ~3.98 dB.

float dp_wfm_ebno_to_snr_db (
    float ebno_db,
    int bits_per_symbol,
    float samples_per_symbol
) 

Parameters:

  • ebno_db Eb/No in dB (energy per bit over noise spectral density).
  • bits_per_symbol Bits carried per modulation symbol: 1 for BPSK, 2 for QPSK.
  • samples_per_symbol Oversampling ratio (sps), e.g. 8.0.

Returns:

SNR in dB measured over the full sample-rate bandwidth.

>>> from doppler.wfm import wfm_ebno_to_snr_db
>>> round(float(wfm_ebno_to_snr_db(10.0, 2, 8.0)), 4)
3.9794
>>> round(float(wfm_ebno_to_snr_db(10.0, 1, 8.0)), 4)
0.9691



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