Skip to content

File spectrogram_core.h

FileList > doppler > spectrogram > spectrogram_core.h

Go to the source code of this file

Streaming spectrogram: a stream of any-size chunks in, rows of nfft-bin spectra out, one row every hop samples. More...

  • #include "doppler/clib_common.h"
  • #include "doppler/dp_state.h"
  • #include "doppler/f32_buffer/f32_buffer_core.h"
  • #include "doppler/psd/psd_core.h"
  • #include <stddef.h>
  • #include <stdint.h>
  • #include "doppler/dp_interrupt_guard/dp_interrupt_guard_core.h"

Classes

Type Name
struct dp_spectrogram_state_t
Spectrogram state. Allocate with dp_spectrogram_create() .

Public Types

Type Name
enum spectrogram__core_8h_1abed82baf7f470b522273a3e37c24c600

Public Functions

Type Name
size_t dp_spectrogram_consumed (const dp_spectrogram_state_t * s)
Input samples the last dp_spectrogram_push() took.
dp_spectrogram_state_t * dp_spectrogram_create (size_t nfft, size_t hop, int window, float beta, int mode)
Create a streaming spectrogram.
void dp_spectrogram_destroy (dp_spectrogram_state_t * s)
Release a spectrogram and everything it owns.
int64_t dp_spectrogram_flush (dp_spectrogram_state_t * s, float * row, size_t row_len)
End the stream: write the one zero-padded row it still owes, if any.
size_t dp_spectrogram_flush_max_out (const dp_spectrogram_state_t * s)
Floats dp_spectrogram_flush() writes: nfft, the one row it can owe.
void dp_spectrogram_get_state (const dp_spectrogram_state_t * s, void * blob)
Serialize the configuration a row depends on, then the stream position, into blob .
size_t dp_spectrogram_pending (const dp_spectrogram_state_t * s)
Samples pushed that no written row has covered yet.
size_t dp_spectrogram_push (dp_spectrogram_state_t * s, const float _Complex * in, size_t n_in, float * out, size_t max_out)
Push input, write every whole row it completes that fits.
size_t dp_spectrogram_push_max_out (const dp_spectrogram_state_t * s, size_t n_in)
Floats one push of n_in samples writes whenout has room: the rows_for(n_in) count times nfft.
void dp_spectrogram_reset (dp_spectrogram_state_t * s)
Forget the stream: drop the carry and restart at sample 0.
size_t dp_spectrogram_rows_for (const dp_spectrogram_state_t * s, size_t n_in)
Rows a push of n_in more samples completes, given the carry: exact, not an estimate.
int dp_spectrogram_set_state (dp_spectrogram_state_t * s, const void * blob)
Restore the stream position from a blob of the same configuration, so the next push continues it bit for bit.
size_t dp_spectrogram_state_bytes (const dp_spectrogram_state_t * s)
Bytes of the state blob, a function of nfft alone.

Macros

