From 405678145eedadfd2708b4d750030bb6fecda94a Mon Sep 17 00:00:00 2001 From: drowe67 Date: Sun, 22 Apr 2018 21:54:31 +0000 Subject: [PATCH] freedv_api tx working for 700D with 1 frame interleaving. Need to refactor to handle multiple frame interleaving, and adjust output level git-svn-id: https://svn.code.sf.net/p/freetel/code@3518 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/src/freedv_api.c | 141 +++++++++++++++++++++++---- codec2-dev/src/freedv_api.h | 32 +++--- codec2-dev/src/freedv_api_internal.h | 12 ++- codec2-dev/src/freedv_tx.c | 4 +- codec2-dev/src/interldpc.c | 22 ++++- codec2-dev/src/interldpc.h | 4 +- codec2-dev/src/ofdm_mod.c | 19 ++-- 7 files changed, 178 insertions(+), 56 deletions(-) diff --git a/codec2-dev/src/freedv_api.c b/codec2-dev/src/freedv_api.c index 85570425..e0fa45ea 100644 --- a/codec2-dev/src/freedv_api.c +++ b/codec2-dev/src/freedv_api.c @@ -70,6 +70,11 @@ #define NORM_PWR_COHPSK 1.74 /* experimentally derived fudge factor to normalise power for cohpsk modes */ #define NORM_PWR_FSK 0.193 /* experimentally derived fudge factor to normalise power for fsk modes */ +#define NORM_PWR_OFDM 10.00 /* todo: experimentally derived fudge factor to normalise power for OFDM modes */ + +/* OFDM payload data test frame for 700D */ + +extern int payload_data_bits[]; /*---------------------------------------------------------------------------*\ @@ -163,7 +168,7 @@ struct freedv *freedv_open(int mode) { return NULL; f->sz_error_pattern = cohpsk_error_pattern_size(); } - + if (mode == FREEDV_MODE_700D) { /* TODO: @@ -177,6 +182,8 @@ struct freedv *freedv_open(int mode) { codec2_mode = CODEC2_MODE_700C; f->ofdm = ofdm_create(OFDM_CONFIG_700D); + f->interleave_frames = 1; + f->nin = ofdm_get_samples_per_frame(); f->n_nat_modem_samples = ofdm_get_samples_per_frame(); f->n_nom_modem_samples = ofdm_get_samples_per_frame(); @@ -295,7 +302,7 @@ struct freedv *freedv_open(int mode) { f->codec2 = codec2_create(codec2_mode); if (f->codec2 == NULL) return NULL; - + /* work out how many codec 2 frames per mode frame, and number of bytes of storage for packed and unpacket bits. TODO: do we really need to work in packed bits at all? It's messy, chars would probably @@ -325,18 +332,18 @@ struct freedv *freedv_open(int mode) { assert((f->ldpc->data_bits_per_frame % codec2_bits_per_frame(f->codec2)) == 0); - int Ncodec2frames = f->ldpc->data_bits_per_frame/codec2_bits_per_frame(f->codec2); - + int Ncodec2frames = f->ldpc->data_bits_per_frame*f->interleave_frames/codec2_bits_per_frame(f->codec2); f->n_speech_samples = Ncodec2frames*codec2_samples_per_frame(f->codec2); f->n_codec_bits = Ncodec2frames*codec2_bits_per_frame(f->codec2); nbit = f->n_codec_bits; nbyte = (nbit + 7) / 8; + fprintf(stderr, "Ncodec2frames: %d nbit: %d nbyte: %d \n", Ncodec2frames, nbit, nbyte); } f->packed_codec_bits = (unsigned char*)malloc(nbyte*sizeof(char)); if (mode == FREEDV_MODE_1600) f->codec_bits = (int*)malloc(nbit*sizeof(int)); - if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) + if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C) || (mode == FREEDV_MODE_700D)) f->codec_bits = (int*)malloc(COHPSK_BITS_PER_FRAME*sizeof(int)); /* Note: VHF Framer/deframer goes directly from packed codec/vc/proto bits to filled frame */ @@ -621,7 +628,8 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { COMP tx_fdm[f->n_nom_modem_samples]; int i; assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || - (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) || + (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) || + (f->mode == FREEDV_MODE_700D) || (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)); @@ -629,7 +637,7 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { * stick them in the real sample tx/rx functions than to add a comp->real converter * to comptx */ - if((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ + if ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ /* 800XA has two codec frames per modem frame */ if((f->mode == FREEDV_MODE_800XA)){ codec2_encode(f->codec2, &f->packed_codec_bits[0], &speech_in[ 0]); @@ -638,7 +646,7 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { codec2_encode(f->codec2, f->packed_codec_bits, speech_in); } freedv_tx_fsk_voice(f, mod_out); - }else{ + } else{ freedv_comptx(f, tx_fdm, speech_in); for(i=0; in_nom_modem_samples; i++) mod_out[i] = tx_fdm[i].real; @@ -741,7 +749,7 @@ static void freedv_comptx_fdmdv_1600(struct freedv *f, COMP mod_out[]) { } #ifndef CORTEX_M4 -static void freedv_comptx_fdmdv_700(struct freedv *f, COMP mod_out[]) { +static void freedv_comptx_700(struct freedv *f, COMP mod_out[]) { int bit, byte, i, j, k; int bits_per_codec_frame, bits_per_modem_frame; int data_flag_index, nspare; @@ -821,8 +829,98 @@ static void freedv_comptx_fdmdv_700(struct freedv *f, COMP mod_out[]) { // Caution: assert fails if f->n_nat_modem_samples * 16.0 / 15.0 is not an integer } + +/* + TODO: Configure to input/output one modem frame for each call, even + when interleaved. This means we'll need to buffer coded bits, + then buffer tx modem signal. Start with single frame, get that + working first. +*/ + +static void freedv_comptx_700d(struct freedv *f, COMP mod_out[]) { + int bit, byte, i, j, k; + int bits_per_codec_frame, bits_per_modem_frame; + int nspare; + + int data_bits_per_frame = f->ldpc->data_bits_per_frame; + + bits_per_modem_frame = f->interleave_frames*data_bits_per_frame; + uint8_t tx_bits[bits_per_modem_frame]; + + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + + byte = 0; + for (j=0; jpacked_codec_bits[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; + } + } + if (bit != 7) + byte++; + } + + // Generate Varicode txt bits. Txt bits in OFDM frame come just + // after Unique Word (UW). Txt bits aren't protected by FEC, and need to be + // added to each frame after interleaver as done it's thing + + nspare = OFDM_NTXTBITS*f->interleave_frames; + uint8_t txt_bits[nspare]; + + for(k=0; knvaricode_bits) { + txt_bits[k] = f->tx_varicode_bits[f->varicode_bit_index++]; + f->nvaricode_bits--; + } + if (f->nvaricode_bits == 0) { + /* get new char and encode */ + char s[2]; + if (f->freedv_get_next_tx_char != NULL) { + s[0] = (*f->freedv_get_next_tx_char)(f->callback_state); + f->nvaricode_bits = varicode_encode(f->tx_varicode_bits, s, VARICODE_MAX_BITS, 1, 1); + f->varicode_bit_index = 0; + } + } + } + + /* optionally replace codec payload bits with test frames known to rx */ + + if (f->test_frames) { + for (j=0; jinterleave_frames; j++) { + for(i=0; iinterleave_frames*ofdm_get_samples_per_frame()]; + COMP asam; + + ofdm_ldpc_interleave_tx(f->ofdm, f->ldpc, tx_sams, tx_bits, txt_bits, f->interleave_frames); + + assert(f->n_nat_modem_samples == f->interleave_frames*ofdm_get_samples_per_frame()); + + for(i=0; in_nat_modem_samples; i++) { + asam.real = crealf(tx_sams[i]); + asam.imag = cimagf(tx_sams[i]); + mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR_OFDM, asam); + } + + assert(f->clip == 0); /* todo: support clipping, requires some simulations and testing */ +} + #endif + void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { assert(f != NULL); #ifndef CORTEX_M4 @@ -832,7 +930,8 @@ void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) || - (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B)); + (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || + (f->mode == FREEDV_MODE_700D)); if (f->mode == FREEDV_MODE_1600) { codec2_encode(f->codec2, f->packed_codec_bits, speech_in); @@ -840,16 +939,24 @@ void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { } #ifndef CORTEX_M4 - if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)|| (f->mode == FREEDV_MODE_700C)) { + + /* all these modes need to pack a bunch of codec frames into one modem frame */ + + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C) + || (f->mode == FREEDV_MODE_700D)) { bits_per_codec_frame = codec2_bits_per_frame(f->codec2); int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; int codec_frames = f->n_codec_bits / bits_per_codec_frame; - for (j=0; j < codec_frames; j++) { + for (j=0; jcodec2, f->packed_codec_bits + j * bytes_per_codec_frame, speech_in); speech_in += codec2_samples_per_frame(f->codec2); } - freedv_comptx_fdmdv_700(f, mod_out); + if (f->mode == FREEDV_MODE_700D) { + freedv_comptx_700d(f, mod_out); + } else { + freedv_comptx_700(f, mod_out); + } } #endif /* 2400 A and B are handled by the real-mode TX */ @@ -880,7 +987,7 @@ void freedv_codectx(struct freedv *f, short mod_out[], unsigned char *packed_cod case FREEDV_MODE_700: case FREEDV_MODE_700B: case FREEDV_MODE_700C: - freedv_comptx_fdmdv_700(f, tx_fdm); + freedv_comptx_700(f, tx_fdm); break; case FREEDV_MODE_2400A: case FREEDV_MODE_2400B: @@ -1246,7 +1353,7 @@ static int freedv_comprx_fdmdv_1600(struct freedv *f, COMP demod_in[], int *vali } #ifndef CORTEX_M4 -static int freedv_comprx_fdmdv_700(struct freedv *f, COMP demod_in_8kHz[], int *valid) { +static int freedv_comprx_700(struct freedv *f, COMP demod_in_8kHz[], int *valid) { int bits_per_codec_frame, bytes_per_codec_frame; int i, j, bit, byte, nout, k; int data_flag_index, n_ascii, nspare; @@ -1445,7 +1552,7 @@ int freedv_comprx(struct freedv *f, short speech_out[], COMP demod_in[]) { } #ifndef CORTEX_M4 if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { - nout = freedv_comprx_fdmdv_700(f, demod_in, &valid); + nout = freedv_comprx_700(f, demod_in, &valid); //valid = -1; } @@ -1496,7 +1603,7 @@ int freedv_codecrx(struct freedv *f, unsigned char *packed_codec_bits, short dem #ifndef CORTEX_M4 if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { - freedv_comprx_fdmdv_700(f, rx_fdm, &valid); + freedv_comprx_700(f, rx_fdm, &valid); } #endif diff --git a/codec2-dev/src/freedv_api.h b/codec2-dev/src/freedv_api.h index 28e0cc66..2d02e0b3 100644 --- a/codec2-dev/src/freedv_api.h +++ b/codec2-dev/src/freedv_api.h @@ -100,12 +100,12 @@ int freedv_codecrx (struct freedv *freedv, unsigned char *packed_codec_bits, sh void freedv_set_callback_txt (struct freedv *freedv, freedv_callback_rx rx, freedv_callback_tx tx, void *callback_state); void freedv_set_callback_protocol (struct freedv *freedv, freedv_callback_protorx rx, freedv_callback_prototx tx, void *callback_state); -void freedv_set_callback_data (struct freedv *freedv, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state); -void freedv_set_test_frames (struct freedv *freedv, int test_frames); +void freedv_set_callback_data (struct freedv *freedv, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state); +void freedv_set_test_frames (struct freedv *freedv, int test_frames); void freedv_set_test_frames_diversity (struct freedv *freedv, int test_frames_diversity); -void freedv_set_smooth_symbols (struct freedv *freedv, int smooth_symbols); -void freedv_set_squelch_en (struct freedv *freedv, int squelch_en); -void freedv_set_snr_squelch_thresh (struct freedv *freedv, float snr_squelch_thresh); +void freedv_set_smooth_symbols (struct freedv *freedv, int smooth_symbols); +void freedv_set_squelch_en (struct freedv *freedv, int squelch_en); +void freedv_set_snr_squelch_thresh (struct freedv *freedv, float snr_squelch_thresh); void freedv_set_clip (struct freedv *freedv, int val); void freedv_set_total_bit_errors (struct freedv *freedv, int val); void freedv_set_total_bits (struct freedv *freedv, int val); @@ -122,17 +122,17 @@ int freedv_get_version(void); int freedv_get_mode (struct freedv *freedv); void freedv_get_modem_stats (struct freedv *freedv, int *sync, float *snr_est); void freedv_get_modem_extended_stats(struct freedv *freedv, struct MODEM_STATS *stats); -int freedv_get_test_frames (struct freedv *freedv); -int freedv_get_n_speech_samples (struct freedv *freedv); -int freedv_get_modem_sample_rate (struct freedv *freedv); -int freedv_get_n_max_modem_samples (struct freedv *freedv); -int freedv_get_n_nom_modem_samples (struct freedv *freedv); -int freedv_get_total_bits (struct freedv *freedv); -int freedv_get_total_bit_errors (struct freedv *freedv); -int freedv_get_sync (struct freedv *freedv); -struct FSK * freedv_get_fsk(struct freedv *f); -struct CODEC2 *freedv_get_codec2 (struct freedv *freedv); -int freedv_get_n_codec_bits (struct freedv *freedv); +int freedv_get_test_frames (struct freedv *freedv); +int freedv_get_n_speech_samples (struct freedv *freedv); +int freedv_get_modem_sample_rate (struct freedv *freedv); +int freedv_get_n_max_modem_samples (struct freedv *freedv); +int freedv_get_n_nom_modem_samples (struct freedv *freedv); +int freedv_get_total_bits (struct freedv *freedv); +int freedv_get_total_bit_errors (struct freedv *freedv); +int freedv_get_sync (struct freedv *freedv); +struct FSK * freedv_get_fsk (struct freedv *f); +struct CODEC2 *freedv_get_codec2 (struct freedv *freedv); +int freedv_get_n_codec_bits (struct freedv *freedv); int freedv_get_sz_error_pattern (struct freedv *freedv); int freedv_get_protocol_bits (struct freedv *freedv); diff --git a/codec2-dev/src/freedv_api_internal.h b/codec2-dev/src/freedv_api_internal.h index 7657879c..a7c56d7c 100644 --- a/codec2-dev/src/freedv_api_internal.h +++ b/codec2-dev/src/freedv_api_internal.h @@ -77,12 +77,12 @@ struct freedv { struct quisk_cfFilter * ptFilter7500to8000; // Filters to change to/from 7500 and 8000 sps struct quisk_cfFilter * ptFilter8000to7500; - int n_speech_samples; + int n_speech_samples; // number of speech samples we need for each freedv_tx() call int n_nom_modem_samples; // size of tx and most rx modem sample buffers int n_max_modem_samples; // make your rx modem sample buffers this big int n_nat_modem_samples; // tx modem sample block length as used by the modem before interpolation to output - - int modem_sample_rate; // ATM caller is responsible for meeting this (TBC) + // usually the same as n_nom_modem_samples, except for 700..700C + int modem_sample_rate; // ATM caller is responsible for meeting this int clip; // non-zero for cohpsk modem output clipping for low PAPR unsigned char *packed_codec_bits; @@ -92,7 +92,7 @@ struct freedv { int *rx_bits; int tx_sync_bit; int smooth_symbols; - int n_codec_bits; // amount of codec bits in a frame + int n_codec_bits; // number of codec bits in a frame int *ptest_bits_coh; int *ptest_bits_coh_end; @@ -123,6 +123,10 @@ struct freedv { int nvaricode_bits; int varicode_bit_index; + /* interleaved LDPC OFDM states */ + + int interleave_frames; // number of OFDM modem frames in interleaver, e.g. 1,2,4,8,16 + /* user defined function ptrs to produce and consume ASCII characters using aux txt channel */ diff --git a/codec2-dev/src/freedv_tx.c b/codec2-dev/src/freedv_tx.c index 874a9b59..89dbf69a 100644 --- a/codec2-dev/src/freedv_tx.c +++ b/codec2-dev/src/freedv_tx.c @@ -98,7 +98,7 @@ int main(int argc, char *argv[]) { int i; if (argc < 4) { - printf("usage: %s 1600|700|700B|700C|2400A|2400B|800XA InputRawSpeechFile OutputModemRawFile [--testframes] [--codectx] [--datatx]\n", argv[0]); + printf("usage: %s 1600|700|700B|700C|700D|2400A|2400B|800XA InputRawSpeechFile OutputModemRawFile [--testframes] [--codectx] [--datatx]\n", argv[0]); printf("e.g %s 1600 hts1a.raw hts1a_fdmdv.raw\n", argv[0]); exit(1); } @@ -112,6 +112,8 @@ int main(int argc, char *argv[]) { mode = FREEDV_MODE_700B; if (!strcmp(argv[1],"700C")) mode = FREEDV_MODE_700C; + if (!strcmp(argv[1],"700D")) + mode = FREEDV_MODE_700D; if (!strcmp(argv[1],"2400A")){ mode = FREEDV_MODE_2400A; } diff --git a/codec2-dev/src/interldpc.c b/codec2-dev/src/interldpc.c index 4c1a9365..cf656c14 100644 --- a/codec2-dev/src/interldpc.c +++ b/codec2-dev/src/interldpc.c @@ -188,7 +188,16 @@ int count_errors(int tx_bits[], char rx_bits[], int n) { } -void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, complex float tx_sams[], uint8_t tx_bits_char[], complex float tx_symbols[], int interleave_frames) +/* + Given an array of tx_bits, LDPC encodes, interleaves, and OFDM + modulates. + + Note this could be refactored to save memory, e.g. for embedded + applications we could call ofdm_txframe on a frame by frame + basis +*/ + +void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, complex float tx_sams[], uint8_t tx_bits[], uint8_t txt_bits[], int interleave_frames) { int coded_syms_per_frame = ldpc->coded_syms_per_frame; int coded_bits_per_frame = ldpc->coded_bits_per_frame; @@ -198,16 +207,19 @@ void ofdm_ldpc_interleave_tx(struct OFDM *ofdm, struct LDPC *ldpc, complex float COMP coded_symbols[interleave_frames*coded_syms_per_frame]; COMP coded_symbols_inter[interleave_frames*coded_syms_per_frame]; int Nsamperframe = ofdm_get_samples_per_frame(); - + complex float tx_symbols[(OFDM_NUWBITS+OFDM_NTXTBITS)/OFDM_BPS + coded_syms_per_frame]; + int i,j; for (j=0; j