Skip to content

Commit

Permalink
first pass of EOO data through C API
Browse files Browse the repository at this point in the history
  • Loading branch information
drowe67 committed Nov 28, 2024
1 parent bcb032f commit 09e9547
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 19 deletions.
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,15 @@ add_test(NAME radae_eoo_data_py
cat rx.f32 | python3 radae_rxe.py -v 2 --eoo_data_test > /dev/null")
set_tests_properties(radae_eoo_data_py PROPERTIES PASS_REGULAR_EXPRESSION "PASS")

# C tx. C rx. Note Python radae_txe.py just generates eoo_tx.f32 for C radae_tx
add_test(NAME radae_eoo_data_c
COMMAND sh -c "cd ${CMAKE_SOURCE_DIR}; \
${CMAKE_CURRENT_BINARY_DIR}/src/lpcnet_demo -features wav/brian_g8sez.wav features_in.f32; \
cat features_in.f32 | python3 radae_txe.py --eoo_data_test > /dev/null; \
cat features_in.f32 | PYTHONPATH='.' ${CMAKE_CURRENT_BINARY_DIR}/src/radae_tx > rx.f32; \
cat rx.f32 | PYTHONPATH='.' ${CMAKE_CURRENT_BINARY_DIR}/src/radae_rx > /dev/null; \
python3 eoo_ber.py eoo_tx.f32 eoo_rx.f32")
set_tests_properties(radae_eoo_data_c PROPERTIES PASS_REGULAR_EXPRESSION "PASS")

# BBFM -----------------------------------------------------------------------------------------------

Expand Down
12 changes: 12 additions & 0 deletions eoo_ber.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import sys
import numpy as np

# bits are in float form, e.g. +/-1 or +/-1000
tx_bits = np.fromfile(sys.argv[1], dtype=np.float32)
rx_bits = np.fromfile(sys.argv[2], dtype=np.float32)
n_bits = len(tx_bits)
n_errors = sum(rx_bits*tx_bits < 0)
ber = n_errors/n_bits
print(f"EOO data n_bits: {n_bits} n_errors: {n_errors} BER: {ber:5.2f}", file=sys.stderr)
if ber < 0.05:
print("PASS", file=sys.stderr)
7 changes: 5 additions & 2 deletions radae_txe.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ def get_Neoo(self):
def get_Neoo_bits(self):
return self.model.Nseoo*self.model.bps
def set_eoo_bits(self,eoo_bits):
self.model.set_eoo_bits(torch.tensor(tx_bits, dtype=torch.float32))
print("setting bits!", file=sys.stderr)
print(eoo_bits[0:8],file=sys.stderr)
self.model.set_eoo_bits(torch.tensor(eoo_bits, dtype=torch.float32))

def do_radae_tx(self,buffer_f32,tx_out):
model = self.model
Expand Down Expand Up @@ -155,8 +157,9 @@ def do_eoo(self,tx_out):
if args.eoo_data_test:
# create a RNG with same sequence for BER testing with separate tx and rx
seed = 65647; rng = np.random.default_rng(seed)
tx_bits = np.sign(rng.random(tx.get_Neoo_bits())-0.5)
tx_bits = np.sign(rng.random(tx.get_Neoo_bits())-0.5, dtype=np.float32)
tx.set_eoo_bits(tx_bits)
tx_bits.tofile("eoo_tx.f32")

tx_out = np.zeros(tx.Nmf,dtype=np.csingle)
while True:
Expand Down
10 changes: 5 additions & 5 deletions src/radae_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
int main(int argc, char *argv[])
{
rade_initialize();
int flags = RADE_USE_C_DECODER | RADE_VERBOSE_0;
int flags = RADE_USE_C_DECODER; // | RADE_VERBOSE_0;
/* special test mode that induces a frequency offset error to test UW false sync detection */
if (argc == 2) {
if (atoi(argv[1]) == 1) {
Expand All @@ -27,8 +27,8 @@ int main(int argc, char *argv[])
int n_rx_in = rade_nin_max(r);
RADE_COMP rx_in[n_rx_in];
int nin = rade_nin(r);
int n_eoo_features_out = rade_n_eoo_features_out(r);
FILE *feoo = fopen("eoo.f32","wb"); assert(feoo != NULL);
int n_eoo_bits = rade_n_eoo_bits(r);
FILE *feoo = fopen("eoo_rx.f32","wb"); assert(feoo != NULL);

#ifdef _WIN32
// Note: freopen() returns NULL if filename is NULL, so
Expand All @@ -43,8 +43,8 @@ int main(int argc, char *argv[])
fwrite(features_out, sizeof(float), n_features_out, stdout);
fflush(stdout);
}
if (n_out == n_eoo_features_out) {
fwrite(features_out, sizeof(float), n_eoo_features_out, feoo);
if (n_out == n_eoo_bits) {
fwrite(features_out, sizeof(float), n_eoo_bits, feoo);
}
nin = rade_nin(r);
}
Expand Down
15 changes: 15 additions & 0 deletions src/radae_tx.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
// For _setmode().
#include <io.h>
Expand All @@ -21,6 +22,16 @@ int main(void)
int n_tx_eoo_out = rade_n_tx_eoo_out(r);
RADE_COMP tx_eoo_out[n_tx_eoo_out];

FILE *feoo_bits = fopen("eoo_tx.f32","rb");
if (feoo_bits) {
int n_eoo_bits = rade_n_eoo_bits(r);
float eoo_bits[n_eoo_bits];
int ret = fread(eoo_bits, sizeof(float), n_eoo_bits, feoo_bits);
assert(ret == n_eoo_bits);
rade_tx_set_eoo_bits(r, eoo_bits);
fclose(feoo_bits);
}

#ifdef _WIN32
// Note: freopen() returns NULL if filename is NULL, so
// we have to use setmode() to make it a binary stream instead.
Expand All @@ -36,6 +47,10 @@ int main(void)
rade_tx_eoo(r,tx_eoo_out);
fwrite(tx_eoo_out, sizeof(RADE_COMP), n_tx_eoo_out, stdout);

// extra silence buf to let Rx finish processing EOO
memset(tx_eoo_out,0,sizeof(tx_eoo_out));
fwrite(tx_eoo_out, sizeof(RADE_COMP), n_tx_eoo_out, stdout);

rade_close(r);
rade_finalize();

Expand Down
51 changes: 41 additions & 10 deletions src/rade_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct rade {

npy_intp Nmf, Neoo;
npy_intp nin, nin_max;
npy_intp n_features_in, n_features_out, n_eoo_features_out;
npy_intp n_features_in, n_features_out, n_eoo_bits;

RADEEnc enc_model;
RADEEncState enc_state;
Expand All @@ -75,6 +75,8 @@ struct rade {
RADE_COMP *tx_out;
PyObject *pMeth_radae_tx_eoo, *pArgs_radae_tx_eoo;
RADE_COMP *tx_eoo_out;
PyObject *pMeth_radae_set_eoo_bits, *pArgs_radae_set_eoo_bits;
float *eoo_bits;

RADEDec dec_model;
RADEDecState dec_state;
Expand Down Expand Up @@ -131,6 +133,7 @@ int rade_tx_open(struct rade *r) {
char *python_module_name = "radae_txe";
char *do_radae_tx_meth_name = "do_radae_tx";
char *do_eoo_meth_name = "do_eoo";
char *set_eoo_bits_meth_name = "set_eoo_bits";

// Load module of Python code
pName = PyUnicode_DecodeFSDefault(python_module_name);
Expand All @@ -153,7 +156,10 @@ int rade_tx_open(struct rade *r) {
r->n_floats_in = (int)call_getter(r->pInst_radae_tx, "get_n_floats_in");
r->Nmf = (int)call_getter(r->pInst_radae_tx, "get_Nmf");
r->Neoo = (int)call_getter(r->pInst_radae_tx, "get_Neoo");
fprintf(stderr, "n_features_in: %d n_floats_in: %d Nmf: %d Neoo: %d\n", (int)r->n_features_in, (int)r->n_floats_in, (int)r->Nmf, (int)r->Neoo);
// num floats is 2 x number of complex QPSK symbols
r->n_eoo_bits = (int)call_getter(r->pInst_radae_tx, "get_Neoo_bits");
fprintf(stderr, "n_features_in: %d n_floats_in: %d Nmf: %d Neoo: %d n_eoo_bits: %d\n",
(int)r->n_features_in, (int)r->n_floats_in, (int)r->Nmf, (int)r->Neoo, (int)r->n_eoo_bits);

// RADAE Tx ---------------------------------------------------------

Expand All @@ -177,7 +183,7 @@ int rade_tx_open(struct rade *r) {
check_error(pValue, "setting up numpy array", "tx_out");
PyTuple_SetItem(r->pArgs_radae_tx, 1, pValue);

// End of Over --------------------------------------------------------
// End of Over Samples --------------------------------------------------

r->pMeth_radae_tx_eoo = PyObject_GetAttrString(r->pInst_radae_tx, do_eoo_meth_name);
check_error(r->pMeth_radae_tx_eoo, "finding", do_eoo_meth_name);
Expand All @@ -191,6 +197,20 @@ int rade_tx_open(struct rade *r) {
check_error(pValue, "setting up numpy array", "tx_eoo_out");
PyTuple_SetItem(r->pArgs_radae_tx_eoo, 0, pValue);

// Set End Of Over Bits -------------------------------------------------

r->pMeth_radae_set_eoo_bits = PyObject_GetAttrString(r->pInst_radae_tx, set_eoo_bits_meth_name);
check_error(r->pMeth_radae_set_eoo_bits, "finding", set_eoo_bits_meth_name);
check_callable(r->pMeth_radae_set_eoo_bits, set_eoo_bits_meth_name, "not callable");
r->pArgs_radae_set_eoo_bits = PyTuple_New(1);

// Python arg is a numpy array used for output to C
r->eoo_bits = (float*)malloc(sizeof(float)*r->n_eoo_bits);
assert(r->eoo_bits != NULL);
pValue = PyArray_SimpleNewFromData(1, &r->n_eoo_bits, NPY_FLOAT, r->eoo_bits);
check_error(pValue, "setting up numpy array", "eoo_bits");
PyTuple_SetItem(r->pArgs_radae_set_eoo_bits, 0, pValue);

if (r->flags & RADE_USE_C_ENCODER) {
if (init_radeenc(&r->enc_model, radeenc_arrays, r->num_features*RADE_FRAMES_PER_STEP) != 0) {
fprintf(stderr, "Error initialising built-in C encoder model\n");
Expand All @@ -208,12 +228,15 @@ void rade_tx_close(struct rade *r) {
Py_DECREF(r->pMeth_radae_tx);
Py_DECREF(r->pMeth_radae_tx_eoo);
Py_DECREF(r->pArgs_radae_tx_eoo);
Py_DECREF(r->pMeth_radae_set_eoo_bits);
Py_DECREF(r->pArgs_radae_set_eoo_bits);
Py_DECREF(r->pInst_radae_tx);
Py_DECREF(r->pModule_radae_tx);

free(r->floats_in);
free(r->tx_out);
free(r->tx_eoo_out);
free(r->eoo_bits);
}

// returns 0 for success
Expand Down Expand Up @@ -249,13 +272,11 @@ int rade_rx_open(struct rade *r) {
Py_DECREF(pkwArgs);

r->n_features_out = (int)call_getter(r->pInst_radae_rx, "get_n_features_out");
// num floats is 2 x number of complex QPSK symbols
r->n_eoo_features_out = (int)2*call_getter(r->pInst_radae_rx, "get_n_eoo_features_out");
r->n_floats_out = (int)call_getter(r->pInst_radae_rx, "get_n_floats_out");
r->nin_max = (int)call_getter(r->pInst_radae_rx, "get_nin_max");
r->nin = (int)call_getter(r->pInst_radae_rx, "get_nin");
fprintf(stderr, "n_features_out: %d n_eoo_features_out: %d n_floats_out: %d nin_max: %d nin: %d\n",
(int)r->n_features_out, (int)r->n_eoo_features_out, (int)r->n_floats_out, (int)r->nin_max, (int)r->nin);
fprintf(stderr, "n_features_out: %d n_eoo_bits: %d n_floats_out: %d nin_max: %d nin: %d\n",
(int)r->n_features_out, (int)r->n_eoo_bits, (int)r->n_floats_out, (int)r->nin_max, (int)r->nin);

r->pMeth_radae_rx = PyObject_GetAttrString(r->pInst_radae_rx, do_radae_rx_meth_name);
check_error(r->pMeth_radae_rx, "finding", do_radae_rx_meth_name);
Expand Down Expand Up @@ -364,7 +385,17 @@ int rade_n_tx_eoo_out(struct rade *r) { assert(r != NULL); return (int)r->Neoo;
int rade_nin_max(struct rade *r) { assert(r != NULL); return r->nin_max; }
int rade_nin(struct rade *r) { assert(r != NULL); return r->nin; }
int rade_n_features_in_out(struct rade *r) { assert(r != NULL); return r->n_features_in; }
int rade_n_eoo_features_out(struct rade *r) { assert(r != NULL); return r->n_eoo_features_out; }
int rade_n_eoo_bits(struct rade *r) { assert(r != NULL); return r->n_eoo_bits; }

RADE_EXPORT void rade_tx_set_eoo_bits(struct rade *r, float eoo_bits[]) {
assert(r != NULL);
assert(eoo_bits != NULL);
PyGILState_STATE gstate = PyGILState_Ensure();
fprintf(stderr, "n_eoo_bits: %ld\n", r->n_eoo_bits);
memcpy(r->eoo_bits, eoo_bits, sizeof(float)*r->n_eoo_bits);
PyObject_CallObject(r->pMeth_radae_set_eoo_bits, r->pArgs_radae_set_eoo_bits);
PyGILState_Release(gstate);
}

int rade_tx(struct rade *r, RADE_COMP tx_out[], float floats_in[]) {
assert(r != NULL);
Expand Down Expand Up @@ -485,7 +516,7 @@ int rade_rx(struct rade *r, float features_out[], RADE_COMP rx_in[]) {
}

if (endofover)
memcpy(features_out, r->floats_out, sizeof(float)*(r->n_eoo_features_out));
memcpy(features_out, r->floats_out, sizeof(float)*(r->n_eoo_bits));

// sample nin so we have an updated copy
r->nin = (int)call_getter(r->pInst_radae_rx, "get_nin");
Expand All @@ -496,7 +527,7 @@ int rade_rx(struct rade *r, float features_out[], RADE_COMP rx_in[]) {
if (valid_out)
return r->n_features_out;
if (endofover)
return r->n_eoo_features_out;
return r->n_eoo_bits;
return 0;
}

Expand Down
7 changes: 5 additions & 2 deletions src/rade_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,15 @@ RADE_EXPORT int rade_n_tx_out(struct rade *r);
RADE_EXPORT int rade_n_tx_eoo_out(struct rade *r);
RADE_EXPORT int rade_nin_max(struct rade *r);
RADE_EXPORT int rade_n_features_in_out(struct rade *r);
RADE_EXPORT int rade_n_eoo_features_out(struct rade *r);
RADE_EXPORT int rade_n_eoo_bits(struct rade *r);

// note vocoder is not encapsulated in API in this version
// returns number of RADE_COMP samples written to tx_out[]
RADE_EXPORT int rade_tx(struct rade *r, RADE_COMP tx_out[], float features_in[]);

// Set the rade_n_eoo_bits() bits to be sent in the EOO frame
RADE_EXPORT void rade_tx_set_eoo_bits(struct rade *r, float eoo_bits[]);

// call this for the final frame at the end of over
// returns the number of RADE_COMP samples written to tx_eoo_out[]
RADE_EXPORT int rade_tx_eoo(struct rade *r, RADE_COMP tx_eoo_out[]);
Expand All @@ -111,7 +114,7 @@ RADE_EXPORT int rade_nin(struct rade *r);

// returns non-zero if features_out[] contains valid output. The number
// returned is the number of samples written to features_out[]. If the
// number returned is equal to rade_n_eoo_features_out(), features_out[]
// number returned is equal to rade_n_eoo_bits(), features_out[]
// contains End of Over soft decision bits rather that vocoder features
RADE_EXPORT int rade_rx(struct rade *r, float features_out[], RADE_COMP rx_in[]);

Expand Down

0 comments on commit 09e9547

Please sign in to comment.