/* fmmod.c This file is part of a program that implements a Software-Defined Radio. Copyright (C) 2013, 2016, 2023 Warren Pratt, NR0V This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. The author can be reached by email at warren@wpratt.com */ #include "comm.h" void calc_fmmod (FMMOD a) { // ctcss gen a->tscale = 1.0 / (1.0 + a->ctcss_level); a->tphase = 0.0; a->tdelta = TWOPI * a->ctcss_freq / a->samplerate; // mod a->sphase = 0.0; a->sdelta = TWOPI * a->deviation / a->samplerate; // bandpass a->bp_fc = a->deviation + a->f_high; } FMMOD create_fmmod (int run, int size, double* in, double* out, int rate, double dev, double f_low, double f_high, int ctcss_run, double ctcss_level, double ctcss_freq, int bp_run, int nc, int mp) { FMMOD a = (FMMOD) malloc0 (sizeof (fmmod)); double* impulse; a->run = run; a->size = size; a->in = in; a->out = out; a->samplerate = (double)rate; a->deviation = dev; a->f_low = f_low; a->f_high = f_high; a->ctcss_run = ctcss_run; a->ctcss_level = ctcss_level; a->ctcss_freq = ctcss_freq; a->bp_run = bp_run; a->nc = nc; a->mp = mp; calc_fmmod (a); impulse = fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); a->p = create_fircore (a->size, a->out, a->out, a->nc, a->mp, impulse); _aligned_free (impulse); return a; } void destroy_fmmod (FMMOD a) { destroy_fircore (a->p); _aligned_free (a); } void flush_fmmod (FMMOD a) { a->tphase = 0.0; a->sphase = 0.0; } void xfmmod (FMMOD a) { int i; double dp, magdp, peak; if (a->run) { peak = 0.0; for (i = 0; i < a->size; i++) { if (a->ctcss_run) { a->tphase += a->tdelta; if (a->tphase >= TWOPI) a->tphase -= TWOPI; a->out[2 * i + 0] = a->tscale * (a->in[2 * i + 0] + a->ctcss_level * cos (a->tphase)); } dp = a->out[2 * i + 0] * a->sdelta; a->sphase += dp; if (a->sphase >= TWOPI) a->sphase -= TWOPI; if (a->sphase < 0.0 ) a->sphase += TWOPI; a->out[2 * i + 0] = 0.7071 * cos (a->sphase); a->out[2 * i + 1] = 0.7071 * sin (a->sphase); if ((magdp = dp) < 0.0) magdp = - magdp; if (magdp > peak) peak = magdp; } //print_deviation ("peakdev.txt", peak, a->samplerate); if (a->bp_run) xfircore (a->p); } else if (a->in != a->out) memcpy (a->out, a->in, a->size * sizeof (complex)); } void setBuffers_fmmod (FMMOD a, double* in, double* out) { a->in = in; a->out = out; calc_fmmod (a); setBuffers_fircore (a->p, a->out, a->out); } void setSamplerate_fmmod (FMMOD a, int rate) { double* impulse; a->samplerate = rate; calc_fmmod (a); impulse = fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); setImpulse_fircore (a->p, impulse, 1); _aligned_free (impulse); } void setSize_fmmod (FMMOD a, int size) { double* impulse; a->size = size; calc_fmmod (a); setSize_fircore (a->p, a->size); impulse = fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); setImpulse_fircore (a->p, impulse, 1); _aligned_free (impulse); } /******************************************************************************************************** * * * TXA Properties * * * ********************************************************************************************************/ PORT void SetTXAFMDeviation (int channel, double deviation) { FMMOD a = txa[channel].fmmod.p; double bp_fc = a->f_high + deviation; double* impulse = fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); setImpulse_fircore (a->p, impulse, 0); _aligned_free (impulse); EnterCriticalSection (&ch[channel].csDSP); a->deviation = deviation; // mod a->sphase = 0.0; a->sdelta = TWOPI * a->deviation / a->samplerate; // bandpass a->bp_fc = bp_fc; setUpdate_fircore (a->p); LeaveCriticalSection (&ch[channel].csDSP); } PORT void SetTXACTCSSFreq (int channel, double freq) { FMMOD a; EnterCriticalSection (&ch[channel].csDSP); a = txa[channel].fmmod.p; a->ctcss_freq = freq; a->tphase = 0.0; a->tdelta = TWOPI * a->ctcss_freq / a->samplerate; LeaveCriticalSection (&ch[channel].csDSP); } PORT void SetTXACTCSSRun (int channel, int run) { EnterCriticalSection (&ch[channel].csDSP); txa[channel].fmmod.p->ctcss_run = run; LeaveCriticalSection (&ch[channel].csDSP); } PORT void SetTXAFMNC (int channel, int nc) { FMMOD a; double* impulse; EnterCriticalSection (&ch[channel].csDSP); a = txa[channel].fmmod.p; if (a->nc != nc) { a->nc = nc; impulse = fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); setNc_fircore (a->p, a->nc, impulse); _aligned_free (impulse); } LeaveCriticalSection (&ch[channel].csDSP); } PORT void SetTXAFMMP (int channel, int mp) { FMMOD a; a = txa[channel].fmmod.p; if (a->mp != mp) { a->mp = mp; setMp_fircore (a->p, a->mp); } } PORT void SetTXAFMAFFreqs (int channel, double low, double high) { FMMOD a; double* impulse; EnterCriticalSection(&ch[channel].csDSP); a = txa[channel].fmmod.p; if (a->f_low != low || a->f_high != high) { a->f_low = low; a->f_high = high; a->bp_fc = a->deviation + a->f_high; impulse = fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); setImpulse_fircore (a->p, impulse, 1); _aligned_free (impulse); } LeaveCriticalSection(&ch[channel].csDSP); }