/*---------------------------------------------------------------------------*\ FILE........: fm.c AUTHOR......: David Rowe DATE CREATED: February 2015 Functions that implement analog FM modulation and demodulation, see also octave/fm.m. \*---------------------------------------------------------------------------*/ /* Copyright (C) 2015 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 . */ /*---------------------------------------------------------------------------*\ DEFINES \*---------------------------------------------------------------------------*/ #define FILT_MEM 200 /*---------------------------------------------------------------------------*\ INCLUDES \*---------------------------------------------------------------------------*/ #include #include #include #include #include #include "codec2_fm.h" #include "fm_fir_coeff.h" #include "comp_prim.h" /*---------------------------------------------------------------------------*\ FUNCTIONS \*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*\ FUNCTION....: fm_create AUTHOR......: David Rowe DATE CREATED: 24 Feb 2015 Create and initialise an instance of the "modem". Returns a pointer to the modem states or NULL on failure. One set of states is sufficient for a full duplex modem. \*---------------------------------------------------------------------------*/ struct FM *fm_create(int nsam) { struct FM *fm; fm = (struct FM*)malloc(sizeof(struct FM)); if (fm == NULL) return NULL; fm->rx_bb = (COMP*)malloc(sizeof(COMP)*(FILT_MEM+nsam)); assert(fm->rx_bb != NULL); fm->rx_bb_filt_prev.real = 0.0; fm->rx_bb_filt_prev.imag = 0.0; fm->lo_phase.real = 1.0; fm->lo_phase.imag = 0.0; fm->tx_phase = 0; fm->rx_dem_mem = (float*)malloc(sizeof(float)*(FILT_MEM+nsam)); assert(fm->rx_dem_mem != NULL); fm->nsam = nsam; return fm; } void fm_destroy(struct FM *fm_states) { free(fm_states->rx_bb); free(fm_states->rx_dem_mem); free(fm_states); } /*---------------------------------------------------------------------------*\ FUNCTION....: fm_demod AUTHOR......: David Rowe DATE CREATED: 24 Feb 2015 Demodulate a FM signal to baseband audio. \*---------------------------------------------------------------------------*/ void fm_demod(struct FM *fm_states, float rx_out[], float rx[]) { float Fs = fm_states->Fs; float fc = fm_states->fc; float wc = 2*M_PI*fc/Fs; float fd = fm_states->fd; float wd = 2*M_PI*fd/Fs; COMP *rx_bb = fm_states->rx_bb + FILT_MEM; COMP wc_rect, rx_bb_filt, rx_bb_diff; float rx_dem; /* float acc; */ float *rx_dem_mem = fm_states->rx_dem_mem + FILT_MEM; int nsam = fm_states->nsam; float mag; int i,k; wc_rect.real = cosf(wc); wc_rect.imag = -sinf(wc); for(i=0; ilo_phase = cmult(fm_states->lo_phase, wc_rect); rx_bb[i] = fcmult(rx[i], fm_states->lo_phase); /* input FIR filter */ rx_bb_filt.real = 0.0; rx_bb_filt.imag = 0.0; for(k=0; klo_phase.real, fm_states->lo_phase.imag); //printf("%f %f %f\n", rx[i], rx_bb[i].real, rx_bb[i].imag); //printf("%f %f\n", rx_bb_filt.real, rx_bb_filt.imag); /* Differentiate first, in rect domain, then find angle, this puts signal on the positive side of the real axis and helps atan2() behaive. */ rx_bb_diff = cmult(rx_bb_filt, cconj(fm_states->rx_bb_filt_prev)); fm_states->rx_bb_filt_prev = rx_bb_filt; rx_dem = atan2f(rx_bb_diff.imag, rx_bb_diff.real); /* limit maximum phase jumps, to remove static type noise at low SNRs */ if (rx_dem > wd) rx_dem = wd; if (rx_dem < -wd) rx_dem = -wd; rx_dem *= (1/wd); //printf("%f %f\n", rx_bb_diff.real, rx_bb_diff.imag); rx_dem_mem[i] = rx_dem; /* acc = 0; for(k=0; klo_phase); fm_states->lo_phase.real /= mag; fm_states->lo_phase.imag /= mag; } /*---------------------------------------------------------------------------*\ FUNCTION....: fm_mod AUTHOR......: Brady O'Brien DATE CREATED: Sept. 10 2015 Modulate an FM signal from a baseband modulating signal struct FM *fm - FM state structure. Can be reused from fm_demod. float tx_in[] - nsam baseband samples to be modulated float tx_out[] - nsam samples in which to place the modulated FM \*---------------------------------------------------------------------------*/ void fm_mod(struct FM *fm_states, float tx_in[], float tx_out[]) { float Fs = fm_states->Fs; //Sampling freq float fc = fm_states->fc; //Center freq float wc = 2*M_PI*fc/Fs; //Center freq in rads/samp float fd = fm_states->fd; //Max deviation in cycles/samp float wd = 2*M_PI*fd/Fs; //Max deviation in rads/samp int nsam = fm_states->nsam; //Samples per batch of modulation float tx_phase = fm_states->tx_phase; //Transmit phase in rads float w; //Temp variable for phase of VFO during loop int i; //Go through the samples, spin the oscillator, and generate some FM for(i=0; i 2*M_PI) tx_phase -= 2*M_PI; tx_out[i] = cosf(tx_phase); } //Save phase back into state struct fm_states->tx_phase = tx_phase; } /*---------------------------------------------------------------------------*\ FUNCTION....: fm_mod AUTHOR......: Brady O'Brien DATE CREATED: Sept. 10 2015 Modulate an FM signal from a baseband modulating signal. Output signal is in complex domain struct FM *fm - FM state structure. Can be reused from fm_demod. float tx_in[] - nsam baseband samples to be modulated COMP tx_out[] - nsam samples in which to place the modulated FM \*---------------------------------------------------------------------------*/ void fm_mod_comp(struct FM *fm_states, float tx_in[], COMP tx_out[]){ float Fs = fm_states->Fs; //Sampling freq float fc = fm_states->fc; //Center freq float wc = 2*M_PI*fc/Fs; //Center freq in rads/samp float fd = fm_states->fd; //Max deviation in cycles/samp float wd = 2*M_PI*fd/Fs; //Max deviation in rads/samp int nsam = fm_states->nsam; //Samples per batch of modulation float tx_phase = fm_states->tx_phase; //Transmit phase in rads float w; //Temp variable for phase of VFO during loop int i; //Go through the samples, spin the oscillator, and generate some FM for(i=0; i 2*M_PI) tx_phase -= 2*M_PI; tx_out[i].real = cosf(tx_phase); tx_out[i].imag = sinf(tx_phase); } //Save phase back into state struct fm_states->tx_phase = tx_phase; }