Skip to content

Python Type Converter API

The doppler.cvt module converts sample streams between float32 and the fixed-point / integer formats used at the edges of a DSP chain — ADC codes, Q15 fractions, Q15-in-wide-word for CIC — plus an ideal-quantiser ADC model for characterisation. Every converter is a tiny stateful object with a scale (or ADC depth) fixed at construction; steps() runs a whole block (optionally in-place via out=), step() does one sample.

For the F32To* direction, scale is the gain applied before quantising (code = round(x · scale)); the default scale = 32768 maps the normalised range [-1, +1) onto full-scale int16 and saturates beyond it. The *ToF32 direction divides by the same scale to recover the float.

Source: src/doppler/cvt/__init__.py

For the quantisation theory behind the scaling and the UQ15 unsigned format, see the Quantization gallery page.


Converter map

Class In → Out Use
F32ToI16 / I16ToF32 float32 ↔ int16 signed Q15 / 16-bit PCM round-trip
I32ToF32 int32 → float32 24/32-bit ADC codes to float
I8ToF32 int8 → float32 8-bit codes to float
F32ToUQ15 / UQ15ToF32 float32 ↔ uint16 unsigned Q15 (offset-binary) round-trip
F32ToI16U32 / I16U32ToF32 float32 ↔ uint32 one Q15 in the low 16 bits (CIC integer in)
F32ToI16U64 / I16U64ToF32 float32 ↔ uint64 one Q15 in the low 16 bits (CIC integer in)
ADC float32 → int64 ideal bits-bit quantiser (codes)

The F32To* directions expose a clipped flag that latches when an input exceeded full scale during the last steps().


Examples

float32 ↔ int16 round-trip

The default scale = 32768 maps [-1, +1) to full-scale int16.

import numpy as np
from doppler.cvt import F32ToI16, I16ToF32

x = np.array([0.0, 0.5, -1.0, 0.999], dtype=np.float32)

enc = F32ToI16()                # default scale = 32768
codes = enc.steps(x)            # array([0, 16384, -32768, 32735], dtype=int16)
if enc.clipped:                 # latches when an input exceeded full scale
    print("input exceeded full scale")

dec = I16ToF32()                # default scale = 32768
back = dec.steps(codes)         # ~= x  ([0.0, 0.5, -1.0, 0.999])

In-place block conversion

Pass a pre-allocated out= to avoid an allocation in a hot loop.

codes = np.empty(len(x), dtype=np.int16)
enc.steps(x, out=codes)

Ideal ADC quantiser (characterisation)

ADC(bits, dbfs, dithering) models an ideal converter: a full-scale sine at dbfs=0.0 spans ±2**(bits-1) codes. Feed its output straight into ToneMeasure to recover ENOB ≈ bits.

from doppler.cvt import ADC

adc = ADC(12, 0.0, 0)                       # 12-bit, 0 dBFS, no dither
x = np.array([0.0, 0.5, 0.999, -1.0], dtype=np.float32)
adc.steps(x)                                # array([0, 1024, 2046, -2048])

Unsigned Q15 (offset binary)

F32ToUQ15 maps [-1, 1) onto uint16 centred at 32768 (offset binary), the convention used by many unsigned ADCs.

from doppler.cvt import F32ToUQ15, UQ15ToF32

enc = F32ToUQ15()              # default scale = 32768
u = enc.steps(np.array([-1.0, 0.0, 0.999], dtype=np.float32))   # 0, 32768, 65503
back = UQ15ToF32().steps(u)    # ~= input

Signed integer ↔ float

F32ToI16

F32ToI16 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import F32ToI16
>>> obj = F32ToI16(scale=32768.0)

clipped property

clipped: bool

True if any sample has been saturated since the last reset().

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: float) -> int

Process one input sample.

steps

steps(x: NDArray[float32], out: NDArray[int16] | None = None) -> NDArray[np.int16]

Process a block of float samples to int16.

Applies step() to every element. The clipped flag is updated cumulatively across the block — a single saturating sample raises it for the entire call. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Returns:

Type Description
NDArray[int16]

Output.

Examples:

