| 
									
										
										
										
											2019-03-04 23:04:27 +01:00
										 |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FILE........: freedv_api.c | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: August 2014 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Library of API functions that implement FreeDV "modes", useful for | 
					
						
							|  |  |  |   embedding FreeDV in other programs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   Copyright (C) 2014 David Rowe | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |   it under the terms of the GNU Lesser General Public License version 2.1, as | 
					
						
							|  |  |  |   published by the Free Software Foundation.  This program is | 
					
						
							|  |  |  |   distributed in the hope that it will be useful, but WITHOUT ANY | 
					
						
							|  |  |  |   WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public | 
					
						
							|  |  |  |   License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  |   along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <stdint.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <math.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef TT
 | 
					
						
							|  |  |  | #if defined(__APPLE__)
 | 
					
						
							|  |  |  | #include <malloc/malloc.h>
 | 
					
						
							|  |  |  | #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__)
 | 
					
						
							|  |  |  | #include <sys/malloc.h>
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #include <malloc.h>
 | 
					
						
							|  |  |  | #endif /* __APPLE__ */
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "fsk.h"
 | 
					
						
							|  |  |  | #include "fmfsk.h"
 | 
					
						
							|  |  |  | #include "codec2/codec2.h"
 | 
					
						
							|  |  |  | #include "codec2_fdmdv.h"
 | 
					
						
							|  |  |  | #include "fdmdv_internal.h"
 | 
					
						
							|  |  |  | #include "codec2/golay23.h"
 | 
					
						
							|  |  |  | #include "codec2/varicode.h"
 | 
					
						
							|  |  |  | #include "libfreedv.h"
 | 
					
						
							|  |  |  | #include "freedv_api_internal.h"
 | 
					
						
							|  |  |  | #include "freedv_vhf_framing.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-05 03:36:04 +01:00
										 |  |  | #include "comp_prim.h"
 | 
					
						
							| 
									
										
										
										
											2019-03-04 23:04:27 +01:00
										 |  |  | #include "freedv_filter.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "codec2_ofdm.h"
 | 
					
						
							|  |  |  | #include "ofdm_internal.h"
 | 
					
						
							|  |  |  | #include "mpdecode_core.h"
 | 
					
						
							|  |  |  | #include "gp_interleaver.h"
 | 
					
						
							|  |  |  | #include "interldpc.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define VERSION     12    /* 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. */ | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Version 10   Initial version August 2, 2015. | 
					
						
							|  |  |  |  * Version 11   September 2015 | 
					
						
							|  |  |  |  *              Added: freedv_zero_total_bit_errors(), freedv_get_sync() | 
					
						
							|  |  |  |  *              Changed all input and output sample rates to 8000 sps.  Rates for FREEDV_MODE_700 and 700B were 7500. | 
					
						
							|  |  |  |  * Version 12   August 2018 | 
					
						
							|  |  |  |  *              Added OFDM configuration switch structure | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* experimentally derived fudge factors to normalise power across modes */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define NORM_PWR_COHPSK  1.74
 | 
					
						
							|  |  |  | #define NORM_PWR_FSK     0.193
 | 
					
						
							|  |  |  | #define NORM_PWR_OFDM    1.00
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FreeDV | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct OFDM_CONFIG *ofdm_config; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ofdm_bitsperframe; | 
					
						
							|  |  |  | static int ofdm_nuwbits; | 
					
						
							|  |  |  | static int ofdm_ntxtbits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char *statemode[] = { | 
					
						
							|  |  |  |     "search", | 
					
						
							|  |  |  |     "trial", | 
					
						
							|  |  |  |     "synced" | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_open | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 3 August 2014 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Call this first to initialise.  Returns NULL if initialisation fails | 
					
						
							|  |  |  |   (e.g. out of memory or mode not supported). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct freedv *freedv_open(int mode) { | 
					
						
							|  |  |  |     return freedv_open_advanced(mode, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct freedv *freedv_open_advanced(int mode, struct freedv_advanced *adv) { | 
					
						
							|  |  |  |     struct freedv *f; | 
					
						
							|  |  |  |     int            Nc, codec2_mode, nbit, nbyte; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mode != FREEDV_MODE_1600) && (mode != FREEDV_MODE_700) && | 
					
						
							|  |  |  |         (mode != FREEDV_MODE_700B) && (mode != FREEDV_MODE_2400A) && | 
					
						
							|  |  |  |         (mode != FREEDV_MODE_2400B) && (mode != FREEDV_MODE_800XA) && | 
					
						
							|  |  |  |         (mode != FREEDV_MODE_700C) && (mode != FREEDV_MODE_700D) ) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f = (struct freedv*) malloc(sizeof(struct freedv)); | 
					
						
							|  |  |  |     if (f == NULL) | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f->mode = mode; | 
					
						
							|  |  |  |     f->verbose = 0; | 
					
						
							|  |  |  |     f->test_frames = f->smooth_symbols = 0; | 
					
						
							|  |  |  |     f->freedv_put_error_pattern = NULL; | 
					
						
							|  |  |  |     f->error_pattern_callback_state = NULL; | 
					
						
							|  |  |  |     f->n_protocol_bits = 0; | 
					
						
							|  |  |  |     f->frames = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Init states for this mode, and set up samples in/out -----------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mode == FREEDV_MODE_1600) { | 
					
						
							|  |  |  |         f->snr_squelch_thresh = 2.0; | 
					
						
							|  |  |  |         f->squelch_en = 1; | 
					
						
							|  |  |  |         Nc = 16; | 
					
						
							|  |  |  |         f->tx_sync_bit = 0; | 
					
						
							|  |  |  |         codec2_mode = CODEC2_MODE_1300; | 
					
						
							|  |  |  |         f->fdmdv = fdmdv_create(Nc); | 
					
						
							|  |  |  |         if (f->fdmdv == NULL) | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         golay23_init(); | 
					
						
							|  |  |  |         f->nin = FDMDV_NOM_SAMPLES_PER_FRAME; | 
					
						
							|  |  |  |         f->n_nom_modem_samples = 2*FDMDV_NOM_SAMPLES_PER_FRAME; | 
					
						
							|  |  |  |         f->n_nat_modem_samples = f->n_nom_modem_samples; | 
					
						
							|  |  |  |         f->n_max_modem_samples = FDMDV_NOM_SAMPLES_PER_FRAME+FDMDV_MAX_SAMPLES_PER_FRAME; | 
					
						
							|  |  |  |         f->modem_sample_rate = FS; | 
					
						
							|  |  |  |         nbit = fdmdv_bits_per_frame(f->fdmdv); | 
					
						
							|  |  |  |         f->fdmdv_bits = (int*) malloc(nbit*sizeof(int)); | 
					
						
							|  |  |  |         if (f->fdmdv_bits == NULL) | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         nbit = 2*fdmdv_bits_per_frame(f->fdmdv); | 
					
						
							|  |  |  |         f->tx_bits = (int*) malloc(nbit*sizeof(int)); | 
					
						
							|  |  |  |         f->rx_bits = (int*) malloc(nbit*sizeof(int)); | 
					
						
							|  |  |  |         if ((f->tx_bits == NULL) || (f->rx_bits == NULL)) { | 
					
						
							|  |  |  |             if (f->tx_bits != NULL) { | 
					
						
							|  |  |  |               free(f->tx_bits); | 
					
						
							|  |  |  |               f->tx_bits = NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (f->rx_bits != NULL) { | 
					
						
							|  |  |  |                 free(f->rx_bits); | 
					
						
							|  |  |  |               f->rx_bits = NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         f->evenframe = 0; | 
					
						
							|  |  |  |         f->sz_error_pattern = fdmdv_error_pattern_size(f->fdmdv); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |         f->snr_squelch_thresh = 0.0; | 
					
						
							|  |  |  |         f->squelch_en = 0; | 
					
						
							|  |  |  |         switch(mode) { | 
					
						
							|  |  |  |         case FREEDV_MODE_700: | 
					
						
							|  |  |  |             codec2_mode = CODEC2_MODE_700; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700B: | 
					
						
							|  |  |  |             codec2_mode = CODEC2_MODE_700B; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700C: | 
					
						
							|  |  |  |             codec2_mode = CODEC2_MODE_700C; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							| 
									
										
										
										
											2019-03-05 10:45:08 +01:00
										 |  |  |             codec2_mode = CODEC2_MODE_700C; | 
					
						
							|  |  |  |             fprintf(stderr, "FreeDV::freedv_open_advanced: unknown mode default to FREEDV_MODE_700C"); | 
					
						
							| 
									
										
										
										
											2019-03-04 23:04:27 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->cohpsk = cohpsk_create(); | 
					
						
							|  |  |  |         f->nin = COHPSK_NOM_SAMPLES_PER_FRAME; | 
					
						
							|  |  |  |         f->n_nat_modem_samples = COHPSK_NOM_SAMPLES_PER_FRAME;             // native modem samples as used by the modem
 | 
					
						
							|  |  |  |         f->n_nom_modem_samples = f->n_nat_modem_samples * FS / COHPSK_FS;  // number of samples after native samples are interpolated to 8000 sps
 | 
					
						
							|  |  |  |         f->n_max_modem_samples = COHPSK_MAX_SAMPLES_PER_FRAME * FS / COHPSK_FS + 1; | 
					
						
							|  |  |  |         f->modem_sample_rate = FS;                                         /* note wierd sample rate tamed by interpolator */ | 
					
						
							|  |  |  |         f->clip = 1; | 
					
						
							|  |  |  |         nbit = COHPSK_BITS_PER_FRAME; | 
					
						
							|  |  |  |         f->tx_bits = (int*) malloc(nbit*sizeof(int)); | 
					
						
							|  |  |  |         if (f->tx_bits == NULL) | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         f->sz_error_pattern = cohpsk_error_pattern_size(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         /*
 | 
					
						
							|  |  |  |           TODO: | 
					
						
							|  |  |  |             [ ] how to set up interleaver, prob init time option best, as many arrays depend on it | 
					
						
							|  |  |  |             [ ] clip option?  Haven't tried clipping OFDM waveform yet | 
					
						
							|  |  |  |             [ ] support for uncoded and coded error patterns | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->snr_squelch_thresh = 0.0; | 
					
						
							|  |  |  |         f->squelch_en = 0; | 
					
						
							|  |  |  |         codec2_mode = CODEC2_MODE_700C; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ((ofdm_config = (struct OFDM_CONFIG *) calloc(1, sizeof (struct OFDM_CONFIG))) == NULL) { | 
					
						
							|  |  |  |             if (f->tx_bits != NULL) { | 
					
						
							|  |  |  |               free(f->tx_bits); | 
					
						
							|  |  |  |               f->tx_bits = NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (f->rx_bits != NULL) { | 
					
						
							|  |  |  |               free(f->rx_bits); | 
					
						
							|  |  |  |               f->rx_bits = NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->ofdm = ofdm_create(ofdm_config); | 
					
						
							|  |  |  |         free(ofdm_config); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Get a copy of the actual modem config */ | 
					
						
							|  |  |  |         ofdm_config = ofdm_get_config_param(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ofdm_bitsperframe = ofdm_get_bits_per_frame(); | 
					
						
							|  |  |  |         ofdm_nuwbits = (ofdm_config->ns - 1) * ofdm_config->bps - ofdm_config->txtbits; | 
					
						
							|  |  |  |         ofdm_ntxtbits = ofdm_config->txtbits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->ldpc = (struct LDPC*) malloc(sizeof(struct LDPC)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->ldpc == NULL) { | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         set_up_hra_112_112(f->ldpc, ofdm_config); | 
					
						
							|  |  |  |         int coded_syms_per_frame = f->ldpc->coded_syms_per_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (adv == NULL) { | 
					
						
							|  |  |  |             f->interleave_frames = 1; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             assert((adv->interleave_frames >= 0) && (adv->interleave_frames <= 16)); | 
					
						
							|  |  |  |             f->interleave_frames = adv->interleave_frames; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->modem_frame_count_tx = f->modem_frame_count_rx = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->codeword_symbols = (COMP*) malloc(sizeof(COMP)*f->interleave_frames*coded_syms_per_frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->codeword_symbols == NULL) {return NULL;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->codeword_amps = (float*) malloc(sizeof(float)*f->interleave_frames*coded_syms_per_frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->codeword_amps == NULL) {free(f->codeword_symbols); return NULL;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (int i=0; i<f->interleave_frames*coded_syms_per_frame; i++) { | 
					
						
							|  |  |  |             f->codeword_symbols[i].real = 0.0; | 
					
						
							|  |  |  |             f->codeword_symbols[i].imag = 0.0; | 
					
						
							|  |  |  |             f->codeword_amps[i] = 0.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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(); | 
					
						
							|  |  |  |         f->n_max_modem_samples = ofdm_get_max_samples_per_frame(); | 
					
						
							|  |  |  |         f->modem_sample_rate = ofdm_config->fs; | 
					
						
							|  |  |  |         f->clip = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         nbit = f->sz_error_pattern = ofdm_bitsperframe; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->tx_bits = NULL; /* not used for 700D */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (f->interleave_frames > 1) { | 
					
						
							|  |  |  |             /* only allocate this array for interleaver sizes > 1 to save memory on SM1000 port */ | 
					
						
							|  |  |  |             f->mod_out = (COMP*) malloc(sizeof(COMP)*f->interleave_frames*f->n_nat_modem_samples); | 
					
						
							|  |  |  |             if (f->mod_out == NULL) { | 
					
						
							|  |  |  |                 if (f->codeword_symbols != NULL) { free(f->codeword_symbols); } | 
					
						
							|  |  |  |                 return NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (int i=0; i<f->interleave_frames*f->n_nat_modem_samples; i++) { | 
					
						
							|  |  |  |                 f->mod_out[i].real = 0.0; | 
					
						
							|  |  |  |                 f->mod_out[i].imag = 0.0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef __EMBEDDED__
 | 
					
						
							|  |  |  |         /* tx BPF on by default, can't see any reason we'd want this off */ | 
					
						
							|  |  |  |         ofdm_set_tx_bpf(f->ofdm, 1); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Set up the C2 mode */ | 
					
						
							|  |  |  |         codec2_mode = CODEC2_MODE_1300; | 
					
						
							|  |  |  |         /* Set the number of protocol bits */ | 
					
						
							|  |  |  |         f->n_protocol_bits = 20; | 
					
						
							|  |  |  |         f->sz_error_pattern = 0; | 
					
						
							|  |  |  |         f->ext_vco = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mode == FREEDV_MODE_2400A) { | 
					
						
							|  |  |  |         /* Create the framer|deframer */ | 
					
						
							|  |  |  |         f->deframer = fvhff_create_deframer(FREEDV_VHF_FRAME_A,0); | 
					
						
							|  |  |  |         if(f->deframer == NULL) | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->fsk = fsk_create_hbr(48000,1200,10,4,1200,1200); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ | 
					
						
							|  |  |  |         f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(f->fsk == NULL){ | 
					
						
							|  |  |  |             fvhff_destroy_deframer(f->deframer); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->n_nom_modem_samples = f->fsk->N; | 
					
						
							|  |  |  |         f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); | 
					
						
							|  |  |  |         f->n_nat_modem_samples = f->fsk->N; | 
					
						
							|  |  |  |         f->nin = fsk_nin(f->fsk); | 
					
						
							|  |  |  |         f->modem_sample_rate = 48000; | 
					
						
							|  |  |  |         f->modem_symbol_rate = 1200; | 
					
						
							|  |  |  |         /* Malloc something to appease freedv_init and freedv_destroy */ | 
					
						
							|  |  |  |         f->codec_bits = (int*) malloc(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mode == FREEDV_MODE_2400B) { | 
					
						
							|  |  |  |         /* Create the framer|deframer */ | 
					
						
							|  |  |  |         f->deframer = fvhff_create_deframer(FREEDV_VHF_FRAME_A,1); | 
					
						
							|  |  |  |         if(f->deframer == NULL) { | 
					
						
							|  |  |  |             if (f->codec_bits != NULL) { free(f->codec_bits); } | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->fmfsk = fmfsk_create(48000,2400); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(f->fmfsk == NULL){ | 
					
						
							|  |  |  |             fvhff_destroy_deframer(f->deframer); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ | 
					
						
							|  |  |  |         f->tx_bits = (int*) malloc(f->fmfsk->nbit*sizeof(uint8_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->n_nom_modem_samples = f->fmfsk->N; | 
					
						
							|  |  |  |         f->n_max_modem_samples = f->fmfsk->N + (f->fmfsk->Ts); | 
					
						
							|  |  |  |         f->n_nat_modem_samples = f->fmfsk->N; | 
					
						
							|  |  |  |         f->nin = fmfsk_nin(f->fmfsk); | 
					
						
							|  |  |  |         f->modem_sample_rate = 48000; | 
					
						
							|  |  |  |         /* Malloc something to appease freedv_init and freedv_destroy */ | 
					
						
							|  |  |  |         f->codec_bits = (int*) malloc(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (mode == FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |         /* Create the framer|deframer */ | 
					
						
							|  |  |  |         f->deframer = fvhff_create_deframer(FREEDV_HF_FRAME_B,0); | 
					
						
							|  |  |  |         if(f->deframer == NULL) | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->fsk = fsk_create_hbr(8000,400,10,4,800,400); | 
					
						
							|  |  |  |         fsk_set_nsym(f->fsk,32); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ | 
					
						
							|  |  |  |         f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(f->fsk == NULL){ | 
					
						
							|  |  |  |             fvhff_destroy_deframer(f->deframer); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->n_nom_modem_samples = f->fsk->N; | 
					
						
							|  |  |  |         f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); | 
					
						
							|  |  |  |         f->n_nat_modem_samples = f->fsk->N; | 
					
						
							|  |  |  |         f->nin = fsk_nin(f->fsk); | 
					
						
							|  |  |  |         f->modem_sample_rate = 8000; | 
					
						
							|  |  |  |         f->modem_symbol_rate = 400; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* Malloc something to appease freedv_init and freedv_destroy */ | 
					
						
							|  |  |  |         f->codec_bits = (int*) malloc(1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->n_protocol_bits = 0; | 
					
						
							|  |  |  |         codec2_mode = CODEC2_MODE_700C; | 
					
						
							|  |  |  |         fsk_stats_normalise_eye(f->fsk, 0); | 
					
						
							|  |  |  |         f->sz_error_pattern = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Init test frame states */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f->test_frames_diversity = 1; | 
					
						
							|  |  |  |     f->test_frame_sync_state = 0; | 
					
						
							|  |  |  |     f->test_frame_sync_state_upper = 0; | 
					
						
							|  |  |  |     f->total_bits = 0; | 
					
						
							|  |  |  |     f->total_bit_errors = 0; | 
					
						
							|  |  |  |     f->total_bits_coded = 0; | 
					
						
							|  |  |  |     f->total_bit_errors_coded = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Init Codec 2 for this FreeDV 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 | 
					
						
							|  |  |  |        be OK.... */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mode == FREEDV_MODE_1600) || (mode == FREEDV_MODE_2400A) || (mode == FREEDV_MODE_2400B)) { | 
					
						
							|  |  |  |         f->n_speech_samples = codec2_samples_per_frame(f->codec2); | 
					
						
							|  |  |  |         f->n_codec_bits = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |         nbit = f->n_codec_bits; | 
					
						
							|  |  |  |         nbyte = (nbit + 7) / 8; | 
					
						
							|  |  |  |     } else if (mode == FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |         f->n_speech_samples = 2*codec2_samples_per_frame(f->codec2); | 
					
						
							|  |  |  |         f->n_codec_bits = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |         nbit = f->n_codec_bits; | 
					
						
							|  |  |  |         nbyte = (nbit + 7) / 8; | 
					
						
							|  |  |  |         nbyte = nbyte*2; | 
					
						
							|  |  |  |         nbit = 8*nbyte; | 
					
						
							|  |  |  |         f->n_codec_bits = nbit; | 
					
						
							|  |  |  |     } else if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |         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); | 
					
						
							|  |  |  |     } else /* mode == FREEDV_MODE_700D */ { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* should be exactly an integer number ofCodec 2 frames in a OFDM modem frame */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->n_speech_samples = Ncodec2frames*codec2_samples_per_frame(f->codec2); | 
					
						
							|  |  |  |         f->n_codec_bits = f->interleave_frames*Ncodec2frames*codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |         nbit = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |         nbyte = (nbit + 7) / 8; | 
					
						
							|  |  |  |         nbyte = nbyte*Ncodec2frames*f->interleave_frames; | 
					
						
							|  |  |  |         f->nbyte_packed_codec_bits = nbyte; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //fprintf(stderr, "Ncodec2frames: %d n_speech_samples: %d n_codec_bits: %d nbit: %d  nbyte: %d\n",
 | 
					
						
							|  |  |  |         //        Ncodec2frames, f->n_speech_samples, f->n_codec_bits, nbit, nbyte);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->packed_codec_bits_tx = (unsigned char*) malloc(nbyte*sizeof(char)); | 
					
						
							|  |  |  |         if (f->packed_codec_bits_tx == NULL) return(NULL); | 
					
						
							|  |  |  |         f->codec_bits = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f->packed_codec_bits = (unsigned char*) malloc(nbyte*sizeof(char)); | 
					
						
							|  |  |  |     if (f->packed_codec_bits == NULL) return(NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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)) | 
					
						
							|  |  |  |         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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->packed_codec_bits == NULL) { | 
					
						
							|  |  |  |         if (f->codec_bits != NULL) {free(f->codec_bits); f->codec_bits = NULL; } | 
					
						
							|  |  |  |         return NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Sample rate conversion for modes using COHPSK */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((mode == FREEDV_MODE_700) || (mode == FREEDV_MODE_700B) || (mode == FREEDV_MODE_700C) ) { | 
					
						
							|  |  |  |         f->ptFilter7500to8000 = (struct quisk_cfFilter *) malloc(sizeof(struct quisk_cfFilter)); | 
					
						
							|  |  |  |         f->ptFilter8000to7500 = (struct quisk_cfFilter *) malloc(sizeof(struct quisk_cfFilter)); | 
					
						
							|  |  |  |         quisk_filt_cfInit(f->ptFilter8000to7500, quiskFilt120t480, sizeof(quiskFilt120t480)/sizeof(float)); | 
					
						
							|  |  |  |         quisk_filt_cfInit(f->ptFilter7500to8000, quiskFilt120t480, sizeof(quiskFilt120t480)/sizeof(float)); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         f->ptFilter7500to8000 = NULL; | 
					
						
							|  |  |  |         f->ptFilter8000to7500 = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Varicode low bit rate text states */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     varicode_decode_init(&f->varicode_dec_states, 1); | 
					
						
							|  |  |  |     f->nvaricode_bits = 0; | 
					
						
							|  |  |  |     f->varicode_bit_index = 0; | 
					
						
							|  |  |  |     f->freedv_get_next_tx_char = NULL; | 
					
						
							|  |  |  |     f->freedv_put_next_rx_char = NULL; | 
					
						
							|  |  |  |     f->freedv_put_next_proto = NULL; | 
					
						
							|  |  |  |     f->freedv_get_next_proto = NULL; | 
					
						
							|  |  |  |     f->total_bit_errors = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return f; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_close | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 3 August 2014 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Frees up memory. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_close(struct freedv *freedv) { | 
					
						
							|  |  |  |     assert(freedv != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(freedv->packed_codec_bits); | 
					
						
							|  |  |  |     free(freedv->codec_bits); | 
					
						
							|  |  |  |     free(freedv->tx_bits); | 
					
						
							|  |  |  |     if (freedv->mode == FREEDV_MODE_1600) | 
					
						
							|  |  |  |         fdmdv_destroy(freedv->fdmdv); | 
					
						
							|  |  |  |     if ((freedv->mode == FREEDV_MODE_700) || (freedv->mode == FREEDV_MODE_700B) || (freedv->mode == FREEDV_MODE_700C)) | 
					
						
							|  |  |  |         cohpsk_destroy(freedv->cohpsk); | 
					
						
							|  |  |  |     if (freedv->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         free(freedv->packed_codec_bits_tx); | 
					
						
							|  |  |  |         if (freedv->interleave_frames > 1) | 
					
						
							|  |  |  |             free(freedv->mod_out); | 
					
						
							|  |  |  |         free(freedv->codeword_symbols); | 
					
						
							|  |  |  |         free(freedv->codeword_amps); | 
					
						
							|  |  |  |         free(freedv->ldpc); | 
					
						
							|  |  |  |         ofdm_destroy(freedv->ofdm); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ((freedv->mode == FREEDV_MODE_2400A) || (freedv->mode == FREEDV_MODE_800XA)){ | 
					
						
							|  |  |  |         fsk_destroy(freedv->fsk); | 
					
						
							|  |  |  |         fvhff_destroy_deframer(freedv->deframer); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (freedv->mode == FREEDV_MODE_2400B){ | 
					
						
							|  |  |  |         fmfsk_destroy(freedv->fmfsk); | 
					
						
							|  |  |  | 		fvhff_destroy_deframer(freedv->deframer); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     codec2_destroy(freedv->codec2); | 
					
						
							|  |  |  |     if (freedv->ptFilter8000to7500) { | 
					
						
							|  |  |  |         quisk_filt_destroy(freedv->ptFilter8000to7500); | 
					
						
							|  |  |  |         free(freedv->ptFilter8000to7500); | 
					
						
							|  |  |  |         freedv->ptFilter8000to7500 = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (freedv->ptFilter7500to8000) { | 
					
						
							|  |  |  |         quisk_filt_destroy(freedv->ptFilter7500to8000); | 
					
						
							|  |  |  |         free(freedv->ptFilter7500to8000); | 
					
						
							|  |  |  |         freedv->ptFilter7500to8000 = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     free(freedv); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_tx | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 3 August 2014 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Takes a frame of input speech samples, encodes and modulates them to | 
					
						
							|  |  |  |   produce a frame of modem samples that can be sent to the | 
					
						
							|  |  |  |   transmitter.  See freedv_tx.c for an example. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   speech_in[] is sampled at 8000 Hz, and the user must supply a block | 
					
						
							|  |  |  |   of exactly freedv_get_n_speech_samples(). The speech_in[] level | 
					
						
							|  |  |  |   should be such that the peak speech level is between +/- 16384 and | 
					
						
							|  |  |  |   +/- 32767. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The FDM modem signal mod_out[] is sampled at 8000 Hz and is | 
					
						
							|  |  |  |   freedv_get_n_nom_modem_samples() long.  mod_out[] will be scaled | 
					
						
							|  |  |  |   such that the peak level is just less than +/-32767. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The complex-valued output can directly drive an I/Q modulator to | 
					
						
							|  |  |  |   produce a single sideband signal.  To generate the other sideband, | 
					
						
							|  |  |  |   take the complex conjugate of mod_out[]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The FreeDV 1600 modem has a high crest factor (around 12dB), however | 
					
						
							|  |  |  |   the energy and duration of the peaks is small.  FreeDV 1600 is | 
					
						
							|  |  |  |   usually operated at a "backoff" of 8dB.  Adjust the power amplifier | 
					
						
							|  |  |  |   drive so that the average power is 8dB less than the peak power of | 
					
						
							|  |  |  |   the PA.  For example, on a radio rated at 100W PEP for SSB, the | 
					
						
							|  |  |  |   average FreeDV power is typically 20W. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The FreeDV 700 modem has a crest factor of about 8dB (with | 
					
						
							|  |  |  |   f->clip=1, the default), so if your PA can handle it, it can be | 
					
						
							|  |  |  |   driven harder than FreeDV 1600.  Caution - some PAs cannot handle a | 
					
						
							|  |  |  |   high continuous power.  A conservative level is 20W average for a | 
					
						
							|  |  |  |   100W PEP rated PA. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* 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_voice(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 */ | 
					
						
							|  |  |  |     uint8_t proto_bits[3]; /* Prococol bits for 2400 framing */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Frame for 2400A/B */ | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B){ | 
					
						
							|  |  |  |         /* 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_proto != NULL){ | 
					
						
							|  |  |  |             (*f->freedv_get_next_proto)(f->proto_callback_state,(char*)proto_bits); | 
					
						
							|  |  |  |             fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),proto_bits,vc_bits); | 
					
						
							|  |  |  |         }else 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,vc_bits); | 
					
						
							|  |  |  |         }else { | 
					
						
							|  |  |  |             fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     /* Frame for 800XA */ | 
					
						
							|  |  |  |     }else if(f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  |         fvhff_frame_bits(FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Allocate floating point buffer for FSK mod */ | 
					
						
							|  |  |  |     tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* do 4fsk mod */ | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  |         if (f->ext_vco) { | 
					
						
							|  |  |  |             fsk_mod_ext_vco(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  |             for(i=0; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |                 mod_out[i] = (short)tx_float[i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  |             /* Convert float samps to short */ | 
					
						
							|  |  |  |             for(i=0; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |                 mod_out[i] = (short)(tx_float[i]*FSK_SCALE*NORM_PWR_FSK); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     /* 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; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |             mod_out[i] = (short)(tx_float[i]*FMFSK_SCALE); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(tx_float); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TX routines for 2400 FSK modes, after codec2 encoding */ | 
					
						
							|  |  |  | static void freedv_comptx_fsk_voice(struct freedv *f, COMP 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 */ | 
					
						
							|  |  |  |     uint8_t proto_bits[3]; /* Prococol bits for 2400 framing */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Frame for 2400A/B */ | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B){ | 
					
						
							|  |  |  |         /* 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_proto != NULL){ | 
					
						
							|  |  |  |             (*f->freedv_get_next_proto)(f->proto_callback_state,(char*)proto_bits); | 
					
						
							|  |  |  |             fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),proto_bits,vc_bits); | 
					
						
							|  |  |  |         }else 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,vc_bits); | 
					
						
							|  |  |  |         }else { | 
					
						
							|  |  |  |             fvhff_frame_bits(FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     /* Frame for 800XA */ | 
					
						
							|  |  |  |     }else if(f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  |         fvhff_frame_bits(FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits),(uint8_t*)(f->packed_codec_bits),NULL,NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Allocate floating point buffer for FSK mod */ | 
					
						
							|  |  |  |     tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* do 4fsk mod */ | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  |         fsk_mod_c(f->fsk,mod_out,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  |         /* Convert float samps to short */ | 
					
						
							|  |  |  |         for(i=0; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |         	mod_out[i] = fcmult(NORM_PWR_FSK,mod_out[i]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     /* 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; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |             mod_out[i].real = (tx_float[i]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(tx_float); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TX routines for 2400 FSK modes, data channel */ | 
					
						
							|  |  |  | static void freedv_tx_fsk_data(struct freedv *f, short mod_out[]) { | 
					
						
							|  |  |  |     int  i; | 
					
						
							|  |  |  |     float *tx_float; /* To hold on to modulated samps from fsk/fmfsk */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode != FREEDV_MODE_800XA) | 
					
						
							|  |  |  |     	fvhff_frame_data_bits(f->deframer, FREEDV_VHF_FRAME_A,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         fvhff_frame_data_bits(f->deframer, FREEDV_HF_FRAME_B,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* Allocate floating point buffer for FSK mod */ | 
					
						
							|  |  |  |     tx_float = (float*) malloc(sizeof(float)*f->n_nom_modem_samples); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* do 4fsk mod */ | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  |         fsk_mod(f->fsk,tx_float,(uint8_t*)(f->tx_bits)); | 
					
						
							|  |  |  |         /* Convert float samps to short */ | 
					
						
							|  |  |  |         for(i=0; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |             mod_out[i] = (short)(tx_float[i]*FSK_SCALE); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     /* 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; i<f->n_nom_modem_samples; i++){ | 
					
						
							|  |  |  |             mod_out[i] = (short)(tx_float[i]*FMFSK_SCALE); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     free(tx_float); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_tx(struct freedv *f, short mod_out[], short speech_in[]) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     COMP *tx_fdm = new COMP[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_700D)  || | 
					
						
							|  |  |  |            (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || | 
					
						
							|  |  |  |            (f->mode == FREEDV_MODE_800XA)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* FSK and MEFSK/FMFSK modems work only on real samples. It's simpler to just
 | 
					
						
							|  |  |  |      * 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)){ | 
					
						
							|  |  |  |         /* 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]); | 
					
						
							|  |  |  |             codec2_encode(f->codec2, &f->packed_codec_bits[4], &speech_in[320]); | 
					
						
							|  |  |  |         }else{ | 
					
						
							|  |  |  |             codec2_encode(f->codec2, f->packed_codec_bits, speech_in); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         freedv_tx_fsk_voice(f, mod_out); | 
					
						
							|  |  |  |     } else{ | 
					
						
							|  |  |  |         freedv_comptx(f, tx_fdm, speech_in); | 
					
						
							|  |  |  |         for(i=0; i<f->n_nom_modem_samples; i++) | 
					
						
							|  |  |  |             mod_out[i] = tx_fdm[i].real; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tx_fdm; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* complex valued output, useful for suitable for single sided freq shifting */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     COMP   *tx_fdm = new COMP[f->n_nat_modem_samples]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bits_per_codec_frame = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |     bits_per_modem_frame = fdmdv_bits_per_frame(f->fdmdv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* unpack bits, MSB first */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bit = 7; byte = 0; | 
					
						
							|  |  |  |     for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |         f->codec_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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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 == 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:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |        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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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; i<bits_per_codec_frame; i++) | 
					
						
							|  |  |  |         f->tx_bits[i] = f->codec_bits[i]; | 
					
						
							|  |  |  |     for(j=0; i<bits_per_codec_frame+11; i++,j++) { | 
					
						
							|  |  |  |         f->tx_bits[i] = (codeword1 >> (10-j)) & 0x1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     f->tx_bits[i] = 0; /* spare bit */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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");
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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[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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0; i<f->n_nom_modem_samples; i++) | 
					
						
							|  |  |  |         mod_out[i] = fcmult(FDMDV_SCALE, tx_fdm[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tx_fdm; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     COMP   *tx_fdm = new COMP[f->n_nat_modem_samples]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bits_per_codec_frame = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |     bits_per_modem_frame = COHPSK_BITS_PER_FRAME; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     byte = 0; | 
					
						
							|  |  |  |     for (j=0; j<bits_per_modem_frame; j+=bits_per_codec_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* unpack bits, MSB first */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bit = 7; | 
					
						
							|  |  |  |         for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |             f->codec_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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch(f->mode) { | 
					
						
							|  |  |  |         case FREEDV_MODE_700: | 
					
						
							|  |  |  |             nspare = 2; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700B: | 
					
						
							|  |  |  |             nspare = 1; // Just one spare bit for FREEDV_MODE_700B
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700C: | 
					
						
							|  |  |  |             nspare = 0; // and no spare bits for 700C atm
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             nspare = 0; | 
					
						
							| 
									
										
										
										
											2019-03-05 10:45:08 +01:00
										 |  |  |             fprintf(stderr, "FreeDV::freedv_comptx_700: unknown mode default to nspare = 0"); | 
					
						
							| 
									
										
										
										
											2019-03-04 23:04:27 +01:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         data_flag_index = codec2_get_spare_bit_index(f->codec2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(k=0; k<nspare; k++) { | 
					
						
							|  |  |  |             if (f->nvaricode_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; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* 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, COHPSK_BITS_PER_FRAME); | 
					
						
							|  |  |  |     if (f->clip) | 
					
						
							|  |  |  |         cohpsk_clip(tx_fdm, COHPSK_CLIP, COHPSK_NOM_SAMPLES_PER_FRAME); | 
					
						
							|  |  |  |     for(i=0; i<f->n_nat_modem_samples; i++) | 
					
						
							|  |  |  |         mod_out[i] = fcmult(FDMDV_SCALE*NORM_PWR_COHPSK, tx_fdm[i]); | 
					
						
							|  |  |  |     i = quisk_cfInterpDecim((std::complex<float> *)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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tx_fdm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   Ok so when interleaved, we take the interleaver length of input samples, | 
					
						
							|  |  |  |   and output that many modem samples, e.g. for interleaver of length 4: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   record input speech 1234 | 
					
						
							|  |  |  |   freedv tx              | | 
					
						
							|  |  |  |   play modem sig         1234 | 
					
						
							|  |  |  |   record modem sig       1234 | 
					
						
							|  |  |  |   freedv_rx                   | | 
					
						
							|  |  |  |   play output speech          1234 | 
					
						
							|  |  |  |   time axis --------->123456789012----> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   So a sample of input speech at time 1 is ouput at time 9.  We assume | 
					
						
							|  |  |  |   the freedv_tx and freedv_rx and propogation time over channel (from | 
					
						
							|  |  |  |   when a modem signal is played at the HF tx to when it is recorded at | 
					
						
							|  |  |  |   the HF rx) is zero. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The freedv tx interface ouputs n_nom_modem_samples, which a single | 
					
						
							|  |  |  |   OFDM modem frame, 112 payload bits or 4 speech codec frames.  So | 
					
						
							|  |  |  |   this function must always have 1280 speech samples as input, and | 
					
						
							|  |  |  |   1280 modem samples as output, regradless of interleaver_frames.  For | 
					
						
							|  |  |  |   interleaver_frames > 1, we need to buffer samples. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void freedv_comptx_700d(struct freedv *f, COMP mod_out[]) { | 
					
						
							|  |  |  |     int    bit, byte, i, j, k; | 
					
						
							|  |  |  |     int    nspare; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int data_bits_per_frame = f->ldpc->data_bits_per_frame; | 
					
						
							|  |  |  |     int bits_per_interleaved_frame = f->interleave_frames*data_bits_per_frame; | 
					
						
							|  |  |  |     uint8_t *tx_bits = new uint8_t[bits_per_interleaved_frame]; | 
					
						
							|  |  |  |     int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     byte = 0; | 
					
						
							|  |  |  |     for (j=0; j<bits_per_interleaved_frame; j+=bits_per_codec_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* unpack bits, MSB first */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bit = 7; | 
					
						
							|  |  |  |         for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |             tx_bits[j+i] = (f->packed_codec_bits_tx[byte] >> bit) & 0x1; | 
					
						
							|  |  |  |             bit--; | 
					
						
							|  |  |  |             if (bit < 0) { | 
					
						
							|  |  |  |                 bit = 7; | 
					
						
							|  |  |  |                 byte++; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 	if (bit != 7) | 
					
						
							|  |  |  | 	    byte++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(byte <= f->nbyte_packed_codec_bits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 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 = new uint8_t[nspare]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(k=0; k<nspare; k++) { | 
					
						
							|  |  |  |         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) { | 
					
						
							|  |  |  |             txt_bits[k] = f->tx_varicode_bits[f->varicode_bit_index++]; | 
					
						
							|  |  |  |             f->nvaricode_bits--; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* optionally replace codec payload bits with test frames known to rx */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->test_frames) { | 
					
						
							|  |  |  |         uint8_t *payload_data_bits = new uint8_t[data_bits_per_frame]; | 
					
						
							|  |  |  |         ofdm_generate_payload_data_bits(payload_data_bits, data_bits_per_frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (j=0; j<f->interleave_frames; j++) { | 
					
						
							|  |  |  |             for(i=0; i<data_bits_per_frame; i++) { | 
					
						
							|  |  |  |                 tx_bits[j*data_bits_per_frame + i] = payload_data_bits[i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         delete[] payload_data_bits; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* OK now ready to LDPC encode, interleave, and OFDM modulate */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     std::complex<float> *tx_sams = new std::complex<float>[f->interleave_frames*f->n_nat_modem_samples]; | 
					
						
							|  |  |  |     COMP asam; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ofdm_ldpc_interleave_tx(f->ofdm, f->ldpc, tx_sams, tx_bits, txt_bits, f->interleave_frames, ofdm_config); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(i=0; i<f->interleave_frames*f->n_nat_modem_samples; i++) { | 
					
						
							|  |  |  |         asam.real = tx_sams[i].real(); | 
					
						
							|  |  |  |         asam.imag = tx_sams[i].imag(); | 
					
						
							|  |  |  |         mod_out[i] = fcmult(OFDM_AMP_SCALE*NORM_PWR_OFDM, asam); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->clip) { | 
					
						
							|  |  |  |         //fprintf(stderr, "clip ");
 | 
					
						
							|  |  |  |         cohpsk_clip(mod_out, OFDM_CLIP, f->interleave_frames*f->n_nat_modem_samples); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tx_sams; | 
					
						
							|  |  |  |     delete[] txt_bits; | 
					
						
							|  |  |  |     delete[] tx_bits; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_comptx(struct freedv *f, COMP mod_out[], short speech_in[]) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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_700D)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_1600) { | 
					
						
							|  |  |  |         codec2_encode(f->codec2, f->packed_codec_bits, speech_in); | 
					
						
							|  |  |  |         freedv_comptx_fdmdv_1600(f, mod_out); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  |     int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; | 
					
						
							|  |  |  |     int i,j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 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)) { | 
					
						
							|  |  |  | 	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_700(f, mod_out); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* special treatment due to interleaver */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         int data_bits_per_frame = f->ldpc->data_bits_per_frame; | 
					
						
							|  |  |  | 	int codec_frames = data_bits_per_frame / bits_per_codec_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         //fprintf(stderr, "modem_frame_count_tx: %d dec_frames: %d bytes offset: %d\n",
 | 
					
						
							|  |  |  |         //        f->modem_frame_count_tx, codec_frames, (f->modem_frame_count_tx*codec_frames)*bytes_per_codec_frame);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* buffer up bits until we get enough encoded bits for interleaver */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (j=0; j<codec_frames; j++) { | 
					
						
							|  |  |  |             codec2_encode(f->codec2, f->packed_codec_bits_tx + (f->modem_frame_count_tx*codec_frames+j)*bytes_per_codec_frame, speech_in); | 
					
						
							|  |  |  |             speech_in += codec2_samples_per_frame(f->codec2); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Only use extra local buffer if needed for interleave > 1 */ | 
					
						
							|  |  |  | 	if (f->interleave_frames == 1) { | 
					
						
							|  |  |  |             freedv_comptx_700d(f, mod_out); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  |             /* call modulate function when we have enough frames to run interleaver */ | 
					
						
							|  |  |  |             assert((f->modem_frame_count_tx >= 0) && | 
					
						
							|  |  |  | 	    		(f->modem_frame_count_tx < f->interleave_frames)); | 
					
						
							|  |  |  |             f->modem_frame_count_tx++; | 
					
						
							|  |  |  |             if (f->modem_frame_count_tx == f->interleave_frames) { | 
					
						
							|  |  |  |                 freedv_comptx_700d(f, f->mod_out); | 
					
						
							|  |  |  |                 //fprintf(stderr, "  calling freedv_comptx_700d()\n");
 | 
					
						
							|  |  |  |                 f->modem_frame_count_tx = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             /* output n_nom_modem_samples at a time from modulated buffer */ | 
					
						
							|  |  |  |             for(i=0; i<f->n_nat_modem_samples; i++) { | 
					
						
							|  |  |  |                 mod_out[i] = | 
					
						
							|  |  |  | 		    f->mod_out[f->modem_frame_count_tx * f->n_nat_modem_samples+i]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 2400 A and B are handled by the real-mode TX */ | 
					
						
							|  |  |  |     if((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B)){ | 
					
						
							|  |  |  |     	codec2_encode(f->codec2, f->packed_codec_bits, speech_in); | 
					
						
							|  |  |  |         freedv_comptx_fsk_voice(f,mod_out); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_codectx(struct freedv *f, short mod_out[], unsigned char *packed_codec_bits) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     COMP *tx_fdm = new COMP[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_1600: | 
					
						
							|  |  |  |             freedv_comptx_fdmdv_1600(f, tx_fdm); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700: | 
					
						
							|  |  |  |         case FREEDV_MODE_700B: | 
					
						
							|  |  |  |         case FREEDV_MODE_700C: | 
					
						
							|  |  |  |             freedv_comptx_700(f, tx_fdm); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case FREEDV_MODE_700D: { | 
					
						
							|  |  |  |             /* special treatment due to interleaver */ | 
					
						
							|  |  |  |             int data_bits_per_frame = f->ldpc->data_bits_per_frame; | 
					
						
							|  |  |  | 	    int codec_frames = data_bits_per_frame / bits_per_codec_frame; | 
					
						
							|  |  |  | 	    int j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* buffer up bits until we get enough encoded bits for interleaver */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (j=0; j<codec_frames; j++) { | 
					
						
							|  |  |  |                 memcpy(f->packed_codec_bits_tx + (f->modem_frame_count_tx*codec_frames+j)*bytes_per_codec_frame, packed_codec_bits, bytes_per_codec_frame); | 
					
						
							|  |  |  | 	        packed_codec_bits += bytes_per_codec_frame; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* call modulate function when we have enough frames to run interleaver */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             assert((f->modem_frame_count_tx >= 0) && (f->modem_frame_count_tx < f->interleave_frames)); | 
					
						
							|  |  |  |             f->modem_frame_count_tx++; | 
					
						
							|  |  |  |             if (f->modem_frame_count_tx == f->interleave_frames) { | 
					
						
							|  |  |  |                 freedv_comptx_700d(f, f->mod_out); | 
					
						
							|  |  |  |                 f->modem_frame_count_tx = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* output n_nom_modem_samples at a time from modulated buffer */ | 
					
						
							|  |  |  |             for(i=0; i<f->n_nat_modem_samples; i++) { | 
					
						
							|  |  |  |                 mod_out[i] = f->mod_out[f->modem_frame_count_tx*f->n_nat_modem_samples+i].real; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    return; /* output is already real */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |         case FREEDV_MODE_2400A: | 
					
						
							|  |  |  |         case FREEDV_MODE_2400B: | 
					
						
							|  |  |  |         case FREEDV_MODE_800XA: | 
					
						
							|  |  |  |             freedv_tx_fsk_voice(f, mod_out); | 
					
						
							|  |  |  |             return; /* output is already real */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     /* convert complex to real */ | 
					
						
							|  |  |  |     for(i=0; i<f->n_nom_modem_samples; i++) | 
					
						
							|  |  |  |         mod_out[i] = tx_fdm[i].real; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] tx_fdm; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_datatx  (struct freedv *f, short mod_out[]){ | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B || f->mode == FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |             freedv_tx_fsk_data(f, mod_out); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int  freedv_data_ntxframes (struct freedv *f){ | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_2400B) { | 
					
						
							|  |  |  |         if (f->deframer->fdc) | 
					
						
							|  |  |  |             return freedv_data_get_n_tx_frames(f->deframer->fdc, 8); | 
					
						
							|  |  |  |     } else if (f->mode == FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |         if (f->deframer->fdc) | 
					
						
							|  |  |  |             return freedv_data_get_n_tx_frames(f->deframer->fdc, 6); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_nin(struct freedv *f) { | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) | 
					
						
							|  |  |  |         // For mode 700, the input rate is 8000 sps, but the modem rate is 7500 sps
 | 
					
						
							|  |  |  |         // For mode 700, we request a larger number of Rx samples that will be decimated to f->nin samples
 | 
					
						
							|  |  |  |         return (16 * f->nin + f->ptFilter8000to7500->decim_index) / 15; | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         return f->nin; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_rx | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: 3 August 2014 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Takes a frame of samples from the radio receiver, demodulates and | 
					
						
							|  |  |  |   decodes them, producing a frame of decoded speech samples.  See | 
					
						
							|  |  |  |   freedv_rx.c for an example. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   demod_in[] is a block of received samples sampled at 8000 Hz. | 
					
						
							|  |  |  |   To account for difference in the transmit and receive sample clock | 
					
						
							|  |  |  |   frequencies, the number of demod_in[] samples is time varying. You | 
					
						
							|  |  |  |   MUST call freedv_nin() BEFORE each call to freedv_rx() and pass | 
					
						
							|  |  |  |   exactly that many samples to this function. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   To help set your buffer sizes, The maximum value of freedv_nin() is | 
					
						
							|  |  |  |   freedv_get_n_max_modem_samples(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   freedv_rx() returns the number of output speech samples available in | 
					
						
							|  |  |  |   speech_out[], which is sampled at 8000 Hz.  You should ALWAYS check | 
					
						
							|  |  |  |   the return value of freedv_rx(), and read EXACTLY that number of | 
					
						
							|  |  |  |   speech samples from speech_out[]. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   1600 and 700D mode: When out of sync, the number of output speech | 
					
						
							|  |  |  |   samples returned will be freedv_nin(). When in sync to a valid | 
					
						
							|  |  |  |   FreeDV 1600 signal, the number of output speech samples will | 
					
						
							|  |  |  |   alternate between freedv_get_n_speech_samples() and 0. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   700 .. 700C modes: The number of output speech samples returned will | 
					
						
							|  |  |  |   always be freedv_get_n_speech_samples(), regardless of sync. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   The peak level of demod_in[] is not critical, as the demod works | 
					
						
							|  |  |  |   well over a wide range of amplitude scaling.  However avoid clipping | 
					
						
							|  |  |  |   (overload, or samples pinned to +/- 32767).  speech_out[] will peak | 
					
						
							|  |  |  |   at just less than +/-32767. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   When out of sync, this function echoes the demod_in[] samples to | 
					
						
							|  |  |  |   speech_out[].  This allows the user to listen to the channel, which | 
					
						
							|  |  |  |   is useful for tuning FreeDV signals or reception of non-FreeDV | 
					
						
							|  |  |  |   signals.  Setting the squelch with freedv_set_squelch_en(1) will | 
					
						
							|  |  |  |   return zero-valued samples instead. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // short version
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_rx(struct freedv *f, short speech_out[], short demod_in[]) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     int nin = freedv_nin(f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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) || (f->mode == FREEDV_MODE_800XA) ){ | 
					
						
							|  |  |  |         float *rx_float = new float[f->n_max_modem_samples]; | 
					
						
							|  |  |  |         for(i=0; i<nin; i++) { | 
					
						
							|  |  |  |             rx_float[i] = ((float)demod_in[i]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int rc = freedv_floatrx(f,speech_out,rx_float); | 
					
						
							|  |  |  |         delete[] rx_float; | 
					
						
							|  |  |  |         return rc; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( (f->mode == FREEDV_MODE_1600) || (f->mode == FREEDV_MODE_700) || | 
					
						
							|  |  |  |          (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         float gain = 1.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* FDM RX happens with complex samps, so do that */ | 
					
						
							|  |  |  |         COMP *rx_fdm = new COMP[f->n_max_modem_samples]; | 
					
						
							|  |  |  |         for(i=0; i<nin; i++) { | 
					
						
							|  |  |  |             rx_fdm[i].real = gain*(float)demod_in[i]; | 
					
						
							|  |  |  |             rx_fdm[i].imag = 0.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         int rc = freedv_comprx(f, speech_out, rx_fdm); | 
					
						
							|  |  |  |         delete[] rx_fdm; | 
					
						
							|  |  |  |         return rc; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         float gain = 2.0; /* keep levels the same as Octave simulations and C unit tests for real signals */ | 
					
						
							|  |  |  |         return freedv_shortrx(f, speech_out, demod_in, gain); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; /* should never get here */ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // float input samples version
 | 
					
						
							|  |  |  | int freedv_comprx_fsk(struct freedv *f, COMP demod_in[], int *valid) { | 
					
						
							|  |  |  |     /* Varicode and protocol bits */ | 
					
						
							|  |  |  |     uint8_t vc_bits[2]; | 
					
						
							|  |  |  |     uint8_t proto_bits[3]; | 
					
						
							|  |  |  |     short vc_bit; | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     int n_ascii; | 
					
						
							|  |  |  |     char ascii_out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if(f->mode == FREEDV_MODE_2400A || f->mode == FREEDV_MODE_800XA){ | 
					
						
							|  |  |  | 	fsk_demod(f->fsk,(uint8_t*)f->tx_bits,demod_in); | 
					
						
							|  |  |  |         f->nin = fsk_nin(f->fsk); | 
					
						
							|  |  |  |         float EbNodB = f->fsk->stats->snr_est;           /* fsk demod actually estimates Eb/No     */ | 
					
						
							|  |  |  |         f->snr_est = EbNodB + 10.0*log10f(800.0/3000.0); /* so convert to SNR Rb=800, noise B=3000 */ | 
					
						
							|  |  |  |         //fprintf(stderr," %f %f\n", EbNodB, f->snr_est);
 | 
					
						
							|  |  |  |     } else{ | 
					
						
							|  |  |  |         /* 2400B needs real input samples */ | 
					
						
							|  |  |  |         int n = fmfsk_nin(f->fmfsk); | 
					
						
							|  |  |  |         float *demod_in_float = new float[n]; | 
					
						
							|  |  |  |         for(i=0; i<n; i++) { | 
					
						
							|  |  |  |             demod_in_float[i] = demod_in[i].real; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         fmfsk_demod(f->fmfsk,(uint8_t*)f->tx_bits,demod_in_float); | 
					
						
							|  |  |  |         delete[] demod_in_float; | 
					
						
							|  |  |  |         f->nin = fmfsk_nin(f->fmfsk); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if(fvhff_deframe_bits(f->deframer,f->packed_codec_bits,proto_bits,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); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         /* Pass proto bits on down if callback is present */ | 
					
						
							|  |  |  |         if( f->freedv_put_next_proto != NULL){ | 
					
						
							|  |  |  |             (*f->freedv_put_next_proto)(f->proto_callback_state,(char*)proto_bits); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         *valid = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* squelch if if sync but SNR too low */ | 
					
						
							|  |  |  |         if (f->squelch_en && (f->snr_est < f->snr_squelch_thresh)) { | 
					
						
							|  |  |  |             *valid = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         /* squelch if out of sync, or echo input of squelch off */ | 
					
						
							|  |  |  |         if (f->squelch_en) | 
					
						
							|  |  |  |             *valid = 0; | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |             *valid = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     f->sync = f->deframer->state; | 
					
						
							|  |  |  |     f->stats.sync = f->deframer->state; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return f->n_speech_samples; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_floatrx(struct freedv *f, short speech_out[], float demod_in[]) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     int  i; | 
					
						
							|  |  |  |     int nin = freedv_nin(f); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(nin <= f->n_max_modem_samples); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP *rx_fdm = new COMP[f->n_max_modem_samples]; | 
					
						
							|  |  |  |     for(i=0; i<nin; i++) { | 
					
						
							|  |  |  |         rx_fdm[i].real = demod_in[i]; | 
					
						
							|  |  |  |         rx_fdm[i].imag = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int rc = freedv_comprx(f, speech_out, rx_fdm); | 
					
						
							|  |  |  |     delete[] rx_fdm; | 
					
						
							|  |  |  |     return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // complex input samples version
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     int                 recd_codeword, codeword1, data_flag_index, n_ascii; | 
					
						
							|  |  |  |     short               abit[1]; | 
					
						
							|  |  |  |     char                ascii_out; | 
					
						
							|  |  |  |     int                 reliable_sync_bit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP *ademod_in = new COMP[f->nin]; | 
					
						
							|  |  |  |     for(i=0; i<f->nin; i++) | 
					
						
							|  |  |  |         ademod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bits_per_fdmdv_frame  = fdmdv_bits_per_frame(f->fdmdv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nin_prev = f->nin; | 
					
						
							|  |  |  |     fdmdv_demod(f->fdmdv, f->fdmdv_bits, &reliable_sync_bit, ademod_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 (f->stats.sync) { | 
					
						
							|  |  |  |         if (f->evenframe == 0) { | 
					
						
							|  |  |  |             memcpy(f->rx_bits, f->fdmdv_bits, bits_per_fdmdv_frame*sizeof(int)); | 
					
						
							|  |  |  |             nout = 0; | 
					
						
							|  |  |  | 	    *valid = 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; i<bits_per_codec_frame+11; i++) { | 
					
						
							|  |  |  |                     recd_codeword <<= 1; | 
					
						
							|  |  |  |                     recd_codeword |= (f->rx_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);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for(i=0; i<bits_per_codec_frame; i++) | 
					
						
							|  |  |  |                     f->codec_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; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // extract txt msg data bit ------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // reconstruct missing bit we steal for data bit and decode speech
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 codec2_rebuild_spare_bit(f->codec2, f->codec_bits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // pack bits, MSB received first
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 bit  = 7; | 
					
						
							|  |  |  |                 byte = 0; | 
					
						
							|  |  |  |                 memset(f->packed_codec_bits, 0,  bytes_per_codec_frame); | 
					
						
							|  |  |  |                 for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |                     f->packed_codec_bits[byte] |= (f->codec_bits[i] << bit); | 
					
						
							|  |  |  |                     bit--; | 
					
						
							|  |  |  |                     if(bit < 0) { | 
					
						
							|  |  |  |                         bit = 7; | 
					
						
							|  |  |  |                         byte++; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 *valid = 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 int   test_frame_sync, bit_errors, ntest_bits, k; | 
					
						
							|  |  |  |                 short *error_pattern = new short[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]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     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; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     //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);
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 delete[] error_pattern; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* 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);
 | 
					
						
							|  |  |  |                 *valid = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             nout = f->n_speech_samples; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* 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 (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");
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->squelch_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; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     delete[] ademod_in; | 
					
						
							|  |  |  |     return nout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							|  |  |  |     short               abit[1]; | 
					
						
							|  |  |  |     char                ascii_out; | 
					
						
							|  |  |  |     float rx_bits[COHPSK_BITS_PER_FRAME]; /* soft decn rx bits */ | 
					
						
							|  |  |  |     int   sync; | 
					
						
							|  |  |  |     int   frames; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // echo samples back out as default (say if sync not found)
 | 
					
						
							|  |  |  |     *valid = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // quisk_cfInterpDecim() modifies input data so lets make a copy just in case there
 | 
					
						
							|  |  |  |     // is no sync and we need to echo inout to output
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     COMP *demod_in = new COMP[freedv_nin(f)]; | 
					
						
							|  |  |  |     for(i=0; i<freedv_nin(f); i++) | 
					
						
							|  |  |  |         demod_in[i] = demod_in_8kHz[i]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     i = quisk_cfInterpDecim((std::complex<float> *)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; i<f->nin; i++) | 
					
						
							|  |  |  |         demod_in[i] = fcmult(1.0/FDMDV_SCALE, demod_in[i]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     memset(f->packed_codec_bits, 0, bytes_per_codec_frame * frames); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sync) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->test_frames == 0) { | 
					
						
							|  |  |  |             data_flag_index = codec2_get_spare_bit_index(f->codec2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* optional smoothing of codec symbols */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (f->smooth_symbols) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |                     rx_bits[i] += rx_bits[i+bits_per_codec_frame]; | 
					
						
							|  |  |  |                     rx_bits[i+bits_per_codec_frame] = rx_bits[i]; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             byte = 0; | 
					
						
							|  |  |  |             for (j=0; j<COHPSK_BITS_PER_FRAME; j+=bits_per_codec_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* extract txt msg data bit(s) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 switch(f->mode) { | 
					
						
							|  |  |  |                 case FREEDV_MODE_700: | 
					
						
							|  |  |  |                     nspare = 2; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case FREEDV_MODE_700B: | 
					
						
							|  |  |  |                     nspare = 1; // Just one spare bit for FREEDV_MODE_700B
 | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case FREEDV_MODE_700C: | 
					
						
							|  |  |  |                     nspare = 0; // and no spare bits for 700C atm
 | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     nspare = 0; | 
					
						
							| 
									
										
										
										
											2019-03-05 10:45:08 +01:00
										 |  |  |                     fprintf(stderr, "FreeDV::freedv_comprx_700: unknown mode default to nspare = 0"); | 
					
						
							| 
									
										
										
										
											2019-03-04 23:04:27 +01:00
										 |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for(k=0; k<nspare; k++)  { | 
					
						
							|  |  |  |                     abit[0] = rx_bits[data_flag_index+j+k] < 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     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 */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 bit = 7; | 
					
						
							|  |  |  |                 for(i=0; i<bits_per_codec_frame; i++) { | 
					
						
							|  |  |  |                     f->packed_codec_bits[byte] |= ((rx_bits[j+i] < 0.0) << bit); | 
					
						
							|  |  |  |                     bit--; | 
					
						
							|  |  |  |                     if (bit < 0) { | 
					
						
							|  |  |  |                         bit = 7; | 
					
						
							|  |  |  |                         byte++; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 		if (bit != 7) | 
					
						
							|  |  |  | 		    byte++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { | 
					
						
							|  |  |  | 		   *valid = 0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 		*valid = 1; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             nout = f->n_speech_samples; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             //fprintf(stderr, " freedv_api:  f->test_frames_diversity: %d\n", f->test_frames_diversity);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (f->test_frames_diversity) { | 
					
						
							|  |  |  |                 /* normal operation - error pattern on frame after diveristy combination */ | 
					
						
							|  |  |  |                 short error_pattern[COHPSK_BITS_PER_FRAME]; | 
					
						
							|  |  |  |                 int   bit_errors; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* test data, lets see if we can sync to the test data sequence */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 char rx_bits_char[COHPSK_BITS_PER_FRAME]; | 
					
						
							|  |  |  |                 for(i=0; i<COHPSK_BITS_PER_FRAME; i++) | 
					
						
							|  |  |  |                     rx_bits_char[i] = rx_bits[i] < 0.0; | 
					
						
							|  |  |  |                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors, rx_bits_char, 0); | 
					
						
							|  |  |  |                 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); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							|  |  |  |                 /* calculate error pattern on uncombined carriers - test mode to spot any carrier specific issues like
 | 
					
						
							|  |  |  |                    tx passband filtering */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 short error_pattern[2*COHPSK_BITS_PER_FRAME]; | 
					
						
							|  |  |  |                 char  rx_bits_char[COHPSK_BITS_PER_FRAME]; | 
					
						
							|  |  |  |                 int   bit_errors_lower, bit_errors_upper; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* lower group of carriers */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 float *rx_bits_lower = cohpsk_get_rx_bits_lower(f->cohpsk); | 
					
						
							|  |  |  |                 for(i=0; i<COHPSK_BITS_PER_FRAME; i++) { | 
					
						
							|  |  |  |                     rx_bits_char[i] = rx_bits_lower[i] < 0.0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state, error_pattern, &bit_errors_lower, rx_bits_char, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* upper group of carriers */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 float *rx_bits_upper = cohpsk_get_rx_bits_upper(f->cohpsk); | 
					
						
							|  |  |  |                 for(i=0; i<COHPSK_BITS_PER_FRAME; i++) { | 
					
						
							|  |  |  |                     rx_bits_char[i] = rx_bits_upper[i] < 0.0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 cohpsk_put_test_bits(f->cohpsk, &f->test_frame_sync_state_upper, &error_pattern[COHPSK_BITS_PER_FRAME], &bit_errors_upper, rx_bits_char, 1); | 
					
						
							|  |  |  |                 //                fprintf(stderr, " freedv_api:  f->test_frame_sync_state: %d f->test_frame_sync_state_upper: %d\n",
 | 
					
						
							|  |  |  |                 //        f->test_frame_sync_state, f->test_frame_sync_state_upper);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 /* combine total errors and call callback */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (f->test_frame_sync_state && f->test_frame_sync_state_upper) { | 
					
						
							|  |  |  |                     f->total_bit_errors += bit_errors_lower + bit_errors_upper; | 
					
						
							|  |  |  |                     f->total_bits       += 2*COHPSK_BITS_PER_FRAME; | 
					
						
							|  |  |  |                     if (f->freedv_put_error_pattern != NULL) { | 
					
						
							|  |  |  |                         (*f->freedv_put_error_pattern)(f->error_pattern_callback_state, error_pattern, 2*COHPSK_BITS_PER_FRAME); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    *valid = 0; | 
					
						
							|  |  |  |             nout = f->n_speech_samples; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* no valid FreeDV signal - squelch output */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (sync == 0) { | 
					
						
							|  |  |  |         nout = freedv_nin(f); | 
					
						
							|  |  |  |         if (f->squelch_en) { | 
					
						
							|  |  |  | 	    *valid = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     delete[] demod_in; | 
					
						
							|  |  |  |     return nout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |   TODO: | 
					
						
							|  |  |  |     [X] in testframe mode count coded and uncoded errors | 
					
						
							|  |  |  |     [X] freedv getter for modem and interleaver sync | 
					
						
							|  |  |  |     [X] rms level the same as fdmdv | 
					
						
							|  |  |  |     [X] way to stay in sync and not resync automatically | 
					
						
							|  |  |  |     [X] SNR est, maybe from pilots, cohpsk have an example? | 
					
						
							|  |  |  |     [X] work out how to handle return of multiple interleaved frames over time | 
					
						
							|  |  |  |     [ ] error pattern support? | 
					
						
							|  |  |  |     [ ] deal with out of sync returning nin samples, listening to analog audio when out of sync | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int freedv_comprx_700d(struct freedv *f, short demod_in_8kHz[], float gain, int *valid) { | 
					
						
							|  |  |  |     int   bits_per_codec_frame, bytes_per_codec_frame; | 
					
						
							|  |  |  |     int   i, j, bit, byte, nout, k; | 
					
						
							|  |  |  |     int   n_ascii; | 
					
						
							|  |  |  |     char  ascii_out; | 
					
						
							|  |  |  |     int   frames; | 
					
						
							|  |  |  |     struct OFDM *ofdm = f->ofdm; | 
					
						
							|  |  |  |     struct LDPC *ldpc = f->ldpc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int    data_bits_per_frame = ldpc->data_bits_per_frame; | 
					
						
							|  |  |  |     int    coded_bits_per_frame = ldpc->coded_bits_per_frame; | 
					
						
							|  |  |  |     int    coded_syms_per_frame = ldpc->coded_syms_per_frame; | 
					
						
							|  |  |  |     int    interleave_frames = f->interleave_frames; | 
					
						
							|  |  |  |     COMP  *codeword_symbols = f->codeword_symbols; | 
					
						
							|  |  |  |     float *codeword_amps = f->codeword_amps; | 
					
						
							|  |  |  |     int   *rx_bits = new int[ofdm_bitsperframe]; | 
					
						
							|  |  |  |     short *txt_bits = new short[ofdm_ntxtbits]; | 
					
						
							|  |  |  |     COMP  *payload_syms = new COMP[coded_syms_per_frame]; | 
					
						
							|  |  |  |     float *payload_amps = new float[coded_syms_per_frame]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // pass through is too noisey ....
 | 
					
						
							|  |  |  |     //nout = f->n_speech_samples;
 | 
					
						
							|  |  |  |     nout = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int Nerrs_raw = 0; | 
					
						
							|  |  |  |     int Nerrs_coded = 0; | 
					
						
							|  |  |  |     int iter = 0; | 
					
						
							|  |  |  |     int parityCheckCount = 0; | 
					
						
							|  |  |  |     uint8_t *rx_uw = new uint8_t[ofdm_nuwbits]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float new_gain = gain / OFDM_AMP_SCALE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* echo samples back out as default (say if sync not found) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *valid = 1; | 
					
						
							|  |  |  |     f->sync = f->stats.sync = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* TODO estimate this properly from signal */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     float EsNo = 3.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* looking for modem sync */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (ofdm->sync_state == search) { | 
					
						
							|  |  |  |         ofdm_sync_search_shorts(f->ofdm, demod_in_8kHz, new_gain); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |      /* OK modem is in sync */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((ofdm->sync_state == synced) || (ofdm->sync_state == trial)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         ofdm_demod_shorts(ofdm, rx_bits, demod_in_8kHz, new_gain); | 
					
						
							|  |  |  |         ofdm_disassemble_modem_frame(ofdm, rx_uw, payload_syms, payload_amps, txt_bits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         f->sync = 1; | 
					
						
							|  |  |  |         ofdm_get_demod_stats(f->ofdm, &f->stats); | 
					
						
							|  |  |  |         f->snr_est = f->stats.snr_est; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         assert((ofdm_nuwbits+ofdm_ntxtbits+coded_bits_per_frame) == ofdm_bitsperframe); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* now we need to buffer for de-interleaving -------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* shift interleaved symbol buffers to make room for new symbols */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(i=0, j=coded_syms_per_frame; j<interleave_frames*coded_syms_per_frame; i++,j++) { | 
					
						
							|  |  |  |             codeword_symbols[i] = codeword_symbols[j]; | 
					
						
							|  |  |  |             codeword_amps[i] = codeword_amps[j]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* newest symbols at end of buffer (uses final i from last loop), note we
 | 
					
						
							|  |  |  |            change COMP formats from what modem uses internally */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(i=(interleave_frames-1)*coded_syms_per_frame,j=0; i<interleave_frames*coded_syms_per_frame; i++,j++) { | 
					
						
							|  |  |  |             codeword_symbols[i] = payload_syms[j]; | 
					
						
							|  |  |  |             codeword_amps[i]    = payload_amps[j]; | 
					
						
							|  |  |  |          } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* run de-interleaver */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         COMP  *codeword_symbols_de = new COMP[interleave_frames*coded_syms_per_frame]; | 
					
						
							|  |  |  |         float *codeword_amps_de = new float[interleave_frames*coded_syms_per_frame]; | 
					
						
							|  |  |  |         gp_deinterleave_comp (codeword_symbols_de, codeword_symbols, interleave_frames*coded_syms_per_frame); | 
					
						
							|  |  |  |         gp_deinterleave_float(codeword_amps_de   , codeword_amps   , interleave_frames*coded_syms_per_frame); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         float *llr = new float[coded_bits_per_frame]; | 
					
						
							|  |  |  |         uint8_t *out_char = new uint8_t[coded_bits_per_frame]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         interleaver_sync_state_machine(ofdm, ldpc, ofdm_config, codeword_symbols_de, codeword_amps_de, EsNo, | 
					
						
							|  |  |  |                                        interleave_frames, &iter, &parityCheckCount, &Nerrs_coded); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ((ofdm->sync_state_interleaver == synced) && (ofdm->frame_count_interleaver == interleave_frames)) { | 
					
						
							|  |  |  |             ofdm->frame_count_interleaver = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (f->test_frames) { | 
					
						
							|  |  |  |                 int *tmp = new int[interleave_frames]; | 
					
						
							|  |  |  |                 Nerrs_raw = count_uncoded_errors(ldpc, ofdm_config, tmp, interleave_frames, codeword_symbols_de); | 
					
						
							|  |  |  |                 f->total_bit_errors += Nerrs_raw; | 
					
						
							|  |  |  |                 f->total_bits       += ofdm_bitsperframe*interleave_frames; | 
					
						
							|  |  |  |                 delete[] tmp; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             memset(f->packed_codec_bits, 0, bytes_per_codec_frame * frames); | 
					
						
							|  |  |  |             byte = 0; f->modem_frame_count_rx = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (j=0; j<interleave_frames; j++) { | 
					
						
							|  |  |  |                 symbols_to_llrs(llr, &codeword_symbols_de[j*coded_syms_per_frame], | 
					
						
							|  |  |  |                                 &codeword_amps_de[j*coded_syms_per_frame], | 
					
						
							|  |  |  |                                 EsNo, ofdm->mean_amp, coded_syms_per_frame); | 
					
						
							|  |  |  |                 iter = run_ldpc_decoder(ldpc, out_char, llr, &parityCheckCount); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (f->test_frames) { | 
					
						
							|  |  |  |                     uint8_t *payload_data_bits = new uint8_t[data_bits_per_frame]; | 
					
						
							|  |  |  |                     ofdm_generate_payload_data_bits(payload_data_bits, data_bits_per_frame); | 
					
						
							|  |  |  |                     Nerrs_coded = count_errors(payload_data_bits, out_char, data_bits_per_frame); | 
					
						
							|  |  |  |                     f->total_bit_errors_coded += Nerrs_coded; | 
					
						
							|  |  |  |                     f->total_bits_coded       += data_bits_per_frame; | 
					
						
							|  |  |  |                     delete[] payload_data_bits; | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     /* a frame of valid Codec 2 bits, pack into Codec 2 frame  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     for (i=0; i<data_bits_per_frame; i+=bits_per_codec_frame) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         /* pack bits, MSB received first */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         bit = 7; | 
					
						
							|  |  |  |                         for(k=0; k<bits_per_codec_frame; k++) { | 
					
						
							|  |  |  |                             f->packed_codec_bits[byte] |= (out_char[i+k] << bit); | 
					
						
							|  |  |  |                             bit--; | 
					
						
							|  |  |  |                             if (bit < 0) { | 
					
						
							|  |  |  |                                 bit = 7; | 
					
						
							|  |  |  |                                 byte++; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         if (bit != 7) | 
					
						
							|  |  |  |                             byte++; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } /* for interleave frames ... */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             /* make sure we don't overrun packed byte array */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             assert(byte <= f->nbyte_packed_codec_bits); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             nout = f->n_speech_samples; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (f->squelch_en && (f->stats.snr_est < f->snr_squelch_thresh)) { | 
					
						
							|  |  |  |                 *valid = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } /* if interleaver synced ..... */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* If modem is synced we can decode txt bits */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(k=0; k<ofdm_ntxtbits; k++)  { | 
					
						
							|  |  |  |             //fprintf(stderr, "txt_bits[%d] = %d\n", k, rx_bits[i]);
 | 
					
						
							|  |  |  |             n_ascii = varicode_decode(&f->varicode_dec_states, &ascii_out, &txt_bits[k], 1, 1); | 
					
						
							|  |  |  |             if (n_ascii && (f->freedv_put_next_rx_char != NULL)) { | 
					
						
							|  |  |  |                 (*f->freedv_put_next_rx_char)(f->callback_state, ascii_out); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* estimate uncoded BER from UW.  Coded bit errors could
 | 
					
						
							|  |  |  |            probably be estimated as half of all failed LDPC parity | 
					
						
							|  |  |  |            checks */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(i=0; i<ofdm_nuwbits; i++) { | 
					
						
							|  |  |  |             if (rx_uw[i] != ofdm->tx_uw[i]) { | 
					
						
							|  |  |  |                 f->total_bit_errors++; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         f->total_bits += ofdm_nuwbits; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         delete[] out_char; | 
					
						
							|  |  |  |         delete[] llr; | 
					
						
							|  |  |  |         delete[] codeword_amps_de; | 
					
						
							|  |  |  |         delete[] codeword_symbols_de; | 
					
						
							|  |  |  |     } /* if modem synced .... */ | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         *valid = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* iterate state machine and update nin for next call */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     f->nin = ofdm_get_nin(ofdm); | 
					
						
							|  |  |  |     //fprintf(stderr, "nin: %d\n", ofdm_get_nin(ofdm));
 | 
					
						
							|  |  |  |     ofdm_sync_state_machine(ofdm, rx_uw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->verbose && (ofdm->last_sync_state == search)) { | 
					
						
							|  |  |  |         fprintf(stderr, "%3d st: %-6s euw: %2d %1d f: %5.1f ist: %-6s %2d eraw: %3d ecdd: %3d iter: %3d pcc: %3d vld: %d, nout: %4d\n", | 
					
						
							|  |  |  |                 f->frames++, statemode[ofdm->last_sync_state], ofdm->uw_errors, ofdm->sync_counter, | 
					
						
							|  |  |  | 		(double)ofdm->foff_est_hz, | 
					
						
							|  |  |  |                 statemode[ofdm->last_sync_state_interleaver], ofdm->frame_count_interleaver, | 
					
						
							|  |  |  |                 Nerrs_raw, Nerrs_coded, iter, parityCheckCount, *valid, nout); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* no valid FreeDV signal - squelch output */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool sync = ((ofdm->sync_state == synced) || (ofdm->sync_state == trial)); | 
					
						
							|  |  |  |     if (sync == false) { | 
					
						
							|  |  |  |          if (f->squelch_en == true) { | 
					
						
							|  |  |  |  	    *valid = 0; | 
					
						
							|  |  |  |          } | 
					
						
							|  |  |  |          //f->snr_est = 0.0;
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //fprintf(stderr, "sync: %d valid: %d snr: %3.2f\n", f->sync, *valid, f->snr_est);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delete[] rx_uw; | 
					
						
							|  |  |  |     delete[] payload_amps; | 
					
						
							|  |  |  |     delete[] payload_syms; | 
					
						
							|  |  |  |     delete[] txt_bits; | 
					
						
							|  |  |  |     delete[] rx_bits; | 
					
						
							|  |  |  |     return nout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Original version for all but 700D */ | 
					
						
							|  |  |  | 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 = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |         nout = freedv_comprx_700(f, demod_in, &valid); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ | 
					
						
							|  |  |  |         nout = freedv_comprx_fsk(f, demod_in, &valid); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (valid == 0) { | 
					
						
							|  |  |  |         //fprintf(stderr, "squelch nout: %d\n", nout);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* squelch */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (i = 0; i < nout; i++) | 
					
						
							|  |  |  |             speech_out[i] = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (valid < 0) { | 
					
						
							|  |  |  |         /* we havent got sync so play audio from radio.  This might
 | 
					
						
							|  |  |  |            not work for all modes due to nin bouncing about */ | 
					
						
							|  |  |  |         for (i = 0; i < nout; i++) | 
					
						
							|  |  |  |             speech_out[i] = demod_in[i].real; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         /* decoded audio to play */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int frames = f->n_codec_bits / bits_per_codec_frame; | 
					
						
							|  |  |  |         //fprintf(stderr, "frames: %d\n", frames);
 | 
					
						
							|  |  |  |         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 valid: %d\n", freedv_nin(f), nout, valid);
 | 
					
						
							|  |  |  |     return nout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* 700D version */ | 
					
						
							|  |  |  | int freedv_shortrx(struct freedv *f, short speech_out[], short demod_in[], float gain) { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     int                 bits_per_codec_frame, bytes_per_codec_frame; | 
					
						
							|  |  |  |     int                 i, nout = 0; | 
					
						
							|  |  |  |     int valid = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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_700D) { | 
					
						
							|  |  |  |         nout = freedv_comprx_700d(f, demod_in, gain, &valid); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (valid == 0) { | 
					
						
							|  |  |  |         //fprintf(stderr, "squelch nout: %d\n", nout);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* squelch */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (i = 0; i < nout; i++) | 
					
						
							|  |  |  |             speech_out[i] = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else if (valid < 0) { | 
					
						
							|  |  |  |         /* we havent got sync so play audio from radio.  This might
 | 
					
						
							|  |  |  |            not work for all modes due to nin bouncing about */ | 
					
						
							|  |  |  |         for (i = 0; i < nout; i++) | 
					
						
							|  |  |  |             speech_out[i] = demod_in[i]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         /* decoded audio to play */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int data_bits_per_frame = f->ldpc->data_bits_per_frame; | 
					
						
							|  |  |  |         int frames = data_bits_per_frame/bits_per_codec_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         nout = 0; | 
					
						
							|  |  |  |         if (f->modem_frame_count_rx < f->interleave_frames) { | 
					
						
							|  |  |  |             nout = f->n_speech_samples; | 
					
						
							|  |  |  |             //fprintf(stderr, "modem_frame_count_rx: %d nout: %d\n", f->modem_frame_count_rx, nout);
 | 
					
						
							|  |  |  |             for (i = 0; i < frames; i++) { | 
					
						
							|  |  |  |                 codec2_decode(f->codec2, speech_out, f->packed_codec_bits + (i + frames*f->modem_frame_count_rx)* bytes_per_codec_frame); | 
					
						
							|  |  |  |                 speech_out += codec2_samples_per_frame(f->codec2); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             f->modem_frame_count_rx++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return nout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_codecrx(struct freedv *f, unsigned char *packed_codec_bits, short demod_in[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     assert(f != NULL); | 
					
						
							|  |  |  |     int i; | 
					
						
							|  |  |  |     int nin = freedv_nin(f); | 
					
						
							|  |  |  |     int valid; | 
					
						
							|  |  |  |     int ret = 0; | 
					
						
							|  |  |  |     int bits_per_codec_frame = codec2_bits_per_frame(f->codec2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(nin <= f->n_max_modem_samples); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode != FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         COMP *rx_fdm = new COMP[f->n_max_modem_samples]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for(i=0; i<nin; i++) { | 
					
						
							|  |  |  |             rx_fdm[i].real = (float)demod_in[i]; | 
					
						
							|  |  |  |             rx_fdm[i].imag = 0.0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (f->mode == FREEDV_MODE_1600) { | 
					
						
							|  |  |  |             freedv_comprx_fdmdv_1600(f, rx_fdm, &valid); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |             freedv_comprx_700(f, rx_fdm, &valid); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if( (f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ | 
					
						
							|  |  |  |             freedv_comprx_fsk(f, rx_fdm, &valid); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         delete[] rx_fdm; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     int bytes_per_codec_frame = (bits_per_codec_frame + 7) / 8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         freedv_comprx_700d(f, demod_in, 1.0, &valid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         int data_bits_per_frame = f->ldpc->data_bits_per_frame; | 
					
						
							|  |  |  |         int frames = data_bits_per_frame/bits_per_codec_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (valid == 1 && f->modem_frame_count_rx < f->interleave_frames) { | 
					
						
							|  |  |  |              for (i = 0; i < frames; i++) { | 
					
						
							|  |  |  |                  memcpy(packed_codec_bits, f->packed_codec_bits + (i + frames*f->modem_frame_count_rx)* bytes_per_codec_frame, bytes_per_codec_frame); | 
					
						
							|  |  |  |                  packed_codec_bits += bytes_per_codec_frame; | 
					
						
							|  |  |  |                  ret += bytes_per_codec_frame; | 
					
						
							|  |  |  |              } | 
					
						
							|  |  |  |              f->modem_frame_count_rx++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (valid == 1) { | 
					
						
							|  |  |  |         int codec_frames = f->n_codec_bits / bits_per_codec_frame; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         memcpy(packed_codec_bits, f->packed_codec_bits, bytes_per_codec_frame * codec_frames); | 
					
						
							|  |  |  | 	ret = bytes_per_codec_frame * codec_frames; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_get_version | 
					
						
							|  |  |  |   AUTHOR......: Jim Ahlstrom | 
					
						
							|  |  |  |   DATE CREATED: 28 July 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Return the version of the FreeDV API.  This is meant to help API users determine when | 
					
						
							|  |  |  |   incompatible changes have occurred. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_get_version(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return VERSION; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_set_callback_txt | 
					
						
							|  |  |  |   AUTHOR......: Jim Ahlstrom | 
					
						
							|  |  |  |   DATE CREATED: 28 July 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Set the callback functions and the callback state pointer that will be used | 
					
						
							|  |  |  |   for the aux txt channel.  The freedv_callback_rx is a function pointer that | 
					
						
							|  |  |  |   will be called to return received characters.  The freedv_callback_tx is a | 
					
						
							|  |  |  |   function pointer that will be called to send transmitted characters.  The callback | 
					
						
							|  |  |  |   state is a user-defined void pointer that will be passed to the callback functions. | 
					
						
							|  |  |  |   Any or all can be NULL, and the default is all NULL. | 
					
						
							|  |  |  |   The function signatures are: | 
					
						
							|  |  |  |     void receive_char(void *callback_state, char c); | 
					
						
							|  |  |  |     char transmit_char(void *callback_state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_callback_txt(struct freedv *f, freedv_callback_rx rx, freedv_callback_tx tx, void *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (f->mode != FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |         f->freedv_put_next_rx_char = rx; | 
					
						
							|  |  |  |         f->freedv_get_next_tx_char = tx; | 
					
						
							|  |  |  |         f->callback_state = state; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_set_callback_protocol | 
					
						
							|  |  |  |   AUTHOR......: Brady OBrien | 
					
						
							|  |  |  |   DATE CREATED: 21 February 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Set the callback functions and callback pointer that will be used for the | 
					
						
							|  |  |  |   protocol data channel. freedv_callback_protorx will be called when a frame | 
					
						
							|  |  |  |   containing protocol data arrives. freedv_callback_prototx will be called | 
					
						
							|  |  |  |   when a frame containing protocol information is being generated. Protocol | 
					
						
							|  |  |  |   information is intended to be used to develop protocols and fancy features | 
					
						
							|  |  |  |   atop VHF freedv, much like those present in DMR. | 
					
						
							|  |  |  |    Protocol bits are to be passed in an msb-first char array | 
					
						
							|  |  |  |    The number of protocol bits are findable with freedv_get_protocol_bits | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_callback_protocol(struct freedv *f, freedv_callback_protorx rx, freedv_callback_prototx tx, void *callback_state){ | 
					
						
							|  |  |  |     if (f->mode != FREEDV_MODE_800XA) { | 
					
						
							|  |  |  |         f->freedv_put_next_proto = rx; | 
					
						
							|  |  |  |         f->freedv_get_next_proto = tx; | 
					
						
							|  |  |  |         f->proto_callback_state = callback_state; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_set_callback_datarx / freedv_set_callback_datatx | 
					
						
							|  |  |  |   AUTHOR......: Jeroen Vreeken | 
					
						
							|  |  |  |   DATE CREATED: 04 March 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Set the callback functions and callback pointer that will be used for the | 
					
						
							|  |  |  |   data channel. freedv_callback_datarx will be called when a packet has been | 
					
						
							|  |  |  |   successfully received. freedv_callback_data_tx will be called when | 
					
						
							|  |  |  |   transmission of a new packet can begin. | 
					
						
							|  |  |  |   If the returned size of the datatx callback is zero the data frame is still | 
					
						
							|  |  |  |   generated, but will contain only a header update. | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | void freedv_set_callback_data(struct freedv *f, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state) { | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ | 
					
						
							|  |  |  |         if (!f->deframer->fdc) | 
					
						
							|  |  |  |             f->deframer->fdc = freedv_data_channel_create(); | 
					
						
							|  |  |  |         if (!f->deframer->fdc) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         freedv_data_set_cb_rx(f->deframer->fdc, datarx, callback_state); | 
					
						
							|  |  |  |         freedv_data_set_cb_tx(f->deframer->fdc, datatx, callback_state); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_set_data_header | 
					
						
							|  |  |  |   AUTHOR......: Jeroen Vreeken | 
					
						
							|  |  |  |   DATE CREATED: 04 March 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Set the data header for the data channel. | 
					
						
							|  |  |  |   Header compression will be used whenever packets from this header are sent. | 
					
						
							|  |  |  |   The header will also be used for fill packets when a data frame is requested | 
					
						
							|  |  |  |   without a packet available. | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | void freedv_set_data_header(struct freedv *f, unsigned char *header) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_2400B) || (f->mode == FREEDV_MODE_800XA)){ | 
					
						
							|  |  |  |         if (!f->deframer->fdc) | 
					
						
							|  |  |  |             f->deframer->fdc = freedv_data_channel_create(); | 
					
						
							|  |  |  |         if (!f->deframer->fdc) | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         freedv_data_set_header(f->deframer->fdc, header); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTION....: freedv_get_modem_stats | 
					
						
							|  |  |  |   AUTHOR......: Jim Ahlstrom | 
					
						
							|  |  |  |   DATE CREATED: 28 July 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Return data from the modem.  The arguments are pointers to the data items.  The | 
					
						
							|  |  |  |   pointers can be NULL if the data item is not wanted. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_get_modem_stats(struct freedv *f, int *sync, float *snr_est) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_1600) | 
					
						
							|  |  |  |         fdmdv_get_demod_stats(f->fdmdv, &f->stats); | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B)  || (f->mode == FREEDV_MODE_700C)) | 
					
						
							|  |  |  |         cohpsk_get_demod_stats(f->cohpsk, &f->stats); | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         ofdm_get_demod_stats(f->ofdm, &f->stats); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_2400B) { | 
					
						
							|  |  |  |         fmfsk_get_demod_stats(f->fmfsk, &f->stats); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (sync) *sync = f->stats.sync; | 
					
						
							|  |  |  |     if (snr_est) *snr_est = f->stats.snr_est; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTIONS...: freedv_set_* | 
					
						
							|  |  |  |   AUTHOR......: Jim Ahlstrom | 
					
						
							|  |  |  |   DATE CREATED: 28 July 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Set some parameters used by FreeDV.  It is possible to write a macro using ## for | 
					
						
							|  |  |  |   this, but I wasn't sure it would be 100% portable. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Set integers
 | 
					
						
							|  |  |  | void freedv_set_test_frames               (struct freedv *f, int val) {f->test_frames = val;} | 
					
						
							|  |  |  | void freedv_set_test_frames_diversity	  (struct freedv *f, int val) {f->test_frames_diversity = val;} | 
					
						
							|  |  |  | void freedv_set_squelch_en                (struct freedv *f, int val) {f->squelch_en = val;} | 
					
						
							|  |  |  | void freedv_set_total_bit_errors          (struct freedv *f, int val) {f->total_bit_errors = val;} | 
					
						
							|  |  |  | void freedv_set_total_bits                (struct freedv *f, int val) {f->total_bits = val;} | 
					
						
							|  |  |  | void freedv_set_total_bit_errors_coded    (struct freedv *f, int val) {f->total_bit_errors_coded = val;} | 
					
						
							|  |  |  | void freedv_set_total_bits_coded          (struct freedv *f, int val) {f->total_bits_coded = val;} | 
					
						
							|  |  |  | void freedv_set_clip                      (struct freedv *f, int val) {f->clip = val;} | 
					
						
							|  |  |  | void freedv_set_varicode_code_num         (struct freedv *f, int val) {varicode_set_code_num(&f->varicode_dec_states, val);} | 
					
						
							|  |  |  | void freedv_set_ext_vco                   (struct freedv *f, int val) {f->ext_vco = val;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Band Pass Filter to cleanup OFDM tx waveform, only supported by FreeDV 700D */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_tx_bpf(struct freedv *f, int val) { | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         ofdm_set_tx_bpf(f->ofdm, val); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_verbose(struct freedv *f, int verbosity) { | 
					
						
							|  |  |  |     f->verbose = verbosity; | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         ofdm_set_verbose(f->ofdm, f->verbose); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Set floats
 | 
					
						
							|  |  |  | void freedv_set_snr_squelch_thresh        (struct freedv *f, float val) {f->snr_squelch_thresh = val;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_callback_error_pattern    (struct freedv *f, freedv_calback_error_pattern cb, void *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     f->freedv_put_error_pattern = cb; | 
					
						
							|  |  |  |     f->error_pattern_callback_state = state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_carrier_ampl(struct freedv *freedv, int c, float ampl) { | 
					
						
							|  |  |  |     assert(freedv->mode == FREEDV_MODE_700C); | 
					
						
							|  |  |  |     cohpsk_set_carrier_ampl(freedv->cohpsk, c, ampl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTIONS...: freedv_set_alt_modem_samp_rate | 
					
						
							|  |  |  |   AUTHOR......: Brady O'Brien | 
					
						
							|  |  |  |   DATE CREATED: 25 June 2016 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Attempt to set the alternative sample rate on the modem side of the api. Only | 
					
						
							|  |  |  |    a few alternative sample rates are supported. Please see below. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    2400A - 48000, 96000 | 
					
						
							|  |  |  |    2400B - 48000, 96000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   TODO: Implement 2400B rate changing, allow other rate changing. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_set_alt_modem_samp_rate(struct freedv *f, int samp_rate){ | 
					
						
							|  |  |  | 	if(f->mode == FREEDV_MODE_2400A){ | 
					
						
							|  |  |  | 		if(samp_rate == 24000 || samp_rate == 48000 || samp_rate == 96000){ | 
					
						
							|  |  |  | 			fsk_destroy(f->fsk); | 
					
						
							|  |  |  | 			f->fsk = fsk_create_hbr(samp_rate,1200,10,4,1200,1200); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			free(f->tx_bits); | 
					
						
							|  |  |  | 			/* Note: fsk expects tx/rx bits as an array of uint8_ts, not ints */ | 
					
						
							|  |  |  | 			f->tx_bits = (int*) malloc(f->fsk->Nbits*sizeof(uint8_t)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			f->n_nom_modem_samples = f->fsk->N; | 
					
						
							|  |  |  | 			f->n_max_modem_samples = f->fsk->N + (f->fsk->Ts); | 
					
						
							|  |  |  | 			f->n_nat_modem_samples = f->fsk->N; | 
					
						
							|  |  |  | 			f->nin = fsk_nin(f->fsk); | 
					
						
							|  |  |  | 			f->modem_sample_rate = samp_rate; | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		}else | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 	}else if(f->mode == FREEDV_MODE_2400B){ | 
					
						
							|  |  |  | 		if(samp_rate == 48000 || samp_rate == 96000){ | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		}else | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return -1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------* \
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTIONS...: freedv_set_sync | 
					
						
							|  |  |  |   AUTHOR......: David Rowe | 
					
						
							|  |  |  |   DATE CREATED: May 2018 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Extended control of sync state machines, especially for FreeDV 700D. | 
					
						
							|  |  |  |   This mode is required to acquire sync up at very low SNRS.  This is | 
					
						
							|  |  |  |   difficult to implement, for example we may get a false sync, or the | 
					
						
							|  |  |  |   state machine may fall out of sync by mistake during a long fade. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   So with this API call we allow some operator assistance. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Ensure this is called inthe same thread as freedv_rx(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_set_sync(struct freedv *freedv, Sync sync_cmd) { | 
					
						
							|  |  |  |     assert (freedv != NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (freedv->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         ofdm_set_sync(freedv->ofdm, sync_cmd); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct FSK * freedv_get_fsk(struct freedv *f){ | 
					
						
							|  |  |  | 	return f->fsk; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*---------------------------------------------------------------------------*\
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FUNCTIONS...: freedv_get_* | 
					
						
							|  |  |  |   AUTHOR......: Jim Ahlstrom | 
					
						
							|  |  |  |   DATE CREATED: 28 July 2015 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Get some parameters from FreeDV.  It is possible to write a macro using ## for | 
					
						
							|  |  |  |   this, but I wasn't sure it would be 100% portable. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | \*---------------------------------------------------------------------------*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get integers
 | 
					
						
							|  |  |  | int freedv_get_protocol_bits              (struct freedv *f) {return f->n_protocol_bits;} | 
					
						
							|  |  |  | 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 f->modem_sample_rate;} | 
					
						
							|  |  |  | int freedv_get_modem_symbol_rate          (struct freedv *f) {return f->modem_symbol_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;} | 
					
						
							|  |  |  | int freedv_get_total_bit_errors           (struct freedv *f) {return f->total_bit_errors;} | 
					
						
							|  |  |  | int freedv_get_total_bits_coded           (struct freedv *f) {return f->total_bits_coded;} | 
					
						
							|  |  |  | int freedv_get_total_bit_errors_coded     (struct freedv *f) {return f->total_bit_errors_coded;} | 
					
						
							|  |  |  | int freedv_get_sync                       (struct freedv *f) {return f->stats.sync;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_get_sync_interleaver(struct freedv *f) { | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         return f->ofdm->sync_state_interleaver == synced; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int freedv_get_sz_error_pattern(struct freedv *f) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |         /* if diversity disabled callback sends error pattern for upper and lower carriers */ | 
					
						
							|  |  |  |         return f->sz_error_pattern * (2 - f->test_frames_diversity); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else { | 
					
						
							|  |  |  |         return f->sz_error_pattern; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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;} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get modem status
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void freedv_get_modem_extended_stats(struct freedv *f, struct MODEM_STATS *stats) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_1600) | 
					
						
							|  |  |  |         fdmdv_get_demod_stats(f->fdmdv, stats); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_2400A) || (f->mode == FREEDV_MODE_800XA)) { | 
					
						
							|  |  |  |         fsk_get_demod_stats(f->fsk, stats); | 
					
						
							|  |  |  |         float EbNodB = stats->snr_est;                       /* fsk demod actually estimates Eb/No     */ | 
					
						
							|  |  |  |         stats->snr_est = EbNodB + 10.0*log10f(800.0/3000.0); /* so convert to SNR Rb=800, noise B=3000 */ | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_2400B) { | 
					
						
							|  |  |  |         fmfsk_get_demod_stats(f->fmfsk, stats); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ((f->mode == FREEDV_MODE_700) || (f->mode == FREEDV_MODE_700B) || (f->mode == FREEDV_MODE_700C)) { | 
					
						
							|  |  |  |         cohpsk_get_demod_stats(f->cohpsk, stats); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (f->mode == FREEDV_MODE_700D) { | 
					
						
							|  |  |  |         ofdm_get_demod_stats(f->ofdm, stats); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // FreeDV
 | 
					
						
							|  |  |  | 
 |