From 0f7983a76bc1e3a20cdcde3fd294b5a4bcf54778 Mon Sep 17 00:00:00 2001 From: drowe67 Date: Thu, 25 Feb 2016 09:36:05 +0000 Subject: [PATCH] Jeroen's DML patch, applied cleanly, code builds, but not tested git-svn-id: https://svn.code.sf.net/p/freetel/code@2708 01035d8c-6547-0410-b346-abe4f91aad63 --- codec2-dev/src/freedv_api.c | 1005 +++++++++++++++----------- codec2-dev/src/freedv_api.h | 3 + codec2-dev/src/freedv_api_internal.h | 1 + 3 files changed, 593 insertions(+), 416 deletions(-) diff --git a/codec2-dev/src/freedv_api.c b/codec2-dev/src/freedv_api.c index 0ebd06bb..d1156467 100644 --- a/codec2-dev/src/freedv_api.c +++ b/codec2-dev/src/freedv_api.c @@ -212,17 +212,22 @@ struct freedv *freedv_open(int mode) { f->codec2 = codec2_create(codec2_mode); if (f->codec2 == NULL) return NULL; - if ((mode == FREEDV_MODE_1600) || (mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) + if ((mode == FREEDV_MODE_1600) || (mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) { f->n_speech_samples = codec2_samples_per_frame(f->codec2); - if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B)) + f->n_codec_bits = codec2_bits_per_frame(f->codec2); + nbit = f->n_codec_bits; + nbyte = (nbit + 7) / 8; + } else { /* ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B)) */ f->n_speech_samples = 2*codec2_samples_per_frame(f->codec2); + f->n_codec_bits = 2*codec2_bits_per_frame(f->codec2); + nbit = f->n_codec_bits; + nbyte = 2*((codec2_bits_per_frame(f->codec2) + 7) / 8); + } f->prev_rx_bits = (float*)malloc(sizeof(float)*2*codec2_bits_per_frame(f->codec2)); if (f->prev_rx_bits == NULL) return NULL; - nbit = codec2_bits_per_frame(f->codec2); - nbyte = (nbit + 7) / 8; f->packed_codec_bits = (unsigned char*)malloc(nbyte*sizeof(char)); if (mode == FREEDV_MODE_1600) f->codec_bits = (int*)malloc(nbit*sizeof(int)); @@ -338,12 +343,61 @@ void freedv_close(struct freedv *freedv) { /* real-valued short sample output, useful for going straight to DAC */ +/* TX routines for 2400 FSK modes, after codec2 encoding */ +static void freedv_tx_fsk(struct freedv *f, short mod_out[]) { + int i; + float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ + uint8_t vc_bits[2]; /* Varicode bits for 2400 framing */ + + + /* Get varicode bits for TX and possibly ask for a new char */ + /* 2 bits per 2400A/B frame, so this has to be done twice */ + for(i=0;i<2;i++){ + if (f->nvaricode_bits) { + vc_bits[i] = 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; + } + } + } + + /* If the API user hasn't set up message callbacks, don't bother with varicode bits */ + if(f->freedv_get_next_tx_char == NULL){ + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); + }else{ + fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,vc_bits); + } + + /* Allocate floating point buffer for FSK mod */ + tx_float = alloca(sizeof(float)*f->n_nom_modem_samples); + + /* do 4fsk mod */ + if(f->mode == FREEDV_MODE_2400A){ + fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); + + /* do me-fsk mod */ + }else if(f->mode == FREEDV_MODE_2400B){ + fmfsk_mod(f->fmfsk,tx_float,(uint8_t*)(f->tx_bits)); + } + + /* Convert float samps to short */ + for(i=0; in_nom_modem_samples; i++){ + mod_out[i] = (short)(tx_float[i]*FDMDV_SCALE); + } +} + void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { assert(f != NULL); COMP tx_fdm[f->n_nom_modem_samples]; int i; - float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ - uint8_t vc_bits[2]; /* Varicode bits for 2400 framing */ assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_2400A) || @@ -355,48 +409,7 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { if((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B)){ codec2_encode(f->codec2, f->packed_codec_bits, speech_in); - /* Get varicode bits for TX and possibly ask for a new char */ - /* 2 bits per 2400A/B frame, so this has to be done twice */ - for(i=0;i<2;i++){ - if (f->nvaricode_bits) { - vc_bits[i] = 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; - } - } - } - - /* If the API user hasn't set up message callbacks, don't bother with varicode bits */ - if(f->freedv_get_next_tx_char == NULL){ - fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); - }else{ - fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,vc_bits); - } - - /* Allocate floating point buffer for FSK mod */ - tx_float = alloca(sizeof(float)*f->n_nom_modem_samples); - - /* do 4fsk mod */ - if(f->mode == FREEDV_MODE_2400A){ - fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); - - /* do me-fsk mod */ - }else if(f->mode == FREEDV_MODE_2400B){ - fmfsk_mod(f->fmfsk,tx_float,(uint8_t*)(f->tx_bits)); - } - - /* Convert float samps to short */ - for(i=0; in_nom_modem_samples; i++){ - mod_out[i] = (short)(tx_float[i]*FDMDV_SCALE); - } + freedv_tx_fsk(f, mod_out); }else{ freedv_comptx(f, tx_fdm, speech_in); for(i=0; in_nom_modem_samples; i++) @@ -406,176 +419,204 @@ void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { /* complex valued output, useful for suitable for single sided freq shifting */ -void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { - assert(f != NULL); - int bit, byte, i, j, k; +static void freedv_comptx_fdmdv_1600(struct freedv *f, COMP mod_out[]) { + int bit, byte, i, j; int bits_per_codec_frame, bits_per_modem_frame; - int data, codeword1, data_flag_index, nspare; + int data, codeword1, data_flag_index; COMP tx_fdm[f->n_nat_modem_samples]; - short tx_real[f->n_nom_modem_samples]; - assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || - (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_2400A) || - (f->mode == FREEDV_MODE_2400B)); + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bits_per_modem_frame = fdmdv_bits_per_frame(f->fdmdv); - if (f->mode == FREEDV_MODE_1600) { - bits_per_codec_frame = codec2_bits_per_frame(f->codec2); - bits_per_modem_frame = fdmdv_bits_per_frame(f->fdmdv); - - codec2_encode(f->codec2, f->packed_codec_bits, speech_in); + /* unpack bits, MSB first */ - /* unpack bits, MSB first */ - - bit = 7; byte = 0; - for(i=0; icodec_bits[i] = (f->packed_codec_bits[byte] >> bit) & 0x1; - bit--; - if (bit < 0) { - bit = 7; - byte++; - } + bit = 7; byte = 0; + for(i=0; icodec_bits[i] = (f->packed_codec_bits[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; } + } - // spare bit in frame that codec defines. Use this 1 - // bit/frame to send txt messages + // spare bit in frame that codec defines. Use this 1 + // bit/frame to send txt messages - data_flag_index = codec2_get_spare_bit_index(f->codec2); + data_flag_index = codec2_get_spare_bit_index(f->codec2); - if (f->nvaricode_bits) { - f->codec_bits[data_flag_index] = f->tx_varicode_bits[f->varicode_bit_index++]; - f->nvaricode_bits--; - } + if (f->nvaricode_bits) { + f->codec_bits[data_flag_index] = 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; - } + 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; } + } - /* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code: + /* Protect first 12 out of first 16 excitation bits with (23,12) Golay Code: - 0,1,2,3: v[0]..v[1] - 4,5,6,7: MSB of pitch - 11,12,13,14: MSB of energy + 0,1,2,3: v[0]..v[1] + 4,5,6,7: MSB of pitch + 11,12,13,14: MSB of energy - */ + */ - data = 0; - for(i=0; i<8; i++) { - data <<= 1; - data |= f->codec_bits[i]; - } - for(i=11; i<15; i++) { - data <<= 1; - data |= f->codec_bits[i]; - } - codeword1 = golay23_encode(data); + data = 0; + for(i=0; i<8; i++) { + data <<= 1; + data |= f->codec_bits[i]; + } + for(i=11; i<15; i++) { + data <<= 1; + data |= f->codec_bits[i]; + } + codeword1 = golay23_encode(data); - /* now pack output frame with parity bits at end to make them - as far apart as possible from the data they protect. Parity - bits are LSB of the Golay codeword */ + /* now pack output frame with parity bits at end to make them + as far apart as possible from the data they protect. Parity + bits are LSB of the Golay codeword */ - for(i=0; itx_bits[i] = f->codec_bits[i]; - for(j=0; itx_bits[i] = (codeword1 >> (10-j)) & 0x1; - } - f->tx_bits[i] = 0; /* spare bit */ + for(i=0; itx_bits[i] = f->codec_bits[i]; + for(j=0; itx_bits[i] = (codeword1 >> (10-j)) & 0x1; + } + f->tx_bits[i] = 0; /* spare bit */ - /* optionally overwrite with test frames */ + /* optionally overwrite with test frames */ - if (f->test_frames) { - fdmdv_get_test_bits(f->fdmdv, f->tx_bits); - fdmdv_get_test_bits(f->fdmdv, &f->tx_bits[bits_per_modem_frame]); - //fprintf(stderr, "test frames on tx\n"); - } + if (f->test_frames) { + fdmdv_get_test_bits(f->fdmdv, f->tx_bits); + fdmdv_get_test_bits(f->fdmdv, &f->tx_bits[bits_per_modem_frame]); + //fprintf(stderr, "test frames on tx\n"); + } - /* modulate even and odd frames */ + /* modulate even and odd frames */ - fdmdv_mod(f->fdmdv, tx_fdm, f->tx_bits, &f->tx_sync_bit); - assert(f->tx_sync_bit == 1); + fdmdv_mod(f->fdmdv, tx_fdm, f->tx_bits, &f->tx_sync_bit); + assert(f->tx_sync_bit == 1); - fdmdv_mod(f->fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &f->tx_bits[bits_per_modem_frame], &f->tx_sync_bit); - assert(f->tx_sync_bit == 0); + fdmdv_mod(f->fdmdv, &tx_fdm[FDMDV_NOM_SAMPLES_PER_FRAME], &f->tx_bits[bits_per_modem_frame], &f->tx_sync_bit); + assert(f->tx_sync_bit == 0); - assert(2*FDMDV_NOM_SAMPLES_PER_FRAME == f->n_nom_modem_samples); + assert(2*FDMDV_NOM_SAMPLES_PER_FRAME == f->n_nom_modem_samples); - for(i=0; in_nom_modem_samples; i++) - mod_out[i] = fcmult(FDMDV_SCALE, tx_fdm[i]); - } + for(i=0; in_nom_modem_samples; i++) + mod_out[i] = fcmult(FDMDV_SCALE, tx_fdm[i]); +} +static void freedv_comptx_fdmdv_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; + COMP tx_fdm[f->n_nat_modem_samples]; -#ifndef CORTEX_M4 - if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) { - bits_per_codec_frame = codec2_bits_per_frame(f->codec2); - bits_per_modem_frame = COHPSK_BITS_PER_FRAME; + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bits_per_modem_frame = COHPSK_BITS_PER_FRAME; - for (j=0; jcodec2, f->packed_codec_bits, speech_in); - speech_in += codec2_samples_per_frame(f->codec2); + byte = 0; + for (j=0; jcodec_bits[j+i] = (f->packed_codec_bits[byte] >> bit) & 0x1; - bit--; - if (bit < 0) { - bit = 7; - byte++; - } + bit = 7; + for(i=0; icodec_bits[j+i] = (f->packed_codec_bits[byte] >> bit) & 0x1; + bit--; + if (bit < 0) { + bit = 7; + byte++; } + } + if (bit != 7) + byte++; - // spare bits in frame that codec defines. Use these spare - // bits/frame to send txt messages + // spare bits in frame that codec defines. Use these spare + // bits/frame to send txt messages - if (f->mode == FREEDV_MODE_700) - nspare = 2; - else - nspare = 1; // Just one spare bit for FREEDV_MODE_700B + if (f->mode == FREEDV_MODE_700) + nspare = 2; + else + nspare = 1; // Just one spare bit for FREEDV_MODE_700B - data_flag_index = codec2_get_spare_bit_index(f->codec2); + data_flag_index = codec2_get_spare_bit_index(f->codec2); - for(k=0; knvaricode_bits) { - f->codec_bits[j+data_flag_index+k] = f->tx_varicode_bits[f->varicode_bit_index++]; - //fprintf(stderr, "%d %d\n", j+data_flag_index+k, f->codec_bits[j+data_flag_index+k]); - f->nvaricode_bits--; - } + for(k=0; knvaricode_bits) { + f->codec_bits[j+data_flag_index+k] = f->tx_varicode_bits[f->varicode_bit_index++]; + //fprintf(stderr, "%d %d\n", j+data_flag_index+k, f->codec_bits[j+data_flag_index+k]); + 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; - } + 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 ovwerwrite the codec bits with test frames */ + } - if (f->test_frames) { - cohpsk_get_test_bits(f->cohpsk, f->codec_bits); - } + /* optionally ovwerwrite the codec bits with test frames */ + + if (f->test_frames) { + cohpsk_get_test_bits(f->cohpsk, f->codec_bits); + } + + /* cohpsk modulator */ + + cohpsk_mod(f->cohpsk, tx_fdm, f->codec_bits); + if (f->clip) + cohpsk_clip(tx_fdm); + for(i=0; in_nat_modem_samples; i++) + mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR, tx_fdm[i]); + i = quisk_cfInterpDecim(mod_out, f->n_nat_modem_samples, f->ptFilter7500to8000, 16, 15); + //assert(i == f->n_nom_modem_samples); + // Caution: assert fails if f->n_nat_modem_samples * 16.0 / 15.0 is not an integer + +} + +void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { + assert(f != NULL); + int i, j; + int bits_per_codec_frame; + short tx_real[f->n_nom_modem_samples]; + + assert((f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || + (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_2400A) || + (f->mode == FREEDV_MODE_2400B)); + + if (f->mode == FREEDV_MODE_1600) { + + codec2_encode(f->codec2, f->packed_codec_bits, speech_in); + + freedv_comptx_fdmdv_1600(f, mod_out); + } - /* cohpsk modulator */ - cohpsk_mod(f->cohpsk, tx_fdm, f->codec_bits); - if (f->clip) - cohpsk_clip(tx_fdm); - for(i=0; in_nat_modem_samples; i++) - mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR, tx_fdm[i]); - i = quisk_cfInterpDecim(mod_out, f->n_nat_modem_samples, f->ptFilter7500to8000, 16, 15); - //assert(i == f->n_nom_modem_samples); - // Caution: assert fails if f->n_nat_modem_samples * 16.0 / 15.0 is not an integer +#ifndef CORTEX_M4 + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) { + 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++) { + codec2_encode(f->codec2, 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); } /* 2400 A and B are handled by the real-mode TX */ if((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B)){ @@ -589,6 +630,37 @@ void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { #endif } +void freedv_codectx(struct freedv *f, short mod_out[], unsigned char *packed_codec_bits) { + assert(f != NULL); + COMP tx_fdm[f->n_nom_modem_samples]; + int bits_per_codec_frame; + int bytes_per_codec_frame; + int codec_frames; + int i; + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + codec_frames = f->n_codec_bits / bits_per_codec_frame; + + memcpy(f->packed_codec_bits, packed_codec_bits, bytes_per_codec_frame * codec_frames); + + switch(f->mode) { + case FREEDV_MODE_700: + case FREEDV_MODE_700B: + freedv_comptx_fdmdv_700(f, tx_fdm); + break; + case FREEDV_MODE_1600: + freedv_comptx_fdmdv_1600(f, tx_fdm); + break; + case FREEDV_MODE_2400A: + case FREEDV_MODE_2400B: + freedv_tx_fsk(f, mod_out); + return; /* output is already real */ + } + /* convert complex to real */ + for(i=0; in_nom_modem_samples; i++) + mod_out[i] = tx_fdm[i].real; +} + int freedv_nin(struct freedv *f) { if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) // For mode 700, the input rate is 8000 sps, but the modem rate is 7500 sps @@ -674,51 +746,71 @@ int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { // float input samples version +int freedv_floatrx_fsk_2400(struct freedv *f, float demod_in[], int *valid) { + uint8_t vc_bits[2]; + short vc_bit; + int i; + int n_ascii; + char ascii_out; + + if(f->mode == FREEDV_MODE_2400A){ + fsk_demod(f->fsk,(uint8_t*)f->tx_bits,demod_in); + f->nin = fsk_nin(f->fsk); + }else{ + fmfsk_demod(f->fmfsk,(uint8_t*)f->tx_bits,demod_in); + f->nin = fmfsk_nin(f->fmfsk); + } + /* TODO: Protocol and varicode bits */ + if(fvhff_deframe_bits(f->deframer,f->packed_codec_bits,NULL,NULL,(uint8_t*)f->tx_bits)){ + /* Decode varicode text */ + for(i=0; i<2; i++){ + /* Note: deframe_bits spits out bits in uint8_ts while varicode_decode expects shorts */ + vc_bit = vc_bits[i]; + n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, &vc_bit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); + } + } + *valid = 1; + f->sync = 1; + f->stats.sync = 1; + } else { + /* Fill with silence */ + *valid = 0; + f->sync = 0; + f->stats.sync = 0; + } + return f->n_speech_samples; +} + int freedv_floatrx(struct freedv *f, short speech_out[], float demod_in[]) { assert(f != NULL); COMP rx_fdm[f->n_max_modem_samples]; - uint8_t vc_bits[2]; - short vc_bit; int i; int nin = freedv_nin(f); - int n_ascii; - char ascii_out; assert(nin <= f->n_max_modem_samples); /* FSK RX happens in real floats, so demod for those goes here */ if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) ){ - if(f->mode == FREEDV_MODE_2400A){ - fsk_demod(f->fsk,(uint8_t*)f->tx_bits,demod_in); - f->nin = fsk_nin(f->fsk); - }else{ - fmfsk_demod(f->fmfsk,(uint8_t*)f->tx_bits,demod_in); - f->nin = fmfsk_nin(f->fmfsk); - } - /* TODO: Protocol bits */ - if(fvhff_deframe_bits(f->deframer,f->packed_codec_bits,NULL,vc_bits,(uint8_t*)f->tx_bits)){ - /* Decode varicode text */ - for(i=0; i<2; i++){ - /* Note: deframe_bits spits out bits in uint8_ts while varicode_decode expects shorts */ - vc_bit = vc_bits[i]; - n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, &vc_bit, 1, 1); - if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { - (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); - } - } - /* Decode the codec data */ - codec2_decode(f->codec2,speech_out,f->packed_codec_bits); - f->sync = 1; - f->stats.sync = 1; - - - } else { - /* Fill with silence */ - for(i=0;in_speech_samples;i++){ + int valid; + int nout = freedv_floatrx_fsk_2400(f, demod_in, &valid); + + if (valid == 0) + for (i = 0; i < nout; i++) speech_out[i] = 0; + else if (valid < 0) + for (i = 0; i < nout; i++) + speech_out[i] = demod_in[i]; + else { + int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + int frames = f->n_codec_bits / bits_per_codec_frame; + for (i = 0; i < frames; i++) { + codec2_decode(f->codec2, speech_out, f->packed_codec_bits + i * bytes_per_codec_frame); + speech_out += codec2_samples_per_frame(f->codec2); } - f->sync = 0; - f->stats.sync = 0; + } return f->n_speech_samples; }else { @@ -733,299 +825,379 @@ int freedv_floatrx(struct freedv *f, short speech_out[], float demod_in[]) { // complex input samples version -int freedv_comprx(struct freedv *f, short speech_out[], COMP demod_in[]) { - assert(f != NULL); +static int freedv_comprx_fdmdv_1600(struct freedv *f, COMP demod_in[], int *valid) { int bits_per_codec_frame, bytes_per_codec_frame, bits_per_fdmdv_frame; - int i, j, bit, byte, nin_prev, nout, k; - int recd_codeword, codeword1, data_flag_index, n_ascii, nspare; + int i, j, bit, byte, nin_prev, nout; + int recd_codeword, codeword1, data_flag_index, n_ascii; short abit[1]; char ascii_out; int reliable_sync_bit; - assert(f->nin <= f->n_max_modem_samples); - bits_per_codec_frame = codec2_bits_per_frame(f->codec2); bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; nout = f->n_speech_samples; - if (f->mode == FREEDV_MODE_1600) { + for(i=0; inin; i++) + demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); - for(i=0; inin; i++) - demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); + bits_per_fdmdv_frame = fdmdv_bits_per_frame(f->fdmdv); - bits_per_fdmdv_frame = fdmdv_bits_per_frame(f->fdmdv); + nin_prev = f->nin; + fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, demod_in, &f->nin); + fdmdv_get_demod_stats(f->fdmdv, &f->stats); + f->sync = f->fdmdv->sync; + f->snr_est = f->stats.snr_est; - nin_prev = f->nin; - fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, demod_in, &f->nin); - fdmdv_get_demod_stats(f->fdmdv, &f->stats); - f->sync = f->fdmdv->sync; - f->snr_est = f->stats.snr_est; + if (reliable_sync_bit == 1) { + f->evenframe = 1; + } - if (reliable_sync_bit == 1) { - f->evenframe = 1; + if (f->stats.sync) { + if (f->evenframe == 0) { + memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); + nout = 0; } + else { + memcpy(&f->rx_bits[bits_per_fdmdv_frame], f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); - if (f->stats.sync) { - if (f->evenframe == 0) { - memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); - nout = 0; - } - else { - memcpy(&f->rx_bits[bits_per_fdmdv_frame], f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); - - if (f->test_frames == 0) { - recd_codeword = 0; - for(i=0; i<8; i++) { - recd_codeword <<= 1; - recd_codeword |= (f->rx_bits[i] & 0x1); - } - for(i=11; i<15; i++) { - recd_codeword <<= 1; - recd_codeword |= (f->rx_bits[i] & 0x1); - } - for(i=bits_per_codec_frame; irx_bits[i] & 0x1); - } - codeword1 = golay23_decode(recd_codeword); - f->total_bit_errors += golay23_count_errors(recd_codeword, codeword1); - f->total_bits += 23; + if (f->test_frames == 0) { + recd_codeword = 0; + for(i=0; i<8; i++) { + recd_codeword <<= 1; + recd_codeword |= (f->rx_bits[i] & 0x1); + } + for(i=11; i<15; i++) { + recd_codeword <<= 1; + recd_codeword |= (f->rx_bits[i] & 0x1); + } + for(i=bits_per_codec_frame; irx_bits[i] & 0x1); + } + codeword1 = golay23_decode(recd_codeword); + f->total_bit_errors += golay23_count_errors(recd_codeword, codeword1); + f->total_bits += 23; - //codeword1 = recd_codeword; - //fprintf(stderr, "received codeword1: 0x%x decoded codeword1: 0x%x\n", recd_codeword, codeword1); + //codeword1 = recd_codeword; + //fprintf(stderr, "received codeword1: 0x%x decoded codeword1: 0x%x\n", recd_codeword, codeword1); - for(i=0; icodec_bits[i] = f->rx_bits[i]; + for(i=0; icodec_bits[i] = f->rx_bits[i]; - for(i=0; i<8; i++) { - f->codec_bits[i] = (codeword1 >> (22-i)) & 0x1; - } - for(i=8,j=11; i<12; i++,j++) { - f->codec_bits[j] = (codeword1 >> (22-i)) & 0x1; - } + for(i=0; i<8; i++) { + f->codec_bits[i] = (codeword1 >> (22-i)) & 0x1; + } + for(i=8,j=11; i<12; i++,j++) { + f->codec_bits[j] = (codeword1 >> (22-i)) & 0x1; + } - // extract txt msg data bit ------------------------------------------------------------ + // extract txt msg data bit ------------------------------------------------------------ - data_flag_index = codec2_get_spare_bit_index(f->codec2); - abit[0] = f->codec_bits[data_flag_index]; + data_flag_index = codec2_get_spare_bit_index(f->codec2); + abit[0] = f->codec_bits[data_flag_index]; - n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1); - if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { - (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); - } + n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); + } - // reconstruct missing bit we steal for data bit and decode speech + // reconstruct missing bit we steal for data bit and decode speech - codec2_rebuild_spare_bit(f->codec2, f->codec_bits); + codec2_rebuild_spare_bit(f->codec2, f->codec_bits); - // pack bits, MSB received first + // pack bits, MSB received first - bit = 7; - byte = 0; - memset(f->packed_codec_bits, 0, bytes_per_codec_frame); - for(i=0; ipacked_codec_bits[byte] |= (f->codec_bits[i] << bit); - bit--; - if(bit < 0) { - bit = 7; - byte++; - } + bit = 7; + byte = 0; + memset(f->packed_codec_bits, 0, bytes_per_codec_frame); + for(i=0; ipacked_codec_bits[byte] |= (f->codec_bits[i] << bit); + bit--; + if(bit < 0) { + bit = 7; + byte++; } - - codec2_decode(f->codec2, speech_out, f->packed_codec_bits); } - else { - int test_frame_sync, bit_errors, ntest_bits, k; - short error_pattern[fdmdv_error_pattern_size(f->fdmdv)]; + *valid = 1; + } + else { + int test_frame_sync, bit_errors, ntest_bits, k; + short error_pattern[fdmdv_error_pattern_size(f->fdmdv)]; - for(k=0; k<2; k++) { - /* test frames, so lets sync up to the test frames and count any errors */ - fdmdv_put_test_bits(f->fdmdv, &test_frame_sync, error_pattern, &bit_errors, &ntest_bits, &f->rx_bits[k*bits_per_fdmdv_frame]); + for(k=0; k<2; k++) { + /* test frames, so lets sync up to the test frames and count any errors */ + fdmdv_put_test_bits(f->fdmdv, &test_frame_sync, error_pattern, &bit_errors, &ntest_bits, &f->rx_bits[k*bits_per_fdmdv_frame]); - if (test_frame_sync == 1) { - f->test_frame_sync_state = 1; - f->test_frame_count = 0; - } + if (test_frame_sync == 1) { + f->test_frame_sync_state = 1; + f->test_frame_count = 0; + } - if (f->test_frame_sync_state) { - if (f->test_frame_count == 0) { - f->total_bit_errors += bit_errors; - f->total_bits += ntest_bits; - if (f->freedv_put_error_pattern != NULL) { - (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, fdmdv_error_pattern_size(f->fdmdv)); - } - } - f->test_frame_count++; - if (f->test_frame_count == 4) - f->test_frame_count = 0; + if (f->test_frame_sync_state) { + if (f->test_frame_count == 0) { + f->total_bit_errors += bit_errors; + f->total_bits += ntest_bits; + if (f->freedv_put_error_pattern != NULL) { + (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, fdmdv_error_pattern_size(f->fdmdv)); + } } - - //fprintf(stderr, "test_frame_sync: %d test_frame_sync_state: %d bit_errors: %d ntest_bits: %d\n", - // test_frame_sync, f->test_frame_sync_state, bit_errors, ntest_bits); + f->test_frame_count++; + if (f->test_frame_count == 4) + f->test_frame_count = 0; } + + //fprintf(stderr, "test_frame_sync: %d test_frame_sync_state: %d bit_errors: %d ntest_bits: %d\n", + // test_frame_sync, f->test_frame_sync_state, bit_errors, ntest_bits); } + } - /* squelch if beneath SNR threshold or test frames enabled */ + /* squelch if beneath SNR threshold or test frames enabled */ - if ((f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) || f->test_frames) { - //fprintf(stderr,"squelch %f %f !\n", f->stats.snr_est, f->snr_squelch_thresh); - for(i=0; in_speech_samples; i++) - speech_out[i] = 0; - } + if ((f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) || f->test_frames) { + //fprintf(stderr,"squelch %f %f !\n", f->stats.snr_est, f->snr_squelch_thresh); + *valid = 0; + } - nout = f->n_speech_samples; + nout = f->n_speech_samples; - } + } - /* note this freewheels if reliable sync dissapears on bad channels */ + /* note this freewheels if reliable sync dissapears on bad channels */ - if (f->evenframe) - f->evenframe = 0; - else - f->evenframe = 1; - //fprintf(stderr,"%d\n", f->evenframe); + if (f->evenframe) + f->evenframe = 0; + else + f->evenframe = 1; + //fprintf(stderr,"%d\n", f->evenframe); - } /* if (sync) .... */ - else { - /* if not in sync pass through analog samples */ - /* this lets us "hear" whats going on, e.g. during tuning */ + } /* if (sync) .... */ + else { + /* if not in sync pass through analog samples */ + /* this lets us "hear" whats going on, e.g. during tuning */ - //fprintf(stderr, "out of sync\n"); + //fprintf(stderr, "out of sync\n"); - if (f->squelch_en == 0) { - for(i=0; isquelch_en == 0) { + *valid = -1; + } + else { + *valid = 0; } + //fprintf(stderr, "%d %d %d\n", nin_prev, speech_out[0], speech_out[nin_prev-1]); + nout = nin_prev; } - + return nout; +} #ifndef CORTEX_M4 - if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) { - float rx_bits[COHPSK_BITS_PER_FRAME]; - int sync; +static int freedv_comprx_fdmdv_700(struct freedv *f, COMP demod_in[], 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; + short abit[1]; + char ascii_out; + float rx_bits[COHPSK_BITS_PER_FRAME]; + int sync; + int frames; - // echo samples back out as default (say if sync not found) + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + frames = f->n_codec_bits / bits_per_codec_frame; + nout = f->n_speech_samples; - for(i=0; iptFilter8000to7500, 15, 16); - //if (i != f->nin) - // printf("freedv_comprx decimation: input %d output %d\n", freedv_nin(f), i); + i = quisk_cfInterpDecim(demod_in, freedv_nin(f), f->ptFilter8000to7500, 15, 16); + //if (i != f->nin) + // printf("freedv_comprx decimation: input %d output %d\n", freedv_nin(f), i); - for(i=0; inin; i++) - demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); + for(i=0; inin; i++) + demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); - nin_prev = f->nin; - cohpsk_demod(f->cohpsk, rx_bits, &sync, demod_in, &f->nin); - f->sync = sync; - cohpsk_get_demod_stats(f->cohpsk, &f->stats); - f->snr_est = f->stats.snr_est; + cohpsk_demod(f->cohpsk, rx_bits, &sync, demod_in, &f->nin); + f->sync = sync; + cohpsk_get_demod_stats(f->cohpsk, &f->stats); + f->snr_est = f->stats.snr_est; - if (sync) { + memset(f->packed_codec_bits, 0, bytes_per_codec_frame * frames); - if (f->test_frames == 0) { - data_flag_index = codec2_get_spare_bit_index(f->codec2); + if (sync) { - /* optional smoothing of codec symbols */ + if (f->test_frames == 0) { + data_flag_index = codec2_get_spare_bit_index(f->codec2); - if (f->smooth_symbols) { + /* optional smoothing of codec symbols */ - for(i=0; ismooth_symbols) { + + for(i=0; imode == FREEDV_MODE_700) - nspare = 2; - else - nspare = 1; + if (f->mode == FREEDV_MODE_700) + nspare = 2; + else + nspare = 1; - for(k=0; kvaricode_dec_states, &ascii_out, abit, 1, 1); - if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { - (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); - } + n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, abit, 1, 1); + if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { + (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); } + } - /* pack bits, MSB received first */ + /* pack bits, MSB received first */ - bit = 7; byte = 0; - memset(f->packed_codec_bits, 0, bytes_per_codec_frame); - for(i=0; ipacked_codec_bits[byte] |= ((rx_bits[j+i] < 0.0) << bit); - bit--; - if (bit < 0) { - bit = 7; - byte++; - } + bit = 7; + for(i=0; ipacked_codec_bits[byte] |= ((rx_bits[j+i] < 0.0) << bit); + bit--; + if (bit < 0) { + bit = 7; + byte++; } + } + if (bit != 7) + byte++; - codec2_decode(f->codec2, speech_out, f->packed_codec_bits); - - if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { - for(i=0; in_speech_samples; i++) - speech_out[i] = 0; - } - speech_out += codec2_samples_per_frame(f->codec2); + if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { + *valid = 0; } - nout = f->n_speech_samples; + *valid = 1; } - else { - short error_pattern[COHPSK_BITS_PER_FRAME]; - int bit_errors; + nout = f->n_speech_samples; + } + else { + short error_pattern[COHPSK_BITS_PER_FRAME]; + int bit_errors; - /* test data, lets see if we can sync to the test data sequence */ + /* test data, lets see if we can sync to the test data sequence */ - cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors, rx_bits); - if (f->test_frame_sync_state) { - f->total_bit_errors += bit_errors; - f->total_bits += COHPSK_BITS_PER_FRAME; - if (f->freedv_put_error_pattern != NULL) { - (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, COHPSK_BITS_PER_FRAME); - } + cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors, rx_bits); + if (f->test_frame_sync_state) { + f->total_bit_errors += bit_errors; + f->total_bits += COHPSK_BITS_PER_FRAME; + if (f->freedv_put_error_pattern != NULL) { + (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, COHPSK_BITS_PER_FRAME); } - - for(i=0; in_speech_samples; i++) - speech_out[i] = 0; - nout = f->n_speech_samples; } - + + *valid = 0; + nout = f->n_speech_samples; } + } - /* no valid FreeDV signal - squelch output */ - if (sync == 0) { - nout = freedv_nin(f); - if (f->squelch_en) { - for(i=0; isquelch_en) { + *valid = 0; } + } + return nout; +} +#endif + + +int freedv_comprx(struct freedv *f, short speech_out[], COMP demod_in[]) { + assert(f != NULL); + int bits_per_codec_frame, bytes_per_codec_frame; + int i, nout = 0; + int valid; + + assert(f->nin <= f->n_max_modem_samples); + bits_per_codec_frame = codec2_bits_per_frame(f->codec2); + bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; + + if (f->mode == FREEDV_MODE_1600) { + nout = freedv_comprx_fdmdv_1600(f, demod_in, &valid); + } +#ifndef CORTEX_M4 + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) { + nout = freedv_comprx_fdmdv_700(f, demod_in, &valid); } #endif + if (valid == 0) + for (i = 0; i < nout; i++) + speech_out[i] = 0; + else if (valid < 0) + for (i = 0; i < nout; i++) + speech_out[i] = FDMDV_SCALE*demod_in[i].real; + else { + int frames = f->n_codec_bits / bits_per_codec_frame; + for (i = 0; i < frames; i++) { + codec2_decode(f->codec2, speech_out, f->packed_codec_bits + i * bytes_per_codec_frame); + speech_out += codec2_samples_per_frame(f->codec2); + } + } + //fprintf(stderr,"freedv_nin(f): %d nout: %d\n", freedv_nin(f), nout); return nout; } +int freedv_codecrx(struct freedv *f, unsigned char *packed_codec_bits, short demod_in[]) +{ + assert(f != NULL); + COMP rx_fdm[f->n_max_modem_samples]; + int i; + int nin = freedv_nin(f); + int valid; + int ret = 0; + float rx_float[f->n_max_modem_samples]; + + assert(nin <= f->n_max_modem_samples); + + /* FSK RX happens in real floats, so convert to those and call their demod here */ + if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) ){ + for(i=0; imode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) ){ + freedv_floatrx_fsk_2400(f, rx_float, &valid); + } + if (f->mode == FREEDV_MODE_1600) { + freedv_comprx_fdmdv_1600(f, rx_fdm, &valid); + } +#ifndef CORTEX_M4 + if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)) { + freedv_comprx_fdmdv_700(f, rx_fdm, &valid); + } +#endif + + if (valid == 0) { + int bytes_per_frame; + bytes_per_frame = (f->n_codec_bits + 7) / 8; + + memcpy(packed_codec_bits, f->packed_codec_bits, bytes_per_frame); + ret = bytes_per_frame; + } + + return ret; +} + /*---------------------------------------------------------------------------*\ FUNCTION....: freedv_get_version @@ -1156,7 +1328,7 @@ int freedv_get_protocol_bits (struct freedv *f) {return f->n_proto int freedv_get_mode (struct freedv *f) {return f->mode;} int freedv_get_test_frames (struct freedv *f) {return f->test_frames;} int freedv_get_n_speech_samples (struct freedv *f) {return f->n_speech_samples;} -int freedv_get_modem_sample_rate (struct freedv *f) {return FS;} +int freedv_get_modem_sample_rate (struct freedv *f) {return f->modem_sample_rate;} int freedv_get_n_max_modem_samples (struct freedv *f) {return f->n_max_modem_samples;} int freedv_get_n_nom_modem_samples (struct freedv *f) {return f->n_nom_modem_samples;} int freedv_get_total_bits (struct freedv *f) {return f->total_bits;} @@ -1166,6 +1338,7 @@ int freedv_get_sz_error_pattern (struct freedv *f) {return f->sz_erro // Get floats struct CODEC2 *freedv_get_codec2 (struct freedv *f){return f->codec2;} +int freedv_get_n_codec_bits (struct freedv *f){return f->n_codec_bits;} void freedv_get_modem_extended_stats(struct freedv *f, struct MODEM_STATS *stats) { diff --git a/codec2-dev/src/freedv_api.h b/codec2-dev/src/freedv_api.h index 80521a08..a9805781 100644 --- a/codec2-dev/src/freedv_api.h +++ b/codec2-dev/src/freedv_api.h @@ -65,11 +65,13 @@ void freedv_close (struct freedv *freedv); // Transmit void freedv_tx (struct freedv *freedv, short mod_out[], short speech_in[]); void freedv_comptx (struct freedv *freedv, COMP mod_out[], short speech_in[]); +void freedv_codectx (struct freedv *f, short mod_out[], unsigned char *packed_codec_bits); // Receive int freedv_nin (struct freedv *freedv); int freedv_rx (struct freedv *freedv, short speech_out[], short demod_in[]); int freedv_floatrx (struct freedv *freedv, short speech_out[], float demod_in[]); int freedv_comprx (struct freedv *freedv, short speech_out[], COMP demod_in[]); +int freedv_codecrx (struct freedv *freedv, unsigned char *packed_codec_bits, short demod_in[]); // Set parameters 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); @@ -97,6 +99,7 @@ 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 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); #endif diff --git a/codec2-dev/src/freedv_api_internal.h b/codec2-dev/src/freedv_api_internal.h index 0b577529..0a0cfe31 100644 --- a/codec2-dev/src/freedv_api_internal.h +++ b/codec2-dev/src/freedv_api_internal.h @@ -89,6 +89,7 @@ struct freedv { int tx_sync_bit; int smooth_symbols; float *prev_rx_bits; + int n_codec_bits; // amount of codec bits in a frame int *ptest_bits_coh; int *ptest_bits_coh_end; -- 2.25.1