Type Name
define SPECTROGRAM_STATE_MAGIC [**DP\_FOURCC**](dp__state_8h.md#define-dp_fourcc) ('S', 'P', 'G', 'M')
State-blob magic ('SPGM') and layout version.
define SPECTROGRAM_STATE_VERSION 2u

Detailed Description

A row is the PSD of one frame, and nothing else: row k is PSD's per-frame kernel applied to stream samples [k*hop, k*hop + nfft) dp_psd_frame_linear() for power rows, the default, or dp_psd_frame_db() for dB rows, which a caller asks for by name. So the window, the FFT and the full-scale reference are PSD's own, and a full-scale tone on a bin reads 1.0 in power, 0 dB in dB. The rows are a function of the INPUT STREAM, not of how it was split into calls: pushing it in one call, a sample at a time, or in any other partition gives the same rows, bit for bit.

The object composes and re-implements none of its parts. The carry between calls is the ring's framed face (DECLARE_DP_BUFFER_FRAMES): fewer than nfft samples are held once a push returns, so the state blob has a size that depends on nfft alone. The spectrum is PSD's per-frame kernel, held by value (dp_psd_kernel_t): the window, the transform and the full-scale reference, and none of PSD's averaging. It does not average rows (fold them with AccTrace), detect, display or decimate.

Lifecycle: dp_spectrogram_create(), then any number of dp_spectrogram_push() calls, then dp_spectrogram_flush() once to end the stream, then dp_spectrogram_destroy(). A short output buffer never loses input: push stops at a whole row and dp_spectrogram_consumed() says where to resume.

Not thread-safe on one object (the kernel uses the object's scratch). Complex float32 input only. The design, its goals and what it costs are docs/design/spectrogram.md. The C examples are native/examples/ spectrogram_demo.c, which the example gate runs; they are not kept in this header, because the Python docstrings are generated from it.

Public Types Documentation

enum spectrogram__core_8h_1abed82baf7f470b522273a3e37c24c600

enum spectrogram__core_8h_1abed82baf7f470b522273a3e37c24c600 {
    DP_SPECTROGRAM_POWER = 0,
    DP_SPECTROGRAM_DB = 1
};

Public Functions Documentation

function dp_spectrogram_consumed

Input samples the last dp_spectrogram_push() took.

size_t dp_spectrogram_consumed (
    const dp_spectrogram_state_t * s
) 

Equal to its n_in unless out ran out of room; then the caller resumes at in + consumed. 0 after create, reset, flush and set_state.

Parameters:

  • s Must be non-NULL.

function dp_spectrogram_create

Create a streaming spectrogram.

dp_spectrogram_state_t * dp_spectrogram_create (
    size_t nfft,
    size_t hop,
    int window,
    float beta,
    int mode
) 

Parameters:

  • nfft Samples per frame and bins per row, which must be the same number: an nfft that would be zero-padded to a longer transform (anything but a power of two >= 2) is refused rather than given rows wider than its frames.
  • hop Samples between row starts, 1 to nfft. 0 means nfft, which tiles the stream; the default. Less than nfft overlaps the frames. A hop above nfft would leave samples no row covers, so it is refused.
  • window The window: "hann", "kaiser", "blackman-harris" or "rect", as PSD takes them. It also refuses a window that sums to zero at this nfft (the symmetric Hann at nfft = 2).
  • beta Kaiser beta (ignored for the other windows).
  • mode Row units: "power" (linear rows, the default) or "db" (dBFS rows, asked for by name). Any other value is refused.

Returns:

Heap-allocated state, or NULL on an invalid argument.

Every row is DC-centred exactly as PSD's kernel emits it: bin k at index nfft/2 + k, negative frequencies first.

Note:

Call dp_spectrogram_destroy() when done.


function dp_spectrogram_destroy

Release a spectrogram and everything it owns.

void dp_spectrogram_destroy (
    dp_spectrogram_state_t * s
) 

Parameters:

  • s May be NULL (no-op).

function dp_spectrogram_flush

End the stream: write the one zero-padded row it still owes, if any.

int64_t dp_spectrogram_flush (
    dp_spectrogram_state_t * s,
    float * row,
    size_t row_len
) 

The row sits on the hop grid: it starts at the next row start k*hop, never at the first sample no row covered, so it is the row a one-shot push of the input zero-padded to that row's end would have written. It is written if and only if the stream holds a sample no earlier row covered. Either way the spectrogram then restarts at sample 0, as after reset, so a second flush writes nothing.

Parameters:

  • s Must be non-NULL.
  • row Room for nfft floats.
  • row_len Floats of room in row. Must be exactly nfft: any other length is refused before anything moves, the owed frame stays owed, and -1 is returned (never a count).

Returns:

Floats written: nfft, or 0 if no row was owed; -1 if the row is not exactly nfft floats long. From Python, the owed row as a (1, nfft) matrix, or None when none was owed.


function dp_spectrogram_flush_max_out

Floats dp_spectrogram_flush() writes: nfft, the one row it can owe.

size_t dp_spectrogram_flush_max_out (
    const dp_spectrogram_state_t * s
) 

Sized by the object alone, since a flush owes at most one row. It is also the only row length dp_spectrogram_flush() accepts.

Parameters:

  • s Must be non-NULL.

function dp_spectrogram_get_state

Serialize the configuration a row depends on, then the stream position, into blob .

void dp_spectrogram_get_state (
    const dp_spectrogram_state_t * s,
    void * blob
) 

Parameters:

  • s Must be non-NULL.
  • blob dp_spectrogram_state_bytes(s) bytes, every one written.

function dp_spectrogram_pending

Samples pushed that no written row has covered yet.

size_t dp_spectrogram_pending (
    const dp_spectrogram_state_t * s
) 

What dp_spectrogram_flush() would turn into a row: 0 means the stream is complete as it stands.

Parameters:

  • s Must be non-NULL.

function dp_spectrogram_push

Push input, write every whole row it completes that fits.

size_t dp_spectrogram_push (
    dp_spectrogram_state_t * s,
    const float _Complex * in,
    size_t n_in,
    float * out,
    size_t max_out
) 

Takes the input in order and produces each row as soon as a frame of nfft samples is complete, at every hop. A partial frame is carried to the next call, so the rows do not depend on how the input was split into calls. Input that completes no row is always taken whole.

Only whole rows are written. A C caller bounds the write with max_out and takes the rows that fit; the consumed count reports the input those rows used, and the caller offers the rest again. The Python face has no short buffer: its binding sizes the output for the whole chunk (see the Python method doc).

Parameters:

  • s Must be non-NULL.
  • in Complex baseband samples (cf32).
  • n_in Samples in in.
  • out Rows, row-major, nfft floats each.
  • max_out Floats out has room for.

Returns:

Floats written, a multiple of nfft. From Python, the completed rows as a (rows, nfft) matrix, or None when none is completed.


function dp_spectrogram_push_max_out

Floats one push of n_in samples writes whenout has room: the rows_for(n_in) count times nfft.

size_t dp_spectrogram_push_max_out (
    const dp_spectrogram_state_t * s,
    size_t n_in
) 

The capacity that makes a push take ALL of its input. Saturates at SIZE_MAX rather than wrapping.

Parameters:

  • s Must be non-NULL.
  • n_in Samples about to be pushed.

function dp_spectrogram_reset

Forget the stream: drop the carry and restart at sample 0.

void dp_spectrogram_reset (
    dp_spectrogram_state_t * s
) 

The next row covers samples [0, nfft) of whatever is pushed next. The consumed count and the pending count read 0. Configuration is kept.

Parameters:

  • s Must be non-NULL.

function dp_spectrogram_rows_for

Rows a push of n_in more samples completes, given the carry: exact, not an estimate.

size_t dp_spectrogram_rows_for (
    const dp_spectrogram_state_t * s,
    size_t n_in
) 

The carry plus n_in samples, cut into frames of nfft at the hop. A push with room for this many rows writes exactly this many and takes all of n_in.

Parameters:

  • s Must be non-NULL.
  • n_in Samples about to be pushed.

function dp_spectrogram_set_state

Restore the stream position from a blob of the same configuration, so the next push continues it bit for bit.

int dp_spectrogram_set_state (
    dp_spectrogram_state_t * s,
    const void * blob
) 

The blob is refused, with s unchanged, when its magic, version or size is wrong; when the configuration it carries differs from s's another mode, nfft, hop or window, or under the Kaiser window another beta, since a checkpoint from another configuration would mean something else (#2102); or when its carry is corrupt. A beta under any other window changes no row, so it is not compared. A version-1 blob, which carried no configuration, is refused by its version.

Parameters:

Returns:

DP_OK, or DP_ERR_INVALID.


function dp_spectrogram_state_bytes

Bytes of the state blob, a function of nfft alone.

size_t dp_spectrogram_state_bytes (
    const dp_spectrogram_state_t * s
) 

The blob is the configuration a row depends on mode, nfft, hop, window and beta then the stream position: the framer's snapshot, which is the carry (fewer than nfft samples, padded to nfft - 1), its element size, the stream counts and the hop that frames them. Both sit inside the spectrogram's own envelope, version 2. Every field has a fixed size, so the length depends on nfft alone. It does not carry consumed(), which reads 0 after a restore. What a restore refuses is dp_spectrogram_set_state()'s.

Parameters:

  • s Must be non-NULL.

Macro Definition Documentation

define SPECTROGRAM_STATE_MAGIC

State-blob magic ('SPGM') and layout version.

#define SPECTROGRAM_STATE_MAGIC `DP_FOURCC ('S', 'P', 'G', 'M')`


define SPECTROGRAM_STATE_VERSION

#define SPECTROGRAM_STATE_VERSION `2u`


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