aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt6
-rw-r--r--octave/gp_interleaver.m29
-rw-r--r--src/freedv_700.c31
-rw-r--r--src/freedv_api.c46
-rw-r--r--src/freedv_api.h10
-rw-r--r--src/freedv_api_internal.h2
-rw-r--r--src/freedv_data_raw_rx.c24
-rw-r--r--src/freedv_data_raw_tx.c31
-rw-r--r--src/gp_interleaver.c43
-rw-r--r--src/interldpc.c8
-rw-r--r--src/ofdm.c56
-rw-r--r--src/ofdm_internal.h22
-rw-r--r--src/ofdm_mode.c22
13 files changed, 220 insertions, 110 deletions
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;
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/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..3c812ca 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);
@@ -238,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);
@@ -270,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) {
@@ -279,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);
}
/*---------------------------------------------------------------------------*\
@@ -470,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)) {
@@ -1070,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);
@@ -1612,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 e4ca468..0ca80ce 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
@@ -334,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_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
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..7fa1e84 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,33 @@ 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));
+ /* 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);
+ }
assert(freedv != NULL);
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 <assert.h>
+#include <math.h>
#include <stdio.h>
/*
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) {
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
diff --git a/src/ofdm.c b/src/ofdm.c
index ba22f3f..953179f 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")) {
@@ -2678,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]);
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);
}