Merge branch 'wfm' into cpu-optimization
This commit is contained in:
@@ -144,6 +144,8 @@ utilities.c \
|
|||||||
varsamp.c \
|
varsamp.c \
|
||||||
version.c \
|
version.c \
|
||||||
wcpAGC.c \
|
wcpAGC.c \
|
||||||
|
wfmd.c \
|
||||||
|
wfmmod.c \
|
||||||
wisdom.c \
|
wisdom.c \
|
||||||
zetaHat.c
|
zetaHat.c
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,8 @@ utilities.c \
|
|||||||
varsamp.c \
|
varsamp.c \
|
||||||
version.c \
|
version.c \
|
||||||
wcpAGC.c \
|
wcpAGC.c \
|
||||||
|
wfmd.c \
|
||||||
|
wfmmod.c \
|
||||||
wisdom.c \
|
wisdom.c \
|
||||||
zetaHat.c
|
zetaHat.c
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -86,7 +86,7 @@ SOURCES = \
|
|||||||
matchedCW.c meter.c meterlog10.c nbp.c nob.c nobII.c osctrl.c \
|
matchedCW.c meter.c meterlog10.c nbp.c nob.c nobII.c osctrl.c \
|
||||||
patchpanel.c resample.c rmatch.c rnnr.c RXA.c sbnr.c sender.c \
|
patchpanel.c resample.c rmatch.c rnnr.c RXA.c sbnr.c sender.c \
|
||||||
shift.c siphon.c slew.c snb.c ssql.c syncbuffs.c TXA.c \
|
shift.c siphon.c slew.c snb.c ssql.c syncbuffs.c TXA.c \
|
||||||
utilities.c varsamp.c version.c wcpAGC.c wisdom.c zetaHat.c
|
utilities.c varsamp.c version.c wcpAGC.c wfmd.c wfmmod.c wisdom.c zetaHat.c
|
||||||
|
|
||||||
OBJS := $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))
|
OBJS := $(addprefix $(OBJDIR)/, $(SOURCES:.c=.o))
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,23 @@ void create_rxa (int channel)
|
|||||||
max(2048, ch[channel].dsp_size), // number of coefficients for noise filter
|
max(2048, ch[channel].dsp_size), // number of coefficients for noise filter
|
||||||
0); // minimum phase flag
|
0); // minimum phase flag
|
||||||
|
|
||||||
|
// WFM demod
|
||||||
|
rxa[channel].wfmd.p = create_wfmd (
|
||||||
|
0, // run
|
||||||
|
ch[channel].dsp_size, // buffer size
|
||||||
|
rxa[channel].midbuff, // pointer to input buffer
|
||||||
|
rxa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // sample rate
|
||||||
|
75000.0, // deviation
|
||||||
|
20.0, // f_low
|
||||||
|
15000.0, // f_high
|
||||||
|
0.02, // tau - for dc removal
|
||||||
|
1, // run de-emphasis
|
||||||
|
75.0e-6, // de-emphasis time constant
|
||||||
|
0.5, // audio gain
|
||||||
|
max(2048, ch[channel].dsp_size), // # coefs for audio cutoff filter
|
||||||
|
0); // min phase flag for audio cutoff filter
|
||||||
|
|
||||||
// snba
|
// snba
|
||||||
rxa[channel].snba.p = create_snba (
|
rxa[channel].snba.p = create_snba (
|
||||||
0, // run
|
0, // run
|
||||||
@@ -579,6 +596,7 @@ void destroy_rxa (int channel)
|
|||||||
destroy_anf (rxa[channel].anf.p);
|
destroy_anf (rxa[channel].anf.p);
|
||||||
destroy_eqp (rxa[channel].eqp.p);
|
destroy_eqp (rxa[channel].eqp.p);
|
||||||
destroy_snba (rxa[channel].snba.p);
|
destroy_snba (rxa[channel].snba.p);
|
||||||
|
destroy_wfmd (rxa[channel].wfmd.p);
|
||||||
destroy_fmsq (rxa[channel].fmsq.p);
|
destroy_fmsq (rxa[channel].fmsq.p);
|
||||||
destroy_fmd (rxa[channel].fmd.p);
|
destroy_fmd (rxa[channel].fmd.p);
|
||||||
destroy_amd (rxa[channel].amd.p);
|
destroy_amd (rxa[channel].amd.p);
|
||||||
@@ -614,6 +632,7 @@ void flush_rxa (int channel)
|
|||||||
flush_amd (rxa[channel].amd.p);
|
flush_amd (rxa[channel].amd.p);
|
||||||
flush_fmd (rxa[channel].fmd.p);
|
flush_fmd (rxa[channel].fmd.p);
|
||||||
flush_fmsq (rxa[channel].fmsq.p);
|
flush_fmsq (rxa[channel].fmsq.p);
|
||||||
|
flush_wfmd (rxa[channel].wfmd.p);
|
||||||
flush_snba (rxa[channel].snba.p);
|
flush_snba (rxa[channel].snba.p);
|
||||||
flush_eqp (rxa[channel].eqp.p);
|
flush_eqp (rxa[channel].eqp.p);
|
||||||
flush_anf (rxa[channel].anf.p);
|
flush_anf (rxa[channel].anf.p);
|
||||||
@@ -649,6 +668,7 @@ void xrxa (int channel)
|
|||||||
xamd (rxa[channel].amd.p);
|
xamd (rxa[channel].amd.p);
|
||||||
xfmd (rxa[channel].fmd.p);
|
xfmd (rxa[channel].fmd.p);
|
||||||
xfmsq (rxa[channel].fmsq.p);
|
xfmsq (rxa[channel].fmsq.p);
|
||||||
|
xwfmd (rxa[channel].wfmd.p);
|
||||||
xbpsnbain (rxa[channel].bpsnba.p, 1);
|
xbpsnbain (rxa[channel].bpsnba.p, 1);
|
||||||
xbpsnbaout (rxa[channel].bpsnba.p, 1);
|
xbpsnbaout (rxa[channel].bpsnba.p, 1);
|
||||||
xsnba (rxa[channel].snba.p);
|
xsnba (rxa[channel].snba.p);
|
||||||
@@ -733,6 +753,7 @@ void setDSPSamplerate_rxa (int channel)
|
|||||||
setSamplerate_fmd (rxa[channel].fmd.p, ch[channel].dsp_rate);
|
setSamplerate_fmd (rxa[channel].fmd.p, ch[channel].dsp_rate);
|
||||||
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
||||||
setSamplerate_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_rate);
|
setSamplerate_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_wfmd (rxa[channel].wfmd.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_snba (rxa[channel].snba.p, ch[channel].dsp_rate);
|
setSamplerate_snba (rxa[channel].snba.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_eqp (rxa[channel].eqp.p, ch[channel].dsp_rate);
|
setSamplerate_eqp (rxa[channel].eqp.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_anf (rxa[channel].anf.p, ch[channel].dsp_rate);
|
setSamplerate_anf (rxa[channel].anf.p, ch[channel].dsp_rate);
|
||||||
@@ -794,6 +815,8 @@ void setDSPBuffsize_rxa (int channel)
|
|||||||
setSize_fmd (rxa[channel].fmd.p, ch[channel].dsp_size);
|
setSize_fmd (rxa[channel].fmd.p, ch[channel].dsp_size);
|
||||||
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
setBuffers_fmsq (rxa[channel].fmsq.p, rxa[channel].midbuff, rxa[channel].midbuff, rxa[channel].fmd.p->audio);
|
||||||
setSize_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_size);
|
setSize_fmsq (rxa[channel].fmsq.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_wfmd (rxa[channel].wfmd.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
|
setSize_wfmd (rxa[channel].wfmd.p, ch[channel].dsp_size);
|
||||||
setBuffers_snba (rxa[channel].snba.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
setBuffers_snba (rxa[channel].snba.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
setSize_snba (rxa[channel].snba.p, ch[channel].dsp_size);
|
setSize_snba (rxa[channel].snba.p, ch[channel].dsp_size);
|
||||||
setBuffers_eqp (rxa[channel].eqp.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
setBuffers_eqp (rxa[channel].eqp.p, rxa[channel].midbuff, rxa[channel].midbuff);
|
||||||
@@ -857,6 +880,7 @@ void SetRXAMode (int channel, int mode)
|
|||||||
rxa[channel].mode = mode;
|
rxa[channel].mode = mode;
|
||||||
rxa[channel].amd.p->run = 0;
|
rxa[channel].amd.p->run = 0;
|
||||||
rxa[channel].fmd.p->run = 0;
|
rxa[channel].fmd.p->run = 0;
|
||||||
|
rxa[channel].wfmd.p->run = 0;
|
||||||
rxa[channel].agc.p->run = 1;
|
rxa[channel].agc.p->run = 1;
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
@@ -875,6 +899,10 @@ void SetRXAMode (int channel, int mode)
|
|||||||
rxa[channel].fmd.p->run = 1;
|
rxa[channel].fmd.p->run = 1;
|
||||||
rxa[channel].agc.p->run = 0;
|
rxa[channel].agc.p->run = 0;
|
||||||
break;
|
break;
|
||||||
|
case RXA_WFM:
|
||||||
|
rxa[channel].wfmd.p->run = 1;
|
||||||
|
rxa[channel].agc.p->run = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -961,6 +989,7 @@ void RXAbpsnbaCheck (int channel, int mode, int notch_run)
|
|||||||
run_notches = 0;
|
run_notches = 0;
|
||||||
break;
|
break;
|
||||||
case RXA_FM:
|
case RXA_FM:
|
||||||
|
case RXA_WFM:
|
||||||
f_low = +a->abs_low_freq;
|
f_low = +a->abs_low_freq;
|
||||||
f_high = +a->abs_high_freq;
|
f_high = +a->abs_high_freq;
|
||||||
run_notches = 0;
|
run_notches = 0;
|
||||||
@@ -1010,6 +1039,7 @@ void RXAbpsnbaSet (int channel)
|
|||||||
a->position = 1;
|
a->position = 1;
|
||||||
break;
|
break;
|
||||||
case RXA_FM:
|
case RXA_FM:
|
||||||
|
case RXA_WFM:
|
||||||
a->run = rxa[channel].snba.p->run;
|
a->run = rxa[channel].snba.p->run;
|
||||||
a->position = 1;
|
a->position = 1;
|
||||||
break;
|
break;
|
||||||
@@ -1046,6 +1076,7 @@ void RXASetNC (int channel, int nc)
|
|||||||
SetRXAFMSQNC (channel, nc);
|
SetRXAFMSQNC (channel, nc);
|
||||||
SetRXAFMNCde (channel, nc);
|
SetRXAFMNCde (channel, nc);
|
||||||
SetRXAFMNCaud (channel, nc);
|
SetRXAFMNCaud (channel, nc);
|
||||||
|
SetRXAWFMNCaud (channel, nc);
|
||||||
SetChannelState (channel, oldstate, 0);
|
SetChannelState (channel, oldstate, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,4 +1090,5 @@ void RXASetMP (int channel, int mp)
|
|||||||
SetRXAFMSQMP (channel, mp);
|
SetRXAFMSQMP (channel, mp);
|
||||||
SetRXAFMMPde (channel, mp);
|
SetRXAFMMPde (channel, mp);
|
||||||
SetRXAFMMPaud (channel, mp);
|
SetRXAFMMPaud (channel, mp);
|
||||||
|
SetRXAWFMMPaud (channel, mp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ enum rxaMode
|
|||||||
RXA_SPEC,
|
RXA_SPEC,
|
||||||
RXA_DIGL,
|
RXA_DIGL,
|
||||||
RXA_SAM,
|
RXA_SAM,
|
||||||
RXA_DRM
|
RXA_DRM,
|
||||||
|
RXA_WFM
|
||||||
};
|
};
|
||||||
|
|
||||||
enum rxaMeterType
|
enum rxaMeterType
|
||||||
@@ -121,6 +122,10 @@ struct _rxa
|
|||||||
FMSQ p;
|
FMSQ p;
|
||||||
} fmsq;
|
} fmsq;
|
||||||
struct
|
struct
|
||||||
|
{
|
||||||
|
WFMD p;
|
||||||
|
} wfmd;
|
||||||
|
struct
|
||||||
{
|
{
|
||||||
EQP p;
|
EQP p;
|
||||||
} eqp;
|
} eqp;
|
||||||
|
|||||||
@@ -358,6 +358,21 @@ void create_txa (int channel)
|
|||||||
max(2048, ch[channel].dsp_size), // number coefficients for bandpass filter
|
max(2048, ch[channel].dsp_size), // number coefficients for bandpass filter
|
||||||
0); // minimum phase flag
|
0); // minimum phase flag
|
||||||
|
|
||||||
|
txa[channel].wfmmod.p = create_wfmmod (
|
||||||
|
0, // run - OFF by default
|
||||||
|
ch[channel].dsp_size, // size
|
||||||
|
txa[channel].midbuff, // pointer to input buffer
|
||||||
|
txa[channel].midbuff, // pointer to output buffer
|
||||||
|
ch[channel].dsp_rate, // samplerate
|
||||||
|
75000.0, // deviation
|
||||||
|
20.0, // low cutoff frequency
|
||||||
|
15000.0, // high cutoff frequency
|
||||||
|
1, // run pre-emphasis
|
||||||
|
75.0e-6, // pre-emphasis time constant
|
||||||
|
1, // run bandpass filter
|
||||||
|
max(2048, ch[channel].dsp_size), // number coefficients for bandpass filter
|
||||||
|
0); // minimum phase flag
|
||||||
|
|
||||||
txa[channel].gen1.p = create_gen (
|
txa[channel].gen1.p = create_gen (
|
||||||
0, // run
|
0, // run
|
||||||
ch[channel].dsp_size, // buffer size
|
ch[channel].dsp_size, // buffer size
|
||||||
@@ -490,6 +505,7 @@ void destroy_txa (int channel)
|
|||||||
destroy_meter (txa[channel].alcmeter.p);
|
destroy_meter (txa[channel].alcmeter.p);
|
||||||
destroy_uslew (txa[channel].uslew.p);
|
destroy_uslew (txa[channel].uslew.p);
|
||||||
destroy_gen (txa[channel].gen1.p);
|
destroy_gen (txa[channel].gen1.p);
|
||||||
|
destroy_wfmmod (txa[channel].wfmmod.p);
|
||||||
destroy_fmmod (txa[channel].fmmod.p);
|
destroy_fmmod (txa[channel].fmmod.p);
|
||||||
destroy_ammod (txa[channel].ammod.p);
|
destroy_ammod (txa[channel].ammod.p);
|
||||||
destroy_wcpagc (txa[channel].alc.p);
|
destroy_wcpagc (txa[channel].alc.p);
|
||||||
@@ -544,6 +560,7 @@ void flush_txa (int channel)
|
|||||||
flush_wcpagc (txa[channel].alc.p);
|
flush_wcpagc (txa[channel].alc.p);
|
||||||
flush_ammod (txa[channel].ammod.p);
|
flush_ammod (txa[channel].ammod.p);
|
||||||
flush_fmmod (txa[channel].fmmod.p);
|
flush_fmmod (txa[channel].fmmod.p);
|
||||||
|
flush_wfmmod (txa[channel].wfmmod.p);
|
||||||
flush_gen (txa[channel].gen1.p);
|
flush_gen (txa[channel].gen1.p);
|
||||||
flush_uslew (txa[channel].uslew.p);
|
flush_uslew (txa[channel].uslew.p);
|
||||||
flush_meter (txa[channel].alcmeter.p);
|
flush_meter (txa[channel].alcmeter.p);
|
||||||
@@ -580,6 +597,7 @@ void xtxa (int channel)
|
|||||||
xammod (txa[channel].ammod.p); // AM Modulator
|
xammod (txa[channel].ammod.p); // AM Modulator
|
||||||
xemphp (txa[channel].preemph.p, 1); // FM pre-emphasis (second option)
|
xemphp (txa[channel].preemph.p, 1); // FM pre-emphasis (second option)
|
||||||
xfmmod (txa[channel].fmmod.p); // FM Modulator
|
xfmmod (txa[channel].fmmod.p); // FM Modulator
|
||||||
|
xwfmmod (txa[channel].wfmmod.p); // WFM Modulator (pre-emphasis is internal)
|
||||||
xgen (txa[channel].gen1.p); // output signal generator (TUN and Two-tone)
|
xgen (txa[channel].gen1.p); // output signal generator (TUN and Two-tone)
|
||||||
xuslew (txa[channel].uslew.p); // up-slew for AM, FM, and gens
|
xuslew (txa[channel].uslew.p); // up-slew for AM, FM, and gens
|
||||||
xmeter (txa[channel].alcmeter.p); // ALC Meter
|
xmeter (txa[channel].alcmeter.p); // ALC Meter
|
||||||
@@ -653,6 +671,7 @@ void setDSPSamplerate_txa (int channel)
|
|||||||
setSamplerate_wcpagc (txa[channel].alc.p, ch[channel].dsp_rate);
|
setSamplerate_wcpagc (txa[channel].alc.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_ammod (txa[channel].ammod.p, ch[channel].dsp_rate);
|
setSamplerate_ammod (txa[channel].ammod.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_fmmod (txa[channel].fmmod.p, ch[channel].dsp_rate);
|
setSamplerate_fmmod (txa[channel].fmmod.p, ch[channel].dsp_rate);
|
||||||
|
setSamplerate_wfmmod (txa[channel].wfmmod.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_gen (txa[channel].gen1.p, ch[channel].dsp_rate);
|
setSamplerate_gen (txa[channel].gen1.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_uslew (txa[channel].uslew.p, ch[channel].dsp_rate);
|
setSamplerate_uslew (txa[channel].uslew.p, ch[channel].dsp_rate);
|
||||||
setSamplerate_meter (txa[channel].alcmeter.p, ch[channel].dsp_rate);
|
setSamplerate_meter (txa[channel].alcmeter.p, ch[channel].dsp_rate);
|
||||||
@@ -723,6 +742,8 @@ void setDSPBuffsize_txa (int channel)
|
|||||||
setSize_ammod (txa[channel].ammod.p, ch[channel].dsp_size);
|
setSize_ammod (txa[channel].ammod.p, ch[channel].dsp_size);
|
||||||
setBuffers_fmmod (txa[channel].fmmod.p, txa[channel].midbuff, txa[channel].midbuff);
|
setBuffers_fmmod (txa[channel].fmmod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
setSize_fmmod (txa[channel].fmmod.p, ch[channel].dsp_size);
|
setSize_fmmod (txa[channel].fmmod.p, ch[channel].dsp_size);
|
||||||
|
setBuffers_wfmmod (txa[channel].wfmmod.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
|
setSize_wfmmod (txa[channel].wfmmod.p, ch[channel].dsp_size);
|
||||||
setBuffers_gen (txa[channel].gen1.p, txa[channel].midbuff, txa[channel].midbuff);
|
setBuffers_gen (txa[channel].gen1.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
setSize_gen (txa[channel].gen1.p, ch[channel].dsp_size);
|
setSize_gen (txa[channel].gen1.p, ch[channel].dsp_size);
|
||||||
setBuffers_uslew (txa[channel].uslew.p, txa[channel].midbuff, txa[channel].midbuff);
|
setBuffers_uslew (txa[channel].uslew.p, txa[channel].midbuff, txa[channel].midbuff);
|
||||||
@@ -758,6 +779,7 @@ void SetTXAMode (int channel, int mode)
|
|||||||
txa[channel].mode = mode;
|
txa[channel].mode = mode;
|
||||||
txa[channel].ammod.p->run = 0;
|
txa[channel].ammod.p->run = 0;
|
||||||
txa[channel].fmmod.p->run = 0;
|
txa[channel].fmmod.p->run = 0;
|
||||||
|
txa[channel].wfmmod.p->run = 0;
|
||||||
txa[channel].preemph.p->run = 0;
|
txa[channel].preemph.p->run = 0;
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
@@ -779,6 +801,10 @@ void SetTXAMode (int channel, int mode)
|
|||||||
txa[channel].fmmod.p->run = 1;
|
txa[channel].fmmod.p->run = 1;
|
||||||
txa[channel].preemph.p->run = 1;
|
txa[channel].preemph.p->run = 1;
|
||||||
break;
|
break;
|
||||||
|
case TXA_WFM:
|
||||||
|
// wfmmod carries its own RC pre-emphasis; the shared emphp stays off
|
||||||
|
txa[channel].wfmmod.p->run = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -818,10 +844,11 @@ void TXAResCheck (int channel)
|
|||||||
|
|
||||||
int TXAUslewCheck (int channel)
|
int TXAUslewCheck (int channel)
|
||||||
{
|
{
|
||||||
return (txa[channel].ammod.p->run == 1) ||
|
return (txa[channel].ammod.p->run == 1) ||
|
||||||
(txa[channel].fmmod.p->run == 1) ||
|
(txa[channel].fmmod.p->run == 1) ||
|
||||||
(txa[channel].gen0.p->run == 1) ||
|
(txa[channel].wfmmod.p->run == 1) ||
|
||||||
(txa[channel].gen1.p->run == 1);
|
(txa[channel].gen0.p->run == 1) ||
|
||||||
|
(txa[channel].gen1.p->run == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TXASetupBPFilters (int channel)
|
void TXASetupBPFilters (int channel)
|
||||||
@@ -855,6 +882,7 @@ void TXASetupBPFilters (int channel)
|
|||||||
case TXA_AM:
|
case TXA_AM:
|
||||||
case TXA_SAM:
|
case TXA_SAM:
|
||||||
case TXA_FM:
|
case TXA_FM:
|
||||||
|
case TXA_WFM:
|
||||||
if (txa[channel].compressor.p->run)
|
if (txa[channel].compressor.p->run)
|
||||||
{
|
{
|
||||||
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
CalcBandpassFilter (txa[channel].bp0.p, 0.0, txa[channel].f_high, 2.0);
|
||||||
@@ -914,6 +942,7 @@ void TXASetNC (int channel, int nc)
|
|||||||
SetTXAFMEmphNC (channel, nc);
|
SetTXAFMEmphNC (channel, nc);
|
||||||
SetTXAEQNC (channel, nc);
|
SetTXAEQNC (channel, nc);
|
||||||
SetTXAFMNC (channel, nc);
|
SetTXAFMNC (channel, nc);
|
||||||
|
SetTXAWFMNC (channel, nc);
|
||||||
SetTXACFIRNC (channel, nc);
|
SetTXACFIRNC (channel, nc);
|
||||||
SetChannelState (channel, oldstate, 0);
|
SetChannelState (channel, oldstate, 0);
|
||||||
}
|
}
|
||||||
@@ -925,6 +954,7 @@ void TXASetMP (int channel, int mp)
|
|||||||
SetTXAFMEmphMP (channel, mp);
|
SetTXAFMEmphMP (channel, mp);
|
||||||
SetTXAEQMP (channel, mp);
|
SetTXAEQMP (channel, mp);
|
||||||
SetTXAFMMP (channel, mp);
|
SetTXAFMMP (channel, mp);
|
||||||
|
SetTXAWFMMP (channel, mp);
|
||||||
}
|
}
|
||||||
|
|
||||||
PORT
|
PORT
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ enum txaMode
|
|||||||
TXA_SAM,
|
TXA_SAM,
|
||||||
TXA_DRM,
|
TXA_DRM,
|
||||||
TXA_AM_LSB,
|
TXA_AM_LSB,
|
||||||
TXA_AM_USB
|
TXA_AM_USB,
|
||||||
|
TXA_WFM
|
||||||
};
|
};
|
||||||
|
|
||||||
enum txaMeterType
|
enum txaMeterType
|
||||||
@@ -135,6 +136,10 @@ struct _txa
|
|||||||
FMMOD p;
|
FMMOD p;
|
||||||
} fmmod;
|
} fmmod;
|
||||||
struct
|
struct
|
||||||
|
{
|
||||||
|
WFMMOD p;
|
||||||
|
} wfmmod;
|
||||||
|
struct
|
||||||
{
|
{
|
||||||
SIPHON p;
|
SIPHON p;
|
||||||
} sip1;
|
} sip1;
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ CALCC create_calcc (int channel, int runcal, int size, int rate, int ints, int s
|
|||||||
a->stbl = stbl;
|
a->stbl = stbl;
|
||||||
a->npsamps = npsamps;
|
a->npsamps = npsamps;
|
||||||
a->alpha = alpha;
|
a->alpha = alpha;
|
||||||
|
a->outlier_sigma = 0.0;
|
||||||
|
|
||||||
a->info = (int *) malloc0 (16 * sizeof (int));
|
a->info = (int *) malloc0 (16 * sizeof (int));
|
||||||
a->binfo = (int *) malloc0 (16 * sizeof (int));
|
a->binfo = (int *) malloc0 (16 * sizeof (int));
|
||||||
@@ -321,6 +322,125 @@ void rxscheck (int rints, double* tvec, double* coef, int* info)
|
|||||||
if (out < 0.00) *info |= 0x0020;
|
if (out < 0.00) *info |= 0x0020;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yurij_eu2av: fallback rx_scale estimator. It averages the top few
|
||||||
|
// amplitude intervals (ignoring overrange samples) and linearly extrapolates
|
||||||
|
// to full TX scale (env_TX = 1/hw_scale). Used only if the cubic xbuilder
|
||||||
|
// fit fails or is rejected by rxscheck.
|
||||||
|
static int estimate_rx_scale_from_top_intervals(CALCC a, double* rx_scale_out)
|
||||||
|
{
|
||||||
|
const int n_top = 4;
|
||||||
|
double sx[4], sy[4], sw[4];
|
||||||
|
int valid = 0;
|
||||||
|
int b, j;
|
||||||
|
for (b = a->ints - 1; b >= 0 && valid < n_top; b--)
|
||||||
|
{
|
||||||
|
int base = b * a->spi;
|
||||||
|
double sum_x = 0.0, sum_y = 0.0;
|
||||||
|
int n = 0;
|
||||||
|
for (j = 0; j < a->spi; j++)
|
||||||
|
{
|
||||||
|
int k = base + j;
|
||||||
|
double nx = a->env_TX[k] * a->hw_scale;
|
||||||
|
if (nx > 1.0 || nx < 0.0) continue;
|
||||||
|
if (a->env_TX[k] < 1.0e-30 || a->env_RX[k] < 1.0e-30) continue;
|
||||||
|
sum_x += a->env_TX[k];
|
||||||
|
sum_y += a->env_RX[k];
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (n == 0) continue;
|
||||||
|
sx[valid] = sum_x / (double)n;
|
||||||
|
sy[valid] = sum_y / (double)n;
|
||||||
|
sw[valid] = (double)n;
|
||||||
|
valid++;
|
||||||
|
}
|
||||||
|
if (valid < 2) return -1;
|
||||||
|
{
|
||||||
|
double s_w = 0.0, s_x = 0.0, s_y = 0.0, s_xx = 0.0, s_xy = 0.0;
|
||||||
|
double det, aa, bb, target_x, y_at_target;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < valid; i++)
|
||||||
|
{
|
||||||
|
double w = sw[i];
|
||||||
|
s_w += w;
|
||||||
|
s_x += w * sx[i];
|
||||||
|
s_y += w * sy[i];
|
||||||
|
s_xx += w * sx[i] * sx[i];
|
||||||
|
s_xy += w * sx[i] * sy[i];
|
||||||
|
}
|
||||||
|
det = s_w * s_xx - s_x * s_x;
|
||||||
|
if (fabs(det) < 1e-30) return -1;
|
||||||
|
bb = (s_w * s_xy - s_x * s_y) / det;
|
||||||
|
aa = (s_y - bb * s_x) / s_w;
|
||||||
|
target_x = 1.0 / a->hw_scale;
|
||||||
|
y_at_target = aa + bb * target_x;
|
||||||
|
if (y_at_target <= 1e-15) return -1;
|
||||||
|
*rx_scale_out = 1.0 / y_at_target;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yurij_eu2av: robust outlier rejection for the cubic-spline xbuilder.
|
||||||
|
// Fits rx = k*tx through the origin via median ratio, then rejects points
|
||||||
|
// whose residual exceeds sigma * MAD.
|
||||||
|
static int cmp_double(const void* a, const void* b)
|
||||||
|
{
|
||||||
|
double da = *(const double*)a;
|
||||||
|
double db = *(const double*)b;
|
||||||
|
if (da < db) return -1;
|
||||||
|
if (da > db) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double median_double(double* v, int n)
|
||||||
|
{
|
||||||
|
if (n <= 0) return 0.0;
|
||||||
|
if (n % 2 == 1)
|
||||||
|
return v[n / 2];
|
||||||
|
else
|
||||||
|
return 0.5 * (v[n / 2 - 1] + v[n / 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reject_outliers(double* tx, double* rx, int n, double sigma)
|
||||||
|
{
|
||||||
|
const int min_points = 32;
|
||||||
|
int i, keep = 0;
|
||||||
|
double* ratios;
|
||||||
|
double* absres;
|
||||||
|
double med_ratio, med_absres, thr;
|
||||||
|
|
||||||
|
if (n < min_points || sigma <= 0.0) return n;
|
||||||
|
|
||||||
|
ratios = (double*)malloc0(n * sizeof(double));
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
ratios[i] = (tx[i] > 1.0e-30) ? rx[i] / tx[i] : 0.0;
|
||||||
|
qsort(ratios, n, sizeof(double), cmp_double);
|
||||||
|
med_ratio = median_double(ratios, n);
|
||||||
|
_aligned_free(ratios);
|
||||||
|
|
||||||
|
if (fabs(med_ratio) < 1.0e-30) return n;
|
||||||
|
|
||||||
|
absres = (double*)malloc0(n * sizeof(double));
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
absres[i] = fabs(rx[i] - med_ratio * tx[i]);
|
||||||
|
qsort(absres, n, sizeof(double), cmp_double);
|
||||||
|
med_absres = median_double(absres, n);
|
||||||
|
_aligned_free(absres);
|
||||||
|
|
||||||
|
if (med_absres < 1.0e-30) return n;
|
||||||
|
|
||||||
|
thr = sigma * med_absres;
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
if (fabs(rx[i] - med_ratio * tx[i]) <= thr)
|
||||||
|
{
|
||||||
|
tx[keep] = tx[i];
|
||||||
|
rx[keep] = rx[i];
|
||||||
|
keep++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (keep >= min_points) ? keep : n;
|
||||||
|
}
|
||||||
|
|
||||||
void calc (CALCC a)
|
void calc (CALCC a)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -336,21 +456,60 @@ void calc (CALCC a)
|
|||||||
double tvec[3];
|
double tvec[3];
|
||||||
double txrxcoefs[4 * 2];
|
double txrxcoefs[4 * 2];
|
||||||
double rx_scale;
|
double rx_scale;
|
||||||
|
int xb_ok = 0;
|
||||||
|
double* tx_filt;
|
||||||
|
double* rx_filt;
|
||||||
|
int n_filt = 0;
|
||||||
if (a->ints < 16) rints = 1;
|
if (a->ints < 16) rints = 1;
|
||||||
else rints = 2;
|
else rints = 2;
|
||||||
ix = rints - 1;
|
ix = rints - 1;
|
||||||
for (i = 0; i <= rints; i++)
|
for (i = 0; i <= rints; i++)
|
||||||
tvec[i] = (double)i / (double)rints / a->hw_scale;
|
tvec[i] = (double)i / (double)rints / a->hw_scale;
|
||||||
dx = tvec[rints] - tvec[rints - 1];
|
dx = tvec[rints] - tvec[rints - 1];
|
||||||
xbuilder(a->ccbld, a->nsamps, a->env_TX, a->env_RX, rints, tvec, &(a->binfo[0]), txrxcoefs, a->ptol);
|
|
||||||
|
// Yurij_eu2av: build a filtered dataset with overrange samples removed
|
||||||
|
// before running xbuilder. Overrange env_TX*hw_scale > 1.0 can distort
|
||||||
|
// the cubic fit and produce an incorrect rx_scale.
|
||||||
|
tx_filt = (double*)malloc0(a->nsamps * sizeof(double));
|
||||||
|
rx_filt = (double*)malloc0(a->nsamps * sizeof(double));
|
||||||
|
for (i = 0; i < a->nsamps; i++)
|
||||||
|
{
|
||||||
|
double nx = a->env_TX[i] * a->hw_scale;
|
||||||
|
if (nx > 1.0 || nx < 0.0) continue;
|
||||||
|
if (a->env_TX[i] < 1.0e-30 || a->env_RX[i] < 1.0e-30) continue;
|
||||||
|
tx_filt[n_filt] = a->env_TX[i];
|
||||||
|
rx_filt[n_filt] = a->env_RX[i];
|
||||||
|
n_filt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yurij_eu2av: optional outlier rejection before cubic-spline fit.
|
||||||
|
if (a->outlier_sigma > 0.0)
|
||||||
|
n_filt = reject_outliers(tx_filt, rx_filt, n_filt, a->outlier_sigma);
|
||||||
|
|
||||||
|
xbuilder(a->ccbld, n_filt, tx_filt, rx_filt, rints, tvec, &(a->binfo[0]), txrxcoefs, a->ptol);
|
||||||
rxscheck (rints, tvec, txrxcoefs, &a->binfo[7]);
|
rxscheck (rints, tvec, txrxcoefs, &a->binfo[7]);
|
||||||
if ((a->binfo[0] == 0) && (a->binfo[7] == 0))
|
if ((a->binfo[0] == 0) && (a->binfo[7] == 0))
|
||||||
|
{
|
||||||
rx_scale = 1.0 / (txrxcoefs[4 * ix + 0] + dx * (txrxcoefs[4 * ix + 1] + dx * (txrxcoefs[4 * ix + 2] + dx * txrxcoefs[4 * ix + 3])));
|
rx_scale = 1.0 / (txrxcoefs[4 * ix + 0] + dx * (txrxcoefs[4 * ix + 1] + dx * (txrxcoefs[4 * ix + 2] + dx * txrxcoefs[4 * ix + 3])));
|
||||||
else
|
xb_ok = 1;
|
||||||
|
}
|
||||||
|
else if (estimate_rx_scale_from_top_intervals(a, &rx_scale) == 0)
|
||||||
|
{
|
||||||
|
// Yurij_eu2av: xbuilder failed, but the bucket-average fallback
|
||||||
|
// gave a usable rx_scale. Keep binfo[0] bit 0 set for diagnostics.
|
||||||
|
a->binfo[0] |= 0x0001;
|
||||||
|
xb_ok = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_aligned_free(tx_filt);
|
||||||
|
_aligned_free(rx_filt);
|
||||||
|
|
||||||
|
if (!xb_ok)
|
||||||
{
|
{
|
||||||
a->scOK = 0;
|
a->scOK = 0;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a->stbl && _InterlockedAnd (&a->ctrl.running, 1))
|
if (a->stbl && _InterlockedAnd (&a->ctrl.running, 1))
|
||||||
a->rx_scale = a->alpha * a->rx_scale + (1.0 - a->alpha) * rx_scale;
|
a->rx_scale = a->alpha * a->rx_scale + (1.0 - a->alpha) * rx_scale;
|
||||||
else
|
else
|
||||||
@@ -1046,6 +1205,16 @@ void SetPSPtol (int channel, double ptol)
|
|||||||
LeaveCriticalSection (&txa[channel].calcc.cs_update);
|
LeaveCriticalSection (&txa[channel].calcc.cs_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetPSOutlierSigma (int channel, double sigma)
|
||||||
|
{
|
||||||
|
// Yurij_eu2av: 0.0 disables the pre-xbuilder outlier filter.
|
||||||
|
if (sigma < 0.0) sigma = 0.0;
|
||||||
|
EnterCriticalSection (&txa[channel].calcc.cs_update);
|
||||||
|
txa[channel].calcc.p->outlier_sigma = sigma;
|
||||||
|
LeaveCriticalSection (&txa[channel].calcc.cs_update);
|
||||||
|
}
|
||||||
|
|
||||||
PORT
|
PORT
|
||||||
void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs)
|
void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ typedef struct _calcc
|
|||||||
double hw_scale;
|
double hw_scale;
|
||||||
double rx_scale;
|
double rx_scale;
|
||||||
double alpha;
|
double alpha;
|
||||||
|
double outlier_sigma;
|
||||||
|
|
||||||
int tsamps;
|
int tsamps;
|
||||||
double* env_TX;
|
double* env_TX;
|
||||||
|
|||||||
@@ -82,6 +82,8 @@ warren@wpratt.com
|
|||||||
#include "fmd.h"
|
#include "fmd.h"
|
||||||
#include "fmmod.h"
|
#include "fmmod.h"
|
||||||
#include "fmsq.h"
|
#include "fmsq.h"
|
||||||
|
#include "wfmd.h"
|
||||||
|
#include "wfmmod.h"
|
||||||
#include "gain.h"
|
#include "gain.h"
|
||||||
#include "gaussian.h"
|
#include "gaussian.h"
|
||||||
#include "gen.h"
|
#include "gen.h"
|
||||||
|
|||||||
@@ -277,6 +277,7 @@ extern void SetPSHWPeak (int channel, double peak);
|
|||||||
extern void GetPSHWPeak (int channel, double* peak);
|
extern void GetPSHWPeak (int channel, double* peak);
|
||||||
extern void GetPSMaxTX (int channel, double* maxtx);
|
extern void GetPSMaxTX (int channel, double* maxtx);
|
||||||
extern void SetPSPtol (int channel, double ptol);
|
extern void SetPSPtol (int channel, double ptol);
|
||||||
|
extern void SetPSOutlierSigma (int channel, double sigma);
|
||||||
extern void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs);
|
extern void GetPSDisp (int channel, double* x, double* ym, double* yc, double* ys, double* cm, double* cc, double* cs);
|
||||||
extern void SetPSFeedbackRate (int channel, int rate);
|
extern void SetPSFeedbackRate (int channel, int rate);
|
||||||
extern void SetPSPinMode (int channel, int pin);
|
extern void SetPSPinMode (int channel, int pin);
|
||||||
@@ -517,6 +518,30 @@ extern void SetTXAFMNC (int channel, int nc);
|
|||||||
extern void SetTXAFMMP (int channel, int mp);
|
extern void SetTXAFMMP (int channel, int mp);
|
||||||
extern void SetTXAFMAFFreqs (int channel, double low, double high);
|
extern void SetTXAFMAFFreqs (int channel, double low, double high);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interfaces from wfmd.c
|
||||||
|
//
|
||||||
|
|
||||||
|
extern void SetRXAWFMDeviation (int channel, double deviation);
|
||||||
|
extern void SetRXAWFMNCaud (int channel, int nc);
|
||||||
|
extern void SetRXAWFMMPaud (int channel, int mp);
|
||||||
|
extern void SetRXAWFMAFFilter (int channel, double low, double high);
|
||||||
|
extern void SetRXAWFMDeemphRun (int channel, int run);
|
||||||
|
extern void SetRXAWFMDeemphTau (int channel, double tau);
|
||||||
|
extern void SetRXAWFMLimRun (int channel, int run);
|
||||||
|
extern void SetRXAWFMLimGain (int channel, double gaindB);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Interfaces from wfmmod.c
|
||||||
|
//
|
||||||
|
|
||||||
|
extern void SetTXAWFMDeviation (int channel, double deviation);
|
||||||
|
extern void SetTXAWFMNC (int channel, int nc);
|
||||||
|
extern void SetTXAWFMMP (int channel, int mp);
|
||||||
|
extern void SetTXAWFMAFFreqs (int channel, double low, double high);
|
||||||
|
extern void SetTXAWFMPreEmphRun (int channel, int run);
|
||||||
|
extern void SetTXAWFMPreEmphTau (int channel, double tau);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Interfaces from fmsq.c
|
// Interfaces from fmsq.c
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -0,0 +1,322 @@
|
|||||||
|
/* wfmd.c
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013, 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_wfmd (WFMD a)
|
||||||
|
{
|
||||||
|
// discriminator
|
||||||
|
a->pre_i = 0.0;
|
||||||
|
a->pre_q = 0.0;
|
||||||
|
a->again = a->rate / (a->deviation * TWOPI);
|
||||||
|
// dc removal
|
||||||
|
a->mtau = exp(-1.0 / (a->rate * a->tau));
|
||||||
|
a->onem_mtau = 1.0 - a->mtau;
|
||||||
|
a->fmdc = 0.0;
|
||||||
|
// de-emphasis
|
||||||
|
if (a->tau_de > 0.0) a->mde = exp(-1.0 / (a->rate * a->tau_de));
|
||||||
|
else a->mde = 0.0;
|
||||||
|
a->onem_mde = 1.0 - a->mde;
|
||||||
|
a->deemph_z = 0.0;
|
||||||
|
// detector limiter
|
||||||
|
a->plim = create_wcpagc (
|
||||||
|
1, // run - always ON
|
||||||
|
5, // mode
|
||||||
|
1, // 0 for max(I,Q), 1 for envelope
|
||||||
|
a->out, // input buff pointer
|
||||||
|
a->out, // output buff pointer
|
||||||
|
a->size, // io_buffsize
|
||||||
|
(int)a->rate, // sample rate
|
||||||
|
0.001, // tau_attack
|
||||||
|
0.008, // tau_decay
|
||||||
|
4, // n_tau
|
||||||
|
a->lim_gain, // max_gain (sets threshold, initial value)
|
||||||
|
1.0, // var_gain / slope
|
||||||
|
1.0, // fixed_gain
|
||||||
|
1.0, // max_input
|
||||||
|
0.9, // out_targ
|
||||||
|
0.250, // tau_fast_backaverage
|
||||||
|
0.004, // tau_fast_decay
|
||||||
|
4.0, // pop_ratio
|
||||||
|
0, // hang_enable
|
||||||
|
0.500, // tau_hang_backmult
|
||||||
|
0.500, // hangtime
|
||||||
|
2.000, // hang_thresh
|
||||||
|
0.100); // tau_hang_decay
|
||||||
|
}
|
||||||
|
|
||||||
|
void decalc_wfmd (WFMD a)
|
||||||
|
{
|
||||||
|
destroy_wcpagc(a->plim);
|
||||||
|
}
|
||||||
|
|
||||||
|
WFMD create_wfmd (int run, int size, double* in, double* out, int rate, double deviation, double f_low, double f_high,
|
||||||
|
double tau, int deemph_run, double tau_de, double afgain, int nc_aud, int mp_aud)
|
||||||
|
{
|
||||||
|
WFMD a = (WFMD) malloc0 (sizeof (wfmd));
|
||||||
|
double* impulse;
|
||||||
|
a->run = run;
|
||||||
|
a->size = size;
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
a->rate = (double)rate;
|
||||||
|
a->deviation = deviation;
|
||||||
|
a->f_low = f_low;
|
||||||
|
a->f_high = f_high;
|
||||||
|
a->tau = tau;
|
||||||
|
a->deemph_run = deemph_run;
|
||||||
|
a->tau_de = tau_de;
|
||||||
|
a->afgain = afgain;
|
||||||
|
a->nc_aud = nc_aud;
|
||||||
|
a->mp_aud = mp_aud;
|
||||||
|
a->lim_run = 0;
|
||||||
|
a->lim_pre_gain = 0.4;
|
||||||
|
a->lim_gain = 2.5;
|
||||||
|
calc_wfmd (a);
|
||||||
|
a->audio = (double *) malloc0 (a->size * sizeof (complex));
|
||||||
|
// audio filter
|
||||||
|
impulse = fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||||
|
a->paud = create_fircore (a->size, a->audio, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_wfmd (WFMD a)
|
||||||
|
{
|
||||||
|
destroy_fircore (a->paud);
|
||||||
|
_aligned_free (a->audio);
|
||||||
|
decalc_wfmd (a);
|
||||||
|
_aligned_free (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_wfmd (WFMD a)
|
||||||
|
{
|
||||||
|
memset (a->audio, 0, a->size * sizeof (complex));
|
||||||
|
flush_fircore (a->paud);
|
||||||
|
a->pre_i = 0.0;
|
||||||
|
a->pre_q = 0.0;
|
||||||
|
a->fmdc = 0.0;
|
||||||
|
a->deemph_z = 0.0;
|
||||||
|
flush_wcpagc (a->plim);
|
||||||
|
}
|
||||||
|
|
||||||
|
void xwfmd (WFMD a)
|
||||||
|
{
|
||||||
|
if (a->run)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double si, sq, cr, ci, det, aud;
|
||||||
|
for (i = 0; i < a->size; i++)
|
||||||
|
{
|
||||||
|
// quadrature discriminator: det = arg (x[n] * conj (x[n-1]))
|
||||||
|
si = a->in[2 * i + 0];
|
||||||
|
sq = a->in[2 * i + 1];
|
||||||
|
cr = + si * a->pre_i + sq * a->pre_q;
|
||||||
|
ci = - si * a->pre_q + sq * a->pre_i;
|
||||||
|
a->pre_i = si;
|
||||||
|
a->pre_q = sq;
|
||||||
|
det = atan2 (ci, cr);
|
||||||
|
// dc removal, gain, & demod output
|
||||||
|
a->fmdc = a->mtau * a->fmdc + a->onem_mtau * det;
|
||||||
|
aud = a->again * (det - a->fmdc);
|
||||||
|
// de-emphasis
|
||||||
|
if (a->deemph_run)
|
||||||
|
{
|
||||||
|
a->deemph_z = a->mde * a->deemph_z + a->onem_mde * aud;
|
||||||
|
aud = a->deemph_z;
|
||||||
|
}
|
||||||
|
a->audio[2 * i + 0] = aud;
|
||||||
|
a->audio[2 * i + 1] = aud;
|
||||||
|
}
|
||||||
|
// audio filter
|
||||||
|
xfircore (a->paud);
|
||||||
|
if (a->lim_run)
|
||||||
|
{
|
||||||
|
for (i = 0; i < 2 * a->size; i++)
|
||||||
|
a->out[i] *= a->lim_pre_gain;
|
||||||
|
xwcpagc (a->plim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a->in != a->out)
|
||||||
|
memcpy (a->out, a->in, a->size * sizeof (complex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBuffers_wfmd (WFMD a, double* in, double* out)
|
||||||
|
{
|
||||||
|
decalc_wfmd (a);
|
||||||
|
a->in = in;
|
||||||
|
a->out = out;
|
||||||
|
calc_wfmd (a);
|
||||||
|
setBuffers_fircore (a->paud, a->audio, a->out);
|
||||||
|
setBuffers_wcpagc (a->plim, a->out, a->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate_wfmd (WFMD a, int rate)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
decalc_wfmd (a);
|
||||||
|
a->rate = rate;
|
||||||
|
calc_wfmd (a);
|
||||||
|
// audio filter
|
||||||
|
impulse = fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||||
|
setImpulse_fircore (a->paud, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
setSamplerate_wcpagc (a->plim, (int)a->rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSize_wfmd (WFMD a, int size)
|
||||||
|
{
|
||||||
|
double* impulse;
|
||||||
|
decalc_wfmd (a);
|
||||||
|
_aligned_free (a->audio);
|
||||||
|
a->size = size;
|
||||||
|
calc_wfmd (a);
|
||||||
|
a->audio = (double *) malloc0 (a->size * sizeof (complex));
|
||||||
|
// audio filter
|
||||||
|
destroy_fircore (a->paud);
|
||||||
|
impulse = fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||||
|
a->paud = create_fircore (a->size, a->audio, a->out, a->nc_aud, a->mp_aud, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
setSize_wcpagc (a->plim, a->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************************************************************************************************
|
||||||
|
* *
|
||||||
|
* RXA Properties *
|
||||||
|
* *
|
||||||
|
********************************************************************************************************/
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMDeviation (int channel, double deviation)
|
||||||
|
{
|
||||||
|
WFMD a;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = rxa[channel].wfmd.p;
|
||||||
|
a->deviation = deviation;
|
||||||
|
a->again = a->rate / (a->deviation * TWOPI);
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMNCaud (int channel, int nc)
|
||||||
|
{
|
||||||
|
WFMD a;
|
||||||
|
double* impulse;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
a = rxa[channel].wfmd.p;
|
||||||
|
if (a->nc_aud != nc)
|
||||||
|
{
|
||||||
|
a->nc_aud = nc;
|
||||||
|
impulse = fir_bandpass(a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||||
|
setNc_fircore (a->paud, a->nc_aud, impulse);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMMPaud (int channel, int mp)
|
||||||
|
{
|
||||||
|
WFMD a;
|
||||||
|
a = rxa[channel].wfmd.p;
|
||||||
|
if (a->mp_aud != mp)
|
||||||
|
{
|
||||||
|
a->mp_aud = mp;
|
||||||
|
setMp_fircore (a->paud, a->mp_aud);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMAFFilter (int channel, double low, double high)
|
||||||
|
{
|
||||||
|
WFMD a = rxa[channel].wfmd.p;
|
||||||
|
double* impulse;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
if (a->f_low != low || a->f_high != high)
|
||||||
|
{
|
||||||
|
a->f_low = low;
|
||||||
|
a->f_high = high;
|
||||||
|
impulse = fir_bandpass (a->nc_aud, 0.8 * a->f_low, 1.1 * a->f_high, a->rate, 0, 1, a->afgain / (2.0 * a->size));
|
||||||
|
setImpulse_fircore (a->paud, impulse, 1);
|
||||||
|
_aligned_free (impulse);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMDeemphRun (int channel, int run)
|
||||||
|
{
|
||||||
|
WFMD a = rxa[channel].wfmd.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
if (a->deemph_run != run)
|
||||||
|
{
|
||||||
|
a->deemph_run = run;
|
||||||
|
a->deemph_z = 0.0;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMDeemphTau (int channel, double tau)
|
||||||
|
{
|
||||||
|
WFMD a = rxa[channel].wfmd.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
if (a->tau_de != tau && tau > 0.0)
|
||||||
|
{
|
||||||
|
a->tau_de = tau;
|
||||||
|
a->mde = exp(-1.0 / (a->rate * a->tau_de));
|
||||||
|
a->onem_mde = 1.0 - a->mde;
|
||||||
|
a->deemph_z = 0.0;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMLimRun (int channel, int run)
|
||||||
|
{
|
||||||
|
WFMD a = rxa[channel].wfmd.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
if (a->lim_run != run)
|
||||||
|
{
|
||||||
|
a->lim_run = run;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
|
|
||||||
|
PORT
|
||||||
|
void SetRXAWFMLimGain (int channel, double gaindB)
|
||||||
|
{
|
||||||
|
double gain = pow(10.0, gaindB / 20.0);
|
||||||
|
WFMD a = rxa[channel].wfmd.p;
|
||||||
|
EnterCriticalSection (&ch[channel].csDSP);
|
||||||
|
if (a->lim_gain != gain)
|
||||||
|
{
|
||||||
|
decalc_wfmd (a);
|
||||||
|
a->lim_gain = gain;
|
||||||
|
calc_wfmd (a);
|
||||||
|
}
|
||||||
|
LeaveCriticalSection (&ch[channel].csDSP);
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/* wfmd.h
|
||||||
|
|
||||||
|
This file is part of a program that implements a Software-Defined Radio.
|
||||||
|
|
||||||
|
Copyright (C) 2013 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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _wfmd_h
|
||||||
|
#define _wfmd_h
|
||||||
|
#include "firmin.h"
|
||||||
|
#include "wcpAGC.h"
|
||||||
|
typedef struct _wfmd
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int size;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
double rate;
|
||||||
|
double f_low; // audio low cutoff
|
||||||
|
double f_high; // audio high cutoff
|
||||||
|
// quadrature discriminator
|
||||||
|
double deviation; // peak deviation, Hz
|
||||||
|
double again; // discriminator output gain
|
||||||
|
double pre_i; // previous sample, I
|
||||||
|
double pre_q; // previous sample, Q
|
||||||
|
// for dc removal
|
||||||
|
double tau;
|
||||||
|
double mtau;
|
||||||
|
double onem_mtau;
|
||||||
|
double fmdc;
|
||||||
|
// de-emphasis, single-pole RC
|
||||||
|
int deemph_run;
|
||||||
|
double tau_de; // 75.0e-6 (Americas) or 50.0e-6 (elsewhere)
|
||||||
|
double mde;
|
||||||
|
double onem_mde;
|
||||||
|
double deemph_z;
|
||||||
|
// for audio filter
|
||||||
|
double* audio;
|
||||||
|
FIRCORE paud;
|
||||||
|
int nc_aud;
|
||||||
|
int mp_aud;
|
||||||
|
double afgain;
|
||||||
|
// detector limiter
|
||||||
|
WCPAGC plim;
|
||||||
|
int lim_run;
|
||||||
|
double lim_gain;
|
||||||
|
double lim_pre_gain;
|
||||||
|
} wfmd, *WFMD;
|
||||||
|
|
||||||
|
extern WFMD create_wfmd ( int run, int size, double* in, double* out, int rate, double deviation,
|
||||||
|
double f_low, double f_high, double tau, int deemph_run, double tau_de, double afgain,
|
||||||
|
int nc_aud, int mp_aud);
|
||||||
|
|
||||||
|
extern void destroy_wfmd (WFMD a);
|
||||||
|
|
||||||
|
extern void flush_wfmd (WFMD a);
|
||||||
|
|
||||||
|
extern void xwfmd (WFMD a);
|
||||||
|
|
||||||
|
extern void setBuffers_wfmd (WFMD a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_wfmd (WFMD a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_wfmd (WFMD a, int size);
|
||||||
|
|
||||||
|
// RXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMDeviation (int channel, double deviation);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMNCaud (int channel, int nc);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMMPaud (int channel, int mp);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMAFFilter (int channel, double low, double high);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMDeemphRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMDeemphTau (int channel, double tau);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMLimRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetRXAWFMLimGain (int channel, double gaindB);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/* 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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/* wfmmod.h
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _wfmmod_h
|
||||||
|
#define _wfmmod_h
|
||||||
|
#include "firmin.h"
|
||||||
|
typedef struct _wfmmod
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
int size;
|
||||||
|
double* in;
|
||||||
|
double* out;
|
||||||
|
double samplerate;
|
||||||
|
double deviation;
|
||||||
|
double f_low;
|
||||||
|
double f_high;
|
||||||
|
// pre-emphasis, single-pole RC; inverse of the receiver's de-emphasis
|
||||||
|
int pre_run;
|
||||||
|
double tau_pre; // 75.0e-6 (Americas) or 50.0e-6 (elsewhere)
|
||||||
|
double pmult;
|
||||||
|
double pnorm;
|
||||||
|
double pre_z;
|
||||||
|
// mod
|
||||||
|
double sphase;
|
||||||
|
double sdelta;
|
||||||
|
// bandpass
|
||||||
|
int bp_run;
|
||||||
|
double bp_fc;
|
||||||
|
int nc;
|
||||||
|
int mp;
|
||||||
|
FIRCORE p;
|
||||||
|
}wfmmod, *WFMMOD;
|
||||||
|
|
||||||
|
extern 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);
|
||||||
|
|
||||||
|
extern void destroy_wfmmod (WFMMOD a);
|
||||||
|
|
||||||
|
extern void flush_wfmmod (WFMMOD a);
|
||||||
|
|
||||||
|
extern void xwfmmod (WFMMOD a);
|
||||||
|
|
||||||
|
extern void setBuffers_wfmmod (WFMMOD a, double* in, double* out);
|
||||||
|
|
||||||
|
extern void setSamplerate_wfmmod (WFMMOD a, int rate);
|
||||||
|
|
||||||
|
extern void setSize_wfmmod (WFMMOD a, int size);
|
||||||
|
|
||||||
|
// TXA Properties
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMDeviation (int channel, double deviation);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMNC (int channel, int nc);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMMP (int channel, int mp);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMAFFreqs (int channel, double low, double high);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMPreEmphRun (int channel, int run);
|
||||||
|
|
||||||
|
extern __declspec (dllexport) void SetTXAWFMPreEmphTau (int channel, double tau);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user