/* wfmmod.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" // the modulated spectrum spans +/-(deviation + f_high); at the sample rates used for // narrowband modes that exceeds Nyquist, so the bandpass degenerates to a passthrough. static double bpfc_wfmmod (double samplerate, double deviation, double f_high) { double fc = deviation + f_high; double max_fc = 0.45 * samplerate; if (fc > max_fc) fc = max_fc; return fc; } void calc_wfmmod (WFMMOD a) { // pre-emphasis if (a->tau_pre > 0.0) a->pmult = exp(-1.0 / (a->samplerate * a->tau_pre)); else a->pmult = 0.0; a->pnorm = 1.0 / (1.0 - a->pmult); a->pre_z = 0.0; // mod a->sphase = 0.0; a->sdelta = TWOPI * a->deviation / a->samplerate; // bandpass a->bp_fc = bpfc_wfmmod (a->samplerate, a->deviation, a->f_high); } WFMMOD create_wfmmod (int run, int size, double* in, double* out, int rate, double dev, double f_low, double f_high, int pre_run, double tau_pre, int bp_run, int nc, int mp) { WFMMOD a = (WFMMOD) malloc0 (sizeof (wfmmod)); 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->pre_run = pre_run; a->tau_pre = tau_pre; a->bp_run = bp_run; a->nc = nc; a->mp = mp; calc_wfmmod (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_wfmmod (WFMMOD a) { destroy_fircore (a->p); _aligned_free (a); } void flush_wfmmod (WFMMOD a) { a->pre_z = 0.0; a->sphase = 0.0; flush_fircore (a->p); } void xwfmmod (WFMMOD a) { int i; double aud, dp; if (a->run) { for (i = 0; i < a->size; i++) { aud = a->in[2 * i + 0]; if (a->pre_run) { dp = a->pnorm * (aud - a->pmult * a->pre_z); a->pre_z = aud; aud = dp; } dp = aud * a->sdelta; a->sphase += dp; // at the wide deviation, |dp| exceeds TWOPI once samplerate < deviation, // so one subtraction is not enough to bring sphase back into range while (a->sphase >= TWOPI) a->sphase -= TWOPI; while (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 (a->bp_run) xfircore (a->p); } else if (a->in != a->out) memcpy (a->out, a->in, a->size * sizeof (complex)); } void setBuffers_wfmmod (WFMMOD a, double* in, double* out) { a->in = in; a->out = out; calc_wfmmod (a); setBuffers_fircore (a->p, a->out, a->out); } void setSamplerate_wfmmod (WFMMOD a, int rate) { double* impulse; a->samplerate = rate; calc_wfmmod (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_wfmmod (WFMMOD a, int size) { double* impulse; a->size = size; calc_wfmmod (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 SetTXAWFMDeviation (int channel, double deviation) { WFMMOD a = txa[channel].wfmmod.p; double bp_fc = bpfc_wfmmod (a->samplerate, deviation, a->f_high); 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 SetTXAWFMNC (int channel, int nc) { WFMMOD a; double* impulse; EnterCriticalSection (&ch[channel].csDSP); a = txa[channel].wfmmod.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 SetTXAWFMMP (int channel, int mp) { WFMMOD a; a = txa[channel].wfmmod.p; if (a->mp != mp) { a->mp = mp; setMp_fircore (a->p, a->mp); } } PORT void SetTXAWFMAFFreqs (int channel, double low, double high) { WFMMOD a; double* impulse; EnterCriticalSection (&ch[channel].csDSP); a = txa[channel].wfmmod.p; if (a->f_low != low || a->f_high != high) { a->f_low = low; a->f_high = high; a->bp_fc = bpfc_wfmmod (a->samplerate, 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); } PORT void SetTXAWFMPreEmphRun (int channel, int run) { WFMMOD a = txa[channel].wfmmod.p; EnterCriticalSection (&ch[channel].csDSP); if (a->pre_run != run) { a->pre_run = run; a->pre_z = 0.0; } LeaveCriticalSection (&ch[channel].csDSP); } PORT void SetTXAWFMPreEmphTau (int channel, double tau) { WFMMOD a = txa[channel].wfmmod.p; EnterCriticalSection (&ch[channel].csDSP); if (a->tau_pre != tau && tau > 0.0) { a->tau_pre = tau; a->pmult = exp(-1.0 / (a->samplerate * a->tau_pre)); a->pnorm = 1.0 / (1.0 - a->pmult); a->pre_z = 0.0; } LeaveCriticalSection (&ch[channel].csDSP); }