mirror of
https://github.com/drowe67/codec2
synced 2026-08-14 19:31:34 -04:00
Merge pull request #46 from drowe67/dr-freedata-001
FreeDATA Feature Request 001 - FreeDV API support for custom OFDM raw data modes
This commit is contained in:
commit
3d69c8d063
13 changed files with 221 additions and 111 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 is_prime(int x) {
|
||||
for (int i = 2; i < x; i++) {
|
||||
if ((x % i) == 0) return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int next_prime(int x) {
|
||||
x++;
|
||||
while (is_prime(x) == 0) x++;
|
||||
return x;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
/* if we get to here it means a Nbits we don't have in our table so choke */
|
||||
|
||||
fprintf(stderr, "gp_interleaver: Nbits: %d, b not found!\n", Nbits);
|
||||
assert(0);
|
||||
return -1;
|
||||
int b = floor(Nbits / 1.62);
|
||||
b = next_prime(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
void gp_interleave_comp(COMP interleaved_frame[], COMP frame[], int Nbits) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
56
src/ofdm.c
56
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]);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue