From 1687e20be28155bcb134b5dc36116147cc2d8e96 Mon Sep 17 00:00:00 2001 From: Ron Unrau Date: Mon, 30 Jan 2023 13:58:06 -0700 Subject: [PATCH 1/5] Prototype audio packet forwarder --- lib/driver/sdr_rx_fwd.c | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 lib/driver/sdr_rx_fwd.c diff --git a/lib/driver/sdr_rx_fwd.c b/lib/driver/sdr_rx_fwd.c new file mode 100644 index 0000000..d5d02c8 --- /dev/null +++ b/lib/driver/sdr_rx_fwd.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rx_fwd_ctx { + int fd; +} rx_fwd; + +static int cnt = 0; + +struct csp_hdr { + char padding[10]; + uint16_t len; + uint32_t id; + uint16_t bs; + uint16_t pktno; + uint8_t data[0]; +}; + +static void sdr_rx_fwd(void *udata, uint8_t *data, size_t len, void *unused) { + struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) udata; + + struct csp_hdr *hdr = (struct csp_hdr *) data; + printf("csp len 0x%x, id 0x%x, bs 0x%x, cnt 0x%x\n", + hdr->len, hdr->id, hdr->bs, hdr->pktno); + + int rc = write(ctx->fd, data + 20, len - 20); + if (rc < 0) { + printf("sdr_rx_fwd write error: %s", strerror(errno)); + } + else if (rc != (int) (len - 20)) { + printf("sdr_rx_fwd: short write %d<%d\n", rc, (int) (len - 20)); + } + ++cnt; + printf("fwd pkt %d\n", cnt); +} + +static int sdr_rx_fwd_open(const char *host, uint16_t port) { + struct sockaddr_in servaddr; + + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == -1) { + printf("TCP socket creation failed: %d\n", errno); + return -1; + } + memset(&servaddr, 0, sizeof(servaddr)); + + // assign IP, PORT + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(host); + servaddr.sin_port = htons(port); + + // connect the client socket to server socket + if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) != 0) { + printf("connection to port %d failed: %d\n", port, errno); + return -2; + } + + return sockfd; +} + +void sdr_rx_fwd_init(sdr_interface_data_t *ifdata) { + static int started = 0; + + if (!started) { + if ((rx_fwd.fd = sdr_rx_fwd_open("127.0.0.1", 40000)) < 0) return; + + printf("sdr_rx_fwd using fd %d\n", rx_fwd.fd); + started = 1; + } + + printf("sdr_fx_fwd_init new cb: %p\n", sdr_rx_fwd); + ifdata->sdr_conf->rx_callback = sdr_rx_fwd; + ifdata->sdr_conf->rx_callback_data = &rx_fwd; +} From a44aa2d62b120aefc82d172be35df73162229805 Mon Sep 17 00:00:00 2001 From: Ron Unrau Date: Sat, 18 Feb 2023 10:36:30 -0700 Subject: [PATCH 2/5] sdr_rx_fwd.c: decode the codec2 buffer --- lib/driver/sdr_rx_fwd.c | 172 +++++++++++++++++++++++++++++++++++----- 1 file changed, 150 insertions(+), 22 deletions(-) diff --git a/lib/driver/sdr_rx_fwd.c b/lib/driver/sdr_rx_fwd.c index d5d02c8..d6583a2 100644 --- a/lib/driver/sdr_rx_fwd.c +++ b/lib/driver/sdr_rx_fwd.c @@ -9,15 +9,25 @@ #include #include #include +#include #include +#include "codec2/codec2.h" +//#include "codec2/c2file.h" + +typedef void (*rx_done_t)(void *arg); struct rx_fwd_ctx { int fd; + int blksz; + int buflen; + int offset; + unsigned char *buf; + rx_done_t rx_done; + pthread_mutex_t wait_lock; + pthread_cond_t wait_cond; } rx_fwd; -static int cnt = 0; - -struct csp_hdr { +struct csp_ftp_hdr { char padding[10]; uint16_t len; uint32_t id; @@ -26,25 +36,35 @@ struct csp_hdr { uint8_t data[0]; }; -static void sdr_rx_fwd(void *udata, uint8_t *data, size_t len, void *unused) { - struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) udata; +static void sdr_rx_buffer(void *arg, uint8_t *data, size_t len, void *unused) { + struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; + + struct csp_ftp_hdr *hdr = (struct csp_ftp_hdr *) data; + + // len includes the CSP header, the ftp header, and the CSP checksum + int data_len = ntohs(hdr->bs); + if (ctx->blksz == 0) ctx->blksz = data_len; - struct csp_hdr *hdr = (struct csp_hdr *) data; - printf("csp len 0x%x, id 0x%x, bs 0x%x, cnt 0x%x\n", - hdr->len, hdr->id, hdr->bs, hdr->pktno); - - int rc = write(ctx->fd, data + 20, len - 20); - if (rc < 0) { - printf("sdr_rx_fwd write error: %s", strerror(errno)); + if ((ctx->offset + data_len) >= ctx->buflen) { + // We're at the end of the buffer. + data_len = ctx->buflen - ctx->offset; + printf("warning: only %d buffer bytes left\n", data_len); } - else if (rc != (int) (len - 20)) { - printf("sdr_rx_fwd: short write %d<%d\n", rc, (int) (len - 20)); + if (data_len > 0) { + memcpy(ctx->buf + ctx->offset, data + sizeof(*hdr), data_len); + ctx->offset += data_len; + } + + if (data_len < ctx->blksz) { + printf("rx_buffer is done\n"); + ctx->rx_done(arg); } - ++cnt; - printf("fwd pkt %d\n", cnt); } -static int sdr_rx_fwd_open(const char *host, uint16_t port) { +#if 0 +static +#endif +int rx_fwd_open(const char *host, uint16_t port) { struct sockaddr_in servaddr; int sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -68,17 +88,125 @@ static int sdr_rx_fwd_open(const char *host, uint16_t port) { return sockfd; } +#define BS 512 + +#if 0 +static +#endif +void rx_fwd_buffer(void *arg) { + struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; + + pthread_mutex_lock(&(ctx->wait_lock)); + pthread_cond_signal(&(ctx->wait_cond)); + pthread_mutex_unlock(&(ctx->wait_lock)); + +#if 0 + int fd = rx_fwd_open("127.0.0.1", 40000); +#else + int fd = creat("nv.wav", 0644); +#endif + if (fd < 0) return; + + int remaining = ctx->offset; + int offset = 0; + while (remaining > 0) { + int rc = write(fd, ctx->buf + offset, remaining); + if (rc < 0) { + printf("rx_fwd_buffer write error: %s", strerror(errno)); + break; + } + else if (rc == 0) { + printf("rx_fwd_buffer lost connection?\n"); + break; + } + + remaining -= rc; + offset += rc; + } + + printf("rx_fwd_buffer: wrote %d bytes\n", ctx->offset - remaining); + close(fd); +} + +#if 1 +static +#endif +void rx_decode_c2_buffer(void *arg) { + struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; + + pthread_mutex_lock(&(ctx->wait_lock)); + pthread_cond_signal(&(ctx->wait_cond)); + pthread_mutex_unlock(&(ctx->wait_lock)); + + int mode = CODEC2_MODE_2400; + struct CODEC2 *decoder = codec2_create(mode); + int nsam = codec2_samples_per_frame(decoder); + short samples[nsam]; + int bpf = codec2_bytes_per_frame(decoder); + + int fd = creat("nv.wav", 0644); + if (fd < 0) { + perror("creat"); + return; + } + + printf("samples/frame %d, bytes/frame %d\n", nsam, bpf); + for (int off=0; offoffset; off+=bpf) { + printf("%d\n", off); + fflush(stdout); + codec2_decode(decoder, samples, ctx->buf + off); + int len = write(fd, samples, nsam*2); + if (len < nsam) { + if (len < 0) { + perror("write"); + } + else { + printf("short write? %d < %d\n", len, nsam); + } + break; + } + } + close(fd); +} + void sdr_rx_fwd_init(sdr_interface_data_t *ifdata) { static int started = 0; if (!started) { - if ((rx_fwd.fd = sdr_rx_fwd_open("127.0.0.1", 40000)) < 0) return; - - printf("sdr_rx_fwd using fd %d\n", rx_fwd.fd); + rx_fwd.blksz = 0; + rx_fwd.buflen = 1024*1024; // start with 1 MByte + rx_fwd.offset = 0; + if (!(rx_fwd.buf = (unsigned char *) malloc(rx_fwd.buflen))) { + printf("sdr_rx_fwd_init: malloc failed\n"); + return; + } + + // rx_fwd.rx_done = rx_fwd_buffer; + rx_fwd.rx_done = rx_decode_c2_buffer; + + int rc = pthread_mutex_init(&rx_fwd.wait_lock, NULL); + if (rc) { + printf("sdr_rx_fwd_init: mutex_init failed %s\n", strerror(rc)); + return; + } + if ((rc = pthread_cond_init(&rx_fwd.wait_cond, NULL))) { + printf("sdr_rx_fwd_init: cond_init failed %s\n", strerror(rc)); + return; + } started = 1; } - printf("sdr_fx_fwd_init new cb: %p\n", sdr_rx_fwd); - ifdata->sdr_conf->rx_callback = sdr_rx_fwd; + sdr_conf_t sdr_conf = *(ifdata->sdr_conf); + + printf("sdr_rx_fwd_init new cb: %p\n", sdr_rx_buffer); + ifdata->sdr_conf->rx_callback = sdr_rx_buffer; ifdata->sdr_conf->rx_callback_data = &rx_fwd; + + pthread_mutex_lock(&rx_fwd.wait_lock); + pthread_cond_wait(&rx_fwd.wait_cond, &rx_fwd.wait_lock); + pthread_mutex_unlock(&rx_fwd.wait_lock); + + printf("sdr_rx_fwd_init restored cb: %p\n", sdr_conf.rx_callback); + ifdata->sdr_conf->rx_callback = sdr_conf.rx_callback; + ifdata->sdr_conf->rx_callback_data = sdr_conf.rx_callback_data; } From 8ab7b5fba0355fcf94e97746fb7748586817535a Mon Sep 17 00:00:00 2001 From: Ron Unrau Date: Sat, 18 Feb 2023 10:36:52 -0700 Subject: [PATCH 3/5] osal.h: define a bigger rx stack --- include/utilities/osal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utilities/osal.h b/include/utilities/osal.h index 7229b79..2319591 100644 --- a/include/utilities/osal.h +++ b/include/utilities/osal.h @@ -50,7 +50,7 @@ typedef os_task_return_t (*os_task_func_t)(void* parameter); #if defined(OS_POSIX) #define OS_MAX_TIMEOUT (UINT32_MAX) -#define OS_RX_TASK_STACK_SIZE 1024 +#define OS_RX_TASK_STACK_SIZE 1024*1024 #define OS_TickType long #define ex2_log printf #elif defined(OS_FREERTOS) From b297ee1809571e70702e55952bf2ed50ec4d56e0 Mon Sep 17 00:00:00 2001 From: Ron Unrau Date: Tue, 21 Feb 2023 09:19:51 -0700 Subject: [PATCH 4/5] sdr_rx_fwd.c: create and use new include file sdr_rx_fwd.c --- include/driver/sdr_rx_fwd.h | 33 +++++++++ lib/driver/sdr_rx_fwd.c | 135 +++++++++++++++--------------------- 2 files changed, 87 insertions(+), 81 deletions(-) create mode 100644 include/driver/sdr_rx_fwd.h diff --git a/include/driver/sdr_rx_fwd.h b/include/driver/sdr_rx_fwd.h new file mode 100644 index 0000000..db172f1 --- /dev/null +++ b/include/driver/sdr_rx_fwd.h @@ -0,0 +1,33 @@ +#ifndef SDR_RX_FWD_H_ +#define SDR_RX_FWD_H_ + +#include +#include + +/* This include file serves a dual purpose: to provide the sdr_rx_fwd_init() + * prototype for the C-python binding library; and to provide the internal + * forwarding API to other forwarders. + */ + +// The TCP port the forwarder connects to +#define SDR_RX_FWD_PORT 40000 + +// The forwarder is called when the transmission has been received. +typedef void (*rx_done_t)(void *arg); + +struct rx_fwd_ctx { + int blksz; + int buflen; // size of the buffer + int offset; // amount of buffer that has been used + unsigned char *buf; // holds the entire transmission + rx_done_t rx_done; + pthread_mutex_t wait_lock; + pthread_cond_t wait_cond; +}; + +void sdr_rx_fwd_init(sdr_interface_data_t *ifdata, rx_done_t rx_forwarder); + +// Called by the forwarder to unblock sdr_rx_fwd_init +void sdr_rx_signal_waiters(struct rx_fwd_ctx *ctx); + +#endif // SDR_RX_FWD_H_ diff --git a/lib/driver/sdr_rx_fwd.c b/lib/driver/sdr_rx_fwd.c index d6583a2..3b820ec 100644 --- a/lib/driver/sdr_rx_fwd.c +++ b/lib/driver/sdr_rx_fwd.c @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -9,38 +8,37 @@ #include #include #include -#include #include -#include "codec2/codec2.h" -//#include "codec2/c2file.h" - -typedef void (*rx_done_t)(void *arg); - -struct rx_fwd_ctx { - int fd; - int blksz; - int buflen; - int offset; - unsigned char *buf; - rx_done_t rx_done; - pthread_mutex_t wait_lock; - pthread_cond_t wait_cond; -} rx_fwd; +#include "sdr_rx_fwd.h" struct csp_ftp_hdr { char padding[10]; uint16_t len; - uint32_t id; + uint32_t id; // last field of the CSP hdr uint16_t bs; uint16_t pktno; uint8_t data[0]; }; +// The Northern Voices CSP destination port +#define NV_DPORT 24 + static void sdr_rx_buffer(void *arg, uint8_t *data, size_t len, void *unused) { struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; struct csp_ftp_hdr *hdr = (struct csp_ftp_hdr *) data; + /* We only care want to receive Northern Voices broadcasts. Therefore, we + * discard all CSP packets not aimed at NV_DPORT. + */ + int id = ntohl(hdr->id); + if (((id >> 14) & 0x3f) != NV_DPORT) { + /* The dport is a 6-bit field 3rd from the right, after sport (6 bits) + * and flags (8 bits). + */ + return; + } + // len includes the CSP header, the ftp header, and the CSP checksum int data_len = ntohs(hdr->bs); if (ctx->blksz == 0) ctx->blksz = data_len; @@ -50,21 +48,24 @@ static void sdr_rx_buffer(void *arg, uint8_t *data, size_t len, void *unused) { data_len = ctx->buflen - ctx->offset; printf("warning: only %d buffer bytes left\n", data_len); } + if (data_len > 0) { - memcpy(ctx->buf + ctx->offset, data + sizeof(*hdr), data_len); + memcpy(ctx->buf + ctx->offset, hdr->data, data_len); ctx->offset += data_len; } + /* We don't know the total size of the download, all we know is that the + * transmitter sends uniform ctx->blksz chunks until the last block, which + * is however big it needs to be to hold the remaining data. + * We are fervently hoping that the last bs is 0 if the size of the file + * evenly divides ctx->blksz. + */ if (data_len < ctx->blksz) { - printf("rx_buffer is done\n"); ctx->rx_done(arg); } } -#if 0 -static -#endif -int rx_fwd_open(const char *host, uint16_t port) { +static int rx_fwd_open(const char *host, uint16_t port) { struct sockaddr_in servaddr; int sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -88,23 +89,20 @@ int rx_fwd_open(const char *host, uint16_t port) { return sockfd; } -#define BS 512 +void sdr_rx_signal_waiters(struct rx_fwd_ctx *ctx) { + pthread_mutex_lock(&(ctx->wait_lock)); + pthread_cond_signal(&(ctx->wait_cond)); + pthread_mutex_unlock(&(ctx->wait_lock)); +} -#if 0 -static -#endif void rx_fwd_buffer(void *arg) { struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; - pthread_mutex_lock(&(ctx->wait_lock)); - pthread_cond_signal(&(ctx->wait_cond)); - pthread_mutex_unlock(&(ctx->wait_lock)); + // We have all the data so the caller can restore the receiver cal'backs + sdr_rx_signal_waiters(ctx); -#if 0 - int fd = rx_fwd_open("127.0.0.1", 40000); -#else - int fd = creat("nv.wav", 0644); -#endif + // Always connect to localhost for now + int fd = rx_fwd_open("127.0.0.1", SDR_RX_FWD_PORT); if (fd < 0) return; int remaining = ctx->offset; @@ -128,51 +126,17 @@ void rx_fwd_buffer(void *arg) { close(fd); } -#if 1 -static -#endif -void rx_decode_c2_buffer(void *arg) { - struct rx_fwd_ctx *ctx = (struct rx_fwd_ctx *) arg; +struct rx_fwd_ctx rx_fwd; - pthread_mutex_lock(&(ctx->wait_lock)); - pthread_cond_signal(&(ctx->wait_cond)); - pthread_mutex_unlock(&(ctx->wait_lock)); - - int mode = CODEC2_MODE_2400; - struct CODEC2 *decoder = codec2_create(mode); - int nsam = codec2_samples_per_frame(decoder); - short samples[nsam]; - int bpf = codec2_bytes_per_frame(decoder); - - int fd = creat("nv.wav", 0644); - if (fd < 0) { - perror("creat"); - return; - } - - printf("samples/frame %d, bytes/frame %d\n", nsam, bpf); - for (int off=0; offoffset; off+=bpf) { - printf("%d\n", off); - fflush(stdout); - codec2_decode(decoder, samples, ctx->buf + off); - int len = write(fd, samples, nsam*2); - if (len < nsam) { - if (len < 0) { - perror("write"); - } - else { - printf("short write? %d < %d\n", len, nsam); - } - break; - } - } - close(fd); -} - -void sdr_rx_fwd_init(sdr_interface_data_t *ifdata) { +void sdr_rx_fwd_init(sdr_interface_data_t *ifdata, rx_done_t forwarder) { static int started = 0; if (!started) { + /* Because the satellite UHF transmission rate is so low (~2400BAUD) + * we buffer the entire file before forwarding it on. A 1MB buffer + * should be big enough for most files, also because the bandwidth + * is so low. + */ rx_fwd.blksz = 0; rx_fwd.buflen = 1024*1024; // start with 1 MByte rx_fwd.offset = 0; @@ -181,8 +145,13 @@ void sdr_rx_fwd_init(sdr_interface_data_t *ifdata) { return; } - // rx_fwd.rx_done = rx_fwd_buffer; - rx_fwd.rx_done = rx_decode_c2_buffer; + /* Allow the caller to provide alternative forwarders */ + if (forwarder) { + rx_fwd.rx_done = forwarder; + } + else { + rx_fwd.rx_done = rx_fwd_buffer; + } int rc = pthread_mutex_init(&rx_fwd.wait_lock, NULL); if (rc) { @@ -196,17 +165,21 @@ void sdr_rx_fwd_init(sdr_interface_data_t *ifdata) { started = 1; } + // Remember the current settings so they can be restored later sdr_conf_t sdr_conf = *(ifdata->sdr_conf); - printf("sdr_rx_fwd_init new cb: %p\n", sdr_rx_buffer); + /* These instructions change the RX callback. I know they should be set + * atomically, but this is really only for the test harness. When we + * release the customer version we won't be switching receivers. + */ ifdata->sdr_conf->rx_callback = sdr_rx_buffer; ifdata->sdr_conf->rx_callback_data = &rx_fwd; + // The condition blocks this thread until the transmission is received. pthread_mutex_lock(&rx_fwd.wait_lock); pthread_cond_wait(&rx_fwd.wait_cond, &rx_fwd.wait_lock); pthread_mutex_unlock(&rx_fwd.wait_lock); - printf("sdr_rx_fwd_init restored cb: %p\n", sdr_conf.rx_callback); ifdata->sdr_conf->rx_callback = sdr_conf.rx_callback; ifdata->sdr_conf->rx_callback_data = sdr_conf.rx_callback_data; } From c73aa297a88bf7a4aaabca7951d9dd4a97a9d9c2 Mon Sep 17 00:00:00 2001 From: Ron Unrau Date: Tue, 21 Feb 2023 09:21:08 -0700 Subject: [PATCH 5/5] osal.h: USe the default posix stack size --- include/utilities/osal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utilities/osal.h b/include/utilities/osal.h index 2319591..bea76e8 100644 --- a/include/utilities/osal.h +++ b/include/utilities/osal.h @@ -50,7 +50,7 @@ typedef os_task_return_t (*os_task_func_t)(void* parameter); #if defined(OS_POSIX) #define OS_MAX_TIMEOUT (UINT32_MAX) -#define OS_RX_TASK_STACK_SIZE 1024*1024 +#define OS_RX_TASK_STACK_SIZE 0 #define OS_TickType long #define ex2_log printf #elif defined(OS_FREERTOS)