>>> from doppler.cvt import F32ToI16
>>> import numpy as np
>>> x = np.array([0.0, 0.5, -1.0, 0.999], dtype=np.float32)
>>> F32ToI16().steps(x).tolist()   # default scale=32768 -> full-scale int16
[0, 16384, -32768, 32735]

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.

I16ToF32

I16ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import I16ToF32
>>> obj = I16ToF32(scale=32768.0)

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[int16], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of int16 samples to float32.

Applies step() to every element. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[int16]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import I16ToF32
>>> import numpy as np
>>> I16ToF32().steps(np.array([0, 16384, -32768], dtype=np.int16)).tolist()
[0.0, 0.5, -1.0]

destroy

destroy() -> None

Release C resources immediately.

I32ToF32

I32ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

2147483648.0

Examples:

Create with defaults:

>>> from doppler.cvt import I32ToF32
>>> obj = I32ToF32(scale=2147483648.0)

reset

reset() -> None

Reset I32ToF32 to its post-create state.

No mutable state exists beyond the immutable iscale; reset is a no-op provided for lifecycle symmetry with other converters.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[int32], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of int32 samples to float32.

Applies step() to every element. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[int32]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import I32ToF32
>>> import numpy as np
>>> I32ToF32().steps(np.array([0, 2**30, -2**31], dtype=np.int32)).tolist()
[0.0, 0.5, -1.0]

destroy

destroy() -> None

Release C resources immediately.

I8ToF32

I8ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

128.0

Examples:

Create with defaults:

>>> from doppler.cvt import I8ToF32
>>> obj = I8ToF32(scale=128.0)

reset

reset() -> None

Reset I8ToF32 to its post-create state.

No mutable state exists beyond the immutable iscale; reset is a no-op provided for lifecycle symmetry with other converters.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[int8], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of int8 samples to float32.

Applies step() to every element. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[int8]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import I8ToF32
>>> import numpy as np
>>> I8ToF32().steps(np.array([0, 64, -128], dtype=np.int8)).tolist()
[0.0, 0.5, -1.0]

destroy

destroy() -> None

Release C resources immediately.


Unsigned Q15 (offset binary)

F32ToUQ15

F32ToUQ15 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import F32ToUQ15
>>> obj = F32ToUQ15(scale=32768.0)

clipped property

clipped: bool

True if any sample has been saturated since the last reset().

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: float) -> int

Process one input sample.

steps

steps(x: NDArray[float32], out: NDArray[uint16] | None = None) -> NDArray[np.uint16]

Process a block of float samples to UQ15 uint16.

Applies step() to every element. The clipped flag is updated cumulatively across the block. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Returns:

Type Description
NDArray[uint16]

Output.

Examples:

>>> from doppler.cvt import F32ToUQ15
>>> import numpy as np
>>> F32ToUQ15().steps(np.array([-1.0, 0.0, 0.999], dtype=np.float32)).tolist()
[0, 32768, 65503]

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.

UQ15ToF32

UQ15ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import UQ15ToF32
>>> obj = UQ15ToF32(scale=32768.0)

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[uint16], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of UQ15 samples to float32.

Applies step() to every element. State is not mutated (no clipped flag). Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[uint16]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import UQ15ToF32
>>> import numpy as np
>>> UQ15ToF32().steps(np.array([0, 32768], dtype=np.uint16)).tolist()
[-1.0, 0.0]

destroy

destroy() -> None

Release C resources immediately.


Q15 in a wide word (CIC integer input)

These pack a single saturated Q15 into the low 16 bits of a uint32/uint64 (upper bits zero) — the integer-input wire format the CIC filter expects, where the headroom absorbs the bit-growth of the integrator cascade.

F32ToI16U32

F32ToI16U32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import F32ToI16U32
>>> obj = F32ToI16U32(scale=32768.0)

clipped property

clipped: bool

True if any sample has been saturated since the last reset().

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: float) -> int

Process one input sample.

steps

steps(x: NDArray[float32], out: NDArray[uint32] | None = None) -> NDArray[np.uint32]

Process a block of float samples to Q15-in-uint32.

Applies step() to every element. The clipped flag is updated cumulatively across the block. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Returns:

Type Description
NDArray[uint32]

Output.

Examples:

>>> from doppler.cvt import F32ToI16U32
>>> import numpy as np
>>> F32ToI16U32().steps(np.array([0.0, 0.5], dtype=np.float32)).tolist()
[0, 16384]

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.

