Skip to content

File cvt_core.h

FileList > cvt > cvt_core.h

Go to the source code of this file

Cvt module — public C API.

  • #include "clib_common.h"

Public Types

Type Name
enum dp_bitorder_t
Bit order within a byte, for the bit/value conversions below.

Public Functions

Type Name
size_t bin_to_hex (const uint8_t * bits, size_t bits_len, uint8_t * out, size_t out_len, int bitorder)
Render unpacked bits to hex digits inverse of hex_to_bin.
uint64_t bin_to_int (const uint8_t * bits, size_t bits_len, int bitorder)
Read unpacked bits back into an integer inverse of int_to_bin.
size_t bin_to_nrz (const uint8_t * bits, size_t bits_len, float * out, size_t out_len)
Map unpacked bits to bipolar NRZ symbols: 0 -> +1, 1 -> -1.
size_t hex_to_bin (const char * hex, uint8_t * out, size_t out_len, int bitorder)
Expand a hex string to unpacked bits, one per byte.
size_t int_to_bin (uint64_t v, uint32_t n_bits, uint8_t * out, size_t out_len, int bitorder)
Expand the low n_bits of an integer to unpacked bits.
size_t nrz_to_bin (const float * nrz, size_t nrz_len, uint8_t * out, size_t out_len)
Hard-decide NRZ symbols back to bits inverse of bin_to_nrz.

Public Static Functions

Type Name
size_t cvt_bit_slot (size_t i, size_t width, int bitorder)
Where the i-th bit of a unit lands under bitorder .
size_t cvt_unit_width (size_t done, size_t total)
The unit both directions walk in: 8 bits, then whatever is left.

Public Types Documentation

enum dp_bitorder_t

Bit order within a byte, for the bit/value conversions below.

enum dp_bitorder_t {
    DP_BITORDER_BIG = 0,
    DP_BITORDER_LITTLE = 1
};

The name and the values follow numpy's packbits/unpackbits bitorder= argument, because that is the convention anyone writing this conversion has already met. It is a DIFFERENT axis from the endian (le/be) the BLUE writer takes, which selects a file's BYTE order the "EEEI" / "IEEE" field of a type-1000 header. A literal's digit order already fixes which byte comes first; what is left to choose is the order of bits inside one. Overloading one word for both would let a sync word and a sample stream disagree silently.


Public Functions Documentation

function bin_to_hex

Render unpacked bits to hex digits inverse of hex_to_bin.

size_t bin_to_hex (
    const uint8_t * bits,
    size_t bits_len,
    uint8_t * out,
    size_t out_len,
    int bitorder
) 

