From 735d7882764134f6982586683a42ed79f5e95951 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Wed, 3 Apr 2024 17:16:58 +1030 Subject: WIP freedv API support for custom raw data modes --- src/freedv_700.c | 31 +++++++++++++++++++------------ src/freedv_api.c | 27 ++++++++++++++++++--------- src/freedv_api.h | 7 +++++++ src/freedv_api_internal.h | 2 +- 4 files changed, 45 insertions(+), 22 deletions(-) diff --git a/src/freedv_700.c b/src/freedv_700.c index 7c8536c..a7d7aac 100644 --- a/src/freedv_700.c +++ b/src/freedv_700.c @@ -189,7 +189,7 @@ void freedv_ofdm_voice_open(struct freedv *f, char *mode) { // open function for OFDM data modes, TODO consider moving to a new // (freedv_ofdm_data.c) file -void freedv_ofdm_data_open(struct freedv *f) { +void freedv_ofdm_data_open(struct freedv *f, struct freedv_advanced *adv) { struct OFDM_CONFIG ofdm_config; char mode[32]; if (f->mode == FREEDV_MODE_DATAC0) strcpy(mode, "datac0"); @@ -198,8 +198,14 @@ void freedv_ofdm_data_open(struct freedv *f) { if (f->mode == FREEDV_MODE_DATAC4) strcpy(mode, "datac4"); if (f->mode == FREEDV_MODE_DATAC13) strcpy(mode, "datac13"); if (f->mode == FREEDV_MODE_DATAC14) strcpy(mode, "datac14"); - - ofdm_init_mode(mode, &ofdm_config); + if (f->mode == FREEDV_MODE_DATA_CUSTOM) { + assert(adv != NULL); + assert(adv->config != NULL); + memcpy(&ofdm_config, (struct OFDM_CONFIG *)adv->config, + sizeof(struct OFDM_CONFIG)); + } else { + ofdm_init_mode(mode, &ofdm_config); + } f->ofdm = ofdm_create(&ofdm_config); assert(f->ofdm != NULL); @@ -219,8 +225,8 @@ void freedv_ofdm_data_open(struct freedv *f) { f->ofdm_ntxtbits = ofdm_config.txtbits; /* payload bits per FreeDV API "frame". In OFDM modem nomenclature this is - the number of payload data bits per packet, or the number of data bits in a - LDPC codeword */ + the number of payload data bits per packet, or the number of data bits in + a LDPC codeword */ f->bits_per_modem_frame = f->ldpc->data_bits_per_frame; // buffers for received symbols for one packet/LDPC codeword - may span many @@ -299,8 +305,8 @@ int freedv_comprx_700c(struct freedv *f, COMP demod_in_8kHz[]) { int rx_status = 0; - // quisk_cfInterpDecim() modifies input data so lets make a copy just in case - // there is no sync and we need to echo input to output + // quisk_cfInterpDecim() modifies input data so lets make a copy just in + // case there is no sync and we need to echo input to output // freedv_nin(f): input samples at Fs=8000 Hz // f->nin: input samples at Fs=7500 Hz @@ -332,7 +338,8 @@ int freedv_comprx_700c(struct freedv *f, COMP demod_in_8kHz[]) { rx_status |= FREEDV_RX_BITS; } else { if (f->test_frames_diversity) { - /* normal operation - error pattern on frame after diveristy combination + /* normal operation - error pattern on frame after diveristy + * combination */ short error_pattern[COHPSK_BITS_PER_FRAME]; int bit_errors; @@ -471,8 +478,8 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, memcpy(&rx_amps[Nsymsperpacket - Nsymsperframe], ofdm->rx_amp, sizeof(float) * Nsymsperframe); - /* look for UW as frames enter packet buffer, note UW may span several modem - * frames */ + /* look for UW as frames enter packet buffer, note UW may span several + * modem frames */ int st_uw = Nsymsperpacket - ofdm->nuwframes * Nsymsperframe; ofdm_extract_uw(ofdm, &rx_syms[st_uw], &rx_amps[st_uw], rx_uw); @@ -510,8 +517,8 @@ int freedv_comp_short_rx_ofdm(struct freedv *f, void *demod_in_8kHz, else rx_status |= FREEDV_RX_BIT_ERRORS; } else { - // voice modes aren't as strict - pass everything through to the speech - // decoder, but flag frame with possible errors + // voice modes aren't as strict - pass everything through to the + // speech decoder, but flag frame with possible errors rx_status |= FREEDV_RX_BITS; if (parityCheckCount != ldpc->NumberParityBits) rx_status |= FREEDV_RX_BIT_ERRORS; diff --git a/src/freedv_api.c b/src/freedv_api.c index 3ccf63d..a158d2a 100644 --- a/src/freedv_api.c +++ b/src/freedv_api.c @@ -60,7 +60,7 @@ /* The API version number. The first version is 10. Increment if the API changes in a way that would require changes by the API user. */ -#define VERSION 15 +#define VERSION 16 /* Version 10 Initial version August 2, 2015. @@ -79,6 +79,9 @@ Version 15 December 2022 Removing rarely used DPSK support which is not needed given fast fading modes + + Version 16 April 2024, added field to struct freedv_advanced to support + FREEDV_MODE_DATA_CUSTOM */ char *ofdm_statemode[] = {"search", "trial", "synced"}; @@ -101,7 +104,8 @@ char *rx_sync_flags_to_text[] = {"----", "---T", "--S-", "--ST", "-B--", "-B-T", struct freedv *freedv_open(int mode) { // defaults for those modes that support the use of adv - struct freedv_advanced adv = {0, 2, 100, 8000, 1000, 200, "H_256_512_4"}; + struct freedv_advanced adv = {0, 2, 100, 8000, + 1000, 200, "H_256_512_4", NULL}; return freedv_open_advanced(mode, &adv); } @@ -126,7 +130,8 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) == false) + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, mode)) == false) return NULL; /* set everything to zero just in case */ @@ -150,12 +155,16 @@ struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { if (FDV_MODE_ACTIVE(FREEDV_MODE_2400B, mode)) freedv_2400b_open(f); if (FDV_MODE_ACTIVE(FREEDV_MODE_800XA, mode)) freedv_800xa_open(f); if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, mode)) freedv_fsk_ldpc_open(f, adv); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, mode)) freedv_ofdm_data_open(f); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, mode)) freedv_ofdm_data_open(f); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode)) freedv_ofdm_data_open(f); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode)) freedv_ofdm_data_open(f); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode)) freedv_ofdm_data_open(f); - if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) freedv_ofdm_data_open(f); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC0, mode)) freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC1, mode)) freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, mode)) freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, mode)) freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, mode)) + freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, mode)) + freedv_ofdm_data_open(f, NULL); + if (FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, mode)) + freedv_ofdm_data_open(f, adv); varicode_decode_init(&f->varicode_dec_states, 1); diff --git a/src/freedv_api.h b/src/freedv_api.h index e4ca468..9f72c8f 100644 --- a/src/freedv_api.h +++ b/src/freedv_api.h @@ -62,6 +62,7 @@ extern "C" { #define FREEDV_MODE_DATAC4 18 #define FREEDV_MODE_DATAC13 19 #define FREEDV_MODE_DATAC14 20 +#define FREEDV_MODE_DATA_CUSTOM 21 // Sample rates used #define FREEDV_FS_8000 8000 @@ -144,6 +145,9 @@ extern "C" { #if !defined(FREEDV_MODE_DATAC14_EN) #define FREEDV_MODE_DATAC14_EN FREEDV_MODE_EN_DEFAULT #endif +#if !defined(FREEDV_MODE_DATA_CUSTOM_EN) +#define FREEDV_MODE_DATA_CUSTOM_EN FREEDV_MODE_EN_DEFAULT +#endif #define FDV_MODE_ACTIVE(mode_name, var) \ ((mode_name##_EN) == 0 ? 0 : (var) == mode_name) @@ -163,6 +167,9 @@ struct freedv_advanced { int first_tone; // Freq of first tone Hz int tone_spacing; // Spacing between tones Hz char *codename; // LDPC codename, from codes listed in ldpc_codes.c + + // parameters for FREEDV_MODE_DATA_CUSTOM + void *config; // ptr to struct OFDM_CONFIG }; // Called when text message char is decoded diff --git a/src/freedv_api_internal.h b/src/freedv_api_internal.h index ef6426f..9559a2d 100644 --- a/src/freedv_api_internal.h +++ b/src/freedv_api_internal.h @@ -221,7 +221,7 @@ void freedv_2400a_open(struct freedv *f); void freedv_2400b_open(struct freedv *f); void freedv_800xa_open(struct freedv *f); void freedv_fsk_ldpc_open(struct freedv *f, struct freedv_advanced *adv); -void freedv_ofdm_data_open(struct freedv *f); +void freedv_ofdm_data_open(struct freedv *f, struct freedv_advanced *adv); // each mode has tx and rx functions in various flavours for real and complex // valued samples -- cgit v1.2.3 From cf069d2e62bd1e69efaddcfa60ca37374ed025b5 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Wed, 3 Apr 2024 18:00:35 +1030 Subject: demo custom mode --- src/freedv_api.c | 15 ++++++++++----- src/freedv_data_raw_rx.c | 24 ++++++++++++++++++++++-- src/freedv_data_raw_tx.c | 26 +++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/freedv_api.c b/src/freedv_api.c index a158d2a..d04dd7c 100644 --- a/src/freedv_api.c +++ b/src/freedv_api.c @@ -247,7 +247,8 @@ void freedv_close(struct freedv *freedv) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, freedv->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, freedv->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, freedv->mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, freedv->mode)) { + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, freedv->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, freedv->mode)) { FREE(freedv->rx_syms); FREE(freedv->rx_amps); FREE(freedv->ldpc); @@ -279,7 +280,8 @@ static int is_ofdm_mode(struct freedv *f) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode); + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode); } static int is_ofdm_data_mode(struct freedv *f) { @@ -288,7 +290,8 @@ static int is_ofdm_data_mode(struct freedv *f) { FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode); + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode); } /*---------------------------------------------------------------------------*\ @@ -479,7 +482,8 @@ void freedv_rawdatacomptx(struct freedv *f, COMP mod_out[], FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode)) + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode)) freedv_comptx_ofdm(f, mod_out); if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) { @@ -1079,7 +1083,8 @@ int freedv_rawdatacomprx(struct freedv *f, unsigned char *packed_payload_bits, FDV_MODE_ACTIVE(FREEDV_MODE_DATAC3, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC4, f->mode) || FDV_MODE_ACTIVE(FREEDV_MODE_DATAC13, f->mode) || - FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode)) + FDV_MODE_ACTIVE(FREEDV_MODE_DATAC14, f->mode) || + FDV_MODE_ACTIVE(FREEDV_MODE_DATA_CUSTOM, f->mode)) rx_status = freedv_comp_short_rx_ofdm(f, (void *)demod_in, 0, 1.0f); if (FDV_MODE_ACTIVE(FREEDV_MODE_FSK_LDPC, f->mode)) { rx_status = freedv_rx_fsk_ldpc_data(f, demod_in); diff --git a/src/freedv_data_raw_rx.c b/src/freedv_data_raw_rx.c index 0353ae5..435648d 100644 --- a/src/freedv_data_raw_rx.c +++ b/src/freedv_data_raw_rx.c @@ -39,6 +39,7 @@ #include "ldpc_codes.h" #include "modem_stats.h" #include "octave.h" +#include "ofdm_internal.h" /* other processes can end this program using signals */ @@ -216,6 +217,8 @@ int main(int argc, char *argv[]) { mode = FREEDV_MODE_DATAC13; if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14")) mode = FREEDV_MODE_DATAC14; + if (!strcmp(argv[dx], "CUSTOM") || !strcmp(argv[dx], "custom")) + mode = FREEDV_MODE_DATA_CUSTOM; if (mode == -1) { fprintf(stderr, "Error in mode: %s\n", argv[dx]); exit(1); @@ -248,6 +251,23 @@ int main(int argc, char *argv[]) { fprintf(stderr, "Setting estimator limits to %d to %d Hz.\n", fsk_lower, fsk_upper); fsk_set_freq_est_limits(fsk, fsk_lower, fsk_upper); + } else if (mode == FREEDV_MODE_DATA_CUSTOM) { + // demonstrate custom OFDM raw data modes + struct OFDM_CONFIG ofdm_config; + ofdm_init_mode("datac14", &ofdm_config); + // modify datac14 to have 3 carriers instead of 4, which means + // we have to tweak Np, and the number of unique word bits + ofdm_config.nc = 3; + ofdm_config.np = 6; + ofdm_config.nuwbits = 48; + ofdm_config.bad_uw_errors = 18; + uint8_t uw[] = {1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}; + memcpy(ofdm_config.tx_uw, uw, sizeof(uw)); + memcpy(&ofdm_config.tx_uw[ofdm_config.nuwbits - sizeof(uw)], uw, + sizeof(uw)); + adv.config = (void *)&ofdm_config; + freedv = freedv_open_advanced(mode, &adv); } else { freedv = freedv_open(mode); } @@ -264,8 +284,8 @@ int main(int argc, char *argv[]) { fsk->Ndft); } - /* for streaming bytes it's much easier use the modes that have a multiple of - * 8 payload bits/frame */ + /* for streaming bytes it's much easier use the modes that have a multiple + * of 8 payload bits/frame */ assert((freedv_get_bits_per_modem_frame(freedv) % 8) == 0); int bytes_per_modem_frame = freedv_get_bits_per_modem_frame(freedv) / 8; // last two bytes used for CRC diff --git a/src/freedv_data_raw_tx.c b/src/freedv_data_raw_tx.c index cb74107..ce5da56 100644 --- a/src/freedv_data_raw_tx.c +++ b/src/freedv_data_raw_tx.c @@ -238,6 +238,8 @@ int main(int argc, char *argv[]) { mode = FREEDV_MODE_DATAC13; if (!strcmp(argv[dx], "DATAC14") || !strcmp(argv[dx], "datac14")) mode = FREEDV_MODE_DATAC14; + if (!strcmp(argv[dx], "CUSTOM") || !strcmp(argv[dx], "custom")) + mode = FREEDV_MODE_DATA_CUSTOM; if (mode == -1) { fprintf(stderr, "Error: in mode: %s", argv[dx]); exit(1); @@ -259,10 +261,28 @@ int main(int argc, char *argv[]) { exit(1); } - if (mode != FREEDV_MODE_FSK_LDPC) - freedv = freedv_open(mode); - else + if (mode == FREEDV_MODE_FSK_LDPC) + freedv = freedv_open_advanced(mode, &adv); + else if (mode == FREEDV_MODE_DATA_CUSTOM) { + // demonstrate custom OFDM raw data modes + struct OFDM_CONFIG ofdm_config; + ofdm_init_mode("datac14", &ofdm_config); + // modify datac14 to have 3 carriers instead of 4, which means + // we have to tweak Np, and the number of unique word bits + ofdm_config.nc = 3; + ofdm_config.np = 6; + ofdm_config.nuwbits = 48; + ofdm_config.bad_uw_errors = 18; + uint8_t uw[] = {1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}; + memcpy(ofdm_config.tx_uw, uw, sizeof(uw)); + memcpy(&ofdm_config.tx_uw[ofdm_config.nuwbits - sizeof(uw)], uw, + sizeof(uw)); + adv.config = (void *)&ofdm_config; freedv = freedv_open_advanced(mode, &adv); + } else { + freedv = freedv_open(mode); + } assert(freedv != NULL); -- cgit v1.2.3 From 1de1b46b1279a13517bcbc3da19d7e1effe6ecd5 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Wed, 3 Apr 2024 18:25:40 +1030 Subject: ctest for custom mode --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa518d8..e62c58a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1291,6 +1291,12 @@ endif(NOT APPLE) ./freedv_data_raw_rx --framesperburst 2 --testframes DATAC0 - /dev/null --vv") set_tests_properties(test_freedv_data_raw_ofdm_datac0_burst PROPERTIES PASS_REGULAR_EXPRESSION "Coded FER: 0.0000 Tfrms: 6 Tfers: 0") + add_test(NAME test_freedv_data_raw_ofdm_data_custom + COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; + ./freedv_data_raw_tx --bursts 3 --testframes 3 custom /dev/zero - | + ./freedv_data_raw_rx --testframes custom - /dev/null --vv") + set_tests_properties(test_freedv_data_raw_ofdm_data_custom PROPERTIES PASS_REGULAR_EXPRESSION "Coded FER: 0.0000 Tfrms: 3 Tfers: 0") + # Burst mode with data file I/O: add_test(NAME test_freedv_data_raw_ofdm_datac0_burst_file COMMAND sh -c "cd ${CMAKE_CURRENT_BINARY_DIR}/src; -- cgit v1.2.3 From 2841e6c50982d559831a21cccddd74a69e2798f0 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Fri, 5 Apr 2024 05:06:39 +1030 Subject: non-table driven interleaver b --- octave/gp_interleaver.m | 29 +++++++++++++++++++++-------- src/gp_interleaver.c | 43 +++++++++++++++---------------------------- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/octave/gp_interleaver.m b/octave/gp_interleaver.m index 1ee0ee3..c97af0f 100644 --- a/octave/gp_interleaver.m +++ b/octave/gp_interleaver.m @@ -7,18 +7,31 @@ 1; +% return 1 if prime +function ret = is_prime(x) + for i=2:x-1 + if mod(x,i) == 0 + ret = 0; + return; + end + end + ret = 1; +end + +function x = next_prime(x) + x++; + while is_prime(x) == 0 + x++; + end +end + % Choose b for Golden Prime Interleaver. b is chosen to be the % closest integer, which is relatively prime to N, to the Golden % section of N. function b = choose_interleaver_b(Nbits) - - p = primes(Nbits); - i = 1; - while(p(i) < Nbits/1.62) - i++; - end - b = p(i); + b = floor(Nbits/1.62); + b = next_prime(b); assert(gcd(b,Nbits) == 1, "b and Nbits must be co-prime"); end @@ -36,7 +49,7 @@ endfunction function frame = gp_deinterleave(interleaved_frame) Nbits = length(interleaved_frame); - b = choose_interleaver_b(Nbits); + b = choose_interleaver_b(Nbits); frame = zeros(1,Nbits); for i=1:Nbits j = mod((b*(i-1)), Nbits); diff --git a/src/gp_interleaver.c b/src/gp_interleaver.c index 6710b0c..70b8ad0 100644 --- a/src/gp_interleaver.c +++ b/src/gp_interleaver.c @@ -31,45 +31,32 @@ #include "gp_interleaver.h" #include +#include #include /* Choose b for Golden Prime Interleaver. b is chosen to be the closest integer, which is relatively prime to N, to the Golden section of N. - - Implemented with a LUT in C for convenience, Octave version - has a more complete implementation. If you find you need some more - numbers head back to the Octave choose_interleaver_b() function. */ -static const int b_table[] = { - 48, 31, /* datac14: HRA_56_56, 40 data bits used */ - 56, 37, /* 700E: HRA_56_56 */ - 106, 67, /* 2020B: (112,56) partial protection */ - 112, 71, /* 700D: HRA_112_112 */ - 128, 83, /* datac0: H_128_256_5 */ - 192, 127, /* datac13: H_256_512_4, 128 data bits used */ - 210, 131, /* 2020: HRAb_396_504 with 312 data bits used */ - 736, 457, /* datac4: H_1024_2048_4f, 448 data bits used */ - 1024, 641, /* datac3: H_1024_2048_4f */ - 1290, 797, /* datac2: H2064_516_sparse */ - 4096, 2531 /* datac1: H_4096_8192_3d */ -}; - -int choose_interleaver_b(int Nbits) { - int i; - for (i = 0; i < sizeof(b_table) / sizeof(int); i += 2) { - if (b_table[i] == Nbits) { - return b_table[i + 1]; - } +int is_prime(int x) { + for (int i = 2; i < x; i++) { + if ((x % i) == 0) return 0; } + return 1; +} - /* if we get to here it means a Nbits we don't have in our table so choke */ +int next_prime(int x) { + x++; + while (is_prime(x) == 0) x++; + return x; +} - fprintf(stderr, "gp_interleaver: Nbits: %d, b not found!\n", Nbits); - assert(0); - return -1; +int choose_interleaver_b(int Nbits) { + int b = floor(Nbits / 1.62); + b = next_prime(b); + return b; } void gp_interleave_comp(COMP interleaved_frame[], COMP frame[], int Nbits) { -- cgit v1.2.3 From d026cfede7053c7f38bfed6f5089b403e961a45a Mon Sep 17 00:00:00 2001 From: drowe67 Date: Fri, 3 May 2024 05:23:55 +0930 Subject: automatically compute the number of used data bits in a codeword from other waveform parameters --- src/interldpc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/interldpc.c b/src/interldpc.c index 8cec647..b94f711 100644 --- a/src/interldpc.c +++ b/src/interldpc.c @@ -76,9 +76,11 @@ void ldpc_mode_specific_setup(struct OFDM *ofdm, struct LDPC *ldpc) { set_data_bits_per_frame(ldpc, 156); ldpc->protection_mode = LDPC_PROT_2020B; } - if (!strcmp(ofdm->mode, "datac4")) set_data_bits_per_frame(ldpc, 448); - if (!strcmp(ofdm->mode, "datac13")) set_data_bits_per_frame(ldpc, 128); - if (!strcmp(ofdm->mode, "datac14")) set_data_bits_per_frame(ldpc, 40); + + /* compute the number of data bits used in the codeword */ + int data_bits_per_frame = ofdm->bitsperpacket - ofdm->nuwbits - + ofdm->ntxtbits - ldpc->NumberParityBits; + set_data_bits_per_frame(ldpc, data_bits_per_frame); } /* LDPC encode frame - generate parity bits and a codeword, applying the -- cgit v1.2.3 From cfa9cd02e44652cdf625cbed729bd15905045523 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Fri, 3 May 2024 10:10:49 +0930 Subject: first pass a config driven Tx BPF set up --- src/ofdm.c | 51 +++++++++++++++++++++------------------------------ src/ofdm_internal.h | 22 +++++++++++++--------- src/ofdm_mode.c | 22 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/ofdm.c b/src/ofdm.c index ba22f3f..8d7a6df 100644 --- a/src/ofdm.c +++ b/src/ofdm.c @@ -192,6 +192,8 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->codename = "HRA_112_112"; ofdm->amp_est_mode = 0; ofdm->tx_bpf_en = true; + ofdm->tx_bpf_proto = filtP650S900; + ofdm->tx_bpf_proto_n = sizeof(filtP650S900) / sizeof(float); ofdm->rx_bpf_en = false; ofdm->amp_scale = 245E3; ofdm->clip_gain1 = 2.0; @@ -226,6 +228,8 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->codename = config->codename; ofdm->amp_est_mode = config->amp_est_mode; ofdm->tx_bpf_en = config->tx_bpf_en; + ofdm->tx_bpf_proto = config->tx_bpf_proto; + ofdm->tx_bpf_proto_n = config->tx_bpf_proto_n; ofdm->rx_bpf_en = config->rx_bpf_en; ofdm->foff_limiter = config->foff_limiter; ofdm->amp_scale = config->amp_scale; @@ -275,6 +279,8 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { ofdm->config.codename = ofdm->codename; ofdm->config.amp_est_mode = ofdm->amp_est_mode; ofdm->config.tx_bpf_en = ofdm->tx_bpf_en; + ofdm->config.tx_bpf_proto = ofdm->tx_bpf_proto; + ofdm->config.tx_bpf_proto_n = ofdm->tx_bpf_proto_n; ofdm->config.rx_bpf_en = ofdm->rx_bpf_en; ofdm->config.foff_limiter = ofdm->foff_limiter; ofdm->config.amp_scale = ofdm->amp_scale; @@ -539,36 +545,19 @@ struct OFDM *ofdm_create(const struct OFDM_CONFIG *config) { static void allocate_tx_bpf(struct OFDM *ofdm) { ofdm->tx_bpf = MALLOC(sizeof(struct quisk_cfFilter)); assert(ofdm->tx_bpf != NULL); + assert(ofdm->tx_bpf_proto != NULL); + assert(ofdm->tx_bpf_proto_n != 0); - /* Transmit bandpass filter; complex coefficients, center frequency */ - - if (!strcmp(ofdm->mode, "700D")) { - quisk_filt_cfInit(ofdm->tx_bpf, filtP650S900, - sizeof(filtP650S900) / sizeof(float)); - quisk_cfTune(ofdm->tx_bpf, ofdm->tx_centre / ofdm->fs); - } else if (!strcmp(ofdm->mode, "700E") || !strcmp(ofdm->mode, "2020") || - !strcmp(ofdm->mode, "datac1")) { - quisk_filt_cfInit(ofdm->tx_bpf, filtP900S1100, - sizeof(filtP900S1100) / sizeof(float)); - quisk_cfTune(ofdm->tx_bpf, ofdm->tx_centre / ofdm->fs); - } else if (!strcmp(ofdm->mode, "2020B")) { - quisk_filt_cfInit(ofdm->tx_bpf, filtP1100S1300, - sizeof(filtP1100S1300) / sizeof(float)); - quisk_cfTune(ofdm->tx_bpf, ofdm->tx_centre / ofdm->fs); - } else if (!strcmp(ofdm->mode, "datac0") || !strcmp(ofdm->mode, "datac3")) { - quisk_filt_cfInit(ofdm->tx_bpf, filtP400S600, - sizeof(filtP400S600) / sizeof(float)); - quisk_cfTune(ofdm->tx_bpf, ofdm->tx_centre / ofdm->fs); - } else if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13") || - !strcmp(ofdm->mode, "datac14")) { - quisk_filt_cfInit(ofdm->tx_bpf, filtP200S400, - sizeof(filtP200S400) / sizeof(float)); - // centre the filter on the mean carrier freq, allows a narrower filter to - // be used - float tx_centre = find_carrier_centre(ofdm); - quisk_cfTune(ofdm->tx_bpf, tx_centre / ofdm->fs); - } else - assert(0); + quisk_filt_cfInit(ofdm->tx_bpf, ofdm->tx_bpf_proto, ofdm->tx_bpf_proto_n); + float tx_centre = ofdm->tx_centre; + if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13") || + !strcmp(ofdm->mode, "datac14")) { + // Centre the filter on the mean carrier freq, allows a narrower + // filter to be used. Only really useful for very narrow, Nc odd + // waveforms. TODO: make this feature config controlled + tx_centre = find_carrier_centre(ofdm); + } + quisk_cfTune(ofdm->tx_bpf, tx_centre / ofdm->fs); } static void deallocate_tx_bpf(struct OFDM *ofdm) { @@ -589,7 +578,9 @@ static void allocate_rx_bpf(struct OFDM *ofdm) { ofdm->rx_bpf = MALLOC(sizeof(struct quisk_cfFilter)); assert(ofdm->rx_bpf != NULL); - /* Receive bandpass filter; complex coefficients, center frequency */ + /* Receive bandpass filter; complex coefficients, center frequency, only + really needed for very low SNR waveforms. TODO: make this config + controlled. */ if (!strcmp(ofdm->mode, "datac4") || !strcmp(ofdm->mode, "datac13") || !strcmp(ofdm->mode, "datac14")) { diff --git a/src/ofdm_internal.h b/src/ofdm_internal.h index 8007934..3cb9130 100644 --- a/src/ofdm_internal.h +++ b/src/ofdm_internal.h @@ -94,15 +94,17 @@ struct OFDM_CONFIG { char *codename; /* name of LDPC code used with this mode */ uint8_t tx_uw[MAX_UW_BITS]; /* user defined unique word */ int amp_est_mode; - bool tx_bpf_en; /* default tx (mod) hilbert clipper BPF enable */ - bool rx_bpf_en; /* default rx (demod) input BPF enable */ - bool foff_limiter; /* tames freq offset updates in low SNR */ - float amp_scale; /* used to scale Tx waveform to approx FREEDV_PEAK with - clipper off */ - float clip_gain1; /* gain we apply to Tx signal before clipping to control - PAPR*/ - float clip_gain2; /* gain we apply to Tx signal after clipping and BBF to - control peak level */ + bool tx_bpf_en; /* default tx (mod) hilbert clipper BPF enable */ + bool rx_bpf_en; /* default rx (demod) input BPF enable */ + float *tx_bpf_proto; /* low pass prototype for complex BPF */ + int tx_bpf_proto_n; /* number of taps in low pass prototype */ + bool foff_limiter; /* tames freq offset updates in low SNR */ + float amp_scale; /* used to scale Tx waveform to approx FREEDV_PEAK with + clipper off */ + float clip_gain1; /* gain we apply to Tx signal before clipping to control + PAPR*/ + float clip_gain2; /* gain we apply to Tx signal after clipping and BBF to + control peak level */ bool clip_en; char mode[16]; /* OFDM mode in string form */ char *data_mode; @@ -156,6 +158,7 @@ struct OFDM { int packetsperburst; /* for OFDM data modes, how many packets before we reset state machine */ int amp_est_mode; /* amplitude estimtor algorithm */ + int tx_bpf_proto_n; /* number of taps in low pass prototype */ float amp_scale; float clip_gain1; float clip_gain2; @@ -220,6 +223,7 @@ struct OFDM { float coarse_foff_est_hz; float timing_norm; float mean_amp; + float *tx_bpf_proto; // Integer int clock_offset_counter; diff --git a/src/ofdm_mode.c b/src/ofdm_mode.c index df0b4f5..b6c8c36 100644 --- a/src/ofdm_mode.c +++ b/src/ofdm_mode.c @@ -46,6 +46,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->clip_gain2 = 0.8; config->clip_en = true; config->tx_bpf_en = true; + config->tx_bpf_proto = filtP650S900; + config->tx_bpf_proto_n = sizeof(filtP650S900) / sizeof(float); config->rx_bpf_en = false; config->amp_scale = 245E3; config->foff_limiter = false; @@ -69,6 +71,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->amp_scale = 155E3; config->clip_gain1 = 3; config->clip_gain2 = 0.8; + config->tx_bpf_proto = filtP900S1100; + config->tx_bpf_proto_n = sizeof(filtP900S1100) / sizeof(float); } else if ((strcmp(mode, "2020") == 0)) { config->ts = 0.0205; config->nc = 31; @@ -76,6 +80,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->amp_scale = 167E3; config->clip_gain1 = 2.5; config->clip_gain2 = 0.8; + config->tx_bpf_proto = filtP900S1100; + config->tx_bpf_proto_n = sizeof(filtP900S1100) / sizeof(float); } else if (strcmp(mode, "2020B") == 0) { config->ts = 0.014; config->tcp = 0.004; @@ -92,6 +98,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->state_machine = "voice2"; config->ftwindowwidth = 64; config->foff_limiter = true; + config->tx_bpf_proto = filtP1100S1300; + config->tx_bpf_proto_n = sizeof(filtP1100S1300) / sizeof(float); } else if (strcmp(mode, "qam16") == 0) { /* not in use yet */ config->ns = 5; @@ -109,6 +117,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->tx_bpf_en = false; config->clip_en = false; config->data_mode = "streaming"; + config->tx_bpf_proto = NULL; + config->tx_bpf_proto_n = 0; } else if (strcmp(mode, "datac0") == 0) { config->ns = 5; config->np = 4; @@ -130,6 +140,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->amp_scale = 300E3; config->clip_gain1 = 2.2; config->clip_gain2 = 0.85; + config->tx_bpf_proto = filtP400S600; + config->tx_bpf_proto_n = sizeof(filtP400S600) / sizeof(float); } else if (strcmp(mode, "datac1") == 0) { config->ns = 5; config->np = 38; @@ -152,6 +164,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->amp_scale = 145E3; config->clip_gain1 = 2.7; config->clip_gain2 = 0.8; + config->tx_bpf_proto = filtP900S1100; + config->tx_bpf_proto_n = sizeof(filtP900S1100) / sizeof(float); } else if (strcmp(mode, "datac3") == 0) { config->ns = 5; config->np = 29; @@ -176,6 +190,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->amp_scale = 300E3; config->clip_gain1 = 2.2; config->clip_gain2 = 0.8; + config->tx_bpf_proto = filtP400S600; + config->tx_bpf_proto_n = sizeof(filtP400S600) / sizeof(float); } else if (strcmp(mode, "datac4") == 0) { config->ns = 5; config->np = 47; @@ -201,6 +217,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->clip_gain1 = 1.2; config->clip_gain2 = 1.0; config->rx_bpf_en = true; + config->tx_bpf_proto = filtP200S400; + config->tx_bpf_proto_n = sizeof(filtP200S400) / sizeof(float); } else if (strcmp(mode, "datac13") == 0) { config->ns = 5; config->np = 18; @@ -226,6 +244,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->clip_gain1 = 1.2; config->clip_gain2 = 1.0; config->rx_bpf_en = true; + config->tx_bpf_proto = filtP200S400; + config->tx_bpf_proto_n = sizeof(filtP200S400) / sizeof(float); } else if (strcmp(mode, "datac14") == 0) { config->ns = 5; config->np = 4; @@ -251,6 +271,8 @@ void ofdm_init_mode(char mode[], struct OFDM_CONFIG *config) { config->clip_gain1 = 2.0; config->clip_gain2 = 1.0; config->rx_bpf_en = true; + config->tx_bpf_proto = filtP200S400; + config->tx_bpf_proto_n = sizeof(filtP200S400) / sizeof(float); } else { assert(0); } -- cgit v1.2.3 From b2311e5a967e09e5b293578bb596c83c50af536d Mon Sep 17 00:00:00 2001 From: drowe67 Date: Sat, 4 May 2024 07:16:26 +0930 Subject: custom config debugging: dump ofdm config from a FreeDV API function --- src/freedv_api.c | 4 ++++ src/freedv_api.h | 3 +++ src/freedv_data_raw_tx.c | 5 +++++ src/ofdm.c | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/src/freedv_api.c b/src/freedv_api.c index d04dd7c..3c812ca 100644 --- a/src/freedv_api.c +++ b/src/freedv_api.c @@ -1626,3 +1626,7 @@ unsigned short freedv_gen_crc16(unsigned char *data_p, int length) { return crc; } + +void freedv_ofdm_print_info(struct freedv *freedv) { + ofdm_print_info(freedv->ofdm); +} diff --git a/src/freedv_api.h b/src/freedv_api.h index 9f72c8f..0ca80ce 100644 --- a/src/freedv_api.h +++ b/src/freedv_api.h @@ -341,6 +341,9 @@ int freedv_get_bits_per_modem_frame(struct freedv *freedv); int freedv_get_sz_error_pattern(struct freedv *freedv); int freedv_get_protocol_bits(struct freedv *freedv); +// dump OFDM modem config +void freedv_ofdm_print_info(struct freedv *freedv); + #ifdef __cplusplus } #endif diff --git a/src/freedv_data_raw_tx.c b/src/freedv_data_raw_tx.c index ce5da56..7fa1e84 100644 --- a/src/freedv_data_raw_tx.c +++ b/src/freedv_data_raw_tx.c @@ -278,8 +278,13 @@ int main(int argc, char *argv[]) { memcpy(ofdm_config.tx_uw, uw, sizeof(uw)); memcpy(&ofdm_config.tx_uw[ofdm_config.nuwbits - sizeof(uw)], uw, sizeof(uw)); + /* set up a trivial Tx band pass filter as a demo */ + static float tx_bpf[] = {1.0, 1.0, 1.0}; + ofdm_config.tx_bpf_proto = tx_bpf; + ofdm_config.tx_bpf_proto_n = 3; adv.config = (void *)&ofdm_config; freedv = freedv_open_advanced(mode, &adv); + freedv_ofdm_print_info(freedv); } else { freedv = freedv_open(mode); } diff --git a/src/ofdm.c b/src/ofdm.c index 8d7a6df..953179f 100644 --- a/src/ofdm.c +++ b/src/ofdm.c @@ -2669,6 +2669,11 @@ void ofdm_print_info(struct OFDM *ofdm) { ofdm->phase_est_en ? "true" : "false"); fprintf(stderr, "ofdm->tx_bpf_en = %s\n", ofdm->tx_bpf_en ? "true" : "false"); fprintf(stderr, "ofdm->rx_bpf_en = %s\n", ofdm->rx_bpf_en ? "true" : "false"); + fprintf(stderr, "ofdm->tx_bpf_proto_n = %d\n", ofdm->tx_bpf_proto_n); + fprintf(stderr, "ofdm->tx_bpf_proto:\n"); + for (int i = 0; i < ofdm->tx_bpf_proto_n; i++) + fprintf(stderr, "%f\t", ofdm->tx_bpf_proto[i]); + fprintf(stderr, "\n"); fprintf(stderr, "ofdm->dpsk_en = %s\n", ofdm->dpsk_en ? "true" : "false"); fprintf(stderr, "ofdm->phase_est_bandwidth_mode = %s\n", phase_est_bandwidth_mode[ofdm->phase_est_bandwidth_mode]); -- cgit v1.2.3