I16U32ToF32

I16U32ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import I16U32ToF32
>>> obj = I16U32ToF32(scale=32768.0)

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[uint32], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of Q15-in-uint32 samples to float32.

Applies step() to every element. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[uint32]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import I16U32ToF32
>>> import numpy as np
>>> I16U32ToF32().steps(np.array([0, 16384], dtype=np.uint32)).tolist()
[0.0, 0.5]

destroy

destroy() -> None

Release C resources immediately.

F32ToI16U64

F32ToI16U64 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import F32ToI16U64
>>> obj = F32ToI16U64(scale=32768.0)

clipped property

clipped: bool

True if any sample has been saturated since the last reset().

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: float) -> int

Process one input sample.

steps

steps(x: NDArray[float32], out: NDArray[uint64] | None = None) -> NDArray[np.uint64]

Process a block of float samples to Q15-in-uint64.

Applies step() to every element. The clipped flag is updated cumulatively across the block. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Returns:

Type Description
NDArray[uint64]

Output.

Examples:

>>> from doppler.cvt import F32ToI16U64
>>> import numpy as np
>>> F32ToI16U64().steps(np.array([0.0, 0.5], dtype=np.float32)).tolist()
[0, 16384]

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.

I16U64ToF32

I16U64ToF32 component.

Parameters:

Name Type Description Default
scale float

scale constructor parameter.

32768.0

Examples:

Create with defaults:

>>> from doppler.cvt import I16U64ToF32
>>> obj = I16U64ToF32(scale=32768.0)

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: int) -> float

Process one input sample.

steps

steps(x: NDArray[uint64], out: NDArray[float32] | None = None) -> NDArray[np.float32]

Process a block of Q15-in-uint64 samples to float32.

Applies step() to every element. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[uint64]

Input.

required

Returns:

Type Description
NDArray[float32]

Output.

Examples:

>>> from doppler.cvt import I16U64ToF32
>>> import numpy as np
>>> I16U64ToF32().steps(np.array([0, 16384], dtype=np.uint64)).tolist()
[0.0, 0.5]

destroy

destroy() -> None

Release C resources immediately.


Ideal ADC model

ADC

Create an ADC instance.

Parameters:

Name Type Description Default
bits int

ADC resolution in bits (1..64).

16
dbfs float

Full-scale reference level in dBFS (typically negative, e.g. -10.0). A signal with amplitude 10^(dbfs/20) fills the converter's integer range exactly.

-10.0
dithering int

0 = no dither; non-zero = TPDF dither before rounding.

0

Examples:

Create with defaults:

>>> from doppler.cvt import ADC
>>> obj = ADC(bits=16, dbfs=-10.0, dithering=0)

clipped property

clipped: bool

Clipped.

scale property

scale: float

Scale.

bits property

bits: int

Bits.

reset

reset() -> None

Reset state to post-create defaults.

step

step(x: float) -> int

Process one input sample.

steps

steps(x: NDArray[float32], out: NDArray[int64] | None = None) -> NDArray[np.int64]

Process a block of float samples to int64.

When dithering is disabled the float-to-double multiply can use SIMD widening (jm_simd.h); the int64_t conversion and clamp remain scalar. When dithering is enabled the loop is scalar to preserve sequential PRNG state. Accepts an optional pre-allocated output array; allocates a fresh one when output is NULL.

Parameters:

Name Type Description Default
x NDArray[float32]

Input.

required

Returns:

Type Description
NDArray[int64]

Output.

Examples:

>>> from doppler.cvt import ADC
>>> import numpy as np
>>> # ideal 12-bit ADC: full scale spans +-2**11 codes
>>> ADC(12, 0.0, 0).steps(np.array([0.0, 0.5, 0.999, -1.0],
...                                dtype=np.float32)).tolist()
[0, 1024, 2046, -2048]

state_bytes

state_bytes() -> int

Serialized state size in bytes.

get_state

get_state() -> bytes

Serialize the engine's mutable state to bytes.

set_state

set_state(blob: bytes) -> None

Restore mutable state from a get_state() blob.

destroy

destroy() -> None

Release C resources immediately.