The digits come back as ASCII BYTES rather than a string: jm has no string out-parameter for a module function (just-buildit/just-makeit#1180), and uint8_t is the same type as the unsigned char a C caller would use anyway. A NUL is written after the digits. The C face is honest; only the Python face pays, with a bytes(out).decode().

Parameters:

  • bits unpacked bits; any non-zero byte reads as 1.
  • bits_len number of bits; must be a multiple of 4.
  • out receives the digits plus a NUL.
  • out_len capacity of out in bytes, NUL included.
  • bitorder DP_BITORDER_BIG or DP_BITORDER_LITTLE.

Returns:

digits written, NOT counting the NUL, or 0 on refusal.

>>> import numpy as np
>>> from doppler.cvt import hex_to_bin, bin_to_hex
>>> b = np.zeros(32, np.uint8)
>>> hex_to_bin("1acffc1d", b, 0)
32
>>> h = np.zeros(16, np.uint8)
>>> n = bin_to_hex(b, h, 0)
>>> bytes(h[:n]).decode()
'1acffc1d'

function bin_to_int

Read unpacked bits back into an integer inverse of int_to_bin.

uint64_t bin_to_int (
    const uint8_t * bits,
    size_t bits_len,
    int bitorder
) 

Returns the value rather than a status, because that is the shape a binding can carry. 0 is therefore both "the value zero" and "refused", which is acceptable only because every refusal here is a programming error in the WIDTH the caller chose (0, or over 64) or the bit order it named never a property of the data.

Parameters:

  • bits 1..64 unpacked bits; any non-zero byte reads as 1.
  • bits_len number of bits.
  • bitorder DP_BITORDER_BIG or DP_BITORDER_LITTLE.

Returns:

the value, or 0 on refusal.

>>> import numpy as np
>>> from doppler.cvt import bin_to_int
>>> bits = np.array([0, 0, 0, 1, 1, 0, 1, 0], np.uint8)
>>> hex(bin_to_int(bits, 0))
'0x1a'

function bin_to_nrz

Map unpacked bits to bipolar NRZ symbols: 0 -> +1, 1 -> -1.

size_t bin_to_nrz (
    const uint8_t * bits,
    size_t bits_len,
    float * out,
    size_t out_len
) 

That is 1 - 2*b, and the convention's HOME is mpsk_core.h: BPSK is M-PSK at m = 2, where phi0 is 0, so label 0 lands at +1 and label 1 at -1. This states the same thing in the form a per-bit loop can afford, and test_cvt_core asserts the two agree rather than trusting them to. A mapper that disagreed with the receiver's would decode every bit INVERTED while looking perfectly locked which a round-trip test cannot see.

Parameters:

  • bits unpacked bits; any non-zero byte reads as 1.
  • bits_len number of bits.
  • out receives bits_len symbols, each +1.0f or -1.0f.
  • out_len capacity of out in symbols.

Returns:

symbols written, or 0 on refusal.

>>> import numpy as np
>>> from doppler.cvt import bin_to_nrz
>>> bits = np.array([0, 1, 1, 0], np.uint8)
>>> sym = np.zeros(4, np.float32)
>>> bin_to_nrz(bits, sym)
4
>>> sym.tolist()
[1.0, -1.0, -1.0, 1.0]

function hex_to_bin

Expand a hex string to unpacked bits, one per byte.

size_t hex_to_bin (
    const char * hex,
    uint8_t * out,
    size_t out_len,
    int bitorder
) 

For what int_to_bin cannot serve: a literal wider than 64 bits, or one arriving as TEXT from a CLI flag or a JSON record. Each digit contributes 4 bits and digits read left to right, so an ODD number of digits is accepted and yields a 4-bit tail.

A bad digit is a REFUSAL, never a skipped one: a typo'd marker that silently shortens is the failure this exists to prevent, and it syncs to nothing rather than failing loudly.

Parameters:

  • hex NUL-terminated 0-9a-fA-F. No 0x, no separators.
  • out receives 4 * strlen(hex) bytes, each 0 or 1.
  • out_len capacity of out in bits.
  • bitorder DP_BITORDER_BIG or DP_BITORDER_LITTLE.

Returns:

bits written, or 0 on refusal out untouched.

>>> import numpy as np
>>> from doppler.cvt import hex_to_bin
>>> b = np.zeros(32, np.uint8)
>>> hex_to_bin("1ACFFC1D", b, 0)       # the CCSDS attached sync marker
32
>>> b[:8].tolist()
[0, 0, 0, 1, 1, 0, 1, 0]

function int_to_bin

Expand the low n_bits of an integer to unpacked bits.

size_t int_to_bin (
    uint64_t v,
    uint32_t n_bits,
    uint8_t * out,
    size_t out_len,
    int bitorder
) 

The form a frame field literal usually wants, and the one to reach for first: exact, compiler-checked, with no failure mode a typo can reach. hex_to_bin is for the two cases this cannot serve a literal wider than 64 bits, and text arriving from outside.

Bit 0 out is the MOST significant of the n_bits requested under DP_BITORDER_BIG, which is what makes int_to_bin(0x1A, 8, ...) read 0,0,0,1,1,0,1,0. Only the low n_bits are read, so a caller need not mask first.

Parameters:

  • v the value.
  • n_bits 1..64.
  • out receives n_bits bytes, each 0 or 1.
  • out_len capacity of out in bits.
  • bitorder DP_BITORDER_BIG or DP_BITORDER_LITTLE.

Returns:

n_bits, or 0 on refusal out untouched.

>>> import numpy as np
>>> from doppler.cvt import int_to_bin
>>> b = np.zeros(8, np.uint8)
>>> int_to_bin(0x1A, 8, b, 0)          # 0 = big, MSB of each byte first
8
>>> b.tolist()
[0, 0, 0, 1, 1, 0, 1, 0]

function nrz_to_bin

Hard-decide NRZ symbols back to bits inverse of bin_to_nrz.

size_t nrz_to_bin (
    const float * nrz,
    size_t nrz_len,
    uint8_t * out,
    size_t out_len
) 

Negative is a 1; zero and positive are a 0, matching 1 - 2*b. Exactly zero decides to 0 rather than a coin toss, so the mapping is TOTAL and a round trip is exact. A caller that wants an erasure handled as an erasure wants a soft demapper, not this.

Parameters:

  • nrz symbols.
  • nrz_len number of symbols.
  • out receives nrz_len bytes, each 0 or 1.
  • out_len capacity of out in bits.

Returns:

bits written, or 0 on refusal.

>>> import numpy as np
>>> from doppler.cvt import nrz_to_bin
>>> sym = np.array([1.0, -1.0, -1.0, 1.0], np.float32)
>>> bits = np.zeros(4, np.uint8)
>>> nrz_to_bin(sym, bits)
4
>>> bits.tolist()
[0, 1, 1, 0]

Public Static Functions Documentation

function cvt_bit_slot

Where the i-th bit of a unit lands under bitorder .

static inline size_t cvt_bit_slot (
    size_t i,
    size_t width,
    int bitorder
) 


function cvt_unit_width

The unit both directions walk in: 8 bits, then whatever is left.

static inline size_t cvt_unit_width (
    size_t done,
    size_t total
) 

Written once so the value and the string forms cannot disagree about where a short final unit begins the only place they could drift, and the place a marker would then be expanded two ways.



The documentation for this class was generated from the following file native/inc/cvt/cvt_core.h