From 994898d9fcc11f11b267196dfed795dbfc06a6cf Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 23 Jul 2024 00:39:21 +0200 Subject: [PATCH 01/46] WDSP: simplify RXA structs --- .../channelrx/demoddatv/ldpctool/encoder.h | 4 +- wdsp/RXA.cpp | 534 +++++++++--------- wdsp/RXA.hpp | 143 +---- wdsp/amd.cpp | 14 +- wdsp/amsq.cpp | 8 +- wdsp/anb.cpp | 16 +- wdsp/anf.cpp | 42 +- wdsp/anr.cpp | 42 +- wdsp/bandpass.cpp | 6 +- wdsp/bps.cpp | 20 +- wdsp/bps.hpp | 14 +- wdsp/bpsnba.cpp | 4 +- wdsp/cblock.cpp | 2 +- wdsp/emnr.cpp | 24 +- wdsp/eq.cpp | 16 +- wdsp/fmd.cpp | 20 +- wdsp/fmsq.cpp | 10 +- wdsp/gen.cpp | 24 +- wdsp/iir.cpp | 20 +- wdsp/nbp.cpp | 44 +- wdsp/nob.cpp | 18 +- wdsp/patchpanel.cpp | 18 +- wdsp/sender.cpp | 2 +- wdsp/shift.cpp | 6 +- wdsp/siphon.cpp | 4 +- wdsp/snba.cpp | 36 +- wdsp/ssql.cpp | 8 +- wdsp/wcpAGC.cpp | 100 ++-- 28 files changed, 568 insertions(+), 631 deletions(-) diff --git a/plugins/channelrx/demoddatv/ldpctool/encoder.h b/plugins/channelrx/demoddatv/ldpctool/encoder.h index 2c8fbd7bd..0c3527045 100644 --- a/plugins/channelrx/demoddatv/ldpctool/encoder.h +++ b/plugins/channelrx/demoddatv/ldpctool/encoder.h @@ -45,9 +45,7 @@ class LDPCEncoder return b < TYPE(0) ? -a : b > TYPE(0) ? a : TYPE(0); } public: - LDPCEncoder() - { - } + LDPCEncoder() = default void init(LDPCInterface *it) { diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 745ef73bb..9a1cf3d96 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -89,7 +89,7 @@ RXA* RXA::create_rxa ( std::fill(rxa->meter, rxa->meter + RXA_METERTYPE_LAST, 0); // Noise blanker (ANB or "NB") - rxa->anb.p = ANB::create_anb( + rxa->anb = ANB::create_anb( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -102,7 +102,7 @@ RXA* RXA::create_rxa ( 30 // thershold ); // Noise blanker (NOB or "NB2") - rxa->nob.p = NOB::create_nob( + rxa->nob = NOB::create_nob( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -119,7 +119,7 @@ RXA* RXA::create_rxa ( ); // Ftequency shifter - shift to select a slice of spectrum - rxa->shift.p = SHIFT::create_shift ( + rxa->shift = SHIFT::create_shift ( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -128,7 +128,7 @@ RXA* RXA::create_rxa ( 0.0); // amount to shift (Hz) // Input resampler - resample to dsp rate for main processing - rxa->rsmpin.p = RESAMPLE::create_resample ( + rxa->rsmpin = RESAMPLE::create_resample ( 0, // run - will be turned ON below if needed rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -140,7 +140,7 @@ RXA* RXA::create_rxa ( 1.0); // gain // Signal generator - rxa->gen0.p = GEN::create_gen ( + rxa->gen0 = GEN::create_gen ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // input buffer @@ -149,7 +149,7 @@ RXA* RXA::create_rxa ( 2); // mode // Input meter - ADC - rxa->adcmeter.p = METER::create_meter ( + rxa->adcmeter = METER::create_meter ( 0, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -166,12 +166,12 @@ RXA* RXA::create_rxa ( // Notched bandpass section // notch database - rxa->ndb.p = NOTCHDB::create_notchdb ( + rxa->ndb = NOTCHDB::create_notchdb ( 0, // master run for all nbp's 1024); // max number of notches // notched bandpass - rxa->nbp0.p = NBP::create_nbp ( + rxa->nbp0 = NBP::create_nbp ( 1, // run, always runs 0, // run the notches 0, // position @@ -187,10 +187,10 @@ RXA* RXA::create_rxa ( 1.0, // gain 1, // auto-increase notch width 1025, // max number of passbands - rxa->ndb.p); // addr of database pointer + rxa->ndb); // addr of database pointer // bandpass for snba - rxa->bpsnba.p = BPSNBA::create_bpsnba ( + rxa->bpsnba = BPSNBA::create_bpsnba ( 0, // bpsnba run flag 0, // run the notches 0, // position @@ -208,10 +208,10 @@ RXA* RXA::create_rxa ( 1.0, // gain 1, // auto-increase notch width 1025, // max number of passbands - rxa->ndb.p); // addr of database pointer + rxa->ndb); // addr of database pointer // Post filter display send - send spectrum display (after S-meter in the block diagram) - rxa->sender.p = SENDER::create_sender ( + rxa->sender = SENDER::create_sender ( 0, // run 0, // flag 0, // mode @@ -222,7 +222,7 @@ RXA* RXA::create_rxa ( // End notched bandpass section // S-meter - rxa->smeter.p = METER::create_meter ( + rxa->smeter = METER::create_meter ( 1, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -237,7 +237,7 @@ RXA* RXA::create_rxa ( 0); // pointer for gain computation // AM squelch capture (for other modes than FM) - rxa->amsq.p = AMSQ::create_amsq ( + rxa->amsq = AMSQ::create_amsq ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to signal input buffer used by xamsq @@ -254,7 +254,7 @@ RXA* RXA::create_rxa ( 0.0); // muted gain // AM/SAM demodulator - rxa->amd.p = AMD::create_amd ( + rxa->amd = AMD::create_amd ( 0, // run - OFF by default rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -271,7 +271,7 @@ RXA* RXA::create_rxa ( 1.4); // tauI // FM demodulator - rxa->fmd.p = FMD::create_fmd ( + rxa->fmd = FMD::create_fmd ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -294,15 +294,15 @@ RXA* RXA::create_rxa ( 0); // min phase flag for audio cutoff filter // FM squelch apply - rxa->fmsq.p = FMSQ::create_fmsq ( + rxa->fmsq = FMSQ::create_fmsq ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input signal buffer rxa->midbuff, // pointer to output signal buffer - rxa->fmd.p->audio, // pointer to trigger buffer + rxa->fmd->audio, // pointer to trigger buffer rxa->dsp_rate, // sample rate 5000.0, // cutoff freq for noise filter (Hz) - &rxa->fmd.p->pllpole, // pointer to pole frequency of the fmd pll (Hz) + &rxa->fmd->pllpole, // pointer to pole frequency of the fmd pll (Hz) 0.100, // delay time after channel flush 0.001, // tau for noise averaging 0.100, // tau for long noise averaging @@ -316,7 +316,7 @@ RXA* RXA::create_rxa ( 0); // minimum phase flag // Spectral noise blanker (SNB) - rxa->snba.p = SNBA::create_snba ( + rxa->snba = SNBA::create_snba ( 0, // run rxa->midbuff, // input buffer rxa->midbuff, // output buffer @@ -341,7 +341,7 @@ RXA* RXA::create_rxa ( float default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; //float default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; float default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - rxa->eqp.p = EQP::create_eqp ( + rxa->eqp = EQP::create_eqp ( 0, // run - OFF by default rxa->dsp_size, // buffer size std::max(2048, rxa->dsp_size), // number of filter coefficients @@ -357,7 +357,7 @@ RXA* RXA::create_rxa ( } // Auto notch filter - rxa->anf.p = ANF::create_anf ( + rxa->anf = ANF::create_anf ( 0, // run - OFF by default 0, // position rxa->dsp_size, // buffer size @@ -377,7 +377,7 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // LMS noise reduction (ANR or "NR") - rxa->anr.p = ANR::create_anr ( + rxa->anr = ANR::create_anr ( 0, // run - OFF by default 0, // position rxa->dsp_size, // buffer size @@ -397,7 +397,7 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // Spectral noise reduyction (EMNR or "NR2") - rxa->emnr.p = EMNR::create_emnr ( + rxa->emnr = EMNR::create_emnr ( 0, // run 0, // position rxa->dsp_size, // buffer size @@ -413,7 +413,7 @@ RXA* RXA::create_rxa ( 1); // ae_run // AGC - rxa->agc.p = WCPAGC::create_wcpagc ( + rxa->agc = WCPAGC::create_wcpagc ( 1, // run 3, // mode 1, // peakmode = envelope @@ -439,7 +439,7 @@ RXA* RXA::create_rxa ( 0.100); // tau_hang_decay // AGC meter - rxa->agcmeter.p = METER::create_meter ( + rxa->agcmeter = METER::create_meter ( 0, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -451,10 +451,10 @@ RXA* RXA::create_rxa ( RXA_AGC_AV, // index for average value RXA_AGC_PK, // index for peak value RXA_AGC_GAIN, // index for gain value - &rxa->agc.p->gain); // pointer for gain computation + &rxa->agc->gain); // pointer for gain computation // Bandpass filter - After spectral noise reduction in the block diagram - rxa->bp1.p = BANDPASS::create_bandpass ( + rxa->bp1 = BANDPASS::create_bandpass ( 1, // run - used only with ( AM || ANF || ANR || EMNR) 0, // position rxa->dsp_size, // buffer size @@ -469,7 +469,7 @@ RXA* RXA::create_rxa ( 1.0); // gain // Scope/phase display send - pull phase & scope display data - rxa->sip1.p = SIPHON::create_siphon ( + rxa->sip1 = SIPHON::create_siphon ( 0, // run - needed only for phase display 0, // position 0, // mode @@ -481,7 +481,7 @@ RXA* RXA::create_rxa ( 0); // specmode // AM carrier block - rxa->cbl.p = CBL::create_cbl ( + rxa->cbl = CBL::create_cbl ( 0, // run - needed only if set to ON rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -491,7 +491,7 @@ RXA* RXA::create_rxa ( 0.02); // tau // CW peaking filter - rxa->speak.p = SPEAK::create_speak ( + rxa->speak = SPEAK::create_speak ( 0, // run rxa->dsp_size, // buffer size, rxa->midbuff, // pointer to input buffer @@ -509,7 +509,7 @@ RXA* RXA::create_rxa ( double def_freq[2] = {2125.0, 2295.0}; double def_bw[2] = {75.0, 75.0}; double def_gain[2] = {1.0, 1.0}; - rxa->mpeak.p = MPEAK::create_mpeak ( + rxa->mpeak = MPEAK::create_mpeak ( 0, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -524,7 +524,7 @@ RXA* RXA::create_rxa ( } // Syllabic squelch (Voice suelch) - Not in the block diagram - rxa->ssql.p = SSQL::create_ssql( + rxa->ssql = SSQL::create_ssql( 0, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -541,7 +541,7 @@ RXA* RXA::create_rxa ( 2000.0); // max freq for f_to_v converter // PatchPanel - rxa->panel.p = PANEL::create_panel ( + rxa->panel = PANEL::create_panel ( 1, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -555,7 +555,7 @@ RXA* RXA::create_rxa ( // AM squelch apply - absent but in the block diagram // Output resampler - rxa->rsmpout.p = RESAMPLE::create_resample ( + rxa->rsmpout = RESAMPLE::create_resample ( 0, // run - will be turned ON below if needed rxa->dsp_size, // input buffer size rxa->midbuff, // pointer to input buffer @@ -573,36 +573,36 @@ RXA* RXA::create_rxa ( void RXA::destroy_rxa (RXA *rxa) { - RESAMPLE::destroy_resample (rxa->rsmpout.p); - PANEL::destroy_panel (rxa->panel.p); - SSQL::destroy_ssql (rxa->ssql.p); - MPEAK::destroy_mpeak (rxa->mpeak.p); - SPEAK::destroy_speak (rxa->speak.p); - CBL::destroy_cbl (rxa->cbl.p); - SIPHON::destroy_siphon (rxa->sip1.p); - BANDPASS::destroy_bandpass (rxa->bp1.p); - METER::destroy_meter (rxa->agcmeter.p); - WCPAGC::destroy_wcpagc (rxa->agc.p); - EMNR::destroy_emnr (rxa->emnr.p); - ANR::destroy_anr (rxa->anr.p); - ANF::destroy_anf (rxa->anf.p); - EQP::destroy_eqp (rxa->eqp.p); - SNBA::destroy_snba (rxa->snba.p); - FMSQ::destroy_fmsq (rxa->fmsq.p); - FMD::destroy_fmd (rxa->fmd.p); - AMD::destroy_amd (rxa->amd.p); - AMSQ::destroy_amsq (rxa->amsq.p); - METER::destroy_meter (rxa->smeter.p); - SENDER::destroy_sender (rxa->sender.p); - BPSNBA::destroy_bpsnba (rxa->bpsnba.p); - NBP::destroy_nbp (rxa->nbp0.p); - NOTCHDB::destroy_notchdb (rxa->ndb.p); - METER::destroy_meter (rxa->adcmeter.p); - GEN::destroy_gen (rxa->gen0.p); - RESAMPLE::destroy_resample (rxa->rsmpin.p); - SHIFT::destroy_shift (rxa->shift.p); - ANB::destroy_anb(rxa->anb.p); - NOB::destroy_nob(rxa->nob.p); + RESAMPLE::destroy_resample (rxa->rsmpout); + PANEL::destroy_panel (rxa->panel); + SSQL::destroy_ssql (rxa->ssql); + MPEAK::destroy_mpeak (rxa->mpeak); + SPEAK::destroy_speak (rxa->speak); + CBL::destroy_cbl (rxa->cbl); + SIPHON::destroy_siphon (rxa->sip1); + BANDPASS::destroy_bandpass (rxa->bp1); + METER::destroy_meter (rxa->agcmeter); + WCPAGC::destroy_wcpagc (rxa->agc); + EMNR::destroy_emnr (rxa->emnr); + ANR::destroy_anr (rxa->anr); + ANF::destroy_anf (rxa->anf); + EQP::destroy_eqp (rxa->eqp); + SNBA::destroy_snba (rxa->snba); + FMSQ::destroy_fmsq (rxa->fmsq); + FMD::destroy_fmd (rxa->fmd); + AMD::destroy_amd (rxa->amd); + AMSQ::destroy_amsq (rxa->amsq); + METER::destroy_meter (rxa->smeter); + SENDER::destroy_sender (rxa->sender); + BPSNBA::destroy_bpsnba (rxa->bpsnba); + NBP::destroy_nbp (rxa->nbp0); + NOTCHDB::destroy_notchdb (rxa->ndb); + METER::destroy_meter (rxa->adcmeter); + GEN::destroy_gen (rxa->gen0); + RESAMPLE::destroy_resample (rxa->rsmpin); + SHIFT::destroy_shift (rxa->shift); + NOB::destroy_nob(rxa->nob); + ANB::destroy_anb(rxa->anb); delete[] (rxa->midbuff); delete[] (rxa->outbuff); delete[] (rxa->inbuff); @@ -614,76 +614,76 @@ void RXA::flush_rxa (RXA *rxa) std::fill(rxa->inbuff, rxa->inbuff + 1 * rxa->dsp_insize * 2, 0); std::fill(rxa->outbuff, rxa->outbuff + 1 * rxa->dsp_outsize * 2, 0); std::fill(rxa->midbuff, rxa->midbuff + 2 * rxa->dsp_size * 2, 0); - SHIFT::flush_shift (rxa->shift.p); - RESAMPLE::flush_resample (rxa->rsmpin.p); - GEN::flush_gen (rxa->gen0.p); - METER::flush_meter (rxa->adcmeter.p); - NBP::flush_nbp (rxa->nbp0.p); - BPSNBA::flush_bpsnba (rxa->bpsnba.p); - SENDER::flush_sender (rxa->sender.p); - METER::flush_meter (rxa->smeter.p); - AMSQ::flush_amsq (rxa->amsq.p); - AMD::flush_amd (rxa->amd.p); - FMD::flush_fmd (rxa->fmd.p); - FMSQ::flush_fmsq (rxa->fmsq.p); - SNBA::flush_snba (rxa->snba.p); - EQP::flush_eqp (rxa->eqp.p); - ANF::flush_anf (rxa->anf.p); - ANR::flush_anr (rxa->anr.p); - EMNR::flush_emnr (rxa->emnr.p); - WCPAGC::flush_wcpagc (rxa->agc.p); - METER::flush_meter (rxa->agcmeter.p); - BANDPASS::flush_bandpass (rxa->bp1.p); - SIPHON::flush_siphon (rxa->sip1.p); - CBL::flush_cbl (rxa->cbl.p); - SPEAK::flush_speak (rxa->speak.p); - MPEAK::flush_mpeak (rxa->mpeak.p); - SSQL::flush_ssql (rxa->ssql.p); - PANEL::flush_panel (rxa->panel.p); - RESAMPLE::flush_resample (rxa->rsmpout.p); - ANB::flush_anb (rxa->anb.p); - NOB::flush_nob(rxa->nob.p); + ANB::flush_anb (rxa->anb); + NOB::flush_nob(rxa->nob); + SHIFT::flush_shift (rxa->shift); + RESAMPLE::flush_resample (rxa->rsmpin); + GEN::flush_gen (rxa->gen0); + METER::flush_meter (rxa->adcmeter); + NBP::flush_nbp (rxa->nbp0); + BPSNBA::flush_bpsnba (rxa->bpsnba); + SENDER::flush_sender (rxa->sender); + METER::flush_meter (rxa->smeter); + AMSQ::flush_amsq (rxa->amsq); + AMD::flush_amd (rxa->amd); + FMD::flush_fmd (rxa->fmd); + FMSQ::flush_fmsq (rxa->fmsq); + SNBA::flush_snba (rxa->snba); + EQP::flush_eqp (rxa->eqp); + ANF::flush_anf (rxa->anf); + ANR::flush_anr (rxa->anr); + EMNR::flush_emnr (rxa->emnr); + WCPAGC::flush_wcpagc (rxa->agc); + METER::flush_meter (rxa->agcmeter); + BANDPASS::flush_bandpass (rxa->bp1); + SIPHON::flush_siphon (rxa->sip1); + CBL::flush_cbl (rxa->cbl); + SPEAK::flush_speak (rxa->speak); + MPEAK::flush_mpeak (rxa->mpeak); + SSQL::flush_ssql (rxa->ssql); + PANEL::flush_panel (rxa->panel); + RESAMPLE::flush_resample (rxa->rsmpout); } void RXA::xrxa (RXA *rxa) { - ANB::xanb (rxa->anb.p); - NOB::xnob (rxa->nob.p); - SHIFT::xshift (rxa->shift.p); - RESAMPLE::xresample (rxa->rsmpin.p); - GEN::xgen (rxa->gen0.p); - METER::xmeter (rxa->adcmeter.p); - BPSNBA::xbpsnbain (rxa->bpsnba.p, 0); - NBP::xnbp (rxa->nbp0.p, 0); - METER::xmeter (rxa->smeter.p); - SENDER::xsender (rxa->sender.p); - AMSQ::xamsqcap (rxa->amsq.p); - BPSNBA::xbpsnbaout (rxa->bpsnba.p, 0); - AMD::xamd (rxa->amd.p); - FMD::xfmd (rxa->fmd.p); - FMSQ::xfmsq (rxa->fmsq.p); - BPSNBA::xbpsnbain (rxa->bpsnba.p, 1); - BPSNBA::xbpsnbaout (rxa->bpsnba.p, 1); - SNBA::xsnba (rxa->snba.p); - EQP::xeqp (rxa->eqp.p); - ANF::xanf (rxa->anf.p, 0); - ANR::xanr (rxa->anr.p, 0); - EMNR::xemnr (rxa->emnr.p, 0); - BANDPASS::xbandpass (rxa->bp1.p, 0); - WCPAGC::xwcpagc (rxa->agc.p); - ANF::xanf (rxa->anf.p, 1); - ANR::xanr (rxa->anr.p, 1); - EMNR::xemnr (rxa->emnr.p, 1); - BANDPASS::xbandpass (rxa->bp1.p, 1); - METER::xmeter (rxa->agcmeter.p); - SIPHON::xsiphon (rxa->sip1.p, 0); - CBL::xcbl (rxa->cbl.p); - SPEAK::xspeak (rxa->speak.p); - MPEAK::xmpeak (rxa->mpeak.p); - SSQL::xssql (rxa->ssql.p); - PANEL::xpanel (rxa->panel.p); - AMSQ::xamsq (rxa->amsq.p); - RESAMPLE::xresample (rxa->rsmpout.p); + ANB::xanb (rxa->anb); + NOB::xnob (rxa->nob); + SHIFT::xshift (rxa->shift); + RESAMPLE::xresample (rxa->rsmpin); + GEN::xgen (rxa->gen0); + METER::xmeter (rxa->adcmeter); + BPSNBA::xbpsnbain (rxa->bpsnba, 0); + NBP::xnbp (rxa->nbp0, 0); + METER::xmeter (rxa->smeter); + SENDER::xsender (rxa->sender); + AMSQ::xamsqcap (rxa->amsq); + BPSNBA::xbpsnbaout (rxa->bpsnba, 0); + AMD::xamd (rxa->amd); + FMD::xfmd (rxa->fmd); + FMSQ::xfmsq (rxa->fmsq); + BPSNBA::xbpsnbain (rxa->bpsnba, 1); + BPSNBA::xbpsnbaout (rxa->bpsnba, 1); + SNBA::xsnba (rxa->snba); + EQP::xeqp (rxa->eqp); + ANF::xanf (rxa->anf, 0); + ANR::xanr (rxa->anr, 0); + EMNR::xemnr (rxa->emnr, 0); + BANDPASS::xbandpass (rxa->bp1, 0); + WCPAGC::xwcpagc (rxa->agc); + ANF::xanf (rxa->anf, 1); + ANR::xanr (rxa->anr, 1); + EMNR::xemnr (rxa->emnr, 1); + BANDPASS::xbandpass (rxa->bp1, 1); + METER::xmeter (rxa->agcmeter); + SIPHON::xsiphon (rxa->sip1, 0); + CBL::xcbl (rxa->cbl); + SPEAK::xspeak (rxa->speak); + MPEAK::xmpeak (rxa->mpeak); + SSQL::xssql (rxa->ssql); + PANEL::xpanel (rxa->panel); + AMSQ::xamsq (rxa->amsq); + RESAMPLE::xresample (rxa->rsmpout); } void RXA::setInputSamplerate (RXA *rxa, int in_rate) @@ -698,21 +698,21 @@ void RXA::setInputSamplerate (RXA *rxa, int in_rate) delete[] (rxa->inbuff); rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *)malloc0(1 * ch.dsp_insize * sizeof(complex)); // anb - ANB::setBuffers_anb(rxa->anb.p, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb(rxa->anb.p, rxa->dsp_insize); - ANB::setSamplerate_anb(rxa->anb.p, rxa->in_rate); + ANB::setBuffers_anb(rxa->anb, rxa->inbuff, rxa->inbuff); + ANB::setSize_anb(rxa->anb, rxa->dsp_insize); + ANB::setSamplerate_anb(rxa->anb, rxa->in_rate); // nob - NOB::setBuffers_nob(rxa->nob.p, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob.p, rxa->dsp_insize); - NOB::setSamplerate_nob(rxa->nob.p, rxa->in_rate); + NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); + NOB::setSize_nob(rxa->nob, rxa->dsp_insize); + NOB::setSamplerate_nob(rxa->nob, rxa->in_rate); // shift - SHIFT::setBuffers_shift (rxa->shift.p, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift.p, rxa->dsp_insize); - SHIFT::setSamplerate_shift (rxa->shift.p, rxa->in_rate); + SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); + SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); + SHIFT::setSamplerate_shift (rxa->shift, rxa->in_rate); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin.p, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin.p, rxa->dsp_insize); - RESAMPLE::setInRate_resample (rxa->rsmpin.p, rxa->in_rate); + RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); + RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); + RESAMPLE::setInRate_resample (rxa->rsmpin, rxa->in_rate); ResCheck (*rxa); } @@ -728,8 +728,8 @@ void RXA::setOutputSamplerate (RXA *rxa, int out_rate) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * ch.dsp_outsize * sizeof(complex)); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout.p, rxa->midbuff, rxa->outbuff); - RESAMPLE::setOutRate_resample (rxa->rsmpout.p, rxa->out_rate); + RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); + RESAMPLE::setOutRate_resample (rxa->rsmpout, rxa->out_rate); ResCheck (*rxa); } @@ -752,47 +752,47 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); // anb - ANB::setBuffers_anb (rxa->anb.p, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb(rxa->anb.p, rxa->dsp_insize); + ANB::setBuffers_anb (rxa->anb, rxa->inbuff, rxa->inbuff); + ANB::setSize_anb(rxa->anb, rxa->dsp_insize); // nob - NOB::setBuffers_nob(rxa->nob.p, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob.p, rxa->dsp_insize); + NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); + NOB::setSize_nob(rxa->nob, rxa->dsp_insize); // shift - SHIFT::setBuffers_shift (rxa->shift.p, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift.p, rxa->dsp_insize); + SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); + SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin.p, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin.p, rxa->dsp_insize); - RESAMPLE::setOutRate_resample (rxa->rsmpin.p, rxa->dsp_rate); + RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); + RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); + RESAMPLE::setOutRate_resample (rxa->rsmpin, rxa->dsp_rate); // dsp_rate blocks - GEN::setSamplerate_gen (rxa->gen0.p, rxa->dsp_rate); - METER::setSamplerate_meter (rxa->adcmeter.p, rxa->dsp_rate); - NBP::setSamplerate_nbp (rxa->nbp0.p, rxa->dsp_rate); - BPSNBA::setSamplerate_bpsnba (rxa->bpsnba.p, rxa->dsp_rate); - METER::setSamplerate_meter (rxa->smeter.p, rxa->dsp_rate); - SENDER::setSamplerate_sender (rxa->sender.p, rxa->dsp_rate); - AMSQ::setSamplerate_amsq (rxa->amsq.p, rxa->dsp_rate); - AMD::setSamplerate_amd (rxa->amd.p, rxa->dsp_rate); - FMD::setSamplerate_fmd (rxa->fmd.p, rxa->dsp_rate); - FMSQ::setBuffers_fmsq (rxa->fmsq.p, rxa->midbuff, rxa->midbuff, rxa->fmd.p->audio); - FMSQ::setSamplerate_fmsq (rxa->fmsq.p, rxa->dsp_rate); - SNBA::setSamplerate_snba (rxa->snba.p, rxa->dsp_rate); - EQP::setSamplerate_eqp (rxa->eqp.p, rxa->dsp_rate); - ANF::setSamplerate_anf (rxa->anf.p, rxa->dsp_rate); - ANR::setSamplerate_anr (rxa->anr.p, rxa->dsp_rate); - EMNR::setSamplerate_emnr (rxa->emnr.p, rxa->dsp_rate); - BANDPASS::setSamplerate_bandpass (rxa->bp1.p, rxa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (rxa->agc.p, rxa->dsp_rate); - METER::setSamplerate_meter (rxa->agcmeter.p, rxa->dsp_rate); - SIPHON::setSamplerate_siphon (rxa->sip1.p, rxa->dsp_rate); - CBL::setSamplerate_cbl (rxa->cbl.p, rxa->dsp_rate); - SPEAK::setSamplerate_speak (rxa->speak.p, rxa->dsp_rate); - MPEAK::setSamplerate_mpeak (rxa->mpeak.p, rxa->dsp_rate); - SSQL::setSamplerate_ssql (rxa->ssql.p, rxa->dsp_rate); - PANEL::setSamplerate_panel (rxa->panel.p, rxa->dsp_rate); + GEN::setSamplerate_gen (rxa->gen0, rxa->dsp_rate); + METER::setSamplerate_meter (rxa->adcmeter, rxa->dsp_rate); + NBP::setSamplerate_nbp (rxa->nbp0, rxa->dsp_rate); + BPSNBA::setSamplerate_bpsnba (rxa->bpsnba, rxa->dsp_rate); + METER::setSamplerate_meter (rxa->smeter, rxa->dsp_rate); + SENDER::setSamplerate_sender (rxa->sender, rxa->dsp_rate); + AMSQ::setSamplerate_amsq (rxa->amsq, rxa->dsp_rate); + AMD::setSamplerate_amd (rxa->amd, rxa->dsp_rate); + FMD::setSamplerate_fmd (rxa->fmd, rxa->dsp_rate); + FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + FMSQ::setSamplerate_fmsq (rxa->fmsq, rxa->dsp_rate); + SNBA::setSamplerate_snba (rxa->snba, rxa->dsp_rate); + EQP::setSamplerate_eqp (rxa->eqp, rxa->dsp_rate); + ANF::setSamplerate_anf (rxa->anf, rxa->dsp_rate); + ANR::setSamplerate_anr (rxa->anr, rxa->dsp_rate); + EMNR::setSamplerate_emnr (rxa->emnr, rxa->dsp_rate); + BANDPASS::setSamplerate_bandpass (rxa->bp1, rxa->dsp_rate); + WCPAGC::setSamplerate_wcpagc (rxa->agc, rxa->dsp_rate); + METER::setSamplerate_meter (rxa->agcmeter, rxa->dsp_rate); + SIPHON::setSamplerate_siphon (rxa->sip1, rxa->dsp_rate); + CBL::setSamplerate_cbl (rxa->cbl, rxa->dsp_rate); + SPEAK::setSamplerate_speak (rxa->speak, rxa->dsp_rate); + MPEAK::setSamplerate_mpeak (rxa->mpeak, rxa->dsp_rate); + SSQL::setSamplerate_ssql (rxa->ssql, rxa->dsp_rate); + PANEL::setSamplerate_panel (rxa->panel, rxa->dsp_rate); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout.p, rxa->midbuff, rxa->outbuff); - RESAMPLE::setInRate_resample (rxa->rsmpout.p, rxa->dsp_rate); + RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); + RESAMPLE::setInRate_resample (rxa->rsmpout, rxa->dsp_rate); ResCheck (*rxa); } @@ -817,75 +817,75 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); // anb - ANB::setBuffers_anb (rxa->anb.p, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb (rxa->anb.p, rxa->dsp_insize); + ANB::setBuffers_anb (rxa->anb, rxa->inbuff, rxa->inbuff); + ANB::setSize_anb (rxa->anb, rxa->dsp_insize); // nob - NOB::setBuffers_nob(rxa->nob.p, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob.p, rxa->dsp_insize); + NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); + NOB::setSize_nob(rxa->nob, rxa->dsp_insize); // shift - SHIFT::setBuffers_shift (rxa->shift.p, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift.p, rxa->dsp_insize); + SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); + SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin.p, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin.p, rxa->dsp_insize); + RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); + RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); // dsp_size blocks - GEN::setBuffers_gen (rxa->gen0.p, rxa->midbuff, rxa->midbuff); - GEN::setSize_gen (rxa->gen0.p, rxa->dsp_size); - METER::setBuffers_meter (rxa->adcmeter.p, rxa->midbuff); - METER::setSize_meter (rxa->adcmeter.p, rxa->dsp_size); - NBP::setBuffers_nbp (rxa->nbp0.p, rxa->midbuff, rxa->midbuff); - NBP::setSize_nbp (rxa->nbp0.p, rxa->dsp_size); - BPSNBA::setBuffers_bpsnba (rxa->bpsnba.p, rxa->midbuff, rxa->midbuff); - BPSNBA::setSize_bpsnba (rxa->bpsnba.p, rxa->dsp_size); - METER::setBuffers_meter (rxa->smeter.p, rxa->midbuff); - METER::setSize_meter (rxa->smeter.p, rxa->dsp_size); - SENDER::setBuffers_sender (rxa->sender.p, rxa->midbuff); - SENDER::setSize_sender (rxa->sender.p, rxa->dsp_size); - AMSQ::setBuffers_amsq (rxa->amsq.p, rxa->midbuff, rxa->midbuff, rxa->midbuff); - AMSQ::setSize_amsq (rxa->amsq.p, rxa->dsp_size); - AMD::setBuffers_amd (rxa->amd.p, rxa->midbuff, rxa->midbuff); - AMD::setSize_amd (rxa->amd.p, rxa->dsp_size); - FMD::setBuffers_fmd (rxa->fmd.p, rxa->midbuff, rxa->midbuff); - FMD::setSize_fmd (rxa->fmd.p, rxa->dsp_size); - FMSQ::setBuffers_fmsq (rxa->fmsq.p, rxa->midbuff, rxa->midbuff, rxa->fmd.p->audio); - FMSQ::setSize_fmsq (rxa->fmsq.p, rxa->dsp_size); - SNBA::setBuffers_snba (rxa->snba.p, rxa->midbuff, rxa->midbuff); - SNBA::setSize_snba (rxa->snba.p, rxa->dsp_size); - EQP::setBuffers_eqp (rxa->eqp.p, rxa->midbuff, rxa->midbuff); - EQP::setSize_eqp (rxa->eqp.p, rxa->dsp_size); - ANF::setBuffers_anf (rxa->anf.p, rxa->midbuff, rxa->midbuff); - ANF::setSize_anf (rxa->anf.p, rxa->dsp_size); - ANR::setBuffers_anr (rxa->anr.p, rxa->midbuff, rxa->midbuff); - ANR::setSize_anr (rxa->anr.p, rxa->dsp_size); - EMNR::setBuffers_emnr (rxa->emnr.p, rxa->midbuff, rxa->midbuff); - EMNR::setSize_emnr (rxa->emnr.p, rxa->dsp_size); - BANDPASS::setBuffers_bandpass (rxa->bp1.p, rxa->midbuff, rxa->midbuff); - BANDPASS::setSize_bandpass (rxa->bp1.p, rxa->dsp_size); - WCPAGC::setBuffers_wcpagc (rxa->agc.p, rxa->midbuff, rxa->midbuff); - WCPAGC::setSize_wcpagc (rxa->agc.p, rxa->dsp_size); - METER::setBuffers_meter (rxa->agcmeter.p, rxa->midbuff); - METER::setSize_meter (rxa->agcmeter.p, rxa->dsp_size); - SIPHON::setBuffers_siphon (rxa->sip1.p, rxa->midbuff); - SIPHON::setSize_siphon (rxa->sip1.p, rxa->dsp_size); - CBL::setBuffers_cbl (rxa->cbl.p, rxa->midbuff, rxa->midbuff); - CBL::setSize_cbl (rxa->cbl.p, rxa->dsp_size); - SPEAK::setBuffers_speak (rxa->speak.p, rxa->midbuff, rxa->midbuff); - SPEAK::setSize_speak (rxa->speak.p, rxa->dsp_size); - MPEAK::setBuffers_mpeak (rxa->mpeak.p, rxa->midbuff, rxa->midbuff); - MPEAK::setSize_mpeak (rxa->mpeak.p, rxa->dsp_size); - SSQL::setBuffers_ssql (rxa->ssql.p, rxa->midbuff, rxa->midbuff); - SSQL::setSize_ssql (rxa->ssql.p, rxa->dsp_size); - PANEL::setBuffers_panel (rxa->panel.p, rxa->midbuff, rxa->midbuff); - PANEL::setSize_panel (rxa->panel.p, rxa->dsp_size); + GEN::setBuffers_gen (rxa->gen0, rxa->midbuff, rxa->midbuff); + GEN::setSize_gen (rxa->gen0, rxa->dsp_size); + METER::setBuffers_meter (rxa->adcmeter, rxa->midbuff); + METER::setSize_meter (rxa->adcmeter, rxa->dsp_size); + NBP::setBuffers_nbp (rxa->nbp0, rxa->midbuff, rxa->midbuff); + NBP::setSize_nbp (rxa->nbp0, rxa->dsp_size); + BPSNBA::setBuffers_bpsnba (rxa->bpsnba, rxa->midbuff, rxa->midbuff); + BPSNBA::setSize_bpsnba (rxa->bpsnba, rxa->dsp_size); + METER::setBuffers_meter (rxa->smeter, rxa->midbuff); + METER::setSize_meter (rxa->smeter, rxa->dsp_size); + SENDER::setBuffers_sender (rxa->sender, rxa->midbuff); + SENDER::setSize_sender (rxa->sender, rxa->dsp_size); + AMSQ::setBuffers_amsq (rxa->amsq, rxa->midbuff, rxa->midbuff, rxa->midbuff); + AMSQ::setSize_amsq (rxa->amsq, rxa->dsp_size); + AMD::setBuffers_amd (rxa->amd, rxa->midbuff, rxa->midbuff); + AMD::setSize_amd (rxa->amd, rxa->dsp_size); + FMD::setBuffers_fmd (rxa->fmd, rxa->midbuff, rxa->midbuff); + FMD::setSize_fmd (rxa->fmd, rxa->dsp_size); + FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + FMSQ::setSize_fmsq (rxa->fmsq, rxa->dsp_size); + SNBA::setBuffers_snba (rxa->snba, rxa->midbuff, rxa->midbuff); + SNBA::setSize_snba (rxa->snba, rxa->dsp_size); + EQP::setBuffers_eqp (rxa->eqp, rxa->midbuff, rxa->midbuff); + EQP::setSize_eqp (rxa->eqp, rxa->dsp_size); + ANF::setBuffers_anf (rxa->anf, rxa->midbuff, rxa->midbuff); + ANF::setSize_anf (rxa->anf, rxa->dsp_size); + ANR::setBuffers_anr (rxa->anr, rxa->midbuff, rxa->midbuff); + ANR::setSize_anr (rxa->anr, rxa->dsp_size); + EMNR::setBuffers_emnr (rxa->emnr, rxa->midbuff, rxa->midbuff); + EMNR::setSize_emnr (rxa->emnr, rxa->dsp_size); + BANDPASS::setBuffers_bandpass (rxa->bp1, rxa->midbuff, rxa->midbuff); + BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); + WCPAGC::setBuffers_wcpagc (rxa->agc, rxa->midbuff, rxa->midbuff); + WCPAGC::setSize_wcpagc (rxa->agc, rxa->dsp_size); + METER::setBuffers_meter (rxa->agcmeter, rxa->midbuff); + METER::setSize_meter (rxa->agcmeter, rxa->dsp_size); + SIPHON::setBuffers_siphon (rxa->sip1, rxa->midbuff); + SIPHON::setSize_siphon (rxa->sip1, rxa->dsp_size); + CBL::setBuffers_cbl (rxa->cbl, rxa->midbuff, rxa->midbuff); + CBL::setSize_cbl (rxa->cbl, rxa->dsp_size); + SPEAK::setBuffers_speak (rxa->speak, rxa->midbuff, rxa->midbuff); + SPEAK::setSize_speak (rxa->speak, rxa->dsp_size); + MPEAK::setBuffers_mpeak (rxa->mpeak, rxa->midbuff, rxa->midbuff); + MPEAK::setSize_mpeak (rxa->mpeak, rxa->dsp_size); + SSQL::setBuffers_ssql (rxa->ssql, rxa->midbuff, rxa->midbuff); + SSQL::setSize_ssql (rxa->ssql, rxa->dsp_size); + PANEL::setBuffers_panel (rxa->panel, rxa->midbuff, rxa->midbuff); + PANEL::setSize_panel (rxa->panel, rxa->dsp_size); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout.p, rxa->midbuff, rxa->outbuff); - RESAMPLE::setSize_resample (rxa->rsmpout.p, rxa->dsp_size); + RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); + RESAMPLE::setSize_resample (rxa->rsmpout, rxa->dsp_size); } void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) { SENDER::SetSpectrum(*this, 1, spectrumProbe); - sender.p->run = 1; + sender->run = 1; } /******************************************************************************************************** @@ -899,33 +899,33 @@ void RXA::SetMode (RXA& rxa, int mode) if (rxa.mode != mode) { int amd_run = (mode == RXA_AM) || (mode == RXA_SAM); - bpsnbaCheck (rxa, mode, rxa.ndb.p->master_run); + bpsnbaCheck (rxa, mode, rxa.ndb->master_run); bp1Check ( rxa, amd_run, - rxa.snba.p->run, - rxa.emnr.p->run, - rxa.anf.p->run, - rxa.anr.p->run + rxa.snba->run, + rxa.emnr->run, + rxa.anf->run, + rxa.anr->run ); rxa.mode = mode; - rxa.amd.p->run = 0; - rxa.fmd.p->run = 0; + rxa.amd->run = 0; + rxa.fmd->run = 0; switch (mode) { case RXA_AM: - rxa.amd.p->run = 1; - rxa.amd.p->mode = 0; + rxa.amd->run = 1; + rxa.amd->mode = 0; break; case RXA_SAM: - rxa.amd.p->run = 1; - rxa.amd.p->mode = 1; + rxa.amd->run = 1; + rxa.amd->mode = 1; break; case RXA_DSB: break; case RXA_FM: - rxa.fmd.p->run = 1; + rxa.fmd->run = 1; break; default: @@ -940,12 +940,12 @@ void RXA::SetMode (RXA& rxa, int mode) void RXA::ResCheck (RXA& rxa) { // turn OFF/ON resamplers depending upon whether they're needed - RESAMPLE *a = rxa.rsmpin.p; + RESAMPLE *a = rxa.rsmpin; if (rxa.in_rate != rxa.dsp_rate) a->run = 1; else a->run = 0; - a = rxa.rsmpout.p; + a = rxa.rsmpout; if (rxa.dsp_rate != rxa.out_rate) a->run = 1; else @@ -961,7 +961,7 @@ void RXA::bp1Check ( int anr_run ) { - BANDPASS *a = rxa.bp1.p; + BANDPASS *a = rxa.bp1; float gain; if (amd_run || snba_run || @@ -978,13 +978,13 @@ void RXA::bp1Check ( void RXA::bp1Set (RXA& rxa) { - BANDPASS *a = rxa.bp1.p; + BANDPASS *a = rxa.bp1; int old = a->run; - if ((rxa.amd.p->run == 1) || - (rxa.snba.p->run == 1) || - (rxa.emnr.p->run == 1) || - (rxa.anf.p->run == 1) || - (rxa.anr.p->run == 1) + if ((rxa.amd->run == 1) || + (rxa.snba->run == 1) || + (rxa.emnr->run == 1) || + (rxa.anf->run == 1) || + (rxa.anr->run == 1) ) a->run = 1; else @@ -998,7 +998,7 @@ void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) { // for BPSNBA: set run, position, freqs, run_notches // call this upon change in RXA_mode, snba_run, notch_master_run - BPSNBA *a = rxa.bpsnba.p; + BPSNBA *a = rxa.bpsnba; float f_low = 0.0, f_high = 0.0; int run_notches = 0; switch (mode) @@ -1052,29 +1052,29 @@ void RXA::bpsnbaSet (RXA& rxa) { // for BPSNBA: set run, position, freqs, run_notches // call this upon change in RXA_mode, snba_run, notch_master_run - BPSNBA *a = rxa.bpsnba.p; + BPSNBA *a = rxa.bpsnba; switch (rxa.mode) { case RXA_LSB: case RXA_CWL: case RXA_DIGL: - a->run = rxa.snba.p->run; + a->run = rxa.snba->run; a->position = 0; break; case RXA_USB: case RXA_CWU: case RXA_DIGU: - a->run = rxa.snba.p->run; + a->run = rxa.snba->run; a->position = 0; break; case RXA_AM: case RXA_SAM: case RXA_DSB: - a->run = rxa.snba.p->run; + a->run = rxa.snba->run; a->position = 1; break; case RXA_FM: - a->run = rxa.snba.p->run; + a->run = rxa.snba->run; a->position = 1; break; case RXA_DRM: diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 7ccff2224..30b66beb2 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -98,118 +98,37 @@ public: int mode; double meter[RXA_METERTYPE_LAST]; - struct - { - METER *p; - } smeter, adcmeter, agcmeter; - struct - { - SHIFT *p; - } shift; - struct - { - RESAMPLE *p; - } rsmpin, rsmpout; - struct - { - GEN *p; - } gen0; - struct - { - BANDPASS *p; - } bp1; - struct - { - BPS *p; - } bps1; - struct - { - NOTCHDB *p; - } ndb; - struct - { - NBP *p; - } nbp0; - struct - { - BPSNBA *p; - } bpsnba; - struct - { - SNBA *p; - } snba; - struct - { - SENDER *p; - } sender; - struct - { - AMSQ *p; - } amsq; - struct - { - AMD *p; - } amd; - struct - { - FMD *p; - } fmd; - struct - { - FMSQ *p; - } fmsq; - struct - { - EQP *p; - } eqp; - struct - { - ANF *p; - } anf; - struct - { - ANR *p; - } anr; - struct - { - EMNR *p; - } emnr; - struct - { - WCPAGC *p; - } agc; - struct - { - SPEAK *p; - } speak; - struct - { - MPEAK *p; - } mpeak; - struct - { - PANEL *p; - } panel; - struct - { - SIPHON *p; - } sip1; - struct - { - CBL *p; - } cbl; - struct - { - SSQL *p; - } ssql; - struct - { - ANB *p; - } anb; - struct - { - NOB *p; - } nob; + ANB *anb; + NOB *nob; + SHIFT *shift; + RESAMPLE *rsmpin; + GEN *gen0; + METER *adcmeter; + NOTCHDB *ndb; + NBP *nbp0; + BPSNBA *bpsnba; + SENDER *sender; + METER *smeter; + AMSQ *amsq; + AMD *amd; + FMD *fmd; + FMSQ *fmsq; + SNBA *snba; + EQP *eqp; + ANF *anf; + ANR *anr; + EMNR *emnr; + WCPAGC *agc; + METER *agcmeter; + BANDPASS *bp1; + BPS *bps1; + SIPHON *sip1; + CBL *cbl; + SPEAK *speak; + MPEAK *mpeak; + SSQL *ssql; + PANEL *panel; + RESAMPLE *rsmpout; static RXA* create_rxa ( int in_rate, // input samplerate diff --git a/wdsp/amd.cpp b/wdsp/amd.cpp index 354d56c06..2b5090c01 100644 --- a/wdsp/amd.cpp +++ b/wdsp/amd.cpp @@ -296,17 +296,17 @@ void AMD::setSize_amd (AMD *a, int size) void AMD::SetAMDRun(RXA& rxa, int run) { - AMD *a = rxa.amd.p; + AMD *a = rxa.amd; if (a->run != run) { RXA::bp1Check ( rxa, run, - rxa.snba.p->run, - rxa.emnr.p->run, - rxa.anf.p->run, - rxa.anr.p->run + rxa.snba->run, + rxa.emnr->run, + rxa.anf->run, + rxa.anr->run ); a->run = run; @@ -316,12 +316,12 @@ void AMD::SetAMDRun(RXA& rxa, int run) void AMD::SetAMDSBMode(RXA& rxa, int sbmode) { - rxa.amd.p->sbmode = sbmode; + rxa.amd->sbmode = sbmode; } void AMD::SetAMDFadeLevel(RXA& rxa, int levelfade) { - rxa.amd.p->levelfade = levelfade; + rxa.amd->levelfade = levelfade; } } // namesoace WDSP diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index 476a8ed03..41ea0717c 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -255,20 +255,20 @@ void AMSQ::setSize_amsq (AMSQ *a, int size) void AMSQ::SetAMSQRun (RXA& rxa, int run) { - rxa.amsq.p->run = run; + rxa.amsq->run = run; } void AMSQ::SetAMSQThreshold (RXA& rxa, double threshold) { double thresh = pow (10.0, threshold / 20.0); - rxa.amsq.p->tail_thresh = 0.9 * thresh; - rxa.amsq.p->unmute_thresh = thresh; + rxa.amsq->tail_thresh = 0.9 * thresh; + rxa.amsq->unmute_thresh = thresh; } void AMSQ::SetAMSQMaxTail (RXA& rxa, double tail) { AMSQ *a; - a = rxa.amsq.p; + a = rxa.amsq; if (tail < a->min_tail) tail = a->min_tail; diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 36a5d9d59..d782da646 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -241,54 +241,54 @@ void ANB::setSize_anb (ANB *a, int size) void ANB::SetANBRun (RXA& rxa, int run) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->run = run; } void ANB::SetANBBuffsize (RXA& rxa, int size) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->buffsize = size; } void ANB::SetANBSamplerate (RXA& rxa, int rate) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->samplerate = (double) rate; initBlanker (a); } void ANB::SetANBTau (RXA& rxa, double tau) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->tau = tau; initBlanker (a); } void ANB::SetANBHangtime (RXA& rxa, double time) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->hangtime = time; initBlanker (a); } void ANB::SetANBAdvtime (RXA& rxa, double time) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->advtime = time; initBlanker (a); } void ANB::SetANBBacktau (RXA& rxa, double tau) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->backtau = tau; initBlanker (a); } void ANB::SetANBThreshold (RXA& rxa, double thresh) { - ANB *a = rxa.anb.p; + ANB *a = rxa.anb; a->threshold = thresh; } diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index 6d8db0935..e08ce1a67 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -184,17 +184,17 @@ void ANF::setSize_anf (ANF *a, int size) void ANF::SetANFRun (RXA& rxa, int run) { - ANF *a = rxa.anf.p; + ANF *a = rxa.anf; if (a->run != run) { RXA::bp1Check ( rxa, - rxa.amd.p->run, - rxa.snba.p->run, - rxa.emnr.p->run, + rxa.amd->run, + rxa.snba->run, + rxa.emnr->run, run, - rxa.anr.p->run + rxa.anr->run ); a->run = run; RXA::bp1Set (rxa); @@ -205,42 +205,42 @@ void ANF::SetANFRun (RXA& rxa, int run) void ANF::SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage) { - rxa.anf.p->n_taps = taps; - rxa.anf.p->delay = delay; - rxa.anf.p->two_mu = gain; //try two_mu = 1e-4 - rxa.anf.p->gamma = leakage; //try gamma = 0.10 - flush_anf (rxa.anf.p); + rxa.anf->n_taps = taps; + rxa.anf->delay = delay; + rxa.anf->two_mu = gain; //try two_mu = 1e-4 + rxa.anf->gamma = leakage; //try gamma = 0.10 + flush_anf (rxa.anf); } void ANF::SetANFTaps (RXA& rxa, int taps) { - rxa.anf.p->n_taps = taps; - flush_anf (rxa.anf.p); + rxa.anf->n_taps = taps; + flush_anf (rxa.anf); } void ANF::SetANFDelay (RXA& rxa, int delay) { - rxa.anf.p->delay = delay; - flush_anf (rxa.anf.p); + rxa.anf->delay = delay; + flush_anf (rxa.anf); } void ANF::SetANFGain (RXA& rxa, double gain) { - rxa.anf.p->two_mu = gain; - flush_anf (rxa.anf.p); + rxa.anf->two_mu = gain; + flush_anf (rxa.anf); } void ANF::SetANFLeakage (RXA& rxa, double leakage) { - rxa.anf.p->gamma = leakage; - flush_anf (rxa.anf.p); + rxa.anf->gamma = leakage; + flush_anf (rxa.anf); } void ANF::SetANFPosition (RXA& rxa, int position) { - rxa.anf.p->position = position; - rxa.bp1.p->position = position; - flush_anf (rxa.anf.p); + rxa.anf->position = position; + rxa.bp1->position = position; + flush_anf (rxa.anf); } } // namespace WDSP diff --git a/wdsp/anr.cpp b/wdsp/anr.cpp index b4c9f1831..6dcadb664 100644 --- a/wdsp/anr.cpp +++ b/wdsp/anr.cpp @@ -184,16 +184,16 @@ void ANR::setSize_anr (ANR *a, int size) void ANR::SetANRRun (RXA& rxa, int run) { - ANR *a = rxa.anr.p; + ANR *a = rxa.anr; if (a->run != run) { RXA::bp1Check ( rxa, - rxa.amd.p->run, - rxa.snba.p->run, - rxa.emnr.p->run, - rxa.anf.p->run, + rxa.amd->run, + rxa.snba->run, + rxa.emnr->run, + rxa.anf->run, run ); a->run = run; @@ -204,42 +204,42 @@ void ANR::SetANRRun (RXA& rxa, int run) void ANR::SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage) { - rxa.anr.p->n_taps = taps; - rxa.anr.p->delay = delay; - rxa.anr.p->two_mu = gain; - rxa.anr.p->gamma = leakage; - flush_anr (rxa.anr.p); + rxa.anr->n_taps = taps; + rxa.anr->delay = delay; + rxa.anr->two_mu = gain; + rxa.anr->gamma = leakage; + flush_anr (rxa.anr); } void ANR::SetANRTaps (RXA& rxa, int taps) { - rxa.anr.p->n_taps = taps; - flush_anr (rxa.anr.p); + rxa.anr->n_taps = taps; + flush_anr (rxa.anr); } void ANR::SetANRDelay (RXA& rxa, int delay) { - rxa.anr.p->delay = delay; - flush_anr (rxa.anr.p); + rxa.anr->delay = delay; + flush_anr (rxa.anr); } void ANR::SetANRGain (RXA& rxa, double gain) { - rxa.anr.p->two_mu = gain; - flush_anr (rxa.anr.p); + rxa.anr->two_mu = gain; + flush_anr (rxa.anr); } void ANR::SetANRLeakage (RXA& rxa, double leakage) { - rxa.anr.p->gamma = leakage; - flush_anr (rxa.anr.p); + rxa.anr->gamma = leakage; + flush_anr (rxa.anr); } void ANR::SetANRPosition (RXA& rxa, int position) { - rxa.anr.p->position = position; - rxa.bp1.p->position = position; - flush_anr (rxa.anr.p); + rxa.anr->position = position; + rxa.bp1->position = position; + flush_anr (rxa.anr); } } // namespace WDSP diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index a9e53e050..18264a83e 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -189,7 +189,7 @@ void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, dou void BANDPASS::SetBandpassFreqs (RXA& rxa, double f_low, double f_high) { - BANDPASS *a = rxa.bp1.p; + BANDPASS *a = rxa.bp1; if ((f_low != a->f_low) || (f_high != a->f_high)) { @@ -215,7 +215,7 @@ void BANDPASS::SetBandpassNC (RXA& rxa, int nc) { // NOTE: 'nc' must be >= 'size' BANDPASS *a; - a = rxa.bp1.p; + a = rxa.bp1; if (nc != a->nc) { @@ -237,7 +237,7 @@ void BANDPASS::SetBandpassNC (RXA& rxa, int nc) void BANDPASS::SetBandpassMP (RXA& rxa, int mp) { BANDPASS *a; - a = rxa.bp1.p; + a = rxa.bp1; if (mp != a->mp) { diff --git a/wdsp/bps.cpp b/wdsp/bps.cpp index 6d2741a9b..92bea41e0 100644 --- a/wdsp/bps.cpp +++ b/wdsp/bps.cpp @@ -61,8 +61,18 @@ void BPS::decalc_bps (BPS *a) delete[] (a->infilt); } -BPS* BPS::create_bps (int run, int position, int size, float* in, float* out, - float f_low, float f_high, int samplerate, int wintype, float gain) +BPS* BPS::create_bps ( + int run, + int position, + int size, + float* in, + float* out, + float f_low, + float f_high, + int samplerate, + int wintype, + float gain +) { BPS *a = new BPS; a->run = run; @@ -150,14 +160,14 @@ void BPS::setFreqs_bps (BPS *a, float f_low, float f_high) void BPS::SetBPSRun (RXA& rxa, int run) { - rxa.bp1.p->run = run; + rxa.bp1->run = run; } void BPS::SetBPSFreqs (RXA& rxa, float f_low, float f_high) { float* impulse; BPS *a1; - a1 = rxa.bps1.p; + a1 = rxa.bps1; if ((f_low != a1->f_low) || (f_high != a1->f_high)) { @@ -174,7 +184,7 @@ void BPS::SetBPSWindow (RXA& rxa, int wintype) { float* impulse; BPS *a1; - a1 = rxa.bps1.p; + a1 = rxa.bps1; if ((a1->wintype != wintype)) { diff --git a/wdsp/bps.hpp b/wdsp/bps.hpp index 20fdb6f96..b4ceb3cee 100644 --- a/wdsp/bps.hpp +++ b/wdsp/bps.hpp @@ -61,8 +61,18 @@ public: fftwf_plan CFor; fftwf_plan CRev; - static BPS* create_bps (int run, int position, int size, float* in, float* out, - float f_low, float f_high, int samplerate, int wintype, float gain); + static BPS* create_bps ( + int run, + int position, + int size, + float* in, + float* out, + float f_low, + float f_high, + int samplerate, + int wintype, + float gain + ); static void destroy_bps (BPS *a); static void flush_bps (BPS *a); static void xbps (BPS *a, int pos); diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index 84817d6f7..f441b9e41 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -193,7 +193,7 @@ void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update) void BPSNBA::BPSNBASetNC (RXA& rxa, int nc) { - BPSNBA *a = rxa.bpsnba.p; + BPSNBA *a = rxa.bpsnba; if (a->nc != nc) { @@ -205,7 +205,7 @@ void BPSNBA::BPSNBASetNC (RXA& rxa, int nc) void BPSNBA::BPSNBASetMP (RXA& rxa, int mp) { - BPSNBA *a = rxa.bpsnba.p; + BPSNBA *a = rxa.bpsnba; if (a->mp != mp) { diff --git a/wdsp/cblock.cpp b/wdsp/cblock.cpp index 20214a59f..04119bed8 100644 --- a/wdsp/cblock.cpp +++ b/wdsp/cblock.cpp @@ -130,7 +130,7 @@ void CBL::setSize_cbl (CBL *a, int size) void CBL::SetCBLRun(RXA& rxa, int setit) { - rxa.cbl.p->run = setit; + rxa.cbl->run = setit; } } // namespace WDSP diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index d54c0357c..e956fc618 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -1077,17 +1077,17 @@ void EMNR::setSize_emnr (EMNR *a, int size) void EMNR::SetEMNRRun (RXA& rxa, int run) { - EMNR *a = rxa.emnr.p; + EMNR *a = rxa.emnr; if (a->run != run) { RXA::bp1Check ( rxa, - rxa.amd.p->run, - rxa.snba.p->run, + rxa.amd->run, + rxa.snba->run, run, - rxa.anf.p->run, - rxa.anr.p->run + rxa.anf->run, + rxa.anr->run ); a->run = run; RXA::bp1Set (rxa); @@ -1096,33 +1096,33 @@ void EMNR::SetEMNRRun (RXA& rxa, int run) void EMNR::SetEMNRgainMethod (RXA& rxa, int method) { - rxa.emnr.p->g.gain_method = method; + rxa.emnr->g.gain_method = method; } void EMNR::SetEMNRnpeMethod (RXA& rxa, int method) { - rxa.emnr.p->g.npe_method = method; + rxa.emnr->g.npe_method = method; } void EMNR::SetEMNRaeRun (RXA& rxa, int run) { - rxa.emnr.p->g.ae_run = run; + rxa.emnr->g.ae_run = run; } void EMNR::SetEMNRPosition (RXA& rxa, int position) { - rxa.emnr.p->position = position; - rxa.bp1.p->position = position; + rxa.emnr->position = position; + rxa.bp1->position = position; } void EMNR::SetEMNRaeZetaThresh (RXA& rxa, double zetathresh) { - rxa.emnr.p->ae.zetaThresh = zetathresh; + rxa.emnr->ae.zetaThresh = zetathresh; } void EMNR::SetEMNRaePsi (RXA& rxa, double psi) { - rxa.emnr.p->ae.psi = psi; + rxa.emnr->ae.psi = psi; } } // namespace WDSP diff --git a/wdsp/eq.cpp b/wdsp/eq.cpp index 3fa82b4d6..669a5b6d2 100644 --- a/wdsp/eq.cpp +++ b/wdsp/eq.cpp @@ -289,14 +289,14 @@ void EQP::setSize_eqp (EQP *a, int size) void EQP::SetEQRun (RXA& rxa, int run) { - rxa.eqp.p->run = run; + rxa.eqp->run = run; } void EQP::SetEQNC (RXA& rxa, int nc) { EQP *a; float* impulse; - a = rxa.eqp.p; + a = rxa.eqp; if (a->nc != nc) { @@ -310,7 +310,7 @@ void EQP::SetEQNC (RXA& rxa, int nc) void EQP::SetEQMP (RXA& rxa, int mp) { EQP *a; - a = rxa.eqp.p; + a = rxa.eqp; if (a->mp != mp) { a->mp = mp; @@ -322,7 +322,7 @@ void EQP::SetEQProfile (RXA& rxa, int nfreqs, const float* F, const float* G) { EQP *a; float* impulse; - a = rxa.eqp.p; + a = rxa.eqp; delete[] (a->G); delete[] (a->F); a->nfreqs = nfreqs; @@ -340,7 +340,7 @@ void EQP::SetEQCtfmode (RXA& rxa, int mode) { EQP *a; float* impulse; - a = rxa.eqp.p; + a = rxa.eqp; a->ctfmode = mode; impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); FIRCORE::setImpulse_fircore (a->p, impulse, 1); @@ -351,7 +351,7 @@ void EQP::SetEQWintype (RXA& rxa, int wintype) { EQP *a; float* impulse; - a = rxa.eqp.p; + a = rxa.eqp; a->wintype = wintype; impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); FIRCORE::setImpulse_fircore (a->p, impulse, 1); @@ -362,7 +362,7 @@ void EQP::SetGrphEQ (RXA& rxa, int *rxeq) { // three band equalizer (legacy compatibility) EQP *a; float* impulse; - a = rxa.eqp.p; + a = rxa.eqp; delete[] (a->G); delete[] (a->F); a->nfreqs = 4; @@ -388,7 +388,7 @@ void EQP::SetGrphEQ10 (RXA& rxa, int *rxeq) EQP *a; float* impulse; int i; - a = rxa.eqp.p; + a = rxa.eqp; delete[] (a->G); delete[] (a->F); a->nfreqs = 10; diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index e4780e628..e82b1db49 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -275,7 +275,7 @@ void FMD::setSize_fmd (FMD *a, int size) void FMD::SetFMDeviation (RXA& rxa, double deviation) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; a->deviation = deviation; a->again = a->rate / (a->deviation * TWOPI); } @@ -283,7 +283,7 @@ void FMD::SetFMDeviation (RXA& rxa, double deviation) void FMD::SetCTCSSFreq (RXA& rxa, double freq) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; a->ctcss_freq = freq; SNOTCH::SetSNCTCSSFreq (a->sntch, a->ctcss_freq); } @@ -291,7 +291,7 @@ void FMD::SetCTCSSFreq (RXA& rxa, double freq) void FMD::SetCTCSSRun (RXA& rxa, int run) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; a->sntch_run = run; SNOTCH::SetSNCTCSSRun (a->sntch, a->sntch_run); } @@ -300,7 +300,7 @@ void FMD::SetFMNCde (RXA& rxa, int nc) { FMD *a; float* impulse; - a = rxa.fmd.p; + a = rxa.fmd; if (a->nc_de != nc) { @@ -314,7 +314,7 @@ void FMD::SetFMNCde (RXA& rxa, int nc) void FMD::SetFMMPde (RXA& rxa, int mp) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; if (a->mp_de != mp) { a->mp_de = mp; @@ -326,7 +326,7 @@ void FMD::SetFMNCaud (RXA& rxa, int nc) { FMD *a; float* impulse; - a = rxa.fmd.p; + a = rxa.fmd; if (a->nc_aud != nc) { @@ -340,7 +340,7 @@ void FMD::SetFMNCaud (RXA& rxa, int nc) void FMD::SetFMMPaud (RXA& rxa, int mp) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; if (a->mp_aud != mp) { a->mp_aud = mp; @@ -351,7 +351,7 @@ void FMD::SetFMMPaud (RXA& rxa, int mp) void FMD::SetFMLimRun (RXA& rxa, int run) { FMD *a; - a = rxa.fmd.p; + a = rxa.fmd; if (a->lim_run != run) { a->lim_run = run; @@ -361,7 +361,7 @@ void FMD::SetFMLimRun (RXA& rxa, int run) void FMD::SetFMLimGain (RXA& rxa, double gaindB) { double gain = pow(10.0, gaindB / 20.0); - FMD *a = rxa.fmd.p; + FMD *a = rxa.fmd; if (a->lim_gain != gain) { @@ -373,7 +373,7 @@ void FMD::SetFMLimGain (RXA& rxa, double gaindB) void FMD::SetFMAFFilter(RXA& rxa, double low, double high) { - FMD *a = rxa.fmd.p; + FMD *a = rxa.fmd; float* impulse; if (a->f_low != low || a->f_high != high) diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 1f520a121..1b7c018c8 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -290,20 +290,20 @@ void FMSQ::setSize_fmsq (FMSQ *a, int size) void FMSQ::SetFMSQRun (RXA& rxa, int run) { - rxa.fmsq.p->run = run; + rxa.fmsq->run = run; } void FMSQ::SetFMSQThreshold (RXA& rxa, double threshold) { - rxa.fmsq.p->tail_thresh = threshold; - rxa.fmsq.p->unmute_thresh = 0.9 * threshold; + rxa.fmsq->tail_thresh = threshold; + rxa.fmsq->unmute_thresh = 0.9 * threshold; } void FMSQ::SetFMSQNC (RXA& rxa, int nc) { FMSQ *a; float* impulse; - a = rxa.fmsq.p; + a = rxa.fmsq; if (a->nc != nc) { @@ -317,7 +317,7 @@ void FMSQ::SetFMSQNC (RXA& rxa, int nc) void FMSQ::SetFMSQMP (RXA& rxa, int mp) { FMSQ *a; - a = rxa.fmsq.p; + a = rxa.fmsq; if (a->mp != mp) { diff --git a/wdsp/gen.cpp b/wdsp/gen.cpp index fc81e65c0..998f3d1fd 100644 --- a/wdsp/gen.cpp +++ b/wdsp/gen.cpp @@ -390,46 +390,46 @@ void GEN::setSize_gen (GEN *a, int size) void GEN::SetPreGenRun (RXA& rxa, int run) { - rxa.gen0.p->run = run; + rxa.gen0->run = run; } void GEN::SetPreGenMode (RXA& rxa, int mode) { - rxa.gen0.p->mode = mode; + rxa.gen0->mode = mode; } void GEN::SetPreGenToneMag (RXA& rxa, float mag) { - rxa.gen0.p->tone.mag = mag; + rxa.gen0->tone.mag = mag; } void GEN::SetPreGenToneFreq (RXA& rxa, float freq) { - rxa.gen0.p->tone.freq = freq; - calc_tone (rxa.gen0.p); + rxa.gen0->tone.freq = freq; + calc_tone (rxa.gen0); } void GEN::SetPreGenNoiseMag (RXA& rxa, float mag) { - rxa.gen0.p->noise.mag = mag; + rxa.gen0->noise.mag = mag; } void GEN::SetPreGenSweepMag (RXA& rxa, float mag) { - rxa.gen0.p->sweep.mag = mag; + rxa.gen0->sweep.mag = mag; } void GEN::SetPreGenSweepFreq (RXA& rxa, float freq1, float freq2) { - rxa.gen0.p->sweep.f1 = freq1; - rxa.gen0.p->sweep.f2 = freq2; - calc_sweep (rxa.gen0.p); + rxa.gen0->sweep.f1 = freq1; + rxa.gen0->sweep.f2 = freq2; + calc_sweep (rxa.gen0); } void GEN::SetPreGenSweepRate (RXA& rxa, float rate) { - rxa.gen0.p->sweep.sweeprate = rate; - calc_sweep (rxa.gen0.p); + rxa.gen0->sweep.sweeprate = rate; + calc_sweep (rxa.gen0); } diff --git a/wdsp/iir.cpp b/wdsp/iir.cpp index fcb9c0e44..754d5c376 100644 --- a/wdsp/iir.cpp +++ b/wdsp/iir.cpp @@ -334,27 +334,27 @@ void SPEAK::setSize_speak (SPEAK *a, int size) void SPEAK::SetSPCWRun (RXA& rxa, int run) { - SPEAK *a = rxa.speak.p; + SPEAK *a = rxa.speak; a->run = run; } void SPEAK::SetSPCWFreq (RXA& rxa, double freq) { - SPEAK *a = rxa.speak.p; + SPEAK *a = rxa.speak; a->f = freq; calc_speak (a); } void SPEAK::SetSPCWBandwidth (RXA& rxa, double bw) { - SPEAK *a = rxa.speak.p; + SPEAK *a = rxa.speak; a->bw = bw; calc_speak (a); } void SPEAK::SetSPCWGain (RXA& rxa, double gain) { - SPEAK *a = rxa.speak.p; + SPEAK *a = rxa.speak; a->gain = gain; calc_speak (a); } @@ -504,25 +504,25 @@ void MPEAK::setSize_mpeak (MPEAK *a, int size) void MPEAK::SetmpeakRun (RXA& rxa, int run) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->run = run; } void MPEAK::SetmpeakNpeaks (RXA& rxa, int npeaks) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->npeaks = npeaks; } void MPEAK::SetmpeakFilEnable (RXA& rxa, int fil, int enable) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->enable[fil] = enable; } void MPEAK::SetmpeakFilFreq (RXA& rxa, int fil, double freq) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->f[fil] = freq; a->pfil[fil]->f = freq; SPEAK::calc_speak(a->pfil[fil]); @@ -530,7 +530,7 @@ void MPEAK::SetmpeakFilFreq (RXA& rxa, int fil, double freq) void MPEAK::SetmpeakFilBw (RXA& rxa, int fil, double bw) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->bw[fil] = bw; a->pfil[fil]->bw = bw; SPEAK::calc_speak(a->pfil[fil]); @@ -538,7 +538,7 @@ void MPEAK::SetmpeakFilBw (RXA& rxa, int fil, double bw) void MPEAK::SetmpeakFilGain (RXA& rxa, int fil, double gain) { - MPEAK *a = rxa.mpeak.p; + MPEAK *a = rxa.mpeak; a->gain[fil] = gain; a->pfil[fil]->gain = gain; SPEAK::calc_speak(a->pfil[fil]); diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index 6ebe0d723..4c866e08e 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -414,14 +414,14 @@ void NBP::setMp_nbp (NBP *a) void NBP::UpdateNBPFiltersLightWeight (RXA& rxa) { // called when setting tune freq or shift freq - calc_nbp_lightweight (rxa.nbp0.p); - calc_nbp_lightweight (rxa.bpsnba.p->bpsnba); + calc_nbp_lightweight (rxa.nbp0); + calc_nbp_lightweight (rxa.bpsnba->bpsnba); } void NBP::UpdateNBPFilters(RXA& rxa) { - NBP *a = rxa.nbp0.p; - BPSNBA *b = rxa.bpsnba.p; + NBP *a = rxa.nbp0; + BPSNBA *b = rxa.bpsnba; if (a->fnfrun) { calc_nbp_impulse (a); @@ -439,7 +439,7 @@ int NBP::NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int ac NOTCHDB *b; int i, j; int rval; - b = rxa.ndb.p; + b = rxa.ndb; if (notch <= b->nn && b->nn < b->maxnotches) { b->nn++; @@ -468,7 +468,7 @@ int NBP::NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* { NOTCHDB *a; int rval; - a = rxa.ndb.p; + a = rxa.ndb; if (notch < a->nn) { @@ -493,7 +493,7 @@ int NBP::NBPDeleteNotch (RXA& rxa, int notch) int i, j; int rval; NOTCHDB *a; - a = rxa.ndb.p; + a = rxa.ndb; if (notch < a->nn) { a->nn--; @@ -517,7 +517,7 @@ int NBP::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int a { NOTCHDB *a; int rval; - a = rxa.ndb.p; + a = rxa.ndb; if (notch < a->nn) { a->fcenter[notch] = fcenter; @@ -536,14 +536,14 @@ int NBP::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int a void NBP::NBPGetNumNotches (RXA& rxa, int* nnotches) { NOTCHDB *a; - a = rxa.ndb.p; + a = rxa.ndb; *nnotches = a->nn; } void NBP::NBPSetTuneFrequency (RXA& rxa, double tunefreq) { NOTCHDB *a; - a = rxa.ndb.p; + a = rxa.ndb; if (tunefreq != a->tunefreq) { @@ -555,7 +555,7 @@ void NBP::NBPSetTuneFrequency (RXA& rxa, double tunefreq) void NBP::NBPSetShiftFrequency (RXA& rxa, double shift) { NOTCHDB *a; - a = rxa.ndb.p; + a = rxa.ndb; if (shift != a->shift) { a->shift = shift; @@ -565,8 +565,8 @@ void NBP::NBPSetShiftFrequency (RXA& rxa, double shift) void NBP::NBPSetNotchesRun (RXA& rxa, int run) { - NOTCHDB *a = rxa.ndb.p; - NBP *b = rxa.nbp0.p; + NOTCHDB *a = rxa.ndb; + NBP *b = rxa.nbp0; if ( run != a->master_run) { @@ -586,14 +586,14 @@ void NBP::NBPSetNotchesRun (RXA& rxa, int run) void NBP::NBPSetRun (RXA& rxa, int run) { NBP *a; - a = rxa.nbp0.p; + a = rxa.nbp0; a->run = run; } void NBP::NBPSetFreqs (RXA& rxa, double flow, double fhigh) { NBP *a; - a = rxa.nbp0.p; + a = rxa.nbp0; if ((flow != a->flow) || (fhigh != a->fhigh)) { @@ -609,8 +609,8 @@ void NBP::NBPSetWindow (RXA& rxa, int wintype) { NBP *a; BPSNBA *b; - a = rxa.nbp0.p; - b = rxa.bpsnba.p; + a = rxa.nbp0; + b = rxa.bpsnba; if ((a->wintype != wintype)) { @@ -631,7 +631,7 @@ void NBP::NBPSetNC (RXA& rxa, int nc) { // NOTE: 'nc' must be >= 'size' NBP *a; - a = rxa.nbp0.p; + a = rxa.nbp0; if (a->nc != nc) { @@ -643,7 +643,7 @@ void NBP::NBPSetNC (RXA& rxa, int nc) void NBP::NBPSetMP (RXA& rxa, int mp) { NBP *a; - a = rxa.nbp0.p; + a = rxa.nbp0; if (a->mp != mp) { @@ -655,7 +655,7 @@ void NBP::NBPSetMP (RXA& rxa, int mp) void NBP::NBPGetMinNotchWidth (RXA& rxa, double* minwidth) { NBP *a; - a = rxa.nbp0.p; + a = rxa.nbp0; *minwidth = min_notch_width (a); } @@ -663,8 +663,8 @@ void NBP::NBPSetAutoIncrease (RXA& rxa, int autoincr) { NBP *a; BPSNBA *b; - a = rxa.nbp0.p; - b = rxa.bpsnba.p; + a = rxa.nbp0; + b = rxa.bpsnba; if ((a->autoincr != autoincr)) { diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index 5b0dfec4d..40faa6e37 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -614,32 +614,32 @@ void NOB::setSize_nob (NOB *a, int size) void NOB::SetNOBRun (RXA& rxa, int run) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->run = run; } void NOB::SetNOBMode (RXA& rxa, int mode) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->mode = mode; } void NOB::SetNOBBuffsize (RXA& rxa, int size) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->buffsize = size; } void NOB::SetNOBSamplerate (RXA& rxa, int rate) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->samplerate = (double) rate; init_nob (a); } void NOB::SetNOBTau (RXA& rxa, double tau) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->advslewtime = tau; a->hangslewtime = tau; init_nob (a); @@ -647,28 +647,28 @@ void NOB::SetNOBTau (RXA& rxa, double tau) void NOB::SetNOBHangtime (RXA& rxa, double time) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->hangtime = time; init_nob (a); } void NOB::SetNOBAdvtime (RXA& rxa, double time) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->advtime = time; init_nob (a); } void NOB::SetNOBBacktau (RXA& rxa, double tau) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->backtau = tau; init_nob (a); } void NOB::SetNOBThreshold (RXA& rxa, double thresh) { - NOB *a = rxa.nob.p; + NOB *a = rxa.nob; a->threshold = thresh; } diff --git a/wdsp/patchpanel.cpp b/wdsp/patchpanel.cpp index 9637866ab..8a2b91c63 100644 --- a/wdsp/patchpanel.cpp +++ b/wdsp/patchpanel.cpp @@ -139,23 +139,23 @@ void PANEL::setSize_panel (PANEL *a, int size) void PANEL::SetPanelRun (RXA& rxa, int run) { - rxa.panel.p->run = run; + rxa.panel->run = run; } void PANEL::SetPanelSelect (RXA& rxa, int select) { - rxa.panel.p->inselect = select; + rxa.panel->inselect = select; } void PANEL::SetPanelGain1 (RXA& rxa, double gain) { - rxa.panel.p->gain1 = gain; + rxa.panel->gain1 = gain; } void PANEL::SetPanelGain2 (RXA& rxa, double gainI, double gainQ) { - rxa.panel.p->gain2I = gainI; - rxa.panel.p->gain2Q = gainQ; + rxa.panel->gain2I = gainI; + rxa.panel->gain2Q = gainQ; } void PANEL::SetPanelPan (RXA& rxa, double pan) @@ -173,18 +173,18 @@ void PANEL::SetPanelPan (RXA& rxa, double pan) gain2 = 1.0; } - rxa.panel.p->gain2I = gain1; - rxa.panel.p->gain2Q = gain2; + rxa.panel->gain2I = gain1; + rxa.panel->gain2Q = gain2; } void PANEL::SetPanelCopy (RXA& rxa, int copy) { - rxa.panel.p->copy = copy; + rxa.panel->copy = copy; } void PANEL::SetPanelBinaural (RXA& rxa, int bin) { - rxa.panel.p->copy = 1 - bin; + rxa.panel->copy = 1 - bin; } /******************************************************************************************************** diff --git a/wdsp/sender.cpp b/wdsp/sender.cpp index 938edfb8c..9af4b8231 100644 --- a/wdsp/sender.cpp +++ b/wdsp/sender.cpp @@ -94,7 +94,7 @@ void SENDER::setSize_sender (SENDER *a, int size) void SENDER::SetSpectrum (RXA& rxa, int flag, BufferProbe *spectrumProbe) { SENDER *a; - a = rxa.sender.p; + a = rxa.sender; a->flag = flag; a->spectrumProbe = spectrumProbe; } diff --git a/wdsp/shift.cpp b/wdsp/shift.cpp index d952fd312..8ab78fad3 100644 --- a/wdsp/shift.cpp +++ b/wdsp/shift.cpp @@ -123,13 +123,13 @@ void SHIFT::setSize_shift (SHIFT *a, int size) void SHIFT::SetShiftRun (RXA& rxa, int run) { - rxa.shift.p->run = run; + rxa.shift->run = run; } void SHIFT::SetShiftFreq (RXA& rxa, double fshift) { - rxa.shift.p->shift = fshift; - calc_shift (rxa.shift.p); + rxa.shift->shift = fshift; + calc_shift (rxa.shift); } } // namespace WDSP diff --git a/wdsp/siphon.cpp b/wdsp/siphon.cpp index 1d3384901..393a325b5 100644 --- a/wdsp/siphon.cpp +++ b/wdsp/siphon.cpp @@ -194,7 +194,7 @@ void SIPHON::sip_spectrum (SIPHON *a) void SIPHON::GetaSipF (RXA& rxa, float* out, int size) { // return raw samples as floats - SIPHON *a=rxa.sip1.p; + SIPHON *a=rxa.sip1; int i; a->outsize = size; suck (a); @@ -206,7 +206,7 @@ void SIPHON::GetaSipF (RXA& rxa, float* out, int size) void SIPHON::GetaSipF1 (RXA& rxa, float* out, int size) { // return raw samples as floats - SIPHON *a=rxa.sip1.p; + SIPHON *a=rxa.sip1; int i; a->outsize = size; suck (a); diff --git a/wdsp/snba.cpp b/wdsp/snba.cpp index 3df71971b..ff7d4f176 100644 --- a/wdsp/snba.cpp +++ b/wdsp/snba.cpp @@ -719,18 +719,18 @@ void SNBA::xsnba (SNBA *d) void SNBA::SetSNBARun (RXA& rxa, int run) { - SNBA *a = rxa.snba.p; + SNBA *a = rxa.snba; if (a->run != run) { - RXA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb.p->master_run); + RXA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb->master_run); RXA::bp1Check ( rxa, - rxa.amd.p->run, + rxa.amd->run, run, - rxa.emnr.p->run, - rxa.anf.p->run, - rxa.anr.p->run + rxa.emnr->run, + rxa.anf->run, + rxa.anr->run ); a->run = run; RXA::bp1Set (rxa); @@ -740,54 +740,54 @@ void SNBA::SetSNBARun (RXA& rxa, int run) void SNBA::SetSNBAovrlp (RXA& rxa, int ovrlp) { - decalc_snba (rxa.snba.p); - rxa.snba.p->ovrlp = ovrlp; - calc_snba (rxa.snba.p); + decalc_snba (rxa.snba); + rxa.snba->ovrlp = ovrlp; + calc_snba (rxa.snba); } void SNBA::SetSNBAasize (RXA& rxa, int size) { - rxa.snba.p->exec.asize = size; + rxa.snba->exec.asize = size; } void SNBA::SetSNBAnpasses (RXA& rxa, int npasses) { - rxa.snba.p->exec.npasses = npasses; + rxa.snba->exec.npasses = npasses; } void SNBA::SetSNBAk1 (RXA& rxa, double k1) { - rxa.snba.p->sdet.k1 = k1; + rxa.snba->sdet.k1 = k1; } void SNBA::SetSNBAk2 (RXA& rxa, double k2) { - rxa.snba.p->sdet.k2 = k2; + rxa.snba->sdet.k2 = k2; } void SNBA::SetSNBAbridge (RXA& rxa, int bridge) { - rxa.snba.p->sdet.b = bridge; + rxa.snba->sdet.b = bridge; } void SNBA::SetSNBApresamps (RXA& rxa, int presamps) { - rxa.snba.p->sdet.pre = presamps; + rxa.snba->sdet.pre = presamps; } void SNBA::SetSNBApostsamps (RXA& rxa, int postsamps) { - rxa.snba.p->sdet.post = postsamps; + rxa.snba->sdet.post = postsamps; } void SNBA::SetSNBApmultmin (RXA& rxa, double pmultmin) { - rxa.snba.p->scan.pmultmin = pmultmin; + rxa.snba->scan.pmultmin = pmultmin; } void SNBA::SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh) { - SNBA *a = rxa.snba.p; + SNBA *a = rxa.snba; RESAMPLE *d = a->outresamp; double f_low, f_high; diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index a0d3d511b..f6213b445 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -350,21 +350,21 @@ void SSQL::setSize_ssql (SSQL *a, int size) void SSQL::SetSSQLRun (RXA& rxa, int run) { - rxa.ssql.p->run = run; + rxa.ssql->run = run; } void SSQL::SetSSQLThreshold (RXA& rxa, double threshold) { // 'threshold' should be between 0.0 and 1.0 // WU2O testing: 0.16 is a good default for 'threshold'; => 0.08 for 'wthresh' - rxa.ssql.p->wthresh = threshold / 2.0; + rxa.ssql->wthresh = threshold / 2.0; } void SSQL::SetSSQLTauMute (RXA& rxa, double tau_mute) { // reasonable (wide) range is 0.1 to 2.0 // WU2O testing: 0.1 is good default value - SSQL *a = rxa.ssql.p; + SSQL *a = rxa.ssql; a->tr_tau_mute = tau_mute; a->mute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_mute)); } @@ -373,7 +373,7 @@ void SSQL::SetSSQLTauUnMute (RXA& rxa, double tau_unmute) { // reasonable (wide) range is 0.1 to 1.0 // WU2O testing: 0.1 is good default value - SSQL *a = rxa.ssql.p; + SSQL *a = rxa.ssql; a->tr_tau_unmute = tau_unmute; a->unmute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_unmute)); } diff --git a/wdsp/wcpAGC.cpp b/wdsp/wcpAGC.cpp index 80ffddb9e..ec90261f2 100644 --- a/wdsp/wcpAGC.cpp +++ b/wdsp/wcpAGC.cpp @@ -390,63 +390,63 @@ void WCPAGC::SetAGCMode (RXA& rxa, int mode) switch (mode) { case 0: //agcOFF - rxa.agc.p->mode = 0; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->mode = 0; + loadWcpAGC ( rxa.agc ); break; case 1: //agcLONG - rxa.agc.p->mode = 1; - rxa.agc.p->hangtime = 2.000; - rxa.agc.p->tau_decay = 2.000; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->mode = 1; + rxa.agc->hangtime = 2.000; + rxa.agc->tau_decay = 2.000; + loadWcpAGC ( rxa.agc ); break; case 2: //agcSLOW - rxa.agc.p->mode = 2; - rxa.agc.p->hangtime = 1.000; - rxa.agc.p->tau_decay = 0.500; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->mode = 2; + rxa.agc->hangtime = 1.000; + rxa.agc->tau_decay = 0.500; + loadWcpAGC ( rxa.agc ); break; case 3: //agcMED - rxa.agc.p->mode = 3; - rxa.agc.p->hang_thresh = 1.0; - rxa.agc.p->hangtime = 0.000; - rxa.agc.p->tau_decay = 0.250; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->mode = 3; + rxa.agc->hang_thresh = 1.0; + rxa.agc->hangtime = 0.000; + rxa.agc->tau_decay = 0.250; + loadWcpAGC ( rxa.agc ); break; case 4: //agcFAST - rxa.agc.p->mode = 4; - rxa.agc.p->hang_thresh = 1.0; - rxa.agc.p->hangtime = 0.000; - rxa.agc.p->tau_decay = 0.050; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->mode = 4; + rxa.agc->hang_thresh = 1.0; + rxa.agc->hangtime = 0.000; + rxa.agc->tau_decay = 0.050; + loadWcpAGC ( rxa.agc ); break; default: - rxa.agc.p->mode = 5; + rxa.agc->mode = 5; break; } } void WCPAGC::SetAGCAttack (RXA& rxa, int attack) { - rxa.agc.p->tau_attack = (float)attack / 1000.0; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->tau_attack = (float)attack / 1000.0; + loadWcpAGC ( rxa.agc ); } void WCPAGC::SetAGCDecay (RXA& rxa, int decay) { - rxa.agc.p->tau_decay = (float)decay / 1000.0; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->tau_decay = (float)decay / 1000.0; + loadWcpAGC ( rxa.agc ); } void WCPAGC::SetAGCHang (RXA& rxa, int hang) { - rxa.agc.p->hangtime = (float)hang / 1000.0; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->hangtime = (float)hang / 1000.0; + loadWcpAGC ( rxa.agc ); } void WCPAGC::GetAGCHangLevel(RXA& rxa, double *hangLevel) //for line on bandscope { - *hangLevel = 20.0 * log10( rxa.agc.p->hang_level / 0.637 ); + *hangLevel = 20.0 * log10( rxa.agc->hang_level / 0.637 ); } void WCPAGC::SetAGCHangLevel(RXA& rxa, double hangLevel) @@ -454,77 +454,77 @@ void WCPAGC::SetAGCHangLevel(RXA& rxa, double hangLevel) { double convert, tmp; - if (rxa.agc.p->max_input > rxa.agc.p->min_volts) + if (rxa.agc->max_input > rxa.agc->min_volts) { convert = pow (10.0, hangLevel / 20.0); - tmp = std::max(1e-8, (convert - rxa.agc.p->min_volts) / (rxa.agc.p->max_input - rxa.agc.p->min_volts)); - rxa.agc.p->hang_thresh = 1.0 + 0.125 * log10 (tmp); + tmp = std::max(1e-8, (convert - rxa.agc->min_volts) / (rxa.agc->max_input - rxa.agc->min_volts)); + rxa.agc->hang_thresh = 1.0 + 0.125 * log10 (tmp); } else - rxa.agc.p->hang_thresh = 1.0; + rxa.agc->hang_thresh = 1.0; - loadWcpAGC ( rxa.agc.p ); + loadWcpAGC ( rxa.agc ); } void WCPAGC::GetAGCHangThreshold(RXA& rxa, int *hangthreshold) //for slider in setup { - *hangthreshold = (int) (100.0 * rxa.agc.p->hang_thresh); + *hangthreshold = (int) (100.0 * rxa.agc->hang_thresh); } void WCPAGC::SetAGCHangThreshold (RXA& rxa, int hangthreshold) //For slider in setup { - rxa.agc.p->hang_thresh = (double) hangthreshold / 100.0; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->hang_thresh = (double) hangthreshold / 100.0; + loadWcpAGC ( rxa.agc ); } void WCPAGC::GetAGCThresh(RXA& rxa, double *thresh, double size, double rate) //for line on bandscope. { double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0.p->fhigh - rxa.nbp0.p->flow) * size / rate); - *thresh = 20.0 * log10( rxa.agc.p->min_volts ) - noise_offset; + noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); + *thresh = 20.0 * log10( rxa.agc->min_volts ) - noise_offset; } void WCPAGC::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) //for line on bandscope { double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0.p->fhigh - rxa.nbp0.p->flow) * size / rate); - rxa.agc.p->max_gain = rxa.agc.p->out_target / (rxa.agc.p->var_gain * pow (10.0, (thresh + noise_offset) / 20.0)); - loadWcpAGC ( rxa.agc.p ); + noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); + rxa.agc->max_gain = rxa.agc->out_target / (rxa.agc->var_gain * pow (10.0, (thresh + noise_offset) / 20.0)); + loadWcpAGC ( rxa.agc ); } void WCPAGC::GetAGCTop(RXA& rxa, double *max_agc) //for AGC Max Gain in setup { - *max_agc = 20 * log10 (rxa.agc.p->max_gain); + *max_agc = 20 * log10 (rxa.agc->max_gain); } void WCPAGC::SetAGCTop (RXA& rxa, double max_agc) //for AGC Max Gain in setup { - rxa.agc.p->max_gain = pow (10.0, (double) max_agc / 20.0); - loadWcpAGC ( rxa.agc.p ); + rxa.agc->max_gain = pow (10.0, (double) max_agc / 20.0); + loadWcpAGC ( rxa.agc ); } void WCPAGC::SetAGCSlope (RXA& rxa, int slope) { - rxa.agc.p->var_gain = pow (10.0, (double) slope / 20.0 / 10.0); - loadWcpAGC ( rxa.agc.p ); + rxa.agc->var_gain = pow (10.0, (double) slope / 20.0 / 10.0); + loadWcpAGC ( rxa.agc ); } void WCPAGC::SetAGCFixed (RXA& rxa, double fixed_agc) { - rxa.agc.p->fixed_gain = pow (10.0, (double) fixed_agc / 20.0); - loadWcpAGC ( rxa.agc.p ); + rxa.agc->fixed_gain = pow (10.0, (double) fixed_agc / 20.0); + loadWcpAGC ( rxa.agc ); } void WCPAGC::SetAGCMaxInputLevel (RXA& rxa, double level) { - rxa.agc.p->max_input = level; - loadWcpAGC ( rxa.agc.p ); + rxa.agc->max_input = level; + loadWcpAGC ( rxa.agc ); } /******************************************************************************************************** From b975658758f6d650a22e7903a34b464ce7102085 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 23 Jul 2024 08:06:13 +0200 Subject: [PATCH 02/46] WDSP: Nose blanker: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 28 +- wdsp/RXA.cpp | 44 +- wdsp/anb.cpp | 277 +++++----- wdsp/anb.hpp | 33 +- wdsp/nob.cpp | 645 ++++++++++++------------ wdsp/nob.hpp | 38 +- 6 files changed, 511 insertions(+), 554 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index cdcbc66fb..d114d176b 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -591,18 +591,18 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_dnb != settings.m_dnb) || (m_settings.m_nbScheme != settings.m_nbScheme) || force) { - WDSP::ANB::SetANBRun(*m_rxa, 0); - WDSP::NOB::SetNOBRun(*m_rxa, 0); + m_rxa->anb->setRun(0); + m_rxa->nob->setRun(0); if (settings.m_dnb) { switch(settings.m_nbScheme) { case WDSPRxProfile::NBSchemeNB: - WDSP::ANB::SetANBRun(*m_rxa, 1); + m_rxa->anb->setRun(1); break; case WDSPRxProfile::NBSchemeNB2: - WDSP::NOB::SetNOBRun(*m_rxa, 1); + m_rxa->nob->setRun(1); break; default: break; @@ -612,32 +612,32 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_nbSlewTime != settings.m_nbSlewTime) || force) { - WDSP::ANB::SetANBTau(*m_rxa, settings.m_nbSlewTime * 0.001); - WDSP::NOB::SetNOBTau(*m_rxa, settings.m_nbSlewTime * 0.001); + m_rxa->anb->setTau(settings.m_nbSlewTime * 0.001); + m_rxa->nob->setTau(settings.m_nbSlewTime * 0.001); } if ((m_settings.m_nbLeadTime != settings.m_nbLeadTime) || force) { - WDSP::ANB::SetANBAdvtime(*m_rxa, settings.m_nbLeadTime * 0.001); - WDSP::NOB::SetNOBAdvtime(*m_rxa, settings.m_nbLeadTime * 0.001); + m_rxa->anb->setAdvtime(settings.m_nbLeadTime * 0.001); + m_rxa->nob->setAdvtime(settings.m_nbLeadTime * 0.001); } if ((m_settings.m_nbLagTime != settings.m_nbLagTime) || force) { - WDSP::ANB::SetANBHangtime(*m_rxa, settings.m_nbLagTime * 0.001); - WDSP::NOB::SetNOBHangtime(*m_rxa, settings.m_nbLagTime * 0.001); + m_rxa->anb->setHangtime(settings.m_nbLagTime * 0.001); + m_rxa->nob->setHangtime(settings.m_nbLagTime * 0.001); } if ((m_settings.m_nbThreshold != settings.m_nbThreshold) || force) { - WDSP::ANB::SetANBThreshold(*m_rxa, settings.m_nbThreshold); - WDSP::NOB::SetNOBThreshold(*m_rxa, settings.m_nbThreshold); + m_rxa->anb->setThreshold(settings.m_nbThreshold); + m_rxa->nob->setThreshold(settings.m_nbThreshold); } if ((m_settings.m_nbAvgTime != settings.m_nbAvgTime) || force) { - WDSP::ANB::SetANBBacktau(*m_rxa, settings.m_nbAvgTime * 0.001); - WDSP::NOB::SetNOBBacktau(*m_rxa, settings.m_nbAvgTime * 0.001); + m_rxa->anb->setBacktau(settings.m_nbAvgTime * 0.001); + m_rxa->nob->setBacktau(settings.m_nbAvgTime * 0.001); } // AM option diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 9a1cf3d96..206bc44f7 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -89,7 +89,7 @@ RXA* RXA::create_rxa ( std::fill(rxa->meter, rxa->meter + RXA_METERTYPE_LAST, 0); // Noise blanker (ANB or "NB") - rxa->anb = ANB::create_anb( + rxa->anb = new ANB( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -102,7 +102,7 @@ RXA* RXA::create_rxa ( 30 // thershold ); // Noise blanker (NOB or "NB2") - rxa->nob = NOB::create_nob( + rxa->nob = new NOB( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -601,8 +601,8 @@ void RXA::destroy_rxa (RXA *rxa) GEN::destroy_gen (rxa->gen0); RESAMPLE::destroy_resample (rxa->rsmpin); SHIFT::destroy_shift (rxa->shift); - NOB::destroy_nob(rxa->nob); - ANB::destroy_anb(rxa->anb); + delete (rxa->nob); + delete (rxa->anb); delete[] (rxa->midbuff); delete[] (rxa->outbuff); delete[] (rxa->inbuff); @@ -614,8 +614,8 @@ void RXA::flush_rxa (RXA *rxa) std::fill(rxa->inbuff, rxa->inbuff + 1 * rxa->dsp_insize * 2, 0); std::fill(rxa->outbuff, rxa->outbuff + 1 * rxa->dsp_outsize * 2, 0); std::fill(rxa->midbuff, rxa->midbuff + 2 * rxa->dsp_size * 2, 0); - ANB::flush_anb (rxa->anb); - NOB::flush_nob(rxa->nob); + rxa->anb->flush(); + rxa->nob->flush(); SHIFT::flush_shift (rxa->shift); RESAMPLE::flush_resample (rxa->rsmpin); GEN::flush_gen (rxa->gen0); @@ -647,8 +647,8 @@ void RXA::flush_rxa (RXA *rxa) void RXA::xrxa (RXA *rxa) { - ANB::xanb (rxa->anb); - NOB::xnob (rxa->nob); + rxa->anb->x(); + rxa->nob->x(); SHIFT::xshift (rxa->shift); RESAMPLE::xresample (rxa->rsmpin); GEN::xgen (rxa->gen0); @@ -698,13 +698,13 @@ void RXA::setInputSamplerate (RXA *rxa, int in_rate) delete[] (rxa->inbuff); rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *)malloc0(1 * ch.dsp_insize * sizeof(complex)); // anb - ANB::setBuffers_anb(rxa->anb, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb(rxa->anb, rxa->dsp_insize); - ANB::setSamplerate_anb(rxa->anb, rxa->in_rate); + rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->anb->setSize(rxa->dsp_insize); + rxa->anb->setSamplerate(rxa->in_rate); // nob - NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob, rxa->dsp_insize); - NOB::setSamplerate_nob(rxa->nob, rxa->in_rate); + rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->nob->setSize(rxa->dsp_insize); + rxa->nob->setSamplerate(rxa->in_rate); // shift SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); @@ -752,11 +752,11 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); // anb - ANB::setBuffers_anb (rxa->anb, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb(rxa->anb, rxa->dsp_insize); + rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->anb->setSize(rxa->dsp_insize); // nob - NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob, rxa->dsp_insize); + rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->nob->setSize(rxa->dsp_insize); // shift SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); @@ -817,11 +817,11 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); // anb - ANB::setBuffers_anb (rxa->anb, rxa->inbuff, rxa->inbuff); - ANB::setSize_anb (rxa->anb, rxa->dsp_insize); + rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->anb->setSize(rxa->dsp_insize); // nob - NOB::setBuffers_nob(rxa->nob, rxa->inbuff, rxa->inbuff); - NOB::setSize_nob(rxa->nob, rxa->dsp_insize); + rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->nob->setSize(rxa->dsp_insize); // shift SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index d782da646..1be6f8973 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -35,261 +35,238 @@ warren@wpratt.com namespace WDSP { -void ANB::initBlanker(ANB *a) +void ANB::initBlanker() { int i; - a->trans_count = (int)(a->tau * a->samplerate); + trans_count = (int)(tau * samplerate); - if (a->trans_count < 2) - a->trans_count = 2; + if (trans_count < 2) + trans_count = 2; - a->hang_count = (int)(a->hangtime * a->samplerate); - a->adv_count = (int)(a->advtime * a->samplerate); - a->count = 0; - a->in_idx = a->trans_count + a->adv_count; - a->out_idx = 0; - a->coef = PI / a->trans_count; - a->state = 0; - a->avg = 1.0; - a->power = 1.0; - a->backmult = exp(-1.0 / (a->samplerate * a->backtau)); - a->ombackmult = 1.0 - a->backmult; + hang_count = (int)(hangtime * samplerate); + adv_count = (int)(advtime * samplerate); + count = 0; + in_idx = trans_count + adv_count; + out_idx = 0; + coef = PI / trans_count; + state = 0; + avg = 1.0; + power = 1.0; + backmult = exp(-1.0 / (samplerate * backtau)); + ombackmult = 1.0 - backmult; - for (i = 0; i <= a->trans_count; i++) - a->wave[i] = 0.5 * cos(i * a->coef); + for (i = 0; i <= trans_count; i++) + wave[i] = 0.5 * cos(i * coef); - std::fill(a->dline, a->dline + a->dline_size * 2, 0); + std::fill(dline, dline + dline_size * 2, 0); } -ANB* ANB::create_anb ( - int run, - int buffsize, - float* in, - float* out, - double samplerate, - double tau, - double hangtime, - double advtime, - double backtau, - double threshold +ANB::ANB ( + int _run, + int _buffsize, + float* _in, + float* _out, + double _samplerate, + double _tau, + double _hangtime, + double _advtime, + double _backtau, + double _threshold ) { - ANB *a; - a = new ANB; - a->run = run; - a->buffsize = buffsize; - a->in = in; - a->out = out; - a->samplerate = samplerate; - a->tau = tau; - a->hangtime = hangtime; - a->advtime = advtime; - a->backtau = backtau; - a->threshold = threshold; - a->wave = new double[((int)(MAX_SAMPLERATE * MAX_TAU) + 1)]; - a->dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1; - a->dline = new float[a->dline_size * 2]; - initBlanker(a); - a->legacy = new float[2048 * 2]; /////////////// legacy interface - remove - return a; + run = _run; + buffsize = _buffsize; + in = _in; + out = _out; + samplerate = _samplerate; + tau = _tau; + hangtime = _hangtime; + advtime = _advtime; + backtau = _backtau; + threshold = _threshold; + wave = new double[((int)(MAX_SAMPLERATE * MAX_TAU) + 1)]; + dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1; + dline = new float[dline_size * 2]; + initBlanker(); + legacy = new float[2048 * 2]; /////////////// legacy interface - remove } -void ANB::destroy_anb (ANB *a) +ANB::~ANB() { - delete[] (a->legacy); /////////////// legacy interface - remove - delete[] (a->dline); - delete[] (a->wave); - delete (a); + delete[] legacy; /////////////// legacy interface - remove + delete[] dline; + delete[] wave; } -void ANB::flush_anb (ANB *a) -{ - initBlanker (a); -} - -void ANB::xanb (ANB *a) +void ANB::x() { double scale; double mag; int i; - if (a->run) + if (run) { - for (i = 0; i < a->buffsize; i++) + for (i = 0; i < buffsize; i++) { - double xr = a->in[2 * i + 0]; - double xi = a->in[2 * i + 1]; + double xr = in[2 * i + 0]; + double xi = in[2 * i + 1]; mag = sqrt(xr*xr + xi*xi); - a->avg = a->backmult * a->avg + a->ombackmult * mag; - a->dline[2 * a->in_idx + 0] = a->in[2 * i + 0]; - a->dline[2 * a->in_idx + 1] = a->in[2 * i + 1]; + avg = backmult * avg + ombackmult * mag; + dline[2 * in_idx + 0] = in[2 * i + 0]; + dline[2 * in_idx + 1] = in[2 * i + 1]; - if (mag > (a->avg * a->threshold)) - a->count = a->trans_count + a->adv_count; + if (mag > (avg * threshold)) + count = trans_count + adv_count; - switch (a->state) + switch (state) { case 0: - a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0]; - a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1]; + out[2 * i + 0] = dline[2 * out_idx + 0]; + out[2 * i + 1] = dline[2 * out_idx + 1]; - if (a->count > 0) + if (count > 0) { - a->state = 1; - a->dtime = 0; - a->power = 1.0; + state = 1; + dtime = 0; + power = 1.0; } break; case 1: - scale = a->power * (0.5 + a->wave[a->dtime]); - a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale; - a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale; + scale = power * (0.5 + wave[dtime]); + out[2 * i + 0] = dline[2 * out_idx + 0] * scale; + out[2 * i + 1] = dline[2 * out_idx + 1] * scale; - if (++a->dtime > a->trans_count) + if (++dtime > trans_count) { - a->state = 2; - a->atime = 0; + state = 2; + atime = 0; } break; case 2: - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; - if (++a->atime > a->adv_count) - a->state = 3; + if (++atime > adv_count) + state = 3; break; case 3: - if (a->count > 0) - a->htime = -a->count; + if (count > 0) + htime = -count; - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; - if (++a->htime > a->hang_count) + if (++htime > hang_count) { - a->state = 4; - a->itime = 0; + state = 4; + itime = 0; } break; case 4: - scale = 0.5 - a->wave[a->itime]; - a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0] * scale; - a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1] * scale; + scale = 0.5 - wave[itime]; + out[2 * i + 0] = dline[2 * out_idx + 0] * scale; + out[2 * i + 1] = dline[2 * out_idx + 1] * scale; - if (a->count > 0) + if (count > 0) { - a->state = 1; - a->dtime = 0; - a->power = scale; + state = 1; + dtime = 0; + power = scale; } - else if (++a->itime > a->trans_count) + else if (++itime > trans_count) { - a->state = 0; + state = 0; } break; } - if (a->count > 0) - a->count--; + if (count > 0) + count--; - if (++a->in_idx == a->dline_size) - a->in_idx = 0; + if (++in_idx == dline_size) + in_idx = 0; - if (++a->out_idx == a->dline_size) - a->out_idx = 0; + if (++out_idx == dline_size) + out_idx = 0; } } - else if (a->in != a->out) + else if (in != out) { - std::copy(a->in, a->in + a->buffsize * 2, a->out); + std::copy(in, in + buffsize * 2, out); } } -void ANB::setBuffers_anb (ANB *a, float* in, float* out) +void ANB::setBuffers(float* in, float* out) { - a->in = in; - a->out = out; + in = in; + out = out; } -void ANB::setSamplerate_anb (ANB *a, int rate) +void ANB::setSize(int size) { - a->samplerate = rate; - initBlanker (a); -} - -void ANB::setSize_anb (ANB *a, int size) -{ - a->buffsize = size; - initBlanker (a); + buffsize = size; + initBlanker(); } /******************************************************************************************************** * * -* RXA PROPERTIES * +* Common interface * * * ********************************************************************************************************/ -void ANB::SetANBRun (RXA& rxa, int run) +void ANB::setRun (int _run) { - ANB *a = rxa.anb; - a->run = run; + run = _run; } -void ANB::SetANBBuffsize (RXA& rxa, int size) +void ANB::setBuffsize (int size) { - ANB *a = rxa.anb; - a->buffsize = size; + buffsize = size; } -void ANB::SetANBSamplerate (RXA& rxa, int rate) +void ANB::setSamplerate (int rate) { - ANB *a = rxa.anb; - a->samplerate = (double) rate; - initBlanker (a); + samplerate = (double) rate; + initBlanker(); } -void ANB::SetANBTau (RXA& rxa, double tau) +void ANB::setTau (double _tau) { - ANB *a = rxa.anb; - a->tau = tau; - initBlanker (a); + tau = _tau; + initBlanker(); } -void ANB::SetANBHangtime (RXA& rxa, double time) +void ANB::setHangtime (double time) { - ANB *a = rxa.anb; - a->hangtime = time; - initBlanker (a); + hangtime = time; + initBlanker(); } -void ANB::SetANBAdvtime (RXA& rxa, double time) +void ANB::setAdvtime (double time) { - ANB *a = rxa.anb; - a->advtime = time; - initBlanker (a); + advtime = time; + initBlanker(); } -void ANB::SetANBBacktau (RXA& rxa, double tau) +void ANB::setBacktau (double tau) { - ANB *a = rxa.anb; - a->backtau = tau; - initBlanker (a); + backtau = tau; + initBlanker(); } -void ANB::SetANBThreshold (RXA& rxa, double thresh) +void ANB::setThreshold (double thresh) { - ANB *a = rxa.anb; - a->threshold = thresh; + threshold = thresh; } } diff --git a/wdsp/anb.hpp b/wdsp/anb.hpp index 74056c2ec..a0859a5ed 100644 --- a/wdsp/anb.hpp +++ b/wdsp/anb.hpp @@ -66,7 +66,7 @@ public: double ombackmult; // multiplier for waveform averaging float *legacy; - static ANB* create_anb ( + ANB( int run, int buffsize, float* in, @@ -78,25 +78,24 @@ public: double backtau, double threshold ); + ~ANB(); - static void destroy_anb (ANB *a); - static void flush_anb (ANB *a); - static void xanb (ANB *a); - static void setBuffers_anb (ANB *a, float* in, float* out); - static void setSamplerate_anb (ANB *a, int rate); - static void setSize_anb (ANB *a, int size); - // RXA - static void SetANBRun (RXA& rxa, int run); - static void SetANBBuffsize (RXA& rxa, int size); - static void SetANBSamplerate (RXA& rxa, int rate); - static void SetANBTau (RXA& rxa, double tau); - static void SetANBHangtime (RXA& rxa, double time); - static void SetANBAdvtime (RXA& rxa, double time); - static void SetANBBacktau (RXA& rxa, double tau); - static void SetANBThreshold (RXA& rxa, double thresh); + void flush(); + void x(); + void setBuffers(float* in, float* out); + void setSize(int size); + // Common interface + void setRun (int run); + void setBuffsize (int size); + void setSamplerate (int rate); + void setTau (double tau); + void setHangtime (double time); + void setAdvtime (double time); + void setBacktau (double tau); + void setThreshold (double thresh); private: - static void initBlanker(ANB *a); //////////// legacy interface - remove + void initBlanker(); }; diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index 40faa6e37..f11f90392 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -39,129 +39,127 @@ warren@wpratt.com namespace WDSP { -void NOB::init_nob (NOB *a) +void NOB::init_nob() { int i; double coef; - a->adv_slew_count = (int)(a->advslewtime * a->samplerate); - a->adv_count = (int)(a->advtime * a->samplerate); - a->hang_count = (int)(a->hangtime * a->samplerate); - a->hang_slew_count = (int)(a->hangslewtime * a->samplerate); - a->max_imp_seq = (int)(a->max_imp_seq_time * a->samplerate); - a->backmult = exp (-1.0 / (a->samplerate * a->backtau)); - a->ombackmult = 1.0 - a->backmult; + adv_slew_count = (int)(advslewtime * samplerate); + adv_count = (int)(advtime * samplerate); + hang_count = (int)(hangtime * samplerate); + hang_slew_count = (int)(hangslewtime * samplerate); + max_imp_seq = (int)(max_imp_seq_time * samplerate); + backmult = exp (-1.0 / (samplerate * backtau)); + ombackmult = 1.0 - backmult; - if (a->adv_slew_count > 0) + if (adv_slew_count > 0) { - coef = PI / (a->adv_slew_count + 1); + coef = PI / (adv_slew_count + 1); - for (i = 0; i < a->adv_slew_count; i++) - a->awave[i] = 0.5 * cos ((i + 1) * coef); + for (i = 0; i < adv_slew_count; i++) + awave[i] = 0.5 * cos ((i + 1) * coef); } - if (a->hang_slew_count > 0) + if (hang_slew_count > 0) { - coef = PI / a->hang_slew_count; + coef = PI / hang_slew_count; - for (i = 0; i < a->hang_slew_count; i++) - a->hwave[i] = 0.5 * cos (i * coef); + for (i = 0; i < hang_slew_count; i++) + hwave[i] = 0.5 * cos (i * coef); } - flush_nob (a); + flush(); } -NOB* NOB::create_nob ( - int run, - int buffsize, - float* in, - float* out, - double samplerate, - int mode, - double advslewtime, - double advtime, - double hangslewtime, - double hangtime, - double max_imp_seq_time, - double backtau, - double threshold - ) +NOB::NOB ( + int _run, + int _buffsize, + float* _in, + float* _out, + double _samplerate, + int _mode, + double _advslewtime, + double _advtime, + double _hangslewtime, + double _hangtime, + double _max_imp_seq_time, + double _backtau, + double _threshold +) { - NOB *a = new NOB; - a->run = run; - a->buffsize = buffsize; - a->in = in; - a->out = out; - a->samplerate = samplerate; - a->mode = mode; - a->advslewtime = advslewtime; - a->advtime = advtime; - a->hangslewtime = hangslewtime; - a->hangtime = hangtime; - a->max_imp_seq_time = max_imp_seq_time; - a->backtau = backtau; - a->threshold = threshold; - a->dline_size = (int)(MAX_SAMPLERATE * (MAX_ADV_SLEW_TIME + - MAX_ADV_TIME + - MAX_HANG_SLEW_TIME + - MAX_HANG_TIME + - MAX_SEQ_TIME ) + 2); - a->dline = new double[a->dline_size * 2]; - a->imp = new int[a->dline_size]; - a->awave = new double[(int)(MAX_ADV_SLEW_TIME * MAX_SAMPLERATE + 1)]; - a->hwave = new double[(int)(MAX_HANG_SLEW_TIME * MAX_SAMPLERATE + 1)]; + run = _run; + buffsize = _buffsize; + in = _in; + out = _out; + samplerate = _samplerate; + mode = _mode; + advslewtime = _advslewtime; + advtime = _advtime; + hangslewtime = _hangslewtime; + hangtime = _hangtime; + max_imp_seq_time = _max_imp_seq_time; + backtau = _backtau; + threshold = _threshold; + dline_size = (int)(MAX_SAMPLERATE * ( + MAX_ADV_SLEW_TIME + + MAX_ADV_TIME + + MAX_HANG_SLEW_TIME + + MAX_HANG_TIME + + MAX_SEQ_TIME ) + 2); + dline = new double[dline_size * 2]; + imp = new int[dline_size]; + awave = new double[(int)(MAX_ADV_SLEW_TIME * MAX_SAMPLERATE + 1)]; + hwave = new double[(int)(MAX_HANG_SLEW_TIME * MAX_SAMPLERATE + 1)]; - a->filterlen = 10; - a->bfbuff = new double[a->filterlen * 2]; - a->ffbuff = new double[a->filterlen * 2]; - a->fcoefs = new double[a->filterlen]; - a->fcoefs[0] = 0.308720593; - a->fcoefs[1] = 0.216104415; - a->fcoefs[2] = 0.151273090; - a->fcoefs[3] = 0.105891163; - a->fcoefs[4] = 0.074123814; - a->fcoefs[5] = 0.051886670; - a->fcoefs[6] = 0.036320669; - a->fcoefs[7] = 0.025424468; - a->fcoefs[8] = 0.017797128; - a->fcoefs[9] = 0.012457989; + filterlen = 10; + bfbuff = new double[filterlen * 2]; + ffbuff = new double[filterlen * 2]; + fcoefs = new double[filterlen]; + fcoefs[0] = 0.308720593; + fcoefs[1] = 0.216104415; + fcoefs[2] = 0.151273090; + fcoefs[3] = 0.105891163; + fcoefs[4] = 0.074123814; + fcoefs[5] = 0.051886670; + fcoefs[6] = 0.036320669; + fcoefs[7] = 0.025424468; + fcoefs[8] = 0.017797128; + fcoefs[9] = 0.012457989; - init_nob (a); + init_nob(); - a->legacy = new double[2048 * 2]; /////////////// legacy interface - remove - return a; + legacy = new double[2048 * 2]; /////////////// legacy interface - remove } -void NOB::destroy_nob (NOB *a) +NOB::~NOB() { - delete[] (a->legacy); /////////////// remove - delete[] (a->fcoefs); - delete[] (a->ffbuff); - delete[] (a->bfbuff); - delete[] (a->hwave); - delete[] (a->awave); - delete[] (a->imp); - delete[] (a->dline); - delete (a); + delete[] (legacy); /////////////// remove + delete[] (fcoefs); + delete[] (ffbuff); + delete[] (bfbuff); + delete[] (hwave); + delete[] (awave); + delete[] (imp); + delete[] (dline); } -void NOB::flush_nob (NOB *a) +void NOB::flush() { - a->out_idx = 0; - a->scan_idx = a->out_idx + a->adv_slew_count + a->adv_count + 1; - a->in_idx = a->scan_idx + a->max_imp_seq + a->hang_count + a->hang_slew_count + a->filterlen; - a->state = 0; - a->overflow = 0; - a->avg = 1.0; - a->bfb_in_idx = a->filterlen - 1; - a->ffb_in_idx = a->filterlen - 1; - std::fill(a->dline, a->dline + a->dline_size * 2, 0); - std::fill(a->imp, a->imp + a->dline_size, 0); - std::fill(a->bfbuff, a->bfbuff + a->filterlen * 2, 0); - std::fill(a->ffbuff, a->ffbuff + a->filterlen * 2, 0); + out_idx = 0; + scan_idx = out_idx + adv_slew_count + adv_count + 1; + in_idx = scan_idx + max_imp_seq + hang_count + hang_slew_count + filterlen; + state = 0; + overflow = 0; + avg = 1.0; + bfb_in_idx = filterlen - 1; + ffb_in_idx = filterlen - 1; + std::fill(dline, dline + dline_size * 2, 0); + std::fill(imp, imp + dline_size, 0); + std::fill(bfbuff, bfbuff + filterlen * 2, 0); + std::fill(ffbuff, ffbuff + filterlen * 2, 0); } -void NOB::xnob (NOB *a) +void NOB::x() { double scale; double mag; @@ -176,204 +174,204 @@ void NOB::xnob (NOB *a) int ffcount; int staydown; - if (a->run) + if (run) { - for (i = 0; i < a->buffsize; i++) + for (i = 0; i < buffsize; i++) { - a->dline[2 * a->in_idx + 0] = a->in[2 * i + 0]; - a->dline[2 * a->in_idx + 1] = a->in[2 * i + 1]; - mag = sqrt(a->dline[2 * a->in_idx + 0] * a->dline[2 * a->in_idx + 0] + a->dline[2 * a->in_idx + 1] * a->dline[2 * a->in_idx + 1]); - a->avg = a->backmult * a->avg + a->ombackmult * mag; + dline[2 * in_idx + 0] = in[2 * i + 0]; + dline[2 * in_idx + 1] = in[2 * i + 1]; + mag = sqrt(dline[2 * in_idx + 0] * dline[2 * in_idx + 0] + dline[2 * in_idx + 1] * dline[2 * in_idx + 1]); + avg = backmult * avg + ombackmult * mag; - if (mag > (a->avg * a->threshold)) - a->imp[a->in_idx] = 1; + if (mag > (avg * threshold)) + imp[in_idx] = 1; else - a->imp[a->in_idx] = 0; + imp[in_idx] = 0; - if ((bf_idx = a->out_idx + a->adv_slew_count) >= a->dline_size) - bf_idx -= a->dline_size; + if ((bf_idx = out_idx + adv_slew_count) >= dline_size) + bf_idx -= dline_size; - if (a->imp[bf_idx] == 0) + if (imp[bf_idx] == 0) { - if (++a->bfb_in_idx == a->filterlen) - a->bfb_in_idx -= a->filterlen; + if (++bfb_in_idx == filterlen) + bfb_in_idx -= filterlen; - a->bfbuff[2 * a->bfb_in_idx + 0] = a->dline[2 * bf_idx + 0]; - a->bfbuff[2 * a->bfb_in_idx + 1] = a->dline[2 * bf_idx + 1]; + bfbuff[2 * bfb_in_idx + 0] = dline[2 * bf_idx + 0]; + bfbuff[2 * bfb_in_idx + 1] = dline[2 * bf_idx + 1]; } - switch (a->state) + switch (state) { case 0: // normal output & impulse setup { - a->out[2 * i + 0] = a->dline[2 * a->out_idx + 0]; - a->out[2 * i + 1] = a->dline[2 * a->out_idx + 1]; - a->Ilast = a->dline[2 * a->out_idx + 0]; - a->Qlast = a->dline[2 * a->out_idx + 1]; + out[2 * i + 0] = dline[2 * out_idx + 0]; + out[2 * i + 1] = dline[2 * out_idx + 1]; + Ilast = dline[2 * out_idx + 0]; + Qlast = dline[2 * out_idx + 1]; - if (a->imp[a->scan_idx] > 0) + if (imp[scan_idx] > 0) { - a->time = 0; + time = 0; - if (a->adv_slew_count > 0) - a->state = 1; - else if (a->adv_count > 0) - a->state = 2; + if (adv_slew_count > 0) + state = 1; + else if (adv_count > 0) + state = 2; else - a->state = 3; + state = 3; - tidx = a->scan_idx; - a->blank_count = 0; + tidx = scan_idx; + blank_count = 0; do { len = 0; hcount = 0; - while ((a->imp[tidx] > 0 || hcount > 0) && a->blank_count < a->max_imp_seq) + while ((imp[tidx] > 0 || hcount > 0) && blank_count < max_imp_seq) { - a->blank_count++; + blank_count++; if (hcount > 0) hcount--; - if (a->imp[tidx] > 0) - hcount = a->hang_count + a->hang_slew_count; - if (++tidx >= a->dline_size) - tidx -= a->dline_size; + if (imp[tidx] > 0) + hcount = hang_count + hang_slew_count; + if (++tidx >= dline_size) + tidx -= dline_size; } j = 1; len = 0; lidx = tidx; - while (j <= a->adv_slew_count + a->adv_count && len == 0) + while (j <= adv_slew_count + adv_count && len == 0) { - if (a->imp[lidx] == 1) + if (imp[lidx] == 1) { len = j; tidx = lidx; } - if (++lidx >= a->dline_size) - lidx -= a->dline_size; + if (++lidx >= dline_size) + lidx -= dline_size; j++; } - if((a->blank_count += len) > a->max_imp_seq) + if((blank_count += len) > max_imp_seq) { - a->blank_count = a->max_imp_seq; - a->overflow = 1; + blank_count = max_imp_seq; + overflow = 1; break; } } while (len != 0); - if (a->overflow == 0) + if (overflow == 0) { - a->blank_count -= a->hang_slew_count; - a->Inext = a->dline[2 * tidx + 0]; - a->Qnext = a->dline[2 * tidx + 1]; + blank_count -= hang_slew_count; + Inext = dline[2 * tidx + 0]; + Qnext = dline[2 * tidx + 1]; - if (a->mode == 1 || a->mode == 2 || a->mode == 4) + if (mode == 1 || mode == 2 || mode == 4) { - bfboutidx = a->bfb_in_idx; - a->I1 = 0.0; - a->Q1 = 0.0; + bfboutidx = bfb_in_idx; + I1 = 0.0; + Q1 = 0.0; - for (k = 0; k < a->filterlen; k++) + for (k = 0; k < filterlen; k++) { - a->I1 += a->fcoefs[k] * a->bfbuff[2 * bfboutidx + 0]; - a->Q1 += a->fcoefs[k] * a->bfbuff[2 * bfboutidx + 1]; + I1 += fcoefs[k] * bfbuff[2 * bfboutidx + 0]; + Q1 += fcoefs[k] * bfbuff[2 * bfboutidx + 1]; if (--bfboutidx < 0) - bfboutidx += a->filterlen; + bfboutidx += filterlen; } } - if (a->mode == 2 || a->mode == 3 || a->mode == 4) + if (mode == 2 || mode == 3 || mode == 4) { - if ((ff_idx = a->scan_idx + a->blank_count) >= a->dline_size) - ff_idx -= a->dline_size; + if ((ff_idx = scan_idx + blank_count) >= dline_size) + ff_idx -= dline_size; ffcount = 0; - while (ffcount < a->filterlen) + while (ffcount < filterlen) { - if (a->imp[ff_idx] == 0) + if (imp[ff_idx] == 0) { - if (++a->ffb_in_idx == a->filterlen) - a->ffb_in_idx -= a->filterlen; + if (++ffb_in_idx == filterlen) + ffb_in_idx -= filterlen; - a->ffbuff[2 * a->ffb_in_idx + 0] = a->dline[2 * ff_idx + 0]; - a->ffbuff[2 * a->ffb_in_idx + 1] = a->dline[2 * ff_idx + 1]; + ffbuff[2 * ffb_in_idx + 0] = dline[2 * ff_idx + 0]; + ffbuff[2 * ffb_in_idx + 1] = dline[2 * ff_idx + 1]; ++ffcount; } - if (++ff_idx >= a->dline_size) - ff_idx -= a->dline_size; + if (++ff_idx >= dline_size) + ff_idx -= dline_size; } - if ((ffboutidx = a->ffb_in_idx + 1) >= a->filterlen) - ffboutidx -= a->filterlen; + if ((ffboutidx = ffb_in_idx + 1) >= filterlen) + ffboutidx -= filterlen; - a->I2 = 0.0; - a->Q2 = 0.0; + I2 = 0.0; + Q2 = 0.0; - for (k = 0; k < a->filterlen; k++) + for (k = 0; k < filterlen; k++) { - a->I2 += a->fcoefs[k] * a->ffbuff[2 * ffboutidx + 0]; - a->Q2 += a->fcoefs[k] * a->ffbuff[2 * ffboutidx + 1]; + I2 += fcoefs[k] * ffbuff[2 * ffboutidx + 0]; + Q2 += fcoefs[k] * ffbuff[2 * ffboutidx + 1]; - if (++ffboutidx >= a->filterlen) - ffboutidx -= a->filterlen; + if (++ffboutidx >= filterlen) + ffboutidx -= filterlen; } } - switch (a->mode) + switch (mode) { case 0: // zero - a->deltaI = 0.0; - a->deltaQ = 0.0; - a->I = 0.0; - a->Q = 0.0; + deltaI = 0.0; + deltaQ = 0.0; + I = 0.0; + Q = 0.0; break; case 1: // sample-hold - a->deltaI = 0.0; - a->deltaQ = 0.0; - a->I = a->I1; - a->Q = a->Q1; + deltaI = 0.0; + deltaQ = 0.0; + I = I1; + Q = Q1; break; case 2: // mean-hold - a->deltaI = 0.0; - a->deltaQ = 0.0; - a->I = 0.5 * (a->I1 + a->I2); - a->Q = 0.5 * (a->Q1 + a->Q2); + deltaI = 0.0; + deltaQ = 0.0; + I = 0.5 * (I1 + I2); + Q = 0.5 * (Q1 + Q2); break; case 3: // hold-sample - a->deltaI = 0.0; - a->deltaQ = 0.0; - a->I = a->I2; - a->Q = a->Q2; + deltaI = 0.0; + deltaQ = 0.0; + I = I2; + Q = Q2; break; case 4: // linear interpolation - a->deltaI = (a->I2 - a->I1) / (a->adv_count + a->blank_count); - a->deltaQ = (a->Q2 - a->Q1) / (a->adv_count + a->blank_count); - a->I = a->I1; - a->Q = a->Q1; + deltaI = (I2 - I1) / (adv_count + blank_count); + deltaQ = (Q2 - Q1) / (adv_count + blank_count); + I = I1; + Q = Q1; break; } } else { - if (a->adv_slew_count > 0) + if (adv_slew_count > 0) { - a->state = 5; + state = 5; } else { - a->state = 6; - a->time = 0; - a->blank_count += a->adv_count + a->filterlen; + state = 6; + time = 0; + blank_count += adv_count + filterlen; } } } @@ -383,18 +381,18 @@ void NOB::xnob (NOB *a) case 1: // slew output in advance of blanking period { - scale = 0.5 + a->awave[a->time]; - a->out[2 * i + 0] = a->Ilast * scale + (1.0 - scale) * a->I; - a->out[2 * i + 1] = a->Qlast * scale + (1.0 - scale) * a->Q; + scale = 0.5 + awave[time]; + out[2 * i + 0] = Ilast * scale + (1.0 - scale) * I; + out[2 * i + 1] = Qlast * scale + (1.0 - scale) * Q; - if (++a->time == a->adv_slew_count) + if (++time == adv_slew_count) { - a->time = 0; + time = 0; - if (a->adv_count > 0) - a->state = 2; + if (adv_count > 0) + state = 2; else - a->state = 3; + state = 3; } break; @@ -402,15 +400,15 @@ void NOB::xnob (NOB *a) case 2: // initial advance period { - a->out[2 * i + 0] = a->I; - a->out[2 * i + 1] = a->Q; - a->I += a->deltaI; - a->Q += a->deltaQ; + out[2 * i + 0] = I; + out[2 * i + 1] = Q; + I += deltaI; + Q += deltaQ; - if (++a->time == a->adv_count) + if (++time == adv_count) { - a->state = 3; - a->time = 0; + state = 3; + time = 0; } break; @@ -418,21 +416,21 @@ void NOB::xnob (NOB *a) case 3: // impulse & hang period { - a->out[2 * i + 0] = a->I; - a->out[2 * i + 1] = a->Q; - a->I += a->deltaI; - a->Q += a->deltaQ; + out[2 * i + 0] = I; + out[2 * i + 1] = Q; + I += deltaI; + Q += deltaQ; - if (++a->time == a->blank_count) + if (++time == blank_count) { - if (a->hang_slew_count > 0) + if (hang_slew_count > 0) { - a->state = 4; - a->time = 0; + state = 4; + time = 0; } else { - a->state = 0; + state = 0; } } @@ -441,27 +439,27 @@ void NOB::xnob (NOB *a) case 4: // slew output after blanking period { - scale = 0.5 - a->hwave[a->time]; - a->out[2 * i + 0] = a->Inext * scale + (1.0 - scale) * a->I; - a->out[2 * i + 1] = a->Qnext * scale + (1.0 - scale) * a->Q; + scale = 0.5 - hwave[time]; + out[2 * i + 0] = Inext * scale + (1.0 - scale) * I; + out[2 * i + 1] = Qnext * scale + (1.0 - scale) * Q; - if (++a->time == a->hang_slew_count) - a->state = 0; + if (++time == hang_slew_count) + state = 0; break; } case 5: { - scale = 0.5 + a->awave[a->time]; - a->out[2 * i + 0] = a->Ilast * scale; - a->out[2 * i + 1] = a->Qlast * scale; + scale = 0.5 + awave[time]; + out[2 * i + 0] = Ilast * scale; + out[2 * i + 1] = Qlast * scale; - if (++a->time == a->adv_slew_count) + if (++time == adv_slew_count) { - a->state = 6; - a->time = 0; - a->blank_count += a->adv_count + a->filterlen; + state = 6; + time = 0; + blank_count += adv_count + filterlen; } break; @@ -469,56 +467,56 @@ void NOB::xnob (NOB *a) case 6: { - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; - if (++a->time == a->blank_count) - a->state = 7; + if (++time == blank_count) + state = 7; break; } case 7: { - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; staydown = 0; - a->time = 0; + time = 0; - if ((tidx = a->scan_idx + a->hang_slew_count + a->hang_count) >= a->dline_size) - tidx -= a->dline_size; + if ((tidx = scan_idx + hang_slew_count + hang_count) >= dline_size) + tidx -= dline_size; - while (a->time++ <= a->adv_count + a->adv_slew_count + a->hang_slew_count + a->hang_count) // CHECK EXACT COUNTS!!!!!!!!!!!!!!!!!!!!!!! + while (time++ <= adv_count + adv_slew_count + hang_slew_count + hang_count) // CHECK EXACT COUNTS!!!!!!!!!!!!!!!!!!!!!!! { - if (a->imp[tidx] == 1) staydown = 1; - if (--tidx < 0) tidx += a->dline_size; + if (imp[tidx] == 1) staydown = 1; + if (--tidx < 0) tidx += dline_size; } if (staydown == 0) { - if (a->hang_count > 0) + if (hang_count > 0) { - a->state = 8; - a->time = 0; + state = 8; + time = 0; } - else if (a->hang_slew_count > 0) + else if (hang_slew_count > 0) { - a->state = 9; - a->time = 0; + state = 9; + time = 0; - if ((tidx = a->scan_idx + a->hang_slew_count + a->hang_count - a->adv_count - a->adv_slew_count) >= a->dline_size) - tidx -= a->dline_size; + if ((tidx = scan_idx + hang_slew_count + hang_count - adv_count - adv_slew_count) >= dline_size) + tidx -= dline_size; if (tidx < 0) - tidx += a->dline_size; + tidx += dline_size; - a->Inext = a->dline[2 * tidx + 0]; - a->Qnext = a->dline[2 * tidx + 1]; + Inext = dline[2 * tidx + 0]; + Qnext = dline[2 * tidx + 1]; } else { - a->state = 0; - a->overflow = 0; + state = 0; + overflow = 0; } } @@ -527,29 +525,29 @@ void NOB::xnob (NOB *a) case 8: { - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; - if (++a->time == a->hang_count) + if (++time == hang_count) { - if (a->hang_slew_count > 0) + if (hang_slew_count > 0) { - a->state = 9; - a->time = 0; + state = 9; + time = 0; - if ((tidx = a->scan_idx + a->hang_slew_count - a->adv_count - a->adv_slew_count) >= a->dline_size) - tidx -= a->dline_size; + if ((tidx = scan_idx + hang_slew_count - adv_count - adv_slew_count) >= dline_size) + tidx -= dline_size; if (tidx < 0) - tidx += a->dline_size; + tidx += dline_size; - a->Inext = a->dline[2 * tidx + 0]; - a->Qnext = a->dline[2 * tidx + 1]; + Inext = dline[2 * tidx + 0]; + Qnext = dline[2 * tidx + 1]; } else { - a->state = 0; - a->overflow = 0; + state = 0; + overflow = 0; } } @@ -558,118 +556,103 @@ void NOB::xnob (NOB *a) case 9: { - scale = 0.5 - a->hwave[a->time]; - a->out[2 * i + 0] = a->Inext * scale; - a->out[2 * i + 1] = a->Qnext * scale; + scale = 0.5 - hwave[time]; + out[2 * i + 0] = Inext * scale; + out[2 * i + 1] = Qnext * scale; - if (++a->time >= a->hang_slew_count) + if (++time >= hang_slew_count) { - a->state = 0; - a->overflow = 0; + state = 0; + overflow = 0; } break; } } - if (++a->in_idx == a->dline_size) - a->in_idx = 0; + if (++in_idx == dline_size) + in_idx = 0; - if (++a->scan_idx == a->dline_size) - a->scan_idx = 0; + if (++scan_idx == dline_size) + scan_idx = 0; - if (++a->out_idx == a->dline_size) - a->out_idx = 0; + if (++out_idx == dline_size) + out_idx = 0; } } - else if (a->in != a->out) + else if (in != out) { - std::copy(a->in, a->in + a->buffsize * 2, a->out); + std::copy(in, in + buffsize * 2, out); } } -void NOB::setBuffers_nob (NOB *a, float* in, float* out) +void NOB::setBuffers(float* in, float* out) { - a->in = in; - a->out = out; + in = in; + out = out; } -void NOB::setSamplerate_nob (NOB *a, int rate) +void NOB::setSize(int size) { - a->samplerate = rate; - init_nob (a); -} - -void NOB::setSize_nob (NOB *a, int size) -{ - a->buffsize = size; - flush_nob (a); + buffsize = size; + flush(); } /******************************************************************************************************** * * -* RXA PROPERTIES * +* Common interface * * * ********************************************************************************************************/ -void NOB::SetNOBRun (RXA& rxa, int run) +void NOB::setRun(int _run) { - NOB *a = rxa.nob; - a->run = run; + run = _run; } -void NOB::SetNOBMode (RXA& rxa, int mode) +void NOB::setMode(int _mode) { - NOB *a = rxa.nob; - a->mode = mode; + mode = _mode; } -void NOB::SetNOBBuffsize (RXA& rxa, int size) +void NOB::setBuffsize(int size) { - NOB *a = rxa.nob; - a->buffsize = size; + buffsize = size; } -void NOB::SetNOBSamplerate (RXA& rxa, int rate) +void NOB::setSamplerate(int rate) { - NOB *a = rxa.nob; - a->samplerate = (double) rate; - init_nob (a); + samplerate = (double) rate; + init_nob(); } -void NOB::SetNOBTau (RXA& rxa, double tau) +void NOB::setTau(double tau) { - NOB *a = rxa.nob; - a->advslewtime = tau; - a->hangslewtime = tau; - init_nob (a); + advslewtime = tau; + hangslewtime = tau; + init_nob(); } -void NOB::SetNOBHangtime (RXA& rxa, double time) +void NOB::setHangtime(double time) { - NOB *a = rxa.nob; - a->hangtime = time; - init_nob (a); + hangtime = time; + init_nob(); } -void NOB::SetNOBAdvtime (RXA& rxa, double time) +void NOB::setAdvtime(double time) { - NOB *a = rxa.nob; - a->advtime = time; - init_nob (a); + advtime = time; + init_nob(); } -void NOB::SetNOBBacktau (RXA& rxa, double tau) +void NOB::setBacktau(double tau) { - NOB *a = rxa.nob; - a->backtau = tau; - init_nob (a); + backtau = tau; + init_nob(); } -void NOB::SetNOBThreshold (RXA& rxa, double thresh) +void NOB::setThreshold(double thresh) { - NOB *a = rxa.nob; - a->threshold = thresh; + threshold = thresh; } } // namespace diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 2c590d2ec..1a1c4f673 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -82,8 +82,7 @@ public: int overflow; double *legacy; - //////////// legacy interface - remove - static NOB* create_nob ( + NOB( int run, int buffsize, float* in, @@ -98,26 +97,25 @@ public: double backtau, double threshold ); - - static void destroy_nob (NOB* a); - static void flush_nob (NOB* a); - static void xnob (NOB* a); - static void setBuffers_nob (NOB *a, float* in, float* out); - static void setSamplerate_nob (NOB *a, int rate); - static void setSize_nob (NOB *a, int size); - // RXA - static void SetNOBRun (RXA& rxa, int run); - static void SetNOBMode (RXA& rxa, int mode); - static void SetNOBBuffsize (RXA& rxa, int size); - static void SetNOBSamplerate (RXA& rxa, int size); - static void SetNOBTau (RXA& rxa, double tau); - static void SetNOBHangtime (RXA& rxa, double time); - static void SetNOBAdvtime (RXA& rxa, double time); - static void SetNOBBacktau (RXA& rxa, double tau); - static void SetNOBThreshold (RXA& rxa, double thresh); + ~NOB(); + //////////// legacy interface - remove + void flush(); + void x(); + void setBuffers(float* in, float* out); + void setSize(int size); + // Common interface + void setRun (int run); + void setMode (int mode); + void setBuffsize (int size); + void setSamplerate (int size); + void setTau (double tau); + void setHangtime (double time); + void setAdvtime (double time); + void setBacktau (double tau); + void setThreshold (double thresh); private: - static void init_nob (NOB *a); + void init_nob(); }; From 3bdfeb54fff05674946f52b49f4f6601c3306acd Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 23 Jul 2024 19:39:09 +0200 Subject: [PATCH 03/46] WDSP: added missing exports for Mac and Windows --- wdsp/RXA.cpp | 4 ++-- wdsp/anb.cpp | 2 +- wdsp/anb.hpp | 6 ++++-- wdsp/nob.cpp | 2 +- wdsp/nob.hpp | 6 ++++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 206bc44f7..a7a848b25 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -647,8 +647,8 @@ void RXA::flush_rxa (RXA *rxa) void RXA::xrxa (RXA *rxa) { - rxa->anb->x(); - rxa->nob->x(); + rxa->anb->execute(); + rxa->nob->execute(); SHIFT::xshift (rxa->shift); RESAMPLE::xresample (rxa->rsmpin); GEN::xgen (rxa->gen0); diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 1be6f8973..9b07c46fd 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -98,7 +98,7 @@ ANB::~ANB() delete[] wave; } -void ANB::x() +void ANB::execute() { double scale; double mag; diff --git a/wdsp/anb.hpp b/wdsp/anb.hpp index a0859a5ed..7e9de4669 100644 --- a/wdsp/anb.hpp +++ b/wdsp/anb.hpp @@ -28,11 +28,13 @@ warren@wpratt.com #ifndef wdsp_anb_h #define wdsp_anb_h +#include "export.h" + namespace WDSP { class RXA; -class ANB +class WDSP_API ANB { public: int run; @@ -81,7 +83,7 @@ public: ~ANB(); void flush(); - void x(); + void execute(); void setBuffers(float* in, float* out); void setSize(int size); // Common interface diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index f11f90392..cd5079f33 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -159,7 +159,7 @@ void NOB::flush() std::fill(ffbuff, ffbuff + filterlen * 2, 0); } -void NOB::x() +void NOB::execute() { double scale; double mag; diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 1a1c4f673..370619994 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -28,11 +28,13 @@ warren@wpratt.com #ifndef wdsp_nob_h #define wdsp_nob_h +#include "export.h" + namespace WDSP { class RXA; -class NOB +class WDSP_API NOB { public: int run; @@ -100,7 +102,7 @@ public: ~NOB(); //////////// legacy interface - remove void flush(); - void x(); + void execute(); void setBuffers(float* in, float* out); void setSize(int size); // Common interface From 808fe2c075190aa268b6903b2702b3e5f24676a1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 23 Jul 2024 20:21:57 +0200 Subject: [PATCH 04/46] WDSP: removed for Winmdows and Mac --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 92af1b54a..a7e6e50b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -860,7 +860,7 @@ if (FFTW3F_FOUND) set(FT8_SUPPORT ON CACHE INTERNAL "") endif() -if (FFTW3F_FOUND) +if (FFTW3F_FOUND AND LINUX) add_subdirectory(wdsp) add_definitions(-DHAS_WDSP) set(WDSP_SUPPORT ON CACHE INTERNAL "") From 71a5d7a1b472c554f855b057e5274d11800dac3d Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 23 Jul 2024 23:12:10 +0200 Subject: [PATCH 05/46] WDSP: some Sonar fixes --- wdsp/anb.cpp | 12 +++++++----- wdsp/emnr.cpp | 18 +++++++----------- wdsp/nob.cpp | 33 ++++++++++++++++----------------- wdsp/nob.hpp | 1 - 4 files changed, 30 insertions(+), 34 deletions(-) diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 9b07c46fd..ded241cbb 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -187,6 +187,8 @@ void ANB::execute() state = 0; } + break; + default: break; } @@ -206,10 +208,10 @@ void ANB::execute() } } -void ANB::setBuffers(float* in, float* out) +void ANB::setBuffers(float* _in, float* _out) { - in = in; - out = out; + in = _in; + out = _out; } void ANB::setSize(int size) @@ -258,9 +260,9 @@ void ANB::setAdvtime (double time) initBlanker(); } -void ANB::setBacktau (double tau) +void ANB::setBacktau (double _tau) { - backtau = tau; + backtau = _tau; initBlanker(); } diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index e956fc618..a0bc55cee 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -245,7 +245,6 @@ void EMNR::interpM (double* res, double x, int nvals, double* xvals, double* yva void EMNR::calc_emnr(EMNR *a) { - int i; double Dvals[18] = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0, 60.0, 80.0, 120.0, 140.0, 160.0, 180.0, 220.0, 260.0, 300.0 }; double Mvals[18] = { 0.000, 0.260, 0.480, 0.580, 0.610, 0.668, 0.705, 0.762, 0.800, @@ -291,7 +290,7 @@ void EMNR::calc_emnr(EMNR *a) a->revfftout = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); a->save = new float*[a->ovrlp]; // (float **)malloc0(a->ovrlp * sizeof(float *)); - for (i = 0; i < a->ovrlp; i++) + for (int i = 0; i < a->ovrlp; i++) a->save[i] = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); a->outaccum = new float[a->oasize]; // (float *)malloc0(a->oasize * sizeof(float)); @@ -329,11 +328,8 @@ void EMNR::calc_emnr(EMNR *a) a->g.gamma_max = 1000.0; a->g.q = 0.2; - for (i = 0; i < a->g.msize; i++) - { - a->g.prev_mask[i] = 1.0; - a->g.prev_gamma[i] = 1.0; - } + std::fill(a->g.prev_mask, a->g.prev_mask + a->msize, 1.0); + std::fill(a->g.prev_gamma, a->g.prev_gamma + a->msize, 1.0); a->g.gmax = 10000.0; // @@ -433,7 +429,7 @@ void EMNR::calc_emnr(EMNR *a) a->np.pmin_u = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); a->np.actminbuff = new double*[a->np.U]; // (float**)malloc0(a->np.U * sizeof(float*)); - for (i = 0; i < a->np.U; i++) + for (int i = 0; i < a->np.U; i++) a->np.actminbuff[i] = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); { @@ -486,7 +482,7 @@ void EMNR::calc_emnr(EMNR *a) a->nps.Pbar = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); a->nps.EN2y = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); - for (i = 0; i < a->nps.msize; i++) + for (int i = 0; i < a->nps.msize; i++) { a->nps.sigma2N[i] = 0.5; a->nps.Pbar[i] = 0.5; @@ -881,7 +877,7 @@ void EMNR::calc_gain (EMNR *a) { int k; - for (k = 0; k < a->g.msize; k++) + for (k = 0; k < a->msize; k++) { double y0 = a->g.y[2 * k + 0]; double y1 = a->g.y[2 * k + 1]; @@ -937,7 +933,7 @@ void EMNR::calc_gain (EMNR *a) { double gamma, eps_hat, v, ehr; - for (k = 0; k < a->g.msize; k++) + for (k = 0; k < a->msize; k++) { gamma = std::min (a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max); eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k] diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index cd5079f33..3bc3e3ca0 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -127,20 +127,17 @@ NOB::NOB ( fcoefs[9] = 0.012457989; init_nob(); - - legacy = new double[2048 * 2]; /////////////// legacy interface - remove } NOB::~NOB() { - delete[] (legacy); /////////////// remove - delete[] (fcoefs); - delete[] (ffbuff); - delete[] (bfbuff); - delete[] (hwave); - delete[] (awave); - delete[] (imp); - delete[] (dline); + delete[] fcoefs; + delete[] ffbuff; + delete[] bfbuff; + delete[] hwave; + delete[] awave; + delete[] imp; + delete[] dline; } void NOB::flush() @@ -568,6 +565,8 @@ void NOB::execute() break; } + default: + break; } if (++in_idx == dline_size) @@ -586,10 +585,10 @@ void NOB::execute() } } -void NOB::setBuffers(float* in, float* out) +void NOB::setBuffers(float* _in, float* _out) { - in = in; - out = out; + in = _in; + out = _out; } void NOB::setSize(int size) @@ -632,15 +631,15 @@ void NOB::setTau(double tau) init_nob(); } -void NOB::setHangtime(double time) +void NOB::setHangtime(double _time) { - hangtime = time; + hangtime = _time; init_nob(); } -void NOB::setAdvtime(double time) +void NOB::setAdvtime(double _time) { - advtime = time; + advtime = _time; init_nob(); } diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 370619994..2c72c97f0 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -82,7 +82,6 @@ public: double deltaI, deltaQ; double Inext, Qnext; int overflow; - double *legacy; NOB( int run, From e81c9cc5b099e5780e106e2b7ba6c0c0709eba36 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 04:33:42 +0200 Subject: [PATCH 06/46] WDSP: shift and resampler: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 4 +- wdsp/RXA.cpp | 66 +++---- wdsp/TXA.cpp | 44 ++--- wdsp/resample.cpp | 230 ++++++++++++------------ wdsp/resample.hpp | 31 ++-- wdsp/shift.cpp | 105 +++++------ wdsp/shift.hpp | 23 +-- wdsp/snba.cpp | 22 +-- 8 files changed, 257 insertions(+), 268 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index d114d176b..45fe96d60 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -373,8 +373,8 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_rit != settings.m_rit) || (m_settings.m_ritFrequency != settings.m_ritFrequency) || force) { - WDSP::SHIFT::SetShiftFreq(*m_rxa, settings.m_ritFrequency); - WDSP::SHIFT::SetShiftRun(*m_rxa, settings.m_rit ? 1 : 0); + m_rxa->shift->SetFreq(settings.m_ritFrequency); + m_rxa->shift->SetRun(settings.m_rit ? 1 : 0); } // Filter and mode diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index a7a848b25..b514abea4 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -119,7 +119,7 @@ RXA* RXA::create_rxa ( ); // Ftequency shifter - shift to select a slice of spectrum - rxa->shift = SHIFT::create_shift ( + rxa->shift = new SHIFT( 0, // run rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -128,7 +128,7 @@ RXA* RXA::create_rxa ( 0.0); // amount to shift (Hz) // Input resampler - resample to dsp rate for main processing - rxa->rsmpin = RESAMPLE::create_resample ( + rxa->rsmpin = new RESAMPLE( 0, // run - will be turned ON below if needed rxa->dsp_insize, // input buffer size rxa->inbuff, // pointer to input buffer @@ -555,7 +555,7 @@ RXA* RXA::create_rxa ( // AM squelch apply - absent but in the block diagram // Output resampler - rxa->rsmpout = RESAMPLE::create_resample ( + rxa->rsmpout = new RESAMPLE( 0, // run - will be turned ON below if needed rxa->dsp_size, // input buffer size rxa->midbuff, // pointer to input buffer @@ -573,7 +573,7 @@ RXA* RXA::create_rxa ( void RXA::destroy_rxa (RXA *rxa) { - RESAMPLE::destroy_resample (rxa->rsmpout); + delete (rxa->rsmpout); PANEL::destroy_panel (rxa->panel); SSQL::destroy_ssql (rxa->ssql); MPEAK::destroy_mpeak (rxa->mpeak); @@ -599,8 +599,8 @@ void RXA::destroy_rxa (RXA *rxa) NOTCHDB::destroy_notchdb (rxa->ndb); METER::destroy_meter (rxa->adcmeter); GEN::destroy_gen (rxa->gen0); - RESAMPLE::destroy_resample (rxa->rsmpin); - SHIFT::destroy_shift (rxa->shift); + delete (rxa->rsmpin); + delete (rxa->shift); delete (rxa->nob); delete (rxa->anb); delete[] (rxa->midbuff); @@ -616,8 +616,8 @@ void RXA::flush_rxa (RXA *rxa) std::fill(rxa->midbuff, rxa->midbuff + 2 * rxa->dsp_size * 2, 0); rxa->anb->flush(); rxa->nob->flush(); - SHIFT::flush_shift (rxa->shift); - RESAMPLE::flush_resample (rxa->rsmpin); + rxa->shift->flush(); + rxa->rsmpin->flush(); GEN::flush_gen (rxa->gen0); METER::flush_meter (rxa->adcmeter); NBP::flush_nbp (rxa->nbp0); @@ -642,15 +642,15 @@ void RXA::flush_rxa (RXA *rxa) MPEAK::flush_mpeak (rxa->mpeak); SSQL::flush_ssql (rxa->ssql); PANEL::flush_panel (rxa->panel); - RESAMPLE::flush_resample (rxa->rsmpout); + rxa->rsmpout->flush(); } void RXA::xrxa (RXA *rxa) { rxa->anb->execute(); rxa->nob->execute(); - SHIFT::xshift (rxa->shift); - RESAMPLE::xresample (rxa->rsmpin); + rxa->shift->execute(); + rxa->rsmpin->execute(); GEN::xgen (rxa->gen0); METER::xmeter (rxa->adcmeter); BPSNBA::xbpsnbain (rxa->bpsnba, 0); @@ -683,7 +683,7 @@ void RXA::xrxa (RXA *rxa) SSQL::xssql (rxa->ssql); PANEL::xpanel (rxa->panel); AMSQ::xamsq (rxa->amsq); - RESAMPLE::xresample (rxa->rsmpout); + rxa->rsmpout->execute(); } void RXA::setInputSamplerate (RXA *rxa, int in_rate) @@ -706,13 +706,13 @@ void RXA::setInputSamplerate (RXA *rxa, int in_rate) rxa->nob->setSize(rxa->dsp_insize); rxa->nob->setSamplerate(rxa->in_rate); // shift - SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); - SHIFT::setSamplerate_shift (rxa->shift, rxa->in_rate); + rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->shift->setSize(rxa->dsp_insize); + rxa->shift->setSamplerate(rxa->in_rate); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); - RESAMPLE::setInRate_resample (rxa->rsmpin, rxa->in_rate); + rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); + rxa->rsmpin->setSize(rxa->dsp_insize); + rxa->rsmpin->setInRate(rxa->in_rate); ResCheck (*rxa); } @@ -728,8 +728,8 @@ void RXA::setOutputSamplerate (RXA *rxa, int out_rate) delete[] (rxa->outbuff); rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * ch.dsp_outsize * sizeof(complex)); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); - RESAMPLE::setOutRate_resample (rxa->rsmpout, rxa->out_rate); + rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); + rxa->rsmpout->setOutRate(rxa->out_rate); ResCheck (*rxa); } @@ -758,12 +758,12 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); rxa->nob->setSize(rxa->dsp_insize); // shift - SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); + rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->shift->setSize(rxa->dsp_insize); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); - RESAMPLE::setOutRate_resample (rxa->rsmpin, rxa->dsp_rate); + rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); + rxa->rsmpin->setSize(rxa->dsp_insize); + rxa->rsmpin->setOutRate(rxa->dsp_rate); // dsp_rate blocks GEN::setSamplerate_gen (rxa->gen0, rxa->dsp_rate); METER::setSamplerate_meter (rxa->adcmeter, rxa->dsp_rate); @@ -791,8 +791,8 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) SSQL::setSamplerate_ssql (rxa->ssql, rxa->dsp_rate); PANEL::setSamplerate_panel (rxa->panel, rxa->dsp_rate); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); - RESAMPLE::setInRate_resample (rxa->rsmpout, rxa->dsp_rate); + rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); + rxa->rsmpout->setInRate(rxa->dsp_rate); ResCheck (*rxa); } @@ -823,11 +823,11 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); rxa->nob->setSize(rxa->dsp_insize); // shift - SHIFT::setBuffers_shift (rxa->shift, rxa->inbuff, rxa->inbuff); - SHIFT::setSize_shift (rxa->shift, rxa->dsp_insize); + rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); + rxa->shift->setSize(rxa->dsp_insize); // input resampler - RESAMPLE::setBuffers_resample (rxa->rsmpin, rxa->inbuff, rxa->midbuff); - RESAMPLE::setSize_resample (rxa->rsmpin, rxa->dsp_insize); + rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); + rxa->rsmpin->setSize(rxa->dsp_insize); // dsp_size blocks GEN::setBuffers_gen (rxa->gen0, rxa->midbuff, rxa->midbuff); GEN::setSize_gen (rxa->gen0, rxa->dsp_size); @@ -878,8 +878,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) PANEL::setBuffers_panel (rxa->panel, rxa->midbuff, rxa->midbuff); PANEL::setSize_panel (rxa->panel, rxa->dsp_size); // output resampler - RESAMPLE::setBuffers_resample (rxa->rsmpout, rxa->midbuff, rxa->outbuff); - RESAMPLE::setSize_resample (rxa->rsmpout, rxa->dsp_size); + rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); + rxa->rsmpout->setSize(rxa->dsp_size); } void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 5331cd35e..7a9f8e802 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -82,7 +82,7 @@ TXA* TXA::create_txa ( txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *) malloc0 (1 * txa->dsp_outsize * sizeof (complex)); txa->midbuff = new float[3 * txa->dsp_size * 2]; //(float *) malloc0 (2 * txa->dsp_size * sizeof (complex)); - txa->rsmpin.p = RESAMPLE::create_resample ( + txa->rsmpin.p = new RESAMPLE( 0, // run - will be turned on below if needed txa->dsp_insize, // input buffer size txa->inbuff, // pointer to input buffer @@ -486,7 +486,7 @@ TXA* TXA::create_txa ( 0.0, // raised-cosine transition width 0); // window type - txa->rsmpout.p = RESAMPLE::create_resample ( + txa->rsmpout.p = new RESAMPLE( 0, // run - will be turned ON below if needed txa->dsp_size, // input size txa->midbuff, // pointer to input buffer @@ -520,7 +520,7 @@ void TXA::destroy_txa (TXA *txa) { // in reverse order, free each item we created METER::destroy_meter (txa->outmeter.p); - RESAMPLE::destroy_resample (txa->rsmpout.p); + delete (txa->rsmpout.p); CFIR::destroy_cfir(txa->cfir.p); // destroy_calcc (txa->calcc.p); IQC::destroy_iqc (txa->iqc.p0); @@ -549,7 +549,7 @@ void TXA::destroy_txa (TXA *txa) PHROT::destroy_phrot (txa->phrot.p); PANEL::destroy_panel (txa->panel.p); GEN::destroy_gen (txa->gen0.p); - RESAMPLE::destroy_resample (txa->rsmpin.p); + delete (txa->rsmpin.p); delete[] (txa->midbuff); delete[] (txa->outbuff); delete[] (txa->inbuff); @@ -561,7 +561,7 @@ void TXA::flush_txa (TXA* txa) std::fill(txa->inbuff, txa->inbuff + 1 * txa->dsp_insize * 2, 0); std::fill(txa->outbuff, txa->outbuff + 1 * txa->dsp_outsize * 2, 0); std::fill(txa->midbuff, txa->midbuff + 2 * txa->dsp_size * 2, 0); - RESAMPLE::flush_resample (txa->rsmpin.p); + txa->rsmpin.p->flush(); GEN::flush_gen (txa->gen0.p); PANEL::flush_panel (txa->panel.p); PHROT::flush_phrot (txa->phrot.p); @@ -589,13 +589,13 @@ void TXA::flush_txa (TXA* txa) SIPHON::flush_siphon (txa->sip1.p); IQC::flush_iqc (txa->iqc.p0); CFIR::flush_cfir(txa->cfir.p); - RESAMPLE::flush_resample (txa->rsmpout.p); + txa->rsmpout.p->flush(); METER::flush_meter (txa->outmeter.p); } void xtxa (TXA* txa) { - RESAMPLE::xresample (txa->rsmpin.p); // input resampler + txa->rsmpin.p->execute(); // input resampler GEN::xgen (txa->gen0.p); // input signal generator PANEL::xpanel (txa->panel.p); // includes MIC gain PHROT::xphrot (txa->phrot.p); // phase rotator @@ -625,7 +625,7 @@ void xtxa (TXA* txa) SIPHON::xsiphon (txa->sip1.p, 0); // siphon data for display IQC::xiqc (txa->iqc.p0); // PureSignal correction CFIR::xcfir(txa->cfir.p); // compensating FIR filter (used Protocol_2 only) - RESAMPLE::xresample (txa->rsmpout.p); // output resampler + txa->rsmpout.p->execute(); // output resampler METER::xmeter (txa->outmeter.p); // output meter // print_peak_env ("env_exception.txt", txa->dsp_outsize, txa->outbuff, 0.7); } @@ -642,9 +642,9 @@ void TXA::setInputSamplerate (TXA *txa, int in_rate) delete[] (txa->inbuff); txa->inbuff = new float[1 * txa->dsp_insize * 2]; //(float *)malloc0(1 * txa->dsp_insize * sizeof(complex)); // input resampler - RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff); - RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize); - RESAMPLE::setInRate_resample (txa->rsmpin.p, txa->in_rate); + txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin.p->setSize(txa->dsp_insize); + txa->rsmpin.p->setInRate(txa->in_rate); ResCheck (*txa); } @@ -662,8 +662,8 @@ void TXA::setOutputSamplerate (TXA* txa, int out_rate) // cfir - needs to know input rate of firmware CIC CFIR::setOutRate_cfir (txa->cfir.p, txa->out_rate); // output resampler - RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff); - RESAMPLE::setOutRate_resample (txa->rsmpout.p, txa->out_rate); + txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout.p->setOutRate(txa->out_rate); ResCheck (*txa); // output meter METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); @@ -690,9 +690,9 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) delete[] (txa->outbuff); txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); // input resampler - RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff); - RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize); - RESAMPLE::setOutRate_resample (txa->rsmpin.p, txa->dsp_rate); + txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin.p->setSize(txa->dsp_insize); + txa->rsmpin.p->setOutRate(txa->dsp_rate); // dsp_rate blocks GEN::setSamplerate_gen (txa->gen0.p, txa->dsp_rate); PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate); @@ -722,8 +722,8 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate); CFIR::setSamplerate_cfir (txa->cfir.p, txa->dsp_rate); // output resampler - RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff); - RESAMPLE::setInRate_resample (txa->rsmpout.p, txa->dsp_rate); + txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout.p->setInRate(txa->dsp_rate); ResCheck (*txa); // output meter METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); @@ -751,8 +751,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) delete[] (txa->outbuff); txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); // input resampler - RESAMPLE::setBuffers_resample (txa->rsmpin.p, txa->inbuff, txa->midbuff); - RESAMPLE::setSize_resample (txa->rsmpin.p, txa->dsp_insize); + txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin.p->setSize(txa->dsp_insize); // dsp_size blocks GEN::setBuffers_gen (txa->gen0.p, txa->midbuff, txa->midbuff); GEN::setSize_gen (txa->gen0.p, txa->dsp_size); @@ -809,8 +809,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) CFIR::setBuffers_cfir (txa->cfir.p, txa->midbuff, txa->midbuff); CFIR::setSize_cfir (txa->cfir.p, txa->dsp_size); // output resampler - RESAMPLE::setBuffers_resample (txa->rsmpout.p, txa->midbuff, txa->outbuff); - RESAMPLE::setSize_resample (txa->rsmpout.p, txa->dsp_size); + txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout.p->setSize(txa->dsp_size); // output meter METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize); diff --git a/wdsp/resample.cpp b/wdsp/resample.cpp index 02ba5b4b9..ba94436c2 100644 --- a/wdsp/resample.cpp +++ b/wdsp/resample.cpp @@ -37,7 +37,7 @@ namespace WDSP { * * ************************************************************************************************/ -void RESAMPLE::calc_resample (RESAMPLE *a) +void RESAMPLE::calc_resample() { int x, y, z; int i, j, k; @@ -45,10 +45,10 @@ void RESAMPLE::calc_resample (RESAMPLE *a) double full_rate; double fc_norm_high, fc_norm_low; float* impulse; - a->fc = a->fcin; - a->ncoef = a->ncoefin; - x = a->in_rate; - y = a->out_rate; + fc = fcin; + ncoef = ncoefin; + x = in_rate; + y = out_rate; while (y != 0) { @@ -57,220 +57,214 @@ void RESAMPLE::calc_resample (RESAMPLE *a) x = z; } - a->L = a->out_rate / x; - a->M = a->in_rate / x; + L = out_rate / x; + M = in_rate / x; - a->L = a->L <= 0 ? 1 : a->L; - a->M = a->M <= 0 ? 1 : a->M; + L = L <= 0 ? 1 : L; + M = M <= 0 ? 1 : M; - if (a->in_rate < a->out_rate) - min_rate = a->in_rate; + if (in_rate < out_rate) + min_rate = in_rate; else - min_rate = a->out_rate; + min_rate = out_rate; - if (a->fc == 0.0) - a->fc = 0.45 * (float)min_rate; + if (fc == 0.0) + fc = 0.45 * (float)min_rate; - full_rate = (double) (a->in_rate * a->L); - fc_norm_high = a->fc / full_rate; + full_rate = (double) (in_rate * L); + fc_norm_high = fc / full_rate; - if (a->fc_low < 0.0) + if (fc_low < 0.0) fc_norm_low = - fc_norm_high; else - fc_norm_low = a->fc_low / full_rate; + fc_norm_low = fc_low / full_rate; - if (a->ncoef == 0) - a->ncoef = (int)(140.0 * full_rate / min_rate); + if (ncoef == 0) + ncoef = (int)(140.0 * full_rate / min_rate); - a->ncoef = (a->ncoef / a->L + 1) * a->L; - a->cpp = a->ncoef / a->L; - a->h = new double[a->ncoef]; // (float *)malloc0(a->ncoef * sizeof(float)); - impulse = FIR::fir_bandpass(a->ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, a->gain * (double)a->L); + ncoef = (ncoef / L + 1) * L; + cpp = ncoef / L; + h = new double[ncoef]; // (float *)malloc0(ncoef * sizeof(float)); + impulse = FIR::fir_bandpass(ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, gain * (double)L); i = 0; - for (j = 0; j < a->L; j++) + for (j = 0; j < L; j++) { - for (k = 0; k < a->ncoef; k += a->L) - a->h[i++] = impulse[j + k]; + for (k = 0; k < ncoef; k += L) + h[i++] = impulse[j + k]; } - a->ringsize = a->cpp; - a->ring = new double[a->ringsize]; // (float *)malloc0(a->ringsize * sizeof(complex)); - a->idx_in = a->ringsize - 1; - a->phnum = 0; + ringsize = cpp; + ring = new double[ringsize]; // (float *)malloc0(ringsize * sizeof(complex)); + idx_in = ringsize - 1; + phnum = 0; delete[] (impulse); } -void RESAMPLE::decalc_resample (RESAMPLE *a) +void RESAMPLE::decalc_resample() { - delete[] (a->ring); - delete[] (a->h); + delete[] ring; + delete[] h; } -RESAMPLE* RESAMPLE::create_resample ( - int run, - int size, - float* in, - float* out, - int in_rate, - int out_rate, - double fc, - int ncoef, - double gain +RESAMPLE::RESAMPLE ( + int _run, + int _size, + float* _in, + float* _out, + int _in_rate, + int _out_rate, + double _fc, + int _ncoef, + double _gain ) { - RESAMPLE *a = new RESAMPLE; - - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->in_rate = in_rate; - a->out_rate = out_rate; - a->fcin = fc; - a->fc_low = -1.0; // could add to create_resample() parameters - a->ncoefin = ncoef; - a->gain = gain; - calc_resample (a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + in_rate = _in_rate; + out_rate = _out_rate; + fcin = _fc; + fc_low = -1.0; // could add to create_resample() parameters + ncoefin = _ncoef; + gain = _gain; + calc_resample(); } - -void RESAMPLE::destroy_resample (RESAMPLE *a) +RESAMPLE::~RESAMPLE() { - decalc_resample (a); - delete (a); + decalc_resample(); } -void RESAMPLE::flush_resample (RESAMPLE *a) +void RESAMPLE::flush() { - std::fill(a->ring, a->ring + 2 * a->ringsize, 0); - a->idx_in = a->ringsize - 1; - a->phnum = 0; + std::fill(ring, ring + 2 * ringsize, 0); + idx_in = ringsize - 1; + phnum = 0; } - -int RESAMPLE::xresample (RESAMPLE *a) +int RESAMPLE::execute() { int outsamps = 0; - if (a->run) + if (run) { int i, j, n; int idx_out; double I, Q; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->ring[2 * a->idx_in + 0] = a->in[2 * i + 0]; - a->ring[2 * a->idx_in + 1] = a->in[2 * i + 1]; + ring[2 * idx_in + 0] = in[2 * i + 0]; + ring[2 * idx_in + 1] = in[2 * i + 1]; - while (a->phnum < a->L) + while (phnum < L) { I = 0.0; Q = 0.0; - n = a->cpp * a->phnum; + n = cpp * phnum; - for (j = 0; j < a->cpp; j++) + for (j = 0; j < cpp; j++) { - if ((idx_out = a->idx_in + j) >= a->ringsize) - idx_out -= a->ringsize; + if ((idx_out = idx_in + j) >= ringsize) + idx_out -= ringsize; - I += a->h[n + j] * a->ring[2 * idx_out + 0]; - Q += a->h[n + j] * a->ring[2 * idx_out + 1]; + I += h[n + j] * ring[2 * idx_out + 0]; + Q += h[n + j] * ring[2 * idx_out + 1]; } - a->out[2 * outsamps + 0] = I; - a->out[2 * outsamps + 1] = Q; + out[2 * outsamps + 0] = I; + out[2 * outsamps + 1] = Q; outsamps++; - a->phnum += a->M; + phnum += M; } - a->phnum -= a->L; + phnum -= L; - if (--a->idx_in < 0) - a->idx_in = a->ringsize - 1; + if (--idx_in < 0) + idx_in = ringsize - 1; } } - else if (a->in != a->out) + else if (in != out) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } return outsamps; } -void RESAMPLE::setBuffers_resample(RESAMPLE *a, float* in, float* out) +void RESAMPLE::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void RESAMPLE::setSize_resample(RESAMPLE *a, int size) +void RESAMPLE::setSize(int _size) { - a->size = size; - flush_resample (a); + size = _size; + flush(); } -void RESAMPLE::setInRate_resample(RESAMPLE *a, int rate) +void RESAMPLE::setInRate(int _rate) { - decalc_resample (a); - a->in_rate = rate; - calc_resample (a); + decalc_resample(); + in_rate = _rate; + calc_resample(); } -void RESAMPLE::setOutRate_resample(RESAMPLE *a, int rate) +void RESAMPLE::setOutRate(int _rate) { - decalc_resample (a); - a->out_rate = rate; - calc_resample (a); + decalc_resample(); + out_rate = _rate; + calc_resample(); } -void RESAMPLE::setFCLow_resample (RESAMPLE *a, double fc_low) +void RESAMPLE::setFCLow(double _fc_low) { - if (fc_low != a->fc_low) + if (fc_low != _fc_low) { - decalc_resample (a); - a->fc_low = fc_low; - calc_resample (a); + decalc_resample(); + fc_low = _fc_low; + calc_resample(); } } -void RESAMPLE::setBandwidth_resample (RESAMPLE *a, double fc_low, double fc_high) +void RESAMPLE::setBandwidth(double _fc_low, double _fc_high) { - if (fc_low != a->fc_low || fc_high != a->fcin) + if (fc_low != _fc_low || _fc_high != fcin) { - decalc_resample (a); - a->fc_low = fc_low; - a->fcin = fc_high; - calc_resample (a); + decalc_resample(); + fc_low = _fc_low; + fcin = _fc_high; + calc_resample(); } } // exported calls -void* RESAMPLE::create_resampleV (int in_rate, int out_rate) +void* RESAMPLE::createV (int in_rate, int out_rate) { - return (void *)create_resample (1, 0, 0, 0, in_rate, out_rate, 0.0, 0, 1.0); + return (void *) new RESAMPLE(1, 0, 0, 0, in_rate, out_rate, 0.0, 0, 1.0); } -void RESAMPLE::xresampleV (float* input, float* output, int numsamps, int* outsamps, void* ptr) +void RESAMPLE::executeV (float* input, float* output, int numsamps, int* outsamps, void* ptr) { RESAMPLE *a = (RESAMPLE*) ptr; a->in = input; a->out = output; a->size = numsamps; - *outsamps = xresample(a); + *outsamps = a->execute(); } -void RESAMPLE::destroy_resampleV (void* ptr) +void RESAMPLE::destroyV (void* ptr) { - destroy_resample ( (RESAMPLE*) ptr ); + delete ( (RESAMPLE*) ptr ); } } // namespace WDSP diff --git a/wdsp/resample.hpp b/wdsp/resample.hpp index f834ef5b2..3880c146f 100644 --- a/wdsp/resample.hpp +++ b/wdsp/resample.hpp @@ -62,7 +62,7 @@ public: int cpp; // coefficients of the phase int phnum; // phase number - static RESAMPLE* create_resample ( + RESAMPLE ( int run, int size, float* in, @@ -73,23 +73,24 @@ public: int ncoef, double gain ); - static void destroy_resample (RESAMPLE *a); - static void flush_resample (RESAMPLE *a); - static int xresample (RESAMPLE *a); - static void setBuffers_resample (RESAMPLE *a, float* in, float* out); - static void setSize_resample(RESAMPLE *a, int size); - static void setInRate_resample(RESAMPLE *a, int rate); - static void setOutRate_resample(RESAMPLE *a, int rate); - static void setFCLow_resample (RESAMPLE *a, double fc_low); - static void setBandwidth_resample (RESAMPLE *a, double fc_low, double fc_high); + ~RESAMPLE(); + + void flush(); + int execute(); + void setBuffers(float* in, float* out); + void setSize(int size); + void setInRate(int rate); + void setOutRate(int rate); + void setFCLow(double fc_low); + void setBandwidth(double fc_low, double fc_high); // Exported calls - static void* create_resampleV (int in_rate, int out_rate); - static void xresampleV (float* input, float* output, int numsamps, int* outsamps, void* ptr); - static void destroy_resampleV (void* ptr); + static void* createV (int in_rate, int out_rate); + static void executeV (float* input, float* output, int numsamps, int* outsamps, void* ptr); + static void destroyV (void* ptr); private: - static void calc_resample (RESAMPLE *a); - static void decalc_resample (RESAMPLE *a); + void calc_resample(); + void decalc_resample(); }; } // namespace WDSP diff --git a/wdsp/shift.cpp b/wdsp/shift.cpp index 8ab78fad3..f5ebae214 100644 --- a/wdsp/shift.cpp +++ b/wdsp/shift.cpp @@ -31,105 +31,98 @@ warren@wpratt.com namespace WDSP { -void SHIFT::calc_shift (SHIFT *a) +void SHIFT::calc_shift() { - a->delta = TWOPI * a->shift / a->rate; - a->cos_delta = cos (a->delta); - a->sin_delta = sin (a->delta); + delta = TWOPI * shift / rate; + cos_delta = cos (delta); + sin_delta = sin (delta); } -SHIFT* SHIFT::create_shift (int run, int size, float* in, float* out, int rate, double fshift) +SHIFT::SHIFT (int _run, int _size, float* _in, float* _out, int _rate, double _fshift) { - SHIFT *a = new SHIFT; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = (double) rate; - a->shift = fshift; - a->phase = 0.0; - calc_shift (a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + rate = (double) _rate; + shift = _fshift; + phase = 0.0; + calc_shift(); } -void SHIFT::destroy_shift (SHIFT *a) +void SHIFT::flush() { - delete (a); + phase = 0.0; } -void SHIFT::flush_shift (SHIFT *a) +void SHIFT::execute() { - a->phase = 0.0; -} - -void SHIFT::xshift (SHIFT *a) -{ - if (a->run) + if (run) { int i; double I1, Q1, t1, t2; - double cos_phase = cos (a->phase); - double sin_phase = sin (a->phase); + double cos_phase = cos (phase); + double sin_phase = sin (phase); - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - I1 = a->in[2 * i + 0]; - Q1 = a->in[2 * i + 1]; - a->out[2 * i + 0] = I1 * cos_phase - Q1 * sin_phase; - a->out[2 * i + 1] = I1 * sin_phase + Q1 * cos_phase; + I1 = in[2 * i + 0]; + Q1 = in[2 * i + 1]; + out[2 * i + 0] = I1 * cos_phase - Q1 * sin_phase; + out[2 * i + 1] = I1 * sin_phase + Q1 * cos_phase; t1 = cos_phase; t2 = sin_phase; - cos_phase = t1 * a->cos_delta - t2 * a->sin_delta; - sin_phase = t1 * a->sin_delta + t2 * a->cos_delta; - a->phase += a->delta; + cos_phase = t1 * cos_delta - t2 * sin_delta; + sin_phase = t1 * sin_delta + t2 * cos_delta; + phase += delta; - if (a->phase >= TWOPI) - a->phase -= TWOPI; + if (phase >= TWOPI) + phase -= TWOPI; - if (a->phase < 0.0 ) - a->phase += TWOPI; + if (phase < 0.0 ) + phase += TWOPI; } } - else if (a->in != a->out) + else if (in != out) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void SHIFT::setBuffers_shift(SHIFT *a, float* in, float* out) +void SHIFT::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void SHIFT::setSamplerate_shift (SHIFT *a, int rate) +void SHIFT::setSamplerate(int _rate) { - a->rate = rate; - a->phase = 0.0; - calc_shift(a); + rate = _rate; + phase = 0.0; + calc_shift(); } -void SHIFT::setSize_shift (SHIFT *a, int size) +void SHIFT::setSize(int _size) { - a->size = size; - flush_shift (a); + size = _size; + flush(); } /******************************************************************************************************** * * -* RXA Properties * +* Non static * * * ********************************************************************************************************/ -void SHIFT::SetShiftRun (RXA& rxa, int run) +void SHIFT::SetRun (int _run) { - rxa.shift->run = run; + run = _run; } -void SHIFT::SetShiftFreq (RXA& rxa, double fshift) +void SHIFT::SetFreq(double fshift) { - rxa.shift->shift = fshift; - calc_shift (rxa.shift); + shift = fshift; + calc_shift(); } } // namespace WDSP diff --git a/wdsp/shift.hpp b/wdsp/shift.hpp index 7e8deb346..aaed8701e 100644 --- a/wdsp/shift.hpp +++ b/wdsp/shift.hpp @@ -48,19 +48,20 @@ public: double cos_delta; double sin_delta; - static SHIFT* create_shift (int run, int size, float* in, float* out, int rate, double fshift); - static void destroy_shift (SHIFT *a); - static void flush_shift (SHIFT *a); - static void xshift (SHIFT *a); - static void setBuffers_shift (SHIFT *a, float* in, float* out); - static void setSamplerate_shift (SHIFT *a, int rate); - static void setSize_shift (SHIFT *a, int size); - // RXA Properties - static void SetShiftRun (RXA& rxa, int run); - static void SetShiftFreq (RXA& rxa, double fshift); + SHIFT(int run, int size, float* in, float* out, int rate, double fshift); + ~SHIFT() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Noin static + void SetRun (int run); + void SetFreq (double fshift); private: - static void calc_shift (SHIFT *a); + void calc_shift (); }; } // namespace WDSP diff --git a/wdsp/snba.cpp b/wdsp/snba.cpp index ff7d4f176..4ad4e901c 100644 --- a/wdsp/snba.cpp +++ b/wdsp/snba.cpp @@ -56,7 +56,7 @@ void SNBA::calc_snba (SNBA *d) else d->resamprun = 0; - d->inresamp = RESAMPLE::create_resample ( + d->inresamp = new RESAMPLE( d->resamprun, d->bsize, d->in, @@ -67,8 +67,8 @@ void SNBA::calc_snba (SNBA *d) 0, 2.0 ); - RESAMPLE::setFCLow_resample (d->inresamp, 250.0); - d->outresamp = RESAMPLE::create_resample ( + d->inresamp->setFCLow(250.0); + d->outresamp = new RESAMPLE( d->resamprun, d->isize, d->outbuff, @@ -79,7 +79,7 @@ void SNBA::calc_snba (SNBA *d) 0, 2.0 ); - RESAMPLE::setFCLow_resample (d->outresamp, 200.0); + d->outresamp->setFCLow(200.0); d->incr = d->xsize / d->ovrlp; if (d->incr > d->isize) @@ -182,8 +182,8 @@ SNBA* SNBA::create_snba ( void SNBA::decalc_snba (SNBA *d) { - RESAMPLE::destroy_resample (d->outresamp); - RESAMPLE::destroy_resample (d->inresamp); + delete (d->outresamp); + delete (d->inresamp); delete[] (d->outbuff); delete[] (d->inbuff); delete[] (d->outaccum); @@ -243,8 +243,8 @@ void SNBA::flush_snba (SNBA *d) std::fill(d->inbuff, d->inbuff + d->isize * 2, 0); std::fill(d->outbuff, d->outbuff + d->isize * 2, 0); - RESAMPLE::flush_resample (d->inresamp); - RESAMPLE::flush_resample (d->outresamp); + d->inresamp->flush(); + d->outresamp->flush(); } void SNBA::setBuffers_snba (SNBA *a, float* in, float* out) @@ -675,7 +675,7 @@ void SNBA::xsnba (SNBA *d) if (d->run) { int i; - RESAMPLE::xresample (d->inresamp); + d->inresamp->execute(); for (i = 0; i < 2 * d->isize; i += 2) { @@ -703,7 +703,7 @@ void SNBA::xsnba (SNBA *d) d->oaoutidx = (d->oaoutidx + 1) % d->oasize; } - RESAMPLE::xresample (d->outresamp); + d->outresamp->execute(); } else if (d->out != d->in) { @@ -824,7 +824,7 @@ void SNBA::SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh) f_high = std::min (a->out_high_cut, absmax); } - RESAMPLE::setBandwidth_resample (d, f_low, f_high); + d->setBandwidth(f_low, f_high); } } // namespace From 49dc91cb6b1cae45f5e713f473f674d92706b7b2 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 04:52:00 +0200 Subject: [PATCH 07/46] WDSP: minor adjustments --- wdsp/anb.cpp | 22 +++++++++++----------- wdsp/nob.cpp | 42 +++++++++++++++++++++--------------------- wdsp/nob.hpp | 4 ++-- wdsp/resample.cpp | 46 +++++++++++++++++++++++----------------------- wdsp/resample.hpp | 6 +++--- wdsp/shift.cpp | 33 ++++++++++++++++++++------------- wdsp/shift.hpp | 2 +- 7 files changed, 81 insertions(+), 74 deletions(-) diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index ded241cbb..37dc8d018 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -72,18 +72,18 @@ ANB::ANB ( double _advtime, double _backtau, double _threshold -) +) : + run(_run), + buffsize(_buffsize), + in(_in), + out(_out), + samplerate(_samplerate), + tau(_tau), + hangtime(_hangtime), + advtime(_advtime), + backtau(_backtau), + threshold(_threshold) { - run = _run; - buffsize = _buffsize; - in = _in; - out = _out; - samplerate = _samplerate; - tau = _tau; - hangtime = _hangtime; - advtime = _advtime; - backtau = _backtau; - threshold = _threshold; wave = new double[((int)(MAX_SAMPLERATE * MAX_TAU) + 1)]; dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1; dline = new float[dline_size * 2]; diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index 3bc3e3ca0..8a58dfd12 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -39,7 +39,7 @@ warren@wpratt.com namespace WDSP { -void NOB::init_nob() +void NOB::init() { int i; double coef; @@ -85,21 +85,21 @@ NOB::NOB ( double _max_imp_seq_time, double _backtau, double _threshold -) +) : + run(_run), + buffsize(_buffsize), + in(_in), + out(_out), + samplerate(_samplerate), + mode(_mode), + advslewtime(_advslewtime), + advtime(_advtime), + hangslewtime(_hangslewtime), + hangtime(_hangtime), + max_imp_seq_time(_max_imp_seq_time), + backtau(_backtau), + threshold(_threshold) { - run = _run; - buffsize = _buffsize; - in = _in; - out = _out; - samplerate = _samplerate; - mode = _mode; - advslewtime = _advslewtime; - advtime = _advtime; - hangslewtime = _hangslewtime; - hangtime = _hangtime; - max_imp_seq_time = _max_imp_seq_time; - backtau = _backtau; - threshold = _threshold; dline_size = (int)(MAX_SAMPLERATE * ( MAX_ADV_SLEW_TIME + MAX_ADV_TIME + @@ -126,7 +126,7 @@ NOB::NOB ( fcoefs[8] = 0.017797128; fcoefs[9] = 0.012457989; - init_nob(); + init(); } NOB::~NOB() @@ -621,32 +621,32 @@ void NOB::setBuffsize(int size) void NOB::setSamplerate(int rate) { samplerate = (double) rate; - init_nob(); + init(); } void NOB::setTau(double tau) { advslewtime = tau; hangslewtime = tau; - init_nob(); + init(); } void NOB::setHangtime(double _time) { hangtime = _time; - init_nob(); + init(); } void NOB::setAdvtime(double _time) { advtime = _time; - init_nob(); + init(); } void NOB::setBacktau(double tau) { backtau = tau; - init_nob(); + init(); } void NOB::setThreshold(double thresh) diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 2c72c97f0..a641bfc5c 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -41,11 +41,11 @@ public: int buffsize; // size of input/output buffer float* in; // input buffer float* out; // output buffer - int mode; int dline_size; // length of delay line which is 'double dline[length][2]' double *dline; // pointer to delay line int *imp; double samplerate; // samplerate, used to convert times into sample counts + int mode; double advslewtime; // transition time, signal<->zero double advtime; // deadtime (zero output) in advance of detected noise double hangslewtime; @@ -116,7 +116,7 @@ public: void setThreshold (double thresh); private: - void init_nob(); + void init(); }; diff --git a/wdsp/resample.cpp b/wdsp/resample.cpp index ba94436c2..b06ed959f 100644 --- a/wdsp/resample.cpp +++ b/wdsp/resample.cpp @@ -37,7 +37,7 @@ namespace WDSP { * * ************************************************************************************************/ -void RESAMPLE::calc_resample() +void RESAMPLE::calc() { int x, y, z; int i, j, k; @@ -102,7 +102,7 @@ void RESAMPLE::calc_resample() delete[] (impulse); } -void RESAMPLE::decalc_resample() +void RESAMPLE::decalc() { delete[] ring; delete[] h; @@ -118,24 +118,24 @@ RESAMPLE::RESAMPLE ( double _fc, int _ncoef, double _gain -) +) : + run(_run), + size(_size), + in(_in), + out(_out), + in_rate(_in_rate), + out_rate(_out_rate), + fcin(_fc), + fc_low(-1.0), // could add to create_resample() parameters + ncoefin(_ncoef), + gain(_gain) { - run = _run; - size = _size; - in = _in; - out = _out; - in_rate = _in_rate; - out_rate = _out_rate; - fcin = _fc; - fc_low = -1.0; // could add to create_resample() parameters - ncoefin = _ncoef; - gain = _gain; - calc_resample(); + calc(); } RESAMPLE::~RESAMPLE() { - decalc_resample(); + decalc(); } @@ -210,25 +210,25 @@ void RESAMPLE::setSize(int _size) void RESAMPLE::setInRate(int _rate) { - decalc_resample(); + decalc(); in_rate = _rate; - calc_resample(); + calc(); } void RESAMPLE::setOutRate(int _rate) { - decalc_resample(); + decalc(); out_rate = _rate; - calc_resample(); + calc(); } void RESAMPLE::setFCLow(double _fc_low) { if (fc_low != _fc_low) { - decalc_resample(); + decalc(); fc_low = _fc_low; - calc_resample(); + calc(); } } @@ -236,10 +236,10 @@ void RESAMPLE::setBandwidth(double _fc_low, double _fc_high) { if (fc_low != _fc_low || _fc_high != fcin) { - decalc_resample(); + decalc(); fc_low = _fc_low; fcin = _fc_high; - calc_resample(); + calc(); } } diff --git a/wdsp/resample.hpp b/wdsp/resample.hpp index 3880c146f..8f4d7f95c 100644 --- a/wdsp/resample.hpp +++ b/wdsp/resample.hpp @@ -50,9 +50,9 @@ public: double fcin; double fc; double fc_low; - double gain; int idx_in; // index for input into ring int ncoefin; + double gain; int ncoef; // number of coefficients int L; // interpolation factor int M; // decimation factor @@ -89,8 +89,8 @@ public: static void destroyV (void* ptr); private: - void calc_resample(); - void decalc_resample(); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/shift.cpp b/wdsp/shift.cpp index f5ebae214..4f225eaec 100644 --- a/wdsp/shift.cpp +++ b/wdsp/shift.cpp @@ -31,23 +31,30 @@ warren@wpratt.com namespace WDSP { -void SHIFT::calc_shift() +void SHIFT::calc() { delta = TWOPI * shift / rate; cos_delta = cos (delta); sin_delta = sin (delta); } -SHIFT::SHIFT (int _run, int _size, float* _in, float* _out, int _rate, double _fshift) +SHIFT::SHIFT ( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _fshift +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate((double) _rate), + shift(_fshift) { - run = _run; - size = _size; - in = _in; - out = _out; - rate = (double) _rate; - shift = _fshift; phase = 0.0; - calc_shift(); + calc(); } void SHIFT::flush() @@ -99,7 +106,7 @@ void SHIFT::setSamplerate(int _rate) { rate = _rate; phase = 0.0; - calc_shift(); + calc(); } void SHIFT::setSize(int _size) @@ -119,10 +126,10 @@ void SHIFT::SetRun (int _run) run = _run; } -void SHIFT::SetFreq(double fshift) +void SHIFT::SetFreq(double _fshift) { - shift = fshift; - calc_shift(); + shift = _fshift; + calc(); } } // namespace WDSP diff --git a/wdsp/shift.hpp b/wdsp/shift.hpp index aaed8701e..d89398233 100644 --- a/wdsp/shift.hpp +++ b/wdsp/shift.hpp @@ -61,7 +61,7 @@ public: void SetFreq (double fshift); private: - void calc_shift (); + void calc(); }; } // namespace WDSP From 8842b5608061e6771b44efac799bfa30c4eb69b9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 05:24:37 +0200 Subject: [PATCH 08/46] WDSP: generator: replaced static methods (RXA) --- wdsp/RXA.cpp | 16 -- wdsp/RXA.hpp | 2 - wdsp/TXA.cpp | 28 ++-- wdsp/anb.cpp | 1 - wdsp/anb.hpp | 2 - wdsp/gen.cpp | 444 +++++++++++++++++++++++++------------------------ wdsp/gen.hpp | 57 ++++--- wdsp/nob.cpp | 1 - wdsp/nob.hpp | 2 - wdsp/shift.cpp | 1 - wdsp/shift.hpp | 2 - 11 files changed, 271 insertions(+), 285 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index b514abea4..32f613e03 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -32,7 +32,6 @@ warren@wpratt.com #include "meter.hpp" #include "shift.hpp" #include "resample.hpp" -#include "gen.hpp" #include "bandpass.hpp" #include "bps.hpp" #include "nbp.hpp" @@ -139,15 +138,6 @@ RXA* RXA::create_rxa ( 0, // select ncoef automatically 1.0); // gain - // Signal generator - rxa->gen0 = GEN::create_gen ( - 0, // run - rxa->dsp_size, // buffer size - rxa->midbuff, // input buffer - rxa->midbuff, // output buffer - rxa->dsp_rate, // sample rate - 2); // mode - // Input meter - ADC rxa->adcmeter = METER::create_meter ( 0, // run @@ -598,7 +588,6 @@ void RXA::destroy_rxa (RXA *rxa) NBP::destroy_nbp (rxa->nbp0); NOTCHDB::destroy_notchdb (rxa->ndb); METER::destroy_meter (rxa->adcmeter); - GEN::destroy_gen (rxa->gen0); delete (rxa->rsmpin); delete (rxa->shift); delete (rxa->nob); @@ -618,7 +607,6 @@ void RXA::flush_rxa (RXA *rxa) rxa->nob->flush(); rxa->shift->flush(); rxa->rsmpin->flush(); - GEN::flush_gen (rxa->gen0); METER::flush_meter (rxa->adcmeter); NBP::flush_nbp (rxa->nbp0); BPSNBA::flush_bpsnba (rxa->bpsnba); @@ -651,7 +639,6 @@ void RXA::xrxa (RXA *rxa) rxa->nob->execute(); rxa->shift->execute(); rxa->rsmpin->execute(); - GEN::xgen (rxa->gen0); METER::xmeter (rxa->adcmeter); BPSNBA::xbpsnbain (rxa->bpsnba, 0); NBP::xnbp (rxa->nbp0, 0); @@ -765,7 +752,6 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->rsmpin->setSize(rxa->dsp_insize); rxa->rsmpin->setOutRate(rxa->dsp_rate); // dsp_rate blocks - GEN::setSamplerate_gen (rxa->gen0, rxa->dsp_rate); METER::setSamplerate_meter (rxa->adcmeter, rxa->dsp_rate); NBP::setSamplerate_nbp (rxa->nbp0, rxa->dsp_rate); BPSNBA::setSamplerate_bpsnba (rxa->bpsnba, rxa->dsp_rate); @@ -829,8 +815,6 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); rxa->rsmpin->setSize(rxa->dsp_insize); // dsp_size blocks - GEN::setBuffers_gen (rxa->gen0, rxa->midbuff, rxa->midbuff); - GEN::setSize_gen (rxa->gen0, rxa->dsp_size); METER::setBuffers_meter (rxa->adcmeter, rxa->midbuff); METER::setSize_meter (rxa->adcmeter, rxa->dsp_size); NBP::setBuffers_nbp (rxa->nbp0, rxa->midbuff, rxa->midbuff); diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 30b66beb2..1209d8f7b 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -37,7 +37,6 @@ namespace WDSP { class METER; class SHIFT; class RESAMPLE; -class GEN; class BANDPASS; class BPS; class NOTCHDB; @@ -102,7 +101,6 @@ public: NOB *nob; SHIFT *shift; RESAMPLE *rsmpin; - GEN *gen0; METER *adcmeter; NOTCHDB *ndb; NBP *nbp0; diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 7a9f8e802..96436f532 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -93,7 +93,7 @@ TXA* TXA::create_txa ( 0, // select ncoef automatically 1.0); // gain - txa->gen0.p = GEN::create_gen ( + txa->gen0.p = new GEN( 0, // run txa->dsp_size, // buffer size txa->midbuff, // input buffer @@ -397,7 +397,7 @@ TXA* TXA::create_txa ( std::max(2048, txa->dsp_size), // number coefficients for bandpass filter 0); // minimum phase flag - txa->gen1.p = GEN::create_gen ( + txa->gen1.p = new GEN( 0, // run txa->dsp_size, // buffer size txa->midbuff, // input buffer @@ -527,7 +527,7 @@ void TXA::destroy_txa (TXA *txa) SIPHON::destroy_siphon (txa->sip1.p); METER::destroy_meter (txa->alcmeter.p); USLEW::destroy_uslew (txa->uslew.p); - GEN::destroy_gen (txa->gen1.p); + delete (txa->gen1.p); FMMOD::destroy_fmmod (txa->fmmod.p); AMMOD::destroy_ammod (txa->ammod.p); WCPAGC::destroy_wcpagc (txa->alc.p); @@ -548,7 +548,7 @@ void TXA::destroy_txa (TXA *txa) METER::destroy_meter (txa->micmeter.p); PHROT::destroy_phrot (txa->phrot.p); PANEL::destroy_panel (txa->panel.p); - GEN::destroy_gen (txa->gen0.p); + delete (txa->gen0.p); delete (txa->rsmpin.p); delete[] (txa->midbuff); delete[] (txa->outbuff); @@ -562,7 +562,7 @@ void TXA::flush_txa (TXA* txa) std::fill(txa->outbuff, txa->outbuff + 1 * txa->dsp_outsize * 2, 0); std::fill(txa->midbuff, txa->midbuff + 2 * txa->dsp_size * 2, 0); txa->rsmpin.p->flush(); - GEN::flush_gen (txa->gen0.p); + txa->gen0.p->flush(); PANEL::flush_panel (txa->panel.p); PHROT::flush_phrot (txa->phrot.p); METER::flush_meter (txa->micmeter.p); @@ -583,7 +583,7 @@ void TXA::flush_txa (TXA* txa) WCPAGC::flush_wcpagc (txa->alc.p); AMMOD::flush_ammod (txa->ammod.p); FMMOD::flush_fmmod (txa->fmmod.p); - GEN::flush_gen (txa->gen1.p); + txa->gen1.p->flush(); USLEW::flush_uslew (txa->uslew.p); METER::flush_meter (txa->alcmeter.p); SIPHON::flush_siphon (txa->sip1.p); @@ -596,7 +596,7 @@ void TXA::flush_txa (TXA* txa) void xtxa (TXA* txa) { txa->rsmpin.p->execute(); // input resampler - GEN::xgen (txa->gen0.p); // input signal generator + txa->gen0.p->execute(); // input signal generator PANEL::xpanel (txa->panel.p); // includes MIC gain PHROT::xphrot (txa->phrot.p); // phase rotator METER::xmeter (txa->micmeter.p); // MIC meter @@ -619,7 +619,7 @@ void xtxa (TXA* txa) AMMOD::xammod (txa->ammod.p); // AM Modulator EMPHP::xemphp (txa->preemph.p, 1); // FM pre-emphasis (second option) FMMOD::xfmmod (txa->fmmod.p); // FM Modulator - GEN::xgen (txa->gen1.p); // output signal generator (TUN and Two-tone) + txa->gen1.p->execute(); // output signal generator (TUN and Two-tone) USLEW::xuslew (txa->uslew.p); // up-slew for AM, FM, and gens METER::xmeter (txa->alcmeter.p); // ALC Meter SIPHON::xsiphon (txa->sip1.p, 0); // siphon data for display @@ -694,7 +694,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->rsmpin.p->setSize(txa->dsp_insize); txa->rsmpin.p->setOutRate(txa->dsp_rate); // dsp_rate blocks - GEN::setSamplerate_gen (txa->gen0.p, txa->dsp_rate); + txa->gen0.p->setSamplerate(txa->dsp_rate); PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate); PHROT::setSamplerate_phrot (txa->phrot.p, txa->dsp_rate); METER::setSamplerate_meter (txa->micmeter.p, txa->dsp_rate); @@ -715,7 +715,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) WCPAGC::setSamplerate_wcpagc (txa->alc.p, txa->dsp_rate); AMMOD::setSamplerate_ammod (txa->ammod.p, txa->dsp_rate); FMMOD::setSamplerate_fmmod (txa->fmmod.p, txa->dsp_rate); - GEN::setSamplerate_gen (txa->gen1.p, txa->dsp_rate); + txa->gen1.p->setSamplerate(txa->dsp_rate); USLEW::setSamplerate_uslew (txa->uslew.p, txa->dsp_rate); METER::setSamplerate_meter (txa->alcmeter.p, txa->dsp_rate); SIPHON::setSamplerate_siphon (txa->sip1.p, txa->dsp_rate); @@ -754,8 +754,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); txa->rsmpin.p->setSize(txa->dsp_insize); // dsp_size blocks - GEN::setBuffers_gen (txa->gen0.p, txa->midbuff, txa->midbuff); - GEN::setSize_gen (txa->gen0.p, txa->dsp_size); + txa->gen0.p->setBuffers(txa->midbuff, txa->midbuff); + txa->gen0.p->setSize(txa->dsp_size); PANEL::setBuffers_panel (txa->panel.p, txa->midbuff, txa->midbuff); PANEL::setSize_panel (txa->panel.p, txa->dsp_size); PHROT::setBuffers_phrot (txa->phrot.p, txa->midbuff, txa->midbuff); @@ -796,8 +796,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) AMMOD::setSize_ammod (txa->ammod.p, txa->dsp_size); FMMOD::setBuffers_fmmod (txa->fmmod.p, txa->midbuff, txa->midbuff); FMMOD::setSize_fmmod (txa->fmmod.p, txa->dsp_size); - GEN::setBuffers_gen (txa->gen1.p, txa->midbuff, txa->midbuff); - GEN::setSize_gen (txa->gen1.p, txa->dsp_size); + txa->gen1.p->setBuffers(txa->midbuff, txa->midbuff); + txa->gen1.p->setSize(txa->dsp_size); USLEW::setBuffers_uslew (txa->uslew.p, txa->midbuff, txa->midbuff); USLEW::setSize_uslew (txa->uslew.p, txa->dsp_size); METER::setBuffers_meter (txa->alcmeter.p, txa->midbuff); diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 37dc8d018..df7304112 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -27,7 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "anb.hpp" -#include "RXA.hpp" #define MAX_TAU (0.01) // maximum transition time, signal<->zero (slew time) #define MAX_ADVTIME (0.01) // maximum deadtime (zero output) in advance of detected noise diff --git a/wdsp/anb.hpp b/wdsp/anb.hpp index 7e9de4669..00619997b 100644 --- a/wdsp/anb.hpp +++ b/wdsp/anb.hpp @@ -32,8 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API ANB { public: diff --git a/wdsp/gen.cpp b/wdsp/gen.cpp index 998f3d1fd..bb541d3d5 100644 --- a/wdsp/gen.cpp +++ b/wdsp/gen.cpp @@ -28,145 +28,151 @@ warren@wpratt.com #include "comm.hpp" #include "gen.hpp" -#include "RXA.hpp" #include "TXA.hpp" namespace WDSP { -void GEN::calc_tone (GEN *a) +void GEN::calc_tone() { - a->tone.phs = 0.0; - a->tone.delta = TWOPI * a->tone.freq / a->rate; - a->tone.cosdelta = cos (a->tone.delta); - a->tone.sindelta = sin (a->tone.delta); + tone.phs = 0.0; + tone.delta = TWOPI * tone.freq / rate; + tone.cosdelta = cos (tone.delta); + tone.sindelta = sin (tone.delta); } -void GEN::calc_tt (GEN *a) +void GEN::calc_tt() { - a->tt.phs1 = 0.0; - a->tt.phs2 = 0.0; - a->tt.delta1 = TWOPI * a->tt.f1 / a->rate; - a->tt.delta2 = TWOPI * a->tt.f2 / a->rate; - a->tt.cosdelta1 = cos (a->tt.delta1); - a->tt.cosdelta2 = cos (a->tt.delta2); - a->tt.sindelta1 = sin (a->tt.delta1); - a->tt.sindelta2 = sin (a->tt.delta2); + tt.phs1 = 0.0; + tt.phs2 = 0.0; + tt.delta1 = TWOPI * tt.f1 / rate; + tt.delta2 = TWOPI * tt.f2 / rate; + tt.cosdelta1 = cos (tt.delta1); + tt.cosdelta2 = cos (tt.delta2); + tt.sindelta1 = sin (tt.delta1); + tt.sindelta2 = sin (tt.delta2); } -void GEN::calc_sweep (GEN *a) +void GEN::calc_sweep() { - a->sweep.phs = 0.0; - a->sweep.dphs = TWOPI * a->sweep.f1 / a->rate; - a->sweep.d2phs = TWOPI * a->sweep.sweeprate / (a->rate * a->rate); - a->sweep.dphsmax = TWOPI * a->sweep.f2 / a->rate; + sweep.phs = 0.0; + sweep.dphs = TWOPI * sweep.f1 / rate; + sweep.d2phs = TWOPI * sweep.sweeprate / (rate * rate); + sweep.dphsmax = TWOPI * sweep.f2 / rate; } -void GEN::calc_sawtooth (GEN *a) +void GEN::calc_sawtooth() { - a->saw.period = 1.0 / a->saw.f; - a->saw.delta = 1.0 / a->rate; - a->saw.t = 0.0; + saw.period = 1.0 / saw.f; + saw.delta = 1.0 / rate; + saw.t = 0.0; } -void GEN::calc_triangle (GEN *a) +void GEN::calc_triangle() { - a->tri.period = 1.0 / a->tri.f; - a->tri.half = 0.5 * a->tri.period; - a->tri.delta = 1.0 / a->rate; - a->tri.t = 0.0; - a->tri.t1 = 0.0; + tri.period = 1.0 / tri.f; + tri.half = 0.5 * tri.period; + tri.delta = 1.0 / rate; + tri.t = 0.0; + tri.t1 = 0.0; } -void GEN::calc_pulse (GEN *a) +void GEN::calc_pulse () { int i; float delta, theta; - a->pulse.pperiod = 1.0 / a->pulse.pf; - a->pulse.tphs = 0.0; - a->pulse.tdelta = TWOPI * a->pulse.tf / a->rate; - a->pulse.tcosdelta = cos (a->pulse.tdelta); - a->pulse.tsindelta = sin (a->pulse.tdelta); - a->pulse.pntrans = (int)(a->pulse.ptranstime * a->rate); - a->pulse.pnon = (int)(a->pulse.pdutycycle * a->pulse.pperiod * a->rate); - a->pulse.pnoff = (int)(a->pulse.pperiod * a->rate) - a->pulse.pnon - 2 * a->pulse.pntrans; - if (a->pulse.pnoff < 0) a->pulse.pnoff = 0; - a->pulse.pcount = a->pulse.pnoff; - a->pulse.state = 0; - a->pulse.ctrans = new float[a->pulse.pntrans + 1]; // (float *) malloc0 ((a->pulse.pntrans + 1) * sizeof (float)); - delta = PI / (float)a->pulse.pntrans; + pulse.pperiod = 1.0 / pulse.pf; + pulse.tphs = 0.0; + pulse.tdelta = TWOPI * pulse.tf / rate; + pulse.tcosdelta = cos (pulse.tdelta); + pulse.tsindelta = sin (pulse.tdelta); + pulse.pntrans = (int)(pulse.ptranstime * rate); + pulse.pnon = (int)(pulse.pdutycycle * pulse.pperiod * rate); + pulse.pnoff = (int)(pulse.pperiod * rate) - pulse.pnon - 2 * pulse.pntrans; + + if (pulse.pnoff < 0) + pulse.pnoff = 0; + + pulse.pcount = pulse.pnoff; + pulse.state = 0; + pulse.ctrans = new float[pulse.pntrans + 1]; // (float *) malloc0 ((pulse.pntrans + 1) * sizeof (float)); + delta = PI / (float)pulse.pntrans; theta = 0.0; - for (i = 0; i <= a->pulse.pntrans; i++) + for (i = 0; i <= pulse.pntrans; i++) { - a->pulse.ctrans[i] = 0.5 * (1.0 - cos (theta)); + pulse.ctrans[i] = 0.5 * (1.0 - cos (theta)); theta += delta; } } -void GEN::calc_gen (GEN *a) +void GEN::calc() { - calc_tone (a); - calc_tt (a); - calc_sweep (a); - calc_sawtooth (a); - calc_triangle (a); - calc_pulse (a); + calc_tone(); + calc_tt(); + calc_sweep(); + calc_sawtooth(); + calc_triangle(); + calc_pulse(); } -void GEN::decalc_gen (GEN *a) +void GEN::decalc() { - delete[] (a->pulse.ctrans); + delete[] (pulse.ctrans); } -GEN* GEN::create_gen (int run, int size, float* in, float* out, int rate, int mode) +GEN::GEN( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + int _mode +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate((double) _rate), + mode(_mode) { - GEN *a = new GEN; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = (float)rate; - a->mode = mode; // tone - a->tone.mag = 1.0; - a->tone.freq = 1000.0; + tone.mag = 1.0; + tone.freq = 1000.0; // two-tone - a->tt.mag1 = 0.5; - a->tt.mag2 = 0.5; - a->tt.f1 = + 900.0; - a->tt.f2 = + 1700.0; + tt.mag1 = 0.5; + tt.mag2 = 0.5; + tt.f1 = + 900.0; + tt.f2 = + 1700.0; // noise srand ((unsigned int) time (0)); - a->noise.mag = 1.0; + noise.mag = 1.0; // sweep - a->sweep.mag = 1.0; - a->sweep.f1 = -20000.0; - a->sweep.f2 = +20000.0; - a->sweep.sweeprate = +4000.0; + sweep.mag = 1.0; + sweep.f1 = -20000.0; + sweep.f2 = +20000.0; + sweep.sweeprate = +4000.0; // sawtooth - a->saw.mag = 1.0; - a->saw.f = 500.0; + saw.mag = 1.0; + saw.f = 500.0; // triangle - a->tri.mag = 1.0; - a->tri.f = 500.0; + tri.mag = 1.0; + tri.f = 500.0; // pulse - a->pulse.mag = 1.0; - a->pulse.pf = 0.25; - a->pulse.pdutycycle = 0.25; - a->pulse.ptranstime = 0.002; - a->pulse.tf = 1000.0; - calc_gen (a); - return a; + pulse.mag = 1.0; + pulse.pf = 0.25; + pulse.pdutycycle = 0.25; + pulse.ptranstime = 0.002; + pulse.tf = 1000.0; + calc(); } -void GEN::destroy_gen (GEN *a) +GEN::~GEN() { - decalc_gen (a); - delete (a); + decalc(); } -void GEN::flush_gen (GEN *a) +void GEN::flush() { - a->pulse.state = 0; + pulse.state = 0; } enum pstate @@ -177,29 +183,29 @@ enum pstate DOWN }; -void GEN::xgen (GEN *a) +void GEN::execute() { - if (a->run) + if (run) { - switch (a->mode) + switch (mode) { case 0: // tone { int i; float t1, t2; - float cosphase = cos (a->tone.phs); - float sinphase = sin (a->tone.phs); - for (i = 0; i < a->size; i++) + float cosphase = cos (tone.phs); + float sinphase = sin (tone.phs); + for (i = 0; i < size; i++) { - a->out[2 * i + 0] = + a->tone.mag * cosphase; - a->out[2 * i + 1] = - a->tone.mag * sinphase; + out[2 * i + 0] = + tone.mag * cosphase; + out[2 * i + 1] = - tone.mag * sinphase; t1 = cosphase; t2 = sinphase; - cosphase = t1 * a->tone.cosdelta - t2 * a->tone.sindelta; - sinphase = t1 * a->tone.sindelta + t2 * a->tone.cosdelta; - a->tone.phs += a->tone.delta; - if (a->tone.phs >= TWOPI) a->tone.phs -= TWOPI; - if (a->tone.phs < 0.0 ) a->tone.phs += TWOPI; + cosphase = t1 * tone.cosdelta - t2 * tone.sindelta; + sinphase = t1 * tone.sindelta + t2 * tone.cosdelta; + tone.phs += tone.delta; + if (tone.phs >= TWOPI) tone.phs -= TWOPI; + if (tone.phs < 0.0 ) tone.phs += TWOPI; } break; } @@ -207,28 +213,28 @@ void GEN::xgen (GEN *a) { int i; float tcos, tsin; - float cosphs1 = cos (a->tt.phs1); - float sinphs1 = sin (a->tt.phs1); - float cosphs2 = cos (a->tt.phs2); - float sinphs2 = sin (a->tt.phs2); - for (i = 0; i < a->size; i++) + float cosphs1 = cos (tt.phs1); + float sinphs1 = sin (tt.phs1); + float cosphs2 = cos (tt.phs2); + float sinphs2 = sin (tt.phs2); + for (i = 0; i < size; i++) { - a->out[2 * i + 0] = + a->tt.mag1 * cosphs1 + a->tt.mag2 * cosphs2; - a->out[2 * i + 1] = - a->tt.mag1 * sinphs1 - a->tt.mag2 * sinphs2; + out[2 * i + 0] = + tt.mag1 * cosphs1 + tt.mag2 * cosphs2; + out[2 * i + 1] = - tt.mag1 * sinphs1 - tt.mag2 * sinphs2; tcos = cosphs1; tsin = sinphs1; - cosphs1 = tcos * a->tt.cosdelta1 - tsin * a->tt.sindelta1; - sinphs1 = tcos * a->tt.sindelta1 + tsin * a->tt.cosdelta1; - a->tt.phs1 += a->tt.delta1; - if (a->tt.phs1 >= TWOPI) a->tt.phs1 -= TWOPI; - if (a->tt.phs1 < 0.0 ) a->tt.phs1 += TWOPI; + cosphs1 = tcos * tt.cosdelta1 - tsin * tt.sindelta1; + sinphs1 = tcos * tt.sindelta1 + tsin * tt.cosdelta1; + tt.phs1 += tt.delta1; + if (tt.phs1 >= TWOPI) tt.phs1 -= TWOPI; + if (tt.phs1 < 0.0 ) tt.phs1 += TWOPI; tcos = cosphs2; tsin = sinphs2; - cosphs2 = tcos * a->tt.cosdelta2 - tsin * a->tt.sindelta2; - sinphs2 = tcos * a->tt.sindelta2 + tsin * a->tt.cosdelta2; - a->tt.phs2 += a->tt.delta2; - if (a->tt.phs2 >= TWOPI) a->tt.phs2 -= TWOPI; - if (a->tt.phs2 < 0.0 ) a->tt.phs2 += TWOPI; + cosphs2 = tcos * tt.cosdelta2 - tsin * tt.sindelta2; + sinphs2 = tcos * tt.sindelta2 + tsin * tt.cosdelta2; + tt.phs2 += tt.delta2; + if (tt.phs2 >= TWOPI) tt.phs2 -= TWOPI; + if (tt.phs2 < 0.0 ) tt.phs2 += TWOPI; } break; } @@ -236,7 +242,7 @@ void GEN::xgen (GEN *a) { int i; float r1, r2, c, rad; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { do { @@ -245,50 +251,50 @@ void GEN::xgen (GEN *a) c = r1 * r1 + r2 * r2; } while (c >= 1.0); rad = sqrt (-2.0 * log (c) / c); - a->out[2 * i + 0] = a->noise.mag * rad * r1; - a->out[2 * i + 1] = a->noise.mag * rad * r2; + out[2 * i + 0] = noise.mag * rad * r1; + out[2 * i + 1] = noise.mag * rad * r2; } break; } case 3: // sweep { int i; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->out[2 * i + 0] = + a->sweep.mag * cos(a->sweep.phs); - a->out[2 * i + 1] = - a->sweep.mag * sin(a->sweep.phs); - a->sweep.phs += a->sweep.dphs; - a->sweep.dphs += a->sweep.d2phs; - if (a->sweep.phs >= TWOPI) a->sweep.phs -= TWOPI; - if (a->sweep.phs < 0.0 ) a->sweep.phs += TWOPI; - if (a->sweep.dphs > a->sweep.dphsmax) - a->sweep.dphs = TWOPI * a->sweep.f1 / a->rate; + out[2 * i + 0] = + sweep.mag * cos(sweep.phs); + out[2 * i + 1] = - sweep.mag * sin(sweep.phs); + sweep.phs += sweep.dphs; + sweep.dphs += sweep.d2phs; + if (sweep.phs >= TWOPI) sweep.phs -= TWOPI; + if (sweep.phs < 0.0 ) sweep.phs += TWOPI; + if (sweep.dphs > sweep.dphsmax) + sweep.dphs = TWOPI * sweep.f1 / rate; } break; } case 4: // sawtooth (audio only) { int i; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - if (a->saw.t > a->saw.period) a->saw.t -= a->saw.period; - a->out[2 * i + 0] = a->saw.mag * (a->saw.t * a->saw.f - 1.0); - a->out[2 * i + 1] = 0.0; - a->saw.t += a->saw.delta; + if (saw.t > saw.period) saw.t -= saw.period; + out[2 * i + 0] = saw.mag * (saw.t * saw.f - 1.0); + out[2 * i + 1] = 0.0; + saw.t += saw.delta; } } break; case 5: // triangle (audio only) { int i; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - if (a->tri.t > a->tri.period) a->tri.t1 = a->tri.t -= a->tri.period; - if (a->tri.t > a->tri.half) a->tri.t1 -= a->tri.delta; - else a->tri.t1 += a->tri.delta; - a->out[2 * i + 0] = a->tri.mag * (4.0 * a->tri.t1 * a->tri.f - 1.0); - a->out[2 * i + 1] = 0.0; - a->tri.t += a->tri.delta; + if (tri.t > tri.period) tri.t1 = tri.t -= tri.period; + if (tri.t > tri.half) tri.t1 -= tri.delta; + else tri.t1 += tri.delta; + out[2 * i + 0] = tri.mag * (4.0 * tri.t1 * tri.f - 1.0); + out[2 * i + 1] = 0.0; + tri.t += tri.delta; } } break; @@ -296,140 +302,140 @@ void GEN::xgen (GEN *a) { int i; float t1, t2; - float cosphase = cos (a->pulse.tphs); - float sinphase = sin (a->pulse.tphs); - for (i = 0; i < a->size; i++) + float cosphase = cos (pulse.tphs); + float sinphase = sin (pulse.tphs); + for (i = 0; i < size; i++) { - if (a->pulse.pnoff != 0) - switch (a->pulse.state) + if (pulse.pnoff != 0) + switch (pulse.state) { case OFF: - a->out[2 * i + 0] = 0.0; - if (--a->pulse.pcount == 0) + out[2 * i + 0] = 0.0; + if (--pulse.pcount == 0) { - a->pulse.state = UP; - a->pulse.pcount = a->pulse.pntrans; + pulse.state = UP; + pulse.pcount = pulse.pntrans; } break; case UP: - a->out[2 * i + 0] = a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pntrans - a->pulse.pcount]; - if (--a->pulse.pcount == 0) + out[2 * i + 0] = pulse.mag * cosphase * pulse.ctrans[pulse.pntrans - pulse.pcount]; + if (--pulse.pcount == 0) { - a->pulse.state = ON; - a->pulse.pcount = a->pulse.pnon; + pulse.state = ON; + pulse.pcount = pulse.pnon; } break; case ON: - a->out[2 * i + 0] = a->pulse.mag * cosphase; - if (--a->pulse.pcount == 0) + out[2 * i + 0] = pulse.mag * cosphase; + if (--pulse.pcount == 0) { - a->pulse.state = DOWN; - a->pulse.pcount = a->pulse.pntrans; + pulse.state = DOWN; + pulse.pcount = pulse.pntrans; } break; case DOWN: - a->out[2 * i + 0] = a->pulse.mag * cosphase * a->pulse.ctrans[a->pulse.pcount]; - if (--a->pulse.pcount == 0) + out[2 * i + 0] = pulse.mag * cosphase * pulse.ctrans[pulse.pcount]; + if (--pulse.pcount == 0) { - a->pulse.state = OFF; - a->pulse.pcount = a->pulse.pnoff; + pulse.state = OFF; + pulse.pcount = pulse.pnoff; } break; } else - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; t1 = cosphase; t2 = sinphase; - cosphase = t1 * a->pulse.tcosdelta - t2 * a->pulse.tsindelta; - sinphase = t1 * a->pulse.tsindelta + t2 * a->pulse.tcosdelta; - a->pulse.tphs += a->pulse.tdelta; - if (a->pulse.tphs >= TWOPI) a->pulse.tphs -= TWOPI; - if (a->pulse.tphs < 0.0 ) a->pulse.tphs += TWOPI; + cosphase = t1 * pulse.tcosdelta - t2 * pulse.tsindelta; + sinphase = t1 * pulse.tsindelta + t2 * pulse.tcosdelta; + pulse.tphs += pulse.tdelta; + if (pulse.tphs >= TWOPI) pulse.tphs -= TWOPI; + if (pulse.tphs < 0.0 ) pulse.tphs += TWOPI; } } break; default: // silence { - std::fill(a->out, a->out + a->size * 2, 0); + std::fill(out, out + size * 2, 0); break; } } } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void GEN::setBuffers_gen (GEN *a, float* in, float* out) +void GEN::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void GEN::setSamplerate_gen (GEN *a, int rate) +void GEN::setSamplerate(int _rate) { - decalc_gen (a); - a->rate = rate; - calc_gen (a); + decalc(); + rate = _rate; + calc(); } -void GEN::setSize_gen (GEN *a, int size) +void GEN::setSize(int _size) { - a->size = size; - flush_gen (a); + size = _size; + flush(); } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ // 'PreGen', gen0 -void GEN::SetPreGenRun (RXA& rxa, int run) +void GEN::SetPreRun(int _run) { - rxa.gen0->run = run; + run = _run; } -void GEN::SetPreGenMode (RXA& rxa, int mode) +void GEN::SetPreMode(int _mode) { - rxa.gen0->mode = mode; + mode = _mode; } -void GEN::SetPreGenToneMag (RXA& rxa, float mag) +void GEN::SetPreToneMag(float mag) { - rxa.gen0->tone.mag = mag; + tone.mag = mag; } -void GEN::SetPreGenToneFreq (RXA& rxa, float freq) +void GEN::SetPreToneFreq(float freq) { - rxa.gen0->tone.freq = freq; - calc_tone (rxa.gen0); + tone.freq = freq; + calc_tone(); } -void GEN::SetPreGenNoiseMag (RXA& rxa, float mag) +void GEN::SetPreNoiseMag (float mag) { - rxa.gen0->noise.mag = mag; + noise.mag = mag; } -void GEN::SetPreGenSweepMag (RXA& rxa, float mag) +void GEN::SetPreSweepMag(float mag) { - rxa.gen0->sweep.mag = mag; + sweep.mag = mag; } -void GEN::SetPreGenSweepFreq (RXA& rxa, float freq1, float freq2) +void GEN::SetPreSweepFreq(float freq1, float freq2) { - rxa.gen0->sweep.f1 = freq1; - rxa.gen0->sweep.f2 = freq2; - calc_sweep (rxa.gen0); + sweep.f1 = freq1; + sweep.f2 = freq2; + calc_sweep(); } -void GEN::SetPreGenSweepRate (RXA& rxa, float rate) +void GEN::SetPreSweepRate(float rate) { - rxa.gen0->sweep.sweeprate = rate; - calc_sweep (rxa.gen0); + sweep.sweeprate = rate; + calc_sweep(); } @@ -459,7 +465,7 @@ void GEN::SetPreGenToneMag (TXA& txa, float mag) void GEN::SetPreGenToneFreq (TXA& txa, float freq) { txa.gen0.p->tone.freq = freq; - calc_tone (txa.gen0.p); + txa.gen0.p->calc_tone(); } void GEN::SetPreGenNoiseMag (TXA& txa, float mag) @@ -476,13 +482,13 @@ void GEN::SetPreGenSweepFreq (TXA& txa, float freq1, float freq2) { txa.gen0.p->sweep.f1 = freq1; txa.gen0.p->sweep.f2 = freq2; - calc_sweep (txa.gen0.p); + txa.gen0.p->calc_sweep(); } void GEN::SetPreGenSweepRate (TXA& txa, float rate) { txa.gen0.p->sweep.sweeprate = rate; - calc_sweep (txa.gen0.p); + txa.gen0.p->calc_sweep(); } void GEN::SetPreGenSawtoothMag (TXA& txa, float mag) @@ -493,7 +499,7 @@ void GEN::SetPreGenSawtoothMag (TXA& txa, float mag) void GEN::SetPreGenSawtoothFreq (TXA& txa, float freq) { txa.gen0.p->saw.f = freq; - calc_sawtooth (txa.gen0.p); + txa.gen0.p->calc_sawtooth(); } void GEN::SetPreGenTriangleMag (TXA& txa, float mag) @@ -504,7 +510,7 @@ void GEN::SetPreGenTriangleMag (TXA& txa, float mag) void GEN::SetPreGenTriangleFreq (TXA& txa, float freq) { txa.gen0.p->tri.f = freq; - calc_triangle (txa.gen0.p); + txa.gen0.p->calc_triangle(); } void GEN::SetPreGenPulseMag (TXA& txa, float mag) @@ -515,25 +521,25 @@ void GEN::SetPreGenPulseMag (TXA& txa, float mag) void GEN::SetPreGenPulseFreq (TXA& txa, float freq) { txa.gen0.p->pulse.pf = freq; - calc_pulse (txa.gen0.p); + txa.gen0.p->calc_pulse(); } void GEN::SetPreGenPulseDutyCycle (TXA& txa, float dc) { txa.gen0.p->pulse.pdutycycle = dc; - calc_pulse (txa.gen0.p); + txa.gen0.p->calc_pulse(); } void GEN::SetPreGenPulseToneFreq (TXA& txa, float freq) { txa.gen0.p->pulse.tf = freq; - calc_pulse (txa.gen0.p); + txa.gen0.p->calc_pulse(); } void GEN::SetPreGenPulseTransition (TXA& txa, float transtime) { txa.gen0.p->pulse.ptranstime = transtime; - calc_pulse (txa.gen0.p); + txa.gen0.p->calc_pulse(); } // 'PostGen', gen1 @@ -556,7 +562,7 @@ void GEN::SetPostGenToneMag (TXA& txa, float mag) void GEN::SetPostGenToneFreq (TXA& txa, float freq) { txa.gen1.p->tone.freq = freq; - calc_tone (txa.gen1.p); + txa.gen1.p->calc_tone(); } void GEN::SetPostGenTTMag (TXA& txa, float mag1, float mag2) @@ -569,7 +575,7 @@ void GEN::SetPostGenTTFreq (TXA& txa, float freq1, float freq2) { txa.gen1.p->tt.f1 = freq1; txa.gen1.p->tt.f2 = freq2; - calc_tt (txa.gen1.p); + txa.gen1.p->calc_tt(); } void GEN::SetPostGenSweepMag (TXA& txa, float mag) @@ -581,13 +587,13 @@ void GEN::SetPostGenSweepFreq (TXA& txa, float freq1, float freq2) { txa.gen1.p->sweep.f1 = freq1; txa.gen1.p->sweep.f2 = freq2; - calc_sweep (txa.gen1.p); + txa.gen1.p->calc_sweep(); } void GEN::SetPostGenSweepRate (TXA& txa, float rate) { txa.gen1.p->sweep.sweeprate = rate; - calc_sweep (txa.gen1.p); + txa.gen1.p->calc_sweep(); } } // namespace WDSP diff --git a/wdsp/gen.hpp b/wdsp/gen.hpp index 752516469..d4703374e 100644 --- a/wdsp/gen.hpp +++ b/wdsp/gen.hpp @@ -32,7 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; class TXA; class WDSP_API GEN @@ -121,22 +120,30 @@ public: int state; } pulse; - static GEN* create_gen (int run, int size, float* in, float* out, int rate, int mode); - static void destroy_gen (GEN *a); - static void flush_gen (GEN *a); - static void xgen (GEN *a); - static void setBuffers_gen (GEN *a, float* in, float* out); - static void setSamplerate_gen (GEN *a, int rate); - static void setSize_gen (GEN *a, int size); - // RXA Properties - static void SetPreGenRun (RXA& rxa, int run); - static void SetPreGenMode (RXA& rxa, int mode); - static void SetPreGenToneMag (RXA& rxa, float mag); - static void SetPreGenToneFreq (RXA& rxa, float freq); - static void SetPreGenNoiseMag (RXA& rxa, float mag); - static void SetPreGenSweepMag (RXA& rxa, float mag); - static void SetPreGenSweepFreq (RXA& rxa, float freq1, float freq2); - static void SetPreGenSweepRate (RXA& rxa, float rate); + GEN( + int run, + int size, + float* in, + float* out, + int rate, + int mode + ); + ~GEN(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void SetPreRun(int run); + void SetPreMode(int mode); + void SetPreToneMag(float mag); + void SetPreToneFreq(float freq); + void SetPreNoiseMag(float mag); + void SetPreSweepMag(float mag); + void SetPreSweepFreq(float freq1, float freq2); + void SetPreSweepRate(float rate); // TXA Properties static void SetPreGenRun (TXA& txa, int run); static void SetPreGenMode (TXA& txa, int mode); @@ -168,14 +175,14 @@ public: static void SetPostGenSweepRate (TXA& txa, float rate); private: - static void calc_tone (GEN *a); - static void calc_tt (GEN *a); - static void calc_sweep (GEN *a); - static void calc_sawtooth (GEN *a); - static void calc_triangle (GEN *a); - static void calc_pulse (GEN *a); - static void calc_gen (GEN *a); - static void decalc_gen (GEN *a); + void calc_tone(); + void calc_tt(); + void calc_sweep(); + void calc_sawtooth(); + void calc_triangle(); + void calc_pulse(); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index 8a58dfd12..cc54dec1d 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -35,7 +35,6 @@ warren@wpratt.com #define MAX_SAMPLERATE (1536000.0) #include "nob.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index a641bfc5c..51dbd78e2 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -32,8 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API NOB { public: diff --git a/wdsp/shift.cpp b/wdsp/shift.cpp index 4f225eaec..80cdb0d8f 100644 --- a/wdsp/shift.cpp +++ b/wdsp/shift.cpp @@ -27,7 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "shift.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/shift.hpp b/wdsp/shift.hpp index d89398233..a4f097cc8 100644 --- a/wdsp/shift.hpp +++ b/wdsp/shift.hpp @@ -32,8 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API SHIFT { public: From 42fa9f5eb235c1964aecec887dcddd7e6d12710f Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 15:32:21 +0200 Subject: [PATCH 09/46] WDSP: meter: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 4 +- wdsp/RXA.cpp | 42 +++--- wdsp/TXA.cpp | 106 ++++++++-------- wdsp/meter.cpp | 162 ++++++++++-------------- wdsp/meter.hpp | 25 ++-- 5 files changed, 156 insertions(+), 183 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 45fe96d60..de00c377d 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -192,8 +192,8 @@ void WDSPRxSink::processOneSample(Complex &ci) WDSP::RXA::xrxa(m_rxa); m_sCount = m_wdspBufSize; - m_sAvg = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_AV); - m_sPeak = WDSP::METER::GetMeter(*m_rxa, WDSP::RXA::RXA_S_PK); + m_sAvg = m_rxa->smeter->getMeter(WDSP::RXA::RXA_S_AV); + m_sPeak = m_rxa->smeter->getMeter(WDSP::RXA::RXA_S_PK); for (int i = 0; i < m_rxa->get_outsize(); i++) { diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 32f613e03..944061299 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -139,7 +139,7 @@ RXA* RXA::create_rxa ( 1.0); // gain // Input meter - ADC - rxa->adcmeter = METER::create_meter ( + rxa->adcmeter = new METER( 0, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -212,7 +212,7 @@ RXA* RXA::create_rxa ( // End notched bandpass section // S-meter - rxa->smeter = METER::create_meter ( + rxa->smeter = new METER( 1, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -429,7 +429,7 @@ RXA* RXA::create_rxa ( 0.100); // tau_hang_decay // AGC meter - rxa->agcmeter = METER::create_meter ( + rxa->agcmeter = new METER( 0, // run 0, // optional pointer to another 'run' rxa->dsp_size, // size @@ -571,7 +571,7 @@ void RXA::destroy_rxa (RXA *rxa) CBL::destroy_cbl (rxa->cbl); SIPHON::destroy_siphon (rxa->sip1); BANDPASS::destroy_bandpass (rxa->bp1); - METER::destroy_meter (rxa->agcmeter); + delete (rxa->agcmeter); WCPAGC::destroy_wcpagc (rxa->agc); EMNR::destroy_emnr (rxa->emnr); ANR::destroy_anr (rxa->anr); @@ -582,12 +582,12 @@ void RXA::destroy_rxa (RXA *rxa) FMD::destroy_fmd (rxa->fmd); AMD::destroy_amd (rxa->amd); AMSQ::destroy_amsq (rxa->amsq); - METER::destroy_meter (rxa->smeter); + delete (rxa->smeter); SENDER::destroy_sender (rxa->sender); BPSNBA::destroy_bpsnba (rxa->bpsnba); NBP::destroy_nbp (rxa->nbp0); NOTCHDB::destroy_notchdb (rxa->ndb); - METER::destroy_meter (rxa->adcmeter); + delete (rxa->adcmeter); delete (rxa->rsmpin); delete (rxa->shift); delete (rxa->nob); @@ -607,11 +607,11 @@ void RXA::flush_rxa (RXA *rxa) rxa->nob->flush(); rxa->shift->flush(); rxa->rsmpin->flush(); - METER::flush_meter (rxa->adcmeter); + rxa->adcmeter->flush(); NBP::flush_nbp (rxa->nbp0); BPSNBA::flush_bpsnba (rxa->bpsnba); SENDER::flush_sender (rxa->sender); - METER::flush_meter (rxa->smeter); + rxa->smeter->flush(); AMSQ::flush_amsq (rxa->amsq); AMD::flush_amd (rxa->amd); FMD::flush_fmd (rxa->fmd); @@ -622,7 +622,7 @@ void RXA::flush_rxa (RXA *rxa) ANR::flush_anr (rxa->anr); EMNR::flush_emnr (rxa->emnr); WCPAGC::flush_wcpagc (rxa->agc); - METER::flush_meter (rxa->agcmeter); + rxa->agcmeter->flush(); BANDPASS::flush_bandpass (rxa->bp1); SIPHON::flush_siphon (rxa->sip1); CBL::flush_cbl (rxa->cbl); @@ -639,10 +639,10 @@ void RXA::xrxa (RXA *rxa) rxa->nob->execute(); rxa->shift->execute(); rxa->rsmpin->execute(); - METER::xmeter (rxa->adcmeter); + rxa->adcmeter->execute(); BPSNBA::xbpsnbain (rxa->bpsnba, 0); NBP::xnbp (rxa->nbp0, 0); - METER::xmeter (rxa->smeter); + rxa->smeter->execute(); SENDER::xsender (rxa->sender); AMSQ::xamsqcap (rxa->amsq); BPSNBA::xbpsnbaout (rxa->bpsnba, 0); @@ -662,7 +662,7 @@ void RXA::xrxa (RXA *rxa) ANR::xanr (rxa->anr, 1); EMNR::xemnr (rxa->emnr, 1); BANDPASS::xbandpass (rxa->bp1, 1); - METER::xmeter (rxa->agcmeter); + rxa->agcmeter->execute(); SIPHON::xsiphon (rxa->sip1, 0); CBL::xcbl (rxa->cbl); SPEAK::xspeak (rxa->speak); @@ -752,10 +752,10 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->rsmpin->setSize(rxa->dsp_insize); rxa->rsmpin->setOutRate(rxa->dsp_rate); // dsp_rate blocks - METER::setSamplerate_meter (rxa->adcmeter, rxa->dsp_rate); + rxa->adcmeter->setSamplerate(rxa->dsp_rate); NBP::setSamplerate_nbp (rxa->nbp0, rxa->dsp_rate); BPSNBA::setSamplerate_bpsnba (rxa->bpsnba, rxa->dsp_rate); - METER::setSamplerate_meter (rxa->smeter, rxa->dsp_rate); + rxa->smeter->setSamplerate(rxa->dsp_rate); SENDER::setSamplerate_sender (rxa->sender, rxa->dsp_rate); AMSQ::setSamplerate_amsq (rxa->amsq, rxa->dsp_rate); AMD::setSamplerate_amd (rxa->amd, rxa->dsp_rate); @@ -769,7 +769,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) EMNR::setSamplerate_emnr (rxa->emnr, rxa->dsp_rate); BANDPASS::setSamplerate_bandpass (rxa->bp1, rxa->dsp_rate); WCPAGC::setSamplerate_wcpagc (rxa->agc, rxa->dsp_rate); - METER::setSamplerate_meter (rxa->agcmeter, rxa->dsp_rate); + rxa->agcmeter->setSamplerate(rxa->dsp_rate); SIPHON::setSamplerate_siphon (rxa->sip1, rxa->dsp_rate); CBL::setSamplerate_cbl (rxa->cbl, rxa->dsp_rate); SPEAK::setSamplerate_speak (rxa->speak, rxa->dsp_rate); @@ -815,14 +815,14 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); rxa->rsmpin->setSize(rxa->dsp_insize); // dsp_size blocks - METER::setBuffers_meter (rxa->adcmeter, rxa->midbuff); - METER::setSize_meter (rxa->adcmeter, rxa->dsp_size); + rxa->adcmeter->setBuffers(rxa->midbuff); + rxa->adcmeter->setSize(rxa->dsp_size); NBP::setBuffers_nbp (rxa->nbp0, rxa->midbuff, rxa->midbuff); NBP::setSize_nbp (rxa->nbp0, rxa->dsp_size); BPSNBA::setBuffers_bpsnba (rxa->bpsnba, rxa->midbuff, rxa->midbuff); BPSNBA::setSize_bpsnba (rxa->bpsnba, rxa->dsp_size); - METER::setBuffers_meter (rxa->smeter, rxa->midbuff); - METER::setSize_meter (rxa->smeter, rxa->dsp_size); + rxa->smeter->setBuffers(rxa->midbuff); + rxa->smeter->METER::setSize(rxa->dsp_size); SENDER::setBuffers_sender (rxa->sender, rxa->midbuff); SENDER::setSize_sender (rxa->sender, rxa->dsp_size); AMSQ::setBuffers_amsq (rxa->amsq, rxa->midbuff, rxa->midbuff, rxa->midbuff); @@ -847,8 +847,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); WCPAGC::setBuffers_wcpagc (rxa->agc, rxa->midbuff, rxa->midbuff); WCPAGC::setSize_wcpagc (rxa->agc, rxa->dsp_size); - METER::setBuffers_meter (rxa->agcmeter, rxa->midbuff); - METER::setSize_meter (rxa->agcmeter, rxa->dsp_size); + rxa->agcmeter->METER::setBuffers(rxa->midbuff); + rxa->agcmeter->METER::setSize(rxa->dsp_size); SIPHON::setBuffers_siphon (rxa->sip1, rxa->midbuff); SIPHON::setSize_siphon (rxa->sip1, rxa->dsp_size); CBL::setBuffers_cbl (rxa->cbl, rxa->midbuff, rxa->midbuff); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 96436f532..9d901c4fb 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -121,7 +121,7 @@ TXA* TXA::create_txa ( 338.0, // 1/2 of phase frequency 8); // number of stages - txa->micmeter.p = METER::create_meter ( + txa->micmeter.p = new METER( 1, // run 0, // optional pointer to another 'run' txa->dsp_size, // size @@ -170,7 +170,7 @@ TXA* TXA::create_txa ( txa->dsp_rate); // samplerate } - txa->eqmeter.p = METER::create_meter ( + txa->eqmeter.p = new METER( 1, // run &(txa->eqp.p->run), // pointer to eqp 'run' txa->dsp_size, // size @@ -222,7 +222,7 @@ TXA* TXA::create_txa ( 2.000, // hang_thresh 0.100); // tau_hang_decay - txa->lvlrmeter.p = METER::create_meter ( + txa->lvlrmeter.p = new METER( 1, // run &(txa->leveler.p->run), // pointer to leveler 'run' txa->dsp_size, // size @@ -262,7 +262,7 @@ TXA* TXA::create_txa ( 0.50); // display time constant } - txa->cfcmeter.p = METER::create_meter ( + txa->cfcmeter.p = new METER( 1, // run &(txa->cfcomp.p->run), // pointer to eqp 'run' txa->dsp_size, // size @@ -333,7 +333,7 @@ TXA* TXA::create_txa ( 1, // wintype 1.0); // gain - txa->compmeter.p = METER::create_meter ( + txa->compmeter.p = new METER( 1, // run &(txa->compressor.p->run), // pointer to compressor 'run' txa->dsp_size, // size @@ -415,7 +415,7 @@ TXA* TXA::create_txa ( 0.000, // delay time 0.005); // upslew time - txa->alcmeter.p = METER::create_meter ( + txa->alcmeter.p = new METER( 1, // run 0, // optional pointer to a 'run' txa->dsp_size, // size @@ -497,7 +497,7 @@ TXA* TXA::create_txa ( 0, // select ncoef automatically 0.980); // gain - txa->outmeter.p = METER::create_meter ( + txa->outmeter.p = new METER( 1, // run 0, // optional pointer to another 'run' txa->dsp_outsize, // size @@ -519,33 +519,33 @@ TXA* TXA::create_txa ( void TXA::destroy_txa (TXA *txa) { // in reverse order, free each item we created - METER::destroy_meter (txa->outmeter.p); + delete (txa->outmeter.p); delete (txa->rsmpout.p); CFIR::destroy_cfir(txa->cfir.p); // destroy_calcc (txa->calcc.p); IQC::destroy_iqc (txa->iqc.p0); SIPHON::destroy_siphon (txa->sip1.p); - METER::destroy_meter (txa->alcmeter.p); + delete (txa->alcmeter.p); USLEW::destroy_uslew (txa->uslew.p); delete (txa->gen1.p); FMMOD::destroy_fmmod (txa->fmmod.p); AMMOD::destroy_ammod (txa->ammod.p); WCPAGC::destroy_wcpagc (txa->alc.p); - METER::destroy_meter (txa->compmeter.p); + delete (txa->compmeter.p); BANDPASS::destroy_bandpass (txa->bp2.p); OSCTRL::destroy_osctrl (txa->osctrl.p); BANDPASS::destroy_bandpass (txa->bp1.p); COMPRESSOR::destroy_compressor (txa->compressor.p); BANDPASS::destroy_bandpass (txa->bp0.p); - METER::destroy_meter (txa->cfcmeter.p); + delete (txa->cfcmeter.p); CFCOMP::destroy_cfcomp (txa->cfcomp.p); - METER::destroy_meter (txa->lvlrmeter.p); + delete (txa->lvlrmeter.p); WCPAGC::destroy_wcpagc (txa->leveler.p); EMPHP::destroy_emphp (txa->preemph.p); - METER::destroy_meter (txa->eqmeter.p); + delete (txa->eqmeter.p); EQP::destroy_eqp (txa->eqp.p); AMSQ::destroy_amsq (txa->amsq.p); - METER::destroy_meter (txa->micmeter.p); + delete (txa->micmeter.p); PHROT::destroy_phrot (txa->phrot.p); PANEL::destroy_panel (txa->panel.p); delete (txa->gen0.p); @@ -565,32 +565,32 @@ void TXA::flush_txa (TXA* txa) txa->gen0.p->flush(); PANEL::flush_panel (txa->panel.p); PHROT::flush_phrot (txa->phrot.p); - METER::flush_meter (txa->micmeter.p); + txa->micmeter.p->flush (); AMSQ::flush_amsq (txa->amsq.p); EQP::flush_eqp (txa->eqp.p); - METER::flush_meter (txa->eqmeter.p); + txa->eqmeter.p->flush (); EMPHP::flush_emphp (txa->preemph.p); WCPAGC::flush_wcpagc (txa->leveler.p); - METER::flush_meter (txa->lvlrmeter.p); + txa->lvlrmeter.p->flush (); CFCOMP::flush_cfcomp (txa->cfcomp.p); - METER::flush_meter (txa->cfcmeter.p); + txa->cfcmeter.p->flush (); BANDPASS::flush_bandpass (txa->bp0.p); COMPRESSOR::flush_compressor (txa->compressor.p); BANDPASS::flush_bandpass (txa->bp1.p); OSCTRL::flush_osctrl (txa->osctrl.p); BANDPASS::flush_bandpass (txa->bp2.p); - METER::flush_meter (txa->compmeter.p); + txa->compmeter.p->flush (); WCPAGC::flush_wcpagc (txa->alc.p); AMMOD::flush_ammod (txa->ammod.p); FMMOD::flush_fmmod (txa->fmmod.p); txa->gen1.p->flush(); USLEW::flush_uslew (txa->uslew.p); - METER::flush_meter (txa->alcmeter.p); + txa->alcmeter.p->flush (); SIPHON::flush_siphon (txa->sip1.p); IQC::flush_iqc (txa->iqc.p0); CFIR::flush_cfir(txa->cfir.p); txa->rsmpout.p->flush(); - METER::flush_meter (txa->outmeter.p); + txa->outmeter.p->flush (); } void xtxa (TXA* txa) @@ -599,34 +599,34 @@ void xtxa (TXA* txa) txa->gen0.p->execute(); // input signal generator PANEL::xpanel (txa->panel.p); // includes MIC gain PHROT::xphrot (txa->phrot.p); // phase rotator - METER::xmeter (txa->micmeter.p); // MIC meter + txa->micmeter.p->execute (); // MIC meter AMSQ::xamsqcap (txa->amsq.p); // downward expander capture AMSQ::xamsq (txa->amsq.p); // downward expander action EQP::xeqp (txa->eqp.p); // pre-EQ - METER::xmeter (txa->eqmeter.p); // EQ meter + txa->eqmeter.p->execute (); // EQ meter EMPHP::xemphp (txa->preemph.p, 0); // FM pre-emphasis (first option) WCPAGC::xwcpagc (txa->leveler.p); // Leveler - METER::xmeter (txa->lvlrmeter.p); // Leveler Meter + txa->lvlrmeter.p->execute (); // Leveler Meter CFCOMP::xcfcomp (txa->cfcomp.p, 0); // Continuous Frequency Compressor with post-EQ - METER::xmeter (txa->cfcmeter.p); // CFC+PostEQ Meter + txa->cfcmeter.p->execute (); // CFC+PostEQ Meter BANDPASS::xbandpass (txa->bp0.p, 0); // primary bandpass filter COMPRESSOR::xcompressor (txa->compressor.p); // COMP compressor BANDPASS::xbandpass (txa->bp1.p, 0); // aux bandpass (runs if COMP) OSCTRL::xosctrl (txa->osctrl.p); // CESSB Overshoot Control BANDPASS::xbandpass (txa->bp2.p, 0); // aux bandpass (runs if CESSB) - METER::xmeter (txa->compmeter.p); // COMP meter + txa->compmeter.p->execute (); // COMP meter WCPAGC::xwcpagc (txa->alc.p); // ALC AMMOD::xammod (txa->ammod.p); // AM Modulator EMPHP::xemphp (txa->preemph.p, 1); // FM pre-emphasis (second option) FMMOD::xfmmod (txa->fmmod.p); // FM Modulator txa->gen1.p->execute(); // output signal generator (TUN and Two-tone) USLEW::xuslew (txa->uslew.p); // up-slew for AM, FM, and gens - METER::xmeter (txa->alcmeter.p); // ALC Meter + txa->alcmeter.p->execute (); // ALC Meter SIPHON::xsiphon (txa->sip1.p, 0); // siphon data for display IQC::xiqc (txa->iqc.p0); // PureSignal correction CFIR::xcfir(txa->cfir.p); // compensating FIR filter (used Protocol_2 only) txa->rsmpout.p->execute(); // output resampler - METER::xmeter (txa->outmeter.p); // output meter + txa->outmeter.p->execute (); // output meter // print_peak_env ("env_exception.txt", txa->dsp_outsize, txa->outbuff, 0.7); } @@ -666,9 +666,9 @@ void TXA::setOutputSamplerate (TXA* txa, int out_rate) txa->rsmpout.p->setOutRate(txa->out_rate); ResCheck (*txa); // output meter - METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); - METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize); - METER::setSamplerate_meter (txa->outmeter.p, txa->out_rate); + txa->outmeter.p->setBuffers(txa->outbuff); + txa->outmeter.p->setSize(txa->dsp_outsize); + txa->outmeter.p->setSamplerate (txa->out_rate); } void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) @@ -697,27 +697,27 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->gen0.p->setSamplerate(txa->dsp_rate); PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate); PHROT::setSamplerate_phrot (txa->phrot.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->micmeter.p, txa->dsp_rate); + txa->micmeter.p->setSamplerate (txa->dsp_rate); AMSQ::setSamplerate_amsq (txa->amsq.p, txa->dsp_rate); EQP::setSamplerate_eqp (txa->eqp.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->eqmeter.p, txa->dsp_rate); + txa->eqmeter.p->setSamplerate (txa->dsp_rate); EMPHP::setSamplerate_emphp (txa->preemph.p, txa->dsp_rate); WCPAGC::setSamplerate_wcpagc (txa->leveler.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->lvlrmeter.p, txa->dsp_rate); + txa->lvlrmeter.p->setSamplerate (txa->dsp_rate); CFCOMP::setSamplerate_cfcomp (txa->cfcomp.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->cfcmeter.p, txa->dsp_rate); + txa->cfcmeter.p->setSamplerate (txa->dsp_rate); BANDPASS::setSamplerate_bandpass (txa->bp0.p, txa->dsp_rate); COMPRESSOR::setSamplerate_compressor (txa->compressor.p, txa->dsp_rate); BANDPASS::setSamplerate_bandpass (txa->bp1.p, txa->dsp_rate); OSCTRL::setSamplerate_osctrl (txa->osctrl.p, txa->dsp_rate); BANDPASS::setSamplerate_bandpass (txa->bp2.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->compmeter.p, txa->dsp_rate); + txa->compmeter.p->setSamplerate (txa->dsp_rate); WCPAGC::setSamplerate_wcpagc (txa->alc.p, txa->dsp_rate); AMMOD::setSamplerate_ammod (txa->ammod.p, txa->dsp_rate); FMMOD::setSamplerate_fmmod (txa->fmmod.p, txa->dsp_rate); txa->gen1.p->setSamplerate(txa->dsp_rate); USLEW::setSamplerate_uslew (txa->uslew.p, txa->dsp_rate); - METER::setSamplerate_meter (txa->alcmeter.p, txa->dsp_rate); + txa->alcmeter.p->setSamplerate (txa->dsp_rate); SIPHON::setSamplerate_siphon (txa->sip1.p, txa->dsp_rate); IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate); CFIR::setSamplerate_cfir (txa->cfir.p, txa->dsp_rate); @@ -726,8 +726,8 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->rsmpout.p->setInRate(txa->dsp_rate); ResCheck (*txa); // output meter - METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); - METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize); + txa->outmeter.p->setBuffers(txa->outbuff); + txa->outmeter.p->setSize (txa->dsp_outsize); } void TXA::setDSPBuffsize (TXA *txa, int dsp_size) @@ -760,24 +760,24 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) PANEL::setSize_panel (txa->panel.p, txa->dsp_size); PHROT::setBuffers_phrot (txa->phrot.p, txa->midbuff, txa->midbuff); PHROT::setSize_phrot (txa->phrot.p, txa->dsp_size); - METER::setBuffers_meter (txa->micmeter.p, txa->midbuff); - METER::setSize_meter (txa->micmeter.p, txa->dsp_size); + txa->micmeter.p->setBuffers (txa->midbuff); + txa->micmeter.p->setSize (txa->dsp_size); AMSQ::setBuffers_amsq (txa->amsq.p, txa->midbuff, txa->midbuff, txa->midbuff); AMSQ::setSize_amsq (txa->amsq.p, txa->dsp_size); EQP::setBuffers_eqp (txa->eqp.p, txa->midbuff, txa->midbuff); EQP::setSize_eqp (txa->eqp.p, txa->dsp_size); - METER::setBuffers_meter (txa->eqmeter.p, txa->midbuff); - METER::setSize_meter (txa->eqmeter.p, txa->dsp_size); + txa->eqmeter.p->setBuffers (txa->midbuff); + txa->eqmeter.p->setSize (txa->dsp_size); EMPHP::setBuffers_emphp (txa->preemph.p, txa->midbuff, txa->midbuff); EMPHP::setSize_emphp (txa->preemph.p, txa->dsp_size); WCPAGC::setBuffers_wcpagc (txa->leveler.p, txa->midbuff, txa->midbuff); WCPAGC::setSize_wcpagc (txa->leveler.p, txa->dsp_size); - METER::setBuffers_meter (txa->lvlrmeter.p, txa->midbuff); - METER::setSize_meter (txa->lvlrmeter.p, txa->dsp_size); + txa->lvlrmeter.p->setBuffers(txa->midbuff); + txa->lvlrmeter.p->setSize(txa->dsp_size); CFCOMP::setBuffers_cfcomp (txa->cfcomp.p, txa->midbuff, txa->midbuff); CFCOMP::setSize_cfcomp (txa->cfcomp.p, txa->dsp_size); - METER::setBuffers_meter (txa->cfcmeter.p, txa->midbuff); - METER::setSize_meter (txa->cfcmeter.p, txa->dsp_size); + txa->cfcmeter.p->setBuffers(txa->midbuff); + txa->cfcmeter.p->setSize(txa->dsp_size); BANDPASS::setBuffers_bandpass (txa->bp0.p, txa->midbuff, txa->midbuff); BANDPASS::setSize_bandpass (txa->bp0.p, txa->dsp_size); COMPRESSOR::setBuffers_compressor (txa->compressor.p, txa->midbuff, txa->midbuff); @@ -788,8 +788,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) OSCTRL::setSize_osctrl (txa->osctrl.p, txa->dsp_size); BANDPASS::setBuffers_bandpass (txa->bp2.p, txa->midbuff, txa->midbuff); BANDPASS::setSize_bandpass (txa->bp2.p, txa->dsp_size); - METER::setBuffers_meter (txa->compmeter.p, txa->midbuff); - METER::setSize_meter (txa->compmeter.p, txa->dsp_size); + txa->compmeter.p->setBuffers(txa->midbuff); + txa->compmeter.p->setSize(txa->dsp_size); WCPAGC::setBuffers_wcpagc (txa->alc.p, txa->midbuff, txa->midbuff); WCPAGC::setSize_wcpagc (txa->alc.p, txa->dsp_size); AMMOD::setBuffers_ammod (txa->ammod.p, txa->midbuff, txa->midbuff); @@ -800,8 +800,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) txa->gen1.p->setSize(txa->dsp_size); USLEW::setBuffers_uslew (txa->uslew.p, txa->midbuff, txa->midbuff); USLEW::setSize_uslew (txa->uslew.p, txa->dsp_size); - METER::setBuffers_meter (txa->alcmeter.p, txa->midbuff); - METER::setSize_meter (txa->alcmeter.p, txa->dsp_size); + txa->alcmeter.p->setBuffers (txa->midbuff); + txa->alcmeter.p->setSize(txa->dsp_size); SIPHON::setBuffers_siphon (txa->sip1.p, txa->midbuff); SIPHON::setSize_siphon (txa->sip1.p, txa->dsp_size); IQC::setBuffers_iqc (txa->iqc.p0, txa->midbuff, txa->midbuff); @@ -812,8 +812,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); txa->rsmpout.p->setSize(txa->dsp_size); // output meter - METER::setBuffers_meter (txa->outmeter.p, txa->outbuff); - METER::setSize_meter (txa->outmeter.p, txa->dsp_outsize); + txa->outmeter.p->setBuffers(txa->outbuff); + txa->outmeter.p->setSize(txa->dsp_outsize); } /******************************************************************************************************** diff --git a/wdsp/meter.cpp b/wdsp/meter.cpp index 7b4920f04..6b9bd2a12 100644 --- a/wdsp/meter.cpp +++ b/wdsp/meter.cpp @@ -28,152 +28,130 @@ warren@wpratt.com #include "comm.hpp" #include "meterlog10.hpp" #include "meter.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { -void METER::calc_meter (METER *a) +void METER::calc() { - a->mult_average = exp(-1.0 / (a->rate * a->tau_average)); - a->mult_peak = exp(-1.0 / (a->rate * a->tau_peak_decay)); - flush_meter(a); + mult_average = exp(-1.0 / (rate * tau_average)); + mult_peak = exp(-1.0 / (rate * tau_peak_decay)); + flush(); } -METER* METER::create_meter ( - int run, - int* prun, - int size, - float* buff, - int rate, - double tau_av, - double tau_decay, - double* result, - int enum_av, - int enum_pk, - int enum_gain, - double* pgain +METER::METER( + int _run, + int* _prun, + int _size, + float* _buff, + int _rate, + double _tau_av, + double _tau_decay, + double* _result, + int _enum_av, + int _enum_pk, + int _enum_gain, + double* _pgain ) { - METER *a = new METER; - a->run = run; - a->prun = prun; - a->size = size; - a->buff = buff; - a->rate = (double) rate; - a->tau_average = tau_av; - a->tau_peak_decay = tau_decay; - a->result = result; - a->enum_av = enum_av; - a->enum_pk = enum_pk; - a->enum_gain = enum_gain; - a->pgain = pgain; - calc_meter(a); - return a; + run = _run; + prun = _prun; + size = _size; + buff = _buff; + rate = (double) _rate; + tau_average = _tau_av; + tau_peak_decay = _tau_decay; + result = _result; + enum_av = _enum_av; + enum_pk = _enum_pk; + enum_gain = _enum_gain; + pgain = _pgain; + calc(); } -void METER::destroy_meter (METER *a) +void METER::flush() { - delete a; + avg = 0.0; + peak = 0.0; + result[enum_av] = -100.0; + result[enum_pk] = -100.0; + + if ((pgain != 0) && (enum_gain >= 0)) + result[enum_gain] = 0.0; } -void METER::flush_meter (METER *a) -{ - a->avg = 0.0; - a->peak = 0.0; - a->result[a->enum_av] = -100.0; - a->result[a->enum_pk] = -100.0; - - if ((a->pgain != 0) && (a->enum_gain >= 0)) - a->result[a->enum_gain] = 0.0; -} - -void METER::xmeter (METER *a) +void METER::execute() { int srun; - if (a->prun != 0) - srun = *(a->prun); + if (prun != 0) + srun = *(prun); else srun = 1; - if (a->run && srun) + if (run && srun) { int i; double smag; double np = 0.0; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - double xr = a->buff[2 * i + 0]; - double xi = a->buff[2 * i + 1]; + double xr = buff[2 * i + 0]; + double xi = buff[2 * i + 1]; smag = xr*xr + xi*xi; - a->avg = a->avg * a->mult_average + (1.0 - a->mult_average) * smag; - a->peak *= a->mult_peak; + avg = avg * mult_average + (1.0 - mult_average) * smag; + peak *= mult_peak; if (smag > np) np = smag; } - if (np > a->peak) - a->peak = np; + if (np > peak) + peak = np; - a->result[a->enum_av] = 10.0 * MemLog::mlog10 (a->avg + 1.0e-40); - a->result[a->enum_pk] = 10.0 * MemLog::mlog10 (a->peak + 1.0e-40); + result[enum_av] = 10.0 * MemLog::mlog10 (avg + 1.0e-40); + result[enum_pk] = 10.0 * MemLog::mlog10 (peak + 1.0e-40); - if ((a->pgain != 0) && (a->enum_gain >= 0)) - a->result[a->enum_gain] = 20.0 * MemLog::mlog10 (*a->pgain + 1.0e-40); + if ((pgain != 0) && (enum_gain >= 0)) + result[enum_gain] = 20.0 * MemLog::mlog10 (*pgain + 1.0e-40); } else { - if (a->enum_av >= 0) - a->result[a->enum_av] = -100.0; - if (a->enum_pk >= 0) - a->result[a->enum_pk] = -100.0; - if (a->enum_gain >= 0) - a->result[a->enum_gain] = 0.0; + if (enum_av >= 0) + result[enum_av] = -100.0; + if (enum_pk >= 0) + result[enum_pk] = -100.0; + if (enum_gain >= 0) + result[enum_gain] = 0.0; } } -void METER::setBuffers_meter (METER *a, float* in) +void METER::setBuffers(float* in) { - a->buff = in; + buff = in; } -void METER::setSamplerate_meter (METER *a, int rate) +void METER::setSamplerate(int _rate) { - a->rate = (double) rate; - calc_meter(a); + rate = (double) _rate; + calc(); } -void METER::setSize_meter (METER *a, int size) +void METER::setSize(int _size) { - a->size = size; - flush_meter (a); + size = _size; + flush(); } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -double METER::GetMeter (RXA& rxa, int mt) +double METER::getMeter(int mt) { - double val = rxa.meter[mt]; - return val; -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -double METER::GetMeter (TXA& txa, int mt) -{ - double val = txa.meter[mt]; - return val; + return result[mt]; } } // namespace WDSP diff --git a/wdsp/meter.hpp b/wdsp/meter.hpp index 44d2966b8..1cc8ab009 100644 --- a/wdsp/meter.hpp +++ b/wdsp/meter.hpp @@ -32,9 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; -class TXA; - class WDSP_API METER { public: @@ -55,7 +52,7 @@ public: double avg; double peak; - static METER* create_meter ( + METER( int run, int* prun, int size, @@ -69,19 +66,17 @@ public: int enum_gain, double* pgain ); - static void destroy_meter (METER *a); - static void flush_meter (METER *a); - static void xmeter (METER *a); - static void setBuffers_meter (METER *a, float* in); - static void setSamplerate_meter (METER *a, int rate); - static void setSize_meter (METER *a, int size); - // RXA Properties - static double GetMeter (RXA& rxa, int mt); - // TXA Properties - static double GetMeter (TXA& txa, int mt); + ~METER() = default; + + void flush(); + void execute(); + void setBuffers(float* in); + void setSamplerate(int rate); + void setSize(int size); + double getMeter(int mt); private: - static void calc_meter (METER *a); + void calc(); }; From cc8c6d800424b7236cd86310dfd6ac6d9a1c4266 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 20:29:55 +0200 Subject: [PATCH 10/46] WDSP: notched bandpass filter: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 2 +- wdsp/RXA.cpp | 182 ++++++- wdsp/RXA.hpp | 13 + wdsp/bpsnba.cpp | 16 +- wdsp/nbp.cpp | 656 ++++++++++-------------- wdsp/nbp.hpp | 76 ++- 6 files changed, 485 insertions(+), 460 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index de00c377d..29aa9af18 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -447,7 +447,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; WDSP::RXA::SetPassband(*m_rxa, fLow, fHigh); - WDSP::NBP::NBPSetWindow(*m_rxa, m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow); + WDSP::RXA::NBPSetWindow(*m_rxa, m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow); if (settings.m_demod == WDSPRxProfile::DemodSSB) { diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 944061299..c9593ecbd 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -156,12 +156,12 @@ RXA* RXA::create_rxa ( // Notched bandpass section // notch database - rxa->ndb = NOTCHDB::create_notchdb ( + rxa->ndb = new NOTCHDB ( 0, // master run for all nbp's 1024); // max number of notches // notched bandpass - rxa->nbp0 = NBP::create_nbp ( + rxa->nbp0 = new NBP ( 1, // run, always runs 0, // run the notches 0, // position @@ -585,8 +585,8 @@ void RXA::destroy_rxa (RXA *rxa) delete (rxa->smeter); SENDER::destroy_sender (rxa->sender); BPSNBA::destroy_bpsnba (rxa->bpsnba); - NBP::destroy_nbp (rxa->nbp0); - NOTCHDB::destroy_notchdb (rxa->ndb); + delete (rxa->nbp0); + delete (rxa->ndb); delete (rxa->adcmeter); delete (rxa->rsmpin); delete (rxa->shift); @@ -608,7 +608,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->shift->flush(); rxa->rsmpin->flush(); rxa->adcmeter->flush(); - NBP::flush_nbp (rxa->nbp0); + rxa->nbp0->flush(); BPSNBA::flush_bpsnba (rxa->bpsnba); SENDER::flush_sender (rxa->sender); rxa->smeter->flush(); @@ -641,7 +641,7 @@ void RXA::xrxa (RXA *rxa) rxa->rsmpin->execute(); rxa->adcmeter->execute(); BPSNBA::xbpsnbain (rxa->bpsnba, 0); - NBP::xnbp (rxa->nbp0, 0); + rxa->nbp0->execute(0); rxa->smeter->execute(); SENDER::xsender (rxa->sender); AMSQ::xamsqcap (rxa->amsq); @@ -753,7 +753,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->rsmpin->setOutRate(rxa->dsp_rate); // dsp_rate blocks rxa->adcmeter->setSamplerate(rxa->dsp_rate); - NBP::setSamplerate_nbp (rxa->nbp0, rxa->dsp_rate); + rxa->nbp0->setSamplerate(rxa->dsp_rate); BPSNBA::setSamplerate_bpsnba (rxa->bpsnba, rxa->dsp_rate); rxa->smeter->setSamplerate(rxa->dsp_rate); SENDER::setSamplerate_sender (rxa->sender, rxa->dsp_rate); @@ -817,8 +817,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) // dsp_size blocks rxa->adcmeter->setBuffers(rxa->midbuff); rxa->adcmeter->setSize(rxa->dsp_size); - NBP::setBuffers_nbp (rxa->nbp0, rxa->midbuff, rxa->midbuff); - NBP::setSize_nbp (rxa->nbp0, rxa->dsp_size); + rxa->nbp0->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->nbp0->setSize(rxa->dsp_size); BPSNBA::setBuffers_bpsnba (rxa->bpsnba, rxa->midbuff, rxa->midbuff); BPSNBA::setSize_bpsnba (rxa->bpsnba, rxa->dsp_size); rxa->smeter->setBuffers(rxa->midbuff); @@ -1066,7 +1066,163 @@ void RXA::bpsnbaSet (RXA& rxa) a->run = 0; break; } - FIRCORE::setUpdate_fircore (a->bpsnba->p); + FIRCORE::setUpdate_fircore (a->bpsnba->fircore); +} + +void RXA::UpdateNBPFiltersLightWeight (RXA& rxa) +{ // called when setting tune freq or shift freq + rxa.nbp0->calc_lightweight(); + rxa.bpsnba->bpsnba->calc_lightweight(); +} + +void RXA::UpdateNBPFilters(RXA& rxa) +{ + NBP *a = rxa.nbp0; + BPSNBA *b = rxa.bpsnba; + if (a->fnfrun) + { + a->calc_impulse(); + FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + delete[] (a->impulse); + } + if (b->bpsnba->fnfrun) + { + BPSNBA::recalc_bpsnba_filter (b, 1); + } +} + +int RXA::NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) +{ + NOTCHDB *b = rxa.ndb; + int rval = b->addNotch(notch, fcenter, fwidth, active); + + if (rval == 0) { + RXA::UpdateNBPFilters (rxa); + } + + return rval; +} + +int RXA::NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active) +{ + NOTCHDB *a = rxa.ndb; + int rval = a->getNotch(notch, fcenter, fwidth, active); + return rval; +} + +int RXA::NBPDeleteNotch (RXA& rxa, int notch) +{ + NOTCHDB *a = rxa.ndb; + int rval = a->deleteNotch(notch); + + if (rval == 0) { + RXA::UpdateNBPFilters (rxa); + } + + return rval; +} + +int RXA::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) +{ + NOTCHDB *a = rxa.ndb; + int rval = a->editNotch(notch, fcenter, fwidth, active); + + if (rval == 0) { + RXA::UpdateNBPFilters (rxa); + } + + return rval; +} + +void RXA::NBPGetNumNotches (RXA& rxa, int* nnotches) +{ + NOTCHDB *a = rxa.ndb; + a->getNumNotches(nnotches); +} + +void RXA::NBPSetTuneFrequency (RXA& rxa, double tunefreq) +{ + NOTCHDB *a; + a = rxa.ndb; + + if (tunefreq != a->tunefreq) + { + a->tunefreq = tunefreq; + RXA::UpdateNBPFiltersLightWeight (rxa); + } +} + +void RXA::NBPSetShiftFrequency (RXA& rxa, double shift) +{ + NOTCHDB *a; + a = rxa.ndb; + if (shift != a->shift) + { + a->shift = shift; + RXA::UpdateNBPFiltersLightWeight (rxa); + } +} + +void RXA::NBPSetNotchesRun (RXA& rxa, int run) +{ + NOTCHDB *a = rxa.ndb; + NBP *b = rxa.nbp0; + + if ( run != a->master_run) + { + a->master_run = run; // update variables + b->fnfrun = a->master_run; + RXA::bpsnbaCheck (rxa, rxa.mode, run); + b->calc_impulse(); // recalc nbp impulse response + FIRCORE::setImpulse_fircore (b->fircore, b->impulse, 0); // calculate new filter masks + delete[] (b->impulse); + RXA::bpsnbaSet (rxa); + FIRCORE::setUpdate_fircore (b->fircore); // apply new filter masks + } +} + +void RXA::NBPSetWindow (RXA& rxa, int wintype) +{ + NBP *a; + BPSNBA *b; + a = rxa.nbp0; + b = rxa.bpsnba; + + if ((a->wintype != wintype)) + { + a->wintype = wintype; + a->calc_impulse(); + FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + delete[] (a->impulse); + } + + if ((b->wintype != wintype)) + { + b->wintype = wintype; + BPSNBA::recalc_bpsnba_filter (b, 1); + } +} + +void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) +{ + NBP *a; + BPSNBA *b; + a = rxa.nbp0; + b = rxa.bpsnba; + + if ((a->autoincr != autoincr)) + { + a->autoincr = autoincr; + a->calc_impulse(); + FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + delete[] (a->impulse); + } + + if ((b->autoincr != autoincr)) + { + b->autoincr = autoincr; + BPSNBA::recalc_bpsnba_filter (b, 1); + } } /******************************************************************************************************** @@ -1079,13 +1235,13 @@ void RXA::SetPassband (RXA& rxa, float f_low, float f_high) { BANDPASS::SetBandpassFreqs (rxa, f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) SNBA::SetSNBAOutputBandwidth (rxa, f_low, f_high); // Spectral noise blanker (SNB) - NBP::NBPSetFreqs (rxa, f_low, f_high); // Notched bandpass + rxa.nbp0->SetFreqs (f_low, f_high); // Notched bandpass } void RXA::SetNC (RXA& rxa, int nc) { int oldstate = rxa.state; - NBP::NBPSetNC (rxa, nc); + rxa.nbp0->SetNC (nc); BPSNBA::BPSNBASetNC (rxa, nc); BANDPASS::SetBandpassNC (rxa, nc); EQP::SetEQNC (rxa, nc); @@ -1097,7 +1253,7 @@ void RXA::SetNC (RXA& rxa, int nc) void RXA::SetMP (RXA& rxa, int mp) { - NBP::NBPSetMP (rxa, mp); + rxa.nbp0->SetMP (mp); BPSNBA::BPSNBASetMP (rxa, mp); BANDPASS::SetBandpassMP (rxa, mp); EQP::SetEQMP (rxa, mp); diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 1209d8f7b..271e2c337 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -154,6 +154,19 @@ public: static void bp1Set (RXA& rxa); static void bpsnbaCheck (RXA& rxa, int mode, int notch_run); static void bpsnbaSet (RXA& rxa); + // NOTCHDB, NBP, SNBA + static void UpdateNBPFiltersLightWeight (RXA& rxa); + static void UpdateNBPFilters(RXA& rxa); + static int NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); + static int NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active); + static int NBPDeleteNotch (RXA& rxa, int notch); + static int NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); + static void NBPGetNumNotches (RXA& rxa, int* nnotches); + static void NBPSetTuneFrequency (RXA& rxa, double tunefreq); + static void NBPSetShiftFrequency (RXA& rxa, double shift); + static void NBPSetNotchesRun (RXA& rxa, int run); + static void NBPSetWindow (RXA& rxa, int wintype); + static void NBPSetAutoIncrease (RXA& rxa, int autoincr); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index f441b9e41..180887676 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -54,7 +54,7 @@ namespace WDSP { void BPSNBA::calc_bpsnba (BPSNBA *a) { a->buff = new float[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex)); - a->bpsnba = NBP::create_nbp ( + a->bpsnba = new NBP ( 1, // run, always runs (use bpsnba 'run') a->run_notches, // run the notches 0, // position variable for nbp (not for bpsnba), always 0 @@ -119,7 +119,7 @@ BPSNBA* BPSNBA::create_bpsnba ( void BPSNBA::decalc_bpsnba (BPSNBA *a) { - NBP::destroy_nbp (a->bpsnba); + delete (a->bpsnba); delete[] (a->buff); } @@ -132,7 +132,7 @@ void BPSNBA::destroy_bpsnba (BPSNBA *a) void BPSNBA::flush_bpsnba (BPSNBA *a) { std::fill(a->buff, a->buff + a->size * 2, 0); - NBP::flush_nbp (a->bpsnba); + a->bpsnba->flush(); } void BPSNBA::setBuffers_bpsnba (BPSNBA *a, float* in, float* out) @@ -166,7 +166,7 @@ void BPSNBA::xbpsnbain (BPSNBA *a, int position) void BPSNBA::xbpsnbaout (BPSNBA *a, int position) { if (a->run && a->position == position) - NBP::xnbp (a->bpsnba, 0); + a->bpsnba->execute(0); } void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update) @@ -180,8 +180,8 @@ void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update) b->wintype = a->wintype; b->gain = a->gain; b->autoincr = a->autoincr; - NBP::calc_nbp_impulse (b); - FIRCORE::setImpulse_fircore (b->p, b->impulse, update); + b->calc_impulse(); + FIRCORE::setImpulse_fircore (b->fircore, b->impulse, update); delete[] (b->impulse); } @@ -199,7 +199,7 @@ void BPSNBA::BPSNBASetNC (RXA& rxa, int nc) { a->nc = nc; a->bpsnba->nc = a->nc; - NBP::setNc_nbp (a->bpsnba); + a->bpsnba->setNc(); } } @@ -211,7 +211,7 @@ void BPSNBA::BPSNBASetMP (RXA& rxa, int mp) { a->mp = mp; a->bpsnba->mp = a->mp; - NBP::setMp_nbp (a->bpsnba); + a->bpsnba->setMp(); } } diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index 4c866e08e..bf75535ce 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -30,7 +30,6 @@ warren@wpratt.com #include "fircore.hpp" #include "bpsnba.hpp" #include "nbp.hpp" -#include "RXA.hpp" namespace WDSP { @@ -40,27 +39,122 @@ namespace WDSP { * * ********************************************************************************************************/ - NOTCHDB* NOTCHDB::create_notchdb (int master_run, int maxnotches) +NOTCHDB::NOTCHDB(int _master_run, int _maxnotches) { - NOTCHDB *a = new NOTCHDB; - a->master_run = master_run; - a->maxnotches = maxnotches; - a->nn = 0; - a->fcenter = new double[a->maxnotches]; // (float *) malloc0 (a->maxnotches * sizeof (float)); - a->fwidth = new double[a->maxnotches]; // (float *) malloc0 (a->maxnotches * sizeof (float)); - a->nlow = new double[a->maxnotches]; // (float *) malloc0 (a->maxnotches * sizeof (float)); - a->nhigh = new double[a->maxnotches]; // (float *) malloc0 (a->maxnotches * sizeof (float)); - a->active = new int[a->maxnotches]; // (int *) malloc0 (a->maxnotches * sizeof (int )); - return a; + master_run = _master_run; + maxnotches = _maxnotches; + nn = 0; + fcenter = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); + fwidth = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); + nlow = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); + nhigh = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); + active = new int[maxnotches]; // (int *) malloc0 (maxnotches * sizeof (int )); } -void NOTCHDB::destroy_notchdb (NOTCHDB *b) +NOTCHDB::~NOTCHDB() { - delete[] (b->active); - delete[] (b->nhigh); - delete[] (b->nlow); - delete[] (b->fwidth); - delete[] (b->fcenter); + delete[] (active); + delete[] (nhigh); + delete[] (nlow); + delete[] (fwidth); + delete[] (fcenter); +} + +int NOTCHDB::addNotch(int notch, double _fcenter, double _fwidth, int _active) +{ + int i, j; + int rval; + + if (notch <= nn && nn < maxnotches) + { + nn++; + + for (i = nn - 2, j = nn - 1; i >= notch; i--, j--) + { + fcenter[j] = fcenter[i]; + fwidth[j] = fwidth[i]; + nlow[j] = nlow[i]; + nhigh[j] = nhigh[i]; + active[j] = active[i]; + } + fcenter[notch] = _fcenter; + fwidth[notch] = _fwidth; + nlow[notch] = _fcenter - 0.5 * _fwidth; + nhigh[notch] = _fcenter + 0.5 * _fwidth; + active[notch] = _active; + rval = 0; + } + else + rval = -1; + return rval; +} + +int NOTCHDB::getNotch(int _notch, double* _fcenter, double* _fwidth, int* _active) +{ + int rval; + + if (_notch < nn) + { + *_fcenter = fcenter[_notch]; + *_fwidth = fwidth[_notch]; + *_active = active[_notch]; + rval = 0; + } + else + { + *_fcenter = -1.0; + *_fwidth = 0.0; + *_active = -1; + rval = -1; + } + + return rval; +} + +int NOTCHDB::deleteNotch(int _notch) +{ + int i, j; + int rval; + + if (_notch < nn) + { + nn--; + for (i = _notch, j = _notch + 1; i < nn; i++, j++) + { + fcenter[i] = fcenter[j]; + fwidth[i] = fwidth[j]; + nlow[i] = nlow[j]; + nhigh[i] = nhigh[j]; + active[i] = active[j]; + } + rval = 0; + } + else + rval = -1; + return rval; +} + +int NOTCHDB::editNotch(int _notch, double _fcenter, double _fwidth, int _active) +{ + int rval; + + if (_notch < nn) + { + fcenter[_notch] = _fcenter; + fwidth[_notch] = _fwidth; + nlow[_notch] = _fcenter - 0.5 * _fwidth; + nhigh[_notch] = _fcenter + 0.5 * _fwidth; + active[_notch] = _active; + rval = 0; + } + else + rval = -1; + return rval; +} + +void NOTCHDB::getNumNotches(int* _nnotches) +{ + *_nnotches = nn; } /******************************************************************************************************** @@ -87,16 +181,16 @@ float* NBP::fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double r return impulse; } -double NBP::min_notch_width (NBP *a) +double NBP::min_notch_width() { double min_width; - switch (a->wintype) + switch (wintype) { case 0: - min_width = 1600.0 / (a->nc / 256) * (a->rate / 48000); + min_width = 1600.0 / (nc / 256) * (rate / 48000); break; case 1: - min_width = 2200.0 / (a->nc / 256) * (a->rate / 48000); + min_width = 2200.0 / (nc / 256) * (rate / 48000); break; } return min_width; @@ -200,485 +294,255 @@ int NBP::make_nbp ( return nbp; } -void NBP::calc_nbp_lightweight (NBP *a) +void NBP::calc_lightweight() { // calculate and set new impulse response; used when changing tune freq or shift freq int i; double fl, fh; double offset; - NOTCHDB *b = a->ptraddr; - if (a->fnfrun) + NOTCHDB *b = notchdb; + if (fnfrun) { offset = b->tunefreq + b->shift; - fl = a->flow + offset; - fh = a->fhigh + offset; - a->numpb = make_nbp ( + fl = flow + offset; + fh = fhigh + offset; + numpb = make_nbp ( b->nn, b->active, b->fcenter, b->fwidth, b->nlow, b->nhigh, - min_notch_width (a), - a->autoincr, + min_notch_width(), + autoincr, fl, fh, - a->bplow, - a->bphigh, - &a->havnotch + bplow, + bphigh, + &havnotch ); // when tuning, no need to recalc filter if there were not and are not any notches in passband - if (a->hadnotch || a->havnotch) + if (hadnotch || havnotch) { - for (i = 0; i < a->numpb; i++) + for (i = 0; i < numpb; i++) { - a->bplow[i] -= offset; - a->bphigh[i] -= offset; + bplow[i] -= offset; + bphigh[i] -= offset; } - a->impulse = fir_mbandpass (a->nc, a->numpb, a->bplow, a->bphigh, - a->rate, a->gain / (float)(2 * a->size), a->wintype); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - // print_impulse ("nbp.txt", a->size + 1, impulse, 1, 0); - delete[](a->impulse); + impulse = fir_mbandpass (nc, numpb, bplow, bphigh, + rate, gain / (float)(2 * size), wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); + delete[](impulse); } - a->hadnotch = a->havnotch; + hadnotch = havnotch; } else - a->hadnotch = 1; + hadnotch = 1; } -void NBP::calc_nbp_impulse (NBP *a) +void NBP::calc_impulse () { // calculates impulse response; for create_fircore() and parameter changes int i; float fl, fh; double offset; - NOTCHDB *b = a->ptraddr; - if (a->fnfrun) + NOTCHDB *b = notchdb; + + if (fnfrun) { offset = b->tunefreq + b->shift; - fl = a->flow + offset; - fh = a->fhigh + offset; - a->numpb = make_nbp ( + fl = flow + offset; + fh = fhigh + offset; + numpb = make_nbp ( b->nn, b->active, b->fcenter, b->fwidth, b->nlow, b->nhigh, - min_notch_width (a), - a->autoincr, + min_notch_width(), + autoincr, fl, fh, - a->bplow, - a->bphigh, - &a->havnotch + bplow, + bphigh, + &havnotch ); - for (i = 0; i < a->numpb; i++) + for (i = 0; i < numpb; i++) { - a->bplow[i] -= offset; - a->bphigh[i] -= offset; + bplow[i] -= offset; + bphigh[i] -= offset; } - a->impulse = fir_mbandpass ( - a->nc, - a->numpb, - a->bplow, - a->bphigh, - a->rate, - a->gain / (float)(2 * a->size), - a->wintype + impulse = fir_mbandpass ( + nc, + numpb, + bplow, + bphigh, + rate, + gain / (float)(2 * size), + wintype ); } else { - a->impulse = FIR::fir_bandpass( - a->nc, - a->flow, - a->fhigh, - a->rate, - a->wintype, + impulse = FIR::fir_bandpass( + nc, + flow, + fhigh, + rate, + wintype, 1, - a->gain / (float)(2 * a->size) + gain / (float)(2 * size) ); } } -NBP* NBP::create_nbp( - int run, - int fnfrun, - int position, - int size, - int nc, - int mp, - float* in, - float* out, - double flow, - double fhigh, - int rate, - int wintype, - double gain, - int autoincr, - int maxpb, - NOTCHDB* ptraddr -) +NBP::NBP( + int _run, + int _fnfrun, + int _position, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + double _flow, + double _fhigh, + int _rate, + int _wintype, + double _gain, + int _autoincr, + int _maxpb, + NOTCHDB* _notchdb +) : + run(_run), + fnfrun(_fnfrun), + position(_position), + size(_size), + nc(_nc), + mp(_mp), + rate((double) _rate), + wintype(_wintype), + gain(_gain), + in(_in), + out(_out), + autoincr(_autoincr), + flow(_flow), + fhigh(_fhigh), + maxpb(_maxpb), + notchdb(_notchdb) { - NBP *a = new NBP; - a->run = run; - a->fnfrun = fnfrun; - a->position = position; - a->size = size; - a->nc = nc; - a->mp = mp; - a->rate = (double) rate; - a->wintype = wintype; - a->gain = gain; - a->in = in; - a->out = out; - a->autoincr = autoincr; - a->flow = flow; - a->fhigh = fhigh; - a->maxpb = maxpb; - a->ptraddr = ptraddr; - a->bplow = new double[a->maxpb]; // (float *) malloc0 (a->maxpb * sizeof (float)); - a->bphigh = new double[a->maxpb]; // (float *) malloc0 (a->maxpb * sizeof (float)); - calc_nbp_impulse (a); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, a->impulse); - // print_impulse ("nbp.txt", a->size + 1, impulse, 1, 0); - delete[](a->impulse); - return a; + bplow = new double[maxpb]; // (float *) malloc0 (maxpb * sizeof (float)); + bphigh = new double[maxpb]; // (float *) malloc0 (maxpb * sizeof (float)); + calc_impulse (); + fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); + // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); + delete[](impulse); } -void NBP::destroy_nbp (NBP *a) +NBP::~NBP() { - FIRCORE::destroy_fircore (a->p); - delete[] (a->bphigh); - delete[] (a->bplow); - delete (a); + FIRCORE::destroy_fircore (fircore); + delete[] (bphigh); + delete[] (bplow); } -void NBP::flush_nbp (NBP *a) +void NBP::flush() { - FIRCORE::flush_fircore (a->p); + FIRCORE::flush_fircore (fircore); } -void NBP::xnbp (NBP *a, int pos) +void NBP::execute (int pos) { - if (a->run && pos == a->position) - FIRCORE::xfircore (a->p); - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + if (run && pos == position) + FIRCORE::xfircore (fircore); + else if (in != out) + std::copy( in, in + size * 2, out); } -void NBP::setBuffers_nbp (NBP *a, float* in, float* out) +void NBP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; - FIRCORE::setBuffers_fircore (a->p, a->in, a->out); + in = _in; + out = _out; + FIRCORE::setBuffers_fircore (fircore, in, out); } -void NBP::setSamplerate_nbp (NBP *a, int rate) +void NBP::setSamplerate(int _rate) { - a->rate = rate; - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); + rate = _rate; + calc_impulse (); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); } -void NBP::setSize_nbp (NBP *a, int size) +void NBP::setSize(int _size) { // NOTE: 'size' must be <= 'nc' - a->size = size; - FIRCORE::setSize_fircore (a->p, a->size); - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); + size = _size; + FIRCORE::setSize_fircore (fircore, size); + calc_impulse (); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); } -void NBP::setNc_nbp (NBP *a) +void NBP::setNc() { - calc_nbp_impulse (a); - FIRCORE::setNc_fircore (a->p, a->nc, a->impulse); - delete[] (a->impulse); + calc_impulse(); + FIRCORE::setNc_fircore (fircore, nc, impulse); + delete[] (impulse); } -void NBP::setMp_nbp (NBP *a) +void NBP::setMp() { - FIRCORE::setMp_fircore (a->p, a->mp); + FIRCORE::setMp_fircore (fircore, mp); } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -// DATABASE PROPERTIES - -void NBP::UpdateNBPFiltersLightWeight (RXA& rxa) -{ // called when setting tune freq or shift freq - calc_nbp_lightweight (rxa.nbp0); - calc_nbp_lightweight (rxa.bpsnba->bpsnba); -} - -void NBP::UpdateNBPFilters(RXA& rxa) -{ - NBP *a = rxa.nbp0; - BPSNBA *b = rxa.bpsnba; - if (a->fnfrun) - { - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); - } - if (b->bpsnba->fnfrun) - { - BPSNBA::recalc_bpsnba_filter (b, 1); - } -} - -int NBP::NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) -{ - NOTCHDB *b; - int i, j; - int rval; - b = rxa.ndb; - if (notch <= b->nn && b->nn < b->maxnotches) - { - b->nn++; - for (i = b->nn - 2, j = b->nn - 1; i >= notch; i--, j--) - { - b->fcenter[j] = b->fcenter[i]; - b->fwidth[j] = b->fwidth[i]; - b->nlow[j] = b->nlow[i]; - b->nhigh[j] = b->nhigh[i]; - b->active[j] = b->active[i]; - } - b->fcenter[notch] = fcenter; - b->fwidth[notch] = fwidth; - b->nlow[notch] = fcenter - 0.5 * fwidth; - b->nhigh[notch] = fcenter + 0.5 * fwidth; - b->active[notch] = active; - UpdateNBPFilters (rxa); - rval = 0; - } - else - rval = -1; - return rval; -} - -int NBP::NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active) -{ - NOTCHDB *a; - int rval; - a = rxa.ndb; - - if (notch < a->nn) - { - *fcenter = a->fcenter[notch]; - *fwidth = a->fwidth[notch]; - *active = a->active[notch]; - rval = 0; - } - else - { - *fcenter = -1.0; - *fwidth = 0.0; - *active = -1; - rval = -1; - } - - return rval; -} - -int NBP::NBPDeleteNotch (RXA& rxa, int notch) -{ - int i, j; - int rval; - NOTCHDB *a; - a = rxa.ndb; - if (notch < a->nn) - { - a->nn--; - for (i = notch, j = notch + 1; i < a->nn; i++, j++) - { - a->fcenter[i] = a->fcenter[j]; - a->fwidth[i] = a->fwidth[j]; - a->nlow[i] = a->nlow[j]; - a->nhigh[i] = a->nhigh[j]; - a->active[i] = a->active[j]; - } - UpdateNBPFilters (rxa); - rval = 0; - } - else - rval = -1; - return rval; -} - -int NBP::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) -{ - NOTCHDB *a; - int rval; - a = rxa.ndb; - if (notch < a->nn) - { - a->fcenter[notch] = fcenter; - a->fwidth[notch] = fwidth; - a->nlow[notch] = fcenter - 0.5 * fwidth; - a->nhigh[notch] = fcenter + 0.5 * fwidth; - a->active[notch] = active; - UpdateNBPFilters (rxa); - rval = 0; - } - else - rval = -1; - return rval; -} - -void NBP::NBPGetNumNotches (RXA& rxa, int* nnotches) -{ - NOTCHDB *a; - a = rxa.ndb; - *nnotches = a->nn; -} - -void NBP::NBPSetTuneFrequency (RXA& rxa, double tunefreq) -{ - NOTCHDB *a; - a = rxa.ndb; - - if (tunefreq != a->tunefreq) - { - a->tunefreq = tunefreq; - UpdateNBPFiltersLightWeight (rxa); - } -} - -void NBP::NBPSetShiftFrequency (RXA& rxa, double shift) -{ - NOTCHDB *a; - a = rxa.ndb; - if (shift != a->shift) - { - a->shift = shift; - UpdateNBPFiltersLightWeight (rxa); - } -} - -void NBP::NBPSetNotchesRun (RXA& rxa, int run) -{ - NOTCHDB *a = rxa.ndb; - NBP *b = rxa.nbp0; - - if ( run != a->master_run) - { - a->master_run = run; // update variables - b->fnfrun = a->master_run; - RXA::bpsnbaCheck (rxa, rxa.mode, run); - calc_nbp_impulse (b); // recalc nbp impulse response - FIRCORE::setImpulse_fircore (b->p, b->impulse, 0); // calculate new filter masks - delete[] (b->impulse); - RXA::bpsnbaSet (rxa); - FIRCORE::setUpdate_fircore (b->p); // apply new filter masks - } -} - // FILTER PROPERTIES -void NBP::NBPSetRun (RXA& rxa, int run) +void NBP::SetRun(int _run) { - NBP *a; - a = rxa.nbp0; - a->run = run; + run = _run; } -void NBP::NBPSetFreqs (RXA& rxa, double flow, double fhigh) +void NBP::SetFreqs(double _flow, double _fhigh) { - NBP *a; - a = rxa.nbp0; - - if ((flow != a->flow) || (fhigh != a->fhigh)) + if ((flow != _flow) || (fhigh != _fhigh)) { - a->flow = flow; - a->fhigh = fhigh; - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); + flow = _flow; + fhigh = _fhigh; + calc_impulse(); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); } } -void NBP::NBPSetWindow (RXA& rxa, int wintype) -{ - NBP *a; - BPSNBA *b; - a = rxa.nbp0; - b = rxa.bpsnba; - - if ((a->wintype != wintype)) - { - a->wintype = wintype; - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); - } - - if ((b->wintype != wintype)) - { - b->wintype = wintype; - BPSNBA::recalc_bpsnba_filter (b, 1); - } -} - -void NBP::NBPSetNC (RXA& rxa, int nc) +void NBP::SetNC(int _nc) { // NOTE: 'nc' must be >= 'size' - NBP *a; - a = rxa.nbp0; - - if (a->nc != nc) + if (nc != _nc) { - a->nc = nc; - setNc_nbp (a); + nc = _nc; + setNc(); } } -void NBP::NBPSetMP (RXA& rxa, int mp) +void NBP::SetMP(int _mp) { - NBP *a; - a = rxa.nbp0; - - if (a->mp != mp) + if (mp != _mp) { - a->mp = mp; - setMp_nbp (a); + mp = _mp; + setMp(); } } -void NBP::NBPGetMinNotchWidth (RXA& rxa, double* minwidth) +void NBP::GetMinNotchWidth(double* minwidth) { - NBP *a; - a = rxa.nbp0; - *minwidth = min_notch_width (a); -} - -void NBP::NBPSetAutoIncrease (RXA& rxa, int autoincr) -{ - NBP *a; - BPSNBA *b; - a = rxa.nbp0; - b = rxa.bpsnba; - - if ((a->autoincr != autoincr)) - { - a->autoincr = autoincr; - calc_nbp_impulse (a); - FIRCORE::setImpulse_fircore (a->p, a->impulse, 1); - delete[] (a->impulse); - } - - if ((b->autoincr != autoincr)) - { - b->autoincr = autoincr; - BPSNBA::recalc_bpsnba_filter (b, 1); - } + *minwidth = min_notch_width(); } } // namespace WDSP diff --git a/wdsp/nbp.hpp b/wdsp/nbp.hpp index 470fce888..23d0a4b5b 100644 --- a/wdsp/nbp.hpp +++ b/wdsp/nbp.hpp @@ -33,7 +33,6 @@ warren@wpratt.com namespace WDSP { class FIRCORE; -class RXA; class WDSP_API NOTCHDB { @@ -49,8 +48,14 @@ public: double* nhigh; int maxnotches; - static NOTCHDB* create_notchdb (int master_run, int maxnotches); - static void destroy_notchdb (NOTCHDB *b); + NOTCHDB(int master_run, int maxnotches); + ~NOTCHDB(); + + int addNotch (int notch, double fcenter, double fwidth, int active); + int getNotch (int notch, double* fcenter, double* fwidth, int* active); + int deleteNotch (int notch); + int editNotch (int notch, double fcenter, double fwidth, int active); + void getNumNotches (int* nnotches); }; @@ -63,25 +68,25 @@ public: int size; // buffer size int nc; // number of filter coefficients int mp; // minimum phase flag - float* in; // input buffer - float* out; // output buffer - double flow; // low bandpass cutoff freq - double fhigh; // high bandpass cutoff freq - float* impulse; // filter impulse response double rate; // sample rate int wintype; // filter window type double gain; // filter gain + float* in; // input buffer + float* out; // output buffer int autoincr; // auto-increment notch width + double flow; // low bandpass cutoff freq + double fhigh; // high bandpass cutoff freq + float* impulse; // filter impulse response int maxpb; // maximum number of passbands - NOTCHDB* ptraddr; // ptr to addr of notch-database data structure + NOTCHDB* notchdb; // ptr to addr of notch-database data structure double* bplow; // array of passband lows double* bphigh; // array of passband highs int numpb; // number of passbands - FIRCORE *p; + FIRCORE *fircore; int havnotch; int hadnotch; - static NBP* create_nbp( + NBP( int run, int fnfrun, int position, @@ -97,41 +102,29 @@ public: double gain, int autoincr, int maxpb, - NOTCHDB* ptraddr + NOTCHDB* notchdb ); - static void destroy_nbp (NBP *a); - static void flush_nbp (NBP *a); - static void xnbp (NBP *a, int pos); - static void setBuffers_nbp (NBP *a, float* in, float* out); - static void setSamplerate_nbp (NBP *a, int rate); - static void setSize_nbp (NBP *a, int size); - static void calc_nbp_impulse (NBP *a); - static void setNc_nbp (NBP *a); - static void setMp_nbp (NBP *a); - // RXA Properties - static void UpdateNBPFiltersLightWeight (RXA& rxa); - static void UpdateNBPFilters(RXA& rxa); - static int NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); - static int NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active); - static int NBPDeleteNotch (RXA& rxa, int notch); - static int NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); - static void NBPGetNumNotches (RXA& rxa, int* nnotches); - static void NBPSetTuneFrequency (RXA& rxa, double tunefreq); - static void NBPSetShiftFrequency (RXA& rxa, double shift); - static void NBPSetNotchesRun (RXA& rxa, int run); - static void NBPSetRun (RXA& rxa, int run); - static void NBPSetFreqs (RXA& rxa, double flow, double fhigh); - static void NBPSetWindow (RXA& rxa, int wintype); + ~NBP(); - static void NBPSetNC (RXA& rxa, int nc); - static void NBPSetMP (RXA& rxa, int mp); - - static void NBPGetMinNotchWidth (RXA& rxa, double* minwidth); - static void NBPSetAutoIncrease (RXA& rxa, int autoincr); + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void calc_impulse(); + void setNc(); + void setMp(); + // public Properties + void SetRun(int run); + void SetFreqs(double flow, double fhigh); + void SetNC(int nc); + void SetMP(int mp); + void GetMinNotchWidth(double* minwidth); + void calc_lightweight(); private: static float* fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double rate, double scale, int wintype); - static double min_notch_width (NBP *a); + double min_notch_width (); static int make_nbp ( int nn, int* active, @@ -147,7 +140,6 @@ private: double* bphigh, int* havnotch ); - static void calc_nbp_lightweight (NBP *a); }; } // namespace WDSP From 0bd4bbe0d4d704fcfa43316d9f92387debf442f6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 23:11:29 +0200 Subject: [PATCH 11/46] WDSP: BPSNBA: replaced static methods --- wdsp/RXA.cpp | 32 ++++---- wdsp/bpsnba.cpp | 211 +++++++++++++++++++++++------------------------- wdsp/bpsnba.hpp | 41 +++++----- 3 files changed, 139 insertions(+), 145 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index c9593ecbd..a565c8066 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -180,7 +180,7 @@ RXA* RXA::create_rxa ( rxa->ndb); // addr of database pointer // bandpass for snba - rxa->bpsnba = BPSNBA::create_bpsnba ( + rxa->bpsnba = new BPSNBA ( 0, // bpsnba run flag 0, // run the notches 0, // position @@ -584,7 +584,7 @@ void RXA::destroy_rxa (RXA *rxa) AMSQ::destroy_amsq (rxa->amsq); delete (rxa->smeter); SENDER::destroy_sender (rxa->sender); - BPSNBA::destroy_bpsnba (rxa->bpsnba); + delete (rxa->bpsnba); delete (rxa->nbp0); delete (rxa->ndb); delete (rxa->adcmeter); @@ -609,7 +609,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->rsmpin->flush(); rxa->adcmeter->flush(); rxa->nbp0->flush(); - BPSNBA::flush_bpsnba (rxa->bpsnba); + rxa->bpsnba->flush(); SENDER::flush_sender (rxa->sender); rxa->smeter->flush(); AMSQ::flush_amsq (rxa->amsq); @@ -640,17 +640,17 @@ void RXA::xrxa (RXA *rxa) rxa->shift->execute(); rxa->rsmpin->execute(); rxa->adcmeter->execute(); - BPSNBA::xbpsnbain (rxa->bpsnba, 0); + rxa->bpsnba->exec_in(0); rxa->nbp0->execute(0); rxa->smeter->execute(); SENDER::xsender (rxa->sender); AMSQ::xamsqcap (rxa->amsq); - BPSNBA::xbpsnbaout (rxa->bpsnba, 0); + rxa->bpsnba->exec_out(0); AMD::xamd (rxa->amd); FMD::xfmd (rxa->fmd); FMSQ::xfmsq (rxa->fmsq); - BPSNBA::xbpsnbain (rxa->bpsnba, 1); - BPSNBA::xbpsnbaout (rxa->bpsnba, 1); + rxa->bpsnba->exec_in(1); + rxa->bpsnba->exec_out(1); SNBA::xsnba (rxa->snba); EQP::xeqp (rxa->eqp); ANF::xanf (rxa->anf, 0); @@ -754,7 +754,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) // dsp_rate blocks rxa->adcmeter->setSamplerate(rxa->dsp_rate); rxa->nbp0->setSamplerate(rxa->dsp_rate); - BPSNBA::setSamplerate_bpsnba (rxa->bpsnba, rxa->dsp_rate); + rxa->bpsnba->setSamplerate(rxa->dsp_rate); rxa->smeter->setSamplerate(rxa->dsp_rate); SENDER::setSamplerate_sender (rxa->sender, rxa->dsp_rate); AMSQ::setSamplerate_amsq (rxa->amsq, rxa->dsp_rate); @@ -819,8 +819,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->adcmeter->setSize(rxa->dsp_size); rxa->nbp0->setBuffers(rxa->midbuff, rxa->midbuff); rxa->nbp0->setSize(rxa->dsp_size); - BPSNBA::setBuffers_bpsnba (rxa->bpsnba, rxa->midbuff, rxa->midbuff); - BPSNBA::setSize_bpsnba (rxa->bpsnba, rxa->dsp_size); + rxa->bpsnba->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->bpsnba->setSize(rxa->dsp_size); rxa->smeter->setBuffers(rxa->midbuff); rxa->smeter->METER::setSize(rxa->dsp_size); SENDER::setBuffers_sender (rxa->sender, rxa->midbuff); @@ -1028,7 +1028,7 @@ void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) a->f_high = f_high; a->run_notches = run_notches; // f_low, f_high, run_notches are needed for the filter recalculation - BPSNBA::recalc_bpsnba_filter (a, 0); + rxa.bpsnba->recalc_bpsnba_filter(0); } } @@ -1087,7 +1087,7 @@ void RXA::UpdateNBPFilters(RXA& rxa) } if (b->bpsnba->fnfrun) { - BPSNBA::recalc_bpsnba_filter (b, 1); + b->recalc_bpsnba_filter(1); } } @@ -1199,7 +1199,7 @@ void RXA::NBPSetWindow (RXA& rxa, int wintype) if ((b->wintype != wintype)) { b->wintype = wintype; - BPSNBA::recalc_bpsnba_filter (b, 1); + b->recalc_bpsnba_filter(1); } } @@ -1221,7 +1221,7 @@ void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) if ((b->autoincr != autoincr)) { b->autoincr = autoincr; - BPSNBA::recalc_bpsnba_filter (b, 1); + b->recalc_bpsnba_filter(1); } } @@ -1242,7 +1242,7 @@ void RXA::SetNC (RXA& rxa, int nc) { int oldstate = rxa.state; rxa.nbp0->SetNC (nc); - BPSNBA::BPSNBASetNC (rxa, nc); + rxa.bpsnba->SetNC (nc); BANDPASS::SetBandpassNC (rxa, nc); EQP::SetEQNC (rxa, nc); FMSQ::SetFMSQNC (rxa, nc); @@ -1254,7 +1254,7 @@ void RXA::SetNC (RXA& rxa, int nc) void RXA::SetMP (RXA& rxa, int mp) { rxa.nbp0->SetMP (mp); - BPSNBA::BPSNBASetMP (rxa, mp); + rxa.bpsnba->SetMP (mp); BANDPASS::SetBandpassMP (rxa, mp); EQP::SetEQMP (rxa, mp); FMSQ::SetFMSQMP (rxa, mp); diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index 180887676..a271c0aa3 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -51,135 +51,132 @@ namespace WDSP { // for its input and output to happen at different points in the processing pipeline. This means it must // include a buffer, 'buff'. Its input and output are done via functions xbpshbain() and xbpshbaout(). -void BPSNBA::calc_bpsnba (BPSNBA *a) +void BPSNBA::calc() { - a->buff = new float[a->size * 2]; // (double *) malloc0 (a->size * sizeof (complex)); - a->bpsnba = new NBP ( + buff = new float[size * 2]; // (double *) malloc0 (size * sizeof (complex)); + bpsnba = new NBP ( 1, // run, always runs (use bpsnba 'run') - a->run_notches, // run the notches + run_notches, // run the notches 0, // position variable for nbp (not for bpsnba), always 0 - a->size, // buffer size - a->nc, // number of filter coefficients - a->mp, // minimum phase flag - a->buff, // pointer to input buffer - a->out, // pointer to output buffer - a->f_low, // lower filter frequency - a->f_high, // upper filter frequency - a->rate, // sample rate - a->wintype, // wintype - a->gain, // gain - a->autoincr, // auto-increase notch width if below min - a->maxpb, // max number of passbands - a->ptraddr); // addr of database pointer + size, // buffer size + nc, // number of filter coefficients + mp, // minimum phase flag + buff, // pointer to input buffer + out, // pointer to output buffer + f_low, // lower filter frequency + f_high, // upper filter frequency + rate, // sample rate + wintype, // wintype + gain, // gain + autoincr, // auto-increase notch width if below min + maxpb, // max number of passbands + notchdb); // addr of database pointer } -BPSNBA* BPSNBA::create_bpsnba ( - int run, - int run_notches, - int position, - int size, - int nc, - int mp, - float* in, - float* out, - int rate, - double abs_low_freq, - double abs_high_freq, - double f_low, - double f_high, - int wintype, - double gain, - int autoincr, - int maxpb, - NOTCHDB* ptraddr -) +BPSNBA::BPSNBA( + int _run, + int _run_notches, + int _position, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + int _rate, + double _abs_low_freq, + double _abs_high_freq, + double _f_low, + double _f_high, + int _wintype, + double _gain, + int _autoincr, + int _maxpb, + NOTCHDB* _notchdb +) : + run(_run), + run_notches(_run_notches), + position(_position), + size(_size), + nc(_nc), + mp(_mp), + in(_in), + out(_out), + rate(_rate), + abs_low_freq(_abs_low_freq), + abs_high_freq(_abs_high_freq), + f_low(_f_low), + f_high(_f_high), + wintype(_wintype), + gain(_gain), + autoincr(_autoincr), + maxpb(_maxpb), + notchdb(_notchdb) { - BPSNBA *a = new BPSNBA; - a->run = run; - a->run_notches = run_notches; - a->position = position; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->rate = rate; - a->abs_low_freq = abs_low_freq; - a->abs_high_freq = abs_high_freq; - a->f_low = f_low; - a->f_high = f_high; - a->wintype = wintype; - a->gain = gain; - a->autoincr = autoincr; - a->maxpb = maxpb; - a->ptraddr = ptraddr; - calc_bpsnba (a); - return a; + calc(); } -void BPSNBA::decalc_bpsnba (BPSNBA *a) +void BPSNBA::decalc() { - delete (a->bpsnba); - delete[] (a->buff); + delete (bpsnba); + delete[] (buff); } -void BPSNBA::destroy_bpsnba (BPSNBA *a) +BPSNBA::~BPSNBA() { - decalc_bpsnba (a); - delete[] (a); + decalc(); } -void BPSNBA::flush_bpsnba (BPSNBA *a) +void BPSNBA::flush() { - std::fill(a->buff, a->buff + a->size * 2, 0); - a->bpsnba->flush(); + std::fill(buff, buff + size * 2, 0); + bpsnba->flush(); } -void BPSNBA::setBuffers_bpsnba (BPSNBA *a, float* in, float* out) +void BPSNBA::setBuffers(float* _in, float* _out) { - decalc_bpsnba (a); - a->in = in; - a->out = out; - calc_bpsnba (a); + decalc(); + in = _in; + out = _out; + calc(); } -void BPSNBA::setSamplerate_bpsnba (BPSNBA *a, int rate) +void BPSNBA::setSamplerate(int _rate) { - decalc_bpsnba (a); - a->rate = rate; - calc_bpsnba (a); + decalc(); + rate = _rate; + calc(); } -void BPSNBA::setSize_bpsnba (BPSNBA *a, int size) +void BPSNBA::setSize(int _size) { - decalc_bpsnba (a); - a->size = size; - calc_bpsnba (a); + decalc(); + size = _size; + calc(); } -void BPSNBA::xbpsnbain (BPSNBA *a, int position) +void BPSNBA::exec_in(int _position) { - if (a->run && a->position == position) - std::copy(a->in, a->in + a->size * 2, a->buff); + if (run && position == _position) + std::copy(in, in + size * 2, buff); } -void BPSNBA::xbpsnbaout (BPSNBA *a, int position) +void BPSNBA::exec_out(int _position) { - if (a->run && a->position == position) - a->bpsnba->execute(0); + if (run && position == _position) + bpsnba->execute(0); } -void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update) +void BPSNBA::recalc_bpsnba_filter(int update) { // Call anytime one of the parameters listed below has been changed in // the BPSNBA struct. - NBP *b = a->bpsnba; - b->fnfrun = a->run_notches; - b->flow = a->f_low; - b->fhigh = a->f_high; - b->wintype = a->wintype; - b->gain = a->gain; - b->autoincr = a->autoincr; + NBP *b = bpsnba; + b->fnfrun = run_notches; + b->flow = f_low; + b->fhigh = f_high; + b->wintype = wintype; + b->gain = gain; + b->autoincr = autoincr; b->calc_impulse(); FIRCORE::setImpulse_fircore (b->fircore, b->impulse, update); delete[] (b->impulse); @@ -187,31 +184,27 @@ void BPSNBA::recalc_bpsnba_filter (BPSNBA *a, int update) /******************************************************************************************************** * * -* RXA Properties * +* Properties * * * ********************************************************************************************************/ -void BPSNBA::BPSNBASetNC (RXA& rxa, int nc) +void BPSNBA::SetNC(int _nc) { - BPSNBA *a = rxa.bpsnba; - - if (a->nc != nc) + if (nc != _nc) { - a->nc = nc; - a->bpsnba->nc = a->nc; - a->bpsnba->setNc(); + nc = _nc; + bpsnba->nc = nc; + bpsnba->setNc(); } } -void BPSNBA::BPSNBASetMP (RXA& rxa, int mp) +void BPSNBA::SetMP(int _mp) { - BPSNBA *a = rxa.bpsnba; - - if (a->mp != mp) + if (mp != _mp) { - a->mp = mp; - a->bpsnba->mp = a->mp; - a->bpsnba->setMp(); + mp = _mp; + bpsnba->mp = mp; + bpsnba->setMp(); } } diff --git a/wdsp/bpsnba.hpp b/wdsp/bpsnba.hpp index 179e5c8d3..4e79f6744 100644 --- a/wdsp/bpsnba.hpp +++ b/wdsp/bpsnba.hpp @@ -47,19 +47,19 @@ public: float* in; // input buffer float* out; // output buffer int rate; // sample rate - float* buff; // internal buffer - NBP *bpsnba; // pointer to the notched bandpass filter, nbp - double f_low; // low cutoff frequency - double f_high; // high cutoff frequency double abs_low_freq; // lowest positive freq supported by SNB double abs_high_freq; // highest positive freq supported by SNG + double f_low; // low cutoff frequency + double f_high; // high cutoff frequency + float* buff; // internal buffer int wintype; // filter window type double gain; // filter gain int autoincr; // use auto increment for notch width int maxpb; // maximum passband segments supported - NOTCHDB* ptraddr; // pointer to address of NOTCH DATABASE + NOTCHDB* notchdb; // pointer to address of NOTCH DATABASE + NBP *bpsnba; // pointer to the notched bandpass filter, nbp - static BPSNBA* create_bpsnba ( + BPSNBA( int run, int run_notches, int position, @@ -77,23 +77,24 @@ public: double gain, int autoincr, int maxpb, - NOTCHDB* ptraddr + NOTCHDB* notchdb ); - static void destroy_bpsnba (BPSNBA *a); - static void flush_bpsnba (BPSNBA *a); - static void setBuffers_bpsnba (BPSNBA *a, float* in, float* out); - static void setSamplerate_bpsnba (BPSNBA *a, int rate); - static void setSize_bpsnba (BPSNBA *a, int size); - static void xbpsnbain (BPSNBA *a, int position); - static void xbpsnbaout (BPSNBA *a, int position); - static void recalc_bpsnba_filter (BPSNBA *a, int update); - // RXA Propertoes - static void BPSNBASetNC (RXA& rxa, int nc); - static void BPSNBASetMP (RXA& rxa, int mp); + ~BPSNBA(); + + void flush(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void exec_in(int position); + void exec_out(int position); + void recalc_bpsnba_filter(int update); + // Propertoes + void SetNC(int nc); + void SetMP(int mp); private: - static void calc_bpsnba (BPSNBA *a); - static void decalc_bpsnba (BPSNBA *a); + void calc(); + void decalc(); }; From dac4bc08df9dbf3850350f306d00c6977f524adb Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 24 Jul 2024 23:30:41 +0200 Subject: [PATCH 12/46] WDSP: sender: replaced static methods --- wdsp/RXA.cpp | 22 ++++++++++---------- wdsp/sender.cpp | 55 +++++++++++++++++++++---------------------------- wdsp/sender.hpp | 20 +++++++++--------- 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index a565c8066..9f9cf68a5 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -201,7 +201,7 @@ RXA* RXA::create_rxa ( rxa->ndb); // addr of database pointer // Post filter display send - send spectrum display (after S-meter in the block diagram) - rxa->sender = SENDER::create_sender ( + rxa->sender = new SENDER ( 0, // run 0, // flag 0, // mode @@ -583,7 +583,7 @@ void RXA::destroy_rxa (RXA *rxa) AMD::destroy_amd (rxa->amd); AMSQ::destroy_amsq (rxa->amsq); delete (rxa->smeter); - SENDER::destroy_sender (rxa->sender); + delete (rxa->sender); delete (rxa->bpsnba); delete (rxa->nbp0); delete (rxa->ndb); @@ -610,7 +610,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->adcmeter->flush(); rxa->nbp0->flush(); rxa->bpsnba->flush(); - SENDER::flush_sender (rxa->sender); + rxa->sender->flush(); rxa->smeter->flush(); AMSQ::flush_amsq (rxa->amsq); AMD::flush_amd (rxa->amd); @@ -643,7 +643,7 @@ void RXA::xrxa (RXA *rxa) rxa->bpsnba->exec_in(0); rxa->nbp0->execute(0); rxa->smeter->execute(); - SENDER::xsender (rxa->sender); + rxa->sender->execute(); AMSQ::xamsqcap (rxa->amsq); rxa->bpsnba->exec_out(0); AMD::xamd (rxa->amd); @@ -756,7 +756,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->nbp0->setSamplerate(rxa->dsp_rate); rxa->bpsnba->setSamplerate(rxa->dsp_rate); rxa->smeter->setSamplerate(rxa->dsp_rate); - SENDER::setSamplerate_sender (rxa->sender, rxa->dsp_rate); + rxa->sender->setSamplerate(rxa->dsp_rate); AMSQ::setSamplerate_amsq (rxa->amsq, rxa->dsp_rate); AMD::setSamplerate_amd (rxa->amd, rxa->dsp_rate); FMD::setSamplerate_fmd (rxa->fmd, rxa->dsp_rate); @@ -822,9 +822,9 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->bpsnba->setBuffers(rxa->midbuff, rxa->midbuff); rxa->bpsnba->setSize(rxa->dsp_size); rxa->smeter->setBuffers(rxa->midbuff); - rxa->smeter->METER::setSize(rxa->dsp_size); - SENDER::setBuffers_sender (rxa->sender, rxa->midbuff); - SENDER::setSize_sender (rxa->sender, rxa->dsp_size); + rxa->smeter->setSize(rxa->dsp_size); + rxa->sender->setBuffers(rxa->midbuff); + rxa->sender->setSize(rxa->dsp_size); AMSQ::setBuffers_amsq (rxa->amsq, rxa->midbuff, rxa->midbuff, rxa->midbuff); AMSQ::setSize_amsq (rxa->amsq, rxa->dsp_size); AMD::setBuffers_amd (rxa->amd, rxa->midbuff, rxa->midbuff); @@ -847,8 +847,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); WCPAGC::setBuffers_wcpagc (rxa->agc, rxa->midbuff, rxa->midbuff); WCPAGC::setSize_wcpagc (rxa->agc, rxa->dsp_size); - rxa->agcmeter->METER::setBuffers(rxa->midbuff); - rxa->agcmeter->METER::setSize(rxa->dsp_size); + rxa->agcmeter->setBuffers(rxa->midbuff); + rxa->agcmeter->setSize(rxa->dsp_size); SIPHON::setBuffers_siphon (rxa->sip1, rxa->midbuff); SIPHON::setSize_siphon (rxa->sip1, rxa->dsp_size); CBL::setBuffers_cbl (rxa->cbl, rxa->midbuff, rxa->midbuff); @@ -868,7 +868,7 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) { - SENDER::SetSpectrum(*this, 1, spectrumProbe); + sender->SetSpectrum(1, spectrumProbe); sender->run = 1; } diff --git a/wdsp/sender.cpp b/wdsp/sender.cpp index 9af4b8231..e8aaf811d 100644 --- a/wdsp/sender.cpp +++ b/wdsp/sender.cpp @@ -32,37 +32,30 @@ warren@wpratt.com namespace WDSP { -SENDER* SENDER::create_sender (int run, int flag, int mode, int size, float* in) +SENDER::SENDER(int _run, int _flag, int _mode, int _size, float* _in) : + run(_run), + flag(_flag), + mode(_mode), + size(_size), + in(_in) { - SENDER *a = new SENDER; - a->run = run; - a->flag = flag; - a->mode = mode; - a->size = size; - a->in = in; - a->spectrumProbe = nullptr; - return a; + spectrumProbe = nullptr; } -void SENDER::destroy_sender (SENDER *a) -{ - delete (a); -} - -void SENDER::flush_sender (SENDER *) +void SENDER::flush() { } -void SENDER::xsender (SENDER *a) +void SENDER::execute() { - if (a->run && a->flag) + if (run && flag) { - switch (a->mode) + switch (mode) { case 0: { - if (a->spectrumProbe) { - a->spectrumProbe->proceed(a->in, a->size); + if (spectrumProbe) { + spectrumProbe->proceed(in, size); } break; } @@ -70,33 +63,31 @@ void SENDER::xsender (SENDER *a) } } -void SENDER::setBuffers_sender (SENDER *a, float* in) +void SENDER::setBuffers(float* _in) { - a->in = in; + in = _in; } -void SENDER::setSamplerate_sender (SENDER *a, int) +void SENDER::setSamplerate(int) { - flush_sender (a); + flush(); } -void SENDER::setSize_sender (SENDER *a, int size) +void SENDER::setSize(int _size) { - a->size = size; + size = _size; } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -void SENDER::SetSpectrum (RXA& rxa, int flag, BufferProbe *spectrumProbe) +void SENDER::SetSpectrum(int _flag, BufferProbe *_spectrumProbe) { - SENDER *a; - a = rxa.sender; - a->flag = flag; - a->spectrumProbe = spectrumProbe; + flag = _flag; + spectrumProbe = _spectrumProbe; } } // namespace WDSP diff --git a/wdsp/sender.hpp b/wdsp/sender.hpp index 23c6752e8..a174690b2 100644 --- a/wdsp/sender.hpp +++ b/wdsp/sender.hpp @@ -50,16 +50,16 @@ public: float* in; // buffer from which to take the data BufferProbe *spectrumProbe; // this is the data handler actually - static SENDER* create_sender (int run, int flag, int mode, int size, float* in); - static void destroy_sender (SENDER *a); - static void flush_sender (SENDER *a); - static void xsender (SENDER *a); - static void setBuffers_sender (SENDER *a, float* in); - static void setSamplerate_sender (SENDER *a, int rate); - static void setSize_sender (SENDER *a, int size); - // RXA Properties - static void SetSpectrum (RXA& rxa, int flag, BufferProbe *spectrumProbe); - // TXA Properties + SENDER(int run, int flag, int mode, int size, float* in); + ~SENDER() = default; + + void flush(); + void execute(); + void setBuffers(float* in); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void SetSpectrum(int flag, BufferProbe *spectrumProbe); }; } // namespace WDSP From a239fe47e92032c8d3c1142467de91ba8c298017 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 26 Jul 2024 17:52:34 +0200 Subject: [PATCH 13/46] WDSP: AMSQ and AMD: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 14 +- wdsp/RXA.cpp | 50 ++-- wdsp/RXA.hpp | 2 + wdsp/TXA.cpp | 16 +- wdsp/amd.cpp | 294 +++++++++++------------- wdsp/amd.hpp | 23 +- wdsp/amsq.cpp | 268 ++++++++++----------- wdsp/amsq.hpp | 62 +++-- 8 files changed, 348 insertions(+), 381 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 29aa9af18..ff93bfd4f 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -466,9 +466,9 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_SAM); if (dsb) { - WDSP::AMD::SetAMDSBMode(*m_rxa, 0); + m_rxa->amd->setSBMode(0); } else { - WDSP::AMD::SetAMDSBMode(*m_rxa, usb ? 2 : 1); + m_rxa->amd->setSBMode(usb ? 2 : 1); } } else if (settings.m_demod == WDSPRxProfile::DemodFMN) @@ -643,7 +643,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // AM option if ((m_settings.m_amFadeLevel != settings.m_amFadeLevel) || force) { - WDSP::AMD::SetAMDFadeLevel(*m_rxa, settings.m_amFadeLevel); + m_rxa->amd->setFadeLevel(settings.m_amFadeLevel); } // FM options @@ -681,7 +681,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) || (m_settings.m_squelchMode != settings.m_squelchMode) || force) { WDSP::SSQL::SetSSQLRun(*m_rxa, 0); - WDSP::AMSQ::SetAMSQRun(*m_rxa, 0); + m_rxa->amsq->setRun(0); WDSP::FMSQ::SetFMSQRun(*m_rxa, 0); if (settings.m_squelch) @@ -697,9 +697,9 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) break; case WDSPRxProfile::SquelchModeAM: { - WDSP::AMSQ::SetAMSQRun(*m_rxa, 1); + m_rxa->amsq->setRun(1); double threshold = ((settings.m_squelchThreshold / 100.0) * 160.0) - 160.0; - WDSP::AMSQ::SetAMSQThreshold(*m_rxa, threshold); + m_rxa->amsq->setThreshold(threshold); } break; case WDSPRxProfile::SquelchModeFM: @@ -725,7 +725,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } if ((m_settings.m_amsqMaxTail != settings.m_amsqMaxTail) || force) { - WDSP::AMSQ::SetAMSQMaxTail(*m_rxa, settings.m_amsqMaxTail); + m_rxa->amsq->setMaxTail(settings.m_amsqMaxTail); } // Equalizer diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 9f9cf68a5..e29ff99c8 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -227,7 +227,7 @@ RXA* RXA::create_rxa ( 0); // pointer for gain computation // AM squelch capture (for other modes than FM) - rxa->amsq = AMSQ::create_amsq ( + rxa->amsq = new AMSQ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to signal input buffer used by xamsq @@ -244,7 +244,7 @@ RXA* RXA::create_rxa ( 0.0); // muted gain // AM/SAM demodulator - rxa->amd = AMD::create_amd ( + rxa->amd = new AMD( 0, // run - OFF by default rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -580,8 +580,8 @@ void RXA::destroy_rxa (RXA *rxa) SNBA::destroy_snba (rxa->snba); FMSQ::destroy_fmsq (rxa->fmsq); FMD::destroy_fmd (rxa->fmd); - AMD::destroy_amd (rxa->amd); - AMSQ::destroy_amsq (rxa->amsq); + delete (rxa->amd); + delete (rxa->amsq); delete (rxa->smeter); delete (rxa->sender); delete (rxa->bpsnba); @@ -612,8 +612,8 @@ void RXA::flush_rxa (RXA *rxa) rxa->bpsnba->flush(); rxa->sender->flush(); rxa->smeter->flush(); - AMSQ::flush_amsq (rxa->amsq); - AMD::flush_amd (rxa->amd); + rxa->amsq->flush(); + rxa->amd->flush(); FMD::flush_fmd (rxa->fmd); FMSQ::flush_fmsq (rxa->fmsq); SNBA::flush_snba (rxa->snba); @@ -644,9 +644,9 @@ void RXA::xrxa (RXA *rxa) rxa->nbp0->execute(0); rxa->smeter->execute(); rxa->sender->execute(); - AMSQ::xamsqcap (rxa->amsq); + rxa->amsq->xcap(); rxa->bpsnba->exec_out(0); - AMD::xamd (rxa->amd); + rxa->amd->execute(); FMD::xfmd (rxa->fmd); FMSQ::xfmsq (rxa->fmsq); rxa->bpsnba->exec_in(1); @@ -669,7 +669,7 @@ void RXA::xrxa (RXA *rxa) MPEAK::xmpeak (rxa->mpeak); SSQL::xssql (rxa->ssql); PANEL::xpanel (rxa->panel); - AMSQ::xamsq (rxa->amsq); + rxa->amsq->execute(); rxa->rsmpout->execute(); } @@ -757,8 +757,8 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->bpsnba->setSamplerate(rxa->dsp_rate); rxa->smeter->setSamplerate(rxa->dsp_rate); rxa->sender->setSamplerate(rxa->dsp_rate); - AMSQ::setSamplerate_amsq (rxa->amsq, rxa->dsp_rate); - AMD::setSamplerate_amd (rxa->amd, rxa->dsp_rate); + rxa->amsq->setSamplerate(rxa->dsp_rate); + rxa->amd->setSamplerate(rxa->dsp_rate); FMD::setSamplerate_fmd (rxa->fmd, rxa->dsp_rate); FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); FMSQ::setSamplerate_fmsq (rxa->fmsq, rxa->dsp_rate); @@ -825,10 +825,10 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->smeter->setSize(rxa->dsp_size); rxa->sender->setBuffers(rxa->midbuff); rxa->sender->setSize(rxa->dsp_size); - AMSQ::setBuffers_amsq (rxa->amsq, rxa->midbuff, rxa->midbuff, rxa->midbuff); - AMSQ::setSize_amsq (rxa->amsq, rxa->dsp_size); - AMD::setBuffers_amd (rxa->amd, rxa->midbuff, rxa->midbuff); - AMD::setSize_amd (rxa->amd, rxa->dsp_size); + rxa->amsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->midbuff); + rxa->amsq->setSize(rxa->dsp_size); + rxa->amd->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->amd->setSize(rxa->dsp_size); FMD::setBuffers_fmd (rxa->fmd, rxa->midbuff, rxa->midbuff); FMD::setSize_fmd (rxa->fmd, rxa->dsp_size); FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); @@ -1225,6 +1225,26 @@ void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) } } +void RXA::SetAMDRun(RXA& rxa, int run) +{ + AMD *a = rxa.amd; + + if (run != run) + { + RXA::bp1Check ( + rxa, + run, + rxa.snba->run, + rxa.emnr->run, + rxa.anf->run, + rxa.anr->run + ); + + run = run; + RXA::bp1Set (rxa); + } +} + /******************************************************************************************************** * * * Collectives * diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 271e2c337..ebc93ddf0 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -167,6 +167,8 @@ public: static void NBPSetNotchesRun (RXA& rxa, int run); static void NBPSetWindow (RXA& rxa, int wintype); static void NBPSetAutoIncrease (RXA& rxa, int autoincr); + // AMD + static void SetAMDRun(RXA& rxa, int run); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 9d901c4fb..d58e1c430 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -135,7 +135,7 @@ TXA* TXA::create_txa ( -1, // index for gain value 0); // pointer for gain computation - txa->amsq.p = AMSQ::create_amsq ( + txa->amsq.p = new AMSQ( 0, // run txa->dsp_size, // size txa->midbuff, // input buffer @@ -544,7 +544,7 @@ void TXA::destroy_txa (TXA *txa) EMPHP::destroy_emphp (txa->preemph.p); delete (txa->eqmeter.p); EQP::destroy_eqp (txa->eqp.p); - AMSQ::destroy_amsq (txa->amsq.p); + delete (txa->amsq.p); delete (txa->micmeter.p); PHROT::destroy_phrot (txa->phrot.p); PANEL::destroy_panel (txa->panel.p); @@ -566,7 +566,7 @@ void TXA::flush_txa (TXA* txa) PANEL::flush_panel (txa->panel.p); PHROT::flush_phrot (txa->phrot.p); txa->micmeter.p->flush (); - AMSQ::flush_amsq (txa->amsq.p); + txa->amsq.p->flush (); EQP::flush_eqp (txa->eqp.p); txa->eqmeter.p->flush (); EMPHP::flush_emphp (txa->preemph.p); @@ -600,8 +600,8 @@ void xtxa (TXA* txa) PANEL::xpanel (txa->panel.p); // includes MIC gain PHROT::xphrot (txa->phrot.p); // phase rotator txa->micmeter.p->execute (); // MIC meter - AMSQ::xamsqcap (txa->amsq.p); // downward expander capture - AMSQ::xamsq (txa->amsq.p); // downward expander action + txa->amsq.p->xcap (); // downward expander capture + txa->amsq.p->execute (); // downward expander action EQP::xeqp (txa->eqp.p); // pre-EQ txa->eqmeter.p->execute (); // EQ meter EMPHP::xemphp (txa->preemph.p, 0); // FM pre-emphasis (first option) @@ -698,7 +698,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate); PHROT::setSamplerate_phrot (txa->phrot.p, txa->dsp_rate); txa->micmeter.p->setSamplerate (txa->dsp_rate); - AMSQ::setSamplerate_amsq (txa->amsq.p, txa->dsp_rate); + txa->amsq.p->setSamplerate (txa->dsp_rate); EQP::setSamplerate_eqp (txa->eqp.p, txa->dsp_rate); txa->eqmeter.p->setSamplerate (txa->dsp_rate); EMPHP::setSamplerate_emphp (txa->preemph.p, txa->dsp_rate); @@ -762,8 +762,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) PHROT::setSize_phrot (txa->phrot.p, txa->dsp_size); txa->micmeter.p->setBuffers (txa->midbuff); txa->micmeter.p->setSize (txa->dsp_size); - AMSQ::setBuffers_amsq (txa->amsq.p, txa->midbuff, txa->midbuff, txa->midbuff); - AMSQ::setSize_amsq (txa->amsq.p, txa->dsp_size); + txa->amsq.p->setBuffers (txa->midbuff, txa->midbuff, txa->midbuff); + txa->amsq.p->setSize (txa->dsp_size); EQP::setBuffers_eqp (txa->eqp.p, txa->midbuff, txa->midbuff); EQP::setSize_eqp (txa->eqp.p, txa->dsp_size); txa->eqmeter.p->setBuffers (txa->midbuff); diff --git a/wdsp/amd.cpp b/wdsp/amd.cpp index 2b5090c01..7db5f2858 100644 --- a/wdsp/amd.cpp +++ b/wdsp/amd.cpp @@ -37,93 +37,85 @@ warren@wpratt.com namespace WDSP { -AMD* AMD::create_amd +AMD::AMD ( - int run, - int buff_size, - float *in_buff, - float *out_buff, - int mode, - int levelfade, - int sbmode, - int sample_rate, - double fmin, - double fmax, - double zeta, - double omegaN, - double tauR, - double tauI + int _run, + int _buff_size, + float *_in_buff, + float *_out_buff, + int _mode, + int _levelfade, + int _sbmode, + int _sample_rate, + double _fmin, + double _fmax, + double _zeta, + double _omegaN, + double _tauR, + double _tauI ) { - AMD *a = new AMD(); - a->run = run; - a->buff_size = buff_size; - a->in_buff = in_buff; - a->out_buff = out_buff; - a->mode = mode; - a->levelfade = levelfade; - a->sbmode = sbmode; - a->sample_rate = (float)sample_rate; - a->fmin = fmin; - a->fmax = fmax; - a->zeta = zeta; - a->omegaN = omegaN; - a->tauR = tauR; - a->tauI = tauI; - - init_amd(a); - return a; + run = _run; + buff_size = _buff_size; + in_buff = _in_buff; + out_buff = _out_buff; + mode = _mode; + levelfade = _levelfade; + sbmode = _sbmode; + sample_rate = (double) _sample_rate; + fmin = _fmin; + fmax = _fmax; + zeta = _zeta; + omegaN = _omegaN; + tauR = _tauR; + tauI = _tauI; + init(); } -void AMD::destroy_amd(AMD *a) -{ - delete a; -} - -void AMD::init_amd(AMD *a) +void AMD::init() { //pll - a->omega_min = 2 * M_PI * a->fmin / a->sample_rate; - a->omega_max = 2 * M_PI * a->fmax / a->sample_rate; - a->g1 = 1.0 - std::exp(-2.0 * a->omegaN * a->zeta / a->sample_rate); - a->g2 = -a->g1 + 2.0 * (1 - exp(-a->omegaN * a->zeta / a->sample_rate) * cos(a->omegaN / a->sample_rate * sqrt(1.0 - a->zeta * a->zeta))); - a->phs = 0.0; - a->fil_out = 0.0; - a->omega = 0.0; + omega_min = 2 * M_PI * fmin / sample_rate; + omega_max = 2 * M_PI * fmax / sample_rate; + g1 = 1.0 - std::exp(-2.0 * omegaN * zeta / sample_rate); + g2 = -g1 + 2.0 * (1 - exp(-omegaN * zeta / sample_rate) * cos(omegaN / sample_rate * sqrt(1.0 - zeta * zeta))); + phs = 0.0; + fil_out = 0.0; + omega = 0.0; //fade leveler - a->dc = 0.0; - a->dc_insert = 0.0; - a->mtauR = exp(-1.0 / (a->sample_rate * a->tauR)); - a->onem_mtauR = 1.0 - a->mtauR; - a->mtauI = exp(-1.0 / (a->sample_rate * a->tauI)); - a->onem_mtauI = 1.0 - a->mtauI; + dc = 0.0; + dc_insert = 0.0; + mtauR = exp(-1.0 / (sample_rate * tauR)); + onem_mtauR = 1.0 - mtauR; + mtauI = exp(-1.0 / (sample_rate * tauI)); + onem_mtauI = 1.0 - mtauI; //sideband separation - a->c0[0] = -0.328201924180698; - a->c0[1] = -0.744171491539427; - a->c0[2] = -0.923022915444215; - a->c0[3] = -0.978490468768238; - a->c0[4] = -0.994128272402075; - a->c0[5] = -0.998458978159551; - a->c0[6] = -0.999790306259206; + c0[0] = -0.328201924180698; + c0[1] = -0.744171491539427; + c0[2] = -0.923022915444215; + c0[3] = -0.978490468768238; + c0[4] = -0.994128272402075; + c0[5] = -0.998458978159551; + c0[6] = -0.999790306259206; - a->c1[0] = -0.0991227952747244; - a->c1[1] = -0.565619728761389; - a->c1[2] = -0.857467122550052; - a->c1[3] = -0.959123933111275; - a->c1[4] = -0.988739372718090; - a->c1[5] = -0.996959189310611; - a->c1[6] = -0.999282492800792; + c1[0] = -0.0991227952747244; + c1[1] = -0.565619728761389; + c1[2] = -0.857467122550052; + c1[3] = -0.959123933111275; + c1[4] = -0.988739372718090; + c1[5] = -0.996959189310611; + c1[6] = -0.999282492800792; } -void AMD::flush_amd (AMD *a) +void AMD::flush() { - a->dc = 0.0; - a->dc_insert = 0.0; + dc = 0.0; + dc_insert = 0.0; } -void AMD::xamd (AMD *a) +void AMD::execute() { int i; double audio; @@ -135,28 +127,28 @@ void AMD::xamd (AMD *a) double ai_ps, bi_ps, aq_ps, bq_ps; int j, k; - if (a->run) + if (run) { - switch (a->mode) + switch (mode) { case 0: //AM Demodulator { - for (i = 0; i < a->buff_size; i++) + for (i = 0; i < buff_size; i++) { - double xr = a->in_buff[2 * i + 0]; - double xi = a->in_buff[2 * i + 1]; + double xr = in_buff[2 * i + 0]; + double xi = in_buff[2 * i + 1]; audio = sqrt(xr*xr + xi*xi); - if (a->levelfade) + if (levelfade) { - a->dc = a->mtauR * a->dc + a->onem_mtauR * audio; - a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * audio; - audio += a->dc_insert - a->dc; + dc = mtauR * dc + onem_mtauR * audio; + dc_insert = mtauI * dc_insert + onem_mtauI * audio; + audio += dc_insert - dc; } - a->out_buff[2 * i + 0] = audio; - a->out_buff[2 * i + 1] = audio; + out_buff[2 * i + 0] = audio; + out_buff[2 * i + 1] = audio; } break; @@ -164,52 +156,52 @@ void AMD::xamd (AMD *a) case 1: //Synchronous AM Demodulator with Sideband Separation { - for (i = 0; i < a->buff_size; i++) + for (i = 0; i < buff_size; i++) { - vco[0] = cos(a->phs); - vco[1] = sin(a->phs); + vco[0] = cos(phs); + vco[1] = sin(phs); - ai = a->in_buff[2 * i + 0] * vco[0]; - bi = a->in_buff[2 * i + 0] * vco[1]; - aq = a->in_buff[2 * i + 1] * vco[0]; - bq = a->in_buff[2 * i + 1] * vco[1]; + ai = in_buff[2 * i + 0] * vco[0]; + bi = in_buff[2 * i + 0] * vco[1]; + aq = in_buff[2 * i + 1] * vco[0]; + bq = in_buff[2 * i + 1] * vco[1]; - if (a->sbmode != 0) + if (sbmode != 0) { - a->a[0] = a->dsI; - a->b[0] = bi; - a->c[0] = a->dsQ; - a->d[0] = aq; - a->dsI = ai; - a->dsQ = bq; + a[0] = dsI; + b[0] = bi; + c[0] = dsQ; + d[0] = aq; + dsI = ai; + dsQ = bq; for (j = 0; j < STAGES; j++) { k = 3 * j; - a->a[k + 3] = a->c0[j] * (a->a[k] - a->a[k + 5]) + a->a[k + 2]; - a->b[k + 3] = a->c1[j] * (a->b[k] - a->b[k + 5]) + a->b[k + 2]; - a->c[k + 3] = a->c0[j] * (a->c[k] - a->c[k + 5]) + a->c[k + 2]; - a->d[k + 3] = a->c1[j] * (a->d[k] - a->d[k + 5]) + a->d[k + 2]; + a[k + 3] = c0[j] * (a[k] - a[k + 5]) + a[k + 2]; + b[k + 3] = c1[j] * (b[k] - b[k + 5]) + b[k + 2]; + c[k + 3] = c0[j] * (c[k] - c[k + 5]) + c[k + 2]; + d[k + 3] = c1[j] * (d[k] - d[k + 5]) + d[k + 2]; } - ai_ps = a->a[OUT_IDX]; - bi_ps = a->b[OUT_IDX]; - bq_ps = a->c[OUT_IDX]; - aq_ps = a->d[OUT_IDX]; + ai_ps = a[OUT_IDX]; + bi_ps = b[OUT_IDX]; + bq_ps = c[OUT_IDX]; + aq_ps = d[OUT_IDX]; for (j = OUT_IDX + 2; j > 0; j--) { - a->a[j] = a->a[j - 1]; - a->b[j] = a->b[j - 1]; - a->c[j] = a->c[j - 1]; - a->d[j] = a->d[j - 1]; + a[j] = a[j - 1]; + b[j] = b[j - 1]; + c[j] = c[j - 1]; + d[j] = d[j - 1]; } } corr[0] = +ai + bq; corr[1] = -bi + aq; - switch(a->sbmode) + switch(sbmode) { case 0: //both sidebands { @@ -228,100 +220,80 @@ void AMD::xamd (AMD *a) } } - if (a->levelfade) + if (levelfade) { - a->dc = a->mtauR * a->dc + a->onem_mtauR * audio; - a->dc_insert = a->mtauI * a->dc_insert + a->onem_mtauI * corr[0]; - audio += a->dc_insert - a->dc; + dc = mtauR * dc + onem_mtauR * audio; + dc_insert = mtauI * dc_insert + onem_mtauI * corr[0]; + audio += dc_insert - dc; } - a->out_buff[2 * i + 0] = audio; - a->out_buff[2 * i + 1] = audio; + out_buff[2 * i + 0] = audio; + out_buff[2 * i + 1] = audio; if ((corr[0] == 0.0) && (corr[1] == 0.0)) corr[0] = 1.0; det = atan2(corr[1], corr[0]); - del_out = a->fil_out; - a->omega += a->g2 * det; + del_out = fil_out; + omega += g2 * det; - if (a->omega < a->omega_min) - a->omega = a->omega_min; + if (omega < omega_min) + omega = omega_min; - if (a->omega > a->omega_max) - a->omega = a->omega_max; + if (omega > omega_max) + omega = omega_max; - a->fil_out = a->g1 * det + a->omega; - a->phs += del_out; + fil_out = g1 * det + omega; + phs += del_out; - while (a->phs >= 2 * M_PI) - a->phs -= 2 * M_PI; + while (phs >= 2 * M_PI) + phs -= 2 * M_PI; - while (a->phs < 0.0) - a->phs += 2 * M_PI; + while (phs < 0.0) + phs += 2 * M_PI; } break; } } } - else if (a->in_buff != a->out_buff) + else if (in_buff != out_buff) { - std::copy (a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff); + std::copy (in_buff, in_buff + buff_size * 2, out_buff); } } -void AMD::setBuffers_amd (AMD *a, float* in, float* out) +void AMD::setBuffers(float* in, float* out) { - a->in_buff = in; - a->out_buff = out; + in_buff = in; + out_buff = out; } -void AMD::setSamplerate_amd (AMD *a, int rate) +void AMD::setSamplerate(int rate) { - a->sample_rate = rate; - init_amd(a); + sample_rate = rate; + init(); } -void AMD::setSize_amd (AMD *a, int size) +void AMD::setSize(int size) { - a->buff_size = size; + buff_size = size; } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -void AMD::SetAMDRun(RXA& rxa, int run) +void AMD::setSBMode(int _sbmode) { - AMD *a = rxa.amd; - - if (a->run != run) - { - RXA::bp1Check ( - rxa, - run, - rxa.snba->run, - rxa.emnr->run, - rxa.anf->run, - rxa.anr->run - ); - - a->run = run; - RXA::bp1Set (rxa); - } + sbmode = _sbmode; } -void AMD::SetAMDSBMode(RXA& rxa, int sbmode) +void AMD::setFadeLevel(int _levelfade) { - rxa.amd->sbmode = sbmode; -} - -void AMD::SetAMDFadeLevel(RXA& rxa, int levelfade) -{ - rxa.amd->levelfade = levelfade; + levelfade = _levelfade; } } // namesoace WDSP diff --git a/wdsp/amd.hpp b/wdsp/amd.hpp index 6616d97fe..5a9a243de 100644 --- a/wdsp/amd.hpp +++ b/wdsp/amd.hpp @@ -80,7 +80,7 @@ public: int sbmode; // sideband mode int levelfade; // Fade Leveler switch - static AMD* create_amd + AMD ( int run, int buff_size, @@ -97,18 +97,17 @@ public: double tauR, double tauI ); + ~AMD() = default; - static void init_amd (AMD *a); - static void destroy_amd (AMD *a); - static void flush_amd (AMD *a); - static void xamd (AMD *a); - static void setBuffers_amd (AMD *a, float* in, float* out); - static void setSamplerate_amd (AMD *a, int rate); - static void setSize_amd (AMD *a, int size); - // RXA Properties - static void SetAMDRun(RXA& rxa, int run); - static void SetAMDSBMode(RXA& rxa, int sbmode); - static void SetAMDFadeLevel(RXA& rxa, int levelfade); + void init(); + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setSBMode(int sbmode); + void setFadeLevel(int levelfade); }; } // namespace WDSP diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index 41ea0717c..25a82c389 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -32,100 +32,97 @@ warren@wpratt.com namespace WDSP { -void AMSQ::compute_slews(AMSQ *a) +void AMSQ::compute_slews() { int i; double delta, theta; - delta = PI / (double)a->ntup; + delta = PI / (double)ntup; theta = 0.0; - for (i = 0; i <= a->ntup; i++) + for (i = 0; i <= ntup; i++) { - a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos (theta)); + cup[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 - cos (theta)); theta += delta; } - delta = PI / (double)a->ntdown; + delta = PI / (double)ntdown; theta = 0.0; - for (i = 0; i <= a->ntdown; i++) + for (i = 0; i <= ntdown; i++) { - a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos (theta)); + cdown[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 + cos (theta)); theta += delta; } } -void AMSQ::calc_amsq(AMSQ *a) +void AMSQ::calc() { // signal averaging - a->trigsig = new float[a->size * 2]; - a->avm = exp(-1.0 / (a->rate * a->avtau)); - a->onem_avm = 1.0 - a->avm; - a->avsig = 0.0; + trigsig = new float[size * 2]; + avm = exp(-1.0 / (rate * avtau)); + onem_avm = 1.0 - avm; + avsig = 0.0; // level change - a->ntup = (int)(a->tup * a->rate); - a->ntdown = (int)(a->tdown * a->rate); - a->cup = new double[(a->ntup + 1) * 2]; // (float *)malloc0((a->ntup + 1) * sizeof(float)); - a->cdown = new double[(a->ntdown + 1) * 2]; // (float *)malloc0((a->ntdown + 1) * sizeof(float)); - compute_slews(a); + ntup = (int)(tup * rate); + ntdown = (int)(tdown * rate); + cup = new double[(ntup + 1) * 2]; // (float *)malloc0((ntup + 1) * sizeof(float)); + cdown = new double[(ntdown + 1) * 2]; // (float *)malloc0((ntdown + 1) * sizeof(float)); + compute_slews(); // control - a->state = 0; + state = 0; } -void AMSQ::decalc_amsq (AMSQ *a) +void AMSQ::decalc() { - delete[] a->cdown; - delete[] a->cup; - delete[] a->trigsig; + delete[] cdown; + delete[] cup; + delete[] trigsig; } -AMSQ* AMSQ::create_amsq ( - int run, - int size, - float* in, - float* out, - float* trigger, - int rate, - double avtau, - double tup, - double tdown, - double tail_thresh, - double unmute_thresh, - double min_tail, - double max_tail, - double muted_gain -) +AMSQ::AMSQ ( + int _run, + int _size, + float* _in, + float* _out, + float* _trigger, + int _rate, + double _avtau, + double _tup, + double _tdown, + double _tail_thresh, + double _unmute_thresh, + double _min_tail, + double _max_tail, + double _muted_gain +) : + run(_run), + size(_size), + in(_in), + out(_out), + trigger(_trigger), + rate((double) _rate), + avtau(_avtau), + tup(_tup), + tdown(_tdown), + tail_thresh(_tail_thresh), + unmute_thresh(_unmute_thresh), + min_tail(_min_tail), + max_tail(_max_tail), + muted_gain(_muted_gain) { - AMSQ *a = new AMSQ; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = (float)rate; - a->muted_gain = muted_gain; - a->trigger = trigger; - a->avtau = avtau; - a->tup = tup; - a->tdown = tdown; - a->tail_thresh = tail_thresh; - a->unmute_thresh = unmute_thresh; - a->min_tail = min_tail; - a->max_tail = max_tail; - calc_amsq (a); - return a; + calc(); } -void AMSQ::destroy_amsq (AMSQ *a) +AMSQ::~AMSQ() { - decalc_amsq (a); - delete a; + decalc(); } -void AMSQ::flush_amsq (AMSQ*a) +void AMSQ::flush() { - std::fill(a->trigsig, a->trigsig + a->size * 2, 0); - a->avsig = 0.0; - a->state = 0; + std::fill(trigsig, trigsig + size * 2, 0); + avsig = 0.0; + state = 0; } enum _amsqstate @@ -137,114 +134,116 @@ enum _amsqstate DECREASE }; -void AMSQ::xamsq (AMSQ *a) +void AMSQ::execute() { - if (a->run) + if (run) { int i; double sig, siglimit; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - sig = sqrt (a->trigsig[2 * i + 0] * a->trigsig[2 * i + 0] + a->trigsig[2 * i + 1] * a->trigsig[2 * i + 1]); - a->avsig = a->avm * a->avsig + a->onem_avm * sig; + double trigr = trigsig[2 * i + 0]; + double trigi = trigsig[2 * i + 1]; + sig = sqrt (trigr*trigr + trigi*trigi); + avsig = avm * avsig + onem_avm * sig; - switch (a->state) + switch (state) { case MUTED: - if (a->avsig > a->unmute_thresh) + if (avsig > unmute_thresh) { - a->state = INCREASE; - a->count = a->ntup; + state = INCREASE; + count = ntup; } - a->out[2 * i + 0] = a->muted_gain * a->in[2 * i + 0]; - a->out[2 * i + 1] = a->muted_gain * a->in[2 * i + 1]; + out[2 * i + 0] = muted_gain * in[2 * i + 0]; + out[2 * i + 1] = muted_gain * in[2 * i + 1]; break; case INCREASE: - a->out[2 * i + 0] = a->in[2 * i + 0] * a->cup[a->ntup - a->count]; - a->out[2 * i + 1] = a->in[2 * i + 1] * a->cup[a->ntup - a->count]; + out[2 * i + 0] = in[2 * i + 0] * cup[ntup - count]; + out[2 * i + 1] = in[2 * i + 1] * cup[ntup - count]; - if (a->count-- == 0) - a->state = UNMUTED; + if (count-- == 0) + state = UNMUTED; break; case UNMUTED: - if (a->avsig < a->tail_thresh) + if (avsig < tail_thresh) { - a->state = TAIL; + state = TAIL; - if ((siglimit = a->avsig) > 1.0) + if ((siglimit = avsig) > 1.0) siglimit = 1.0; - a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * (1.0 - siglimit)) * a->rate); + count = (int)((min_tail + (max_tail - min_tail) * (1.0 - siglimit)) * rate); } - a->out[2 * i + 0] = a->in[2 * i + 0]; - a->out[2 * i + 1] = a->in[2 * i + 1]; + out[2 * i + 0] = in[2 * i + 0]; + out[2 * i + 1] = in[2 * i + 1]; break; case TAIL: - a->out[2 * i + 0] = a->in[2 * i + 0]; - a->out[2 * i + 1] = a->in[2 * i + 1]; + out[2 * i + 0] = in[2 * i + 0]; + out[2 * i + 1] = in[2 * i + 1]; - if (a->avsig > a->unmute_thresh) + if (avsig > unmute_thresh) { - a->state = UNMUTED; + state = UNMUTED; } - else if (a->count-- == 0) + else if (count-- == 0) { - a->state = DECREASE; - a->count = a->ntdown; + state = DECREASE; + count = ntdown; } break; case DECREASE: - a->out[2 * i + 0] = a->in[2 * i + 0] * a->cdown[a->ntdown - a->count]; - a->out[2 * i + 1] = a->in[2 * i + 1] * a->cdown[a->ntdown - a->count]; + out[2 * i + 0] = in[2 * i + 0] * cdown[ntdown - count]; + out[2 * i + 1] = in[2 * i + 1] * cdown[ntdown - count]; - if (a->count-- == 0) - a->state = MUTED; + if (count-- == 0) + state = MUTED; break; } } } - else if (a->in != a->out) + else if (in != out) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void AMSQ::xamsqcap (AMSQ *a) +void AMSQ::xcap() { - std::copy(a->trigger, a->trigger + a->size * 2, a->trigsig); + std::copy(trigger, trigger + size * 2, trigsig); } -void AMSQ::setBuffers_amsq (AMSQ *a, float* in, float* out, float* trigger) +void AMSQ::setBuffers(float* _in, float* _out, float* _trigger) { - a->in = in; - a->out = out; - a->trigger = trigger; + in = _in; + out = _out; + trigger = _trigger; } -void AMSQ::setSamplerate_amsq (AMSQ *a, int rate) +void AMSQ::setSamplerate(int _rate) { - decalc_amsq (a); - a->rate = rate; - calc_amsq (a); + decalc(); + rate = _rate; + calc(); } -void AMSQ::setSize_amsq (AMSQ *a, int size) +void AMSQ::setSize(int _size) { - decalc_amsq (a); - a->size = size; - calc_amsq (a); + decalc(); + size = _size; + calc(); } /******************************************************************************************************** @@ -253,53 +252,30 @@ void AMSQ::setSize_amsq (AMSQ *a, int size) * * ********************************************************************************************************/ -void AMSQ::SetAMSQRun (RXA& rxa, int run) +void AMSQ::setRun(int _run) { - rxa.amsq->run = run; + run = _run; } -void AMSQ::SetAMSQThreshold (RXA& rxa, double threshold) +void AMSQ::setThreshold(double _threshold) { - double thresh = pow (10.0, threshold / 20.0); - rxa.amsq->tail_thresh = 0.9 * thresh; - rxa.amsq->unmute_thresh = thresh; + double thresh = pow (10.0, _threshold / 20.0); + tail_thresh = 0.9 * thresh; + unmute_thresh = thresh; } -void AMSQ::SetAMSQMaxTail (RXA& rxa, double tail) +void AMSQ::setMaxTail(double _tail) { - AMSQ *a; - a = rxa.amsq; + if (_tail < min_tail) + _tail = min_tail; - if (tail < a->min_tail) - tail = a->min_tail; - - a->max_tail = tail; + max_tail = _tail; } -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -void AMSQ::SetAMSQRun (TXA& txa, int run) -{ - txa.amsq.p->run = run; -} - -void AMSQ::SetAMSQMutedGain (TXA& txa, double dBlevel) +void AMSQ::setMutedGain(double dBlevel) { // dBlevel is negative - AMSQ *a; - a = txa.amsq.p; - a->muted_gain = pow (10.0, dBlevel / 20.0); - compute_slews(a); -} - -void AMSQ::SetAMSQThreshold (TXA& txa, double threshold) -{ - double thresh = pow (10.0, threshold / 20.0); - txa.amsq.p->tail_thresh = 0.9 * thresh; - txa.amsq.p->unmute_thresh = thresh; + muted_gain = pow (10.0, dBlevel / 20.0); + compute_slews(); } } // namespace WDSP diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index 333acf355..069ed74da 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -62,42 +62,40 @@ public: double max_tail; double muted_gain; - static AMSQ* create_amsq ( - int run, - int size, - float* in, - float* out, - float* trigger, - int rate, - double avtau, - double tup, - double tdown, - double tail_thresh, - double unmute_thresh, - double min_tail, - double max_tail, - double muted_gain + AMSQ ( + int _run, + int _size, + float* _in, + float* _out, + float* _trigger, + int _rate, + double _avtau, + double _tup, + double _tdown, + double _tail_thresh, + double _unmute_thresh, + double _min_tail, + double _max_tail, + double _muted_gain ); - static void destroy_amsq (AMSQ *a); - static void flush_amsq (AMSQ *a); - static void xamsq (AMSQ *a); - static void xamsqcap (AMSQ *a); - static void setBuffers_amsq (AMSQ *a, float* in, float* out, float* trigger); - static void setSamplerate_amsq (AMSQ *a, int rate); - static void setSize_amsq (AMSQ *a, int size); + ~AMSQ(); + + void flush(); + void execute(); + void xcap(); + void setBuffers(float* in, float* out, float* trigger); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void SetAMSQRun (RXA& rxa, int run); - static void SetAMSQThreshold (RXA& rxa, double threshold); - static void SetAMSQMaxTail (RXA& rxa, double tail); - // TXA Properties - static void SetAMSQRun (TXA& txa, int run); - static void SetAMSQMutedGain (TXA& txa, double dBlevel); - static void SetAMSQThreshold (TXA& txa, double threshold); + void setRun(int run); + void setThreshold(double threshold); + void setMaxTail(double tail); + void setMutedGain(double dBlevel); private: - static void compute_slews(AMSQ *a); - static void calc_amsq(AMSQ *a); - static void decalc_amsq (AMSQ *a); + void compute_slews(); + void calc(); + void decalc(); }; } // namespace WDSP From 8c08f40b5414435f3f7b5d9ea01ec0b23e0f7f89 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 26 Jul 2024 19:10:53 +0200 Subject: [PATCH 14/46] WDSP: FMD and FMSQ: replaced static methods --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 18 +- wdsp/RXA.cpp | 52 ++- wdsp/fmd.cpp | 401 +++++++++++------------- wdsp/fmd.hpp | 40 +-- wdsp/fmsq.cpp | 323 ++++++++++--------- wdsp/fmsq.hpp | 68 ++-- 6 files changed, 434 insertions(+), 468 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index ff93bfd4f..d9a36d7fb 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -649,29 +649,29 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // FM options if ((m_settings.m_fmDeviation != settings.m_fmDeviation) || force) { - WDSP::FMD::SetFMDeviation(*m_rxa, settings.m_fmDeviation); + m_rxa->fmd->setDeviation(settings.m_fmDeviation); } if ((m_settings.m_fmAFLow != settings.m_fmAFLow) || (m_settings.m_fmAFHigh != settings.m_fmAFHigh) || force) { - WDSP::FMD::SetFMAFFilter(*m_rxa, settings.m_fmAFLow, settings.m_fmAFHigh); + m_rxa->fmd->setAFFilter(settings.m_fmAFLow, settings.m_fmAFHigh); } if ((m_settings.m_fmAFLimiter != settings.m_fmAFLimiter) || force) { - WDSP::FMD::SetFMLimRun(*m_rxa, settings.m_fmAFLimiter ? 1 : 0); + m_rxa->fmd->setLimRun(settings.m_fmAFLimiter ? 1 : 0); } if ((m_settings.m_fmAFLimiterGain != settings.m_fmAFLimiterGain) || force) { - WDSP::FMD::SetFMLimGain(*m_rxa, settings.m_fmAFLimiterGain); + m_rxa->fmd->setLimGain(settings.m_fmAFLimiterGain); } if ((m_settings.m_fmCTCSSNotch != settings.m_fmCTCSSNotch) || force) { - WDSP::FMD::SetCTCSSRun(*m_rxa, settings.m_fmCTCSSNotch ? 1 : 0); + m_rxa->fmd->setCTCSSRun(settings.m_fmCTCSSNotch ? 1 : 0); } if ((m_settings.m_fmCTCSSNotchFrequency != settings.m_fmCTCSSNotchFrequency) || force) { - WDSP::FMD::SetCTCSSFreq(*m_rxa, settings.m_fmCTCSSNotchFrequency); + m_rxa->fmd->setCTCSSFreq(settings.m_fmCTCSSNotchFrequency); } // Squelch @@ -682,7 +682,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) { WDSP::SSQL::SetSSQLRun(*m_rxa, 0); m_rxa->amsq->setRun(0); - WDSP::FMSQ::SetFMSQRun(*m_rxa, 0); + m_rxa->fmsq->setRun(0); if (settings.m_squelch) { @@ -704,10 +704,10 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) break; case WDSPRxProfile::SquelchModeFM: { - WDSP::FMSQ::SetFMSQRun(*m_rxa, 1); + m_rxa->fmsq->setRun(1); double threshold = pow(10.0, -2.0 * ((double) settings.m_squelchThreshold) / 100.0); qDebug("WDSPRxSink::applySettings: FM squelch %lf", threshold); - WDSP::FMSQ::SetFMSQThreshold(*m_rxa, threshold); + m_rxa->fmsq->setThreshold(threshold); } break; default: diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index e29ff99c8..81095fa12 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -261,7 +261,7 @@ RXA* RXA::create_rxa ( 1.4); // tauI // FM demodulator - rxa->fmd = FMD::create_fmd ( + rxa->fmd = new FMD( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -284,7 +284,7 @@ RXA* RXA::create_rxa ( 0); // min phase flag for audio cutoff filter // FM squelch apply - rxa->fmsq = FMSQ::create_fmsq ( + rxa->fmsq = new FMSQ( 0, // run rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input signal buffer @@ -578,8 +578,8 @@ void RXA::destroy_rxa (RXA *rxa) ANF::destroy_anf (rxa->anf); EQP::destroy_eqp (rxa->eqp); SNBA::destroy_snba (rxa->snba); - FMSQ::destroy_fmsq (rxa->fmsq); - FMD::destroy_fmd (rxa->fmd); + delete (rxa->fmsq); + delete (rxa->fmd); delete (rxa->amd); delete (rxa->amsq); delete (rxa->smeter); @@ -614,8 +614,8 @@ void RXA::flush_rxa (RXA *rxa) rxa->smeter->flush(); rxa->amsq->flush(); rxa->amd->flush(); - FMD::flush_fmd (rxa->fmd); - FMSQ::flush_fmsq (rxa->fmsq); + rxa->fmd->flush(); + rxa->fmsq->flush(); SNBA::flush_snba (rxa->snba); EQP::flush_eqp (rxa->eqp); ANF::flush_anf (rxa->anf); @@ -647,8 +647,8 @@ void RXA::xrxa (RXA *rxa) rxa->amsq->xcap(); rxa->bpsnba->exec_out(0); rxa->amd->execute(); - FMD::xfmd (rxa->fmd); - FMSQ::xfmsq (rxa->fmsq); + rxa->fmd->execute(); + rxa->fmsq->execute(); rxa->bpsnba->exec_in(1); rxa->bpsnba->exec_out(1); SNBA::xsnba (rxa->snba); @@ -759,9 +759,9 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->sender->setSamplerate(rxa->dsp_rate); rxa->amsq->setSamplerate(rxa->dsp_rate); rxa->amd->setSamplerate(rxa->dsp_rate); - FMD::setSamplerate_fmd (rxa->fmd, rxa->dsp_rate); - FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); - FMSQ::setSamplerate_fmsq (rxa->fmsq, rxa->dsp_rate); + rxa->fmd->setSamplerate(rxa->dsp_rate); + rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + rxa->fmsq->setSamplerate(rxa->dsp_rate); SNBA::setSamplerate_snba (rxa->snba, rxa->dsp_rate); EQP::setSamplerate_eqp (rxa->eqp, rxa->dsp_rate); ANF::setSamplerate_anf (rxa->anf, rxa->dsp_rate); @@ -829,10 +829,10 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->amsq->setSize(rxa->dsp_size); rxa->amd->setBuffers(rxa->midbuff, rxa->midbuff); rxa->amd->setSize(rxa->dsp_size); - FMD::setBuffers_fmd (rxa->fmd, rxa->midbuff, rxa->midbuff); - FMD::setSize_fmd (rxa->fmd, rxa->dsp_size); - FMSQ::setBuffers_fmsq (rxa->fmsq, rxa->midbuff, rxa->midbuff, rxa->fmd->audio); - FMSQ::setSize_fmsq (rxa->fmsq, rxa->dsp_size); + rxa->fmd->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->fmd->setSize(rxa->dsp_size); + rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + rxa->fmsq->setSize(rxa->dsp_size); SNBA::setBuffers_snba (rxa->snba, rxa->midbuff, rxa->midbuff); SNBA::setSize_snba (rxa->snba, rxa->dsp_size); EQP::setBuffers_eqp (rxa->eqp, rxa->midbuff, rxa->midbuff); @@ -1225,22 +1225,20 @@ void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) } } -void RXA::SetAMDRun(RXA& rxa, int run) +void RXA::SetAMDRun(RXA& rxa, int _run) { - AMD *a = rxa.amd; - - if (run != run) + if (rxa.amd->run != _run) { RXA::bp1Check ( rxa, - run, + _run, rxa.snba->run, rxa.emnr->run, rxa.anf->run, rxa.anr->run ); - run = run; + rxa.amd->run = _run; RXA::bp1Set (rxa); } } @@ -1265,9 +1263,9 @@ void RXA::SetNC (RXA& rxa, int nc) rxa.bpsnba->SetNC (nc); BANDPASS::SetBandpassNC (rxa, nc); EQP::SetEQNC (rxa, nc); - FMSQ::SetFMSQNC (rxa, nc); - FMD::SetFMNCde (rxa, nc); - FMD::SetFMNCaud (rxa, nc); + rxa.fmsq->setNC (nc); + rxa.fmd->setNCde (nc); + rxa.fmd->setNCaud (nc); rxa.state = oldstate; } @@ -1277,9 +1275,9 @@ void RXA::SetMP (RXA& rxa, int mp) rxa.bpsnba->SetMP (mp); BANDPASS::SetBandpassMP (rxa, mp); EQP::SetEQMP (rxa, mp); - FMSQ::SetFMSQMP (rxa, mp); - FMD::SetFMMPde (rxa, mp); - FMD::SetFMMPaud (rxa, mp); + rxa.fmsq->setMP (mp); + rxa.fmd->setMPde (mp); + rxa.fmd->setMPaud (mp); } } // namespace WDSP diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index e82b1db49..449658a29 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -32,42 +32,41 @@ warren@wpratt.com #include "fir.hpp" #include "wcpAGC.hpp" #include "fmd.hpp" -#include "RXA.hpp" namespace WDSP { -void FMD::calc_fmd (FMD *a) +void FMD::calc() { // pll - a->omega_min = TWOPI * a->fmin / a->rate; - a->omega_max = TWOPI * a->fmax / a->rate; - a->g1 = 1.0 - exp(-2.0 * a->omegaN * a->zeta / a->rate); - a->g2 = -a->g1 + 2.0 * (1 - exp(-a->omegaN * a->zeta / a->rate) * cos(a->omegaN / a->rate * sqrt(1.0 - a->zeta * a->zeta))); - a->phs = 0.0; - a->fil_out = 0.0; - a->omega = 0.0; - a->pllpole = a->omegaN * sqrt(2.0 * a->zeta * a->zeta + 1.0 + sqrt((2.0 * a->zeta * a->zeta + 1.0) * (2.0 * a->zeta * a->zeta + 1.0) + 1)) / TWOPI; + omega_min = TWOPI * fmin / rate; + omega_max = TWOPI * fmax / rate; + g1 = 1.0 - exp(-2.0 * omegaN * zeta / rate); + g2 = -g1 + 2.0 * (1 - exp(-omegaN * zeta / rate) * cos(omegaN / rate * sqrt(1.0 - zeta * zeta))); + phs = 0.0; + fil_out = 0.0; + omega = 0.0; + pllpole = omegaN * sqrt(2.0 * zeta * zeta + 1.0 + sqrt((2.0 * zeta * zeta + 1.0) * (2.0 * zeta * zeta + 1.0) + 1)) / TWOPI; // dc removal - a->mtau = exp(-1.0 / (a->rate * a->tau)); - a->onem_mtau = 1.0 - a->mtau; - a->fmdc = 0.0; + mtau = exp(-1.0 / (rate * tau)); + onem_mtau = 1.0 - mtau; + fmdc = 0.0; // pll audio gain - a->again = a->rate / (a->deviation * TWOPI); + again = rate / (deviation * TWOPI); // CTCSS Removal - a->sntch = SNOTCH::create_snotch(1, a->size, a->out, a->out, (int)a->rate, a->ctcss_freq, 0.0002); + sntch = SNOTCH::create_snotch(1, size, out, out, (int)rate, ctcss_freq, 0.0002); // detector limiter - a->plim = WCPAGC::create_wcpagc ( + plim = WCPAGC::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 + out, // input buff pointer + out, // output buff pointer + size, // io_buffsize + (int)rate, // sample rate 0.001, // tau_attack 0.008, // tau_decay 4, // n_tau - a->lim_gain, // max_gain (sets threshold, initial value) + lim_gain, // max_gain (sets threshold, initial value) 1.0, // var_gain / slope 1.0, // fixed_gain 1.0, // max_input @@ -82,188 +81,185 @@ void FMD::calc_fmd (FMD *a) 0.100); // tau_hang_decay } -void FMD::decalc_fmd (FMD *a) +void FMD::decalc() { - WCPAGC::destroy_wcpagc(a->plim); - SNOTCH::destroy_snotch(a->sntch); + WCPAGC::destroy_wcpagc(plim); + SNOTCH::destroy_snotch(sntch); } -FMD* FMD::create_fmd( - int run, - int size, - float* in, - float* out, - int rate, - double deviation, - double f_low, - double f_high, - double fmin, - double fmax, - double zeta, - double omegaN, - double tau, - double afgain, - int sntch_run, - double ctcss_freq, - int nc_de, - int mp_de, - int nc_aud, - int mp_aud -) +FMD::FMD( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _deviation, + double _f_low, + double _f_high, + double _fmin, + double _fmax, + double _zeta, + double _omegaN, + double _tau, + double _afgain, + int _sntch_run, + double _ctcss_freq, + int _nc_de, + int _mp_de, + int _nc_aud, + int _mp_aud +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate((double) _rate), + f_low(_f_low), + f_high(_f_high), + fmin(_fmin), + fmax(_fmax), + zeta(_zeta), + omegaN(_omegaN), + tau(_tau), + deviation(_deviation), + nc_de(_nc_de), + mp_de(_mp_de), + nc_aud(_nc_aud), + mp_aud(_mp_aud), + afgain(_afgain), + sntch_run(_sntch_run), + ctcss_freq(_ctcss_freq), + lim_run(0), + lim_gain(0.0001), // 2.5 + lim_pre_gain(0.01) // 0.4 { - FMD *a = new FMD; float* impulse; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = (float)rate; - a->deviation = deviation; - a->f_low = f_low; - a->f_high = f_high; - a->fmin = fmin; - a->fmax = fmax; - a->zeta = zeta; - a->omegaN = omegaN; - a->tau = tau; - a->afgain = afgain; - a->sntch_run = sntch_run; - a->ctcss_freq = ctcss_freq; - a->nc_de = nc_de; - a->mp_de = mp_de; - a->nc_aud = nc_aud; - a->mp_aud = mp_aud; - a->lim_run = 0; - a->lim_pre_gain = 0.01; // 0.4 - a->lim_gain = 0.0001; // 2.5 - calc_fmd (a); + calc(); // de-emphasis filter - a->audio = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); - impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0); - a->pde = FIRCORE::create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse); + audio = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); + impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + pde = FIRCORE::create_fircore (size, audio, out, nc_de, mp_de, impulse); delete[] (impulse); // audio filter - impulse = FIR::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 = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse); + impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); delete[] (impulse); - return a; } -void FMD::destroy_fmd (FMD *a) +FMD::~FMD() { - FIRCORE::destroy_fircore (a->paud); - FIRCORE::destroy_fircore (a->pde); - delete[] (a->audio); - decalc_fmd (a); - delete (a); + FIRCORE::destroy_fircore (paud); + FIRCORE::destroy_fircore (pde); + delete[] (audio); + decalc(); } -void FMD::flush_fmd (FMD *a) +void FMD::flush() { - std::fill(a->audio, a->audio + a->size * 2, 0); - FIRCORE::flush_fircore (a->pde); - FIRCORE::flush_fircore (a->paud); - a->phs = 0.0; - a->fil_out = 0.0; - a->omega = 0.0; - a->fmdc = 0.0; - SNOTCH::flush_snotch (a->sntch); - WCPAGC::flush_wcpagc (a->plim); + std::fill(audio, audio + size * 2, 0); + FIRCORE::flush_fircore (pde); + FIRCORE::flush_fircore (paud); + phs = 0.0; + fil_out = 0.0; + omega = 0.0; + fmdc = 0.0; + SNOTCH::flush_snotch (sntch); + WCPAGC::flush_wcpagc (plim); } -void FMD::xfmd (FMD *a) +void FMD::execute() { - if (a->run) + if (run) { int i; double det, del_out; double vco[2], corr[2]; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { // pll - vco[0] = cos (a->phs); - vco[1] = sin (a->phs); - corr[0] = + a->in[2 * i + 0] * vco[0] + a->in[2 * i + 1] * vco[1]; - corr[1] = - a->in[2 * i + 0] * vco[1] + a->in[2 * i + 1] * vco[0]; + vco[0] = cos (phs); + vco[1] = sin (phs); + corr[0] = + in[2 * i + 0] * vco[0] + in[2 * i + 1] * vco[1]; + corr[1] = - in[2 * i + 0] * vco[1] + in[2 * i + 1] * vco[0]; if ((corr[0] == 0.0) && (corr[1] == 0.0)) corr[0] = 1.0; det = atan2 (corr[1], corr[0]); - del_out = a->fil_out; - a->omega += a->g2 * det; - if (a->omega < a->omega_min) a->omega = a->omega_min; - if (a->omega > a->omega_max) a->omega = a->omega_max; - a->fil_out = a->g1 * det + a->omega; - a->phs += del_out; - while (a->phs >= TWOPI) a->phs -= TWOPI; - while (a->phs < 0.0) a->phs += TWOPI; + del_out = fil_out; + omega += g2 * det; + if (omega < omega_min) omega = omega_min; + if (omega > omega_max) omega = omega_max; + fil_out = g1 * det + omega; + phs += del_out; + while (phs >= TWOPI) phs -= TWOPI; + while (phs < 0.0) phs += TWOPI; // dc removal, gain, & demod output - a->fmdc = a->mtau * a->fmdc + a->onem_mtau * a->fil_out; - a->audio[2 * i + 0] = a->again * (a->fil_out - a->fmdc); - a->audio[2 * i + 1] = a->audio[2 * i + 0]; + fmdc = mtau * fmdc + onem_mtau * fil_out; + audio[2 * i + 0] = again * (fil_out - fmdc); + audio[2 * i + 1] = audio[2 * i + 0]; } // de-emphasis - FIRCORE::xfircore (a->pde); + FIRCORE::xfircore (pde); // audio filter - FIRCORE::xfircore (a->paud); + FIRCORE::xfircore (paud); // CTCSS Removal - SNOTCH::xsnotch (a->sntch); - if (a->lim_run) + SNOTCH::xsnotch (sntch); + if (lim_run) { - for (i = 0; i < 2 * a->size; i++) - a->out[i] *= a->lim_pre_gain; - WCPAGC::xwcpagc (a->plim); + for (i = 0; i < 2 * size; i++) + out[i] *= lim_pre_gain; + WCPAGC::xwcpagc (plim); } } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void FMD::setBuffers_fmd (FMD *a, float* in, float* out) +void FMD::setBuffers(float* _in, float* _out) { - decalc_fmd (a); - a->in = in; - a->out = out; - calc_fmd (a); - FIRCORE::setBuffers_fircore (a->pde, a->audio, a->out); - FIRCORE::setBuffers_fircore (a->paud, a->out, a->out); - WCPAGC::setBuffers_wcpagc (a->plim, a->out, a->out); + decalc(); + in = _in; + out = _out; + calc(); + FIRCORE::setBuffers_fircore (pde, audio, out); + FIRCORE::setBuffers_fircore (paud, out, out); + WCPAGC::setBuffers_wcpagc (plim, out, out); } -void FMD::setSamplerate_fmd (FMD *a, int rate) +void FMD::setSamplerate(int _rate) { float* impulse; - decalc_fmd (a); - a->rate = rate; - calc_fmd (a); + decalc(); + rate = _rate; + calc(); // de-emphasis filter - impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setImpulse_fircore (a->pde, impulse, 1); + impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + FIRCORE::setImpulse_fircore (pde, impulse, 1); delete[] (impulse); // audio filter - impulse = FIR::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)); - FIRCORE::setImpulse_fircore (a->paud, impulse, 1); + impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + FIRCORE::setImpulse_fircore (paud, impulse, 1); delete[] (impulse); - WCPAGC::setSamplerate_wcpagc (a->plim, (int)a->rate); + WCPAGC::setSamplerate_wcpagc (plim, (int)rate); } -void FMD::setSize_fmd (FMD *a, int size) +void FMD::setSize(int _size) { float* impulse; - decalc_fmd (a); - delete[] (a->audio); - a->size = size; - calc_fmd (a); - a->audio = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); + decalc(); + delete[] (audio); + size = _size; + calc(); + audio = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); // de-emphasis filter - FIRCORE::destroy_fircore (a->pde); - impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0); - a->pde = FIRCORE::create_fircore (a->size, a->audio, a->out, a->nc_de, a->mp_de, impulse); + FIRCORE::destroy_fircore (pde); + impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + pde = FIRCORE::create_fircore (size, audio, out, nc_de, mp_de, impulse); delete[] (impulse); // audio filter - FIRCORE::destroy_fircore (a->paud); - impulse = FIR::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 = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc_aud, a->mp_aud, impulse); + FIRCORE::destroy_fircore (paud); + impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); delete[] (impulse); - WCPAGC::setSize_wcpagc (a->plim, a->size); + WCPAGC::setSize_wcpagc (plim, size); } /******************************************************************************************************** @@ -272,121 +268,102 @@ void FMD::setSize_fmd (FMD *a, int size) * * ********************************************************************************************************/ -void FMD::SetFMDeviation (RXA& rxa, double deviation) +void FMD::setDeviation(double _deviation) { - FMD *a; - a = rxa.fmd; - a->deviation = deviation; - a->again = a->rate / (a->deviation * TWOPI); + deviation = _deviation; + again = rate / (deviation * TWOPI); } -void FMD::SetCTCSSFreq (RXA& rxa, double freq) +void FMD::setCTCSSFreq(double freq) { - FMD *a; - a = rxa.fmd; - a->ctcss_freq = freq; - SNOTCH::SetSNCTCSSFreq (a->sntch, a->ctcss_freq); + ctcss_freq = freq; + SNOTCH::SetSNCTCSSFreq (sntch, ctcss_freq); } -void FMD::SetCTCSSRun (RXA& rxa, int run) +void FMD::setCTCSSRun(int run) { - FMD *a; - a = rxa.fmd; - a->sntch_run = run; - SNOTCH::SetSNCTCSSRun (a->sntch, a->sntch_run); + sntch_run = run; + SNOTCH::SetSNCTCSSRun (sntch, sntch_run); } -void FMD::SetFMNCde (RXA& rxa, int nc) +void FMD::setNCde(int nc) { - FMD *a; float* impulse; - a = rxa.fmd; - if (a->nc_de != nc) + if (nc_de != nc) { - a->nc_de = nc; - impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setNc_fircore (a->pde, a->nc_de, impulse); + nc_de = nc; + impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + FIRCORE::setNc_fircore (pde, nc_de, impulse); delete[] (impulse); } } -void FMD::SetFMMPde (RXA& rxa, int mp) +void FMD::setMPde(int mp) { - FMD *a; - a = rxa.fmd; - if (a->mp_de != mp) + if (mp_de != mp) { - a->mp_de = mp; - FIRCORE::setMp_fircore (a->pde, a->mp_de); + mp_de = mp; + FIRCORE::setMp_fircore (pde, mp_de); } } -void FMD::SetFMNCaud (RXA& rxa, int nc) +void FMD::setNCaud(int nc) { - FMD *a; float* impulse; - a = rxa.fmd; - if (a->nc_aud != nc) + if (nc_aud != nc) { - a->nc_aud = nc; - impulse = FIR::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)); - FIRCORE::setNc_fircore (a->paud, a->nc_aud, impulse); + nc_aud = nc; + impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + FIRCORE::setNc_fircore (paud, nc_aud, impulse); delete[] (impulse); } } -void FMD::SetFMMPaud (RXA& rxa, int mp) +void FMD::setMPaud(int mp) { - FMD *a; - a = rxa.fmd; - if (a->mp_aud != mp) + if (mp_aud != mp) { - a->mp_aud = mp; - FIRCORE::setMp_fircore (a->paud, a->mp_aud); + mp_aud = mp; + FIRCORE::setMp_fircore (paud, mp_aud); } } -void FMD::SetFMLimRun (RXA& rxa, int run) +void FMD::setLimRun(int run) { - FMD *a; - a = rxa.fmd; - - if (a->lim_run != run) { - a->lim_run = run; + if (lim_run != run) { + lim_run = run; } } -void FMD::SetFMLimGain (RXA& rxa, double gaindB) +void FMD::setLimGain(double gaindB) { double gain = pow(10.0, gaindB / 20.0); - FMD *a = rxa.fmd; - if (a->lim_gain != gain) + if (lim_gain != gain) { - decalc_fmd(a); - a->lim_gain = gain; - calc_fmd(a); + decalc(); + lim_gain = gain; + calc(); } } -void FMD::SetFMAFFilter(RXA& rxa, double low, double high) +void FMD::setAFFilter(double low, double high) { - FMD *a = rxa.fmd; float* impulse; - if (a->f_low != low || a->f_high != high) + if (f_low != low || f_high != high) { - a->f_low = low; - a->f_high = high; + f_low = low; + f_high = high; // de-emphasis filter - impulse = FCurve::fc_impulse (a->nc_de, a->f_low, a->f_high, +20.0 * log10(a->f_high / a->f_low), 0.0, 1, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setImpulse_fircore (a->pde, impulse, 1); + impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + FIRCORE::setImpulse_fircore (pde, impulse, 1); delete[] (impulse); // audio filter - impulse = FIR::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)); - FIRCORE::setImpulse_fircore (a->paud, impulse, 1); + impulse = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + FIRCORE::setImpulse_fircore (paud, impulse, 1); delete[] (impulse); } } diff --git a/wdsp/fmd.hpp b/wdsp/fmd.hpp index 8013973b1..3b47b045d 100644 --- a/wdsp/fmd.hpp +++ b/wdsp/fmd.hpp @@ -35,7 +35,6 @@ namespace WDSP { class FIRCORE; class SNOTCH; class WCPAGC; -class RXA; class WDSP_API FMD { @@ -87,7 +86,7 @@ public: double lim_gain; double lim_pre_gain; - static FMD* create_fmd ( + FMD( int run, int size, float* in, @@ -109,27 +108,28 @@ public: int nc_aud, int mp_aud ); - static void destroy_fmd (FMD *a); - static void flush_fmd (FMD *a); - static void xfmd (FMD *a); - static void setBuffers_fmd (FMD *a, float* in, float* out); - static void setSamplerate_fmd (FMD *a, int rate); - static void setSize_fmd (FMD *a, int size); + ~FMD(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void SetFMDeviation (RXA& rxa, double deviation); - static void SetCTCSSFreq (RXA& rxa, double freq); - static void SetCTCSSRun (RXA& rxa, int run); - static void SetFMNCde (RXA& rxa, int nc); - static void SetFMMPde (RXA& rxa, int mp); - static void SetFMNCaud (RXA& rxa, int nc); - static void SetFMMPaud (RXA& rxa, int mp); - static void SetFMLimRun (RXA& rxa, int run); - static void SetFMLimGain (RXA& rxa, double gaindB); - static void SetFMAFFilter(RXA& rxa, double low, double high); + void setDeviation(double deviation); + void setCTCSSFreq(double freq); + void setCTCSSRun(int run); + void setNCde(int nc); + void setMPde(int mp); + void setNCaud(int nc); + void setMPaud(int mp); + void setLimRun(int run); + void setLimGain(double gaindB); + void setAFFilter(double low, double high); private: - static void calc_fmd (FMD *a); - static void decalc_fmd (FMD *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 1b7c018c8..158c0021c 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -29,132 +29,128 @@ warren@wpratt.com #include "fircore.hpp" #include "eq.hpp" #include "fmsq.hpp" -#include "RXA.hpp" namespace WDSP { -void FMSQ::calc_fmsq (FMSQ *a) +void FMSQ::calc() { double delta, theta; float* impulse; int i; // noise filter - a->noise = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); - a->F[0] = 0.0; - a->F[1] = a->fc; - a->F[2] = *a->pllpole; - a->F[3] = 20000.0; - a->G[0] = 0.0; - a->G[1] = 0.0; - a->G[2] = 3.0; - a->G[3] = +20.0 * log10(20000.0 / *a->pllpole); - impulse = EQP::eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0); - a->p = FIRCORE::create_fircore (a->size, a->trigger, a->noise, a->nc, a->mp, impulse); + noise = new float[2 * size * 2]; // (float *)malloc0(2 * size * sizeof(complex)); + F[0] = 0.0; + F[1] = fc; + F[2] = *pllpole; + F[3] = 20000.0; + G[0] = 0.0; + G[1] = 0.0; + G[2] = 3.0; + G[3] = +20.0 * log10(20000.0 / *pllpole); + impulse = EQP::eq_impulse (nc, 3, F, G, rate, 1.0 / (2.0 * size), 0, 0); + p = FIRCORE::create_fircore (size, trigger, noise, nc, mp, impulse); delete[] (impulse); // noise averaging - a->avm = exp(-1.0 / (a->rate * a->avtau)); - a->onem_avm = 1.0 - a->avm; - a->avnoise = 100.0; - a->longavm = exp(-1.0 / (a->rate * a->longtau)); - a->onem_longavm = 1.0 - a->longavm; - a->longnoise = 1.0; + avm = exp(-1.0 / (rate * avtau)); + onem_avm = 1.0 - avm; + avnoise = 100.0; + longavm = exp(-1.0 / (rate * longtau)); + onem_longavm = 1.0 - longavm; + longnoise = 1.0; // level change - a->ntup = (int)(a->tup * a->rate); - a->ntdown = (int)(a->tdown * a->rate); - a->cup = new double[a->ntup + 1]; // (float *)malloc0 ((a->ntup + 1) * sizeof(float)); - a->cdown = new double[a->ntdown + 1]; //(float *)malloc0 ((a->ntdown + 1) * sizeof(float)); - delta = PI / (double) a->ntup; + ntup = (int)(tup * rate); + ntdown = (int)(tdown * rate); + cup = new double[ntup + 1]; // (float *)malloc0 ((ntup + 1) * sizeof(float)); + cdown = new double[ntdown + 1]; //(float *)malloc0 ((ntdown + 1) * sizeof(float)); + delta = PI / (double) ntup; theta = 0.0; - for (i = 0; i <= a->ntup; i++) + for (i = 0; i <= ntup; i++) { - a->cup[i] = 0.5 * (1.0 - cos(theta)); + cup[i] = 0.5 * (1.0 - cos(theta)); theta += delta; } - delta = PI / (double) a->ntdown; + delta = PI / (double) ntdown; theta = 0.0; - for (i = 0; i <= a->ntdown; i++) + for (i = 0; i <= ntdown; i++) { - a->cdown[i] = 0.5 * (1 + cos(theta)); + cdown[i] = 0.5 * (1 + cos(theta)); theta += delta; } // control - a->state = 0; - a->ready = 0; - a->ramp = 0.0; - a->rstep = 1.0 / a->rate; + state = 0; + ready = 0; + ramp = 0.0; + rstep = 1.0 / rate; } -void FMSQ::decalc_fmsq (FMSQ *a) +void FMSQ::decalc() { - delete[] (a->cdown); - delete[] (a->cup); - FIRCORE::destroy_fircore (a->p); - delete[] (a->noise); + delete[] (cdown); + delete[] (cup); + FIRCORE::destroy_fircore (p); + delete[] (noise); } -FMSQ* FMSQ::create_fmsq ( - int run, - int size, - float* insig, - float* outsig, - float* trigger, - int rate, - double fc, - double* pllpole, - double tdelay, - double avtau, - double longtau, - double tup, - double tdown, - double tail_thresh, - double unmute_thresh, - double min_tail, - double max_tail, - int nc, - int mp -) +FMSQ::FMSQ( + int _run, + int _size, + float* _insig, + float* _outsig, + float* _trigger, + int _rate, + double _fc, + double* _pllpole, + double _tdelay, + double _avtau, + double _longtau, + double _tup, + double _tdown, + double _tail_thresh, + double _unmute_thresh, + double _min_tail, + double _max_tail, + int _nc, + int _mp +) : + run(_run), + size(_size), + insig(_insig), + outsig(_outsig), + trigger(_trigger), + rate((double) _rate), + fc(_fc), + pllpole(_pllpole), + tdelay(_tdelay), + avtau(_avtau), + longtau(_longtau), + tup(_tup), + tdown(_tdown), + tail_thresh(_tail_thresh), + unmute_thresh(_unmute_thresh), + min_tail(_min_tail), + max_tail(_max_tail), + nc(_nc), + mp(_mp) { - FMSQ *a = new FMSQ; - a->run = run; - a->size = size; - a->insig = insig; - a->outsig = outsig; - a->trigger = trigger; - a->rate = (float)rate; - a->fc = fc; - a->pllpole = pllpole; - a->tdelay = tdelay; - a->avtau = avtau; - a->longtau = longtau; - a->tup = tup; - a->tdown = tdown; - a->tail_thresh = tail_thresh; - a->unmute_thresh = unmute_thresh; - a->min_tail = min_tail; - a->max_tail = max_tail; - a->nc = nc; - a->mp = mp; - calc_fmsq (a); - return a; + calc(); } -void FMSQ::destroy_fmsq (FMSQ *a) +FMSQ::~FMSQ() { - decalc_fmsq (a); - delete (a); + decalc(); } -void FMSQ::flush_fmsq (FMSQ *a) +void FMSQ::flush() { - FIRCORE::flush_fircore (a->p); - a->avnoise = 100.0; - a->longnoise = 1.0; - a->state = 0; - a->ready = 0; - a->ramp = 0.0; + FIRCORE::flush_fircore (p); + avnoise = 100.0; + longnoise = 1.0; + state = 0; + ready = 0; + ramp = 0.0; } enum _fmsqstate @@ -166,120 +162,120 @@ enum _fmsqstate DECREASE }; -void FMSQ::xfmsq (FMSQ *a) +void FMSQ::execute() { - if (a->run) + if (run) { int i; - double noise, lnlimit; - FIRCORE::xfircore (a->p); + double _noise, lnlimit; + FIRCORE::xfircore (p); - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - double noise0 = a->noise[2 * i + 0]; - double noise1 = a->noise[2 * i + 1]; - noise = sqrt(noise0 * noise0 + noise1 * noise1); - a->avnoise = a->avm * a->avnoise + a->onem_avm * noise; - a->longnoise = a->longavm * a->longnoise + a->onem_longavm * noise; + double noise0 = noise[2 * i + 0]; + double noise1 = noise[2 * i + 1]; + _noise = sqrt(noise0 * noise0 + noise1 * noise1); + avnoise = avm * avnoise + onem_avm * _noise; + longnoise = longavm * longnoise + onem_longavm * _noise; - if (!a->ready) - a->ramp += a->rstep; + if (!ready) + ramp += rstep; - if (a->ramp >= a->tdelay) - a->ready = 1; + if (ramp >= tdelay) + ready = 1; - switch (a->state) + switch (state) { case MUTED: - if (a->avnoise < a->unmute_thresh && a->ready) + if (avnoise < unmute_thresh && ready) { - a->state = INCREASE; - a->count = a->ntup; + state = INCREASE; + count = ntup; } - a->outsig[2 * i + 0] = 0.0; - a->outsig[2 * i + 1] = 0.0; + outsig[2 * i + 0] = 0.0; + outsig[2 * i + 1] = 0.0; break; case INCREASE: - a->outsig[2 * i + 0] = a->insig[2 * i + 0] * a->cup[a->ntup - a->count]; - a->outsig[2 * i + 1] = a->insig[2 * i + 1] * a->cup[a->ntup - a->count]; + outsig[2 * i + 0] = insig[2 * i + 0] * cup[ntup - count]; + outsig[2 * i + 1] = insig[2 * i + 1] * cup[ntup - count]; - if (a->count-- == 0) - a->state = UNMUTED; + if (count-- == 0) + state = UNMUTED; break; case UNMUTED: - if (a->avnoise > a->tail_thresh) + if (avnoise > tail_thresh) { - a->state = TAIL; + state = TAIL; - if ((lnlimit = a->longnoise) > 1.0) + if ((lnlimit = longnoise) > 1.0) lnlimit = 1.0; - a->count = (int)((a->min_tail + (a->max_tail - a->min_tail) * lnlimit) * a->rate); + count = (int)((min_tail + (max_tail - min_tail) * lnlimit) * rate); } - a->outsig[2 * i + 0] = a->insig[2 * i + 0]; - a->outsig[2 * i + 1] = a->insig[2 * i + 1]; + outsig[2 * i + 0] = insig[2 * i + 0]; + outsig[2 * i + 1] = insig[2 * i + 1]; break; case TAIL: - a->outsig[2 * i + 0] = a->insig[2 * i + 0]; - a->outsig[2 * i + 1] = a->insig[2 * i + 1]; + outsig[2 * i + 0] = insig[2 * i + 0]; + outsig[2 * i + 1] = insig[2 * i + 1]; - if (a->avnoise < a->unmute_thresh) + if (avnoise < unmute_thresh) { - a->state = UNMUTED; + state = UNMUTED; } - else if (a->count-- == 0) + else if (count-- == 0) { - a->state = DECREASE; - a->count = a->ntdown; + state = DECREASE; + count = ntdown; } break; case DECREASE: - a->outsig[2 * i + 0] = a->insig[2 * i + 0] * a->cdown[a->ntdown - a->count]; - a->outsig[2 * i + 1] = a->insig[2 * i + 1] * a->cdown[a->ntdown - a->count]; + outsig[2 * i + 0] = insig[2 * i + 0] * cdown[ntdown - count]; + outsig[2 * i + 1] = insig[2 * i + 1] * cdown[ntdown - count]; - if (a->count-- == 0) - a->state = MUTED; + if (count-- == 0) + state = MUTED; break; } } } - else if (a->insig != a->outsig) + else if (insig != outsig) { - std::copy(a->insig, a->insig + a->size * 2, a->outsig); + std::copy(insig, insig + size * 2, outsig); } } -void FMSQ::setBuffers_fmsq (FMSQ *a, float* in, float* out, float* trig) +void FMSQ::setBuffers(float* in, float* out, float* trig) { - a->insig = in; - a->outsig = out; - a->trigger = trig; - FIRCORE::setBuffers_fircore (a->p, a->trigger, a->noise); + insig = in; + outsig = out; + trigger = trig; + FIRCORE::setBuffers_fircore (p, trigger, noise); } -void FMSQ::setSamplerate_fmsq (FMSQ *a, int rate) +void FMSQ::setSamplerate(int _rate) { - decalc_fmsq (a); - a->rate = rate; - calc_fmsq (a); + decalc(); + rate = _rate; + calc(); } -void FMSQ::setSize_fmsq (FMSQ *a, int size) +void FMSQ::setSize(int _size) { - decalc_fmsq (a); - a->size = size; - calc_fmsq (a); + decalc(); + size = _size; + calc(); } /******************************************************************************************************** @@ -288,41 +284,36 @@ void FMSQ::setSize_fmsq (FMSQ *a, int size) * * ********************************************************************************************************/ -void FMSQ::SetFMSQRun (RXA& rxa, int run) +void FMSQ::setRun(int _run) { - rxa.fmsq->run = run; + run = _run; } -void FMSQ::SetFMSQThreshold (RXA& rxa, double threshold) +void FMSQ::setThreshold(double threshold) { - rxa.fmsq->tail_thresh = threshold; - rxa.fmsq->unmute_thresh = 0.9 * threshold; + tail_thresh = threshold; + unmute_thresh = 0.9 * threshold; } -void FMSQ::SetFMSQNC (RXA& rxa, int nc) +void FMSQ::setNC(int _nc) { - FMSQ *a; float* impulse; - a = rxa.fmsq; - if (a->nc != nc) + if (nc != _nc) { - a->nc = nc; - impulse = EQP::eq_impulse (a->nc, 3, a->F, a->G, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); + nc = _nc; + impulse = EQP::eq_impulse (nc, 3, F, G, rate, 1.0 / (2.0 * size), 0, 0); + FIRCORE::setNc_fircore (p, nc, impulse); delete[] (impulse); } } -void FMSQ::SetFMSQMP (RXA& rxa, int mp) +void FMSQ::setMP(int _mp) { - FMSQ *a; - a = rxa.fmsq; - - if (a->mp != mp) + if (mp != _mp) { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); + mp = _mp; + FIRCORE::setMp_fircore (p, mp); } } diff --git a/wdsp/fmsq.hpp b/wdsp/fmsq.hpp index d5baafc07..97f47573f 100644 --- a/wdsp/fmsq.hpp +++ b/wdsp/fmsq.hpp @@ -33,7 +33,6 @@ warren@wpratt.com namespace WDSP { class FIRCORE; -class RXA; class WDSP_API FMSQ { @@ -77,42 +76,43 @@ public: int mp; FIRCORE *p; - static FMSQ* create_fmsq ( - int run, - int size, - float* insig, - float* outsig, - float* trigger, - int rate, - double fc, - double* pllpole, - double tdelay, - double avtau, - double longtau, - double tup, - double tdown, - double tail_thresh, - double unmute_thresh, - double min_tail, - double max_tail, - int nc, - int mp + FMSQ( + int _run, + int _size, + float* _insig, + float* _outsig, + float* _trigger, + int _rate, + double _fc, + double* _pllpole, + double _tdelay, + double _avtau, + double _longtau, + double _tup, + double _tdown, + double _tail_thresh, + double _unmute_thresh, + double _min_tail, + double _max_tail, + int _nc, + int _mp ); - static void destroy_fmsq (FMSQ *a); - static void flush_fmsq (FMSQ *a); - static void xfmsq (FMSQ *a); - static void setBuffers_fmsq (FMSQ *a, float* in, float* out, float* trig); - static void setSamplerate_fmsq (FMSQ *a, int rate); - static void setSize_fmsq (FMSQ *a, int size); - // RXA Properties - static void SetFMSQRun (RXA& rxa, int run); - static void SetFMSQThreshold (RXA& rxa, double threshold); - static void SetFMSQNC (RXA& rxa, int nc); - static void SetFMSQMP (RXA& rxa, int mp); + ~FMSQ(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out, float* trig); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setRun(int run); + void setThreshold(double threshold); + void setNC(int nc); + void setMP(int mp); private: - static void calc_fmsq (FMSQ *a); - static void decalc_fmsq (FMSQ *a); + void calc(); + void decalc(); }; } // namespace WDSP From d3cbfe0e3cd989deb089b2f200c3066f51fd3087 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 27 Jul 2024 05:32:45 +0200 Subject: [PATCH 15/46] WDSP: SNBA and EQ: replaced static methods and more... --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 6 +- wdsp/CMakeLists.txt | 4 +- wdsp/RXA.cpp | 57 ++- wdsp/RXA.hpp | 2 + wdsp/TXA.cpp | 556 +++++++++++----------- wdsp/TXA.hpp | 110 ++--- wdsp/ammod.cpp | 4 +- wdsp/bandpass.cpp | 18 +- wdsp/bps.cpp | 14 +- wdsp/cfcomp.cpp | 14 +- wdsp/cfir.cpp | 4 +- wdsp/compress.cpp | 6 +- wdsp/emph.cpp | 10 +- wdsp/eq.cpp | 540 +-------------------- wdsp/eq.hpp | 87 +--- wdsp/eqp.cpp | 399 ++++++++++++++++ wdsp/eqp.hpp | 101 ++++ wdsp/fmmod.cpp | 12 +- wdsp/fmsq.cpp | 4 +- wdsp/gen.cpp | 86 ++-- wdsp/iir.cpp | 8 +- wdsp/osctrl.cpp | 4 +- wdsp/patchpanel.cpp | 10 +- wdsp/siphon.cpp | 14 +- wdsp/slew.cpp | 2 +- wdsp/snba.cpp | 595 ++++++++++++------------ wdsp/snba.hpp | 52 +-- wdsp/wcpAGC.cpp | 36 +- 28 files changed, 1321 insertions(+), 1434 deletions(-) create mode 100644 wdsp/eqp.cpp create mode 100644 wdsp/eqp.hpp diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index d9a36d7fb..389673e37 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -565,7 +565,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // Caution: Causes corruption if ((m_settings.m_snb != settings.m_snb) || force) { - WDSP::SNBA::SetSNBARun(*m_rxa, settings.m_snb ? 1 : 0); + WDSP::RXA::SetSNBARun(*m_rxa, settings.m_snb ? 1 : 0); } // CW Peaking @@ -731,13 +731,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // Equalizer if ((m_settings.m_equalizer != settings.m_equalizer) || force) { - WDSP::EQP::SetEQRun(*m_rxa, settings.m_equalizer ? 1 : 0); + m_rxa->eqp->setRun(settings.m_equalizer ? 1 : 0); } if ((m_settings.m_eqF != settings.m_eqF) || (m_settings.m_eqG != settings.m_eqG) || force) { - WDSP::EQP::SetEQProfile(*m_rxa, 10, settings.m_eqF.data(), settings.m_eqG.data()); + m_rxa->eqp->setProfile(10, settings.m_eqF.data(), settings.m_eqG.data()); } // Audio panel diff --git a/wdsp/CMakeLists.txt b/wdsp/CMakeLists.txt index 02e2909ba..abb56e609 100644 --- a/wdsp/CMakeLists.txt +++ b/wdsp/CMakeLists.txt @@ -20,7 +20,7 @@ set(wdsp_SOURCES delay.cpp emnr.cpp emph.cpp - eq.cpp + eqp.cpp fcurve.cpp fir.cpp fircore.cpp @@ -77,7 +77,7 @@ set(wdsp_HEADERS delay.hpp emnr.hpp emph.hpp - eq.hpp + eqp.hpp fcurve.hpp fir.hpp fircore.hpp diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 81095fa12..9d0a6f60a 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -41,7 +41,7 @@ warren@wpratt.com #include "amsq.hpp" #include "fmd.hpp" #include "fmsq.hpp" -#include "eq.hpp" +#include "eqp.hpp" #include "anf.hpp" #include "anr.hpp" #include "emnr.hpp" @@ -306,7 +306,7 @@ RXA* RXA::create_rxa ( 0); // minimum phase flag // Spectral noise blanker (SNB) - rxa->snba = SNBA::create_snba ( + rxa->snba = new SNBA( 0, // run rxa->midbuff, // input buffer rxa->midbuff, // output buffer @@ -331,7 +331,7 @@ RXA* RXA::create_rxa ( float default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; //float default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; float default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - rxa->eqp = EQP::create_eqp ( + rxa->eqp = new EQP( 0, // run - OFF by default rxa->dsp_size, // buffer size std::max(2048, rxa->dsp_size), // number of filter coefficients @@ -576,8 +576,8 @@ void RXA::destroy_rxa (RXA *rxa) EMNR::destroy_emnr (rxa->emnr); ANR::destroy_anr (rxa->anr); ANF::destroy_anf (rxa->anf); - EQP::destroy_eqp (rxa->eqp); - SNBA::destroy_snba (rxa->snba); + delete (rxa->eqp); + delete (rxa->snba); delete (rxa->fmsq); delete (rxa->fmd); delete (rxa->amd); @@ -616,8 +616,8 @@ void RXA::flush_rxa (RXA *rxa) rxa->amd->flush(); rxa->fmd->flush(); rxa->fmsq->flush(); - SNBA::flush_snba (rxa->snba); - EQP::flush_eqp (rxa->eqp); + rxa->snba->flush(); + rxa->eqp->flush(); ANF::flush_anf (rxa->anf); ANR::flush_anr (rxa->anr); EMNR::flush_emnr (rxa->emnr); @@ -651,8 +651,8 @@ void RXA::xrxa (RXA *rxa) rxa->fmsq->execute(); rxa->bpsnba->exec_in(1); rxa->bpsnba->exec_out(1); - SNBA::xsnba (rxa->snba); - EQP::xeqp (rxa->eqp); + rxa->snba->execute(); + rxa->eqp->execute(); ANF::xanf (rxa->anf, 0); ANR::xanr (rxa->anr, 0); EMNR::xemnr (rxa->emnr, 0); @@ -762,8 +762,8 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->fmd->setSamplerate(rxa->dsp_rate); rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); rxa->fmsq->setSamplerate(rxa->dsp_rate); - SNBA::setSamplerate_snba (rxa->snba, rxa->dsp_rate); - EQP::setSamplerate_eqp (rxa->eqp, rxa->dsp_rate); + rxa->snba->setSamplerate(rxa->dsp_rate); + rxa->eqp->setSamplerate(rxa->dsp_rate); ANF::setSamplerate_anf (rxa->anf, rxa->dsp_rate); ANR::setSamplerate_anr (rxa->anr, rxa->dsp_rate); EMNR::setSamplerate_emnr (rxa->emnr, rxa->dsp_rate); @@ -833,10 +833,10 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->fmd->setSize(rxa->dsp_size); rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); rxa->fmsq->setSize(rxa->dsp_size); - SNBA::setBuffers_snba (rxa->snba, rxa->midbuff, rxa->midbuff); - SNBA::setSize_snba (rxa->snba, rxa->dsp_size); - EQP::setBuffers_eqp (rxa->eqp, rxa->midbuff, rxa->midbuff); - EQP::setSize_eqp (rxa->eqp, rxa->dsp_size); + rxa->snba->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->snba->setSize(rxa->dsp_size); + rxa->eqp->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->eqp->setSize(rxa->dsp_size); ANF::setBuffers_anf (rxa->anf, rxa->midbuff, rxa->midbuff); ANF::setSize_anf (rxa->anf, rxa->dsp_size); ANR::setBuffers_anr (rxa->anr, rxa->midbuff, rxa->midbuff); @@ -1243,6 +1243,27 @@ void RXA::SetAMDRun(RXA& rxa, int _run) } } +void RXA::SetSNBARun (RXA& rxa, int run) +{ + SNBA *a = rxa.snba; + + if (a->run != run) + { + RXA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb->master_run); + RXA::bp1Check ( + rxa, + rxa.amd->run, + run, + rxa.emnr->run, + rxa.anf->run, + rxa.anr->run + ); + a->run = run; + RXA::bp1Set (rxa); + RXA::bpsnbaSet (rxa); + } +} + /******************************************************************************************************** * * * Collectives * @@ -1252,7 +1273,7 @@ void RXA::SetAMDRun(RXA& rxa, int _run) void RXA::SetPassband (RXA& rxa, float f_low, float f_high) { BANDPASS::SetBandpassFreqs (rxa, f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) - SNBA::SetSNBAOutputBandwidth (rxa, f_low, f_high); // Spectral noise blanker (SNB) + rxa.snba->setOutputBandwidth (f_low, f_high); // Spectral noise blanker (SNB) rxa.nbp0->SetFreqs (f_low, f_high); // Notched bandpass } @@ -1262,7 +1283,7 @@ void RXA::SetNC (RXA& rxa, int nc) rxa.nbp0->SetNC (nc); rxa.bpsnba->SetNC (nc); BANDPASS::SetBandpassNC (rxa, nc); - EQP::SetEQNC (rxa, nc); + rxa.eqp->setNC (nc); rxa.fmsq->setNC (nc); rxa.fmd->setNCde (nc); rxa.fmd->setNCaud (nc); @@ -1274,7 +1295,7 @@ void RXA::SetMP (RXA& rxa, int mp) rxa.nbp0->SetMP (mp); rxa.bpsnba->SetMP (mp); BANDPASS::SetBandpassMP (rxa, mp); - EQP::SetEQMP (rxa, mp); + rxa.eqp->setMP (mp); rxa.fmsq->setMP (mp); rxa.fmd->setMPde (mp); rxa.fmd->setMPaud (mp); diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index ebc93ddf0..2fe18254a 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -169,6 +169,8 @@ public: static void NBPSetAutoIncrease (RXA& rxa, int autoincr); // AMD static void SetAMDRun(RXA& rxa, int run); + // SNBA + static void SetSNBARun (RXA& rxa, int run); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index d58e1c430..61243c5e3 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -82,7 +82,7 @@ TXA* TXA::create_txa ( txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *) malloc0 (1 * txa->dsp_outsize * sizeof (complex)); txa->midbuff = new float[3 * txa->dsp_size * 2]; //(float *) malloc0 (2 * txa->dsp_size * sizeof (complex)); - txa->rsmpin.p = new RESAMPLE( + txa->rsmpin = new RESAMPLE( 0, // run - will be turned on below if needed txa->dsp_insize, // input buffer size txa->inbuff, // pointer to input buffer @@ -93,7 +93,7 @@ TXA* TXA::create_txa ( 0, // select ncoef automatically 1.0); // gain - txa->gen0.p = new GEN( + txa->gen0 = new GEN( 0, // run txa->dsp_size, // buffer size txa->midbuff, // input buffer @@ -101,7 +101,7 @@ TXA* TXA::create_txa ( txa->dsp_rate, // sample rate 2); // mode - txa->panel.p = PANEL::create_panel ( + txa->panel = PANEL::create_panel ( 1, // run txa->dsp_size, // size txa->midbuff, // pointer to input buffer @@ -112,7 +112,7 @@ TXA* TXA::create_txa ( 2, // 1 to use Q, 2 to use I for input 0); // 0, no copy - txa->phrot.p = PHROT::create_phrot ( + txa->phrot = PHROT::create_phrot ( 0, // run txa->dsp_size, // size txa->midbuff, // input buffer @@ -121,7 +121,7 @@ TXA* TXA::create_txa ( 338.0, // 1/2 of phase frequency 8); // number of stages - txa->micmeter.p = new METER( + txa->micmeter = new METER( 1, // run 0, // optional pointer to another 'run' txa->dsp_size, // size @@ -135,7 +135,7 @@ TXA* TXA::create_txa ( -1, // index for gain value 0); // pointer for gain computation - txa->amsq.p = new AMSQ( + txa->amsq = new AMSQ( 0, // run txa->dsp_size, // size txa->midbuff, // input buffer @@ -155,7 +155,7 @@ TXA* TXA::create_txa ( float default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; float default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; //float default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - txa->eqp.p = EQP::create_eqp ( + txa->eqp = new EQP ( 0, // run - OFF by default txa->dsp_size, // size std::max(2048, txa->dsp_size), // number of filter coefficients @@ -170,9 +170,9 @@ TXA* TXA::create_txa ( txa->dsp_rate); // samplerate } - txa->eqmeter.p = new METER( + txa->eqmeter = new METER( 1, // run - &(txa->eqp.p->run), // pointer to eqp 'run' + &(txa->eqp->run), // pointer to eqp 'run' txa->dsp_size, // size txa->midbuff, // pointer to buffer txa->dsp_rate, // samplerate @@ -184,7 +184,7 @@ TXA* TXA::create_txa ( -1, // index for gain value 0); // pointer for gain computation - txa->preemph.p = EMPHP::create_emphp ( + txa->preemph = EMPHP::create_emphp ( 0, // run 1, // position txa->dsp_size, // size @@ -197,7 +197,7 @@ TXA* TXA::create_txa ( 300.0, // f_low 3000.0); // f_high - txa->leveler.p = WCPAGC::create_wcpagc ( + txa->leveler = WCPAGC::create_wcpagc ( 0, // run - OFF by default 5, // mode 0, // 0 for max(I,Q), 1 for envelope @@ -222,9 +222,9 @@ TXA* TXA::create_txa ( 2.000, // hang_thresh 0.100); // tau_hang_decay - txa->lvlrmeter.p = new METER( + txa->lvlrmeter = new METER( 1, // run - &(txa->leveler.p->run), // pointer to leveler 'run' + &(txa->leveler->run), // pointer to leveler 'run' txa->dsp_size, // size txa->midbuff, // pointer to buffer txa->dsp_rate, // samplerate @@ -234,13 +234,13 @@ TXA* TXA::create_txa ( TXA_LVLR_AV, // index for average value TXA_LVLR_PK, // index for peak value TXA_LVLR_GAIN, // index for gain value - &txa->leveler.p->gain); // pointer for gain computation + &txa->leveler->gain); // pointer for gain computation { float default_F[5] = {200.0, 1000.0, 2000.0, 3000.0, 4000.0}; float default_G[5] = {0.0, 5.0, 10.0, 10.0, 5.0}; float default_E[5] = {7.0, 7.0, 7.0, 7.0, 7.0}; - txa->cfcomp.p = CFCOMP::create_cfcomp( + txa->cfcomp = CFCOMP::create_cfcomp( 0, // run 0, // position 0, // post-equalizer run @@ -262,9 +262,9 @@ TXA* TXA::create_txa ( 0.50); // display time constant } - txa->cfcmeter.p = new METER( + txa->cfcmeter = new METER( 1, // run - &(txa->cfcomp.p->run), // pointer to eqp 'run' + &(txa->cfcomp->run), // pointer to eqp 'run' txa->dsp_size, // size txa->midbuff, // pointer to buffer txa->dsp_rate, // samplerate @@ -274,9 +274,9 @@ TXA* TXA::create_txa ( TXA_CFC_AV, // index for average value TXA_CFC_PK, // index for peak value TXA_CFC_GAIN, // index for gain value - (double*) &txa->cfcomp.p->gain); // pointer for gain computation + (double*) &txa->cfcomp->gain); // pointer for gain computation - txa->bp0.p = BANDPASS::create_bandpass ( + txa->bp0 = BANDPASS::create_bandpass ( 1, // always runs 0, // position txa->dsp_size, // size @@ -290,14 +290,14 @@ TXA* TXA::create_txa ( 1, // wintype 2.0); // gain - txa->compressor.p = COMPRESSOR::create_compressor ( + txa->compressor = COMPRESSOR::create_compressor ( 0, // run - OFF by default txa->dsp_size, // size txa->midbuff, // pointer to input buffer txa->midbuff, // pointer to output buffer 3.0); // gain - txa->bp1.p = BANDPASS::create_bandpass ( + txa->bp1 = BANDPASS::create_bandpass ( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position txa->dsp_size, // size @@ -311,7 +311,7 @@ TXA* TXA::create_txa ( 1, // wintype 2.0); // gain - txa->osctrl.p = OSCTRL::create_osctrl ( + txa->osctrl = OSCTRL::create_osctrl ( 0, // run txa->dsp_size, // size txa->midbuff, // input buffer @@ -319,7 +319,7 @@ TXA* TXA::create_txa ( txa->dsp_rate, // sample rate 1.95); // gain for clippings - txa->bp2.p = BANDPASS::create_bandpass ( + txa->bp2 = BANDPASS::create_bandpass ( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position txa->dsp_size, // size @@ -333,9 +333,9 @@ TXA* TXA::create_txa ( 1, // wintype 1.0); // gain - txa->compmeter.p = new METER( + txa->compmeter = new METER( 1, // run - &(txa->compressor.p->run), // pointer to compressor 'run' + &(txa->compressor->run), // pointer to compressor 'run' txa->dsp_size, // size txa->midbuff, // pointer to buffer txa->dsp_rate, // samplerate @@ -347,7 +347,7 @@ TXA* TXA::create_txa ( -1, // index for gain value 0); // pointer for gain computation - txa->alc.p = WCPAGC::create_wcpagc ( + txa->alc = WCPAGC::create_wcpagc ( 1, // run - always ON 5, // mode 1, // 0 for max(I,Q), 1 for envelope @@ -372,7 +372,7 @@ TXA* TXA::create_txa ( 2.000, // hang_thresh 0.100); // tau_hang_decay - txa->ammod.p = AMMOD::create_ammod ( + txa->ammod = AMMOD::create_ammod ( 0, // run - OFF by default 0, // mode: 0=>AM, 1=>DSB txa->dsp_size, // size @@ -381,7 +381,7 @@ TXA* TXA::create_txa ( 0.5); // carrier level - txa->fmmod.p = FMMOD::create_fmmod ( + txa->fmmod = FMMOD::create_fmmod ( 0, // run - OFF by default txa->dsp_size, // size txa->midbuff, // pointer to input buffer @@ -397,7 +397,7 @@ TXA* TXA::create_txa ( std::max(2048, txa->dsp_size), // number coefficients for bandpass filter 0); // minimum phase flag - txa->gen1.p = new GEN( + txa->gen1 = new GEN( 0, // run txa->dsp_size, // buffer size txa->midbuff, // input buffer @@ -405,7 +405,7 @@ TXA* TXA::create_txa ( txa->dsp_rate, // sample rate 0); // mode - txa->uslew.p = USLEW::create_uslew ( + txa->uslew = USLEW::create_uslew ( txa, &(txa->upslew), // pointer to channel upslew flag txa->dsp_size, // buffer size @@ -415,7 +415,7 @@ TXA* TXA::create_txa ( 0.000, // delay time 0.005); // upslew time - txa->alcmeter.p = new METER( + txa->alcmeter = new METER( 1, // run 0, // optional pointer to a 'run' txa->dsp_size, // size @@ -427,9 +427,9 @@ TXA* TXA::create_txa ( TXA_ALC_AV, // index for average value TXA_ALC_PK, // index for peak value TXA_ALC_GAIN, // index for gain value - &txa->alc.p->gain); // pointer for gain computation + &txa->alc->gain); // pointer for gain computation - txa->sip1.p = SIPHON::create_siphon ( + txa->sip1 = SIPHON::create_siphon ( 1, // run 0, // position 0, // mode @@ -440,7 +440,7 @@ TXA* TXA::create_txa ( 16384, // fft size for spectrum 1); // specmode - // txa->calcc.p = create_calcc ( + // txa->calcc = create_calcc ( // channel, // channel number // 1, // run calibration // 1024, // input buffer size @@ -469,7 +469,7 @@ TXA* TXA::create_txa ( 0.005, // changeover time 256); // spi - txa->cfir.p = CFIR::create_cfir( + txa->cfir = CFIR::create_cfir( 0, // run txa->dsp_size, // size std::max(2048, txa->dsp_size), // number of filter coefficients @@ -486,7 +486,7 @@ TXA* TXA::create_txa ( 0.0, // raised-cosine transition width 0); // window type - txa->rsmpout.p = new RESAMPLE( + txa->rsmpout = new RESAMPLE( 0, // run - will be turned ON below if needed txa->dsp_size, // input size txa->midbuff, // pointer to input buffer @@ -497,7 +497,7 @@ TXA* TXA::create_txa ( 0, // select ncoef automatically 0.980); // gain - txa->outmeter.p = new METER( + txa->outmeter = new METER( 1, // run 0, // optional pointer to another 'run' txa->dsp_outsize, // size @@ -519,37 +519,37 @@ TXA* TXA::create_txa ( void TXA::destroy_txa (TXA *txa) { // in reverse order, free each item we created - delete (txa->outmeter.p); - delete (txa->rsmpout.p); - CFIR::destroy_cfir(txa->cfir.p); - // destroy_calcc (txa->calcc.p); + delete (txa->outmeter); + delete (txa->rsmpout); + CFIR::destroy_cfir(txa->cfir); + // destroy_calcc (txa->calcc); IQC::destroy_iqc (txa->iqc.p0); - SIPHON::destroy_siphon (txa->sip1.p); - delete (txa->alcmeter.p); - USLEW::destroy_uslew (txa->uslew.p); - delete (txa->gen1.p); - FMMOD::destroy_fmmod (txa->fmmod.p); - AMMOD::destroy_ammod (txa->ammod.p); - WCPAGC::destroy_wcpagc (txa->alc.p); - delete (txa->compmeter.p); - BANDPASS::destroy_bandpass (txa->bp2.p); - OSCTRL::destroy_osctrl (txa->osctrl.p); - BANDPASS::destroy_bandpass (txa->bp1.p); - COMPRESSOR::destroy_compressor (txa->compressor.p); - BANDPASS::destroy_bandpass (txa->bp0.p); - delete (txa->cfcmeter.p); - CFCOMP::destroy_cfcomp (txa->cfcomp.p); - delete (txa->lvlrmeter.p); - WCPAGC::destroy_wcpagc (txa->leveler.p); - EMPHP::destroy_emphp (txa->preemph.p); - delete (txa->eqmeter.p); - EQP::destroy_eqp (txa->eqp.p); - delete (txa->amsq.p); - delete (txa->micmeter.p); - PHROT::destroy_phrot (txa->phrot.p); - PANEL::destroy_panel (txa->panel.p); - delete (txa->gen0.p); - delete (txa->rsmpin.p); + SIPHON::destroy_siphon (txa->sip1); + delete (txa->alcmeter); + USLEW::destroy_uslew (txa->uslew); + delete (txa->gen1); + FMMOD::destroy_fmmod (txa->fmmod); + AMMOD::destroy_ammod (txa->ammod); + WCPAGC::destroy_wcpagc (txa->alc); + delete (txa->compmeter); + BANDPASS::destroy_bandpass (txa->bp2); + OSCTRL::destroy_osctrl (txa->osctrl); + BANDPASS::destroy_bandpass (txa->bp1); + COMPRESSOR::destroy_compressor (txa->compressor); + BANDPASS::destroy_bandpass (txa->bp0); + delete (txa->cfcmeter); + CFCOMP::destroy_cfcomp (txa->cfcomp); + delete (txa->lvlrmeter); + WCPAGC::destroy_wcpagc (txa->leveler); + EMPHP::destroy_emphp (txa->preemph); + delete (txa->eqmeter); + delete (txa->eqp); + delete (txa->amsq); + delete (txa->micmeter); + PHROT::destroy_phrot (txa->phrot); + PANEL::destroy_panel (txa->panel); + delete (txa->gen0); + delete (txa->rsmpin); delete[] (txa->midbuff); delete[] (txa->outbuff); delete[] (txa->inbuff); @@ -561,72 +561,72 @@ void TXA::flush_txa (TXA* txa) std::fill(txa->inbuff, txa->inbuff + 1 * txa->dsp_insize * 2, 0); std::fill(txa->outbuff, txa->outbuff + 1 * txa->dsp_outsize * 2, 0); std::fill(txa->midbuff, txa->midbuff + 2 * txa->dsp_size * 2, 0); - txa->rsmpin.p->flush(); - txa->gen0.p->flush(); - PANEL::flush_panel (txa->panel.p); - PHROT::flush_phrot (txa->phrot.p); - txa->micmeter.p->flush (); - txa->amsq.p->flush (); - EQP::flush_eqp (txa->eqp.p); - txa->eqmeter.p->flush (); - EMPHP::flush_emphp (txa->preemph.p); - WCPAGC::flush_wcpagc (txa->leveler.p); - txa->lvlrmeter.p->flush (); - CFCOMP::flush_cfcomp (txa->cfcomp.p); - txa->cfcmeter.p->flush (); - BANDPASS::flush_bandpass (txa->bp0.p); - COMPRESSOR::flush_compressor (txa->compressor.p); - BANDPASS::flush_bandpass (txa->bp1.p); - OSCTRL::flush_osctrl (txa->osctrl.p); - BANDPASS::flush_bandpass (txa->bp2.p); - txa->compmeter.p->flush (); - WCPAGC::flush_wcpagc (txa->alc.p); - AMMOD::flush_ammod (txa->ammod.p); - FMMOD::flush_fmmod (txa->fmmod.p); - txa->gen1.p->flush(); - USLEW::flush_uslew (txa->uslew.p); - txa->alcmeter.p->flush (); - SIPHON::flush_siphon (txa->sip1.p); + txa->rsmpin->flush(); + txa->gen0->flush(); + PANEL::flush_panel (txa->panel); + PHROT::flush_phrot (txa->phrot); + txa->micmeter->flush (); + txa->amsq->flush (); + txa->eqp->flush(); + txa->eqmeter->flush (); + EMPHP::flush_emphp (txa->preemph); + WCPAGC::flush_wcpagc (txa->leveler); + txa->lvlrmeter->flush (); + CFCOMP::flush_cfcomp (txa->cfcomp); + txa->cfcmeter->flush (); + BANDPASS::flush_bandpass (txa->bp0); + COMPRESSOR::flush_compressor (txa->compressor); + BANDPASS::flush_bandpass (txa->bp1); + OSCTRL::flush_osctrl (txa->osctrl); + BANDPASS::flush_bandpass (txa->bp2); + txa->compmeter->flush (); + WCPAGC::flush_wcpagc (txa->alc); + AMMOD::flush_ammod (txa->ammod); + FMMOD::flush_fmmod (txa->fmmod); + txa->gen1->flush(); + USLEW::flush_uslew (txa->uslew); + txa->alcmeter->flush (); + SIPHON::flush_siphon (txa->sip1); IQC::flush_iqc (txa->iqc.p0); - CFIR::flush_cfir(txa->cfir.p); - txa->rsmpout.p->flush(); - txa->outmeter.p->flush (); + CFIR::flush_cfir(txa->cfir); + txa->rsmpout->flush(); + txa->outmeter->flush (); } void xtxa (TXA* txa) { - txa->rsmpin.p->execute(); // input resampler - txa->gen0.p->execute(); // input signal generator - PANEL::xpanel (txa->panel.p); // includes MIC gain - PHROT::xphrot (txa->phrot.p); // phase rotator - txa->micmeter.p->execute (); // MIC meter - txa->amsq.p->xcap (); // downward expander capture - txa->amsq.p->execute (); // downward expander action - EQP::xeqp (txa->eqp.p); // pre-EQ - txa->eqmeter.p->execute (); // EQ meter - EMPHP::xemphp (txa->preemph.p, 0); // FM pre-emphasis (first option) - WCPAGC::xwcpagc (txa->leveler.p); // Leveler - txa->lvlrmeter.p->execute (); // Leveler Meter - CFCOMP::xcfcomp (txa->cfcomp.p, 0); // Continuous Frequency Compressor with post-EQ - txa->cfcmeter.p->execute (); // CFC+PostEQ Meter - BANDPASS::xbandpass (txa->bp0.p, 0); // primary bandpass filter - COMPRESSOR::xcompressor (txa->compressor.p); // COMP compressor - BANDPASS::xbandpass (txa->bp1.p, 0); // aux bandpass (runs if COMP) - OSCTRL::xosctrl (txa->osctrl.p); // CESSB Overshoot Control - BANDPASS::xbandpass (txa->bp2.p, 0); // aux bandpass (runs if CESSB) - txa->compmeter.p->execute (); // COMP meter - WCPAGC::xwcpagc (txa->alc.p); // ALC - AMMOD::xammod (txa->ammod.p); // AM Modulator - EMPHP::xemphp (txa->preemph.p, 1); // FM pre-emphasis (second option) - FMMOD::xfmmod (txa->fmmod.p); // FM Modulator - txa->gen1.p->execute(); // output signal generator (TUN and Two-tone) - USLEW::xuslew (txa->uslew.p); // up-slew for AM, FM, and gens - txa->alcmeter.p->execute (); // ALC Meter - SIPHON::xsiphon (txa->sip1.p, 0); // siphon data for display + txa->rsmpin->execute(); // input resampler + txa->gen0->execute(); // input signal generator + PANEL::xpanel (txa->panel); // includes MIC gain + PHROT::xphrot (txa->phrot); // phase rotator + txa->micmeter->execute (); // MIC meter + txa->amsq->xcap (); // downward expander capture + txa->amsq->execute (); // downward expander action + txa->eqp->execute (); // pre-EQ + txa->eqmeter->execute (); // EQ meter + EMPHP::xemphp (txa->preemph, 0); // FM pre-emphasis (first option) + WCPAGC::xwcpagc (txa->leveler); // Leveler + txa->lvlrmeter->execute (); // Leveler Meter + CFCOMP::xcfcomp (txa->cfcomp, 0); // Continuous Frequency Compressor with post-EQ + txa->cfcmeter->execute (); // CFC+PostEQ Meter + BANDPASS::xbandpass (txa->bp0, 0); // primary bandpass filter + COMPRESSOR::xcompressor (txa->compressor); // COMP compressor + BANDPASS::xbandpass (txa->bp1, 0); // aux bandpass (runs if COMP) + OSCTRL::xosctrl (txa->osctrl); // CESSB Overshoot Control + BANDPASS::xbandpass (txa->bp2, 0); // aux bandpass (runs if CESSB) + txa->compmeter->execute (); // COMP meter + WCPAGC::xwcpagc (txa->alc); // ALC + AMMOD::xammod (txa->ammod); // AM Modulator + EMPHP::xemphp (txa->preemph, 1); // FM pre-emphasis (second option) + FMMOD::xfmmod (txa->fmmod); // FM Modulator + txa->gen1->execute(); // output signal generator (TUN and Two-tone) + USLEW::xuslew (txa->uslew); // up-slew for AM, FM, and gens + txa->alcmeter->execute (); // ALC Meter + SIPHON::xsiphon (txa->sip1, 0); // siphon data for display IQC::xiqc (txa->iqc.p0); // PureSignal correction - CFIR::xcfir(txa->cfir.p); // compensating FIR filter (used Protocol_2 only) - txa->rsmpout.p->execute(); // output resampler - txa->outmeter.p->execute (); // output meter + CFIR::xcfir(txa->cfir); // compensating FIR filter (used Protocol_2 only) + txa->rsmpout->execute(); // output resampler + txa->outmeter->execute (); // output meter // print_peak_env ("env_exception.txt", txa->dsp_outsize, txa->outbuff, 0.7); } @@ -642,9 +642,9 @@ void TXA::setInputSamplerate (TXA *txa, int in_rate) delete[] (txa->inbuff); txa->inbuff = new float[1 * txa->dsp_insize * 2]; //(float *)malloc0(1 * txa->dsp_insize * sizeof(complex)); // input resampler - txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin.p->setSize(txa->dsp_insize); - txa->rsmpin.p->setInRate(txa->in_rate); + txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin->setSize(txa->dsp_insize); + txa->rsmpin->setInRate(txa->in_rate); ResCheck (*txa); } @@ -660,15 +660,15 @@ void TXA::setOutputSamplerate (TXA* txa, int out_rate) delete[] (txa->outbuff); txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); // cfir - needs to know input rate of firmware CIC - CFIR::setOutRate_cfir (txa->cfir.p, txa->out_rate); + CFIR::setOutRate_cfir (txa->cfir, txa->out_rate); // output resampler - txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout.p->setOutRate(txa->out_rate); + txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout->setOutRate(txa->out_rate); ResCheck (*txa); // output meter - txa->outmeter.p->setBuffers(txa->outbuff); - txa->outmeter.p->setSize(txa->dsp_outsize); - txa->outmeter.p->setSamplerate (txa->out_rate); + txa->outmeter->setBuffers(txa->outbuff); + txa->outmeter->setSize(txa->dsp_outsize); + txa->outmeter->setSamplerate (txa->out_rate); } void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) @@ -690,44 +690,44 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) delete[] (txa->outbuff); txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); // input resampler - txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin.p->setSize(txa->dsp_insize); - txa->rsmpin.p->setOutRate(txa->dsp_rate); + txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin->setSize(txa->dsp_insize); + txa->rsmpin->setOutRate(txa->dsp_rate); // dsp_rate blocks - txa->gen0.p->setSamplerate(txa->dsp_rate); - PANEL::setSamplerate_panel (txa->panel.p, txa->dsp_rate); - PHROT::setSamplerate_phrot (txa->phrot.p, txa->dsp_rate); - txa->micmeter.p->setSamplerate (txa->dsp_rate); - txa->amsq.p->setSamplerate (txa->dsp_rate); - EQP::setSamplerate_eqp (txa->eqp.p, txa->dsp_rate); - txa->eqmeter.p->setSamplerate (txa->dsp_rate); - EMPHP::setSamplerate_emphp (txa->preemph.p, txa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (txa->leveler.p, txa->dsp_rate); - txa->lvlrmeter.p->setSamplerate (txa->dsp_rate); - CFCOMP::setSamplerate_cfcomp (txa->cfcomp.p, txa->dsp_rate); - txa->cfcmeter.p->setSamplerate (txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp0.p, txa->dsp_rate); - COMPRESSOR::setSamplerate_compressor (txa->compressor.p, txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp1.p, txa->dsp_rate); - OSCTRL::setSamplerate_osctrl (txa->osctrl.p, txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp2.p, txa->dsp_rate); - txa->compmeter.p->setSamplerate (txa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (txa->alc.p, txa->dsp_rate); - AMMOD::setSamplerate_ammod (txa->ammod.p, txa->dsp_rate); - FMMOD::setSamplerate_fmmod (txa->fmmod.p, txa->dsp_rate); - txa->gen1.p->setSamplerate(txa->dsp_rate); - USLEW::setSamplerate_uslew (txa->uslew.p, txa->dsp_rate); - txa->alcmeter.p->setSamplerate (txa->dsp_rate); - SIPHON::setSamplerate_siphon (txa->sip1.p, txa->dsp_rate); + txa->gen0->setSamplerate(txa->dsp_rate); + PANEL::setSamplerate_panel (txa->panel, txa->dsp_rate); + PHROT::setSamplerate_phrot (txa->phrot, txa->dsp_rate); + txa->micmeter->setSamplerate (txa->dsp_rate); + txa->amsq->setSamplerate (txa->dsp_rate); + txa->eqp->setSamplerate (txa->dsp_rate); + txa->eqmeter->setSamplerate (txa->dsp_rate); + EMPHP::setSamplerate_emphp (txa->preemph, txa->dsp_rate); + WCPAGC::setSamplerate_wcpagc (txa->leveler, txa->dsp_rate); + txa->lvlrmeter->setSamplerate (txa->dsp_rate); + CFCOMP::setSamplerate_cfcomp (txa->cfcomp, txa->dsp_rate); + txa->cfcmeter->setSamplerate (txa->dsp_rate); + BANDPASS::setSamplerate_bandpass (txa->bp0, txa->dsp_rate); + COMPRESSOR::setSamplerate_compressor (txa->compressor, txa->dsp_rate); + BANDPASS::setSamplerate_bandpass (txa->bp1, txa->dsp_rate); + OSCTRL::setSamplerate_osctrl (txa->osctrl, txa->dsp_rate); + BANDPASS::setSamplerate_bandpass (txa->bp2, txa->dsp_rate); + txa->compmeter->setSamplerate (txa->dsp_rate); + WCPAGC::setSamplerate_wcpagc (txa->alc, txa->dsp_rate); + AMMOD::setSamplerate_ammod (txa->ammod, txa->dsp_rate); + FMMOD::setSamplerate_fmmod (txa->fmmod, txa->dsp_rate); + txa->gen1->setSamplerate(txa->dsp_rate); + USLEW::setSamplerate_uslew (txa->uslew, txa->dsp_rate); + txa->alcmeter->setSamplerate (txa->dsp_rate); + SIPHON::setSamplerate_siphon (txa->sip1, txa->dsp_rate); IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate); - CFIR::setSamplerate_cfir (txa->cfir.p, txa->dsp_rate); + CFIR::setSamplerate_cfir (txa->cfir, txa->dsp_rate); // output resampler - txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout.p->setInRate(txa->dsp_rate); + txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout->setInRate(txa->dsp_rate); ResCheck (*txa); // output meter - txa->outmeter.p->setBuffers(txa->outbuff); - txa->outmeter.p->setSize (txa->dsp_outsize); + txa->outmeter->setBuffers(txa->outbuff); + txa->outmeter->setSize (txa->dsp_outsize); } void TXA::setDSPBuffsize (TXA *txa, int dsp_size) @@ -751,69 +751,69 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) delete[] (txa->outbuff); txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); // input resampler - txa->rsmpin.p->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin.p->setSize(txa->dsp_insize); + txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); + txa->rsmpin->setSize(txa->dsp_insize); // dsp_size blocks - txa->gen0.p->setBuffers(txa->midbuff, txa->midbuff); - txa->gen0.p->setSize(txa->dsp_size); - PANEL::setBuffers_panel (txa->panel.p, txa->midbuff, txa->midbuff); - PANEL::setSize_panel (txa->panel.p, txa->dsp_size); - PHROT::setBuffers_phrot (txa->phrot.p, txa->midbuff, txa->midbuff); - PHROT::setSize_phrot (txa->phrot.p, txa->dsp_size); - txa->micmeter.p->setBuffers (txa->midbuff); - txa->micmeter.p->setSize (txa->dsp_size); - txa->amsq.p->setBuffers (txa->midbuff, txa->midbuff, txa->midbuff); - txa->amsq.p->setSize (txa->dsp_size); - EQP::setBuffers_eqp (txa->eqp.p, txa->midbuff, txa->midbuff); - EQP::setSize_eqp (txa->eqp.p, txa->dsp_size); - txa->eqmeter.p->setBuffers (txa->midbuff); - txa->eqmeter.p->setSize (txa->dsp_size); - EMPHP::setBuffers_emphp (txa->preemph.p, txa->midbuff, txa->midbuff); - EMPHP::setSize_emphp (txa->preemph.p, txa->dsp_size); - WCPAGC::setBuffers_wcpagc (txa->leveler.p, txa->midbuff, txa->midbuff); - WCPAGC::setSize_wcpagc (txa->leveler.p, txa->dsp_size); - txa->lvlrmeter.p->setBuffers(txa->midbuff); - txa->lvlrmeter.p->setSize(txa->dsp_size); - CFCOMP::setBuffers_cfcomp (txa->cfcomp.p, txa->midbuff, txa->midbuff); - CFCOMP::setSize_cfcomp (txa->cfcomp.p, txa->dsp_size); - txa->cfcmeter.p->setBuffers(txa->midbuff); - txa->cfcmeter.p->setSize(txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp0.p, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp0.p, txa->dsp_size); - COMPRESSOR::setBuffers_compressor (txa->compressor.p, txa->midbuff, txa->midbuff); - COMPRESSOR::setSize_compressor (txa->compressor.p, txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp1.p, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp1.p, txa->dsp_size); - OSCTRL::setBuffers_osctrl (txa->osctrl.p, txa->midbuff, txa->midbuff); - OSCTRL::setSize_osctrl (txa->osctrl.p, txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp2.p, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp2.p, txa->dsp_size); - txa->compmeter.p->setBuffers(txa->midbuff); - txa->compmeter.p->setSize(txa->dsp_size); - WCPAGC::setBuffers_wcpagc (txa->alc.p, txa->midbuff, txa->midbuff); - WCPAGC::setSize_wcpagc (txa->alc.p, txa->dsp_size); - AMMOD::setBuffers_ammod (txa->ammod.p, txa->midbuff, txa->midbuff); - AMMOD::setSize_ammod (txa->ammod.p, txa->dsp_size); - FMMOD::setBuffers_fmmod (txa->fmmod.p, txa->midbuff, txa->midbuff); - FMMOD::setSize_fmmod (txa->fmmod.p, txa->dsp_size); - txa->gen1.p->setBuffers(txa->midbuff, txa->midbuff); - txa->gen1.p->setSize(txa->dsp_size); - USLEW::setBuffers_uslew (txa->uslew.p, txa->midbuff, txa->midbuff); - USLEW::setSize_uslew (txa->uslew.p, txa->dsp_size); - txa->alcmeter.p->setBuffers (txa->midbuff); - txa->alcmeter.p->setSize(txa->dsp_size); - SIPHON::setBuffers_siphon (txa->sip1.p, txa->midbuff); - SIPHON::setSize_siphon (txa->sip1.p, txa->dsp_size); + txa->gen0->setBuffers(txa->midbuff, txa->midbuff); + txa->gen0->setSize(txa->dsp_size); + PANEL::setBuffers_panel (txa->panel, txa->midbuff, txa->midbuff); + PANEL::setSize_panel (txa->panel, txa->dsp_size); + PHROT::setBuffers_phrot (txa->phrot, txa->midbuff, txa->midbuff); + PHROT::setSize_phrot (txa->phrot, txa->dsp_size); + txa->micmeter->setBuffers (txa->midbuff); + txa->micmeter->setSize (txa->dsp_size); + txa->amsq->setBuffers (txa->midbuff, txa->midbuff, txa->midbuff); + txa->amsq->setSize (txa->dsp_size); + txa->eqp->setBuffers (txa->midbuff, txa->midbuff); + txa->eqp->setSize (txa->dsp_size); + txa->eqmeter->setBuffers (txa->midbuff); + txa->eqmeter->setSize (txa->dsp_size); + EMPHP::setBuffers_emphp (txa->preemph, txa->midbuff, txa->midbuff); + EMPHP::setSize_emphp (txa->preemph, txa->dsp_size); + WCPAGC::setBuffers_wcpagc (txa->leveler, txa->midbuff, txa->midbuff); + WCPAGC::setSize_wcpagc (txa->leveler, txa->dsp_size); + txa->lvlrmeter->setBuffers(txa->midbuff); + txa->lvlrmeter->setSize(txa->dsp_size); + CFCOMP::setBuffers_cfcomp (txa->cfcomp, txa->midbuff, txa->midbuff); + CFCOMP::setSize_cfcomp (txa->cfcomp, txa->dsp_size); + txa->cfcmeter->setBuffers(txa->midbuff); + txa->cfcmeter->setSize(txa->dsp_size); + BANDPASS::setBuffers_bandpass (txa->bp0, txa->midbuff, txa->midbuff); + BANDPASS::setSize_bandpass (txa->bp0, txa->dsp_size); + COMPRESSOR::setBuffers_compressor (txa->compressor, txa->midbuff, txa->midbuff); + COMPRESSOR::setSize_compressor (txa->compressor, txa->dsp_size); + BANDPASS::setBuffers_bandpass (txa->bp1, txa->midbuff, txa->midbuff); + BANDPASS::setSize_bandpass (txa->bp1, txa->dsp_size); + OSCTRL::setBuffers_osctrl (txa->osctrl, txa->midbuff, txa->midbuff); + OSCTRL::setSize_osctrl (txa->osctrl, txa->dsp_size); + BANDPASS::setBuffers_bandpass (txa->bp2, txa->midbuff, txa->midbuff); + BANDPASS::setSize_bandpass (txa->bp2, txa->dsp_size); + txa->compmeter->setBuffers(txa->midbuff); + txa->compmeter->setSize(txa->dsp_size); + WCPAGC::setBuffers_wcpagc (txa->alc, txa->midbuff, txa->midbuff); + WCPAGC::setSize_wcpagc (txa->alc, txa->dsp_size); + AMMOD::setBuffers_ammod (txa->ammod, txa->midbuff, txa->midbuff); + AMMOD::setSize_ammod (txa->ammod, txa->dsp_size); + FMMOD::setBuffers_fmmod (txa->fmmod, txa->midbuff, txa->midbuff); + FMMOD::setSize_fmmod (txa->fmmod, txa->dsp_size); + txa->gen1->setBuffers(txa->midbuff, txa->midbuff); + txa->gen1->setSize(txa->dsp_size); + USLEW::setBuffers_uslew (txa->uslew, txa->midbuff, txa->midbuff); + USLEW::setSize_uslew (txa->uslew, txa->dsp_size); + txa->alcmeter->setBuffers (txa->midbuff); + txa->alcmeter->setSize(txa->dsp_size); + SIPHON::setBuffers_siphon (txa->sip1, txa->midbuff); + SIPHON::setSize_siphon (txa->sip1, txa->dsp_size); IQC::setBuffers_iqc (txa->iqc.p0, txa->midbuff, txa->midbuff); IQC::setSize_iqc (txa->iqc.p0, txa->dsp_size); - CFIR::setBuffers_cfir (txa->cfir.p, txa->midbuff, txa->midbuff); - CFIR::setSize_cfir (txa->cfir.p, txa->dsp_size); + CFIR::setBuffers_cfir (txa->cfir, txa->midbuff, txa->midbuff); + CFIR::setSize_cfir (txa->cfir, txa->dsp_size); // output resampler - txa->rsmpout.p->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout.p->setSize(txa->dsp_size); + txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); + txa->rsmpout->setSize(txa->dsp_size); // output meter - txa->outmeter.p->setBuffers(txa->outbuff); - txa->outmeter.p->setSize(txa->dsp_outsize); + txa->outmeter->setBuffers(txa->outbuff); + txa->outmeter->setSize(txa->dsp_outsize); } /******************************************************************************************************** @@ -827,29 +827,29 @@ void TXA::SetMode (TXA& txa, int mode) if (txa.mode != mode) { txa.mode = mode; - txa.ammod.p->run = 0; - txa.fmmod.p->run = 0; - txa.preemph.p->run = 0; + txa.ammod->run = 0; + txa.fmmod->run = 0; + txa.preemph->run = 0; switch (mode) { case TXA_AM: case TXA_SAM: - txa.ammod.p->run = 1; - txa.ammod.p->mode = 0; + txa.ammod->run = 1; + txa.ammod->mode = 0; break; case TXA_DSB: - txa.ammod.p->run = 1; - txa.ammod.p->mode = 1; + txa.ammod->run = 1; + txa.ammod->mode = 1; break; case TXA_AM_LSB: case TXA_AM_USB: - txa.ammod.p->run = 1; - txa.ammod.p->mode = 2; + txa.ammod->run = 1; + txa.ammod->mode = 2; break; case TXA_FM: - txa.fmmod.p->run = 1; - txa.preemph.p->run = 1; + txa.fmmod->run = 1; + txa.preemph->run = 1; break; default: @@ -879,12 +879,12 @@ void TXA::SetBandpassFreqs (TXA& txa, float f_low, float f_high) void TXA::ResCheck (TXA& txa) { - RESAMPLE *a = txa.rsmpin.p; + RESAMPLE *a = txa.rsmpin; if (txa.in_rate != txa.dsp_rate) a->run = 1; else a->run = 0; - a = txa.rsmpout.p; + a = txa.rsmpout; if (txa.dsp_rate != txa.out_rate) a->run = 1; else @@ -893,17 +893,17 @@ void TXA::ResCheck (TXA& txa) int TXA::UslewCheck (TXA& txa) { - return (txa.ammod.p->run == 1) || - (txa.fmmod.p->run == 1) || - (txa.gen0.p->run == 1) || - (txa.gen1.p->run == 1); + return (txa.ammod->run == 1) || + (txa.fmmod->run == 1) || + (txa.gen0->run == 1) || + (txa.gen1->run == 1); } void TXA::SetupBPFilters (TXA& txa) { - txa.bp0.p->run = 1; - txa.bp1.p->run = 0; - txa.bp2.p->run = 0; + txa.bp0->run = 1; + txa.bp1->run = 0; + txa.bp2->run = 0; switch (txa.mode) { case TXA_LSB: @@ -914,15 +914,15 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_DIGU: case TXA_SPEC: case TXA_DRM: - BANDPASS::CalcBandpassFilter (txa.bp0.p, txa.f_low, txa.f_high, 2.0); - if (txa.compressor.p->run) + BANDPASS::CalcBandpassFilter (txa.bp0, txa.f_low, txa.f_high, 2.0); + if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1.p, txa.f_low, txa.f_high, 2.0); - txa.bp1.p->run = 1; - if (txa.osctrl.p->run) + BANDPASS::CalcBandpassFilter (txa.bp1, txa.f_low, txa.f_high, 2.0); + txa.bp1->run = 1; + if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2.p, txa.f_low, txa.f_high, 1.0); - txa.bp2.p->run = 1; + BANDPASS::CalcBandpassFilter (txa.bp2, txa.f_low, txa.f_high, 1.0); + txa.bp2->run = 1; } } break; @@ -930,45 +930,45 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_AM: case TXA_SAM: case TXA_FM: - if (txa.compressor.p->run) + if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp0.p, 0.0, txa.f_high, 2.0); - BANDPASS::CalcBandpassFilter (txa.bp1.p, 0.0, txa.f_high, 2.0); - txa.bp1.p->run = 1; - if (txa.osctrl.p->run) + BANDPASS::CalcBandpassFilter (txa.bp0, 0.0, txa.f_high, 2.0); + BANDPASS::CalcBandpassFilter (txa.bp1, 0.0, txa.f_high, 2.0); + txa.bp1->run = 1; + if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2.p, 0.0, txa.f_high, 1.0); - txa.bp2.p->run = 1; + BANDPASS::CalcBandpassFilter (txa.bp2, 0.0, txa.f_high, 1.0); + txa.bp2->run = 1; } } else { - BANDPASS::CalcBandpassFilter (txa.bp0.p, txa.f_low, txa.f_high, 1.0); + BANDPASS::CalcBandpassFilter (txa.bp0, txa.f_low, txa.f_high, 1.0); } break; case TXA_AM_LSB: - BANDPASS::CalcBandpassFilter (txa.bp0.p, -txa.f_high, 0.0, 2.0); - if (txa.compressor.p->run) + BANDPASS::CalcBandpassFilter (txa.bp0, -txa.f_high, 0.0, 2.0); + if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1.p, -txa.f_high, 0.0, 2.0); - txa.bp1.p->run = 1; - if (txa.osctrl.p->run) + BANDPASS::CalcBandpassFilter (txa.bp1, -txa.f_high, 0.0, 2.0); + txa.bp1->run = 1; + if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2.p, -txa.f_high, 0.0, 1.0); - txa.bp2.p->run = 1; + BANDPASS::CalcBandpassFilter (txa.bp2, -txa.f_high, 0.0, 1.0); + txa.bp2->run = 1; } } break; case TXA_AM_USB: - BANDPASS::CalcBandpassFilter (txa.bp0.p, 0.0, txa.f_high, 2.0); - if (txa.compressor.p->run) + BANDPASS::CalcBandpassFilter (txa.bp0, 0.0, txa.f_high, 2.0); + if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1.p, 0.0, txa.f_high, 2.0); - txa.bp1.p->run = 1; - if (txa.osctrl.p->run) + BANDPASS::CalcBandpassFilter (txa.bp1, 0.0, txa.f_high, 2.0); + txa.bp1->run = 1; + if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2.p, 0.0, txa.f_high, 1.0); - txa.bp2.p->run = 1; + BANDPASS::CalcBandpassFilter (txa.bp2, 0.0, txa.f_high, 1.0); + txa.bp2->run = 1; } } break; @@ -986,7 +986,7 @@ void TXA::SetNC (TXA& txa, int nc) int oldstate = txa.state; BANDPASS::SetBandpassNC (txa, nc); EMPHP::SetFMEmphNC (txa, nc); - EQP::SetEQNC (txa, nc); + txa.eqp->setNC (nc); FMMOD::SetFMNC (txa, nc); CFIR::SetCFIRNC (txa, nc); txa.state = oldstate; @@ -996,7 +996,7 @@ void TXA::SetMP (TXA& txa, int mp) { BANDPASS::SetBandpassMP (txa, mp); EMPHP::SetFMEmphMP (txa, mp); - EQP::SetEQMP (txa, mp); + txa.eqp->setMP (mp); FMMOD::SetFMMP (txa, mp); } diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index 0ed9253c7..4437660ff 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -36,7 +36,7 @@ warren@wpratt.com #include "resample.hpp" #include "patchpanel.hpp" #include "amsq.hpp" -#include "eq.hpp" +#include "eqp.hpp" #include "iir.hpp" #include "cfcomp.hpp" #include "compress.hpp" @@ -127,78 +127,37 @@ public: double meter[TXA_METERTYPE_LAST]; long upslew; - struct - { - METER *p; - } micmeter, eqmeter, lvlrmeter, cfcmeter, compmeter, alcmeter, outmeter; - struct - { - RESAMPLE *p; - } rsmpin, rsmpout; - struct - { - PANEL *p; - } panel; - struct - { - AMSQ *p; - } amsq; - struct - { - EQP *p; - } eqp; - struct - { - PHROT *p; - } phrot; - struct - { - CFCOMP *p; - } cfcomp; - struct - { - COMPRESSOR *p; - } compressor; - struct - { - BANDPASS *p; - } bp0, bp1, bp2; - struct - { - BPS *p; - } bps0, bps1, bps2; - struct - { - OSCTRL *p; - } osctrl; - struct - { - WCPAGC *p; - } leveler, alc; - struct - { - AMMOD *p; - } ammod; - struct - { - EMPHP *p; - } preemph; - struct - { - FMMOD *p; - } fmmod; - struct - { - SIPHON *p; - } sip1; - struct - { - GEN *p; - } gen0, gen1; - struct - { - USLEW *p; - } uslew; + METER *micmeter; + METER *eqmeter; + METER *lvlrmeter; + METER *cfcmeter; + METER *compmeter; + METER *alcmeter; + METER *outmeter; + RESAMPLE *rsmpin; + RESAMPLE *rsmpout; + PANEL *panel; + AMSQ *amsq; + EQP *eqp; + PHROT *phrot; + CFCOMP *cfcomp; + COMPRESSOR *compressor; + BANDPASS *bp0; + BANDPASS *bp1; + BANDPASS *bp2; + BPS *bps0; + BPS *bps1; + BPS *bps2; + OSCTRL *osctrl; + WCPAGC *leveler; + WCPAGC *alc; + AMMOD *ammod; + EMPHP *preemph; + FMMOD *fmmod; + SIPHON *sip1; + GEN *gen0; + GEN *gen1; + USLEW *uslew; // struct // { // CALCC *p; @@ -209,10 +168,7 @@ public: IQC *p0, *p1; // p0 for dsp-synchronized reference, p1 for other } iqc; - struct - { - CFIR *p; - } cfir; + CFIR *cfir; static TXA* create_txa ( int in_rate, // input samplerate diff --git a/wdsp/ammod.cpp b/wdsp/ammod.cpp index 236c240c9..b1301e351 100644 --- a/wdsp/ammod.cpp +++ b/wdsp/ammod.cpp @@ -109,8 +109,8 @@ void AMMOD::setSize_ammod(AMMOD *a, int size) void AMMOD::SetAMCarrierLevel (TXA& txa, float c_level) { - txa.ammod.p->c_level = c_level; - txa.ammod.p->a_level = 1.0 - c_level; + txa.ammod->c_level = c_level; + txa.ammod->a_level = 1.0 - c_level; } } // namespace WDSP diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index 18264a83e..6dd65e1f3 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -257,7 +257,7 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) //{ // float* impulse; // BANDPASS a; -// a = txa.bp0.p; +// a = txa.bp0; // if ((f_low != a->f_low) || (f_high != a->f_high)) // { // a->f_low = f_low; @@ -266,7 +266,7 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) // setImpulse_fircore (a->p, impulse, 1); // delete[] (impulse); // } -// a = txa.bp1.p; +// a = txa.bp1; // if ((f_low != a->f_low) || (f_high != a->f_high)) // { // a->f_low = f_low; @@ -275,7 +275,7 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) // setImpulse_fircore (a->p, impulse, 1); // delete[] (impulse); // } -// a = txa.bp2.p; +// a = txa.bp2; // if ((f_low != a->f_low) || (f_high != a->f_high)) // { // a->f_low = f_low; @@ -290,7 +290,7 @@ void BANDPASS::SetBandpassNC (TXA& txa, int nc) { // NOTE: 'nc' must be >= 'size' BANDPASS *a; - a = txa.bp0.p; + a = txa.bp0; if (a->nc != nc) { @@ -308,7 +308,7 @@ void BANDPASS::SetBandpassNC (TXA& txa, int nc) delete[] (impulse); } - a = txa.bp1.p; + a = txa.bp1; if (a->nc != nc) { @@ -326,7 +326,7 @@ void BANDPASS::SetBandpassNC (TXA& txa, int nc) delete[] (impulse); } - a = txa.bp2.p; + a = txa.bp2; if (a->nc != nc) { @@ -348,7 +348,7 @@ void BANDPASS::SetBandpassNC (TXA& txa, int nc) void BANDPASS::SetBandpassMP (TXA& txa, int mp) { BANDPASS *a; - a = txa.bp0.p; + a = txa.bp0; if (mp != a->mp) { @@ -356,7 +356,7 @@ void BANDPASS::SetBandpassMP (TXA& txa, int mp) FIRCORE::setMp_fircore (a->p, a->mp); } - a = txa.bp1.p; + a = txa.bp1; if (mp != a->mp) { @@ -364,7 +364,7 @@ void BANDPASS::SetBandpassMP (TXA& txa, int mp) FIRCORE::setMp_fircore (a->p, a->mp); } - a = txa.bp2.p; + a = txa.bp2; if (mp != a->mp) { diff --git a/wdsp/bps.cpp b/wdsp/bps.cpp index 92bea41e0..168cf95d0 100644 --- a/wdsp/bps.cpp +++ b/wdsp/bps.cpp @@ -204,14 +204,14 @@ void BPS::SetBPSWindow (RXA& rxa, int wintype) // UNCOMMENT properties when pointers in place in txa void BPS::SetBPSRun (TXA& txa, int run) { - txa.bp1.p->run = run; + txa.bp1->run = run; } void BPS::SetBPSFreqs (TXA& txa, float f_low, float f_high) { float* impulse; BPS *a; - a = txa.bps0.p; + a = txa.bps0; if ((f_low != a->f_low) || (f_high != a->f_high)) { @@ -223,7 +223,7 @@ void BPS::SetBPSFreqs (TXA& txa, float f_low, float f_high) delete[] (impulse); } - a = txa.bps1.p; + a = txa.bps1; if ((f_low != a->f_low) || (f_high != a->f_high)) { @@ -235,7 +235,7 @@ void BPS::SetBPSFreqs (TXA& txa, float f_low, float f_high) delete[] (impulse); } - a = txa.bps2.p; + a = txa.bps2; if ((f_low != a->f_low) || (f_high != a->f_high)) { @@ -252,7 +252,7 @@ void BPS::SetBPSWindow (TXA& txa, int wintype) { float* impulse; BPS *a; - a = txa.bps0.p; + a = txa.bps0; if (a->wintype != wintype) { @@ -263,7 +263,7 @@ void BPS::SetBPSWindow (TXA& txa, int wintype) delete[] (impulse); } - a = txa.bps1.p; + a = txa.bps1; if (a->wintype != wintype) { @@ -274,7 +274,7 @@ void BPS::SetBPSWindow (TXA& txa, int wintype) delete[] (impulse); } - a = txa.bps2.p; + a = txa.bps2; if (a->wintype != wintype) { diff --git a/wdsp/cfcomp.cpp b/wdsp/cfcomp.cpp index 9b93f8abe..4a7e67533 100644 --- a/wdsp/cfcomp.cpp +++ b/wdsp/cfcomp.cpp @@ -417,7 +417,7 @@ void CFCOMP::setSize_cfcomp (CFCOMP *a, int size) void CFCOMP::SetCFCOMPRun (TXA& txa, int run) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; if (a->run != run) { a->run = run; @@ -426,7 +426,7 @@ void CFCOMP::SetCFCOMPRun (TXA& txa, int run) void CFCOMP::SetCFCOMPPosition (TXA& txa, int pos) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; if (a->position != pos) { a->position = pos; @@ -435,7 +435,7 @@ void CFCOMP::SetCFCOMPPosition (TXA& txa, int pos) void CFCOMP::SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float *E) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; a->nfreqs = nfreqs < 1 ? 1 : nfreqs; delete[] (a->E); delete[] (a->F); @@ -457,7 +457,7 @@ void CFCOMP::SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float * void CFCOMP::SetCFCOMPPrecomp (TXA& txa, float precomp) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; if (a->precomp != precomp) { @@ -473,7 +473,7 @@ void CFCOMP::SetCFCOMPPrecomp (TXA& txa, float precomp) void CFCOMP::SetCFCOMPPeqRun (TXA& txa, int run) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; if (a->peq_run != run) { a->peq_run = run; @@ -482,7 +482,7 @@ void CFCOMP::SetCFCOMPPeqRun (TXA& txa, int run) void CFCOMP::SetCFCOMPPrePeq (TXA& txa, float prepeq) { - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; a->prepeq = prepeq; a->prepeqlin = pow (10.0, 0.05 * a->prepeq); } @@ -490,7 +490,7 @@ void CFCOMP::SetCFCOMPPrePeq (TXA& txa, float prepeq) void CFCOMP::GetCFCOMPDisplayCompression (TXA& txa, float* comp_values, int* ready) { int i; - CFCOMP *a = txa.cfcomp.p; + CFCOMP *a = txa.cfcomp; if ((*ready = a->mask_ready)) { diff --git a/wdsp/cfir.cpp b/wdsp/cfir.cpp index 1cbd13525..e4def1b4c 100644 --- a/wdsp/cfir.cpp +++ b/wdsp/cfir.cpp @@ -267,14 +267,14 @@ float* CFIR::cfir_impulse ( void CFIR::SetCFIRRun (TXA& txa, int run) { - txa.cfir.p->run = run; + txa.cfir->run = run; } void CFIR::SetCFIRNC(TXA& txa, int nc) { // NOTE: 'nc' must be >= 'size' CFIR *a; - a = txa.cfir.p; + a = txa.cfir; if (a->nc != nc) { diff --git a/wdsp/compress.cpp b/wdsp/compress.cpp index af32e0e9e..949dcf854 100644 --- a/wdsp/compress.cpp +++ b/wdsp/compress.cpp @@ -100,16 +100,16 @@ void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size) void COMPRESSOR::SetCompressorRun (TXA& txa, int run) { - if (txa.compressor.p->run != run) + if (txa.compressor->run != run) { - txa.compressor.p->run = run; + txa.compressor->run = run; TXA::SetupBPFilters (txa); } } void COMPRESSOR::SetCompressorGain (TXA& txa, float gain) { - txa.compressor.p->gain = pow (10.0, gain / 20.0); + txa.compressor->gain = pow (10.0, gain / 20.0); } } // namespace WDSP diff --git a/wdsp/emph.cpp b/wdsp/emph.cpp index 842b7ec90..90498c1e7 100644 --- a/wdsp/emph.cpp +++ b/wdsp/emph.cpp @@ -113,13 +113,13 @@ void EMPHP::setSize_emphp (EMPHP *a, int size) void EMPHP::SetFMEmphPosition (TXA& txa, int position) { - txa.preemph.p->position = position; + txa.preemph->position = position; } void EMPHP::SetFMEmphMP (TXA& txa, int mp) { EMPHP *a; - a = txa.preemph.p; + a = txa.preemph; if (a->mp != mp) { a->mp = mp; @@ -131,7 +131,7 @@ void EMPHP::SetFMEmphNC (TXA& txa, int nc) { EMPHP *a; float* impulse; - a = txa.preemph.p; + a = txa.preemph; if (a->nc != nc) { @@ -146,7 +146,7 @@ void EMPHP::SetFMPreEmphFreqs (TXA& txa, float low, float high) { EMPHP *a; float* impulse; - a = txa.preemph.p; + a = txa.preemph; if (a->f_low != low || a->f_high != high) { @@ -263,7 +263,7 @@ PORT void SetTXAFMEmphPosition (int channel, int position) { ch.csDSP.lock(); - txa.preemph.p->position = position; + txa.preemph->position = position; ch.csDSP.unlock(); } */ diff --git a/wdsp/eq.cpp b/wdsp/eq.cpp index 669a5b6d2..d1450e4e2 100644 --- a/wdsp/eq.cpp +++ b/wdsp/eq.cpp @@ -27,6 +27,7 @@ warren@wpratt.com #include "comm.hpp" #include "eq.hpp" +#include "eqp.hpp" #include "fircore.hpp" #include "fir.hpp" #include "RXA.hpp" @@ -34,515 +35,6 @@ warren@wpratt.com namespace WDSP { -int EQP::fEQcompare (const void * a, const void * b) -{ - if (*(float*)a < *(float*)b) - return -1; - else if (*(float*)a == *(float*)b) - return 0; - else - return 1; -} - -float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype) -{ - float* fp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); - float* gp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); - float* A = new float[N / 2 + 1]; // (float *) malloc0 ((N / 2 + 1) * sizeof (float)); - float* sary = new float[2 * nfreqs]; // (float *) malloc0 (2 * nfreqs * sizeof (float)); - double gpreamp, f, frac; - float* impulse; - int i, j, mid; - fp[0] = 0.0; - fp[nfreqs + 1] = 1.0; - gpreamp = G[0]; - - for (i = 1; i <= nfreqs; i++) - { - fp[i] = 2.0 * F[i] / samplerate; - - if (fp[i] < 0.0) - fp[i] = 0.0; - - if (fp[i] > 1.0) - fp[i] = 1.0; - - gp[i] = G[i]; - } - - for (i = 1, j = 0; i <= nfreqs; i++, j+=2) - { - sary[j + 0] = fp[i]; - sary[j + 1] = gp[i]; - } - - qsort (sary, nfreqs, 2 * sizeof (float), fEQcompare); - - for (i = 1, j = 0; i <= nfreqs; i++, j+=2) - { - fp[i] = sary[j + 0]; - gp[i] = sary[j + 1]; - } - - gp[0] = gp[1]; - gp[nfreqs + 1] = gp[nfreqs]; - mid = N / 2; - j = 0; - - if (N & 1) - { - for (i = 0; i <= mid; i++) - { - f = (double)i / (double)mid; - - while ((f > fp[j + 1]) && (j < nfreqs)) - j++; - - frac = (f - fp[j]) / (fp[j + 1] - fp[j]); - A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; - } - } - else - { - for (i = 0; i < mid; i++) - { - f = ((double)i + 0.5) / (double)mid; - - while ((f > fp[j + 1]) && (j < nfreqs)) - j++; - - frac = (f - fp[j]) / (fp[j + 1] - fp[j]); - A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; - } - } - - if (ctfmode == 0) - { - int k, low, high; - double lowmag, highmag, flow4, fhigh4; - - if (N & 1) - { - low = (int)(fp[1] * mid); - high = (int)(fp[nfreqs] * mid + 0.5); - lowmag = A[low]; - highmag = A[high]; - flow4 = pow((double)low / (double)mid, 4.0); - fhigh4 = pow((double)high / (double)mid, 4.0); - k = low; - - while (--k >= 0) - { - f = (double)k / (double)mid; - lowmag *= (f * f * f * f) / flow4; - if (lowmag < 1.0e-20) lowmag = 1.0e-20; - A[k] = lowmag; - } - - k = high; - - while (++k <= mid) - { - f = (double)k / (double)mid; - highmag *= fhigh4 / (f * f * f * f); - if (highmag < 1.0e-20) highmag = 1.0e-20; - A[k] = highmag; - } - } - else - { - low = (int)(fp[1] * mid - 0.5); - high = (int)(fp[nfreqs] * mid - 0.5); - lowmag = A[low]; - highmag = A[high]; - flow4 = pow((double)low / (double)mid, 4.0); - fhigh4 = pow((double)high / (double)mid, 4.0); - k = low; - - while (--k >= 0) - { - f = (double)k / (double)mid; - lowmag *= (f * f * f * f) / flow4; - if (lowmag < 1.0e-20) lowmag = 1.0e-20; - A[k] = lowmag; - } - - k = high; - - while (++k < mid) - { - f = (double)k / (double)mid; - highmag *= fhigh4 / (f * f * f * f); - if (highmag < 1.0e-20) highmag = 1.0e-20; - A[k] = highmag; - } - } - } - - if (N & 1) - impulse = FIR::fir_fsamp_odd(N, A, 1, 1.0, wintype); - else - impulse = FIR::fir_fsamp(N, A, 1, 1.0, wintype); - - // print_impulse("eq.txt", N, impulse, 1, 0); - delete[] (sary); - delete[] (A); - delete[] (gp); - delete[] (fp); - return impulse; -} - -/******************************************************************************************************** -* * -* Partitioned Overlap-Save Equalizer * -* * -********************************************************************************************************/ - -EQP* EQP::create_eqp ( - int run, - int size, - int nc, - int mp, - float *in, - float *out, - int nfreqs, - float* F, - float* G, - int ctfmode, - int wintype, - int samplerate -) -{ - // NOTE: 'nc' must be >= 'size' - EQP *a = new EQP; - float* impulse; - a->run = run; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->nfreqs = nfreqs; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - a->ctfmode = ctfmode; - a->wintype = wintype; - a->samplerate = (float)samplerate; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse); - delete[] (impulse); - return a; -} - -void EQP::destroy_eqp (EQP *a) -{ - FIRCORE::destroy_fircore (a->p); - delete (a); -} - -void EQP::flush_eqp (EQP *a) -{ - FIRCORE::flush_fircore (a->p); -} - -void EQP::xeqp (EQP *a) -{ - if (a->run) - FIRCORE::xfircore (a->p); - else - std::copy( a->in, a->in + a->size * 2, a->out); -} - -void EQP::setBuffers_eqp (EQP *a, float* in, float* out) -{ - a->in = in; - a->out = out; - FIRCORE::setBuffers_fircore (a->p, a->in, a->out); -} - -void EQP::setSamplerate_eqp (EQP *a, int rate) -{ - float* impulse; - a->samplerate = rate; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::setSize_eqp (EQP *a, int size) -{ - float* impulse; - a->size = size; - FIRCORE::setSize_fircore (a->p, a->size); - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -/******************************************************************************************************** -* * -* Partitioned Overlap-Save Equalizer: RXA Properties * -* * -********************************************************************************************************/ - -void EQP::SetEQRun (RXA& rxa, int run) -{ - rxa.eqp->run = run; -} - -void EQP::SetEQNC (RXA& rxa, int nc) -{ - EQP *a; - float* impulse; - a = rxa.eqp; - - if (a->nc != nc) - { - a->nc = nc; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } -} - -void EQP::SetEQMP (RXA& rxa, int mp) -{ - EQP *a; - a = rxa.eqp; - if (a->mp != mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } -} - -void EQP::SetEQProfile (RXA& rxa, int nfreqs, const float* F, const float* G) -{ - EQP *a; - float* impulse; - a = rxa.eqp; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = nfreqs; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, - a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetEQCtfmode (RXA& rxa, int mode) -{ - EQP *a; - float* impulse; - a = rxa.eqp; - a->ctfmode = mode; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetEQWintype (RXA& rxa, int wintype) -{ - EQP *a; - float* impulse; - a = rxa.eqp; - a->wintype = wintype; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetGrphEQ (RXA& rxa, int *rxeq) -{ // three band equalizer (legacy compatibility) - EQP *a; - float* impulse; - a = rxa.eqp; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 4; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 150.0; - a->F[2] = 400.0; - a->F[3] = 1500.0; - a->F[4] = 6000.0; - a->G[0] = (float)rxeq[0]; - a->G[1] = (float)rxeq[1]; - a->G[2] = (float)rxeq[1]; - a->G[3] = (float)rxeq[2]; - a->G[4] = (float)rxeq[3]; - a->ctfmode = 0; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetGrphEQ10 (RXA& rxa, int *rxeq) -{ // ten band equalizer (legacy compatibility) - EQP *a; - float* impulse; - int i; - a = rxa.eqp; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 10; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 32.0; - a->F[2] = 63.0; - a->F[3] = 125.0; - a->F[4] = 250.0; - a->F[5] = 500.0; - a->F[6] = 1000.0; - a->F[7] = 2000.0; - a->F[8] = 4000.0; - a->F[9] = 8000.0; - a->F[10] = 16000.0; - for (i = 0; i <= a->nfreqs; i++) - a->G[i] = (float)rxeq[i]; - a->ctfmode = 0; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - // print_impulse ("rxeq.txt", a->nc, impulse, 1, 0); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -/******************************************************************************************************** -* * -* Partitioned Overlap-Save Equalizer: TXA Properties * -* * -********************************************************************************************************/ - -void EQP::SetEQRun (TXA& txa, int run) -{ - txa.eqp.p->run = run; -} - -void EQP::SetEQNC (TXA& txa, int nc) -{ - EQP *a; - float* impulse; - a = txa.eqp.p; - - if (a->nc != nc) - { - a->nc = nc; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } -} - -void EQP::SetEQMP (TXA& txa, int mp) -{ - EQP *a; - a = txa.eqp.p; - if (a->mp != mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } -} - -void EQP::SetEQProfile (TXA& txa, int nfreqs, const float* F, const float* G) -{ - EQP *a; - float* impulse; - a = txa.eqp.p; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = nfreqs; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetEQCtfmode (TXA& txa, int mode) -{ - EQP *a; - float* impulse; - a = txa.eqp.p; - a->ctfmode = mode; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetEQWintype (TXA& txa, int wintype) -{ - EQP *a; - float* impulse; - a = txa.eqp.p; - a->wintype = wintype; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetGrphEQ (TXA& txa, int *txeq) -{ // three band equalizer (legacy compatibility) - EQP *a; - float* impulse; - a = txa.eqp.p; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 4; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 150.0; - a->F[2] = 400.0; - a->F[3] = 1500.0; - a->F[4] = 6000.0; - a->G[0] = (float)txeq[0]; - a->G[1] = (float)txeq[1]; - a->G[2] = (float)txeq[1]; - a->G[3] = (float)txeq[2]; - a->G[4] = (float)txeq[3]; - a->ctfmode = 0; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EQP::SetGrphEQ10 (TXA& txa, int *txeq) -{ // ten band equalizer (legacy compatibility) - EQP *a; - float* impulse; - int i; - a = txa.eqp.p; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 10; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 32.0; - a->F[2] = 63.0; - a->F[3] = 125.0; - a->F[4] = 250.0; - a->F[5] = 500.0; - a->F[6] = 1000.0; - a->F[7] = 2000.0; - a->F[8] = 4000.0; - a->F[9] = 8000.0; - a->F[10] = 16000.0; - for (i = 0; i <= a->nfreqs; i++) - a->G[i] = (float)txeq[i]; - a->ctfmode = 0; - impulse = eq_impulse (a->nc, a->nfreqs, a->F, a->G, a->samplerate, 1.0 / (2.0 * a->size), a->ctfmode, a->wintype); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - /******************************************************************************************************** * * * Overlap-Save Equalizer * @@ -550,9 +42,9 @@ void EQP::SetGrphEQ10 (TXA& txa, int *txeq) ********************************************************************************************************/ -float* EQP::eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype) +float* EQ::eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype) { - float* impulse = eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); + float* impulse = EQP::eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); float* mults = FIR::fftcv_mults(2 * size, impulse); delete[] (impulse); return mults; @@ -565,7 +57,7 @@ void EQ::calc_eq (EQ *a) a->product = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT); a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT); - a->mults = EQP::eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); + a->mults = EQ::eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); } void EQ::decalc_eq (EQ *a) @@ -663,7 +155,7 @@ PORT void SetRXAEQRun (int channel, int run) { ch.csDSP.lock(); - rxa.eq.p->run = run; + rxa.eq->run = run; ch.csDSP.unlock(); } @@ -672,7 +164,7 @@ void SetRXAEQProfile (int channel, int nfreqs, float* F, float* G) { EQ a; ch.csDSP.lock(); - a = rxa.eq.p; + a = rxa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = nfreqs; @@ -690,7 +182,7 @@ void SetRXAEQCtfmode (int channel, int mode) { EQ a; ch.csDSP.lock(); - a = rxa.eq.p; + a = rxa.eq; a->ctfmode = mode; delete[] (a->mults); a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); @@ -702,7 +194,7 @@ void SetRXAEQWintype (int channel, int wintype) { EQ a; ch.csDSP.lock(); - a = rxa.eq.p; + a = rxa.eq; a->wintype = wintype; delete[] (a->mults); a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); @@ -714,7 +206,7 @@ void SetRXAGrphEQ (int channel, int *rxeq) { // three band equalizer (legacy compatibility) EQ a; ch.csDSP.lock(); - a = rxa.eq.p; + a = rxa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = 4; @@ -741,7 +233,7 @@ void SetRXAGrphEQ10 (int channel, int *rxeq) EQ a; int i; ch.csDSP.lock(); - a = rxa.eq.p; + a = rxa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = 10; @@ -775,7 +267,7 @@ PORT void SetTXAEQRun (int channel, int run) { ch.csDSP.lock(); - txa.eq.p->run = run; + txa.eq->run = run; ch.csDSP.unlock(); } @@ -784,7 +276,7 @@ void SetTXAEQProfile (int channel, int nfreqs, float* F, float* G) { EQ a; ch.csDSP.lock(); - a = txa.eq.p; + a = txa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = nfreqs; @@ -802,7 +294,7 @@ void SetTXAEQCtfmode (int channel, int mode) { EQ a; ch.csDSP.lock(); - a = txa.eq.p; + a = txa.eq; a->ctfmode = mode; delete[] (a->mults); a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); @@ -814,7 +306,7 @@ void SetTXAEQMethod (int channel, int wintype) { EQ a; ch.csDSP.lock(); - a = txa.eq.p; + a = txa.eq; a->wintype = wintype; delete[] (a->mults); a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); @@ -826,7 +318,7 @@ void SetTXAGrphEQ (int channel, int *txeq) { // three band equalizer (legacy compatibility) EQ a; ch.csDSP.lock(); - a = txa.eq.p; + a = txa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = 4; @@ -853,7 +345,7 @@ void SetTXAGrphEQ10 (int channel, int *txeq) EQ a; int i; ch.csDSP.lock(); - a = txa.eq.p; + a = txa.eq; delete[] (a->G); delete[] (a->F); a->nfreqs = 10; diff --git a/wdsp/eq.hpp b/wdsp/eq.hpp index f2cc5ec62..66748cb55 100644 --- a/wdsp/eq.hpp +++ b/wdsp/eq.hpp @@ -25,92 +25,6 @@ warren@wpratt.com */ -/******************************************************************************************************** -* * -* Partitioned Overlap-Save Equalizer * -* * -********************************************************************************************************/ - -#ifndef wdsp_eqp_h -#define wdsp_eqp_h - -#include "export.h" - -namespace WDSP { - -class FIRCORE; -class RXA; -class TXA; - -class WDSP_API EQP -{ -public: - int run; - int size; - int nc; - int mp; - float* in; - float* out; - int nfreqs; - float* F; - float* G; - int ctfmode; - int wintype; - double samplerate; - FIRCORE *p; - - static EQP* create_eqp ( - int run, - int size, - int nc, - int mp, - float *in, - float *out, - int nfreqs, - float* F, - float* G, - int ctfmode, - int wintype, - int samplerate - ); - static float* eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype); - static void destroy_eqp (EQP *a); - static void flush_eqp (EQP *a); - static void xeqp (EQP *a); - static void setBuffers_eqp (EQP *a, float* in, float* out); - static void setSamplerate_eqp (EQP *a, int rate); - static void setSize_eqp (EQP *a, int size); - // RXA - static void SetEQRun (RXA& rxa, int run); - static void SetEQNC (RXA& rxa, int nc); - static void SetEQMP (RXA& rxa, int mp); - static void SetEQProfile (RXA& rxa, int nfreqs, const float* F, const float* G); - static void SetEQCtfmode (RXA& rxa, int mode); - static void SetEQWintype (RXA& rxa, int wintype); - static void SetGrphEQ (RXA& rxa, int *rxeq); - static void SetGrphEQ10 (RXA& rxa, int *rxeq); - // TXA - static void SetEQRun (TXA& txa, int run); - static void SetEQNC (TXA& txa, int nc); - static void SetEQMP (TXA& txa, int mp); - static void SetEQProfile (TXA& txa, int nfreqs, const float* F, const float* G); - static void SetEQCtfmode (TXA& txa, int mode); - static void SetEQWintype (TXA& txa, int wintype); - static void SetGrphEQ (TXA& txa, int *txeq); - static void SetGrphEQ10 (TXA& txa, int *txeq); - - static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype); - -private: - static int fEQcompare (const void * a, const void * b); -}; - -} // namespace WDSP - -#endif - - - /******************************************************************************************************** * * * Overlap-Save Equalizer * @@ -155,6 +69,7 @@ public: static void setSize_eq (EQ *a, int size); private: + static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype); static void calc_eq (EQ *a); static void decalc_eq (EQ *a); }; diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp new file mode 100644 index 000000000..651d0a7e8 --- /dev/null +++ b/wdsp/eqp.cpp @@ -0,0 +1,399 @@ +/* eq.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2013, 2016, 2017 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "eqp.hpp" +#include "fircore.hpp" +#include "fir.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +int EQP::fEQcompare (const void * a, const void * b) +{ + if (*(float*)a < *(float*)b) + return -1; + else if (*(float*)a == *(float*)b) + return 0; + else + return 1; +} + +float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype) +{ + float* fp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); + float* gp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); + float* A = new float[N / 2 + 1]; // (float *) malloc0 ((N / 2 + 1) * sizeof (float)); + float* sary = new float[2 * nfreqs]; // (float *) malloc0 (2 * nfreqs * sizeof (float)); + double gpreamp, f, frac; + float* impulse; + int i, j, mid; + fp[0] = 0.0; + fp[nfreqs + 1] = 1.0; + gpreamp = G[0]; + + for (i = 1; i <= nfreqs; i++) + { + fp[i] = 2.0 * F[i] / samplerate; + + if (fp[i] < 0.0) + fp[i] = 0.0; + + if (fp[i] > 1.0) + fp[i] = 1.0; + + gp[i] = G[i]; + } + + for (i = 1, j = 0; i <= nfreqs; i++, j+=2) + { + sary[j + 0] = fp[i]; + sary[j + 1] = gp[i]; + } + + qsort (sary, nfreqs, 2 * sizeof (float), fEQcompare); + + for (i = 1, j = 0; i <= nfreqs; i++, j+=2) + { + fp[i] = sary[j + 0]; + gp[i] = sary[j + 1]; + } + + gp[0] = gp[1]; + gp[nfreqs + 1] = gp[nfreqs]; + mid = N / 2; + j = 0; + + if (N & 1) + { + for (i = 0; i <= mid; i++) + { + f = (double)i / (double)mid; + + while ((f > fp[j + 1]) && (j < nfreqs)) + j++; + + frac = (f - fp[j]) / (fp[j + 1] - fp[j]); + A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; + } + } + else + { + for (i = 0; i < mid; i++) + { + f = ((double)i + 0.5) / (double)mid; + + while ((f > fp[j + 1]) && (j < nfreqs)) + j++; + + frac = (f - fp[j]) / (fp[j + 1] - fp[j]); + A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; + } + } + + if (ctfmode == 0) + { + int k, low, high; + double lowmag, highmag, flow4, fhigh4; + + if (N & 1) + { + low = (int)(fp[1] * mid); + high = (int)(fp[nfreqs] * mid + 0.5); + lowmag = A[low]; + highmag = A[high]; + flow4 = pow((double)low / (double)mid, 4.0); + fhigh4 = pow((double)high / (double)mid, 4.0); + k = low; + + while (--k >= 0) + { + f = (double)k / (double)mid; + lowmag *= (f * f * f * f) / flow4; + if (lowmag < 1.0e-20) lowmag = 1.0e-20; + A[k] = lowmag; + } + + k = high; + + while (++k <= mid) + { + f = (double)k / (double)mid; + highmag *= fhigh4 / (f * f * f * f); + if (highmag < 1.0e-20) highmag = 1.0e-20; + A[k] = highmag; + } + } + else + { + low = (int)(fp[1] * mid - 0.5); + high = (int)(fp[nfreqs] * mid - 0.5); + lowmag = A[low]; + highmag = A[high]; + flow4 = pow((double)low / (double)mid, 4.0); + fhigh4 = pow((double)high / (double)mid, 4.0); + k = low; + + while (--k >= 0) + { + f = (double)k / (double)mid; + lowmag *= (f * f * f * f) / flow4; + if (lowmag < 1.0e-20) lowmag = 1.0e-20; + A[k] = lowmag; + } + + k = high; + + while (++k < mid) + { + f = (double)k / (double)mid; + highmag *= fhigh4 / (f * f * f * f); + if (highmag < 1.0e-20) highmag = 1.0e-20; + A[k] = highmag; + } + } + } + + if (N & 1) + impulse = FIR::fir_fsamp_odd(N, A, 1, 1.0, wintype); + else + impulse = FIR::fir_fsamp(N, A, 1, 1.0, wintype); + + // print_impulse("eq.txt", N, impulse, 1, 0); + delete[] (sary); + delete[] (A); + delete[] (gp); + delete[] (fp); + return impulse; +} + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save Equalizer * +* * +********************************************************************************************************/ + +EQP::EQP( + int _run, + int _size, + int _nc, + int _mp, + float *_in, + float *_out, + int _nfreqs, + float* _F, + float* _G, + int _ctfmode, + int _wintype, + int _samplerate +) +{ + // NOTE: 'nc' must be >= 'size' + float* impulse; + run = _run; + size = _size; + nc = _nc; + mp = _mp; + in = _in; + out = _out; + nfreqs = _nfreqs; + F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + memcpy (F, _F, (_nfreqs + 1) * sizeof (float)); + memcpy (G, _G, (_nfreqs + 1) * sizeof (float)); + ctfmode = _ctfmode; + wintype = _wintype; + samplerate = (double) _samplerate; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); + delete[] (impulse); +} + +EQP::~EQP() +{ + FIRCORE::destroy_fircore (fircore); +} + +void EQP::flush() +{ + FIRCORE::flush_fircore (fircore); +} + +void EQP::execute() +{ + if (run) + FIRCORE::xfircore (fircore); + else + std::copy(in, in + size * 2, out); +} + +void EQP::setBuffers(float* _in, float* _out) +{ + in = _in; + out = _out; + FIRCORE::setBuffers_fircore (fircore, in, out); +} + +void EQP::setSamplerate(int rate) +{ + float* impulse; + samplerate = rate; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +void EQP::setSize(int _size) +{ + float* impulse; + size = _size; + FIRCORE::setSize_fircore (fircore, size); + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save Equalizer: Public Properties * +* * +********************************************************************************************************/ + +void EQP::setRun(int _run) +{ + run = _run; +} + +void EQP::setNC(int _nc) +{ + float* impulse; + + if (nc != _nc) + { + nc = _nc; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setNc_fircore (fircore, nc, impulse); + delete[] (impulse); + } +} + +void EQP::setMP(int _mp) +{ + if (mp != _mp) + { + mp = _mp; + FIRCORE::setMp_fircore (fircore, mp); + } +} + +void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) +{ + float* impulse; + delete[] (G); + delete[] (F); + nfreqs = _nfreqs; + F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + memcpy (F, _F, (_nfreqs + 1) * sizeof (float)); + memcpy (G, _G, (_nfreqs + 1) * sizeof (float)); + impulse = eq_impulse (nc, nfreqs, F, G, + samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +void EQP::setCtfmode(int _mode) +{ + float* impulse; + ctfmode = _mode; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +void EQP::setWintype(int _wintype) +{ + float* impulse; + wintype = _wintype; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +void EQP::setGrphEQ(int *rxeq) +{ // three band equalizer (legacy compatibility) + float* impulse; + delete[] (G); + delete[] (F); + nfreqs = 4; + F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F[1] = 150.0; + F[2] = 400.0; + F[3] = 1500.0; + F[4] = 6000.0; + G[0] = (float)rxeq[0]; + G[1] = (float)rxeq[1]; + G[2] = (float)rxeq[1]; + G[3] = (float)rxeq[2]; + G[4] = (float)rxeq[3]; + ctfmode = 0; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +void EQP::setGrphEQ10(int *rxeq) +{ // ten band equalizer (legacy compatibility) + float* impulse; + int i; + delete[] (G); + delete[] (F); + nfreqs = 10; + F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F[1] = 32.0; + F[2] = 63.0; + F[3] = 125.0; + F[4] = 250.0; + F[5] = 500.0; + F[6] = 1000.0; + F[7] = 2000.0; + F[8] = 4000.0; + F[9] = 8000.0; + F[10] = 16000.0; + for (i = 0; i <= nfreqs; i++) + G[i] = (float)rxeq[i]; + ctfmode = 0; + impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + // print_impulse ("rxeq.txt", nc, impulse, 1, 0); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] (impulse); +} + +} // namespace WDSP diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp new file mode 100644 index 000000000..3d1430a25 --- /dev/null +++ b/wdsp/eqp.hpp @@ -0,0 +1,101 @@ +/* eq.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2013, 2016 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save Equalizer * +* * +********************************************************************************************************/ + +#ifndef wdsp_eqp_h +#define wdsp_eqp_h + +#include "export.h" + +namespace WDSP { + +class FIRCORE; +class RXA; +class TXA; + +class WDSP_API EQP +{ +public: + int run; + int size; + int nc; + int mp; + float* in; + float* out; + int nfreqs; + float* F; + float* G; + int ctfmode; + int wintype; + double samplerate; + FIRCORE *fircore; + + EQP( + int run, + int size, + int nc, + int mp, + float *in, + float *out, + int nfreqs, + float* F, + float* G, + int ctfmode, + int wintype, + int samplerate + ); + ~EQP(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public properties + void setRun(int run); + void setNC(int nc); + void setMP(int mp); + void setProfile(int nfreqs, const float* F, const float* G); + void setCtfmode(int mode); + void setWintype(int wintype); + void setGrphEQ(int *rxeq); + void setGrphEQ10(int *rxeq); + + static float* eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype); + +private: + static int fEQcompare (const void * a, const void * b); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/fmmod.cpp b/wdsp/fmmod.cpp index 3d400ac51..59bdaaacf 100644 --- a/wdsp/fmmod.cpp +++ b/wdsp/fmmod.cpp @@ -167,7 +167,7 @@ void FMMOD::setSize_fmmod (FMMOD *a, int size) void FMMOD::SetFMDeviation (TXA& txa, float deviation) { - FMMOD *a = txa.fmmod.p; + FMMOD *a = txa.fmmod; float bp_fc = a->f_high + deviation; float* impulse = FIR::fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); FIRCORE::setImpulse_fircore (a->p, impulse, 0); @@ -184,7 +184,7 @@ void FMMOD::SetFMDeviation (TXA& txa, float deviation) void FMMOD::SetCTCSSFreq (TXA& txa, float freq) { FMMOD *a; - a = txa.fmmod.p; + a = txa.fmmod; a->ctcss_freq = freq; a->tphase = 0.0; a->tdelta = TWOPI * a->ctcss_freq / a->samplerate; @@ -192,14 +192,14 @@ void FMMOD::SetCTCSSFreq (TXA& txa, float freq) void FMMOD::SetCTCSSRun (TXA& txa, int run) { - txa.fmmod.p->ctcss_run = run; + txa.fmmod->ctcss_run = run; } void FMMOD::SetFMNC (TXA& txa, int nc) { FMMOD *a; float* impulse; - a = txa.fmmod.p; + a = txa.fmmod; if (a->nc != nc) { @@ -213,7 +213,7 @@ void FMMOD::SetFMNC (TXA& txa, int nc) void FMMOD::SetFMMP (TXA& txa, int mp) { FMMOD *a; - a = txa.fmmod.p; + a = txa.fmmod; if (a->mp != mp) { a->mp = mp; @@ -225,7 +225,7 @@ void FMMOD::SetFMAFFreqs (TXA& txa, float low, float high) { FMMOD *a; float* impulse; - a = txa.fmmod.p; + a = txa.fmmod; if (a->f_low != low || a->f_high != high) { diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 158c0021c..fb673a954 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -27,7 +27,7 @@ warren@wpratt.com #include "comm.hpp" #include "fircore.hpp" -#include "eq.hpp" +#include "eqp.hpp" #include "fmsq.hpp" namespace WDSP { @@ -123,7 +123,6 @@ FMSQ::FMSQ( rate((double) _rate), fc(_fc), pllpole(_pllpole), - tdelay(_tdelay), avtau(_avtau), longtau(_longtau), tup(_tup), @@ -132,6 +131,7 @@ FMSQ::FMSQ( unmute_thresh(_unmute_thresh), min_tail(_min_tail), max_tail(_max_tail), + tdelay(_tdelay), nc(_nc), mp(_mp) { diff --git a/wdsp/gen.cpp b/wdsp/gen.cpp index bb541d3d5..76f56927d 100644 --- a/wdsp/gen.cpp +++ b/wdsp/gen.cpp @@ -449,151 +449,151 @@ void GEN::SetPreSweepRate(float rate) void GEN::SetPreGenRun (TXA& txa, int run) { - txa.gen0.p->run = run; + txa.gen0->run = run; } void GEN::SetPreGenMode (TXA& txa, int mode) { - txa.gen0.p->mode = mode; + txa.gen0->mode = mode; } void GEN::SetPreGenToneMag (TXA& txa, float mag) { - txa.gen0.p->tone.mag = mag; + txa.gen0->tone.mag = mag; } void GEN::SetPreGenToneFreq (TXA& txa, float freq) { - txa.gen0.p->tone.freq = freq; - txa.gen0.p->calc_tone(); + txa.gen0->tone.freq = freq; + txa.gen0->calc_tone(); } void GEN::SetPreGenNoiseMag (TXA& txa, float mag) { - txa.gen0.p->noise.mag = mag; + txa.gen0->noise.mag = mag; } void GEN::SetPreGenSweepMag (TXA& txa, float mag) { - txa.gen0.p->sweep.mag = mag; + txa.gen0->sweep.mag = mag; } void GEN::SetPreGenSweepFreq (TXA& txa, float freq1, float freq2) { - txa.gen0.p->sweep.f1 = freq1; - txa.gen0.p->sweep.f2 = freq2; - txa.gen0.p->calc_sweep(); + txa.gen0->sweep.f1 = freq1; + txa.gen0->sweep.f2 = freq2; + txa.gen0->calc_sweep(); } void GEN::SetPreGenSweepRate (TXA& txa, float rate) { - txa.gen0.p->sweep.sweeprate = rate; - txa.gen0.p->calc_sweep(); + txa.gen0->sweep.sweeprate = rate; + txa.gen0->calc_sweep(); } void GEN::SetPreGenSawtoothMag (TXA& txa, float mag) { - txa.gen0.p->saw.mag = mag; + txa.gen0->saw.mag = mag; } void GEN::SetPreGenSawtoothFreq (TXA& txa, float freq) { - txa.gen0.p->saw.f = freq; - txa.gen0.p->calc_sawtooth(); + txa.gen0->saw.f = freq; + txa.gen0->calc_sawtooth(); } void GEN::SetPreGenTriangleMag (TXA& txa, float mag) { - txa.gen0.p->tri.mag = mag; + txa.gen0->tri.mag = mag; } void GEN::SetPreGenTriangleFreq (TXA& txa, float freq) { - txa.gen0.p->tri.f = freq; - txa.gen0.p->calc_triangle(); + txa.gen0->tri.f = freq; + txa.gen0->calc_triangle(); } void GEN::SetPreGenPulseMag (TXA& txa, float mag) { - txa.gen0.p->pulse.mag = mag; + txa.gen0->pulse.mag = mag; } void GEN::SetPreGenPulseFreq (TXA& txa, float freq) { - txa.gen0.p->pulse.pf = freq; - txa.gen0.p->calc_pulse(); + txa.gen0->pulse.pf = freq; + txa.gen0->calc_pulse(); } void GEN::SetPreGenPulseDutyCycle (TXA& txa, float dc) { - txa.gen0.p->pulse.pdutycycle = dc; - txa.gen0.p->calc_pulse(); + txa.gen0->pulse.pdutycycle = dc; + txa.gen0->calc_pulse(); } void GEN::SetPreGenPulseToneFreq (TXA& txa, float freq) { - txa.gen0.p->pulse.tf = freq; - txa.gen0.p->calc_pulse(); + txa.gen0->pulse.tf = freq; + txa.gen0->calc_pulse(); } void GEN::SetPreGenPulseTransition (TXA& txa, float transtime) { - txa.gen0.p->pulse.ptranstime = transtime; - txa.gen0.p->calc_pulse(); + txa.gen0->pulse.ptranstime = transtime; + txa.gen0->calc_pulse(); } // 'PostGen', gen1 void GEN::SetPostGenRun (TXA& txa, int run) { - txa.gen1.p->run = run; + txa.gen1->run = run; } void GEN::SetPostGenMode (TXA& txa, int mode) { - txa.gen1.p->mode = mode; + txa.gen1->mode = mode; } void GEN::SetPostGenToneMag (TXA& txa, float mag) { - txa.gen1.p->tone.mag = mag; + txa.gen1->tone.mag = mag; } void GEN::SetPostGenToneFreq (TXA& txa, float freq) { - txa.gen1.p->tone.freq = freq; - txa.gen1.p->calc_tone(); + txa.gen1->tone.freq = freq; + txa.gen1->calc_tone(); } void GEN::SetPostGenTTMag (TXA& txa, float mag1, float mag2) { - txa.gen1.p->tt.mag1 = mag1; - txa.gen1.p->tt.mag2 = mag2; + txa.gen1->tt.mag1 = mag1; + txa.gen1->tt.mag2 = mag2; } void GEN::SetPostGenTTFreq (TXA& txa, float freq1, float freq2) { - txa.gen1.p->tt.f1 = freq1; - txa.gen1.p->tt.f2 = freq2; - txa.gen1.p->calc_tt(); + txa.gen1->tt.f1 = freq1; + txa.gen1->tt.f2 = freq2; + txa.gen1->calc_tt(); } void GEN::SetPostGenSweepMag (TXA& txa, float mag) { - txa.gen1.p->sweep.mag = mag; + txa.gen1->sweep.mag = mag; } void GEN::SetPostGenSweepFreq (TXA& txa, float freq1, float freq2) { - txa.gen1.p->sweep.f1 = freq1; - txa.gen1.p->sweep.f2 = freq2; - txa.gen1.p->calc_sweep(); + txa.gen1->sweep.f1 = freq1; + txa.gen1->sweep.f2 = freq2; + txa.gen1->calc_sweep(); } void GEN::SetPostGenSweepRate (TXA& txa, float rate) { - txa.gen1.p->sweep.sweeprate = rate; - txa.gen1.p->calc_sweep(); + txa.gen1->sweep.sweeprate = rate; + txa.gen1->calc_sweep(); } } // namespace WDSP diff --git a/wdsp/iir.cpp b/wdsp/iir.cpp index 754d5c376..7080d5744 100644 --- a/wdsp/iir.cpp +++ b/wdsp/iir.cpp @@ -663,7 +663,7 @@ void PHROT::setSize_phrot (PHROT *a, int size) void PHROT::SetPHROTRun (TXA& txa, int run) { - PHROT *a = txa.phrot.p; + PHROT *a = txa.phrot; a->run = run; if (a->run) @@ -672,7 +672,7 @@ void PHROT::SetPHROTRun (TXA& txa, int run) void PHROT::SetPHROTCorner (TXA& txa, double corner) { - PHROT *a = txa.phrot.p; + PHROT *a = txa.phrot; decalc_phrot (a); a->fc = corner; calc_phrot (a); @@ -680,7 +680,7 @@ void PHROT::SetPHROTCorner (TXA& txa, double corner) void PHROT::SetPHROTNstages (TXA& txa, int nstages) { - PHROT *a = txa.phrot.p; + PHROT *a = txa.phrot; decalc_phrot (a); a->nstages = nstages; calc_phrot (a); @@ -688,7 +688,7 @@ void PHROT::SetPHROTNstages (TXA& txa, int nstages) void PHROT::SetPHROTReverse (TXA& txa, int reverse) { - PHROT *a = txa.phrot.p; + PHROT *a = txa.phrot; a->reverse = reverse; } diff --git a/wdsp/osctrl.cpp b/wdsp/osctrl.cpp index aee850633..30876fcd1 100644 --- a/wdsp/osctrl.cpp +++ b/wdsp/osctrl.cpp @@ -146,9 +146,9 @@ void OSCTRL::setSize_osctrl (OSCTRL *a, int size) void OSCTRL::SetosctrlRun (TXA& txa, int run) { - if (txa.osctrl.p->run != run) + if (txa.osctrl->run != run) { - txa.osctrl.p->run = run; + txa.osctrl->run = run; TXA::SetupBPFilters (txa); } } diff --git a/wdsp/patchpanel.cpp b/wdsp/patchpanel.cpp index 8a2b91c63..b3d23f1dc 100644 --- a/wdsp/patchpanel.cpp +++ b/wdsp/patchpanel.cpp @@ -195,23 +195,23 @@ void PANEL::SetPanelBinaural (RXA& rxa, int bin) void PANEL::SetPanelRun (TXA& txa, int run) { - txa.panel.p->run = run; + txa.panel->run = run; } void PANEL::SetPanelGain1 (TXA& txa, double gain) { - txa.panel.p->gain1 = gain; + txa.panel->gain1 = gain; //print_message ("micgainset.txt", "Set MIC Gain to", (int)(100.0 * gain), 0, 0); } void PANEL::SetPanelSelect (TXA& txa, int select) { if (select == 1) - txa.panel.p->copy = 3; + txa.panel->copy = 3; else - txa.panel.p->copy = 0; + txa.panel->copy = 0; - txa.panel.p->inselect = select; + txa.panel->inselect = select; } } // namespace WDSP diff --git a/wdsp/siphon.cpp b/wdsp/siphon.cpp index 393a325b5..bbfe583cc 100644 --- a/wdsp/siphon.cpp +++ b/wdsp/siphon.cpp @@ -226,25 +226,25 @@ void SIPHON::GetaSipF1 (RXA& rxa, float* out, int size) void SIPHON::SetSipPosition (TXA& txa, int pos) { - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; a->position = pos; } void SIPHON::SetSipMode (TXA& txa, int mode) { - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; a->mode = mode; } void SIPHON::SetSipDisplay (TXA& txa, int disp) { - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; a->disp = disp; } void SIPHON::GetaSipF (TXA& txa, float* out, int size) { // return raw samples as floats - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; int i; a->outsize = size; suck (a); @@ -256,7 +256,7 @@ void SIPHON::GetaSipF (TXA& txa, float* out, int size) void SIPHON::GetaSipF1 (TXA& txa, float* out, int size) { // return raw samples as floats - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; int i; a->outsize = size; suck (a); @@ -270,7 +270,7 @@ void SIPHON::GetaSipF1 (TXA& txa, float* out, int size) void SIPHON::SetSipSpecmode (TXA& txa, int mode) { - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; if (mode == 0) a->specmode = 0; else @@ -279,7 +279,7 @@ void SIPHON::SetSipSpecmode (TXA& txa, int mode) void SIPHON::GetSpecF1 (TXA& txa, float* out) { // return spectrum magnitudes in dB - SIPHON *a = txa.sip1.p; + SIPHON *a = txa.sip1; int i, j, mid, m, n; a->outsize = a->fftsize; suck (a); diff --git a/wdsp/slew.cpp b/wdsp/slew.cpp index 0a9814f18..04b47eeda 100644 --- a/wdsp/slew.cpp +++ b/wdsp/slew.cpp @@ -198,7 +198,7 @@ void USLEW::setSize_uslew (USLEW *a, int size) void USLEW::SetuSlewTime (TXA& txa, float time) { // NOTE: 'time' is in seconds - USLEW *a = txa.uslew.p; + USLEW *a = txa.uslew; decalc_uslew (a); a->tupslew = time; calc_uslew (a); diff --git a/wdsp/snba.cpp b/wdsp/snba.cpp index 4ad4e901c..1783066c5 100644 --- a/wdsp/snba.cpp +++ b/wdsp/snba.cpp @@ -35,238 +35,251 @@ warren@wpratt.com #include "anr.hpp" #include "emnr.hpp" #include "snba.hpp" -#include "RXA.hpp" #define MAXIMP 256 namespace WDSP { -void SNBA::calc_snba (SNBA *d) +void SNBA::calc() { - if (d->inrate >= d->internalrate) - d->isize = d->bsize / (d->inrate / d->internalrate); + if (inrate >= internalrate) + isize = bsize / (inrate / internalrate); else - d->isize = d->bsize * (d->internalrate / d->inrate); + isize = bsize * (internalrate / inrate); - d->inbuff = new float[d->isize * 2]; // (double *) malloc0 (d->isize * sizeof (complex)); - d->outbuff = new float[d->isize * 2]; // (double *) malloc0 (d->isize * sizeof (complex)); + inbuff = new float[isize * 2]; // (double *) malloc0 (isize * sizeof (complex)); + outbuff = new float[isize * 2]; // (double *) malloc0 (isize * sizeof (complex)); - if (d->inrate != d->internalrate) - d->resamprun = 1; + if (inrate != internalrate) + resamprun = 1; else - d->resamprun = 0; + resamprun = 0; - d->inresamp = new RESAMPLE( - d->resamprun, - d->bsize, - d->in, - d->inbuff, - d->inrate, - d->internalrate, + inresamp = new RESAMPLE( + resamprun, + bsize, + in, + inbuff, + inrate, + internalrate, 0.0, 0, 2.0 ); - d->inresamp->setFCLow(250.0); - d->outresamp = new RESAMPLE( - d->resamprun, - d->isize, - d->outbuff, - d->out, - d->internalrate, - d->inrate, + inresamp->setFCLow(250.0); + outresamp = new RESAMPLE( + resamprun, + isize, + outbuff, + out, + internalrate, + inrate, 0.0, 0, 2.0 ); - d->outresamp->setFCLow(200.0); - d->incr = d->xsize / d->ovrlp; + outresamp->setFCLow(200.0); + incr = xsize / ovrlp; - if (d->incr > d->isize) - d->iasize = d->incr; + if (incr > isize) + iasize = incr; else - d->iasize = d->isize; + iasize = isize; - d->iainidx = 0; - d->iaoutidx = 0; - d->inaccum = new double[d->iasize * 2]; // (double *) malloc0 (d->iasize * sizeof (double)); - d->nsamps = 0; + iainidx = 0; + iaoutidx = 0; + inaccum = new double[iasize * 2]; // (double *) malloc0 (iasize * sizeof (double)); + nsamps = 0; - if (d->incr > d->isize) + if (incr > isize) { - d->oasize = d->incr; - d->oainidx = 0; - d->oaoutidx = d->isize; + oasize = incr; + oainidx = 0; + oaoutidx = isize; } else { - d->oasize = d->isize; - d->oainidx = 0; - d->oaoutidx = 0; + oasize = isize; + oainidx = 0; + oaoutidx = 0; } - d->init_oaoutidx = d->oaoutidx; - d->outaccum = new double[d->oasize * 2]; // (double *) malloc0 (d->oasize * sizeof (double)); + init_oaoutidx = oaoutidx; + outaccum = new double[oasize * 2]; // (double *) malloc0 (oasize * sizeof (double)); } -SNBA* SNBA::create_snba ( - int run, - float* in, - float* out, - int inrate, - int internalrate, - int bsize, - int ovrlp, - int xsize, - int asize, - int npasses, - double k1, - double k2, - int b, - int pre, - int post, - double pmultmin, - double out_low_cut, - double out_high_cut -) +SNBA::SNBA( + int _run, + float* _in, + float* _out, + int _inrate, + int _internalrate, + int _bsize, + int _ovrlp, + int _xsize, + int _asize, + int _npasses, + double _k1, + double _k2, + int _b, + int _pre, + int _post, + double _pmultmin, + double _out_low_cut, + double _out_high_cut +) : + run(_run), + in(_in), + out(_out), + inrate(_inrate), + internalrate(_internalrate), + bsize(_bsize), + xsize(_xsize), + ovrlp(_ovrlp), + incr(0), + iasize(0), + iainidx(0), + iaoutidx(0), + inaccum(nullptr), + xbase(nullptr), + xaux(nullptr), + nsamps(0), + oasize(0), + oainidx(0), + oaoutidx(0), + init_oaoutidx(0), + outaccum(nullptr), + resamprun(0), + isize(0), + inresamp(nullptr), + outresamp(nullptr), + inbuff(nullptr), + outbuff(nullptr), + out_low_cut(_out_low_cut), + out_high_cut(_out_high_cut) { - SNBA *d = new SNBA; - d->run = run; - d->in = in; - d->out = out; - d->inrate = inrate; - d->internalrate = internalrate; - d->bsize = bsize; - d->ovrlp = ovrlp; - d->xsize = xsize; - d->exec.asize = asize; - d->exec.npasses = npasses; - d->sdet.k1 = k1; - d->sdet.k2 = k2; - d->sdet.b = b; - d->sdet.pre = pre; - d->sdet.post = post; - d->scan.pmultmin = pmultmin; - d->out_low_cut = out_low_cut; - d->out_high_cut = out_high_cut; + exec.asize = _asize; + exec.npasses = _npasses; + sdet.k1 = _k1; + sdet.k2 = _k2; + sdet.b = _b; + sdet.pre = _pre; + sdet.post = _post; + scan.pmultmin = _pmultmin; - calc_snba (d); + calc(); - d->xbase = new double[2 * d->xsize]; // (double *) malloc0 (2 * d->xsize * sizeof (double)); - d->xaux = d->xbase + d->xsize; - d->exec.a = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); - d->exec.v = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); - d->exec.detout = new int[d->xsize]; //(int *) malloc0 (d->xsize * sizeof (int)); - d->exec.savex = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); - d->exec.xHout = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); - d->exec.unfixed = new int[d->xsize]; //(int *) malloc0 (d->xsize * sizeof (int)); - d->sdet.vp = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); - d->sdet.vpwr = new double[d->xsize]; //(double *) malloc0 (d->xsize * sizeof (double)); + xbase = new double[2 * xsize]; // (double *) malloc0 (2 * xsize * sizeof (double)); + xaux = xbase + xsize; + exec.a = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); + exec.v = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); + exec.detout = new int[xsize]; //(int *) malloc0 (xsize * sizeof (int)); + exec.savex = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); + exec.xHout = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); + exec.unfixed = new int[xsize]; //(int *) malloc0 (xsize * sizeof (int)); + sdet.vp = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); + sdet.vpwr = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - d->wrk.xHat_a1rows_max = d->xsize + d->exec.asize; - d->wrk.xHat_a2cols_max = d->xsize + 2 * d->exec.asize; - d->wrk.xHat_r = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof(double)); - d->wrk.xHat_ATAI = new double[d->xsize * d->xsize]; // (double *) malloc0 (d->xsize * d->xsize * sizeof(double)); - d->wrk.xHat_A1 = new double[d->wrk.xHat_a1rows_max * d->xsize]; // (double *) malloc0 (d->wrk.xHat_a1rows_max * d->xsize * sizeof(double)); - d->wrk.xHat_A2 = new double[d->wrk.xHat_a1rows_max * d->wrk.xHat_a2cols_max]; // (double *) malloc0 (d->wrk.xHat_a1rows_max * d->wrk.xHat_a2cols_max * sizeof(double)); - d->wrk.xHat_P1 = new double[d->xsize * d->wrk.xHat_a2cols_max]; // (double *) malloc0 (d->xsize * d->wrk.xHat_a2cols_max * sizeof(double)); - d->wrk.xHat_P2 = new double[d->xsize]; // (double *) malloc0 (d->xsize * sizeof(double)); - d->wrk.trI_y = new double[d->xsize - 1]; // (double *) malloc0 ((d->xsize - 1) * sizeof(double)); - d->wrk.trI_v = new double[d->xsize - 1]; // (double *) malloc0 ((d->xsize - 1) * sizeof(double)); - d->wrk.dR_z = new double[d->xsize - 2]; // (double *) malloc0 ((d->xsize - 2) * sizeof(double)); - d->wrk.asolve_r = new double[d->exec.asize + 1]; // (double *) malloc0 ((d->exec.asize + 1) * sizeof(double)); - d->wrk.asolve_z = new double[d->exec.asize + 1]; // (double *) malloc0 ((d->exec.asize + 1) * sizeof(double)); - - return d; + wrk.xHat_a1rows_max = xsize + exec.asize; + wrk.xHat_a2cols_max = xsize + 2 * exec.asize; + wrk.xHat_r = new double[xsize]; // (double *) malloc0 (xsize * sizeof(double)); + wrk.xHat_ATAI = new double[xsize * xsize]; // (double *) malloc0 (xsize * xsize * sizeof(double)); + wrk.xHat_A1 = new double[wrk.xHat_a1rows_max * xsize]; // (double *) malloc0 (wrk.xHat_a1rows_max * xsize * sizeof(double)); + wrk.xHat_A2 = new double[wrk.xHat_a1rows_max * wrk.xHat_a2cols_max]; // (double *) malloc0 (wrk.xHat_a1rows_max * wrk.xHat_a2cols_max * sizeof(double)); + wrk.xHat_P1 = new double[xsize * wrk.xHat_a2cols_max]; // (double *) malloc0 (xsize * wrk.xHat_a2cols_max * sizeof(double)); + wrk.xHat_P2 = new double[xsize]; // (double *) malloc0 (xsize * sizeof(double)); + wrk.trI_y = new double[xsize - 1]; // (double *) malloc0 ((xsize - 1) * sizeof(double)); + wrk.trI_v = new double[xsize - 1]; // (double *) malloc0 ((xsize - 1) * sizeof(double)); + wrk.dR_z = new double[xsize - 2]; // (double *) malloc0 ((xsize - 2) * sizeof(double)); + wrk.asolve_r = new double[exec.asize + 1]; // (double *) malloc0 ((exec.asize + 1) * sizeof(double)); + wrk.asolve_z = new double[exec.asize + 1]; // (double *) malloc0 ((exec.asize + 1) * sizeof(double)); } -void SNBA::decalc_snba (SNBA *d) +void SNBA::decalc() { - delete (d->outresamp); - delete (d->inresamp); - delete[] (d->outbuff); - delete[] (d->inbuff); - delete[] (d->outaccum); - delete[] (d->inaccum); + delete (outresamp); + delete (inresamp); + delete[] (outbuff); + delete[] (inbuff); + delete[] (outaccum); + delete[] (inaccum); } -void SNBA::destroy_snba (SNBA *d) +SNBA::~SNBA() { - delete[] (d->wrk.xHat_r); - delete[] (d->wrk.xHat_ATAI); - delete[] (d->wrk.xHat_A1); - delete[] (d->wrk.xHat_A2); - delete[] (d->wrk.xHat_P1); - delete[] (d->wrk.xHat_P2); - delete[] (d->wrk.trI_y); - delete[] (d->wrk.trI_v); - delete[] (d->wrk.dR_z); - delete[] (d->wrk.asolve_r); - delete[] (d->wrk.asolve_z); + delete[] (wrk.xHat_r); + delete[] (wrk.xHat_ATAI); + delete[] (wrk.xHat_A1); + delete[] (wrk.xHat_A2); + delete[] (wrk.xHat_P1); + delete[] (wrk.xHat_P2); + delete[] (wrk.trI_y); + delete[] (wrk.trI_v); + delete[] (wrk.dR_z); + delete[] (wrk.asolve_r); + delete[] (wrk.asolve_z); - delete[] (d->sdet.vpwr); - delete[] (d->sdet.vp); - delete[] (d->exec.unfixed); - delete[] (d->exec.xHout); - delete[] (d->exec.savex); - delete[] (d->exec.detout); - delete[] (d->exec.v); - delete[] (d->exec.a); + delete[] (sdet.vpwr); + delete[] (sdet.vp); + delete[] (exec.unfixed); + delete[] (exec.xHout); + delete[] (exec.savex); + delete[] (exec.detout); + delete[] (exec.v); + delete[] (exec.a); - delete[] (d->xbase); + delete[] (xbase); - decalc_snba (d); - - delete[] (d); + decalc(); } -void SNBA::flush_snba (SNBA *d) +void SNBA::flush() { - d->iainidx = 0; - d->iaoutidx = 0; - d->nsamps = 0; - d->oainidx = 0; - d->oaoutidx = d->init_oaoutidx; + iainidx = 0; + iaoutidx = 0; + nsamps = 0; + oainidx = 0; + oaoutidx = init_oaoutidx; - memset (d->inaccum, 0, d->iasize * sizeof (double)); - memset (d->outaccum, 0, d->oasize * sizeof (double)); - memset (d->xaux, 0, d->xsize * sizeof (double)); - memset (d->exec.a, 0, d->xsize * sizeof (double)); - memset (d->exec.v, 0, d->xsize * sizeof (double)); - memset (d->exec.detout, 0, d->xsize * sizeof (int)); - memset (d->exec.savex, 0, d->xsize * sizeof (double)); - memset (d->exec.xHout, 0, d->xsize * sizeof (double)); - memset (d->exec.unfixed, 0, d->xsize * sizeof (int)); - memset (d->sdet.vp, 0, d->xsize * sizeof (double)); - memset (d->sdet.vpwr, 0, d->xsize * sizeof (double)); + memset (inaccum, 0, iasize * sizeof (double)); + memset (outaccum, 0, oasize * sizeof (double)); + memset (xaux, 0, xsize * sizeof (double)); + memset (exec.a, 0, xsize * sizeof (double)); + memset (exec.v, 0, xsize * sizeof (double)); + memset (exec.detout, 0, xsize * sizeof (int)); + memset (exec.savex, 0, xsize * sizeof (double)); + memset (exec.xHout, 0, xsize * sizeof (double)); + memset (exec.unfixed, 0, xsize * sizeof (int)); + memset (sdet.vp, 0, xsize * sizeof (double)); + memset (sdet.vpwr, 0, xsize * sizeof (double)); - std::fill(d->inbuff, d->inbuff + d->isize * 2, 0); - std::fill(d->outbuff, d->outbuff + d->isize * 2, 0); + std::fill(inbuff, inbuff + isize * 2, 0); + std::fill(outbuff, outbuff + isize * 2, 0); - d->inresamp->flush(); - d->outresamp->flush(); + inresamp->flush(); + outresamp->flush(); } -void SNBA::setBuffers_snba (SNBA *a, float* in, float* out) +void SNBA::setBuffers(float* _in, float* _out) { - decalc_snba (a); - a->in = in; - a->out = out; - calc_snba (a); + decalc(); + in = _in; + out = _out; + calc(); } -void SNBA::setSamplerate_snba (SNBA *a, int rate) +void SNBA::setSamplerate(int rate) { - decalc_snba (a); - a->inrate = rate; - calc_snba (a); + decalc(); + inrate = rate; + calc(); } -void SNBA::setSize_snba (SNBA *a, int size) +void SNBA::setSize(int size) { - decalc_snba (a); - a->bsize = size; - calc_snba (a); + decalc(); + bsize = size; + calc(); } void SNBA::ATAc0 (int n, int nr, double* A, double* r) @@ -404,35 +417,35 @@ void SNBA::invf(int xsize, int asize, double* a, double* x, double* v) } } -void SNBA::det(SNBA *d, int asize, double* v, int* detout) +void SNBA::det(int asize, double* v, int* detout) { int i, j; double medpwr; double t1, t2; int bstate, bcount, bsamp; - for (i = asize, j = 0; i < d->xsize; i++, j++) + for (i = asize, j = 0; i < xsize; i++, j++) { - d->sdet.vpwr[i] = v[i] * v[i]; - d->sdet.vp[j] = d->sdet.vpwr[i]; + sdet.vpwr[i] = v[i] * v[i]; + sdet.vp[j] = sdet.vpwr[i]; } - LMathd::median(d->xsize - asize, d->sdet.vp, &medpwr); - t1 = d->sdet.k1 * medpwr; + LMathd::median(xsize - asize, sdet.vp, &medpwr); + t1 = sdet.k1 * medpwr; t2 = 0.0; - for (i = asize; i < d->xsize; i++) + for (i = asize; i < xsize; i++) { - if (d->sdet.vpwr[i] <= t1) - t2 += d->sdet.vpwr[i]; - else if (d->sdet.vpwr[i] <= 2.0 * t1) - t2 += 2.0 * t1 - d->sdet.vpwr[i]; + if (sdet.vpwr[i] <= t1) + t2 += sdet.vpwr[i]; + else if (sdet.vpwr[i] <= 2.0 * t1) + t2 += 2.0 * t1 - sdet.vpwr[i]; } - t2 *= d->sdet.k2 / (double) (d->xsize - asize); + t2 *= sdet.k2 / (double) (xsize - asize); - for (i = asize; i < d->xsize; i++) + for (i = asize; i < xsize; i++) { - if (d->sdet.vpwr[i] > t2) + if (sdet.vpwr[i] > t2) detout[i] = 1; else detout[i] = 0; @@ -442,7 +455,7 @@ void SNBA::det(SNBA *d, int asize, double* v, int* detout) bcount = 0; bsamp = 0; - for (i = asize; i < d->xsize; i++) + for (i = asize; i < xsize; i++) { switch (bstate) { @@ -465,7 +478,7 @@ void SNBA::det(SNBA *d, int asize, double* v, int* detout) case 2: ++bcount; - if (bcount > d->sdet.b) + if (bcount > sdet.b) { if (detout[i] == 1) bstate = 1; @@ -483,11 +496,11 @@ void SNBA::det(SNBA *d, int asize, double* v, int* detout) } } - for (i = asize; i < d->xsize; i++) + for (i = asize; i < xsize; i++) { if (detout[i] == 1) { - for (j = i - 1; j > i - 1 - d->sdet.pre; j--) + for (j = i - 1; j > i - 1 - sdet.pre; j--) { if (j >= asize) detout[j] = 1; @@ -495,13 +508,13 @@ void SNBA::det(SNBA *d, int asize, double* v, int* detout) } } - for (i = d->xsize - 1; i >= asize; i--) + for (i = xsize - 1; i >= asize; i--) { if (detout[i] == 1) { - for (j = i + 1; j < i + 1 + d->sdet.post; j++) + for (j = i + 1; j < i + 1 + sdet.post; j++) { - if (j < d->xsize) + if (j < xsize) detout[j] = 1; } } @@ -619,7 +632,7 @@ int SNBA::scanFrame( return nimp; } -void SNBA::execFrame(SNBA *d, double* x) +void SNBA::execFrame(double* x) { int i, k; int pass; @@ -631,200 +644,190 @@ void SNBA::execFrame(SNBA *d, double* x) int p_opt[MAXIMP]; int next = 0; int p; - memcpy (d->exec.savex, x, d->xsize * sizeof (double)); - LMathd::asolve(d->xsize, d->exec.asize, x, d->exec.a, d->wrk.asolve_r, d->wrk.asolve_z); - invf(d->xsize, d->exec.asize, d->exec.a, x, d->exec.v); - det(d, d->exec.asize, d->exec.v, d->exec.detout); + memcpy (exec.savex, x, xsize * sizeof (double)); + LMathd::asolve(xsize, exec.asize, x, exec.a, wrk.asolve_r, wrk.asolve_z); + invf(xsize, exec.asize, exec.a, x, exec.v); + det(exec.asize, exec.v, exec.detout); - for (i = 0; i < d->xsize; i++) + for (i = 0; i < xsize; i++) { - if (d->exec.detout[i] != 0) + if (exec.detout[i] != 0) x[i] = 0.0; } - nimp = scanFrame(d->xsize, d->exec.asize, d->scan.pmultmin, d->exec.detout, bimp, limp, befimp, aftimp, p_opt, &next); + nimp = scanFrame(xsize, exec.asize, scan.pmultmin, exec.detout, bimp, limp, befimp, aftimp, p_opt, &next); - for (pass = 0; pass < d->exec.npasses; pass++) + for (pass = 0; pass < exec.npasses; pass++) { - memcpy (d->exec.unfixed, d->exec.detout, d->xsize * sizeof (int)); + memcpy (exec.unfixed, exec.detout, xsize * sizeof (int)); for (k = 0; k < nimp; k++) { if (k > 0) - scanFrame(d->xsize, d->exec.asize, d->scan.pmultmin, d->exec.unfixed, bimp, limp, befimp, aftimp, p_opt, &next); + scanFrame(xsize, exec.asize, scan.pmultmin, exec.unfixed, bimp, limp, befimp, aftimp, p_opt, &next); if ((p = p_opt[next]) > 0) { - LMathd::asolve(d->xsize, p, x, d->exec.a, d->wrk.asolve_r, d->wrk.asolve_z); - xHat(limp[next], p, &x[bimp[next] - p], d->exec.a, d->exec.xHout, - d->wrk.xHat_r, d->wrk.xHat_ATAI, d->wrk.xHat_A1, d->wrk.xHat_A2, - d->wrk.xHat_P1, d->wrk.xHat_P2, d->wrk.trI_y, d->wrk.trI_v, d->wrk.dR_z); - memcpy (&x[bimp[next]], d->exec.xHout, limp[next] * sizeof (double)); - memset (&d->exec.unfixed[bimp[next]], 0, limp[next] * sizeof (int)); + LMathd::asolve(xsize, p, x, exec.a, wrk.asolve_r, wrk.asolve_z); + xHat( + limp[next], + p, + &x[bimp[next] - p], + exec.a, + exec.xHout, + wrk.xHat_r, + wrk.xHat_ATAI, + wrk.xHat_A1, + wrk.xHat_A2, + wrk.xHat_P1, + wrk.xHat_P2, + wrk.trI_y, + wrk.trI_v, + wrk.dR_z + ); + memcpy (&x[bimp[next]], exec.xHout, limp[next] * sizeof (double)); + memset (&exec.unfixed[bimp[next]], 0, limp[next] * sizeof (int)); } else { - memcpy (&x[bimp[next]], &d->exec.savex[bimp[next]], limp[next] * sizeof (double)); + memcpy (&x[bimp[next]], &exec.savex[bimp[next]], limp[next] * sizeof (double)); } } } } -void SNBA::xsnba (SNBA *d) +void SNBA::execute() { - if (d->run) + if (run) { int i; - d->inresamp->execute(); + inresamp->execute(); - for (i = 0; i < 2 * d->isize; i += 2) + for (i = 0; i < 2 * isize; i += 2) { - d->inaccum[d->iainidx] = d->inbuff[i]; - d->iainidx = (d->iainidx + 1) % d->iasize; + inaccum[iainidx] = inbuff[i]; + iainidx = (iainidx + 1) % iasize; } - d->nsamps += d->isize; + nsamps += isize; - while (d->nsamps >= d->incr) + while (nsamps >= incr) { - memcpy (&d->xaux[d->xsize - d->incr], &d->inaccum[d->iaoutidx], d->incr * sizeof (double)); - execFrame (d, d->xaux); - d->iaoutidx = (d->iaoutidx + d->incr) % d->iasize; - d->nsamps -= d->incr; - memcpy (&d->outaccum[d->oainidx], d->xaux, d->incr * sizeof (double)); - d->oainidx = (d->oainidx + d->incr) % d->oasize; - memmove (d->xbase, &d->xbase[d->incr], (2 * d->xsize - d->incr) * sizeof (double)); + memcpy (&xaux[xsize - incr], &inaccum[iaoutidx], incr * sizeof (double)); + execFrame (xaux); + iaoutidx = (iaoutidx + incr) % iasize; + nsamps -= incr; + memcpy (&outaccum[oainidx], xaux, incr * sizeof (double)); + oainidx = (oainidx + incr) % oasize; + memmove (xbase, &xbase[incr], (2 * xsize - incr) * sizeof (double)); } - for (i = 0; i < d->isize; i++) + for (i = 0; i < isize; i++) { - d->outbuff[2 * i + 0] = d->outaccum[d->oaoutidx]; - d->outbuff[2 * i + 1] = 0.0; - d->oaoutidx = (d->oaoutidx + 1) % d->oasize; + outbuff[2 * i + 0] = outaccum[oaoutidx]; + outbuff[2 * i + 1] = 0.0; + oaoutidx = (oaoutidx + 1) % oasize; } - d->outresamp->execute(); + outresamp->execute(); } - else if (d->out != d->in) + else if (out != in) { - std::copy(d->in, d->in + d->bsize * 2, d->out); + std::copy(in, in + bsize * 2, out); } } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -void SNBA::SetSNBARun (RXA& rxa, int run) +void SNBA::setOvrlp(int _ovrlp) { - SNBA *a = rxa.snba; - - if (a->run != run) - { - RXA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb->master_run); - RXA::bp1Check ( - rxa, - rxa.amd->run, - run, - rxa.emnr->run, - rxa.anf->run, - rxa.anr->run - ); - a->run = run; - RXA::bp1Set (rxa); - RXA::bpsnbaSet (rxa); - } + decalc(); + ovrlp = _ovrlp; + calc(); } -void SNBA::SetSNBAovrlp (RXA& rxa, int ovrlp) +void SNBA::setAsize(int size) { - decalc_snba (rxa.snba); - rxa.snba->ovrlp = ovrlp; - calc_snba (rxa.snba); + exec.asize = size; } -void SNBA::SetSNBAasize (RXA& rxa, int size) +void SNBA::setNpasses(int npasses) { - rxa.snba->exec.asize = size; + exec.npasses = npasses; } -void SNBA::SetSNBAnpasses (RXA& rxa, int npasses) +void SNBA::setK1(double k1) { - rxa.snba->exec.npasses = npasses; + sdet.k1 = k1; } -void SNBA::SetSNBAk1 (RXA& rxa, double k1) +void SNBA::setK2(double k2) { - rxa.snba->sdet.k1 = k1; + sdet.k2 = k2; } -void SNBA::SetSNBAk2 (RXA& rxa, double k2) +void SNBA::setBridge(int bridge) { - rxa.snba->sdet.k2 = k2; + sdet.b = bridge; } -void SNBA::SetSNBAbridge (RXA& rxa, int bridge) +void SNBA::setPresamps(int presamps) { - rxa.snba->sdet.b = bridge; + sdet.pre = presamps; } -void SNBA::SetSNBApresamps (RXA& rxa, int presamps) +void SNBA::setPostsamps(int postsamps) { - rxa.snba->sdet.pre = presamps; + sdet.post = postsamps; } -void SNBA::SetSNBApostsamps (RXA& rxa, int postsamps) +void SNBA::setPmultmin(double pmultmin) { - rxa.snba->sdet.post = postsamps; + scan.pmultmin = pmultmin; } -void SNBA::SetSNBApmultmin (RXA& rxa, double pmultmin) +void SNBA::setOutputBandwidth(double flow, double fhigh) { - rxa.snba->scan.pmultmin = pmultmin; -} - -void SNBA::SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh) -{ - SNBA *a = rxa.snba; - RESAMPLE *d = a->outresamp; double f_low, f_high; if (flow >= 0 && fhigh >= 0) { - if (fhigh < a->out_low_cut) - fhigh = a->out_low_cut; + if (fhigh < out_low_cut) + fhigh = out_low_cut; - if (flow > a->out_high_cut) - flow = a->out_high_cut; + if (flow > out_high_cut) + flow = out_high_cut; - f_low = std::max ( a->out_low_cut, flow); - f_high = std::min (a->out_high_cut, fhigh); + f_low = std::max ( out_low_cut, flow); + f_high = std::min (out_high_cut, fhigh); } else if (flow <= 0 && fhigh <= 0) { - if (flow > -a->out_low_cut) - flow = -a->out_low_cut; + if (flow > -out_low_cut) + flow = -out_low_cut; - if (fhigh < -a->out_high_cut) - fhigh = -a->out_high_cut; + if (fhigh < -out_high_cut) + fhigh = -out_high_cut; - f_low = std::max ( a->out_low_cut, -fhigh); - f_high = std::min (a->out_high_cut, -flow); + f_low = std::max ( out_low_cut, -fhigh); + f_high = std::min (out_high_cut, -flow); } else if (flow < 0 && fhigh > 0) { double absmax = std::max (-flow, fhigh); - if (absmax < a->out_low_cut) - absmax = a->out_low_cut; + if (absmax < out_low_cut) + absmax = out_low_cut; - f_low = a->out_low_cut; - f_high = std::min (a->out_high_cut, absmax); + f_low = out_low_cut; + f_high = std::min (out_high_cut, absmax); } - d->setBandwidth(f_low, f_high); + outresamp->setBandwidth(f_low, f_high); } } // namespace diff --git a/wdsp/snba.hpp b/wdsp/snba.hpp index 5a44c8ebd..e356c02a6 100644 --- a/wdsp/snba.hpp +++ b/wdsp/snba.hpp @@ -31,7 +31,6 @@ warren@wpratt.com namespace WDSP{ class RESAMPLE; -class RXA; class SNBA { @@ -57,13 +56,15 @@ public: int oaoutidx; int init_oaoutidx; double* outaccum; - int resamprun; int isize; RESAMPLE *inresamp; RESAMPLE *outresamp; float* inbuff; float* outbuff; + double out_low_cut; + double out_high_cut; + struct _exec { int asize; @@ -105,10 +106,8 @@ public: double* asolve_r; double* asolve_z; } wrk; - double out_low_cut; - double out_high_cut; - static SNBA* create_snba ( + SNBA( int run, float* in, float* out, @@ -128,29 +127,28 @@ public: double out_low_cut, double out_high_cut ); + ~SNBA(); - static void destroy_snba (SNBA *d); - static void flush_snba (SNBA *d); - static void xsnba (SNBA *d); - static void setBuffers_snba (SNBA *a, float* in, float* out); - static void setSamplerate_snba (SNBA *a, int rate); - static void setSize_snba (SNBA *a, int size); - // RXA Properties - static void SetSNBARun (RXA& rxa, int run); - static void SetSNBAovrlp (RXA& rxa, int ovrlp); - static void SetSNBAasize (RXA& rxa, int size); - static void SetSNBAnpasses (RXA& rxa, int npasses); - static void SetSNBAk1 (RXA& rxa, double k1); - static void SetSNBAk2 (RXA& rxa, double k2); - static void SetSNBAbridge (RXA& rxa, int bridge); - static void SetSNBApresamps (RXA& rxa, int presamps); - static void SetSNBApostsamps (RXA& rxa, int postsamps); - static void SetSNBApmultmin (RXA& rxa, double pmultmin); - static void SetSNBAOutputBandwidth (RXA& rxa, double flow, double fhigh); + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setOvrlp(int ovrlp); + void setAsize(int size); + void setNpasses(int npasses); + void setK1(double k1); + void setK2(double k2); + void setBridge(int bridge); + void setPresamps(int presamps); + void setPostsamps(int postsamps); + void setPmultmin(double pmultmin); + void setOutputBandwidth(double flow, double fhigh); private: - static void calc_snba (SNBA *d); - static void decalc_snba (SNBA *d); + void calc(); + void decalc(); static void ATAc0 (int n, int nr, double* A, double* r); static void multA1TA2(double* a1, double* a2, int m, int n, int q, double* c); static void multXKE(double* a, double* xk, int m, int q, int p, double* vout); @@ -172,7 +170,6 @@ private: double* dR_z ); static void invf(int xsize, int asize, double* a, double* x, double* v); - static void det(SNBA *d, int asize, double* v, int* detout); static int scanFrame( int xsize, int pval, @@ -185,7 +182,8 @@ private: int* p_opt, int* next ); - static void execFrame(SNBA *d, double* x); + void det(int asize, double* v, int* detout); + void execFrame(double* x); }; } // namespace diff --git a/wdsp/wcpAGC.cpp b/wdsp/wcpAGC.cpp index ec90261f2..34a36b4e5 100644 --- a/wdsp/wcpAGC.cpp +++ b/wdsp/wcpAGC.cpp @@ -535,60 +535,60 @@ void WCPAGC::SetAGCMaxInputLevel (RXA& rxa, double level) void WCPAGC::SetALCSt (TXA& txa, int state) { - txa.alc.p->run = state; + txa.alc->run = state; } void WCPAGC::SetALCAttack (TXA& txa, int attack) { - txa.alc.p->tau_attack = (double) attack / 1000.0; - loadWcpAGC(txa.alc.p); + txa.alc->tau_attack = (double) attack / 1000.0; + loadWcpAGC(txa.alc); } void WCPAGC::SetALCDecay (TXA& txa, int decay) { - txa.alc.p->tau_decay = (double) decay / 1000.0; - loadWcpAGC(txa.alc.p); + txa.alc->tau_decay = (double) decay / 1000.0; + loadWcpAGC(txa.alc); } void WCPAGC::SetALCHang (TXA& txa, int hang) { - txa.alc.p->hangtime = (double) hang / 1000.0; - loadWcpAGC(txa.alc.p); + txa.alc->hangtime = (double) hang / 1000.0; + loadWcpAGC(txa.alc); } void WCPAGC::SetALCMaxGain (TXA& txa, double maxgain) { - txa.alc.p->max_gain = pow (10.0,(double) maxgain / 20.0); - loadWcpAGC(txa.alc.p); + txa.alc->max_gain = pow (10.0,(double) maxgain / 20.0); + loadWcpAGC(txa.alc); } void WCPAGC::SetLevelerSt (TXA& txa, int state) { - txa.leveler.p->run = state; + txa.leveler->run = state; } void WCPAGC::SetLevelerAttack (TXA& txa, int attack) { - txa.leveler.p->tau_attack = (double) attack / 1000.0; - loadWcpAGC(txa.leveler.p); + txa.leveler->tau_attack = (double) attack / 1000.0; + loadWcpAGC(txa.leveler); } void WCPAGC::SetLevelerDecay (TXA& txa, int decay) { - txa.leveler.p->tau_decay = (double) decay / 1000.0; - loadWcpAGC(txa.leveler.p); + txa.leveler->tau_decay = (double) decay / 1000.0; + loadWcpAGC(txa.leveler); } void WCPAGC::SetLevelerHang (TXA& txa, int hang) { - txa.leveler.p->hangtime = (double) hang / 1000.0; - loadWcpAGC(txa.leveler.p); + txa.leveler->hangtime = (double) hang / 1000.0; + loadWcpAGC(txa.leveler); } void WCPAGC::SetLevelerTop (TXA& txa, double maxgain) { - txa.leveler.p->max_gain = pow (10.0,(double) maxgain / 20.0); - loadWcpAGC(txa.leveler.p); + txa.leveler->max_gain = pow (10.0,(double) maxgain / 20.0); + loadWcpAGC(txa.leveler); } } // namespace WDSP From 59f97f3912ff651be7685b0094ad76ab49c18262 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 27 Jul 2024 06:01:39 +0200 Subject: [PATCH 16/46] WDSP: restore ANB::flush and re-enable Windows and Mac builds --- CMakeLists.txt | 2 +- wdsp/anb.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a7e6e50b9..92af1b54a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -860,7 +860,7 @@ if (FFTW3F_FOUND) set(FT8_SUPPORT ON CACHE INTERNAL "") endif() -if (FFTW3F_FOUND AND LINUX) +if (FFTW3F_FOUND) add_subdirectory(wdsp) add_definitions(-DHAS_WDSP) set(WDSP_SUPPORT ON CACHE INTERNAL "") diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index df7304112..7dcff5c6e 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -97,6 +97,11 @@ ANB::~ANB() delete[] wave; } +void ANB::flush() +{ + initBlanker(); +} + void ANB::execute() { double scale; From 8a267240df40e8fb71b511ba9f5d5649b6aa8d0f Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 27 Jul 2024 13:45:09 +0200 Subject: [PATCH 17/46] WDSP: ANB, NOB, ANSQ: use vectors instead of C arrays and disable copy constructor --- wdsp/amsq.hpp | 1 + wdsp/anb.cpp | 24 ++++++++++++------------ wdsp/anb.hpp | 14 ++++++++------ wdsp/nob.cpp | 33 +++++++++++---------------------- wdsp/nob.hpp | 23 +++++++++++++---------- 5 files changed, 45 insertions(+), 50 deletions(-) diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index 069ed74da..f9acdf76b 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -78,6 +78,7 @@ public: double _max_tail, double _muted_gain ); + AMSQ(const AMSQ&) = delete; ~AMSQ(); void flush(); diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 7dcff5c6e..33f3d95e9 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -57,7 +57,7 @@ void ANB::initBlanker() for (i = 0; i <= trans_count; i++) wave[i] = 0.5 * cos(i * coef); - std::fill(dline, dline + dline_size * 2, 0); + std::fill(dline.begin(), dline.end(), 0); } ANB::ANB ( @@ -81,20 +81,20 @@ ANB::ANB ( hangtime(_hangtime), advtime(_advtime), backtau(_backtau), - threshold(_threshold) + threshold(_threshold), + dtime(0), + htime(0), + itime(0), + atime(0) { - wave = new double[((int)(MAX_SAMPLERATE * MAX_TAU) + 1)]; + tau = tau < 0.0 ? 0.0 : (tau > MAX_TAU ? MAX_TAU : tau); + hangtime = hangtime < 0.0 ? 0.0 : (hangtime > MAX_ADVTIME ? MAX_ADVTIME : hangtime); + advtime = advtime < 0.0 ? 0.0 : (advtime > MAX_ADVTIME ? MAX_ADVTIME : advtime); + samplerate = samplerate < 0.0 ? 0.0 : (samplerate > MAX_SAMPLERATE ? MAX_SAMPLERATE : samplerate); + wave.resize((int)(MAX_SAMPLERATE * MAX_TAU) + 1); dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1; - dline = new float[dline_size * 2]; + dline.resize(dline_size * 2); initBlanker(); - legacy = new float[2048 * 2]; /////////////// legacy interface - remove -} - -ANB::~ANB() -{ - delete[] legacy; /////////////// legacy interface - remove - delete[] dline; - delete[] wave; } void ANB::flush() diff --git a/wdsp/anb.hpp b/wdsp/anb.hpp index 00619997b..2a349e19f 100644 --- a/wdsp/anb.hpp +++ b/wdsp/anb.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_anb_h #define wdsp_anb_h +#include + #include "export.h" namespace WDSP { @@ -37,17 +39,17 @@ class WDSP_API ANB public: int run; int buffsize; // size of input/output buffer - float* in; // input buffer - float* out; // output buffer + float* in; // input buffer + float* out; // output buffer int dline_size; // length of delay line which is 'double dline[length][2]' - float *dline; // pointer to delay line + std::vector dline; // delay line double samplerate; // samplerate, used to convert times into sample counts double tau; // transition time, signal<->zero double hangtime; // time to stay at zero after noise is no longer detected double advtime; // deadtime (zero output) in advance of detected noise double backtau; // time constant used in averaging the magnitude of the input signal double threshold; // triggers if (noise > threshold * average_signal_magnitude) - double *wave; // pointer to array holding transition waveform + std::vector wave; // array holding transition waveform int state; // state of the state machine double avg; // average value of the signal magnitude int dtime; // count when decreasing the signal magnitude @@ -64,7 +66,6 @@ public: int count; // set each time a noise sample is detected, counts down double backmult; // multiplier for waveform averaging double ombackmult; // multiplier for waveform averaging - float *legacy; ANB( int run, @@ -78,7 +79,8 @@ public: double backtau, double threshold ); - ~ANB(); + ANB(const ANB&) = delete; + ~ANB() = default; void flush(); void execute(); diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index cc54dec1d..5b7834315 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -105,15 +105,15 @@ NOB::NOB ( MAX_HANG_SLEW_TIME + MAX_HANG_TIME + MAX_SEQ_TIME ) + 2); - dline = new double[dline_size * 2]; - imp = new int[dline_size]; - awave = new double[(int)(MAX_ADV_SLEW_TIME * MAX_SAMPLERATE + 1)]; - hwave = new double[(int)(MAX_HANG_SLEW_TIME * MAX_SAMPLERATE + 1)]; + dline.resize(dline_size * 2); + imp.resize(dline_size); + awave.resize((int)(MAX_ADV_SLEW_TIME * MAX_SAMPLERATE + 1)); + hwave.resize((int)(MAX_HANG_SLEW_TIME * MAX_SAMPLERATE + 1)); filterlen = 10; - bfbuff = new double[filterlen * 2]; - ffbuff = new double[filterlen * 2]; - fcoefs = new double[filterlen]; + bfbuff.resize(filterlen * 2); + ffbuff.resize(filterlen * 2); + fcoefs.resize(filterlen); fcoefs[0] = 0.308720593; fcoefs[1] = 0.216104415; fcoefs[2] = 0.151273090; @@ -128,17 +128,6 @@ NOB::NOB ( init(); } -NOB::~NOB() -{ - delete[] fcoefs; - delete[] ffbuff; - delete[] bfbuff; - delete[] hwave; - delete[] awave; - delete[] imp; - delete[] dline; -} - void NOB::flush() { out_idx = 0; @@ -149,10 +138,10 @@ void NOB::flush() avg = 1.0; bfb_in_idx = filterlen - 1; ffb_in_idx = filterlen - 1; - std::fill(dline, dline + dline_size * 2, 0); - std::fill(imp, imp + dline_size, 0); - std::fill(bfbuff, bfbuff + filterlen * 2, 0); - std::fill(ffbuff, ffbuff + filterlen * 2, 0); + std::fill(dline.begin(), dline.end(), 0); + std::fill(imp.begin(), imp.end(), 0); + std::fill(bfbuff.begin(), bfbuff.end(), 0); + std::fill(ffbuff.begin(), ffbuff.end(), 0); } void NOB::execute() diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 51dbd78e2..adfc525d9 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_nob_h #define wdsp_nob_h +#include + #include "export.h" namespace WDSP { @@ -37,11 +39,11 @@ class WDSP_API NOB public: int run; int buffsize; // size of input/output buffer - float* in; // input buffer - float* out; // output buffer + float* in; // input buffer + float* out; // output buffer int dline_size; // length of delay line which is 'double dline[length][2]' - double *dline; // pointer to delay line - int *imp; + std::vector dline; // delay line + std::vector imp; double samplerate; // samplerate, used to convert times into sample counts int mode; double advslewtime; // transition time, signal<->zero @@ -50,15 +52,15 @@ public: double hangtime; // time to stay at zero after noise is no longer detected double max_imp_seq_time; int filterlen; - double *fcoefs; - double *bfbuff; + std::vector fcoefs; + std::vector bfbuff; int bfb_in_idx; - double *ffbuff; + std::vector ffbuff; int ffb_in_idx; double backtau; // time constant used in averaging the magnitude of the input signal double threshold; // triggers if (noise > threshold * average_signal_magnitude) - double *awave; // pointer to array holding transition waveform - double *hwave; + std::vector awave; // array holding transition waveform + std::vector hwave; int state; // state of the state machine double avg; // average value of the signal magnitude int time; // count when decreasing the signal magnitude @@ -96,7 +98,8 @@ public: double backtau, double threshold ); - ~NOB(); + NOB(const NOB&) = delete; + ~NOB() = default; //////////// legacy interface - remove void flush(); void execute(); From 86f27fc4d609e54d3026f3c197f261619287d06d Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 27 Jul 2024 23:29:15 +0200 Subject: [PATCH 18/46] WDSP: NBP: use vectors instead of C arrays and disable copy constructor --- wdsp/nbp.cpp | 54 ++++++++++++++++++++++------------------------- wdsp/nbp.hpp | 34 ++++++++++++++++------------- wdsp/resample.cpp | 21 +++--------------- wdsp/resample.hpp | 10 +++++---- 4 files changed, 53 insertions(+), 66 deletions(-) diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index bf75535ce..37bc29bc5 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -44,20 +44,11 @@ NOTCHDB::NOTCHDB(int _master_run, int _maxnotches) master_run = _master_run; maxnotches = _maxnotches; nn = 0; - fcenter = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); - fwidth = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); - nlow = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); - nhigh = new double[maxnotches]; // (float *) malloc0 (maxnotches * sizeof (float)); - active = new int[maxnotches]; // (int *) malloc0 (maxnotches * sizeof (int )); -} - -NOTCHDB::~NOTCHDB() -{ - delete[] (active); - delete[] (nhigh); - delete[] (nlow); - delete[] (fwidth); - delete[] (fcenter); + fcenter.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); + fwidth.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); + nlow.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); + nhigh.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); + active.resize(maxnotches); // (int *) malloc0 (maxnotches * sizeof (int )); } int NOTCHDB::addNotch(int notch, double _fcenter, double _fwidth, int _active) @@ -198,17 +189,17 @@ double NBP::min_notch_width() int NBP::make_nbp ( int nn, - int* active, - double* center, - double* width, - double* nlow, - double* nhigh, + std::vector& active, + std::vector& center, + std::vector& width, + std::vector& nlow, + std::vector& nhigh, double minwidth, int autoincr, double flow, double fhigh, - double* bplow, - double* bphigh, + std::vector& bplow, + std::vector& bphigh, int* havnotch ) { @@ -328,8 +319,15 @@ void NBP::calc_lightweight() bplow[i] -= offset; bphigh[i] -= offset; } - impulse = fir_mbandpass (nc, numpb, bplow, bphigh, - rate, gain / (float)(2 * size), wintype); + impulse = fir_mbandpass ( + nc, + numpb, + bplow.data(), + bphigh.data(), + rate, + gain / (float)(2 * size), + wintype + ); FIRCORE::setImpulse_fircore (fircore, impulse, 1); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); delete[](impulse); @@ -375,8 +373,8 @@ void NBP::calc_impulse () impulse = fir_mbandpass ( nc, numpb, - bplow, - bphigh, + bplow.data(), + bphigh.data(), rate, gain / (float)(2 * size), wintype @@ -431,8 +429,8 @@ NBP::NBP( maxpb(_maxpb), notchdb(_notchdb) { - bplow = new double[maxpb]; // (float *) malloc0 (maxpb * sizeof (float)); - bphigh = new double[maxpb]; // (float *) malloc0 (maxpb * sizeof (float)); + bplow.resize(maxpb); // (float *) malloc0 (maxpb * sizeof (float)); + bphigh.resize(maxpb); // (float *) malloc0 (maxpb * sizeof (float)); calc_impulse (); fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); @@ -442,8 +440,6 @@ NBP::NBP( NBP::~NBP() { FIRCORE::destroy_fircore (fircore); - delete[] (bphigh); - delete[] (bplow); } void NBP::flush() diff --git a/wdsp/nbp.hpp b/wdsp/nbp.hpp index 23d0a4b5b..97b52c5b3 100644 --- a/wdsp/nbp.hpp +++ b/wdsp/nbp.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_nbp_h #define wdsp_nbp_h +#include + #include "export.h" namespace WDSP { @@ -41,15 +43,16 @@ public: double tunefreq; double shift; int nn; - int* active; - double* fcenter; - double* fwidth; - double* nlow; - double* nhigh; + std::vector active; + std::vector fcenter; + std::vector fwidth; + std::vector nlow; + std::vector nhigh; int maxnotches; NOTCHDB(int master_run, int maxnotches); - ~NOTCHDB(); + NOTCHDB(const NOTCHDB&) = delete; + ~NOTCHDB() = default; int addNotch (int notch, double fcenter, double fwidth, int active); int getNotch (int notch, double* fcenter, double* fwidth, int* active); @@ -79,8 +82,8 @@ public: float* impulse; // filter impulse response int maxpb; // maximum number of passbands NOTCHDB* notchdb; // ptr to addr of notch-database data structure - double* bplow; // array of passband lows - double* bphigh; // array of passband highs + std::vector bplow; // array of passband lows + std::vector bphigh; // array of passband highs int numpb; // number of passbands FIRCORE *fircore; int havnotch; @@ -104,6 +107,7 @@ public: int maxpb, NOTCHDB* notchdb ); + NBP(const NBP&) = delete; ~NBP(); void flush(); @@ -127,17 +131,17 @@ private: double min_notch_width (); static int make_nbp ( int nn, - int* active, - double* center, - double* width, - double* nlow, - double* nhigh, + std::vector& active, + std::vector& center, + std::vector& width, + std::vector& nlow, + std::vector& nhigh, double minwidth, int autoincr, double flow, double fhigh, - double* bplow, - double* bphigh, + std::vector& bplow, + std::vector& bphigh, int* havnotch ); }; diff --git a/wdsp/resample.cpp b/wdsp/resample.cpp index b06ed959f..e4f24309c 100644 --- a/wdsp/resample.cpp +++ b/wdsp/resample.cpp @@ -84,7 +84,7 @@ void RESAMPLE::calc() ncoef = (ncoef / L + 1) * L; cpp = ncoef / L; - h = new double[ncoef]; // (float *)malloc0(ncoef * sizeof(float)); + h.resize(ncoef); // (float *)malloc0(ncoef * sizeof(float)); impulse = FIR::fir_bandpass(ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, gain * (double)L); i = 0; @@ -95,19 +95,13 @@ void RESAMPLE::calc() } ringsize = cpp; - ring = new double[ringsize]; // (float *)malloc0(ringsize * sizeof(complex)); + ring.resize(ringsize); // (float *)malloc0(ringsize * sizeof(complex)); idx_in = ringsize - 1; phnum = 0; delete[] (impulse); } -void RESAMPLE::decalc() -{ - delete[] ring; - delete[] h; -} - RESAMPLE::RESAMPLE ( int _run, int _size, @@ -133,15 +127,10 @@ RESAMPLE::RESAMPLE ( calc(); } -RESAMPLE::~RESAMPLE() -{ - decalc(); -} - void RESAMPLE::flush() { - std::fill(ring, ring + 2 * ringsize, 0); + std::fill(ring.begin(), ring.end(), 0); idx_in = ringsize - 1; phnum = 0; } @@ -210,14 +199,12 @@ void RESAMPLE::setSize(int _size) void RESAMPLE::setInRate(int _rate) { - decalc(); in_rate = _rate; calc(); } void RESAMPLE::setOutRate(int _rate) { - decalc(); out_rate = _rate; calc(); } @@ -226,7 +213,6 @@ void RESAMPLE::setFCLow(double _fc_low) { if (fc_low != _fc_low) { - decalc(); fc_low = _fc_low; calc(); } @@ -236,7 +222,6 @@ void RESAMPLE::setBandwidth(double _fc_low, double _fc_high) { if (fc_low != _fc_low || _fc_high != fcin) { - decalc(); fc_low = _fc_low; fcin = _fc_high; calc(); diff --git a/wdsp/resample.hpp b/wdsp/resample.hpp index 8f4d7f95c..d442039fe 100644 --- a/wdsp/resample.hpp +++ b/wdsp/resample.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_resample_h #define wdsp_resample_h +#include + #include "export.h" namespace WDSP { @@ -56,9 +58,9 @@ public: int ncoef; // number of coefficients int L; // interpolation factor int M; // decimation factor - double* h; // coefficients + std::vector h; // coefficients int ringsize; // number of complex pairs the ring buffer holds - double* ring; // ring buffer + std::vector ring; // ring buffer int cpp; // coefficients of the phase int phnum; // phase number @@ -73,7 +75,8 @@ public: int ncoef, double gain ); - ~RESAMPLE(); + RESAMPLE(const RESAMPLE&) = delete; + ~RESAMPLE() = default; void flush(); int execute(); @@ -90,7 +93,6 @@ public: private: void calc(); - void decalc(); }; } // namespace WDSP From 3c2192603b3deb92a7ba4806f67576f54b1f5f5a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 28 Jul 2024 11:36:45 +0200 Subject: [PATCH 19/46] WDSP: use vectors instead of C arrays and disable copy constructor (more) and other changes --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 10 ++-- wdsp/RXA.cpp | 75 ++++++++++++++++++++++--- wdsp/RXA.hpp | 6 ++ wdsp/amd.hpp | 15 +++-- wdsp/amsq.cpp | 24 ++------ wdsp/amsq.hpp | 21 +++---- wdsp/anf.cpp | 21 ------- wdsp/anf.hpp | 1 - wdsp/anr.cpp | 20 ------- wdsp/anr.hpp | 1 - wdsp/bpsnba.hpp | 1 + wdsp/emnr.cpp | 19 ------- wdsp/emnr.hpp | 1 - wdsp/eqp.cpp | 49 +++++++--------- wdsp/eqp.hpp | 7 ++- wdsp/fmd.cpp | 14 ++--- wdsp/fmd.hpp | 5 +- wdsp/fmsq.cpp | 17 +++--- wdsp/fmsq.hpp | 14 +++-- wdsp/meter.cpp | 7 ++- 20 files changed, 160 insertions(+), 168 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 389673e37..d03c380b1 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -486,18 +486,18 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_dnr != settings.m_dnr) || (m_settings.m_nrScheme != settings.m_nrScheme) || force) { - WDSP::ANR::SetANRRun(*m_rxa, 0); - WDSP::EMNR::SetEMNRRun(*m_rxa, 0); + WDSP::RXA::SetANRRun(*m_rxa, 0); + WDSP::RXA::SetEMNRRun(*m_rxa, 0); if (settings.m_dnr) { switch (settings.m_nrScheme) { case WDSPRxProfile::NRSchemeNR: - WDSP::ANR::SetANRRun(*m_rxa, 1); + WDSP::RXA::SetANRRun(*m_rxa, 1); break; case WDSPRxProfile::NRSchemeNR2: - WDSP::EMNR::SetEMNRRun(*m_rxa, 1); + WDSP::RXA::SetEMNRRun(*m_rxa, 1); break; default: break; @@ -560,7 +560,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } if ((m_settings.m_anf != settings.m_anf) || force) { - WDSP::ANF::SetANFRun(*m_rxa, settings.m_anf ? 1 : 0); + WDSP::RXA::SetANFRun(*m_rxa, settings.m_anf ? 1 : 0); } // Caution: Causes corruption diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 9d0a6f60a..4c1d700d1 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -289,7 +289,7 @@ RXA* RXA::create_rxa ( rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input signal buffer rxa->midbuff, // pointer to output signal buffer - rxa->fmd->audio, // pointer to trigger buffer + rxa->fmd->audio.data(), // pointer to trigger buffer rxa->dsp_rate, // sample rate 5000.0, // cutoff freq for noise filter (Hz) &rxa->fmd->pllpole, // pointer to pole frequency of the fmd pll (Hz) @@ -760,9 +760,9 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->amsq->setSamplerate(rxa->dsp_rate); rxa->amd->setSamplerate(rxa->dsp_rate); rxa->fmd->setSamplerate(rxa->dsp_rate); - rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio.data()); rxa->fmsq->setSamplerate(rxa->dsp_rate); - rxa->snba->setSamplerate(rxa->dsp_rate); + // rxa->snba->setSamplerate(rxa->dsp_rate); SMBA removed rxa->eqp->setSamplerate(rxa->dsp_rate); ANF::setSamplerate_anf (rxa->anf, rxa->dsp_rate); ANR::setSamplerate_anr (rxa->anr, rxa->dsp_rate); @@ -831,7 +831,7 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->amd->setSize(rxa->dsp_size); rxa->fmd->setBuffers(rxa->midbuff, rxa->midbuff); rxa->fmd->setSize(rxa->dsp_size); - rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio); + rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio.data()); rxa->fmsq->setSize(rxa->dsp_size); rxa->snba->setBuffers(rxa->midbuff, rxa->midbuff); rxa->snba->setSize(rxa->dsp_size); @@ -1225,20 +1225,20 @@ void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) } } -void RXA::SetAMDRun(RXA& rxa, int _run) +void RXA::SetAMDRun(RXA& rxa, int run) { - if (rxa.amd->run != _run) + if (rxa.amd->run != run) { RXA::bp1Check ( rxa, - _run, + run, rxa.snba->run, rxa.emnr->run, rxa.anf->run, rxa.anr->run ); - rxa.amd->run = _run; + rxa.amd->run = run; RXA::bp1Set (rxa); } } @@ -1264,6 +1264,65 @@ void RXA::SetSNBARun (RXA& rxa, int run) } } +void RXA::SetANFRun (RXA& rxa, int run) +{ + ANF *a = rxa.anf; + + if (a->run != run) + { + RXA::bp1Check ( + rxa, + rxa.amd->run, + rxa.snba->run, + rxa.emnr->run, + run, + rxa.anr->run + ); + a->run = run; + RXA::bp1Set (rxa); + ANF::flush_anf (a); + } +} + +void RXA::SetANRRun (RXA& rxa, int run) +{ + ANR *a = rxa.anr; + + if (a->run != run) + { + RXA::bp1Check ( + rxa, + rxa.amd->run, + rxa.snba->run, + rxa.emnr->run, + rxa.anf->run, + run + ); + a->run = run; + RXA::bp1Set (rxa); + ANR::flush_anr (a); + } +} + +void RXA::SetEMNRRun (RXA& rxa, int run) +{ + EMNR *a = rxa.emnr; + + if (a->run != run) + { + RXA::bp1Check ( + rxa, + rxa.amd->run, + rxa.snba->run, + run, + rxa.anf->run, + rxa.anr->run + ); + a->run = run; + RXA::bp1Set (rxa); + } +} + /******************************************************************************************************** * * * Collectives * diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 2fe18254a..26eb6167e 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -171,6 +171,12 @@ public: static void SetAMDRun(RXA& rxa, int run); // SNBA static void SetSNBARun (RXA& rxa, int run); + // ANF + static void SetANFRun (RXA& rxa, int run); + // ANR + static void SetANRRun (RXA& rxa, int run); + // EMNR + static void SetEMNRRun (RXA& rxa, int run); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); diff --git a/wdsp/amd.hpp b/wdsp/amd.hpp index 5a9a243de..6b955587c 100644 --- a/wdsp/amd.hpp +++ b/wdsp/amd.hpp @@ -37,6 +37,8 @@ warren@wpratt.com #define OUT_IDX (3 * STAGES) #endif +#include + #include "export.h" namespace WDSP { @@ -68,12 +70,12 @@ public: double onem_mtauR; // 1.0 - carrier_removal_multiplier double mtauI; // carrier insertion multiplier double onem_mtauI; // 1.0 - carrier_insertion_multiplier - double a[3 * STAGES + 3]; // Filter a variables - double b[3 * STAGES + 3]; // Filter b variables - double c[3 * STAGES + 3]; // Filter c variables - double d[3 * STAGES + 3]; // Filter d variables - double c0[STAGES]; // Filter coefficients - path 0 - double c1[STAGES]; // Filter coefficients - path 1 + std::array a; // Filter a variables + std::array b; // Filter b variables + std::array c; // Filter c variables + std::array d; // Filter d variables + std::array c0; // Filter coefficients - path 0 + std::array c1; // Filter coefficients - path 1 double dsI; // delayed sample, I path double dsQ; // delayed sample, Q path double dc_insert; // dc component to insert in output @@ -97,6 +99,7 @@ public: double tauR, double tauI ); + AMD(const AMD&) = delete; ~AMD() = default; void init(); diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index 25a82c389..215646c12 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -58,27 +58,20 @@ void AMSQ::compute_slews() void AMSQ::calc() { // signal averaging - trigsig = new float[size * 2]; + trigsig.resize(size * 2); avm = exp(-1.0 / (rate * avtau)); onem_avm = 1.0 - avm; avsig = 0.0; // level change ntup = (int)(tup * rate); ntdown = (int)(tdown * rate); - cup = new double[(ntup + 1) * 2]; // (float *)malloc0((ntup + 1) * sizeof(float)); - cdown = new double[(ntdown + 1) * 2]; // (float *)malloc0((ntdown + 1) * sizeof(float)); + cup.resize((ntup + 1) * 2); // (float *)malloc0((ntup + 1) * sizeof(float)); + cdown.resize((ntdown + 1) * 2); // (float *)malloc0((ntdown + 1) * sizeof(float)); compute_slews(); // control state = 0; } -void AMSQ::decalc() -{ - delete[] cdown; - delete[] cup; - delete[] trigsig; -} - AMSQ::AMSQ ( int _run, int _size, @@ -113,14 +106,9 @@ AMSQ::AMSQ ( calc(); } -AMSQ::~AMSQ() -{ - decalc(); -} - void AMSQ::flush() { - std::fill(trigsig, trigsig + size * 2, 0); + std::fill(trigsig.begin(), trigsig.end(), 0); avsig = 0.0; state = 0; } @@ -222,7 +210,7 @@ void AMSQ::execute() void AMSQ::xcap() { - std::copy(trigger, trigger + size * 2, trigsig); + std::copy(trigger, trigger + size * 2, trigsig.begin()); } void AMSQ::setBuffers(float* _in, float* _out, float* _trigger) @@ -234,14 +222,12 @@ void AMSQ::setBuffers(float* _in, float* _out, float* _trigger) void AMSQ::setSamplerate(int _rate) { - decalc(); rate = _rate; calc(); } void AMSQ::setSize(int _size) { - decalc(); size = _size; calc(); } diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index f9acdf76b..5e48303b9 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -27,6 +27,8 @@ warren@wpratt.com #ifndef _amsq_h #define _amsq_h +#include + #include "export.h" namespace WDSP { @@ -37,25 +39,25 @@ class TXA; class WDSP_API AMSQ { public: - int run; // 0 if squelch system is OFF; 1 if it's ON - int size; // size of input/output buffers + int run; // 0 if squelch system is OFF; 1 if it's ON + int size; // size of input/output buffers float* in; // squelch input signal buffer float* out; // squelch output signal buffer float* trigger; // pointer to trigger data source - float* trigsig; // buffer containing trigger signal - double rate; // sample rate - double avtau; // time constant for averaging noise + std::vector trigsig; // buffer containing trigger signal + double rate; // sample rate + double avtau; // time constant for averaging noise double avm; double onem_avm; double avsig; - int state; // state machine control + int state; // state machine control int count; double tup; double tdown; int ntup; int ntdown; - double* cup; - double* cdown; + std::vector cup; + std::vector cdown; double tail_thresh; double unmute_thresh; double min_tail; @@ -79,7 +81,7 @@ public: double _muted_gain ); AMSQ(const AMSQ&) = delete; - ~AMSQ(); + ~AMSQ() = default; void flush(); void execute(); @@ -96,7 +98,6 @@ public: private: void compute_slews(); void calc(); - void decalc(); }; } // namespace WDSP diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index e08ce1a67..f4560f51f 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -182,27 +182,6 @@ void ANF::setSize_anf (ANF *a, int size) * * ********************************************************************************************************/ -void ANF::SetANFRun (RXA& rxa, int run) -{ - ANF *a = rxa.anf; - - if (a->run != run) - { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - rxa.emnr->run, - run, - rxa.anr->run - ); - a->run = run; - RXA::bp1Set (rxa); - flush_anf (a); - } -} - - void ANF::SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage) { rxa.anf->n_taps = taps; diff --git a/wdsp/anf.hpp b/wdsp/anf.hpp index 6b9ef6006..987495225 100644 --- a/wdsp/anf.hpp +++ b/wdsp/anf.hpp @@ -87,7 +87,6 @@ public: static void setSamplerate_anf (ANF *a, int rate); static void setSize_anf (ANF *a, int size); // RXA Properties - static void SetANFRun (RXA& rxa, int setit); static void SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage); static void SetANFTaps (RXA& rxa, int taps); static void SetANFDelay (RXA& rxa, int delay); diff --git a/wdsp/anr.cpp b/wdsp/anr.cpp index 6dcadb664..8fe3d5b5b 100644 --- a/wdsp/anr.cpp +++ b/wdsp/anr.cpp @@ -182,26 +182,6 @@ void ANR::setSize_anr (ANR *a, int size) * * ********************************************************************************************************/ -void ANR::SetANRRun (RXA& rxa, int run) -{ - ANR *a = rxa.anr; - - if (a->run != run) - { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - rxa.emnr->run, - rxa.anf->run, - run - ); - a->run = run; - RXA::bp1Set (rxa); - flush_anr (a); - } -} - void ANR::SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage) { rxa.anr->n_taps = taps; diff --git a/wdsp/anr.hpp b/wdsp/anr.hpp index c951c0c14..c0640d00f 100644 --- a/wdsp/anr.hpp +++ b/wdsp/anr.hpp @@ -89,7 +89,6 @@ public: static void setSamplerate_anr (ANR *a, int rate); static void setSize_anr (ANR *a, int size); // RXA Properties - static void SetANRRun (RXA& rxa, int setit); static void SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage); static void SetANRTaps (RXA& rxa, int taps); static void SetANRDelay (RXA& rxa, int delay); diff --git a/wdsp/bpsnba.hpp b/wdsp/bpsnba.hpp index 4e79f6744..a5b320afa 100644 --- a/wdsp/bpsnba.hpp +++ b/wdsp/bpsnba.hpp @@ -79,6 +79,7 @@ public: int maxpb, NOTCHDB* notchdb ); + BPSNBA(const BPSNBA&) = delete; ~BPSNBA(); void flush(); diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index a0bc55cee..20091f0be 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -1071,25 +1071,6 @@ void EMNR::setSize_emnr (EMNR *a, int size) * * ********************************************************************************************************/ -void EMNR::SetEMNRRun (RXA& rxa, int run) -{ - EMNR *a = rxa.emnr; - - if (a->run != run) - { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - run, - rxa.anf->run, - rxa.anr->run - ); - a->run = run; - RXA::bp1Set (rxa); - } -} - void EMNR::SetEMNRgainMethod (RXA& rxa, int method) { rxa.emnr->g.gain_method = method; diff --git a/wdsp/emnr.hpp b/wdsp/emnr.hpp index b3024194d..dadeb0176 100644 --- a/wdsp/emnr.hpp +++ b/wdsp/emnr.hpp @@ -185,7 +185,6 @@ public: static void setSamplerate_emnr (EMNR *a, int rate); static void setSize_emnr (EMNR *a, int size); // RXA Properties - static void SetEMNRRun (RXA& rxa, int run); static void SetEMNRgainMethod (RXA& rxa, int method); static void SetEMNRnpeMethod (RXA& rxa, int method); static void SetEMNRaeRun (RXA& rxa, int run); diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index 651d0a7e8..4dda77be2 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -222,14 +222,14 @@ EQP::EQP( in = _in; out = _out; nfreqs = _nfreqs; - F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - memcpy (F, _F, (_nfreqs + 1) * sizeof (float)); - memcpy (G, _G, (_nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + std::copy(_F, _F + (_nfreqs + 1), F.begin()); + std::copy(_G, _G + (_nfreqs + 1), G.begin()); ctfmode = _ctfmode; wintype = _wintype; samplerate = (double) _samplerate; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); delete[] (impulse); } @@ -263,7 +263,7 @@ void EQP::setSamplerate(int rate) { float* impulse; samplerate = rate; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -273,7 +273,7 @@ void EQP::setSize(int _size) float* impulse; size = _size; FIRCORE::setSize_fircore (fircore, size); - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -296,7 +296,7 @@ void EQP::setNC(int _nc) if (nc != _nc) { nc = _nc; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setNc_fircore (fircore, nc, impulse); delete[] (impulse); } @@ -314,15 +314,12 @@ void EQP::setMP(int _mp) void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) { float* impulse; - delete[] (G); - delete[] (F); nfreqs = _nfreqs; - F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - memcpy (F, _F, (_nfreqs + 1) * sizeof (float)); - memcpy (G, _G, (_nfreqs + 1) * sizeof (float)); - impulse = eq_impulse (nc, nfreqs, F, G, - samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + std::copy(_F, _F + (_nfreqs + 1), F.begin()); + std::copy(_G, _G + (_nfreqs + 1), G.begin()); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -331,7 +328,7 @@ void EQP::setCtfmode(int _mode) { float* impulse; ctfmode = _mode; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -340,7 +337,7 @@ void EQP::setWintype(int _wintype) { float* impulse; wintype = _wintype; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -348,11 +345,9 @@ void EQP::setWintype(int _wintype) void EQP::setGrphEQ(int *rxeq) { // three band equalizer (legacy compatibility) float* impulse; - delete[] (G); - delete[] (F); nfreqs = 4; - F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); F[1] = 150.0; F[2] = 400.0; F[3] = 1500.0; @@ -363,7 +358,7 @@ void EQP::setGrphEQ(int *rxeq) G[3] = (float)rxeq[2]; G[4] = (float)rxeq[3]; ctfmode = 0; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } @@ -372,11 +367,9 @@ void EQP::setGrphEQ10(int *rxeq) { // ten band equalizer (legacy compatibility) float* impulse; int i; - delete[] (G); - delete[] (F); nfreqs = 10; - F = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G = new float[nfreqs + 1]; // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); F[1] = 32.0; F[2] = 63.0; F[3] = 125.0; @@ -390,7 +383,7 @@ void EQP::setGrphEQ10(int *rxeq) for (i = 0; i <= nfreqs; i++) G[i] = (float)rxeq[i]; ctfmode = 0; - impulse = eq_impulse (nc, nfreqs, F, G, samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); // print_impulse ("rxeq.txt", nc, impulse, 1, 0); FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp index 3d1430a25..086bd42ab 100644 --- a/wdsp/eqp.hpp +++ b/wdsp/eqp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_eqp_h #define wdsp_eqp_h +#include + #include "export.h" namespace WDSP { @@ -52,8 +54,8 @@ public: float* in; float* out; int nfreqs; - float* F; - float* G; + std::vector F; + std::vector G; int ctfmode; int wintype; double samplerate; @@ -73,6 +75,7 @@ public: int wintype, int samplerate ); + EQP(const EQP&) = delete; ~EQP(); void flush(); diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 449658a29..27de2f753 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -136,9 +136,9 @@ FMD::FMD( float* impulse; calc(); // de-emphasis filter - audio = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); + audio.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); - pde = FIRCORE::create_fircore (size, audio, out, nc_de, mp_de, impulse); + pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); delete[] (impulse); // audio filter impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); @@ -150,13 +150,12 @@ FMD::~FMD() { FIRCORE::destroy_fircore (paud); FIRCORE::destroy_fircore (pde); - delete[] (audio); decalc(); } void FMD::flush() { - std::fill(audio, audio + size * 2, 0); + std::fill(audio.begin(), audio.end(), 0); FIRCORE::flush_fircore (pde); FIRCORE::flush_fircore (paud); phs = 0.0; @@ -219,7 +218,7 @@ void FMD::setBuffers(float* _in, float* _out) in = _in; out = _out; calc(); - FIRCORE::setBuffers_fircore (pde, audio, out); + FIRCORE::setBuffers_fircore (pde, audio.data(), out); FIRCORE::setBuffers_fircore (paud, out, out); WCPAGC::setBuffers_wcpagc (plim, out, out); } @@ -245,14 +244,13 @@ void FMD::setSize(int _size) { float* impulse; decalc(); - delete[] (audio); size = _size; calc(); - audio = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); + audio.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); // de-emphasis filter FIRCORE::destroy_fircore (pde); impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); - pde = FIRCORE::create_fircore (size, audio, out, nc_de, mp_de, impulse); + pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); delete[] (impulse); // audio filter FIRCORE::destroy_fircore (paud); diff --git a/wdsp/fmd.hpp b/wdsp/fmd.hpp index 3b47b045d..f8e37be77 100644 --- a/wdsp/fmd.hpp +++ b/wdsp/fmd.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_fmd_h #define wdsp_fmd_h +#include + #include "export.h" namespace WDSP { @@ -67,7 +69,7 @@ public: double deviation; double again; // for de-emphasis filter - float* audio; + std::vector audio; FIRCORE *pde; int nc_de; int mp_de; @@ -108,6 +110,7 @@ public: int nc_aud, int mp_aud ); + FMD(const FMD&) = delete; ~FMD(); void flush(); diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index fb673a954..74a9891e9 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -38,7 +38,7 @@ void FMSQ::calc() float* impulse; int i; // noise filter - noise = new float[2 * size * 2]; // (float *)malloc0(2 * size * sizeof(complex)); + noise.resize(2 * size * 2); // (float *)malloc0(2 * size * sizeof(complex)); F[0] = 0.0; F[1] = fc; F[2] = *pllpole; @@ -47,8 +47,8 @@ void FMSQ::calc() G[1] = 0.0; G[2] = 3.0; G[3] = +20.0 * log10(20000.0 / *pllpole); - impulse = EQP::eq_impulse (nc, 3, F, G, rate, 1.0 / (2.0 * size), 0, 0); - p = FIRCORE::create_fircore (size, trigger, noise, nc, mp, impulse); + impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); + p = FIRCORE::create_fircore (size, trigger, noise.data(), nc, mp, impulse); delete[] (impulse); // noise averaging avm = exp(-1.0 / (rate * avtau)); @@ -60,8 +60,8 @@ void FMSQ::calc() // level change ntup = (int)(tup * rate); ntdown = (int)(tdown * rate); - cup = new double[ntup + 1]; // (float *)malloc0 ((ntup + 1) * sizeof(float)); - cdown = new double[ntdown + 1]; //(float *)malloc0 ((ntdown + 1) * sizeof(float)); + cup.resize(ntup + 1); // (float *)malloc0 ((ntup + 1) * sizeof(float)); + cdown.resize(ntdown + 1); //(float *)malloc0 ((ntdown + 1) * sizeof(float)); delta = PI / (double) ntup; theta = 0.0; @@ -88,10 +88,7 @@ void FMSQ::calc() void FMSQ::decalc() { - delete[] (cdown); - delete[] (cup); FIRCORE::destroy_fircore (p); - delete[] (noise); } FMSQ::FMSQ( @@ -261,7 +258,7 @@ void FMSQ::setBuffers(float* in, float* out, float* trig) insig = in; outsig = out; trigger = trig; - FIRCORE::setBuffers_fircore (p, trigger, noise); + FIRCORE::setBuffers_fircore (p, trigger, noise.data()); } void FMSQ::setSamplerate(int _rate) @@ -302,7 +299,7 @@ void FMSQ::setNC(int _nc) if (nc != _nc) { nc = _nc; - impulse = EQP::eq_impulse (nc, 3, F, G, rate, 1.0 / (2.0 * size), 0, 0); + impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); FIRCORE::setNc_fircore (p, nc, impulse); delete[] (impulse); } diff --git a/wdsp/fmsq.hpp b/wdsp/fmsq.hpp index 97f47573f..02d2b13df 100644 --- a/wdsp/fmsq.hpp +++ b/wdsp/fmsq.hpp @@ -28,6 +28,9 @@ warren@wpratt.com #ifndef wdsp_fmsq_h #define wdsp_fmsq_h +#include +#include + #include "export.h" namespace WDSP { @@ -43,11 +46,11 @@ public: float* outsig; // squelch output signal buffer float* trigger; // buffer used to trigger mute/unmute (may be same as input; matches timing of input buffer) double rate; // sample rate - float* noise; + std::vector noise; double fc; // corner frequency for sig / noise detection double* pllpole; // pointer to pole frequency of the fm demodulator pll - float F[4]; - float G[4]; + std::array F; + std::array G; double avtau; // time constant for averaging noise double avm; double onem_avm; @@ -62,8 +65,8 @@ public: double tdown; int ntup; int ntdown; - double* cup; - double* cdown; + std::vector cup; + std::vector cdown; double tail_thresh; double unmute_thresh; double min_tail; @@ -97,6 +100,7 @@ public: int _nc, int _mp ); + FMSQ(const FMSQ&) = delete; ~FMSQ(); void flush(); diff --git a/wdsp/meter.cpp b/wdsp/meter.cpp index 6b9bd2a12..5ad168920 100644 --- a/wdsp/meter.cpp +++ b/wdsp/meter.cpp @@ -109,11 +109,11 @@ void METER::execute() if (np > peak) peak = np; - result[enum_av] = 10.0 * MemLog::mlog10 (avg + 1.0e-40); - result[enum_pk] = 10.0 * MemLog::mlog10 (peak + 1.0e-40); + result[enum_av] = 10.0 * MemLog::mlog10 (avg <= 0 ? 1.0e-20 : avg); + result[enum_pk] = 10.0 * MemLog::mlog10 (peak <= 0 ? 1.0e-20 : peak); if ((pgain != 0) && (enum_gain >= 0)) - result[enum_gain] = 20.0 * MemLog::mlog10 (*pgain + 1.0e-40); + result[enum_gain] = 20.0 * MemLog::mlog10 (*pgain <= 0 ? 1.0e-20 : *pgain); } else { @@ -129,6 +129,7 @@ void METER::execute() void METER::setBuffers(float* in) { buff = in; + flush(); } void METER::setSamplerate(int _rate) From 6662357bcf326eca6c58f2d57b01972b4719a06d Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 29 Jul 2024 08:57:02 +0200 Subject: [PATCH 20/46] WDSP: ANF, ANR, EMNR: use vectors instead of C arrays and disable copy constructor --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 20 +- wdsp/RXA.cpp | 72 +- wdsp/RXA.hpp | 4 +- wdsp/amd.hpp | 1 + wdsp/amsq.hpp | 1 + wdsp/anb.hpp | 1 + wdsp/anf.cpp | 201 ++-- wdsp/anf.hpp | 36 +- wdsp/anr.cpp | 210 ++-- wdsp/anr.hpp | 33 +- wdsp/bpsnba.hpp | 1 + wdsp/emnr.cpp | 1348 ++++++++++++----------- wdsp/emnr.hpp | 167 ++- wdsp/eqp.hpp | 1 + wdsp/fmd.hpp | 1 + wdsp/fmsq.hpp | 1 + wdsp/nbp.hpp | 2 + wdsp/nob.hpp | 1 + wdsp/resample.hpp | 1 + 19 files changed, 1143 insertions(+), 959 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index d03c380b1..f72df0145 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -510,12 +510,12 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) switch (settings.m_nrPosition) { case WDSPRxProfile::NRPositionPreAGC: - WDSP::ANR::SetANRPosition(*m_rxa, 0); - WDSP::EMNR::SetEMNRPosition(*m_rxa, 0); + WDSP::RXA::SetANRPosition(*m_rxa, 0); + WDSP::RXA::SetEMNRPosition(*m_rxa, 0); break; case WDSPRxProfile::NRPositionPostAGC: - WDSP::ANR::SetANRPosition(*m_rxa, 1); - WDSP::EMNR::SetEMNRPosition(*m_rxa, 1); + WDSP::RXA::SetANRPosition(*m_rxa, 1); + WDSP::RXA::SetEMNRPosition(*m_rxa, 1); break; default: break; @@ -527,13 +527,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) switch (settings.m_nr2Gain) { case WDSPRxProfile::NR2GainLinear: - WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 0); + m_rxa->emnr->setGainMethod(0); break; case WDSPRxProfile::NR2GainLog: - WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 1); + m_rxa->emnr->setGainMethod(1); break; case WDSPRxProfile::NR2GainGamma: - WDSP::EMNR::SetEMNRgainMethod(*m_rxa, 2); + m_rxa->emnr->setGainMethod(2); break; default: break; @@ -545,10 +545,10 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) switch (settings.m_nr2NPE) { case WDSPRxProfile::NR2NPEOSMS: - WDSP::EMNR::SetEMNRnpeMethod(*m_rxa, 0); + m_rxa->emnr->setNpeMethod(0); break; case WDSPRxProfile::NR2NPEMMSE: - WDSP::EMNR::SetEMNRnpeMethod(*m_rxa, 1); + m_rxa->emnr->setNpeMethod(1); break; default: break; @@ -556,7 +556,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } if ((m_settings.m_nr2ArtifactReduction != settings.m_nr2ArtifactReduction) || force) { - WDSP::EMNR::SetEMNRaeRun(*m_rxa, settings.m_nr2ArtifactReduction ? 1 : 0); + m_rxa->emnr->setAeRun(settings.m_nr2ArtifactReduction ? 1 : 0); } if ((m_settings.m_anf != settings.m_anf) || force) { diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 4c1d700d1..bae905898 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -347,7 +347,7 @@ RXA* RXA::create_rxa ( } // Auto notch filter - rxa->anf = ANF::create_anf ( + rxa->anf = new ANF( 0, // run - OFF by default 0, // position rxa->dsp_size, // buffer size @@ -367,7 +367,7 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // LMS noise reduction (ANR or "NR") - rxa->anr = ANR::create_anr ( + rxa->anr = new ANR( 0, // run - OFF by default 0, // position rxa->dsp_size, // buffer size @@ -387,7 +387,7 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // Spectral noise reduyction (EMNR or "NR2") - rxa->emnr = EMNR::create_emnr ( + rxa->emnr = new EMNR( 0, // run 0, // position rxa->dsp_size, // buffer size @@ -573,9 +573,9 @@ void RXA::destroy_rxa (RXA *rxa) BANDPASS::destroy_bandpass (rxa->bp1); delete (rxa->agcmeter); WCPAGC::destroy_wcpagc (rxa->agc); - EMNR::destroy_emnr (rxa->emnr); - ANR::destroy_anr (rxa->anr); - ANF::destroy_anf (rxa->anf); + delete (rxa->emnr); + delete (rxa->anr); + delete (rxa->anf); delete (rxa->eqp); delete (rxa->snba); delete (rxa->fmsq); @@ -618,9 +618,9 @@ void RXA::flush_rxa (RXA *rxa) rxa->fmsq->flush(); rxa->snba->flush(); rxa->eqp->flush(); - ANF::flush_anf (rxa->anf); - ANR::flush_anr (rxa->anr); - EMNR::flush_emnr (rxa->emnr); + rxa->anf->flush(); + rxa->anr->flush(); + rxa->emnr->flush(); WCPAGC::flush_wcpagc (rxa->agc); rxa->agcmeter->flush(); BANDPASS::flush_bandpass (rxa->bp1); @@ -653,14 +653,14 @@ void RXA::xrxa (RXA *rxa) rxa->bpsnba->exec_out(1); rxa->snba->execute(); rxa->eqp->execute(); - ANF::xanf (rxa->anf, 0); - ANR::xanr (rxa->anr, 0); - EMNR::xemnr (rxa->emnr, 0); + rxa->anf->execute(0); + rxa->anr->ANR::execute(0); + rxa->emnr->execute(0); BANDPASS::xbandpass (rxa->bp1, 0); WCPAGC::xwcpagc (rxa->agc); - ANF::xanf (rxa->anf, 1); - ANR::xanr (rxa->anr, 1); - EMNR::xemnr (rxa->emnr, 1); + rxa->anf->execute(1); + rxa->anr->execute(1); + rxa->emnr->execute(1); BANDPASS::xbandpass (rxa->bp1, 1); rxa->agcmeter->execute(); SIPHON::xsiphon (rxa->sip1, 0); @@ -764,9 +764,9 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->fmsq->setSamplerate(rxa->dsp_rate); // rxa->snba->setSamplerate(rxa->dsp_rate); SMBA removed rxa->eqp->setSamplerate(rxa->dsp_rate); - ANF::setSamplerate_anf (rxa->anf, rxa->dsp_rate); - ANR::setSamplerate_anr (rxa->anr, rxa->dsp_rate); - EMNR::setSamplerate_emnr (rxa->emnr, rxa->dsp_rate); + rxa->anf->setSamplerate(rxa->dsp_rate); + rxa->anr->setSamplerate(rxa->dsp_rate); + rxa->emnr->setSamplerate(rxa->dsp_rate); BANDPASS::setSamplerate_bandpass (rxa->bp1, rxa->dsp_rate); WCPAGC::setSamplerate_wcpagc (rxa->agc, rxa->dsp_rate); rxa->agcmeter->setSamplerate(rxa->dsp_rate); @@ -837,12 +837,12 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->snba->setSize(rxa->dsp_size); rxa->eqp->setBuffers(rxa->midbuff, rxa->midbuff); rxa->eqp->setSize(rxa->dsp_size); - ANF::setBuffers_anf (rxa->anf, rxa->midbuff, rxa->midbuff); - ANF::setSize_anf (rxa->anf, rxa->dsp_size); - ANR::setBuffers_anr (rxa->anr, rxa->midbuff, rxa->midbuff); - ANR::setSize_anr (rxa->anr, rxa->dsp_size); - EMNR::setBuffers_emnr (rxa->emnr, rxa->midbuff, rxa->midbuff); - EMNR::setSize_emnr (rxa->emnr, rxa->dsp_size); + rxa->anf->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->anf->setSize(rxa->dsp_size); + rxa->anr->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->anr->setSize(rxa->dsp_size); + rxa->emnr->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->emnr->setSize(rxa->dsp_size); BANDPASS::setBuffers_bandpass (rxa->bp1, rxa->midbuff, rxa->midbuff); BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); WCPAGC::setBuffers_wcpagc (rxa->agc, rxa->midbuff, rxa->midbuff); @@ -1280,10 +1280,17 @@ void RXA::SetANFRun (RXA& rxa, int run) ); a->run = run; RXA::bp1Set (rxa); - ANF::flush_anf (a); + a->flush(); } } +void RXA::SetANFPosition (RXA& rxa, int position) +{ + rxa.anf->position = position; + rxa.bp1->position = position; + rxa.anf->flush(); +} + void RXA::SetANRRun (RXA& rxa, int run) { ANR *a = rxa.anr; @@ -1300,10 +1307,17 @@ void RXA::SetANRRun (RXA& rxa, int run) ); a->run = run; RXA::bp1Set (rxa); - ANR::flush_anr (a); + a->flush(); } } +void RXA::SetANRPosition (RXA& rxa, int position) +{ + rxa.anr->position = position; + rxa.bp1->position = position; + rxa.anr->flush(); +} + void RXA::SetEMNRRun (RXA& rxa, int run) { EMNR *a = rxa.emnr; @@ -1323,6 +1337,12 @@ void RXA::SetEMNRRun (RXA& rxa, int run) } } +void RXA::SetEMNRPosition (RXA& rxa, int position) +{ + rxa.emnr->position = position; + rxa.bp1->position = position; +} + /******************************************************************************************************** * * * Collectives * diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 26eb6167e..ac8e1b861 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -173,11 +173,13 @@ public: static void SetSNBARun (RXA& rxa, int run); // ANF static void SetANFRun (RXA& rxa, int run); + static void SetANFPosition (RXA& rxa, int position); // ANR static void SetANRRun (RXA& rxa, int run); + static void SetANRPosition (RXA& rxa, int position); // EMNR static void SetEMNRRun (RXA& rxa, int run); - + static void SetEMNRPosition (RXA& rxa, int position); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); static void SetNC (RXA& rxa, int nc); diff --git a/wdsp/amd.hpp b/wdsp/amd.hpp index 6b955587c..3fb7a91a2 100644 --- a/wdsp/amd.hpp +++ b/wdsp/amd.hpp @@ -100,6 +100,7 @@ public: double tauI ); AMD(const AMD&) = delete; + AMD& operator=(const AMD& other) = delete; ~AMD() = default; void init(); diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index 5e48303b9..02819475d 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -81,6 +81,7 @@ public: double _muted_gain ); AMSQ(const AMSQ&) = delete; + AMSQ& operator=(const AMSQ& other) = delete; ~AMSQ() = default; void flush(); diff --git a/wdsp/anb.hpp b/wdsp/anb.hpp index 2a349e19f..e05998289 100644 --- a/wdsp/anb.hpp +++ b/wdsp/anb.hpp @@ -80,6 +80,7 @@ public: double threshold ); ANB(const ANB&) = delete; + ANB& operator=(const ANB& other) = delete; ~ANB() = default; void flush(); diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index f4560f51f..81edd9578 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -36,144 +36,136 @@ warren@wpratt.com namespace WDSP { -ANF* ANF::create_anf( - int run, - int position, - int buff_size, - float *in_buff, - float *out_buff, - int dline_size, - int n_taps, - int delay, - double two_mu, - double gamma, - double lidx, - double lidx_min, - double lidx_max, - double ngamma, - double den_mult, - double lincr, - double ldecr +ANF::ANF( + int _run, + int _position, + int _buff_size, + float *_in_buff, + float *_out_buff, + int _dline_size, + int _n_taps, + int _delay, + double _two_mu, + double _gamma, + double _lidx, + double _lidx_min, + double _lidx_max, + double _ngamma, + double _den_mult, + double _lincr, + double _ldecr ) { - ANF *a = new ANF; - a->run = run; - a->position = position; - a->buff_size = buff_size; - a->in_buff = in_buff; - a->out_buff = out_buff; - a->dline_size = dline_size; - a->mask = dline_size - 1; - a->n_taps = n_taps; - a->delay = delay; - a->two_mu = two_mu; - a->gamma = gamma; - a->in_idx = 0; - a->lidx = lidx; - a->lidx_min = lidx_min; - a->lidx_max = lidx_max; - a->ngamma = ngamma; - a->den_mult = den_mult; - a->lincr = lincr; - a->ldecr = ldecr; + run = _run; + position = _position; + buff_size = _buff_size; + in_buff = _in_buff; + out_buff = _out_buff; + dline_size = _dline_size; + mask = _dline_size - 1; + n_taps = _n_taps; + delay = _delay; + two_mu = _two_mu; + gamma = _gamma; + in_idx = 0; + lidx = _lidx; + lidx_min = _lidx_min; + lidx_max = _lidx_max; + ngamma = _ngamma; + den_mult = _den_mult; + lincr = _lincr; + ldecr = _ldecr; - memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE); - memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE); - - return a; + std::fill(d.begin(), d.end(), 0); + std::fill(w.begin(), w.end(), 0); } -void ANF::destroy_anf (ANF *a) -{ - delete a; -} - -void ANF::xanf(ANF *a, int position) +void ANF::execute(int _position) { int i, j, idx; double c0, c1; double y, error, sigma, inv_sigp; double nel, nev; - if (a->run && (a->position == position)) + if (run && (position == _position)) { - for (i = 0; i < a->buff_size; i++) + for (i = 0; i < buff_size; i++) { - a->d[a->in_idx] = a->in_buff[2 * i + 0]; + d[in_idx] = in_buff[2 * i + 0]; y = 0; sigma = 0; - for (j = 0; j < a->n_taps; j++) + for (j = 0; j < n_taps; j++) { - idx = (a->in_idx + j + a->delay) & a->mask; - y += a->w[j] * a->d[idx]; - sigma += a->d[idx] * a->d[idx]; + idx = (in_idx + j + delay) & mask; + y += w[j] * d[idx]; + sigma += d[idx] * d[idx]; } inv_sigp = 1.0 / (sigma + 1e-10); - error = a->d[a->in_idx] - y; + error = d[in_idx] - y; - a->out_buff[2 * i + 0] = error; - a->out_buff[2 * i + 1] = 0.0; + out_buff[2 * i + 0] = error; + out_buff[2 * i + 1] = 0.0; - if ((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0) + if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0) nel = -nel; - if ((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0) + if ((nev = d[in_idx] - (1.0 - two_mu * ngamma) * y - two_mu * error * sigma * inv_sigp) < 0.0) nev = -nev; if (nev < nel) { - if ((a->lidx += a->lincr) > a->lidx_max) a->lidx = a->lidx_max; + if ((lidx += lincr) > lidx_max) lidx = lidx_max; } else { - if ((a->lidx -= a->ldecr) < a->lidx_min) a->lidx = a->lidx_min; + if ((lidx -= ldecr) < lidx_min) lidx = lidx_min; } - a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult; + ngamma = gamma * (lidx * lidx) * (lidx * lidx) * den_mult; - c0 = 1.0 - a->two_mu * a->ngamma; - c1 = a->two_mu * error * inv_sigp; + c0 = 1.0 - two_mu * ngamma; + c1 = two_mu * error * inv_sigp; - for (j = 0; j < a->n_taps; j++) + for (j = 0; j < n_taps; j++) { - idx = (a->in_idx + j + a->delay) & a->mask; - a->w[j] = c0 * a->w[j] + c1 * a->d[idx]; + idx = (in_idx + j + delay) & mask; + w[j] = c0 * w[j] + c1 * d[idx]; } - a->in_idx = (a->in_idx + a->mask) & a->mask; + in_idx = (in_idx + mask) & mask; } } - else if (a->in_buff != a->out_buff) + else if (in_buff != out_buff) { - std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff); + std::copy(in_buff, in_buff + buff_size * 2, out_buff); } } -void ANF::flush_anf (ANF *a) +void ANF::flush() { - memset (a->d, 0, sizeof(double) * ANF_DLINE_SIZE); - memset (a->w, 0, sizeof(double) * ANF_DLINE_SIZE); - a->in_idx = 0; + std::fill(d.begin(), d.end(), 0); + std::fill(w.begin(), w.end(), 0); + in_idx = 0; } -void ANF::setBuffers_anf (ANF *a, float* in, float* out) +void ANF::setBuffers(float* _in, float* _out) { - a->in_buff = in; - a->out_buff = out; + in_buff = _in; + out_buff = _out; } -void ANF::setSamplerate_anf (ANF *a, int) +void ANF::setSamplerate(int) { - flush_anf (a); + flush(); } -void ANF::setSize_anf (ANF *a, int size) +void ANF::setSize(int _size) { - a->buff_size = size; - flush_anf (a); + buff_size = _size; + flush(); } /******************************************************************************************************** @@ -182,44 +174,37 @@ void ANF::setSize_anf (ANF *a, int size) * * ********************************************************************************************************/ -void ANF::SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage) +void ANF::setVals(int _taps, int _delay, double _gain, double _leakage) { - rxa.anf->n_taps = taps; - rxa.anf->delay = delay; - rxa.anf->two_mu = gain; //try two_mu = 1e-4 - rxa.anf->gamma = leakage; //try gamma = 0.10 - flush_anf (rxa.anf); + n_taps = _taps; + delay = _delay; + two_mu = _gain; //try two_mu = 1e-4 + gamma = _leakage; //try gamma = 0.10 + flush(); } -void ANF::SetANFTaps (RXA& rxa, int taps) +void ANF::setTaps(int _taps) { - rxa.anf->n_taps = taps; - flush_anf (rxa.anf); + n_taps = _taps; + flush(); } -void ANF::SetANFDelay (RXA& rxa, int delay) +void ANF::setDelay(int _delay) { - rxa.anf->delay = delay; - flush_anf (rxa.anf); + delay = _delay; + flush(); } -void ANF::SetANFGain (RXA& rxa, double gain) +void ANF::setGain(double _gain) { - rxa.anf->two_mu = gain; - flush_anf (rxa.anf); + two_mu = _gain; + flush(); } -void ANF::SetANFLeakage (RXA& rxa, double leakage) +void ANF::setLeakage(double _leakage) { - rxa.anf->gamma = leakage; - flush_anf (rxa.anf); -} - -void ANF::SetANFPosition (RXA& rxa, int position) -{ - rxa.anf->position = position; - rxa.bp1->position = position; - flush_anf (rxa.anf); + gamma = _leakage; + flush(); } } // namespace WDSP diff --git a/wdsp/anf.hpp b/wdsp/anf.hpp index 987495225..73fc1f052 100644 --- a/wdsp/anf.hpp +++ b/wdsp/anf.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_anf_h #define wdsp_anf_h +#include + #include "export.h" #define ANF_DLINE_SIZE 2048 @@ -50,8 +52,8 @@ public: int delay; double two_mu; double gamma; - double d [ANF_DLINE_SIZE]; - double w [ANF_DLINE_SIZE]; + std::array d; + std::array w; int in_idx; double lidx; double lidx_min; @@ -61,7 +63,7 @@ public: double lincr; double ldecr; - static ANF* create_anf( + ANF( int run, int position, int buff_size, @@ -80,19 +82,21 @@ public: double lincr, double ldecr ); - static void destroy_anf (ANF *a); - static void flush_anf (ANF *a); - static void xanf (ANF *a, int position); - static void setBuffers_anf (ANF *a, float* in, float* out); - static void setSamplerate_anf (ANF *a, int rate); - static void setSize_anf (ANF *a, int size); - // RXA Properties - static void SetANFVals (RXA& rxa, int taps, int delay, double gain, double leakage); - static void SetANFTaps (RXA& rxa, int taps); - static void SetANFDelay (RXA& rxa, int delay); - static void SetANFGain (RXA& rxa, double gain); - static void SetANFLeakage (RXA& rxa, double leakage); - static void SetANFPosition (RXA& rxa, int position); + ANF(const ANF&) = delete; + ANF& operator=(const ANF& other) = delete; + ~ANF() = default; + + void flush(); + void execute(int position); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setVals(int taps, int delay, double gain, double leakage); + void setTaps(int taps); + void setDelay(int delay); + void setGain(double gain); + void setLeakage(double leakage); }; } // namespace WDSP diff --git a/wdsp/anr.cpp b/wdsp/anr.cpp index 8fe3d5b5b..de998264a 100644 --- a/wdsp/anr.cpp +++ b/wdsp/anr.cpp @@ -36,190 +36,174 @@ warren@wpratt.com namespace WDSP { -ANR* ANR::create_anr ( - int run, - int position, - int buff_size, - float *in_buff, - float *out_buff, - int dline_size, - int n_taps, - int delay, - double two_mu, - double gamma, - double lidx, - double lidx_min, - double lidx_max, - double ngamma, - double den_mult, - double lincr, - double ldecr -) +ANR::ANR( + int _run, + int _position, + int _buff_size, + float *_in_buff, + float *_out_buff, + int _dline_size, + int _n_taps, + int _delay, + double _two_mu, + double _gamma, + double _lidx, + double _lidx_min, + double _lidx_max, + double _ngamma, + double _den_mult, + double _lincr, + double _ldecr +) : + run(_run), + position(_position), + buff_size(_buff_size), + in_buff(_in_buff), + out_buff(_out_buff), + dline_size(_dline_size), + mask(_dline_size - 1), + n_taps(_n_taps), + delay(_delay), + two_mu(_two_mu), + gamma(_gamma), + in_idx(0), + lidx(_lidx), + lidx_min(_lidx_min), + lidx_max(_lidx_max), + ngamma(_ngamma), + den_mult(_den_mult), + lincr(_lincr), + ldecr(_ldecr) { - ANR *a = new ANR; - a->run = run; - a->position = position; - a->buff_size = buff_size; - a->in_buff = in_buff; - a->out_buff = out_buff; - a->dline_size = dline_size; - a->mask = dline_size - 1; - a->n_taps = n_taps; - a->delay = delay; - a->two_mu = two_mu; - a->gamma = gamma; - a->in_idx = 0; - a->lidx = lidx; - a->lidx_min = lidx_min; - a->lidx_max = lidx_max; - a->ngamma = ngamma; - a->den_mult = den_mult; - a->lincr = lincr; - a->ldecr = ldecr; - - memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE); - memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE); - - return a; + std::fill(d.begin(), d.end(), 0); + std::fill(w.begin(), w.end(), 0); } -void ANR::destroy_anr (ANR *a) -{ - delete a; -} - -void ANR::xanr (ANR *a, int position) +void ANR::execute(int _position) { int i, j, idx; double c0, c1; double y, error, sigma, inv_sigp; double nel, nev; - if (a->run && (a->position == position)) + if (run && (position == _position)) { - for (i = 0; i < a->buff_size; i++) + for (i = 0; i < buff_size; i++) { - a->d[a->in_idx] = a->in_buff[2 * i + 0]; + d[in_idx] = in_buff[2 * i + 0]; y = 0; sigma = 0; - for (j = 0; j < a->n_taps; j++) + for (j = 0; j < n_taps; j++) { - idx = (a->in_idx + j + a->delay) & a->mask; - y += a->w[j] * a->d[idx]; - sigma += a->d[idx] * a->d[idx]; + idx = (in_idx + j + delay) & mask; + y += w[j] * d[idx]; + sigma += d[idx] * d[idx]; } inv_sigp = 1.0 / (sigma + 1e-10); - error = a->d[a->in_idx] - y; + error = d[in_idx] - y; - a->out_buff[2 * i + 0] = y; - a->out_buff[2 * i + 1] = 0.0; + out_buff[2 * i + 0] = y; + out_buff[2 * i + 1] = 0.0; - if ((nel = error * (1.0 - a->two_mu * sigma * inv_sigp)) < 0.0) + if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0) nel = -nel; - if ((nev = a->d[a->in_idx] - (1.0 - a->two_mu * a->ngamma) * y - a->two_mu * error * sigma * inv_sigp) < 0.0) + if ((nev = d[in_idx] - (1.0 - two_mu * ngamma) * y - two_mu * error * sigma * inv_sigp) < 0.0) nev = -nev; if (nev < nel) { - if ((a->lidx += a->lincr) > a->lidx_max) - a->lidx = a->lidx_max; + if ((lidx += lincr) > lidx_max) + lidx = lidx_max; } else { - if ((a->lidx -= a->ldecr) < a->lidx_min) - a->lidx = a->lidx_min; + if ((lidx -= ldecr) < lidx_min) + lidx = lidx_min; } - a->ngamma = a->gamma * (a->lidx * a->lidx) * (a->lidx * a->lidx) * a->den_mult; - c0 = 1.0 - a->two_mu * a->ngamma; - c1 = a->two_mu * error * inv_sigp; + ngamma = gamma * (lidx * lidx) * (lidx * lidx) * den_mult; + c0 = 1.0 - two_mu * ngamma; + c1 = two_mu * error * inv_sigp; - for (j = 0; j < a->n_taps; j++) + for (j = 0; j < n_taps; j++) { - idx = (a->in_idx + j + a->delay) & a->mask; - a->w[j] = c0 * a->w[j] + c1 * a->d[idx]; + idx = (in_idx + j + delay) & mask; + w[j] = c0 * w[j] + c1 * d[idx]; } - a->in_idx = (a->in_idx + a->mask) & a->mask; + in_idx = (in_idx + mask) & mask; } } - else if (a->in_buff != a->out_buff) + else if (in_buff != out_buff) { - std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff); + std::copy(in_buff, in_buff + buff_size * 2, out_buff); } } -void ANR::flush_anr (ANR *a) +void ANR::flush() { - memset (a->d, 0, sizeof(double) * ANR_DLINE_SIZE); - memset (a->w, 0, sizeof(double) * ANR_DLINE_SIZE); - a->in_idx = 0; + std::fill(d.begin(), d.end(), 0); + std::fill(w.begin(), w.end(), 0); + in_idx = 0; } -void ANR::setBuffers_anr (ANR *a, float* in, float* out) +void ANR::setBuffers(float* _in, float* _out) { - a->in_buff = in; - a->out_buff = out; + in_buff = _in; + out_buff = _out; } -void ANR::setSamplerate_anr (ANR *a, int) +void ANR::setSamplerate(int) { - flush_anr(a); + flush(); } -void ANR::setSize_anr (ANR *a, int size) +void ANR::setSize(int _size) { - a->buff_size = size; - flush_anr(a); + buff_size = _size; + flush(); } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -void ANR::SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage) +void ANR::setVals(int _taps, int _delay, double _gain, double _leakage) { - rxa.anr->n_taps = taps; - rxa.anr->delay = delay; - rxa.anr->two_mu = gain; - rxa.anr->gamma = leakage; - flush_anr (rxa.anr); + n_taps = _taps; + delay = _delay; + two_mu = _gain; + gamma = _leakage; + flush(); } -void ANR::SetANRTaps (RXA& rxa, int taps) +void ANR::setTaps(int _taps) { - rxa.anr->n_taps = taps; - flush_anr (rxa.anr); + n_taps = _taps; + flush(); } -void ANR::SetANRDelay (RXA& rxa, int delay) +void ANR::setDelay(int _delay) { - rxa.anr->delay = delay; - flush_anr (rxa.anr); + delay = _delay; + flush(); } -void ANR::SetANRGain (RXA& rxa, double gain) +void ANR::setGain(double _gain) { - rxa.anr->two_mu = gain; - flush_anr (rxa.anr); + two_mu = _gain; + flush(); } -void ANR::SetANRLeakage (RXA& rxa, double leakage) +void ANR::setLeakage(double _leakage) { - rxa.anr->gamma = leakage; - flush_anr (rxa.anr); -} - -void ANR::SetANRPosition (RXA& rxa, int position) -{ - rxa.anr->position = position; - rxa.bp1->position = position; - flush_anr (rxa.anr); + gamma = _leakage; + flush(); } } // namespace WDSP diff --git a/wdsp/anr.hpp b/wdsp/anr.hpp index c0640d00f..8adeebc16 100644 --- a/wdsp/anr.hpp +++ b/wdsp/anr.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_anr_h #define wdsp_anr_h +#include + #include "export.h" #define ANR_DLINE_SIZE 2048 @@ -50,8 +52,8 @@ public: int delay; double two_mu; double gamma; - double d [ANR_DLINE_SIZE]; - double w [ANR_DLINE_SIZE]; + std::array d; + std::array w; int in_idx; double lidx; @@ -62,7 +64,7 @@ public: double lincr; double ldecr; - static ANR* create_anr ( + ANR( int run, int position, int buff_size, @@ -81,20 +83,21 @@ public: double lincr, double ldecr ); + ANR(const ANR&) = delete; + ANR& operator=(const ANR& other) = delete; + ~ANR() = default; - static void destroy_anr (ANR *a); - static void flush_anr (ANR *a); - static void xanr (ANR *a, int position); - static void setBuffers_anr (ANR *a, float* in, float* out); - static void setSamplerate_anr (ANR *a, int rate); - static void setSize_anr (ANR *a, int size); + void flush(); + void execute(int position); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void SetANRVals (RXA& rxa, int taps, int delay, double gain, double leakage); - static void SetANRTaps (RXA& rxa, int taps); - static void SetANRDelay (RXA& rxa, int delay); - static void SetANRGain (RXA& rxa, double gain); - static void SetANRLeakage (RXA& rxa, double leakage); - static void SetANRPosition (RXA& rxa, int position); + void setVals(int taps, int delay, double gain, double leakage); + void setTaps(int taps); + void setDelay(int delay); + void setGain(double gain); + void setLeakage(double leakage); }; } // namespace WDSP diff --git a/wdsp/bpsnba.hpp b/wdsp/bpsnba.hpp index a5b320afa..28221eb63 100644 --- a/wdsp/bpsnba.hpp +++ b/wdsp/bpsnba.hpp @@ -80,6 +80,7 @@ public: NOTCHDB* notchdb ); BPSNBA(const BPSNBA&) = delete; + BPSNBA& operator=(BPSNBA& other) = delete; ~BPSNBA(); void flush(); diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index 20091f0be..35909775b 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -38,6 +38,490 @@ warren@wpratt.com namespace WDSP { +EMNR::AE::AE( + int _msize, + double* _lambda_y, + double _zetaThresh, + double _psi +) : + msize(_msize), + lambda_y(_lambda_y), + zetaThresh(_zetaThresh), + psi(_psi) +{ + nmask = new double[msize]; +} + +EMNR::AE::~AE() +{ + delete[] nmask; +} + +EMNR::NPS::NPS( + int _incr, + double _rate, + int _msize, + double* _lambda_y, + double* _lambda_d, + + double _alpha_pow, + double _alpha_Pbar, + double _epsH1 +) : + incr(_incr), + rate(_rate), + msize(_msize), + lambda_y(_lambda_y), + lambda_d(_lambda_d), + alpha_pow(_alpha_pow), + alpha_Pbar(_alpha_Pbar), + epsH1(_epsH1) +{ + epsH1r = epsH1 / (1.0 + epsH1); + sigma2N = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); + PH1y = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); + Pbar = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); + EN2y = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); + + for (int i = 0; i < msize; i++) + { + sigma2N[i] = 0.5; + Pbar[i] = 0.5; + } +} + +EMNR::NPS::~NPS() +{ + delete[] sigma2N; + delete[] PH1y; + delete[] Pbar; + delete[] EN2y; +} + +void EMNR::NPS::LambdaDs() +{ + int k; + + for (k = 0; k < msize; k++) + { + PH1y[k] = 1.0 / (1.0 + (1.0 + epsH1) * exp (- epsH1r * lambda_y[k] / sigma2N[k])); + Pbar[k] = alpha_Pbar * Pbar[k] + (1.0 - alpha_Pbar) * PH1y[k]; + + if (Pbar[k] > 0.99) + PH1y[k] = std::min (PH1y[k], 0.99); + + EN2y[k] = (1.0 - PH1y[k]) * lambda_y[k] + PH1y[k] * sigma2N[k]; + sigma2N[k] = alpha_pow * sigma2N[k] + (1.0 - alpha_pow) * EN2y[k]; + } + + std::copy(sigma2N, sigma2N + msize, lambda_d); +} + +const std::array EMNR::NP::DVals = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0, + 60.0, 80.0, 120.0, 140.0, 160.0, 180.0, 220.0, 260.0, 300.0 }; +const std::array EMNR::NP::MVals = { 0.000, 0.260, 0.480, 0.580, 0.610, 0.668, 0.705, 0.762, 0.800, + 0.841, 0.865, 0.890, 0.900, 0.910, 0.920, 0.930, 0.935, 0.940 }; + +EMNR::NP::NP( + int _incr, + double _rate, + int _msize, + double* _lambda_y, + double* _lambda_d +) +{ + incr = _incr; + rate = _rate; + msize = _msize; + lambda_y = _lambda_y; + lambda_d = _lambda_d; + + { + double tau = -128.0 / 8000.0 / log(0.7); + alphaCsmooth = exp(-incr / rate / tau); + } + + { + double tau = -128.0 / 8000.0 / log(0.96); + alphaMax = exp(-incr / rate / tau); + } + + { + double tau = -128.0 / 8000.0 / log(0.7); + alphaCmin = exp(-incr / rate / tau); + } + + { + double tau = -128.0 / 8000.0 / log(0.3); + alphaMin_max_value = exp(-incr / rate / tau); + } + + snrq = -incr / (0.064 * rate); + + double tau4 = -128.0 / 8000.0 / log(0.8); + betamax = exp(-incr / rate / tau4); + + invQeqMax = 0.5; + av = 2.12; + Dtime = 8.0 * 12.0 * 128.0 / 8000.0; + U = 8; + V = (int)(0.5 + (Dtime * rate / (U * incr))); + + if (V < 4) + V = 4; + + if ((U = (int)(0.5 + (Dtime * rate / (V * incr)))) < 1) + U = 1; + + D = U * V; + interpM(&MofD, D, 18, DVals, MVals); + interpM(&MofV, V, 18, DVals, MVals); + + invQbar_points[0] = 0.03; + invQbar_points[1] = 0.05; + invQbar_points[2] = 0.06; + invQbar_points[3] = std::numeric_limits::max();; + + { + double db; + db = 10.0 * log10(8.0) / (12.0 * 128 / 8000); + nsmax[0] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(4.0) / (12.0 * 128 / 8000); + nsmax[1] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(2.0) / (12.0 * 128 / 8000); + nsmax[2] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(1.2) / (12.0 * 128 / 8000); + nsmax[3] = pow(10.0, db / 10.0 * V * incr / rate); + } + + p = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + alphaOptHat = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + alphaHat = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + sigma2N = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + pbar = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + p2bar = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + Qeq = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + bmin = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + bmin_sub = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + k_mod = new int[msize]; // (int *)malloc0(np.msize * sizeof(int)); + actmin = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + actmin_sub = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + lmin_flag = new int[msize]; // (int *)malloc0(np.msize * sizeof(int)); + pmin_u = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + actminbuff = new double*[U]; // (float**)malloc0(np.U * sizeof(float*)); + + for (int i = 0; i < U; i++) { + actminbuff[i] = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + } + + { + int k, ku; + alphaC = 1.0; + subwc = V; + amb_idx = 0; + + for (k = 0; k < msize; k++) { + lambda_y[k] = 0.5; + } + + std::copy(lambda_y, lambda_y + msize, p); + std::copy(lambda_y, lambda_y + msize, sigma2N); + std::copy(lambda_y, lambda_y + msize, pbar); + std::copy(lambda_y, lambda_y + msize, pmin_u); + + for (k = 0; k < msize; k++) + { + p2bar[k] = lambda_y[k] * lambda_y[k]; + actmin[k] = std::numeric_limits::max(); + actmin_sub[k] = std::numeric_limits::max();; + + for (ku = 0; ku < U; ku++) { + actminbuff[ku][k] = std::numeric_limits::max();; + } + } + + std::fill(lmin_flag, lmin_flag + msize, 0); + } +} + +EMNR::NP::~NP() +{ + for (int i = 0; i < U; i++) + delete[] (actminbuff[i]); + + delete[] (actminbuff); + delete[] (pmin_u); + delete[] (lmin_flag); + delete[] (actmin_sub); + delete[] (actmin); + delete[] (k_mod); + delete[] (bmin_sub); + delete[] (bmin); + delete[] (Qeq); + delete[] (p2bar); + delete[] (pbar); + delete[] (sigma2N); + delete[] (alphaHat); + delete[] (alphaOptHat); + delete[] (p); +} + +void EMNR::NP::interpM ( + double* res, + double x, + int nvals, + const std::array& xvals, + const std::array& yvals +) +{ + if (x <= xvals[0]) + { + *res = yvals[0]; + } + else if (x >= xvals[nvals - 1]) + { + *res = yvals[nvals - 1]; + } + else + { + int idx = 1; + double xllow, xlhigh, frac; + + while ((x >= xvals[idx]) && (idx < nvals - 1)) + idx++; + + xllow = log10 (xvals[idx - 1]); + xlhigh = log10(xvals[idx]); + frac = (log10 (x) - xllow) / (xlhigh - xllow); + *res = yvals[idx - 1] + frac * (yvals[idx] - yvals[idx - 1]); + } +} + +void EMNR::NP::LambdaD() +{ + int k; + double f0, f1, f2, f3; + double sum_prev_p; + double sum_lambda_y; + double alphaCtilda; + double sum_prev_sigma2N; + double alphaMin, SNR; + double beta, varHat, invQeq; + double invQbar; + double bc; + double QeqTilda, QeqTildaSub; + double noise_slope_max; + + sum_prev_p = 0.0; + sum_lambda_y = 0.0; + sum_prev_sigma2N = 0.0; + + for (k = 0; k < msize; k++) + { + sum_prev_p += p[k]; + sum_lambda_y += lambda_y[k]; + sum_prev_sigma2N += sigma2N[k]; + } + + for (k = 0; k < msize; k++) + { + f0 = p[k] / sigma2N[k] - 1.0; + alphaOptHat[k] = 1.0 / (1.0 + f0 * f0); + } + + SNR = sum_prev_p / sum_prev_sigma2N; + alphaMin = std::min (alphaMin_max_value, pow (SNR, snrq)); + + for (k = 0; k < msize; k++) + if (alphaOptHat[k] < alphaMin) alphaOptHat[k] = alphaMin; + + f1 = sum_prev_p / sum_lambda_y - 1.0; + alphaCtilda = 1.0 / (1.0 + f1 * f1); + alphaC = alphaCsmooth * alphaC + (1.0 - alphaCsmooth) * std::max (alphaCtilda, alphaCmin); + f2 = alphaMax * alphaC; + + for (k = 0; k < msize; k++) + alphaHat[k] = f2 * alphaOptHat[k]; + + for (k = 0; k < msize; k++) + p[k] = alphaHat[k] * p[k] + (1.0 - alphaHat[k]) * lambda_y[k]; + + invQbar = 0.0; + + for (k = 0; k < msize; k++) + { + beta = std::min (betamax, alphaHat[k] * alphaHat[k]); + pbar[k] = beta * pbar[k] + (1.0 - beta) * p[k]; + p2bar[k] = beta * p2bar[k] + (1.0 - beta) * p[k] * p[k]; + varHat = p2bar[k] - pbar[k] * pbar[k]; + invQeq = varHat / (2.0 * sigma2N[k] * sigma2N[k]); + if (invQeq > invQeqMax) + invQeq = invQeqMax; + Qeq[k] = 1.0 / invQeq; + invQbar += invQeq; + } + invQbar /= (double) msize; + bc = 1.0 + av * sqrt (invQbar); + + for (k = 0; k < msize; k++) + { + QeqTilda = (Qeq[k] - 2.0 * MofD) / (1.0 - MofD); + QeqTildaSub = (Qeq[k] - 2.0 * MofV) / (1.0 - MofV); + bmin[k] = 1.0 + 2.0 * (D - 1.0) / QeqTilda; + bmin_sub[k] = 1.0 + 2.0 * (V - 1.0) / QeqTildaSub; + } + + std::fill(k_mod, k_mod + msize, 0); + + for (k = 0; k < msize; k++) + { + f3 = p[k] * bmin[k] * bc; + + if (f3 < actmin[k]) + { + actmin[k] = f3; + actmin_sub[k] = p[k] * bmin_sub[k] * bc; + k_mod[k] = 1; + } + } + + if (subwc == V) + { + if (invQbar < invQbar_points[0]) + noise_slope_max = nsmax[0]; + else if (invQbar < invQbar_points[1]) + noise_slope_max = nsmax[1]; + else if (invQbar < invQbar_points[2]) + noise_slope_max = nsmax[2]; + else + noise_slope_max = nsmax[3]; + + for (k = 0; k < msize; k++) + { + int ku; + double min; + + if (k_mod[k]) + lmin_flag[k] = 0; + + actminbuff[amb_idx][k] = actmin[k]; + min = std::numeric_limits::max();; + + for (ku = 0; ku < U; ku++) + { + if (actminbuff[ku][k] < min) + min = actminbuff[ku][k]; + } + + pmin_u[k] = min; + + if ((lmin_flag[k] == 1) + && (actmin_sub[k] < noise_slope_max * pmin_u[k]) + && (actmin_sub[k] > pmin_u[k])) + { + pmin_u[k] = actmin_sub[k]; + for (ku = 0; ku < U; ku++) + actminbuff[ku][k] = actmin_sub[k]; + } + + lmin_flag[k] = 0; + actmin[k] = std::numeric_limits::max();; + actmin_sub[k] = std::numeric_limits::max();; + } + + if (++amb_idx == U) + amb_idx = 0; + + subwc = 1; + } + else + { + if (subwc > 1) + { + for (k = 0; k < msize; k++) + { + if (k_mod[k]) + { + lmin_flag[k] = 1; + sigma2N[k] = std::min (actmin_sub[k], pmin_u[k]); + pmin_u[k] = sigma2N[k]; + } + } + } + + ++subwc; + } + + std::copy(sigma2N, sigma2N + msize, lambda_d); +} + +EMNR::G::G( + int _incr, + double _rate, + int _msize, + double* _mask, + float* _y +) +{ + incr = _incr; + rate = _rate; + msize = _msize; + mask = _mask; + y = _y; + + lambda_y = new double[msize]; // (float *)malloc0(msize * sizeof(float)); + lambda_d = new double[msize]; // (float *)malloc0(msize * sizeof(float)); + prev_gamma = new double[msize]; // (float *)malloc0(msize * sizeof(float)); + prev_mask = new double[msize]; // (float *)malloc0(msize * sizeof(float)); + + gf1p5 = sqrt(PI) / 2.0; + + { + double tau = -128.0 / 8000.0 / log(0.98); + alpha = exp(-incr / rate / tau); + } + + eps_floor = std::numeric_limits::min(); + gamma_max = 1000.0; + q = 0.2; + + std::fill(prev_mask, prev_mask + msize, 1.0); + std::fill(prev_gamma, prev_gamma + msize, 1.0); + + gmax = 10000.0; + // + GG = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); + GGS = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); + + if ((fileb = fopen("calculus", "rb"))) + { + std::size_t lgg = fread(GG, sizeof(float), 241 * 241, fileb); + if (lgg != 241 * 241) { + fprintf(stderr, "GG file has an invalid size\n"); + } + std::size_t lggs =fread(GGS, sizeof(float), 241 * 241, fileb); + if (lggs != 241 * 241) { + fprintf(stderr, "GGS file has an invalid size\n"); + } + fclose(fileb); + } + else + { + std::copy(Calculus::GG, Calculus::GG + (241 * 241), GG); + std::copy(Calculus::GGS, Calculus::GGS + (241 * 241), GGS); + } +} + +EMNR::G::~G() +{ + delete[] (GGS); + delete[] (GG); + delete[] (prev_mask); + delete[] (prev_gamma); + delete[] (lambda_d); + delete[] (lambda_y); +} + /******************************************************************************************************** * * * Special Functions * @@ -192,27 +676,27 @@ double EMNR::e1xb (double x) * * ********************************************************************************************************/ -void EMNR::calc_window (EMNR *a) +void EMNR::calc_window() { int i; double arg, sum, inv_coherent_gain; - switch (a->wintype) + switch (wintype) { case 0: - arg = 2.0 * PI / (double) a->fsize; + arg = 2.0 * PI / (double) fsize; sum = 0.0; - for (i = 0; i < a->fsize; i++) + for (i = 0; i < fsize; i++) { - a->window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg)); - sum += a->window[i]; + window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg)); + sum += window[i]; } - inv_coherent_gain = (double) a->fsize / sum; + inv_coherent_gain = (double) fsize / sum; - for (i = 0; i < a->fsize; i++) - a->window[i] *= inv_coherent_gain; + for (i = 0; i < fsize; i++) + window[i] *= inv_coherent_gain; break; } @@ -243,549 +727,185 @@ void EMNR::interpM (double* res, double x, int nvals, double* xvals, double* yva } } -void EMNR::calc_emnr(EMNR *a) +void EMNR::calc() { - double Dvals[18] = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0, - 60.0, 80.0, 120.0, 140.0, 160.0, 180.0, 220.0, 260.0, 300.0 }; - double Mvals[18] = { 0.000, 0.260, 0.480, 0.580, 0.610, 0.668, 0.705, 0.762, 0.800, - 0.841, 0.865, 0.890, 0.900, 0.910, 0.920, 0.930, 0.935, 0.940 }; // float Hvals[18] = { 0.000, 0.150, 0.480, 0.780, 0.980, 1.550, 2.000, 2.300, 2.520, // 3.100, 3.380, 4.150, 4.350, 4.250, 3.900, 4.100, 4.700, 5.000 }; - a->incr = a->fsize / a->ovrlp; - a->gain = a->ogain / a->fsize / (float)a->ovrlp; + incr = fsize / ovrlp; + gain = ogain / fsize / (float)ovrlp; - if (a->fsize > a->bsize) - a->iasize = a->fsize; + if (fsize > bsize) + iasize = fsize; else - a->iasize = a->bsize + a->fsize - a->incr; + iasize = bsize + fsize - incr; - a->iainidx = 0; - a->iaoutidx = 0; + iainidx = 0; + iaoutidx = 0; - if (a->fsize > a->bsize) + if (fsize > bsize) { - if (a->bsize > a->incr) - a->oasize = a->bsize; + if (bsize > incr) + oasize = bsize; else - a->oasize = a->incr; + oasize = incr; - a->oainidx = (a->fsize - a->bsize - a->incr) % a->oasize; + oainidx = (fsize - bsize - incr) % oasize; } else { - a->oasize = a->bsize; - a->oainidx = a->fsize - a->incr; + oasize = bsize; + oainidx = fsize - incr; } - a->init_oainidx = a->oainidx; - a->oaoutidx = 0; - a->msize = a->fsize / 2 + 1; - a->window = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); - a->inaccum = new float[a->iasize]; // (float *)malloc0(a->iasize * sizeof(float)); - a->forfftin = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); - a->forfftout = new float[a->msize * 2]; // (float *)malloc0(a->msize * sizeof(complex)); - a->mask = new double[a->msize]; // (float *)malloc0(a->msize * sizeof(float)); - std::fill(a->mask, a->mask + a->msize, 1.0); - a->revfftin = new float[a->msize * 2]; // (float *)malloc0(a->msize * sizeof(complex)); - a->revfftout = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); - a->save = new float*[a->ovrlp]; // (float **)malloc0(a->ovrlp * sizeof(float *)); + init_oainidx = oainidx; + oaoutidx = 0; + msize = fsize / 2 + 1; + window.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); + inaccum.resize(iasize); // (float *)malloc0(iasize * sizeof(float)); + forfftin.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); + forfftout.resize(msize * 2); // (float *)malloc0(msize * sizeof(complex)); + mask.resize(msize); // (float *)malloc0(msize * sizeof(float)); + std::fill(mask.begin(), mask.end(), 1.0); + revfftin.resize(msize * 2); // (float *)malloc0(msize * sizeof(complex)); + revfftout.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); + save.resize(ovrlp); // (float **)malloc0(ovrlp * sizeof(float *)); - for (int i = 0; i < a->ovrlp; i++) - a->save[i] = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); + for (int i = 0; i < ovrlp; i++) + save[i].resize(fsize); // (float *)malloc0(fsize * sizeof(float)); - a->outaccum = new float[a->oasize]; // (float *)malloc0(a->oasize * sizeof(float)); - a->nsamps = 0; - a->saveidx = 0; - a->Rfor = fftwf_plan_dft_r2c_1d( - a->fsize, - a->forfftin, - (fftwf_complex *)a->forfftout, + outaccum.resize(oasize); // (float *)malloc0(oasize * sizeof(float)); + nsamps = 0; + saveidx = 0; + Rfor = fftwf_plan_dft_r2c_1d( + fsize, + forfftin.data(), + (fftwf_complex *)forfftout.data(), FFTW_ESTIMATE ); - a->Rrev = fftwf_plan_dft_c2r_1d( - a->fsize, - (fftwf_complex *)a->revfftin, - a->revfftout, + Rrev = fftwf_plan_dft_c2r_1d( + fsize, + (fftwf_complex *)revfftin.data(), + revfftout.data(), FFTW_ESTIMATE ); - calc_window(a); + calc_window(); - a->g.msize = a->msize; - a->g.mask = a->mask; - a->g.y = a->forfftout; - a->g.lambda_y = new double[a->msize]; // (float *)malloc0(a->msize * sizeof(float)); - a->g.lambda_d = new double[a->msize]; // (float *)malloc0(a->msize * sizeof(float)); - a->g.prev_gamma = new double[a->msize]; // (float *)malloc0(a->msize * sizeof(float)); - a->g.prev_mask = new double[a->msize]; // (float *)malloc0(a->msize * sizeof(float)); + // G - a->g.gf1p5 = sqrt(PI) / 2.0; - { - double tau = -128.0 / 8000.0 / log(0.98); - a->g.alpha = exp(-a->incr / a->rate / tau); - } - a->g.eps_floor = std::numeric_limits::min(); - a->g.gamma_max = 1000.0; - a->g.q = 0.2; + g = new G( + incr, + rate, + msize, + mask.data(), + forfftout.data() + ); - std::fill(a->g.prev_mask, a->g.prev_mask + a->msize, 1.0); - std::fill(a->g.prev_gamma, a->g.prev_gamma + a->msize, 1.0); + // NP - a->g.gmax = 10000.0; - // - a->g.GG = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); - a->g.GGS = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); + np = new NP( + incr, + rate, + msize, + g->lambda_y, + g->lambda_d + ); - if ((a->g.fileb = fopen("calculus", "rb"))) - { - std::size_t lgg = fread(a->g.GG, sizeof(float), 241 * 241, a->g.fileb); - if (lgg != 241 * 241) { - fprintf(stderr, "GG file has an invalid size\n"); - } - std::size_t lggs =fread(a->g.GGS, sizeof(float), 241 * 241, a->g.fileb); - if (lggs != 241 * 241) { - fprintf(stderr, "GGS file has an invalid size\n"); - } - fclose(a->g.fileb); - } - else - { - std::copy(Calculus::GG, Calculus::GG + (241 * 241), a->g.GG); - std::copy(Calculus::GGS, Calculus::GGS + (241 * 241), a->g.GGS); - } - // + // NPS - a->np.incr = a->incr; - a->np.rate = a->rate; - a->np.msize = a->msize; - a->np.lambda_y = a->g.lambda_y; - a->np.lambda_d = a->g.lambda_d; + double tauNPS0 = -128.0 / 8000.0 / log(0.8); + double alpha_pow = exp(-incr / rate / tauNPS0); - { - double tau = -128.0 / 8000.0 / log(0.7); - a->np.alphaCsmooth = exp(-a->np.incr / a->np.rate / tau); - } - { - double tau = -128.0 / 8000.0 / log(0.96); - a->np.alphaMax = exp(-a->np.incr / a->np.rate / tau); - } - { - double tau = -128.0 / 8000.0 / log(0.7); - a->np.alphaCmin = exp(-a->np.incr / a->np.rate / tau); - } - { - double tau = -128.0 / 8000.0 / log(0.3); - a->np.alphaMin_max_value = exp(-a->np.incr / a->np.rate / tau); - } - a->np.snrq = -a->np.incr / (0.064 * a->np.rate); - { - double tau = -128.0 / 8000.0 / log(0.8); - a->np.betamax = exp(-a->np.incr / a->np.rate / tau); - } - a->np.invQeqMax = 0.5; - a->np.av = 2.12; - a->np.Dtime = 8.0 * 12.0 * 128.0 / 8000.0; - a->np.U = 8; - a->np.V = (int)(0.5 + (a->np.Dtime * a->np.rate / (a->np.U * a->np.incr))); + double tauNPS1 = -128.0 / 8000.0 / log(0.9); + double alpha_Pbar = exp(-incr / rate / tauNPS1); - if (a->np.V < 4) - a->np.V = 4; + nps = new NPS( + incr, + rate, + msize, + g->lambda_y, + g->lambda_d, + alpha_pow, + alpha_Pbar, + pow(10.0, 15.0 / 10.0) // epsH1 + ); - if ((a->np.U = (int)(0.5 + (a->np.Dtime * a->np.rate / (a->np.V * a->np.incr)))) < 1) - a->np.U = 1; + // AE - a->np.D = a->np.U * a->np.V; - interpM(&a->np.MofD, a->np.D, 18, Dvals, Mvals); - interpM(&a->np.MofV, a->np.V, 18, Dvals, Mvals); - a->np.invQbar_points[0] = 0.03; - a->np.invQbar_points[1] = 0.05; - a->np.invQbar_points[2] = 0.06; - a->np.invQbar_points[3] = std::numeric_limits::max();; - { - double db; - db = 10.0 * log10(8.0) / (12.0 * 128 / 8000); - a->np.nsmax[0] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate); - db = 10.0 * log10(4.0) / (12.0 * 128 / 8000); - a->np.nsmax[1] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate); - db = 10.0 * log10(2.0) / (12.0 * 128 / 8000); - a->np.nsmax[2] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate); - db = 10.0 * log10(1.2) / (12.0 * 128 / 8000); - a->np.nsmax[3] = pow(10.0, db / 10.0 * a->np.V * a->np.incr / a->np.rate); - } - - a->np.p = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.alphaOptHat = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.alphaHat = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.sigma2N = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.pbar = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.p2bar = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.Qeq = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.bmin = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.bmin_sub = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.k_mod = new int[a->np.msize]; // (int *)malloc0(a->np.msize * sizeof(int)); - a->np.actmin = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.actmin_sub = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.lmin_flag = new int[a->np.msize]; // (int *)malloc0(a->np.msize * sizeof(int)); - a->np.pmin_u = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - a->np.actminbuff = new double*[a->np.U]; // (float**)malloc0(a->np.U * sizeof(float*)); - - for (int i = 0; i < a->np.U; i++) - a->np.actminbuff[i] = new double[a->np.msize]; // (float *)malloc0(a->np.msize * sizeof(float)); - - { - int k, ku; - a->np.alphaC = 1.0; - a->np.subwc = a->np.V; - a->np.amb_idx = 0; - - for (k = 0; k < a->np.msize; k++) - a->np.lambda_y[k] = 0.5; - - std::copy(a->np.lambda_y, a->np.lambda_y + a->np.msize, a->np.p); - std::copy(a->np.lambda_y, a->np.lambda_y + a->np.msize, a->np.sigma2N); - std::copy(a->np.lambda_y, a->np.lambda_y + a->np.msize, a->np.pbar); - std::copy(a->np.lambda_y, a->np.lambda_y + a->np.msize, a->np.pmin_u); - - for (k = 0; k < a->np.msize; k++) - { - a->np.p2bar[k] = a->np.lambda_y[k] * a->np.lambda_y[k]; - a->np.actmin[k] = std::numeric_limits::max(); - a->np.actmin_sub[k] = std::numeric_limits::max();; - - for (ku = 0; ku < a->np.U; ku++) - a->np.actminbuff[ku][k] = std::numeric_limits::max();; - } - - std::fill(a->np.lmin_flag, a->np.lmin_flag + a->np.msize, 0); - } - - a->nps.incr = a->incr; - a->nps.rate = a->rate; - a->nps.msize = a->msize; - a->nps.lambda_y = a->g.lambda_y; - a->nps.lambda_d = a->g.lambda_d; - - { - double tau = -128.0 / 8000.0 / log(0.8); - a->nps.alpha_pow = exp(-a->nps.incr / a->nps.rate / tau); - } - { - double tau = -128.0 / 8000.0 / log(0.9); - a->nps.alpha_Pbar = exp(-a->nps.incr / a->nps.rate / tau); - } - - a->nps.epsH1 = pow(10.0, 15.0 / 10.0); - a->nps.epsH1r = a->nps.epsH1 / (1.0 + a->nps.epsH1); - - a->nps.sigma2N = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); - a->nps.PH1y = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); - a->nps.Pbar = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); - a->nps.EN2y = new double[a->nps.msize]; // (float *)malloc0(a->nps.msize * sizeof(float)); - - for (int i = 0; i < a->nps.msize; i++) - { - a->nps.sigma2N[i] = 0.5; - a->nps.Pbar[i] = 0.5; - } - - a->ae.msize = a->msize; - a->ae.lambda_y = a->g.lambda_y; - - a->ae.zetaThresh = 0.75; - a->ae.psi = 10.0; - - a->ae.nmask = new double[a->ae.msize]; // (float *)malloc0(a->ae.msize * sizeof(float)); + ae = new AE( + msize, + g->lambda_y, + 0.75, // zetaThresh + 10.0 // psi + ); } -void EMNR::decalc_emnr(EMNR *a) +void EMNR::decalc() { - int i; - delete[] (a->ae.nmask); + delete (ae); + delete (nps); + delete (np); + delete (g); - delete[] (a->nps.EN2y); - delete[] (a->nps.Pbar); - delete[] (a->nps.PH1y); - delete[] (a->nps.sigma2N); - - for (i = 0; i < a->np.U; i++) - delete[] (a->np.actminbuff[i]); - - delete[] (a->np.actminbuff); - delete[] (a->np.pmin_u); - delete[] (a->np.lmin_flag); - delete[] (a->np.actmin_sub); - delete[] (a->np.actmin); - delete[] (a->np.k_mod); - delete[] (a->np.bmin_sub); - delete[] (a->np.bmin); - delete[] (a->np.Qeq); - delete[] (a->np.p2bar); - delete[] (a->np.pbar); - delete[] (a->np.sigma2N); - delete[] (a->np.alphaHat); - delete[] (a->np.alphaOptHat); - delete[] (a->np.p); - - delete[] (a->g.GGS); - delete[] (a->g.GG); - delete[] (a->g.prev_mask); - delete[] (a->g.prev_gamma); - delete[] (a->g.lambda_d); - delete[] (a->g.lambda_y); - - fftwf_destroy_plan(a->Rrev); - fftwf_destroy_plan(a->Rfor); - - delete[] (a->outaccum); - - for (i = 0; i < a->ovrlp; i++) - delete[] (a->save[i]); - - delete[] (a->save); - delete[] (a->revfftout); - delete[] (a->revfftin); - delete[] (a->mask); - delete[] (a->forfftout); - delete[] (a->forfftin); - delete[] (a->inaccum); - delete[] (a->window); + fftwf_destroy_plan(Rrev); + fftwf_destroy_plan(Rfor); } -EMNR* EMNR::create_emnr ( - int run, - int position, - int size, - float* in, - float* out, - int fsize, - int ovrlp, - int rate, - int wintype, - double gain, - int gain_method, - int npe_method, - int ae_run +EMNR::EMNR( + int _run, + int _position, + int _size, + float* _in, + float* _out, + int _fsize, + int _ovrlp, + int _rate, + int _wintype, + double _gain, + int _gain_method, + int _npe_method, + int _ae_run ) { - EMNR *a = new EMNR; - - a->run = run; - a->position = position; - a->bsize = size; - a->in = in; - a->out = out; - a->fsize = fsize; - a->ovrlp = ovrlp; - a->rate = rate; - a->wintype = wintype; - a->ogain = gain; - a->g.gain_method = gain_method; - a->g.npe_method = npe_method; - a->g.ae_run = ae_run; - calc_emnr (a); - return a; + run = _run; + position = _position; + bsize = _size; + in = _in; + out = _out; + fsize = _fsize; + ovrlp = _ovrlp; + rate = _rate; + wintype = _wintype; + ogain = _gain; + calc(); + g->gain_method = _gain_method; + g->npe_method = _npe_method; + g->ae_run = _ae_run; } -void EMNR::flush_emnr (EMNR *a) +void EMNR::flush() { int i; - std::fill(a->inaccum, a->inaccum + a->iasize, 0); + std::fill(inaccum.begin(), inaccum.end(), 0); - for (i = 0; i < a->ovrlp; i++) - std::fill(a->save[i], a->save[i] + a->fsize, 0); + for (i = 0; i < ovrlp; i++) + std::fill(save[i].begin(), save[i].end(), 0); - std::fill(a->outaccum, a->outaccum + a->oasize, 0); - a->nsamps = 0; - a->iainidx = 0; - a->iaoutidx = 0; - a->oainidx = a->init_oainidx; - a->oaoutidx = 0; - a->saveidx = 0; + std::fill(outaccum.begin(), outaccum.end(), 0); + nsamps = 0; + iainidx = 0; + iaoutidx = 0; + oainidx = init_oainidx; + oaoutidx = 0; + saveidx = 0; } -void EMNR::destroy_emnr (EMNR *a) +EMNR::~EMNR() { - decalc_emnr (a); - delete[] (a); + decalc(); } -void EMNR::LambdaD(EMNR *a) -{ - int k; - double f0, f1, f2, f3; - double sum_prev_p; - double sum_lambda_y; - double alphaCtilda; - double sum_prev_sigma2N; - double alphaMin, SNR; - double beta, varHat, invQeq; - double invQbar; - double bc; - double QeqTilda, QeqTildaSub; - double noise_slope_max; - - sum_prev_p = 0.0; - sum_lambda_y = 0.0; - sum_prev_sigma2N = 0.0; - - for (k = 0; k < a->np.msize; k++) - { - sum_prev_p += a->np.p[k]; - sum_lambda_y += a->np.lambda_y[k]; - sum_prev_sigma2N += a->np.sigma2N[k]; - } - - for (k = 0; k < a->np.msize; k++) - { - f0 = a->np.p[k] / a->np.sigma2N[k] - 1.0; - a->np.alphaOptHat[k] = 1.0 / (1.0 + f0 * f0); - } - - SNR = sum_prev_p / sum_prev_sigma2N; - alphaMin = std::min (a->np.alphaMin_max_value, pow (SNR, a->np.snrq)); - - for (k = 0; k < a->np.msize; k++) - if (a->np.alphaOptHat[k] < alphaMin) a->np.alphaOptHat[k] = alphaMin; - - f1 = sum_prev_p / sum_lambda_y - 1.0; - alphaCtilda = 1.0 / (1.0 + f1 * f1); - a->np.alphaC = a->np.alphaCsmooth * a->np.alphaC + (1.0 - a->np.alphaCsmooth) * std::max (alphaCtilda, a->np.alphaCmin); - f2 = a->np.alphaMax * a->np.alphaC; - - for (k = 0; k < a->np.msize; k++) - a->np.alphaHat[k] = f2 * a->np.alphaOptHat[k]; - - for (k = 0; k < a->np.msize; k++) - a->np.p[k] = a->np.alphaHat[k] * a->np.p[k] + (1.0 - a->np.alphaHat[k]) * a->np.lambda_y[k]; - - invQbar = 0.0; - - for (k = 0; k < a->np.msize; k++) - { - beta = std::min (a->np.betamax, a->np.alphaHat[k] * a->np.alphaHat[k]); - a->np.pbar[k] = beta * a->np.pbar[k] + (1.0 - beta) * a->np.p[k]; - a->np.p2bar[k] = beta * a->np.p2bar[k] + (1.0 - beta) * a->np.p[k] * a->np.p[k]; - varHat = a->np.p2bar[k] - a->np.pbar[k] * a->np.pbar[k]; - invQeq = varHat / (2.0 * a->np.sigma2N[k] * a->np.sigma2N[k]); - if (invQeq > a->np.invQeqMax) invQeq = a->np.invQeqMax; - a->np.Qeq[k] = 1.0 / invQeq; - invQbar += invQeq; - } - invQbar /= (double) a->np.msize; - bc = 1.0 + a->np.av * sqrt (invQbar); - - for (k = 0; k < a->np.msize; k++) - { - QeqTilda = (a->np.Qeq[k] - 2.0 * a->np.MofD) / (1.0 - a->np.MofD); - QeqTildaSub = (a->np.Qeq[k] - 2.0 * a->np.MofV) / (1.0 - a->np.MofV); - a->np.bmin[k] = 1.0 + 2.0 * (a->np.D - 1.0) / QeqTilda; - a->np.bmin_sub[k] = 1.0 + 2.0 * (a->np.V - 1.0) / QeqTildaSub; - } - - std::fill(a->np.k_mod, a->np.k_mod + a->np.msize, 0); - - for (k = 0; k < a->np.msize; k++) - { - f3 = a->np.p[k] * a->np.bmin[k] * bc; - - if (f3 < a->np.actmin[k]) - { - a->np.actmin[k] = f3; - a->np.actmin_sub[k] = a->np.p[k] * a->np.bmin_sub[k] * bc; - a->np.k_mod[k] = 1; - } - } - - if (a->np.subwc == a->np.V) - { - if (invQbar < a->np.invQbar_points[0]) - noise_slope_max = a->np.nsmax[0]; - else if (invQbar < a->np.invQbar_points[1]) - noise_slope_max = a->np.nsmax[1]; - else if (invQbar < a->np.invQbar_points[2]) - noise_slope_max = a->np.nsmax[2]; - else - noise_slope_max = a->np.nsmax[3]; - - for (k = 0; k < a->np.msize; k++) - { - int ku; - double min; - - if (a->np.k_mod[k]) - a->np.lmin_flag[k] = 0; - - a->np.actminbuff[a->np.amb_idx][k] = a->np.actmin[k]; - min = std::numeric_limits::max();; - - for (ku = 0; ku < a->np.U; ku++) - { - if (a->np.actminbuff[ku][k] < min) - min = a->np.actminbuff[ku][k]; - } - - a->np.pmin_u[k] = min; - - if ((a->np.lmin_flag[k] == 1) - && (a->np.actmin_sub[k] < noise_slope_max * a->np.pmin_u[k]) - && (a->np.actmin_sub[k] > a->np.pmin_u[k])) - { - a->np.pmin_u[k] = a->np.actmin_sub[k]; - for (ku = 0; ku < a->np.U; ku++) - a->np.actminbuff[ku][k] = a->np.actmin_sub[k]; - } - - a->np.lmin_flag[k] = 0; - a->np.actmin[k] = std::numeric_limits::max();; - a->np.actmin_sub[k] = std::numeric_limits::max();; - } - - if (++a->np.amb_idx == a->np.U) - a->np.amb_idx = 0; - - a->np.subwc = 1; - } - else - { - if (a->np.subwc > 1) - { - for (k = 0; k < a->np.msize; k++) - { - if (a->np.k_mod[k]) - { - a->np.lmin_flag[k] = 1; - a->np.sigma2N[k] = std::min (a->np.actmin_sub[k], a->np.pmin_u[k]); - a->np.pmin_u[k] = a->np.sigma2N[k]; - } - } - } - - ++a->np.subwc; - } - - std::copy(a->np.sigma2N, a->np.sigma2N + a->np.msize, a->np.lambda_d); -} - -void EMNR::LambdaDs (EMNR *a) -{ - int k; - - for (k = 0; k < a->nps.msize; k++) - { - a->nps.PH1y[k] = 1.0 / (1.0 + (1.0 + a->nps.epsH1) * exp (- a->nps.epsH1r * a->nps.lambda_y[k] / a->nps.sigma2N[k])); - a->nps.Pbar[k] = a->nps.alpha_Pbar * a->nps.Pbar[k] + (1.0 - a->nps.alpha_Pbar) * a->nps.PH1y[k]; - - if (a->nps.Pbar[k] > 0.99) - a->nps.PH1y[k] = std::min (a->nps.PH1y[k], 0.99); - - a->nps.EN2y[k] = (1.0 - a->nps.PH1y[k]) * a->nps.lambda_y[k] + a->nps.PH1y[k] * a->nps.sigma2N[k]; - a->nps.sigma2N[k] = a->nps.alpha_pow * a->nps.sigma2N[k] + (1.0 - a->nps.alpha_pow) * a->nps.EN2y[k]; - } - - std::copy(a->nps.sigma2N, a->nps.sigma2N + a->nps.msize, a->nps.lambda_d); -} - -void EMNR::aepf(EMNR *a) +void EMNR::aepf() { int k, m; int N, n; @@ -793,15 +913,15 @@ void EMNR::aepf(EMNR *a) sumPre = 0.0; sumPost = 0.0; - for (k = 0; k < a->ae.msize; k++) + for (k = 0; k < ae->msize; k++) { - sumPre += a->ae.lambda_y[k]; - sumPost += a->mask[k] * a->mask[k] * a->ae.lambda_y[k]; + sumPre += ae->lambda_y[k]; + sumPost += mask[k] * mask[k] * ae->lambda_y[k]; } zeta = sumPost / sumPre; - if (zeta >= a->ae.zetaThresh) + if (zeta >= ae->zetaThresh) zetaT = 1.0; else zetaT = zeta; @@ -809,19 +929,19 @@ void EMNR::aepf(EMNR *a) if (zetaT == 1.0) N = 1; else - N = 1 + 2 * (int)(0.5 + a->ae.psi * (1.0 - zetaT / a->ae.zetaThresh)); + N = 1 + 2 * (int)(0.5 + ae->psi * (1.0 - zetaT / ae->zetaThresh)); n = N / 2; - for (k = n; k < (a->ae.msize - n); k++) + for (k = n; k < (ae->msize - n); k++) { - a->ae.nmask[k] = 0.0; + ae->nmask[k] = 0.0; for (m = k - n; m <= (k + n); m++) - a->ae.nmask[k] += a->mask[m]; - a->ae.nmask[k] /= (float)N; + ae->nmask[k] += mask[m]; + ae->nmask[k] /= (float)N; } - std::copy(a->ae.nmask, a->ae.nmask + (a->ae.msize - 2 * n), a->mask + n); + std::copy(ae->nmask, ae->nmask + (ae->msize - 2 * n), &mask[n]); } double EMNR::getKey(double* type, double gamma, double xi) @@ -873,57 +993,57 @@ double EMNR::getKey(double* type, double gamma, double xi) + dg * dx * type[241 * nxi2 + ngamma2]; } -void EMNR::calc_gain (EMNR *a) +void EMNR::calc_gain() { int k; - for (k = 0; k < a->msize; k++) + for (k = 0; k < msize; k++) { - double y0 = a->g.y[2 * k + 0]; - double y1 = a->g.y[2 * k + 1]; - a->g.lambda_y[k] = y0 * y0 + y1 * y1; + double y0 = g->y[2 * k + 0]; + double y1 = g->y[2 * k + 1]; + g->lambda_y[k] = y0 * y0 + y1 * y1; } - switch (a->g.npe_method) + switch (g->npe_method) { case 0: - LambdaD(a); + np->LambdaD(); break; case 1: - LambdaDs(a); + nps->LambdaDs(); break; } - switch (a->g.gain_method) + switch (g->gain_method) { case 0: { double gamma, eps_hat, v; - for (k = 0; k < a->msize; k++) + for (k = 0; k < msize; k++) { - gamma = std::min (a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max); - eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k] - + (1.0 - a->g.alpha) * std::max (gamma - 1.0f, a->g.eps_floor); + gamma = std::min (g->lambda_y[k] / g->lambda_d[k], g->gamma_max); + eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] + + (1.0 - g->alpha) * std::max (gamma - 1.0f, g->eps_floor); v = (eps_hat / (1.0 + eps_hat)) * gamma; - a->g.mask[k] = a->g.gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v) + g->mask[k] = g->gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v) * ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v)); { double v2 = std::min (v, 700.0); - double eta = a->g.mask[k] * a->g.mask[k] * a->g.lambda_y[k] / a->g.lambda_d[k]; - double eps = eta / (1.0 - a->g.q); - double witchHat = (1.0 - a->g.q) / a->g.q * exp (v2) / (1.0 + eps); - a->g.mask[k] *= witchHat / (1.0 + witchHat); + double eta = g->mask[k] * g->mask[k] * g->lambda_y[k] / g->lambda_d[k]; + double eps = eta / (1.0 - g->q); + double witchHat = (1.0 - g->q) / g->q * exp (v2) / (1.0 + eps); + g->mask[k] *= witchHat / (1.0 + witchHat); } - if (a->g.mask[k] > a->g.gmax) - a->g.mask[k] = a->g.gmax; + if (g->mask[k] > g->gmax) + g->mask[k] = g->gmax; - if (a->g.mask[k] != a->g.mask[k]) - a->g.mask[k] = 0.01; + if (g->mask[k] != g->mask[k]) + g->mask[k] = 0.01; - a->g.prev_gamma[k] = gamma; - a->g.prev_mask[k] = a->g.mask[k]; + g->prev_gamma[k] = gamma; + g->prev_mask[k] = g->mask[k]; } break; @@ -933,22 +1053,22 @@ void EMNR::calc_gain (EMNR *a) { double gamma, eps_hat, v, ehr; - for (k = 0; k < a->msize; k++) + for (k = 0; k < msize; k++) { - gamma = std::min (a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max); - eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k] - + (1.0 - a->g.alpha) * std::max (gamma - 1.0f, a->g.eps_floor); + gamma = std::min (g->lambda_y[k] / g->lambda_d[k], g->gamma_max); + eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] + + (1.0 - g->alpha) * std::max (gamma - 1.0f, g->eps_floor); ehr = eps_hat / (1.0 + eps_hat); v = ehr * gamma; - if ((a->g.mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > a->g.gmax) - a->g.mask[k] = a->g.gmax; + if ((g->mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > g->gmax) + g->mask[k] = g->gmax; - if (a->g.mask[k] != a->g.mask[k]) - a->g.mask[k] = 0.01; + if (g->mask[k] != g->mask[k]) + g->mask[k] = 0.01; - a->g.prev_gamma[k] = gamma; - a->g.prev_mask[k] = a->g.mask[k]; + g->prev_gamma[k] = gamma; + g->prev_mask[k] = g->mask[k]; } break; @@ -958,111 +1078,111 @@ void EMNR::calc_gain (EMNR *a) { double gamma, eps_hat, eps_p; - for (k = 0; k < a->msize; k++) + for (k = 0; k < msize; k++) { - gamma = std::min(a->g.lambda_y[k] / a->g.lambda_d[k], a->g.gamma_max); - eps_hat = a->g.alpha * a->g.prev_mask[k] * a->g.prev_mask[k] * a->g.prev_gamma[k] - + (1.0 - a->g.alpha) * std::max(gamma - 1.0f, a->g.eps_floor); - eps_p = eps_hat / (1.0 - a->g.q); - a->g.mask[k] = getKey(a->g.GG, gamma, eps_hat) * getKey(a->g.GGS, gamma, eps_p); - a->g.prev_gamma[k] = gamma; - a->g.prev_mask[k] = a->g.mask[k]; + gamma = std::min(g->lambda_y[k] / g->lambda_d[k], g->gamma_max); + eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] + + (1.0 - g->alpha) * std::max(gamma - 1.0f, g->eps_floor); + eps_p = eps_hat / (1.0 - g->q); + g->mask[k] = getKey(g->GG, gamma, eps_hat) * getKey(g->GGS, gamma, eps_p); + g->prev_gamma[k] = gamma; + g->prev_mask[k] = g->mask[k]; } break; } } - if (a->g.ae_run) - aepf(a); + if (g->ae_run) + aepf(); } -void EMNR::xemnr (EMNR *a, int pos) +void EMNR::execute(int _pos) { - if (a->run && pos == a->position) + if (run && _pos == position) { int i, j, k, sbuff, sbegin; double g1; - for (i = 0; i < 2 * a->bsize; i += 2) + for (i = 0; i < 2 * bsize; i += 2) { - a->inaccum[a->iainidx] = a->in[i]; - a->iainidx = (a->iainidx + 1) % a->iasize; + inaccum[iainidx] = in[i]; + iainidx = (iainidx + 1) % iasize; } - a->nsamps += a->bsize; + nsamps += bsize; - while (a->nsamps >= a->fsize) + while (nsamps >= fsize) { - for (i = 0, j = a->iaoutidx; i < a->fsize; i++, j = (j + 1) % a->iasize) - a->forfftin[i] = a->window[i] * a->inaccum[j]; + for (i = 0, j = iaoutidx; i < fsize; i++, j = (j + 1) % iasize) + forfftin[i] = window[i] * inaccum[j]; - a->iaoutidx = (a->iaoutidx + a->incr) % a->iasize; - a->nsamps -= a->incr; - fftwf_execute (a->Rfor); - calc_gain(a); + iaoutidx = (iaoutidx + incr) % iasize; + nsamps -= incr; + fftwf_execute (Rfor); + calc_gain(); - for (i = 0; i < a->msize; i++) + for (i = 0; i < msize; i++) { - g1 = a->gain * a->mask[i]; - a->revfftin[2 * i + 0] = g1 * a->forfftout[2 * i + 0]; - a->revfftin[2 * i + 1] = g1 * a->forfftout[2 * i + 1]; + g1 = gain * mask[i]; + revfftin[2 * i + 0] = g1 * forfftout[2 * i + 0]; + revfftin[2 * i + 1] = g1 * forfftout[2 * i + 1]; } - fftwf_execute (a->Rrev); + fftwf_execute (Rrev); - for (i = 0; i < a->fsize; i++) - a->save[a->saveidx][i] = a->window[i] * a->revfftout[i]; + for (i = 0; i < fsize; i++) + save[saveidx][i] = window[i] * revfftout[i]; - for (i = a->ovrlp; i > 0; i--) + for (i = ovrlp; i > 0; i--) { - sbuff = (a->saveidx + i) % a->ovrlp; - sbegin = a->incr * (a->ovrlp - i); + sbuff = (saveidx + i) % ovrlp; + sbegin = incr * (ovrlp - i); - for (j = sbegin, k = a->oainidx; j < a->incr + sbegin; j++, k = (k + 1) % a->oasize) + for (j = sbegin, k = oainidx; j < incr + sbegin; j++, k = (k + 1) % oasize) { - if ( i == a->ovrlp) - a->outaccum[k] = a->save[sbuff][j]; + if ( i == ovrlp) + outaccum[k] = save[sbuff][j]; else - a->outaccum[k] += a->save[sbuff][j]; + outaccum[k] += save[sbuff][j]; } } - a->saveidx = (a->saveidx + 1) % a->ovrlp; - a->oainidx = (a->oainidx + a->incr) % a->oasize; + saveidx = (saveidx + 1) % ovrlp; + oainidx = (oainidx + incr) % oasize; } - for (i = 0; i < a->bsize; i++) + for (i = 0; i < bsize; i++) { - a->out[2 * i + 0] = a->outaccum[a->oaoutidx]; - a->out[2 * i + 1] = 0.0; - a->oaoutidx = (a->oaoutidx + 1) % a->oasize; + out[2 * i + 0] = outaccum[oaoutidx]; + out[2 * i + 1] = 0.0; + oaoutidx = (oaoutidx + 1) % oasize; } } - else if (a->out != a->in) + else if (out != in) { - std::copy(a->in, a->in + a->bsize * 2, a->out); + std::copy(in, in + bsize * 2, out); } } -void EMNR::setBuffers_emnr (EMNR *a, float* in, float* out) +void EMNR::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void EMNR::setSamplerate_emnr (EMNR *a, int rate) +void EMNR::setSamplerate(int _rate) { - decalc_emnr (a); - a->rate = rate; - calc_emnr (a); + decalc(); + rate = _rate; + calc(); } -void EMNR::setSize_emnr (EMNR *a, int size) +void EMNR::setSize(int _size) { - decalc_emnr (a); - a->bsize = size; - calc_emnr (a); + decalc(); + bsize = _size; + calc(); } /******************************************************************************************************** @@ -1071,35 +1191,29 @@ void EMNR::setSize_emnr (EMNR *a, int size) * * ********************************************************************************************************/ -void EMNR::SetEMNRgainMethod (RXA& rxa, int method) +void EMNR::setGainMethod(int _method) { - rxa.emnr->g.gain_method = method; + g->gain_method = _method; } -void EMNR::SetEMNRnpeMethod (RXA& rxa, int method) +void EMNR::setNpeMethod(int _method) { - rxa.emnr->g.npe_method = method; + g->npe_method = _method; } -void EMNR::SetEMNRaeRun (RXA& rxa, int run) +void EMNR::setAeRun(int _run) { - rxa.emnr->g.ae_run = run; + g->ae_run = _run; } -void EMNR::SetEMNRPosition (RXA& rxa, int position) +void EMNR::setAeZetaThresh(double _zetathresh) { - rxa.emnr->position = position; - rxa.bp1->position = position; + ae->zetaThresh = _zetathresh; } -void EMNR::SetEMNRaeZetaThresh (RXA& rxa, double zetathresh) +void EMNR::setAePsi(double _psi) { - rxa.emnr->ae.zetaThresh = zetathresh; -} - -void EMNR::SetEMNRaePsi (RXA& rxa, double psi) -{ - rxa.emnr->ae.psi = psi; + ae->psi = _psi; } } // namespace WDSP diff --git a/wdsp/emnr.hpp b/wdsp/emnr.hpp index dadeb0176..fc4a6efb9 100644 --- a/wdsp/emnr.hpp +++ b/wdsp/emnr.hpp @@ -28,6 +28,9 @@ warren@wpratt.com #ifndef wdsp_emnr_h #define wdsp_emnr_h +#include +#include + #include "fftw3.h" #include "export.h" @@ -46,18 +49,18 @@ public: int fsize; int ovrlp; int incr; - float* window; + std::vector window; int iasize; - float* inaccum; - float* forfftin; - float* forfftout; + std::vector inaccum; + std::vector forfftin; + std::vector forfftout; int msize; - double* mask; - float* revfftin; - float* revfftout; - float** save; + std::vector mask; + std::vector revfftin; + std::vector revfftout; + std::vector> save; int oasize; - float* outaccum; + std::vector outaccum; double rate; int wintype; double ogain; @@ -71,14 +74,16 @@ public: int saveidx; fftwf_plan Rfor; fftwf_plan Rrev; - struct _g + struct G { + int incr; + double rate; + int msize; + double* mask; + float* y; int gain_method; int npe_method; int ae_run; - double msize; - double* mask; - float* y; double* lambda_y; double* lambda_d; double* prev_mask; @@ -93,36 +98,48 @@ public: double* GG; double* GGS; FILE* fileb; - } g; - struct _npest + G( + int incr, + double rate, + int msize, + double* mask, + float* y + ); + G(const G&) = delete; + G& operator=(const G& other) = delete; + ~G(); + } *g; + struct NP { int incr; double rate; int msize; double* lambda_y; double* lambda_d; - double* p; - double* alphaOptHat; - double alphaC; double alphaCsmooth; - double alphaCmin; - double* alphaHat; double alphaMax; - double* sigma2N; + double alphaCmin; double alphaMin_max_value; double snrq; double betamax; - double* pbar; - double* p2bar; double invQeqMax; double av; - double* Qeq; - int U; double Dtime; + int U; int V; int D; + double* p; + double* alphaOptHat; + double alphaC; + double* alphaHat; + double* sigma2N; + double* pbar; + double* p2bar; + double* Qeq; double MofD; double MofV; + std::array invQbar_points; + std::array nsmax; double* bmin; double* bmin_sub; int* k_mod; @@ -131,12 +148,32 @@ public: int subwc; int* lmin_flag; double* pmin_u; - double invQbar_points[4]; - double nsmax[4]; double** actminbuff; int amb_idx; - } np; - struct _npests + NP( + int incr, + double rate, + int msize, + double* lambda_y, + double* lambda_d + ); + NP(const NP&) = delete; + NP& operator=(const NP& other) = delete; + ~NP(); + void LambdaD(); + + private: + static const std::array DVals; + static const std::array MVals; + static void interpM ( + double* res, + double x, + int nvals, + const std::array& xvals, + const std::array& yvals + ); + } *np; + struct NPS { int incr; double rate; @@ -153,17 +190,41 @@ public: double* PH1y; double* Pbar; double* EN2y; - } nps; - struct _ae + NPS( + int incr, + double rate, + int msize, + double* lambda_y, + double* lambda_d, + + double alpha_pow, + double alpha_Pbar, + double epsH1 + ); + NPS(const NPS&) = delete; + NPS& operator=(const NPS& other) = delete; + ~NPS(); + void LambdaDs(); + } *nps; + struct AE { int msize; double* lambda_y; double zetaThresh; double psi; double* nmask; - } ae; + AE( + int msize, + double* lambda_y, + double zetaThresh, + double psi + ); + AE(const AE&) = delete; + AE& operator=(const AE& other) = delete; + ~AE(); + } *ae; - static EMNR* create_emnr ( + EMNR( int run, int position, int size, @@ -178,33 +239,33 @@ public: int npe_method, int ae_run ); - static void destroy_emnr (EMNR *a); - static void flush_emnr (EMNR *a); - static void xemnr (EMNR *a, int pos); - static void setBuffers_emnr (EMNR *a, float* in, float* out); - static void setSamplerate_emnr (EMNR *a, int rate); - static void setSize_emnr (EMNR *a, int size); - // RXA Properties - static void SetEMNRgainMethod (RXA& rxa, int method); - static void SetEMNRnpeMethod (RXA& rxa, int method); - static void SetEMNRaeRun (RXA& rxa, int run); - static void SetEMNRPosition (RXA& rxa, int position); - static void SetEMNRaeZetaThresh (RXA& rxa, double zetathresh); - static void SetEMNRaePsi (RXA& rxa, double psi); + EMNR(const EMNR&) = delete; + EMNR& operator=(const EMNR& other) = delete; + ~EMNR(); + + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setGainMethod(int method); + void setNpeMethod(int method); + void setAeRun(int run); + void setAeZetaThresh(double zetathresh); + void setAePsi(double psi); private: static double bessI0 (double x); static double bessI1 (double x); static double e1xb (double x); - static void calc_window (EMNR *a); static void interpM (double* res, double x, int nvals, double* xvals, double* yvals); - static void calc_emnr(EMNR *a); - static void decalc_emnr(EMNR *a); - static void LambdaD(EMNR *a); - static void LambdaDs (EMNR *a); - static void aepf(EMNR *a); static double getKey(double* type, double gamma, double xi); - static void calc_gain (EMNR *a); + void calc_window(); + void calc(); + void decalc(); + void aepf(); + void calc_gain(); }; } // namespace WDSP diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp index 086bd42ab..32423d0c7 100644 --- a/wdsp/eqp.hpp +++ b/wdsp/eqp.hpp @@ -76,6 +76,7 @@ public: int samplerate ); EQP(const EQP&) = delete; + EQP& operator=(const EQP& other) = delete; ~EQP(); void flush(); diff --git a/wdsp/fmd.hpp b/wdsp/fmd.hpp index f8e37be77..204bd5d64 100644 --- a/wdsp/fmd.hpp +++ b/wdsp/fmd.hpp @@ -111,6 +111,7 @@ public: int mp_aud ); FMD(const FMD&) = delete; + FMD& operator=(const FMD& other) = delete; ~FMD(); void flush(); diff --git a/wdsp/fmsq.hpp b/wdsp/fmsq.hpp index 02d2b13df..1d5b8f614 100644 --- a/wdsp/fmsq.hpp +++ b/wdsp/fmsq.hpp @@ -101,6 +101,7 @@ public: int _mp ); FMSQ(const FMSQ&) = delete; + FMSQ& operator=(const FMSQ& other) = delete; ~FMSQ(); void flush(); diff --git a/wdsp/nbp.hpp b/wdsp/nbp.hpp index 97b52c5b3..4bb04429f 100644 --- a/wdsp/nbp.hpp +++ b/wdsp/nbp.hpp @@ -52,6 +52,7 @@ public: NOTCHDB(int master_run, int maxnotches); NOTCHDB(const NOTCHDB&) = delete; + NOTCHDB& operator=(const NOTCHDB& other) = delete; ~NOTCHDB() = default; int addNotch (int notch, double fcenter, double fwidth, int active); @@ -108,6 +109,7 @@ public: NOTCHDB* notchdb ); NBP(const NBP&) = delete; + NBP& operator=(const NBP& other) = delete; ~NBP(); void flush(); diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index adfc525d9..6264d00be 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -99,6 +99,7 @@ public: double threshold ); NOB(const NOB&) = delete; + NOB& operator=(const NOB& other) = delete; ~NOB() = default; //////////// legacy interface - remove void flush(); diff --git a/wdsp/resample.hpp b/wdsp/resample.hpp index d442039fe..5b4a12357 100644 --- a/wdsp/resample.hpp +++ b/wdsp/resample.hpp @@ -76,6 +76,7 @@ public: double gain ); RESAMPLE(const RESAMPLE&) = delete; + RESAMPLE& operator=(const RESAMPLE& other) = delete; ~RESAMPLE() = default; void flush(); From 7cb15bbd95ab08f71efaac1ba296ead0a4152996 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 29 Jul 2024 20:12:44 +0200 Subject: [PATCH 21/46] WDSP: EMNR rework --- wdsp/calculus.cpp | 4 +- wdsp/calculus.hpp | 6 +- wdsp/emnr.cpp | 390 ++++++++++++++++++++-------------------------- wdsp/emnr.hpp | 115 ++++++++------ 4 files changed, 241 insertions(+), 274 deletions(-) diff --git a/wdsp/calculus.cpp b/wdsp/calculus.cpp index 1a08b51e8..2d6a4c5b6 100644 --- a/wdsp/calculus.cpp +++ b/wdsp/calculus.cpp @@ -29,7 +29,7 @@ warren@wpratt.com namespace WDSP { -const double Calculus::GG[241 * 241] = { +const std::array Calculus::GG = { 7.25654181154076983e-01, 7.05038822098223439e-01, 6.85008217584843870e-01, 6.65545775927326222e-01, 6.46635376294157682e-01, 6.28261355371665386e-01, 6.10408494407843394e-01, 5.93062006626410732e-01, 5.76207525000389742e-01, 5.59831090374464435e-01, 5.43919139925240769e-01, 5.28458495948192608e-01, @@ -14552,7 +14552,7 @@ const double Calculus::GG[241 * 241] = { 1.00000000000000000e+00, 1.00000000000000000e+00, 1.00000000000000000e+00, 1.00000000000000000e+00, 1.00000000000000000e+00 }; -const double Calculus::GGS[241 * 241] = { +const std::array Calculus::GGS = { 8.00014908335353492e-01, 8.00020707540703313e-01, 8.00026700706648830e-01, 8.00032894400760863e-01, 8.00039295417528384e-01, 8.00045910786425396e-01, 8.00052747780268358e-01, 8.00059813923879481e-01, 8.00067117003061101e-01, 8.00074665073896907e-01, 8.00082466472385456e-01, 8.00090529824419749e-01, diff --git a/wdsp/calculus.hpp b/wdsp/calculus.hpp index 93fe9306e..c5d3e3888 100644 --- a/wdsp/calculus.hpp +++ b/wdsp/calculus.hpp @@ -29,6 +29,8 @@ warren@wpratt.com #ifndef wdsp_calculus_h #define wdsp_calculus_h +#include + #include "export.h" namespace WDSP { @@ -36,8 +38,8 @@ namespace WDSP { class WDSP_API Calculus { public: - static const double GG[]; - static const double GGS[]; + static const std::array GG; + static const std::array GGS; }; } // namespace WDSP diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index 35909775b..11ecd1f55 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -40,7 +40,7 @@ namespace WDSP { EMNR::AE::AE( int _msize, - double* _lambda_y, + const std::vector& _lambda_y, double _zetaThresh, double _psi ) : @@ -49,20 +49,15 @@ EMNR::AE::AE( zetaThresh(_zetaThresh), psi(_psi) { - nmask = new double[msize]; -} - -EMNR::AE::~AE() -{ - delete[] nmask; + nmask.resize(msize); } EMNR::NPS::NPS( int _incr, double _rate, int _msize, - double* _lambda_y, - double* _lambda_d, + const std::vector& _lambda_y, + std::vector& _lambda_d, double _alpha_pow, double _alpha_Pbar, @@ -78,10 +73,10 @@ EMNR::NPS::NPS( epsH1(_epsH1) { epsH1r = epsH1 / (1.0 + epsH1); - sigma2N = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); - PH1y = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); - Pbar = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); - EN2y = new double[msize]; // (float *)malloc0(nps.msize * sizeof(float)); + sigma2N.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); + PH1y.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); + Pbar.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); + EN2y.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); for (int i = 0; i < msize; i++) { @@ -90,14 +85,6 @@ EMNR::NPS::NPS( } } -EMNR::NPS::~NPS() -{ - delete[] sigma2N; - delete[] PH1y; - delete[] Pbar; - delete[] EN2y; -} - void EMNR::NPS::LambdaDs() { int k; @@ -114,7 +101,7 @@ void EMNR::NPS::LambdaDs() sigma2N[k] = alpha_pow * sigma2N[k] + (1.0 - alpha_pow) * EN2y[k]; } - std::copy(sigma2N, sigma2N + msize, lambda_d); + std::copy(sigma2N.begin(), sigma2N.end(), lambda_d.begin()); } const std::array EMNR::NP::DVals = { 1.0, 2.0, 5.0, 8.0, 10.0, 15.0, 20.0, 30.0, 40.0, @@ -126,15 +113,15 @@ EMNR::NP::NP( int _incr, double _rate, int _msize, - double* _lambda_y, - double* _lambda_d -) + std::vector& _lambda_y, + std::vector& _lambda_d +) : + incr(_incr), + rate(_rate), + msize(_msize), + lambda_y(_lambda_y), + lambda_d(_lambda_d) { - incr = _incr; - rate = _rate; - msize = _msize; - lambda_y = _lambda_y; - lambda_d = _lambda_d; { double tau = -128.0 / 8000.0 / log(0.7); @@ -194,24 +181,24 @@ EMNR::NP::NP( nsmax[3] = pow(10.0, db / 10.0 * V * incr / rate); } - p = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - alphaOptHat = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - alphaHat = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - sigma2N = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - pbar = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - p2bar = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - Qeq = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - bmin = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - bmin_sub = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - k_mod = new int[msize]; // (int *)malloc0(np.msize * sizeof(int)); - actmin = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - actmin_sub = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - lmin_flag = new int[msize]; // (int *)malloc0(np.msize * sizeof(int)); - pmin_u = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); - actminbuff = new double*[U]; // (float**)malloc0(np.U * sizeof(float*)); + p.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + alphaOptHat.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + alphaHat.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + sigma2N.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + pbar.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + p2bar.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + Qeq.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + bmin.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + bmin_sub.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + k_mod.resize(msize); // (int *)malloc0(np.msize * sizeof(int)); + actmin.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + actmin_sub.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + lmin_flag.resize(msize); // (int *)malloc0(np.msize * sizeof(int)); + pmin_u.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + actminbuff.resize(U); // (float**)malloc0(np.U * sizeof(float*)); for (int i = 0; i < U; i++) { - actminbuff[i] = new double[msize]; // (float *)malloc0(np.msize * sizeof(float)); + actminbuff[i].resize(msize); // (float *)malloc0(np.msize * sizeof(float)); } { @@ -224,10 +211,10 @@ EMNR::NP::NP( lambda_y[k] = 0.5; } - std::copy(lambda_y, lambda_y + msize, p); - std::copy(lambda_y, lambda_y + msize, sigma2N); - std::copy(lambda_y, lambda_y + msize, pbar); - std::copy(lambda_y, lambda_y + msize, pmin_u); + std::copy(lambda_y.begin(), lambda_y.end(), p.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), sigma2N.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), pbar.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), pmin_u.begin()); for (k = 0; k < msize; k++) { @@ -240,32 +227,10 @@ EMNR::NP::NP( } } - std::fill(lmin_flag, lmin_flag + msize, 0); + std::fill(lmin_flag.begin(), lmin_flag.end(), 0); } } -EMNR::NP::~NP() -{ - for (int i = 0; i < U; i++) - delete[] (actminbuff[i]); - - delete[] (actminbuff); - delete[] (pmin_u); - delete[] (lmin_flag); - delete[] (actmin_sub); - delete[] (actmin); - delete[] (k_mod); - delete[] (bmin_sub); - delete[] (bmin); - delete[] (Qeq); - delete[] (p2bar); - delete[] (pbar); - delete[] (sigma2N); - delete[] (alphaHat); - delete[] (alphaOptHat); - delete[] (p); -} - void EMNR::NP::interpM ( double* res, double x, @@ -371,7 +336,7 @@ void EMNR::NP::LambdaD() bmin_sub[k] = 1.0 + 2.0 * (V - 1.0) / QeqTildaSub; } - std::fill(k_mod, k_mod + msize, 0); + std::fill(k_mod.begin(), k_mod.end(), 0); for (k = 0; k < msize; k++) { @@ -452,27 +417,27 @@ void EMNR::NP::LambdaD() ++subwc; } - std::copy(sigma2N, sigma2N + msize, lambda_d); + std::copy(sigma2N.begin(), sigma2N.end(), lambda_d.begin()); } EMNR::G::G( int _incr, double _rate, int _msize, - double* _mask, - float* _y -) + std::vector& _mask, + const std::vector& _y +) : + incr(_incr), + rate(_rate), + msize(_msize), + mask(_mask), + y(_y) { - incr = _incr; - rate = _rate; - msize = _msize; - mask = _mask; - y = _y; - lambda_y = new double[msize]; // (float *)malloc0(msize * sizeof(float)); - lambda_d = new double[msize]; // (float *)malloc0(msize * sizeof(float)); - prev_gamma = new double[msize]; // (float *)malloc0(msize * sizeof(float)); - prev_mask = new double[msize]; // (float *)malloc0(msize * sizeof(float)); + lambda_y.resize(msize); // (float *)malloc0(msize * sizeof(float)); + lambda_d.resize(msize); // (float *)malloc0(msize * sizeof(float)); + prev_gamma.resize(msize); // (float *)malloc0(msize * sizeof(float)); + prev_mask.resize(msize); // (float *)malloc0(msize * sizeof(float)); gf1p5 = sqrt(PI) / 2.0; @@ -485,41 +450,113 @@ EMNR::G::G( gamma_max = 1000.0; q = 0.2; - std::fill(prev_mask, prev_mask + msize, 1.0); - std::fill(prev_gamma, prev_gamma + msize, 1.0); + std::fill(prev_mask.begin(), prev_mask.end(), 1.0); + std::fill(prev_gamma.begin(), prev_gamma.end(), 1.0); gmax = 10000.0; - // - GG = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); - GGS = new double[241 * 241]; // (float *)malloc0(241 * 241 * sizeof(float)); + std::copy(Calculus::GG.begin(), Calculus::GG.end(), GG.begin()); + std::copy(Calculus::GGS.begin(), Calculus::GGS.end(), GGS.begin()); + + // We keep this pretty useless part just in case... if ((fileb = fopen("calculus", "rb"))) { - std::size_t lgg = fread(GG, sizeof(float), 241 * 241, fileb); + std::array gg; + std::size_t lgg = fread(&gg[0], sizeof(double), 241 * 241, fileb); if (lgg != 241 * 241) { fprintf(stderr, "GG file has an invalid size\n"); + } else { + std::copy(gg.begin(), gg.end(), GG.begin()); } - std::size_t lggs =fread(GGS, sizeof(float), 241 * 241, fileb); + std::array ggs; + std::size_t lggs =fread(&ggs[0], sizeof(double), 241 * 241, fileb); if (lggs != 241 * 241) { fprintf(stderr, "GGS file has an invalid size\n"); + } else { + std::copy(ggs.begin(), ggs.end(), GGS.begin()); } fclose(fileb); } - else +} + +void EMNR::G::calc_gamma0() +{ + double gamma, eps_hat, v; + + for (int k = 0; k < msize; k++) { - std::copy(Calculus::GG, Calculus::GG + (241 * 241), GG); - std::copy(Calculus::GGS, Calculus::GGS + (241 * 241), GGS); + gamma = std::min (lambda_y[k] / lambda_d[k], gamma_max); + eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k] + + (1.0 - alpha) * std::max (gamma - 1.0f, eps_floor); + v = (eps_hat / (1.0 + eps_hat)) * gamma; + mask[k] = gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v) + * ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v)); + { + double v2 = std::min (v, 700.0); + double eta = mask[k] * mask[k] * lambda_y[k] / lambda_d[k]; + double eps = eta / (1.0 - q); + double witchHat = (1.0 - q) / q * exp (v2) / (1.0 + eps); + mask[k] *= witchHat / (1.0 + witchHat); + } + + if (mask[k] > gmax) + mask[k] = gmax; + + if (mask[k] != mask[k]) + mask[k] = 0.01; + + prev_gamma[k] = gamma; + prev_mask[k] = mask[k]; } } -EMNR::G::~G() +void EMNR::G::calc_gamma1() { - delete[] (GGS); - delete[] (GG); - delete[] (prev_mask); - delete[] (prev_gamma); - delete[] (lambda_d); - delete[] (lambda_y); + double gamma, eps_hat, v, ehr; + + for (int k = 0; k < msize; k++) + { + gamma = std::min (lambda_y[k] / lambda_d[k], gamma_max); + eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k] + + (1.0 - alpha) * std::max (gamma - 1.0f, eps_floor); + ehr = eps_hat / (1.0 + eps_hat); + v = ehr * gamma; + + if ((mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > gmax) + mask[k] = gmax; + + if (mask[k] != mask[k]) + mask[k] = 0.01; + + prev_gamma[k] = gamma; + prev_mask[k] = mask[k]; + } +} + +void EMNR::G::calc_gamma2() +{ + double gamma, eps_hat, eps_p; + + for (int k = 0; k < msize; k++) + { + gamma = std::min(lambda_y[k] / lambda_d[k], gamma_max); + eps_hat = alpha * prev_mask[k] * prev_mask[k] * prev_gamma[k] + + (1.0 - alpha) * std::max(gamma - 1.0f, eps_floor); + eps_p = eps_hat / (1.0 - q); + mask[k] = getKey(GG, gamma, eps_hat) * getKey(GGS, gamma, eps_p); + prev_gamma[k] = gamma; + prev_mask[k] = mask[k]; + } +} + +void EMNR::G::calc_lambda_y() +{ + for (int k = 0; k < msize; k++) + { + double y0 = y[2 * k + 0]; + double y1 = y[2 * k + 1]; + lambda_y[k] = y0 * y0 + y1 * y1; + } } /******************************************************************************************************** @@ -534,7 +571,7 @@ EMNR::G::~G() // Shanjie Zhang and Jianming Jin, "Computation of Special Functions." New York, NY, John Wiley and Sons, // Inc., 1996. [Sample code given in FORTRAN] -double EMNR::bessI0 (double x) +double EMNR::G::bessI0 (double x) { double res, p; @@ -577,7 +614,7 @@ double EMNR::bessI0 (double x) return res; } -double EMNR::bessI1 (double x) +double EMNR::G::bessI1 (double x) { double res, p; @@ -629,7 +666,7 @@ double EMNR::bessI1 (double x) // Shanjie Zhang and Jianming Jin, "Computation of Special Functions." New York, NY, John Wiley and Sons, // Inc., 1996. [Sample code given in FORTRAN] -double EMNR::e1xb (double x) +double EMNR::G::e1xb (double x) { double e1, ga, r, t, t0; int k, m; @@ -702,31 +739,6 @@ void EMNR::calc_window() } } -void EMNR::interpM (double* res, double x, int nvals, double* xvals, double* yvals) -{ - if (x <= xvals[0]) - { - *res = yvals[0]; - } - else if (x >= xvals[nvals - 1]) - { - *res = yvals[nvals - 1]; - } - else - { - int idx = 1; - double xllow, xlhigh, frac; - - while ((x >= xvals[idx]) && (idx < nvals - 1)) - idx++; - - xllow = log10 (xvals[idx - 1]); - xlhigh = log10(xvals[idx]); - frac = (log10 (x) - xllow) / (xlhigh - xllow); - *res = yvals[idx - 1] + frac * (yvals[idx] - yvals[idx - 1]); - } -} - void EMNR::calc() { // float Hvals[18] = { 0.000, 0.150, 0.480, 0.780, 0.980, 1.550, 2.000, 2.300, 2.520, @@ -797,8 +809,8 @@ void EMNR::calc() incr, rate, msize, - mask.data(), - forfftout.data() + mask, + forfftout ); // NP @@ -941,10 +953,10 @@ void EMNR::aepf() ae->nmask[k] /= (float)N; } - std::copy(ae->nmask, ae->nmask + (ae->msize - 2 * n), &mask[n]); + std::copy(ae->nmask.begin(), ae->nmask.end() - 2*n, &mask[n]); } -double EMNR::getKey(double* type, double gamma, double xi) +double EMNR::G::getKey(const std::array& type, double gamma, double xi) { int ngamma1, ngamma2, nxi1 = 0, nxi2 = 0; double tg, tx, dg, dx; @@ -987,22 +999,26 @@ double EMNR::getKey(double* type, double gamma, double xi) dg = (tg - 0.25 * ngamma1) / 0.25; dx = (tx - 0.25 * nxi1) / 0.25; - return (1.0 - dg) * (1.0 - dx) * type[241 * nxi1 + ngamma1] - + (1.0 - dg) * dx * type[241 * nxi2 + ngamma1] - + dg * (1.0 - dx) * type[241 * nxi1 + ngamma2] - + dg * dx * type[241 * nxi2 + ngamma2]; + + std::array ix; + ix[0] = 241 * nxi1 + ngamma1; + ix[1] = 241 * nxi2 + ngamma1; + ix[2] = 241 * nxi1 + ngamma2; + ix[3] = 241 * nxi2 + ngamma2; + + for (auto& ixi : ix) { + ixi = ixi < 0 ? 0 : (ixi >= 241*241 ? 241*241 - 1 : ixi); + } + + return (1.0 - dg) * (1.0 - dx) * type[ix[0]] + + (1.0 - dg) * dx * type[ix[1]] + + dg * (1.0 - dx) * type[ix[2]] + + dg * dx * type[ix[3]]; } void EMNR::calc_gain() { - int k; - - for (k = 0; k < msize; k++) - { - double y0 = g->y[2 * k + 0]; - double y1 = g->y[2 * k + 1]; - g->lambda_y[k] = y0 * y0 + y1 * y1; - } + g->calc_lambda_y(); switch (g->npe_method) { @@ -1017,80 +1033,14 @@ void EMNR::calc_gain() switch (g->gain_method) { case 0: - { - double gamma, eps_hat, v; - - for (k = 0; k < msize; k++) - { - gamma = std::min (g->lambda_y[k] / g->lambda_d[k], g->gamma_max); - eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] - + (1.0 - g->alpha) * std::max (gamma - 1.0f, g->eps_floor); - v = (eps_hat / (1.0 + eps_hat)) * gamma; - g->mask[k] = g->gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v) - * ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v)); - { - double v2 = std::min (v, 700.0); - double eta = g->mask[k] * g->mask[k] * g->lambda_y[k] / g->lambda_d[k]; - double eps = eta / (1.0 - g->q); - double witchHat = (1.0 - g->q) / g->q * exp (v2) / (1.0 + eps); - g->mask[k] *= witchHat / (1.0 + witchHat); - } - - if (g->mask[k] > g->gmax) - g->mask[k] = g->gmax; - - if (g->mask[k] != g->mask[k]) - g->mask[k] = 0.01; - - g->prev_gamma[k] = gamma; - g->prev_mask[k] = g->mask[k]; - } - - break; - } - + g->calc_gamma0(); + break; case 1: - { - double gamma, eps_hat, v, ehr; - - for (k = 0; k < msize; k++) - { - gamma = std::min (g->lambda_y[k] / g->lambda_d[k], g->gamma_max); - eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] - + (1.0 - g->alpha) * std::max (gamma - 1.0f, g->eps_floor); - ehr = eps_hat / (1.0 + eps_hat); - v = ehr * gamma; - - if ((g->mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > g->gmax) - g->mask[k] = g->gmax; - - if (g->mask[k] != g->mask[k]) - g->mask[k] = 0.01; - - g->prev_gamma[k] = gamma; - g->prev_mask[k] = g->mask[k]; - } - - break; - } - + g->calc_gamma1(); + break; case 2: - { - double gamma, eps_hat, eps_p; - - for (k = 0; k < msize; k++) - { - gamma = std::min(g->lambda_y[k] / g->lambda_d[k], g->gamma_max); - eps_hat = g->alpha * g->prev_mask[k] * g->prev_mask[k] * g->prev_gamma[k] - + (1.0 - g->alpha) * std::max(gamma - 1.0f, g->eps_floor); - eps_p = eps_hat / (1.0 - g->q); - g->mask[k] = getKey(g->GG, gamma, eps_hat) * getKey(g->GGS, gamma, eps_p); - g->prev_gamma[k] = gamma; - g->prev_mask[k] = g->mask[k]; - } - - break; - } + g->calc_gamma2(); + break; } if (g->ae_run) diff --git a/wdsp/emnr.hpp b/wdsp/emnr.hpp index fc4a6efb9..58f99390e 100644 --- a/wdsp/emnr.hpp +++ b/wdsp/emnr.hpp @@ -74,20 +74,21 @@ public: int saveidx; fftwf_plan Rfor; fftwf_plan Rrev; + struct G { int incr; double rate; int msize; - double* mask; - float* y; + std::vector& mask; + const std::vector& y; int gain_method; int npe_method; int ae_run; - double* lambda_y; - double* lambda_d; - double* prev_mask; - double* prev_gamma; + std::vector lambda_y; + std::vector lambda_d; + std::vector prev_mask; + std::vector prev_gamma; double gf1p5; double alpha; double eps_floor; @@ -95,27 +96,40 @@ public: double q; double gmax; // - double* GG; - double* GGS; + std::array GG; + std::array GGS; FILE* fileb; + G( int incr, double rate, int msize, - double* mask, - float* y + std::vector& mask, + const std::vector& y ); G(const G&) = delete; G& operator=(const G& other) = delete; - ~G(); + ~G() = default; + + void calc_gamma0(); + void calc_gamma1(); + void calc_gamma2(); + void calc_lambda_y(); + + private: + static double getKey(const std::array& type, double gamma, double xi); + static double e1xb (double x); + static double bessI0 (double x); + static double bessI1 (double x); } *g; + struct NP { int incr; double rate; int msize; - double* lambda_y; - double* lambda_d; + std::vector& lambda_y; + std::vector& lambda_d; double alphaCsmooth; double alphaMax; double alphaCmin; @@ -128,38 +142,40 @@ public: int U; int V; int D; - double* p; - double* alphaOptHat; + std::vector p; + std::vector alphaOptHat; double alphaC; - double* alphaHat; - double* sigma2N; - double* pbar; - double* p2bar; - double* Qeq; + std::vector alphaHat; + std::vector sigma2N; + std::vector pbar; + std::vector p2bar; + std::vector Qeq; double MofD; double MofV; std::array invQbar_points; std::array nsmax; - double* bmin; - double* bmin_sub; - int* k_mod; - double* actmin; - double* actmin_sub; + std::vector bmin; + std::vector bmin_sub; + std::vector k_mod; + std::vector actmin; + std::vector actmin_sub; int subwc; - int* lmin_flag; - double* pmin_u; - double** actminbuff; + std::vector lmin_flag; + std::vector pmin_u; + std::vector> actminbuff; int amb_idx; + NP( int incr, double rate, int msize, - double* lambda_y, - double* lambda_d + std::vector& lambda_y, + std::vector& lambda_d ); NP(const NP&) = delete; NP& operator=(const NP& other) = delete; - ~NP(); + ~NP() = default; + void LambdaD(); private: @@ -173,55 +189,59 @@ public: const std::array& yvals ); } *np; + struct NPS { int incr; double rate; int msize; - double* lambda_y; - double* lambda_d; + const std::vector& lambda_y; + std::vector& lambda_d; double alpha_pow; double alpha_Pbar; double epsH1; double epsH1r; - double* sigma2N; - double* PH1y; - double* Pbar; - double* EN2y; + std::vector sigma2N; + std::vector PH1y; + std::vector Pbar; + std::vector EN2y; + NPS( int incr, double rate, int msize, - double* lambda_y, - double* lambda_d, - + const std::vector& lambda_y, + std::vector& lambda_d, double alpha_pow, double alpha_Pbar, double epsH1 ); NPS(const NPS&) = delete; NPS& operator=(const NPS& other) = delete; - ~NPS(); + ~NPS() = default; + void LambdaDs(); } *nps; + struct AE { int msize; - double* lambda_y; + const std::vector& lambda_y; double zetaThresh; double psi; - double* nmask; + std::vector nmask; + AE( int msize, - double* lambda_y, + const std::vector& lambda_y, double zetaThresh, double psi ); AE(const AE&) = delete; AE& operator=(const AE& other) = delete; - ~AE(); + ~AE() = default; } *ae; EMNR( @@ -256,11 +276,6 @@ public: void setAePsi(double psi); private: - static double bessI0 (double x); - static double bessI1 (double x); - static double e1xb (double x); - static void interpM (double* res, double x, int nvals, double* xvals, double* yvals); - static double getKey(double* type, double gamma, double xi); void calc_window(); void calc(); void decalc(); From bc06095a566b09f120d9e0d20da80907ce0f7636 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 29 Jul 2024 23:31:43 +0200 Subject: [PATCH 22/46] WDSP: WCPAGC rework --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 46 +- wdsp/RXA.cpp | 31 +- wdsp/RXA.hpp | 3 + wdsp/TXA.cpp | 28 +- wdsp/fmd.cpp | 14 +- wdsp/wcpAGC.cpp | 579 ++++++++++-------------- wdsp/wcpAGC.hpp | 77 ++-- 7 files changed, 346 insertions(+), 432 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index f72df0145..c6e7593e9 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -770,46 +770,46 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) || (m_settings.m_agcHangThreshold != settings.m_agcHangThreshold) || (m_settings.m_agcGain != settings.m_agcGain) || force) { - WDSP::WCPAGC::SetAGCSlope(*m_rxa, settings.m_agcSlope); // SetRXAAGCSlope(id, rx->agc_slope); - WDSP::WCPAGC::SetAGCTop(*m_rxa, (float) settings.m_agcGain); // SetRXAAGCTop(id, rx->agc_gain); + m_rxa->agc->setSlope(settings.m_agcSlope); // SetRXAAGCSlope(id, rx->agc_slope); + m_rxa->agc->setTop((float) settings.m_agcGain); // SetRXAAGCTop(id, rx->agc_gain); if (settings.m_agc) { switch (settings.m_agcMode) { case WDSPRxProfile::WDSPRxAGCMode::AGCLong: - WDSP::WCPAGC::SetAGCMode(*m_rxa, 1); - WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2); - WDSP::WCPAGC::SetAGCHang(*m_rxa, 2000); // SetRXAAGCHang(id, 2000); - WDSP::WCPAGC::SetAGCDecay(*m_rxa, 2000); // SetRXAAGCDecay(id, 2000); - WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); + m_rxa->agc->setMode(1); + m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); + m_rxa->agc->setHang(2000); // SetRXAAGCHang(id, 2000); + m_rxa->agc->setDecay(2000); // SetRXAAGCDecay(id, 2000); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); break; case WDSPRxProfile::WDSPRxAGCMode::AGCSlow: - WDSP::WCPAGC::SetAGCMode(*m_rxa, 2); - WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2); - WDSP::WCPAGC::SetAGCHang(*m_rxa, 1000); // SetRXAAGCHang(id, 1000); - WDSP::WCPAGC::SetAGCDecay(*m_rxa, 500); // SetRXAAGCDecay(id, 500); - WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); + m_rxa->agc->setMode(2); + m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); + m_rxa->agc->setHang(1000); // SetRXAAGCHang(id, 1000); + m_rxa->agc->setDecay(500); // SetRXAAGCDecay(id, 500); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); break; case WDSPRxProfile::WDSPRxAGCMode::AGCMedium: - WDSP::WCPAGC::SetAGCMode(*m_rxa, 3); - WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2); - WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0); - WDSP::WCPAGC::SetAGCDecay(*m_rxa, 250); // SetRXAAGCDecay(id, 250); - WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); + m_rxa->agc->setMode(3); + m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); + m_rxa->agc->setHang(0); // SetRXAAGCHang(id, 0); + m_rxa->agc->setDecay(250); // SetRXAAGCDecay(id, 250); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); break; case WDSPRxProfile::WDSPRxAGCMode::AGCFast: - WDSP::WCPAGC::SetAGCMode(*m_rxa, 4); - WDSP::WCPAGC::SetAGCAttack(*m_rxa, 2); // SetRXAAGCAttack(id, 2); - WDSP::WCPAGC::SetAGCHang(*m_rxa, 0); // SetRXAAGCHang(id, 0); - WDSP::WCPAGC::SetAGCDecay(*m_rxa, 50); // SetRXAAGCDecay(id, 50); - WDSP::WCPAGC::SetAGCHangThreshold(*m_rxa, settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); + m_rxa->agc->setMode(4); + m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); + m_rxa->agc->setHang(0); // SetRXAAGCHang(id, 0); + m_rxa->agc->setDecay(50); // SetRXAAGCDecay(id, 50); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); break; } } else { - WDSP::WCPAGC::SetAGCMode(*m_rxa, 0); + m_rxa->agc->setMode(0); } } diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index bae905898..bc681c05e 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -403,7 +403,7 @@ RXA* RXA::create_rxa ( 1); // ae_run // AGC - rxa->agc = WCPAGC::create_wcpagc ( + rxa->agc = new WCPAGC( 1, // run 3, // mode 1, // peakmode = envelope @@ -572,7 +572,7 @@ void RXA::destroy_rxa (RXA *rxa) SIPHON::destroy_siphon (rxa->sip1); BANDPASS::destroy_bandpass (rxa->bp1); delete (rxa->agcmeter); - WCPAGC::destroy_wcpagc (rxa->agc); + delete (rxa->agc); delete (rxa->emnr); delete (rxa->anr); delete (rxa->anf); @@ -621,7 +621,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->anf->flush(); rxa->anr->flush(); rxa->emnr->flush(); - WCPAGC::flush_wcpagc (rxa->agc); + rxa->agc->flush(); rxa->agcmeter->flush(); BANDPASS::flush_bandpass (rxa->bp1); SIPHON::flush_siphon (rxa->sip1); @@ -657,7 +657,7 @@ void RXA::xrxa (RXA *rxa) rxa->anr->ANR::execute(0); rxa->emnr->execute(0); BANDPASS::xbandpass (rxa->bp1, 0); - WCPAGC::xwcpagc (rxa->agc); + rxa->agc->execute(); rxa->anf->execute(1); rxa->anr->execute(1); rxa->emnr->execute(1); @@ -768,7 +768,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->anr->setSamplerate(rxa->dsp_rate); rxa->emnr->setSamplerate(rxa->dsp_rate); BANDPASS::setSamplerate_bandpass (rxa->bp1, rxa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (rxa->agc, rxa->dsp_rate); + rxa->agc->setSamplerate(rxa->dsp_rate); rxa->agcmeter->setSamplerate(rxa->dsp_rate); SIPHON::setSamplerate_siphon (rxa->sip1, rxa->dsp_rate); CBL::setSamplerate_cbl (rxa->cbl, rxa->dsp_rate); @@ -845,8 +845,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->emnr->setSize(rxa->dsp_size); BANDPASS::setBuffers_bandpass (rxa->bp1, rxa->midbuff, rxa->midbuff); BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); - WCPAGC::setBuffers_wcpagc (rxa->agc, rxa->midbuff, rxa->midbuff); - WCPAGC::setSize_wcpagc (rxa->agc, rxa->dsp_size); + rxa->agc->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->agc->setSize(rxa->dsp_size); rxa->agcmeter->setBuffers(rxa->midbuff); rxa->agcmeter->setSize(rxa->dsp_size); SIPHON::setBuffers_siphon (rxa->sip1, rxa->midbuff); @@ -1343,6 +1343,23 @@ void RXA::SetEMNRPosition (RXA& rxa, int position) rxa.bp1->position = position; } +void RXA::GetAGCThresh(RXA& rxa, double *thresh, double size, double rate) +//for line on bandscope. +{ + double noise_offset; + noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); + *thresh = 20.0 * log10( rxa.agc->min_volts ) - noise_offset; +} + +void RXA::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) +//for line on bandscope +{ + double noise_offset; + noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); + rxa.agc->max_gain = rxa.agc->out_target / (rxa.agc->var_gain * pow (10.0, (thresh + noise_offset) / 20.0)); + rxa.agc->loadWcpAGC(); +} + /******************************************************************************************************** * * * Collectives * diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index ac8e1b861..629a9374e 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -180,6 +180,9 @@ public: // EMNR static void SetEMNRRun (RXA& rxa, int run); static void SetEMNRPosition (RXA& rxa, int position); + // WCPAGC + static void SetAGCThresh(RXA& rxa, double thresh, double size, double rate); + static void GetAGCThresh(RXA& rxa, double *thresh, double size, double rate); // Collectives static void SetPassband (RXA& rxa, float f_low, float f_high); static void SetNC (RXA& rxa, int nc); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 61243c5e3..b9062f590 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -197,7 +197,7 @@ TXA* TXA::create_txa ( 300.0, // f_low 3000.0); // f_high - txa->leveler = WCPAGC::create_wcpagc ( + txa->leveler = new WCPAGC( 0, // run - OFF by default 5, // mode 0, // 0 for max(I,Q), 1 for envelope @@ -347,7 +347,7 @@ TXA* TXA::create_txa ( -1, // index for gain value 0); // pointer for gain computation - txa->alc = WCPAGC::create_wcpagc ( + txa->alc = new WCPAGC( 1, // run - always ON 5, // mode 1, // 0 for max(I,Q), 1 for envelope @@ -530,7 +530,7 @@ void TXA::destroy_txa (TXA *txa) delete (txa->gen1); FMMOD::destroy_fmmod (txa->fmmod); AMMOD::destroy_ammod (txa->ammod); - WCPAGC::destroy_wcpagc (txa->alc); + delete (txa->alc); delete (txa->compmeter); BANDPASS::destroy_bandpass (txa->bp2); OSCTRL::destroy_osctrl (txa->osctrl); @@ -540,7 +540,7 @@ void TXA::destroy_txa (TXA *txa) delete (txa->cfcmeter); CFCOMP::destroy_cfcomp (txa->cfcomp); delete (txa->lvlrmeter); - WCPAGC::destroy_wcpagc (txa->leveler); + delete (txa->leveler); EMPHP::destroy_emphp (txa->preemph); delete (txa->eqmeter); delete (txa->eqp); @@ -570,7 +570,7 @@ void TXA::flush_txa (TXA* txa) txa->eqp->flush(); txa->eqmeter->flush (); EMPHP::flush_emphp (txa->preemph); - WCPAGC::flush_wcpagc (txa->leveler); + txa->leveler->flush(); txa->lvlrmeter->flush (); CFCOMP::flush_cfcomp (txa->cfcomp); txa->cfcmeter->flush (); @@ -580,7 +580,7 @@ void TXA::flush_txa (TXA* txa) OSCTRL::flush_osctrl (txa->osctrl); BANDPASS::flush_bandpass (txa->bp2); txa->compmeter->flush (); - WCPAGC::flush_wcpagc (txa->alc); + txa->alc->flush (); AMMOD::flush_ammod (txa->ammod); FMMOD::flush_fmmod (txa->fmmod); txa->gen1->flush(); @@ -605,7 +605,7 @@ void xtxa (TXA* txa) txa->eqp->execute (); // pre-EQ txa->eqmeter->execute (); // EQ meter EMPHP::xemphp (txa->preemph, 0); // FM pre-emphasis (first option) - WCPAGC::xwcpagc (txa->leveler); // Leveler + txa->leveler->execute (); // Leveler txa->lvlrmeter->execute (); // Leveler Meter CFCOMP::xcfcomp (txa->cfcomp, 0); // Continuous Frequency Compressor with post-EQ txa->cfcmeter->execute (); // CFC+PostEQ Meter @@ -615,7 +615,7 @@ void xtxa (TXA* txa) OSCTRL::xosctrl (txa->osctrl); // CESSB Overshoot Control BANDPASS::xbandpass (txa->bp2, 0); // aux bandpass (runs if CESSB) txa->compmeter->execute (); // COMP meter - WCPAGC::xwcpagc (txa->alc); // ALC + txa->alc->execute (); // ALC AMMOD::xammod (txa->ammod); // AM Modulator EMPHP::xemphp (txa->preemph, 1); // FM pre-emphasis (second option) FMMOD::xfmmod (txa->fmmod); // FM Modulator @@ -702,7 +702,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->eqp->setSamplerate (txa->dsp_rate); txa->eqmeter->setSamplerate (txa->dsp_rate); EMPHP::setSamplerate_emphp (txa->preemph, txa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (txa->leveler, txa->dsp_rate); + txa->leveler->setSamplerate (txa->dsp_rate); txa->lvlrmeter->setSamplerate (txa->dsp_rate); CFCOMP::setSamplerate_cfcomp (txa->cfcomp, txa->dsp_rate); txa->cfcmeter->setSamplerate (txa->dsp_rate); @@ -712,7 +712,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) OSCTRL::setSamplerate_osctrl (txa->osctrl, txa->dsp_rate); BANDPASS::setSamplerate_bandpass (txa->bp2, txa->dsp_rate); txa->compmeter->setSamplerate (txa->dsp_rate); - WCPAGC::setSamplerate_wcpagc (txa->alc, txa->dsp_rate); + txa->alc->setSamplerate (txa->dsp_rate); AMMOD::setSamplerate_ammod (txa->ammod, txa->dsp_rate); FMMOD::setSamplerate_fmmod (txa->fmmod, txa->dsp_rate); txa->gen1->setSamplerate(txa->dsp_rate); @@ -770,8 +770,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) txa->eqmeter->setSize (txa->dsp_size); EMPHP::setBuffers_emphp (txa->preemph, txa->midbuff, txa->midbuff); EMPHP::setSize_emphp (txa->preemph, txa->dsp_size); - WCPAGC::setBuffers_wcpagc (txa->leveler, txa->midbuff, txa->midbuff); - WCPAGC::setSize_wcpagc (txa->leveler, txa->dsp_size); + txa->leveler->setBuffers(txa->midbuff, txa->midbuff); + txa->leveler->setSize(txa->dsp_size); txa->lvlrmeter->setBuffers(txa->midbuff); txa->lvlrmeter->setSize(txa->dsp_size); CFCOMP::setBuffers_cfcomp (txa->cfcomp, txa->midbuff, txa->midbuff); @@ -790,8 +790,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) BANDPASS::setSize_bandpass (txa->bp2, txa->dsp_size); txa->compmeter->setBuffers(txa->midbuff); txa->compmeter->setSize(txa->dsp_size); - WCPAGC::setBuffers_wcpagc (txa->alc, txa->midbuff, txa->midbuff); - WCPAGC::setSize_wcpagc (txa->alc, txa->dsp_size); + txa->alc->setBuffers(txa->midbuff, txa->midbuff); + txa->alc->setSize( txa->dsp_size); AMMOD::setBuffers_ammod (txa->ammod, txa->midbuff, txa->midbuff); AMMOD::setSize_ammod (txa->ammod, txa->dsp_size); FMMOD::setBuffers_fmmod (txa->fmmod, txa->midbuff, txa->midbuff); diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 27de2f753..bac132be5 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -55,7 +55,7 @@ void FMD::calc() // CTCSS Removal sntch = SNOTCH::create_snotch(1, size, out, out, (int)rate, ctcss_freq, 0.0002); // detector limiter - plim = WCPAGC::create_wcpagc ( + plim = new WCPAGC( 1, // run - always ON 5, // mode 1, // 0 for max(I,Q), 1 for envelope @@ -83,7 +83,7 @@ void FMD::calc() void FMD::decalc() { - WCPAGC::destroy_wcpagc(plim); + delete (plim); SNOTCH::destroy_snotch(sntch); } @@ -163,7 +163,7 @@ void FMD::flush() omega = 0.0; fmdc = 0.0; SNOTCH::flush_snotch (sntch); - WCPAGC::flush_wcpagc (plim); + plim->flush(); } void FMD::execute() @@ -205,7 +205,7 @@ void FMD::execute() { for (i = 0; i < 2 * size; i++) out[i] *= lim_pre_gain; - WCPAGC::xwcpagc (plim); + plim->execute(); } } else if (in != out) @@ -220,7 +220,7 @@ void FMD::setBuffers(float* _in, float* _out) calc(); FIRCORE::setBuffers_fircore (pde, audio.data(), out); FIRCORE::setBuffers_fircore (paud, out, out); - WCPAGC::setBuffers_wcpagc (plim, out, out); + plim->setBuffers(out, out); } void FMD::setSamplerate(int _rate) @@ -237,7 +237,7 @@ void FMD::setSamplerate(int _rate) impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); FIRCORE::setImpulse_fircore (paud, impulse, 1); delete[] (impulse); - WCPAGC::setSamplerate_wcpagc (plim, (int)rate); + plim->setSamplerate((int) rate); } void FMD::setSize(int _size) @@ -257,7 +257,7 @@ void FMD::setSize(int _size) impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); delete[] (impulse); - WCPAGC::setSize_wcpagc (plim, size); + plim->setSize(size); } /******************************************************************************************************** diff --git a/wdsp/wcpAGC.cpp b/wdsp/wcpAGC.cpp index 34a36b4e5..e1f9a8a42 100644 --- a/wdsp/wcpAGC.cpp +++ b/wdsp/wcpAGC.cpp @@ -39,218 +39,201 @@ Santa Cruz, CA 95060 namespace WDSP { -void WCPAGC::calc_wcpagc (WCPAGC *a) +void WCPAGC::calc() { //assign constants - a->ring_buffsize = RB_SIZE; //do one-time initialization - a->out_index = -1; - a->ring_max = 0.0; - a->volts = 0.0; - a->save_volts = 0.0; - a->fast_backaverage = 0.0; - a->hang_backaverage = 0.0; - a->hang_counter = 0; - a->decay_type = 0; - a->state = 0; - a->ring = new double[RB_SIZE * 2]; // (float *)malloc0(RB_SIZE * sizeof(complex)); - a->abs_ring = new double[RB_SIZE]; //(float *)malloc0(RB_SIZE * sizeof(float)); - loadWcpAGC(a); + out_index = -1; + ring_max = 0.0; + volts = 0.0; + save_volts = 0.0; + fast_backaverage = 0.0; + hang_backaverage = 0.0; + hang_counter = 0; + decay_type = 0; + state = 0; + loadWcpAGC(); } -void WCPAGC::decalc_wcpagc (WCPAGC *a) -{ - delete[] (a->abs_ring); - delete[] (a->ring); -} - -WCPAGC* WCPAGC::create_wcpagc ( - int run, - int mode, - int pmode, - float* in, - float* out, - int io_buffsize, - int sample_rate, - double tau_attack, - double tau_decay, - int n_tau, - double max_gain, - double var_gain, - double fixed_gain, - double max_input, - double out_targ, - double tau_fast_backaverage, - double tau_fast_decay, - double pop_ratio, - int hang_enable, - double tau_hang_backmult, - double hangtime, - double hang_thresh, - double tau_hang_decay -) -{ - WCPAGC *a = new WCPAGC; +WCPAGC::WCPAGC( + int _run, + int _mode, + int _pmode, + float* _in, + float* _out, + int _io_buffsize, + int _sample_rate, + double _tau_attack, + double _tau_decay, + int _n_tau, + double _max_gain, + double _var_gain, + double _fixed_gain, + double _max_input, + double _out_targ, + double _tau_fast_backaverage, + double _tau_fast_decay, + double _pop_ratio, + int _hang_enable, + double _tau_hang_backmult, + double _hangtime, + double _hang_thresh, + double _tau_hang_decay +) : //initialize per call parameters - a->run = run; - a->mode = mode; - a->pmode = pmode; - a->in = in; - a->out = out; - a->io_buffsize = io_buffsize; - a->sample_rate = (double) sample_rate; - a->tau_attack = tau_attack; - a->tau_decay = tau_decay; - a->n_tau = n_tau; - a->max_gain = max_gain; - a->var_gain = var_gain; - a->fixed_gain = fixed_gain; - a->max_input = max_input; - a->out_targ = out_targ; - a->tau_fast_backaverage = tau_fast_backaverage; - a->tau_fast_decay = tau_fast_decay; - a->pop_ratio = pop_ratio; - a->hang_enable = hang_enable; - a->tau_hang_backmult = tau_hang_backmult; - a->hangtime = hangtime; - a->hang_thresh = hang_thresh; - a->tau_hang_decay = tau_hang_decay; - calc_wcpagc (a); - return a; + run(_run), + mode(_mode), + pmode(_pmode), + in(_in), + out(_out), + io_buffsize(_io_buffsize), + sample_rate((double) _sample_rate), + tau_attack(_tau_attack), + tau_decay(_tau_decay), + n_tau(_n_tau), + max_gain(_max_gain), + var_gain(_var_gain), + fixed_gain(_fixed_gain), + max_input(_max_input), + out_targ(_out_targ), + tau_fast_backaverage(_tau_fast_backaverage), + tau_fast_decay(_tau_fast_decay), + pop_ratio(_pop_ratio), + hang_enable(_hang_enable), + tau_hang_backmult(_tau_hang_backmult), + hangtime(_hangtime), + hang_thresh(_hang_thresh), + tau_hang_decay(_tau_hang_decay) +{ + calc(); } -void WCPAGC::loadWcpAGC (WCPAGC *a) +void WCPAGC::loadWcpAGC() { double tmp; //calculate internal parameters - a->attack_buffsize = (int)ceil(a->sample_rate * a->n_tau * a->tau_attack); - a->in_index = a->attack_buffsize + a->out_index; - a->attack_mult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_attack)); - a->decay_mult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_decay)); - a->fast_decay_mult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_fast_decay)); - a->fast_backmult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_fast_backaverage)); - a->onemfast_backmult = 1.0 - a->fast_backmult; + attack_buffsize = (int)ceil(sample_rate * n_tau * tau_attack); + in_index = attack_buffsize + out_index; + attack_mult = 1.0 - exp(-1.0 / (sample_rate * tau_attack)); + decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_decay)); + fast_decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_fast_decay)); + fast_backmult = 1.0 - exp(-1.0 / (sample_rate * tau_fast_backaverage)); + onemfast_backmult = 1.0 - fast_backmult; - a->out_target = a->out_targ * (1.0 - exp(-(double)a->n_tau)) * 0.9999; - a->min_volts = a->out_target / (a->var_gain * a->max_gain); - a->inv_out_target = 1.0 / a->out_target; + out_target = out_targ * (1.0 - exp(-(double)n_tau)) * 0.9999; + min_volts = out_target / (var_gain * max_gain); + inv_out_target = 1.0 / out_target; - tmp = log10(a->out_target / (a->max_input * a->var_gain * a->max_gain)); + tmp = log10(out_target / (max_input * var_gain * max_gain)); if (tmp == 0.0) tmp = 1e-16; - a->slope_constant = (a->out_target * (1.0 - 1.0 / a->var_gain)) / tmp; - a->inv_max_input = 1.0 / a->max_input; - tmp = pow (10.0, (a->hang_thresh - 1.0) / 0.125); - a->hang_level = (a->max_input * tmp + (a->out_target / - (a->var_gain * a->max_gain)) * (1.0 - tmp)) * 0.637; - a->hang_backmult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_hang_backmult)); - a->onemhang_backmult = 1.0 - a->hang_backmult; - a->hang_decay_mult = 1.0 - exp(-1.0 / (a->sample_rate * a->tau_hang_decay)); + slope_constant = (out_target * (1.0 - 1.0 / var_gain)) / tmp; + inv_max_input = 1.0 / max_input; + tmp = pow (10.0, (hang_thresh - 1.0) / 0.125); + hang_level = (max_input * tmp + (out_target / + (var_gain * max_gain)) * (1.0 - tmp)) * 0.637; + hang_backmult = 1.0 - exp(-1.0 / (sample_rate * tau_hang_backmult)); + onemhang_backmult = 1.0 - hang_backmult; + hang_decay_mult = 1.0 - exp(-1.0 / (sample_rate * tau_hang_decay)); } -void WCPAGC::destroy_wcpagc (WCPAGC *a) +void WCPAGC::flush() { - decalc_wcpagc (a); - delete (a); + std::fill(ring.begin(), ring.end(), 0); + std::fill(abs_ring.begin(), abs_ring.end(), 0); + ring_max = 0.0; } -void WCPAGC::flush_wcpagc (WCPAGC *a) -{ - memset ((void *)a->ring, 0, sizeof(double) * RB_SIZE * 2); - a->ring_max = 0.0; - memset ((void *)a->abs_ring, 0, sizeof(double)* RB_SIZE); -} - -void WCPAGC::xwcpagc (WCPAGC *a) +void WCPAGC::execute() { int i, j, k; double mult; - if (a->run) + if (run) { - if (a->mode == 0) + if (mode == 0) { - for (i = 0; i < a->io_buffsize; i++) + for (i = 0; i < io_buffsize; i++) { - a->out[2 * i + 0] = a->fixed_gain * a->in[2 * i + 0]; - a->out[2 * i + 1] = a->fixed_gain * a->in[2 * i + 1]; + out[2 * i + 0] = fixed_gain * in[2 * i + 0]; + out[2 * i + 1] = fixed_gain * in[2 * i + 1]; } return; } - for (i = 0; i < a->io_buffsize; i++) + for (i = 0; i < io_buffsize; i++) { - if (++a->out_index >= a->ring_buffsize) - a->out_index -= a->ring_buffsize; + if (++out_index >= ring_buffsize) + out_index -= ring_buffsize; - if (++a->in_index >= a->ring_buffsize) - a->in_index -= a->ring_buffsize; + if (++in_index >= ring_buffsize) + in_index -= ring_buffsize; - a->out_sample[0] = a->ring[2 * a->out_index + 0]; - a->out_sample[1] = a->ring[2 * a->out_index + 1]; - a->abs_out_sample = a->abs_ring[a->out_index]; - double xr = a->ring[2 * a->in_index + 0] = a->in[2 * i + 0]; - double xi = a->ring[2 * a->in_index + 1] = a->in[2 * i + 1]; + out_sample[0] = ring[2 * out_index + 0]; + out_sample[1] = ring[2 * out_index + 1]; + abs_out_sample = abs_ring[out_index]; + double xr = ring[2 * in_index + 0] = in[2 * i + 0]; + double xi = ring[2 * in_index + 1] = in[2 * i + 1]; - if (a->pmode == 0) - a->abs_ring[a->in_index] = std::max(fabs(xr), fabs(xi)); + if (pmode == 0) + abs_ring[in_index] = std::max(fabs(xr), fabs(xi)); else - a->abs_ring[a->in_index] = sqrt(xr*xr + xi*xi); + abs_ring[in_index] = sqrt(xr*xr + xi*xi); - a->fast_backaverage = a->fast_backmult * a->abs_out_sample + a->onemfast_backmult * a->fast_backaverage; - a->hang_backaverage = a->hang_backmult * a->abs_out_sample + a->onemhang_backmult * a->hang_backaverage; + fast_backaverage = fast_backmult * abs_out_sample + onemfast_backmult * fast_backaverage; + hang_backaverage = hang_backmult * abs_out_sample + onemhang_backmult * hang_backaverage; - if ((a->abs_out_sample >= a->ring_max) && (a->abs_out_sample > 0.0)) + if ((abs_out_sample >= ring_max) && (abs_out_sample > 0.0)) { - a->ring_max = 0.0; - k = a->out_index; + ring_max = 0.0; + k = out_index; - for (j = 0; j < a->attack_buffsize; j++) + for (j = 0; j < attack_buffsize; j++) { - if (++k == a->ring_buffsize) + if (++k == ring_buffsize) k = 0; - if (a->abs_ring[k] > a->ring_max) - a->ring_max = a->abs_ring[k]; + if (abs_ring[k] > ring_max) + ring_max = abs_ring[k]; } } - if (a->abs_ring[a->in_index] > a->ring_max) - a->ring_max = a->abs_ring[a->in_index]; + if (abs_ring[in_index] > ring_max) + ring_max = abs_ring[in_index]; - if (a->hang_counter > 0) - --a->hang_counter; + if (hang_counter > 0) + --hang_counter; - switch (a->state) + switch (state) { case 0: { - if (a->ring_max >= a->volts) + if (ring_max >= volts) { - a->volts += (a->ring_max - a->volts) * a->attack_mult; + volts += (ring_max - volts) * attack_mult; } else { - if (a->volts > a->pop_ratio * a->fast_backaverage) + if (volts > pop_ratio * fast_backaverage) { - a->state = 1; - a->volts += (a->ring_max - a->volts) * a->fast_decay_mult; + state = 1; + volts += (ring_max - volts) * fast_decay_mult; } else { - if (a->hang_enable && (a->hang_backaverage > a->hang_level)) + if (hang_enable && (hang_backaverage > hang_level)) { - a->state = 2; - a->hang_counter = (int)(a->hangtime * a->sample_rate); - a->decay_type = 1; + state = 2; + hang_counter = (int)(hangtime * sample_rate); + decay_type = 1; } else { - a->state = 3; - a->volts += (a->ring_max - a->volts) * a->decay_mult; - a->decay_type = 0; + state = 3; + volts += (ring_max - volts) * decay_mult; + decay_type = 0; } } } @@ -259,34 +242,34 @@ void WCPAGC::xwcpagc (WCPAGC *a) case 1: { - if (a->ring_max >= a->volts) + if (ring_max >= volts) { - a->state = 0; - a->volts += (a->ring_max - a->volts) * a->attack_mult; + state = 0; + volts += (ring_max - volts) * attack_mult; } else { - if (a->volts > a->save_volts) + if (volts > save_volts) { - a->volts += (a->ring_max - a->volts) * a->fast_decay_mult; + volts += (ring_max - volts) * fast_decay_mult; } else { - if (a->hang_counter > 0) + if (hang_counter > 0) { - a->state = 2; + state = 2; } else { - if (a->decay_type == 0) + if (decay_type == 0) { - a->state = 3; - a->volts += (a->ring_max - a->volts) * a->decay_mult; + state = 3; + volts += (ring_max - volts) * decay_mult; } else { - a->state = 4; - a->volts += (a->ring_max - a->volts) * a->hang_decay_mult; + state = 4; + volts += (ring_max - volts) * hang_decay_mult; } } } @@ -296,18 +279,18 @@ void WCPAGC::xwcpagc (WCPAGC *a) case 2: { - if (a->ring_max >= a->volts) + if (ring_max >= volts) { - a->state = 0; - a->save_volts = a->volts; - a->volts += (a->ring_max - a->volts) * a->attack_mult; + state = 0; + save_volts = volts; + volts += (ring_max - volts) * attack_mult; } else { - if (a->hang_counter == 0) + if (hang_counter == 0) { - a->state = 4; - a->volts += (a->ring_max - a->volts) * a->hang_decay_mult; + state = 4; + volts += (ring_max - volts) * hang_decay_mult; } } break; @@ -315,280 +298,202 @@ void WCPAGC::xwcpagc (WCPAGC *a) case 3: { - if (a->ring_max >= a->volts) + if (ring_max >= volts) { - a->state = 0; - a->save_volts = a->volts; - a->volts += (a->ring_max - a->volts) * a->attack_mult; + state = 0; + save_volts = volts; + volts += (ring_max - volts) * attack_mult; } else { - a->volts += (a->ring_max - a->volts) * a->decay_mult; + volts += (ring_max - volts) * decay_mult; } break; } case 4: { - if (a->ring_max >= a->volts) + if (ring_max >= volts) { - a->state = 0; - a->save_volts = a->volts; - a->volts += (a->ring_max - a->volts) * a->attack_mult; + state = 0; + save_volts = volts; + volts += (ring_max - volts) * attack_mult; } else { - a->volts += (a->ring_max - a->volts) * a->hang_decay_mult; + volts += (ring_max - volts) * hang_decay_mult; } break; } } - if (a->volts < a->min_volts) - a->volts = a->min_volts; + if (volts < min_volts) + volts = min_volts; - a->gain = a->volts * a->inv_out_target; - mult = (a->out_target - a->slope_constant * std::min (0.0, log10(a->inv_max_input * a->volts))) / a->volts; - a->out[2 * i + 0] = a->out_sample[0] * mult; - a->out[2 * i + 1] = a->out_sample[1] * mult; + gain = volts * inv_out_target; + mult = (out_target - slope_constant * std::min (0.0, log10(inv_max_input * volts))) / volts; + out[2 * i + 0] = out_sample[0] * mult; + out[2 * i + 1] = out_sample[1] * mult; } } - else if (a->out != a->in) + else if (out != in) { - std::copy(a->in, a->in + a->io_buffsize * 2, a->out); + std::copy(in, in + io_buffsize * 2, out); } } -void WCPAGC::setBuffers_wcpagc (WCPAGC *a, float* in, float* out) +void WCPAGC::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void WCPAGC::setSamplerate_wcpagc (WCPAGC *a, int rate) +void WCPAGC::setSamplerate(int _rate) { - decalc_wcpagc (a); - a->sample_rate = rate; - calc_wcpagc (a); + sample_rate = _rate; + calc(); } -void WCPAGC::setSize_wcpagc (WCPAGC *a, int size) +void WCPAGC::setSize(int _size) { - decalc_wcpagc (a); - a->io_buffsize = size; - calc_wcpagc (a); + io_buffsize = _size; + calc(); } /******************************************************************************************************** * * -* RXA Properties * +* Public Properties * * * ********************************************************************************************************/ -void WCPAGC::SetAGCMode (RXA& rxa, int mode) +void WCPAGC::setMode(int _mode) { - switch (mode) + switch (_mode) { case 0: //agcOFF - rxa.agc->mode = 0; - loadWcpAGC ( rxa.agc ); + mode = 0; + loadWcpAGC(); break; case 1: //agcLONG - rxa.agc->mode = 1; - rxa.agc->hangtime = 2.000; - rxa.agc->tau_decay = 2.000; - loadWcpAGC ( rxa.agc ); + mode = 1; + hangtime = 2.000; + tau_decay = 2.000; + loadWcpAGC(); break; case 2: //agcSLOW - rxa.agc->mode = 2; - rxa.agc->hangtime = 1.000; - rxa.agc->tau_decay = 0.500; - loadWcpAGC ( rxa.agc ); + mode = 2; + hangtime = 1.000; + tau_decay = 0.500; + loadWcpAGC(); break; case 3: //agcMED - rxa.agc->mode = 3; - rxa.agc->hang_thresh = 1.0; - rxa.agc->hangtime = 0.000; - rxa.agc->tau_decay = 0.250; - loadWcpAGC ( rxa.agc ); + mode = 3; + hang_thresh = 1.0; + hangtime = 0.000; + tau_decay = 0.250; + loadWcpAGC(); break; case 4: //agcFAST - rxa.agc->mode = 4; - rxa.agc->hang_thresh = 1.0; - rxa.agc->hangtime = 0.000; - rxa.agc->tau_decay = 0.050; - loadWcpAGC ( rxa.agc ); + mode = 4; + hang_thresh = 1.0; + hangtime = 0.000; + tau_decay = 0.050; + loadWcpAGC(); break; default: - rxa.agc->mode = 5; + mode = 5; break; } } -void WCPAGC::SetAGCAttack (RXA& rxa, int attack) +void WCPAGC::setFixed(double _fixed_agc) { - rxa.agc->tau_attack = (float)attack / 1000.0; - loadWcpAGC ( rxa.agc ); + fixed_gain = pow (10.0, (double) _fixed_agc / 20.0); + loadWcpAGC(); } -void WCPAGC::SetAGCDecay (RXA& rxa, int decay) +void WCPAGC::setAttack(int _attack) { - rxa.agc->tau_decay = (float)decay / 1000.0; - loadWcpAGC ( rxa.agc ); + tau_attack = (double) _attack / 1000.0; + loadWcpAGC(); } -void WCPAGC::SetAGCHang (RXA& rxa, int hang) +void WCPAGC::setDecay(int _decay) { - rxa.agc->hangtime = (float)hang / 1000.0; - loadWcpAGC ( rxa.agc ); + tau_decay = (double) _decay / 1000.0; + loadWcpAGC(); } -void WCPAGC::GetAGCHangLevel(RXA& rxa, double *hangLevel) +void WCPAGC::setHang(int _hang) +{ + hangtime = (double) _hang / 1000.0; + loadWcpAGC(); +} + +void WCPAGC::getHangLevel(double *hangLevel) //for line on bandscope { - *hangLevel = 20.0 * log10( rxa.agc->hang_level / 0.637 ); + *hangLevel = 20.0 * log10(hang_level / 0.637); } -void WCPAGC::SetAGCHangLevel(RXA& rxa, double hangLevel) +void WCPAGC::setHangLevel(double _hangLevel) //for line on bandscope { double convert, tmp; - if (rxa.agc->max_input > rxa.agc->min_volts) + if (max_input > min_volts) { - convert = pow (10.0, hangLevel / 20.0); - tmp = std::max(1e-8, (convert - rxa.agc->min_volts) / (rxa.agc->max_input - rxa.agc->min_volts)); - rxa.agc->hang_thresh = 1.0 + 0.125 * log10 (tmp); + convert = pow (10.0, _hangLevel / 20.0); + tmp = std::max(1e-8, (convert - min_volts) / (max_input - min_volts)); + hang_thresh = 1.0 + 0.125 * log10 (tmp); } else - rxa.agc->hang_thresh = 1.0; + hang_thresh = 1.0; - loadWcpAGC ( rxa.agc ); + loadWcpAGC(); } -void WCPAGC::GetAGCHangThreshold(RXA& rxa, int *hangthreshold) +void WCPAGC::getHangThreshold(int *hangthreshold) //for slider in setup { - *hangthreshold = (int) (100.0 * rxa.agc->hang_thresh); + *hangthreshold = (int) (100.0 * hang_thresh); } -void WCPAGC::SetAGCHangThreshold (RXA& rxa, int hangthreshold) +void WCPAGC::setHangThreshold(int _hangthreshold) //For slider in setup { - rxa.agc->hang_thresh = (double) hangthreshold / 100.0; - loadWcpAGC ( rxa.agc ); + hang_thresh = (double) _hangthreshold / 100.0; + loadWcpAGC(); } -void WCPAGC::GetAGCThresh(RXA& rxa, double *thresh, double size, double rate) -//for line on bandscope. -{ - double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); - *thresh = 20.0 * log10( rxa.agc->min_volts ) - noise_offset; -} - -void WCPAGC::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) -//for line on bandscope -{ - double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); - rxa.agc->max_gain = rxa.agc->out_target / (rxa.agc->var_gain * pow (10.0, (thresh + noise_offset) / 20.0)); - loadWcpAGC ( rxa.agc ); -} - -void WCPAGC::GetAGCTop(RXA& rxa, double *max_agc) +void WCPAGC::getTop(double *max_agc) //for AGC Max Gain in setup { - *max_agc = 20 * log10 (rxa.agc->max_gain); + *max_agc = 20 * log10 (max_gain); } -void WCPAGC::SetAGCTop (RXA& rxa, double max_agc) +void WCPAGC::setTop(double _max_agc) //for AGC Max Gain in setup { - rxa.agc->max_gain = pow (10.0, (double) max_agc / 20.0); - loadWcpAGC ( rxa.agc ); + max_gain = pow (10.0, (double) _max_agc / 20.0); + loadWcpAGC(); } -void WCPAGC::SetAGCSlope (RXA& rxa, int slope) +void WCPAGC::setSlope(int _slope) { - rxa.agc->var_gain = pow (10.0, (double) slope / 20.0 / 10.0); - loadWcpAGC ( rxa.agc ); + var_gain = pow (10.0, (double) _slope / 20.0 / 10.0); + loadWcpAGC(); } -void WCPAGC::SetAGCFixed (RXA& rxa, double fixed_agc) +void WCPAGC::setMaxInputLevel(double _level) { - rxa.agc->fixed_gain = pow (10.0, (double) fixed_agc / 20.0); - loadWcpAGC ( rxa.agc ); + max_input = _level; + loadWcpAGC(); } -void WCPAGC::SetAGCMaxInputLevel (RXA& rxa, double level) +void WCPAGC::setRun(int state) { - rxa.agc->max_input = level; - loadWcpAGC ( rxa.agc ); -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -void WCPAGC::SetALCSt (TXA& txa, int state) -{ - txa.alc->run = state; -} - -void WCPAGC::SetALCAttack (TXA& txa, int attack) -{ - txa.alc->tau_attack = (double) attack / 1000.0; - loadWcpAGC(txa.alc); -} - -void WCPAGC::SetALCDecay (TXA& txa, int decay) -{ - txa.alc->tau_decay = (double) decay / 1000.0; - loadWcpAGC(txa.alc); -} - -void WCPAGC::SetALCHang (TXA& txa, int hang) -{ - txa.alc->hangtime = (double) hang / 1000.0; - loadWcpAGC(txa.alc); -} - -void WCPAGC::SetALCMaxGain (TXA& txa, double maxgain) -{ - txa.alc->max_gain = pow (10.0,(double) maxgain / 20.0); - loadWcpAGC(txa.alc); -} - -void WCPAGC::SetLevelerSt (TXA& txa, int state) -{ - txa.leveler->run = state; -} - -void WCPAGC::SetLevelerAttack (TXA& txa, int attack) -{ - txa.leveler->tau_attack = (double) attack / 1000.0; - loadWcpAGC(txa.leveler); -} - -void WCPAGC::SetLevelerDecay (TXA& txa, int decay) -{ - txa.leveler->tau_decay = (double) decay / 1000.0; - loadWcpAGC(txa.leveler); -} - -void WCPAGC::SetLevelerHang (TXA& txa, int hang) -{ - txa.leveler->hangtime = (double) hang / 1000.0; - loadWcpAGC(txa.leveler); -} - -void WCPAGC::SetLevelerTop (TXA& txa, double maxgain) -{ - txa.leveler->max_gain = pow (10.0,(double) maxgain / 20.0); - loadWcpAGC(txa.leveler); + run = state; } } // namespace WDSP diff --git a/wdsp/wcpAGC.hpp b/wdsp/wcpAGC.hpp index 267595d24..a52e9620d 100644 --- a/wdsp/wcpAGC.hpp +++ b/wdsp/wcpAGC.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_wcpagc_h #define wdsp_wcpagc_h +#include + #include "export.h" #define MAX_SAMPLE_RATE (384000.0) @@ -37,9 +39,6 @@ warren@wpratt.com namespace WDSP { -class RXA; -class TXA; - class WDSP_API WCPAGC { public: @@ -71,16 +70,16 @@ public: int in_index; int attack_buffsize; - double* ring; - double* abs_ring; - int ring_buffsize; + std::array ring; + std::array abs_ring; + static const int ring_buffsize = RB_SIZE; double ring_max; double attack_mult; double decay_mult; double volts; double save_volts; - double out_sample[2]; + std::array out_sample; double abs_out_sample; int state; @@ -106,8 +105,7 @@ public: double hang_decay_mult; int decay_type; - static void xwcpagc (WCPAGC *a); - static WCPAGC* create_wcpagc ( + WCPAGC( int run, int mode, int pmode, @@ -132,43 +130,34 @@ public: double hang_thresh, double tau_hang_decay ); - static void destroy_wcpagc (WCPAGC *a); - static void flush_wcpagc (WCPAGC *a); - static void setBuffers_wcpagc (WCPAGC *a, float* in, float* out); - static void setSamplerate_wcpagc (WCPAGC *a, int rate); - static void setSize_wcpagc (WCPAGC *a, int size); - // RXA Properties - static void SetAGCMode (RXA& rxa, int mode); - static void SetAGCFixed (RXA& rxa, double fixed_agc); - static void SetAGCAttack (RXA& rxa, int attack); - static void SetAGCDecay (RXA& rxa, int decay); - static void SetAGCHang (RXA& rxa, int hang); - static void GetAGCHangLevel(RXA& rxa, double *hangLevel); - static void SetAGCHangLevel(RXA& rxa, double hangLevel); - static void GetAGCHangThreshold(RXA& rxa, int *hangthreshold); - static void SetAGCHangThreshold (RXA& rxa, int hangthreshold); - static void GetAGCTop(RXA& rxa, double *max_agc); - static void SetAGCTop (RXA& rxa, double max_agc); - static void SetAGCSlope (RXA& rxa, int slope); - static void SetAGCThresh(RXA& rxa, double thresh, double size, double rate); - static void GetAGCThresh(RXA& rxa, double *thresh, double size, double rate); - static void SetAGCMaxInputLevel (RXA& rxa, double level); - // TXA Properties - static void SetALCSt (TXA& txa, int state); - static void SetALCAttack (TXA& txa, int attack); - static void SetALCDecay (TXA& txa, int decay); - static void SetALCHang (TXA& txa, int hang); - static void SetLevelerSt (TXA& txa, int state); - static void SetLevelerAttack (TXA& txa, int attack); - static void SetLevelerDecay (TXA& txa, int decay); - static void SetLevelerHang (TXA& txa, int hang); - static void SetLevelerTop (TXA& txa, double maxgain); - static void SetALCMaxGain (TXA& txa, double maxgain); + WCPAGC(const WCPAGC&) = delete; + WCPAGC& operator=(const WCPAGC& other) = delete; + ~WCPAGC() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setMode(int mode); + void setFixed(double fixed_agc); + void setAttack(int attack); + void setDecay(int decay); + void setHang(int hang); + void getHangLevel(double *hangLevel); + void setHangLevel(double hangLevel); + void getHangThreshold(int *hangthreshold); + void setHangThreshold(int hangthreshold); + void getTop(double *max_agc); + void setTop(double max_agc); + void setSlope(int slope); + void setMaxInputLevel(double level); + void setRun(int state); + void loadWcpAGC(); private: - static void loadWcpAGC (WCPAGC *a); - static void calc_wcpagc (WCPAGC *a); - static void decalc_wcpagc (WCPAGC *a); + void calc(); }; } // namespace WDSP From 575fa755f834e244ef10a1e753770f793cbe600a Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 30 Jul 2024 00:45:32 +0200 Subject: [PATCH 23/46] WDSP: BANDPASS rework --- wdsp/RXA.cpp | 28 ++-- wdsp/TXA.cpp | 162 ++++++++++++++++----- wdsp/TXA.hpp | 2 + wdsp/bandpass.cpp | 364 +++++++++++++++++----------------------------- wdsp/bandpass.hpp | 32 ++-- 5 files changed, 291 insertions(+), 297 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index bc681c05e..5caa85090 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -444,7 +444,7 @@ RXA* RXA::create_rxa ( &rxa->agc->gain); // pointer for gain computation // Bandpass filter - After spectral noise reduction in the block diagram - rxa->bp1 = BANDPASS::create_bandpass ( + rxa->bp1 = new BANDPASS ( 1, // run - used only with ( AM || ANF || ANR || EMNR) 0, // position rxa->dsp_size, // buffer size @@ -570,7 +570,7 @@ void RXA::destroy_rxa (RXA *rxa) SPEAK::destroy_speak (rxa->speak); CBL::destroy_cbl (rxa->cbl); SIPHON::destroy_siphon (rxa->sip1); - BANDPASS::destroy_bandpass (rxa->bp1); + delete (rxa->bp1); delete (rxa->agcmeter); delete (rxa->agc); delete (rxa->emnr); @@ -623,7 +623,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->emnr->flush(); rxa->agc->flush(); rxa->agcmeter->flush(); - BANDPASS::flush_bandpass (rxa->bp1); + rxa->bp1->flush(); SIPHON::flush_siphon (rxa->sip1); CBL::flush_cbl (rxa->cbl); SPEAK::flush_speak (rxa->speak); @@ -656,12 +656,12 @@ void RXA::xrxa (RXA *rxa) rxa->anf->execute(0); rxa->anr->ANR::execute(0); rxa->emnr->execute(0); - BANDPASS::xbandpass (rxa->bp1, 0); + rxa->bp1->BANDPASS::execute(0); rxa->agc->execute(); rxa->anf->execute(1); rxa->anr->execute(1); rxa->emnr->execute(1); - BANDPASS::xbandpass (rxa->bp1, 1); + rxa->bp1->execute(1); rxa->agcmeter->execute(); SIPHON::xsiphon (rxa->sip1, 0); CBL::xcbl (rxa->cbl); @@ -767,7 +767,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->anf->setSamplerate(rxa->dsp_rate); rxa->anr->setSamplerate(rxa->dsp_rate); rxa->emnr->setSamplerate(rxa->dsp_rate); - BANDPASS::setSamplerate_bandpass (rxa->bp1, rxa->dsp_rate); + rxa->bp1->setSamplerate(rxa->dsp_rate); rxa->agc->setSamplerate(rxa->dsp_rate); rxa->agcmeter->setSamplerate(rxa->dsp_rate); SIPHON::setSamplerate_siphon (rxa->sip1, rxa->dsp_rate); @@ -843,8 +843,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->anr->setSize(rxa->dsp_size); rxa->emnr->setBuffers(rxa->midbuff, rxa->midbuff); rxa->emnr->setSize(rxa->dsp_size); - BANDPASS::setBuffers_bandpass (rxa->bp1, rxa->midbuff, rxa->midbuff); - BANDPASS::setSize_bandpass (rxa->bp1, rxa->dsp_size); + rxa->bp1->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->bp1->setSize(rxa->dsp_size); rxa->agc->setBuffers(rxa->midbuff, rxa->midbuff); rxa->agc->setSize(rxa->dsp_size); rxa->agcmeter->setBuffers(rxa->midbuff); @@ -957,7 +957,7 @@ void RXA::bp1Check ( else gain = 1.0; if (a->gain != gain) - BANDPASS::setGain_bandpass (a, gain, 0); + a->setGain(gain, 0); } void RXA::bp1Set (RXA& rxa) @@ -974,8 +974,8 @@ void RXA::bp1Set (RXA& rxa) else a->run = 0; if (!old && a->run) - BANDPASS::flush_bandpass (a); - FIRCORE::setUpdate_fircore (a->p); + a->flush(); + FIRCORE::setUpdate_fircore (a->fircore); } void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) @@ -1368,7 +1368,7 @@ void RXA::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) void RXA::SetPassband (RXA& rxa, float f_low, float f_high) { - BANDPASS::SetBandpassFreqs (rxa, f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) + rxa.bp1->setBandpassFreqs (f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) rxa.snba->setOutputBandwidth (f_low, f_high); // Spectral noise blanker (SNB) rxa.nbp0->SetFreqs (f_low, f_high); // Notched bandpass } @@ -1378,7 +1378,7 @@ void RXA::SetNC (RXA& rxa, int nc) int oldstate = rxa.state; rxa.nbp0->SetNC (nc); rxa.bpsnba->SetNC (nc); - BANDPASS::SetBandpassNC (rxa, nc); + rxa.bp1->SetBandpassNC (nc); rxa.eqp->setNC (nc); rxa.fmsq->setNC (nc); rxa.fmd->setNCde (nc); @@ -1390,7 +1390,7 @@ void RXA::SetMP (RXA& rxa, int mp) { rxa.nbp0->SetMP (mp); rxa.bpsnba->SetMP (mp); - BANDPASS::SetBandpassMP (rxa, mp); + rxa.bp1->SetBandpassMP (mp); rxa.eqp->setMP (mp); rxa.fmsq->setMP (mp); rxa.fmd->setMPde (mp); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index b9062f590..84d7e637d 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -47,6 +47,8 @@ warren@wpratt.com #include "slew.hpp" #include "iqc.hpp" #include "cfir.hpp" +#include "fircore.hpp" +#include "fir.hpp" #include "TXA.hpp" namespace WDSP { @@ -276,7 +278,7 @@ TXA* TXA::create_txa ( TXA_CFC_GAIN, // index for gain value (double*) &txa->cfcomp->gain); // pointer for gain computation - txa->bp0 = BANDPASS::create_bandpass ( + txa->bp0 = new BANDPASS( 1, // always runs 0, // position txa->dsp_size, // size @@ -297,7 +299,7 @@ TXA* TXA::create_txa ( txa->midbuff, // pointer to output buffer 3.0); // gain - txa->bp1 = BANDPASS::create_bandpass ( + txa->bp1 = new BANDPASS( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position txa->dsp_size, // size @@ -319,7 +321,7 @@ TXA* TXA::create_txa ( txa->dsp_rate, // sample rate 1.95); // gain for clippings - txa->bp2 = BANDPASS::create_bandpass ( + txa->bp2 = new BANDPASS( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position txa->dsp_size, // size @@ -532,11 +534,11 @@ void TXA::destroy_txa (TXA *txa) AMMOD::destroy_ammod (txa->ammod); delete (txa->alc); delete (txa->compmeter); - BANDPASS::destroy_bandpass (txa->bp2); + delete (txa->bp2); OSCTRL::destroy_osctrl (txa->osctrl); - BANDPASS::destroy_bandpass (txa->bp1); + delete (txa->bp1); COMPRESSOR::destroy_compressor (txa->compressor); - BANDPASS::destroy_bandpass (txa->bp0); + delete (txa->bp0); delete (txa->cfcmeter); CFCOMP::destroy_cfcomp (txa->cfcomp); delete (txa->lvlrmeter); @@ -574,11 +576,11 @@ void TXA::flush_txa (TXA* txa) txa->lvlrmeter->flush (); CFCOMP::flush_cfcomp (txa->cfcomp); txa->cfcmeter->flush (); - BANDPASS::flush_bandpass (txa->bp0); + txa->bp0->flush (); COMPRESSOR::flush_compressor (txa->compressor); - BANDPASS::flush_bandpass (txa->bp1); + txa->bp1->flush (); OSCTRL::flush_osctrl (txa->osctrl); - BANDPASS::flush_bandpass (txa->bp2); + txa->bp2->flush (); txa->compmeter->flush (); txa->alc->flush (); AMMOD::flush_ammod (txa->ammod); @@ -609,11 +611,11 @@ void xtxa (TXA* txa) txa->lvlrmeter->execute (); // Leveler Meter CFCOMP::xcfcomp (txa->cfcomp, 0); // Continuous Frequency Compressor with post-EQ txa->cfcmeter->execute (); // CFC+PostEQ Meter - BANDPASS::xbandpass (txa->bp0, 0); // primary bandpass filter + txa->bp0->execute (0); // primary bandpass filter COMPRESSOR::xcompressor (txa->compressor); // COMP compressor - BANDPASS::xbandpass (txa->bp1, 0); // aux bandpass (runs if COMP) + txa->bp1->execute (0); // aux bandpass (runs if COMP) OSCTRL::xosctrl (txa->osctrl); // CESSB Overshoot Control - BANDPASS::xbandpass (txa->bp2, 0); // aux bandpass (runs if CESSB) + txa->bp2->execute (0); // aux bandpass (runs if CESSB) txa->compmeter->execute (); // COMP meter txa->alc->execute (); // ALC AMMOD::xammod (txa->ammod); // AM Modulator @@ -706,11 +708,11 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->lvlrmeter->setSamplerate (txa->dsp_rate); CFCOMP::setSamplerate_cfcomp (txa->cfcomp, txa->dsp_rate); txa->cfcmeter->setSamplerate (txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp0, txa->dsp_rate); + txa->bp0->setSamplerate (txa->dsp_rate); COMPRESSOR::setSamplerate_compressor (txa->compressor, txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp1, txa->dsp_rate); + txa->bp1->setSamplerate (txa->dsp_rate); OSCTRL::setSamplerate_osctrl (txa->osctrl, txa->dsp_rate); - BANDPASS::setSamplerate_bandpass (txa->bp2, txa->dsp_rate); + txa->bp2->setSamplerate (txa->dsp_rate); txa->compmeter->setSamplerate (txa->dsp_rate); txa->alc->setSamplerate (txa->dsp_rate); AMMOD::setSamplerate_ammod (txa->ammod, txa->dsp_rate); @@ -778,16 +780,16 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) CFCOMP::setSize_cfcomp (txa->cfcomp, txa->dsp_size); txa->cfcmeter->setBuffers(txa->midbuff); txa->cfcmeter->setSize(txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp0, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp0, txa->dsp_size); + txa->bp0->setBuffers (txa->midbuff, txa->midbuff); + txa->bp0->setSize (txa->dsp_size); COMPRESSOR::setBuffers_compressor (txa->compressor, txa->midbuff, txa->midbuff); COMPRESSOR::setSize_compressor (txa->compressor, txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp1, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp1, txa->dsp_size); + txa->bp1->setBuffers (txa->midbuff, txa->midbuff); + txa->bp1->setSize (txa->dsp_size); OSCTRL::setBuffers_osctrl (txa->osctrl, txa->midbuff, txa->midbuff); OSCTRL::setSize_osctrl (txa->osctrl, txa->dsp_size); - BANDPASS::setBuffers_bandpass (txa->bp2, txa->midbuff, txa->midbuff); - BANDPASS::setSize_bandpass (txa->bp2, txa->dsp_size); + txa->bp2->setBuffers (txa->midbuff, txa->midbuff); + txa->bp2->setSize (txa->dsp_size); txa->compmeter->setBuffers(txa->midbuff); txa->compmeter->setSize(txa->dsp_size); txa->alc->setBuffers(txa->midbuff, txa->midbuff); @@ -914,14 +916,14 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_DIGU: case TXA_SPEC: case TXA_DRM: - BANDPASS::CalcBandpassFilter (txa.bp0, txa.f_low, txa.f_high, 2.0); + txa.bp0->calcBandpassFilter (txa.f_low, txa.f_high, 2.0); if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1, txa.f_low, txa.f_high, 2.0); + txa.bp1->calcBandpassFilter (txa.f_low, txa.f_high, 2.0); txa.bp1->run = 1; if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2, txa.f_low, txa.f_high, 1.0); + txa.bp2->calcBandpassFilter (txa.f_low, txa.f_high, 1.0); txa.bp2->run = 1; } } @@ -932,42 +934,42 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_FM: if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp0, 0.0, txa.f_high, 2.0); - BANDPASS::CalcBandpassFilter (txa.bp1, 0.0, txa.f_high, 2.0); + txa.bp0->calcBandpassFilter (0.0, txa.f_high, 2.0); + txa.bp1->calcBandpassFilter (0.0, txa.f_high, 2.0); txa.bp1->run = 1; if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2, 0.0, txa.f_high, 1.0); + txa.bp2->calcBandpassFilter (0.0, txa.f_high, 1.0); txa.bp2->run = 1; } } else { - BANDPASS::CalcBandpassFilter (txa.bp0, txa.f_low, txa.f_high, 1.0); + txa.bp0->calcBandpassFilter (txa.f_low, txa.f_high, 1.0); } break; case TXA_AM_LSB: - BANDPASS::CalcBandpassFilter (txa.bp0, -txa.f_high, 0.0, 2.0); + txa.bp0->calcBandpassFilter (-txa.f_high, 0.0, 2.0); if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1, -txa.f_high, 0.0, 2.0); + txa.bp1->calcBandpassFilter (-txa.f_high, 0.0, 2.0); txa.bp1->run = 1; if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2, -txa.f_high, 0.0, 1.0); + txa.bp2->calcBandpassFilter (-txa.f_high, 0.0, 1.0); txa.bp2->run = 1; } } break; case TXA_AM_USB: - BANDPASS::CalcBandpassFilter (txa.bp0, 0.0, txa.f_high, 2.0); + txa.bp0->calcBandpassFilter (0.0, txa.f_high, 2.0); if (txa.compressor->run) { - BANDPASS::CalcBandpassFilter (txa.bp1, 0.0, txa.f_high, 2.0); + txa.bp1->calcBandpassFilter (0.0, txa.f_high, 2.0); txa.bp1->run = 1; if (txa.osctrl->run) { - BANDPASS::CalcBandpassFilter (txa.bp2, 0.0, txa.f_high, 1.0); + txa.bp2->calcBandpassFilter(0.0, txa.f_high, 1.0); txa.bp2->run = 1; } } @@ -975,6 +977,93 @@ void TXA::SetupBPFilters (TXA& txa) } } +void TXA::SetBandpassNC (TXA& txa, int nc) +{ + // NOTE: 'nc' must be >= 'size' + BANDPASS *a; + a = txa.bp0; + + if (a->nc != nc) + { + a->nc = nc; + float* impulse = FIR::fir_bandpass ( + a->nc, + a->f_low, + a->f_high, + a->samplerate, + a->wintype, + 1, + a->gain / (double)(2 * a->size) + ); + FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + delete[] (impulse); + } + + a = txa.bp1; + + if (a->nc != nc) + { + a->nc = nc; + float* impulse = FIR::fir_bandpass ( + a->nc, + a->f_low, + a->f_high, + a->samplerate, + a->wintype, + 1, + a->gain / (double)(2 * a->size) + ); + FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + delete[] (impulse); + } + + a = txa.bp2; + + if (a->nc != nc) + { + a->nc = nc; + float* impulse = FIR::fir_bandpass ( + a->nc, + a->f_low, + a->f_high, + a->samplerate, + a->wintype, + 1, + a->gain / (double)(2 * a->size) + ); + FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + delete[] (impulse); + } +} + +void TXA::SetBandpassMP (TXA& txa, int mp) +{ + BANDPASS *a; + a = txa.bp0; + + if (mp != a->mp) + { + a->mp = mp; + FIRCORE::setMp_fircore (a->fircore, a->mp); + } + + a = txa.bp1; + + if (mp != a->mp) + { + a->mp = mp; + FIRCORE::setMp_fircore (a->fircore, a->mp); + } + + a = txa.bp2; + + if (mp != a->mp) + { + a->mp = mp; + FIRCORE::setMp_fircore (a->fircore, a->mp); + } +} + /******************************************************************************************************** * * * Collectives * @@ -984,7 +1073,8 @@ void TXA::SetupBPFilters (TXA& txa) void TXA::SetNC (TXA& txa, int nc) { int oldstate = txa.state; - BANDPASS::SetBandpassNC (txa, nc); + + SetBandpassNC (txa, nc); EMPHP::SetFMEmphNC (txa, nc); txa.eqp->setNC (nc); FMMOD::SetFMNC (txa, nc); @@ -994,7 +1084,7 @@ void TXA::SetNC (TXA& txa, int nc) void TXA::SetMP (TXA& txa, int mp) { - BANDPASS::SetBandpassMP (txa, mp); + SetBandpassMP (txa, mp); EMPHP::SetFMEmphMP (txa, mp); txa.eqp->setMP (mp); FMMOD::SetFMMP (txa, mp); diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index 4437660ff..c7b5fa203 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -191,6 +191,8 @@ public: // TXA Properties static void SetMode (TXA& txa, int mode); static void SetBandpassFreqs (TXA& txa, float f_low, float f_high); + static void SetBandpassNC (TXA& txa, int nc); + static void SetBandpassMP (TXA& txa, int mp); // Collectives static void SetNC (TXA& txa, int nc); diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index 6dd65e1f3..78845ad1f 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -40,143 +40,140 @@ namespace WDSP { * * ********************************************************************************************************/ -BANDPASS* BANDPASS::create_bandpass ( - int run, - int position, - int size, - int nc, - int mp, - float* in, - float* out, - double f_low, - double f_high, - int samplerate, - int wintype, - double gain +BANDPASS::BANDPASS( + int _run, + int _position, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + double _f_low, + double _f_high, + int _samplerate, + int _wintype, + double _gain ) { // NOTE: 'nc' must be >= 'size' - BANDPASS *a = new BANDPASS; - a->run = run; - a->position = position; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->f_low = f_low; - a->f_high = f_high; - a->samplerate = samplerate; - a->wintype = wintype; - a->gain = gain; + run = _run; + position = _position; + size = _size; + nc = _nc; + mp = _mp; + in = _in; + out = _out; + f_low = _f_low; + f_high = _f_high; + samplerate = _samplerate; + wintype = _wintype; + gain = _gain; float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, + nc, + f_low, + f_high, + samplerate, + wintype, 1, - a->gain / (double)(2 * a->size) + gain / (double)(2 * size) ); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse); - delete[] impulse; - return a; -} - -void BANDPASS::destroy_bandpass (BANDPASS *a) -{ - FIRCORE::destroy_fircore (a->p); - delete a; -} - -void BANDPASS::flush_bandpass (BANDPASS *a) -{ - FIRCORE::flush_fircore (a->p); -} - -void BANDPASS::xbandpass (BANDPASS *a, int pos) -{ - if (a->run && a->position == pos) - FIRCORE::xfircore (a->p); - else if (a->out != a->in) - std::copy( a->in, a->in + a->size * 2, a->out); -} - -void BANDPASS::setBuffers_bandpass (BANDPASS *a, float* in, float* out) -{ - a->in = in; - a->out = out; - FIRCORE::setBuffers_fircore (a->p, a->in, a->out); -} - -void BANDPASS::setSamplerate_bandpass (BANDPASS *a, int rate) -{ - a->samplerate = rate; - float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, - 1, - a->gain / (float)(2 * a->size) - ); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); delete[] impulse; } -void BANDPASS::setSize_bandpass (BANDPASS *a, int size) +BANDPASS::~BANDPASS() +{ + FIRCORE::destroy_fircore (fircore); +} + +void BANDPASS::flush() +{ + FIRCORE::flush_fircore(fircore); +} + +void BANDPASS::execute(int pos) +{ + if (run && position == pos) + FIRCORE::xfircore(fircore); + else if (out != in) + std::copy(in, in + size * 2, out); +} + +void BANDPASS::setBuffers(float* _in, float* _out) +{ + in = _in; + out = _out; + FIRCORE::setBuffers_fircore(fircore, in, out); +} + +void BANDPASS::setSamplerate(int _rate) +{ + samplerate = _rate; + float* impulse = FIR::fir_bandpass ( + nc, + f_low, + f_high, + samplerate, + wintype, + 1, + gain / (double) (2 * size) + ); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); + delete[] impulse; +} + +void BANDPASS::setSize(int _size) { // NOTE: 'size' must be <= 'nc' - a->size = size; - FIRCORE::setSize_fircore (a->p, a->size); + size = _size; + FIRCORE::setSize_fircore (fircore, size); // recalc impulse because scale factor is a function of size float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, + nc, + f_low, + f_high, + samplerate, + wintype, 1, - a->gain / (float)(2 * a->size) + gain / (double) (2 * size) ); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } -void BANDPASS::setGain_bandpass (BANDPASS *a, double gain, int update) +void BANDPASS::setGain(double _gain, int _update) { - a->gain = gain; + gain = _gain; float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, + nc, + f_low, + f_high, + samplerate, + wintype, 1, - a->gain / (double)(2 * a->size) + gain / (double) (2 * size) ); - FIRCORE::setImpulse_fircore (a->p, impulse, update); + FIRCORE::setImpulse_fircore (fircore, impulse, _update); delete[] (impulse); } -void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, double gain) +void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) { - if ((a->f_low != f_low) || (a->f_high != f_high) || (a->gain != gain)) + if ((f_low != _f_low) || (f_high != _f_high) || (gain != _gain)) { - a->f_low = f_low; - a->f_high = f_high; - a->gain = gain; + f_low = _f_low; + f_high = _f_high; + gain = _gain; float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, + nc, + f_low, + f_high, + samplerate, + wintype, 1, - a->gain / (double)(2 * a->size) + gain / (double)(2 * size) ); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + FIRCORE::setImpulse_fircore (fircore, impulse, 1); delete[] (impulse); } } @@ -187,62 +184,54 @@ void BANDPASS::CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, dou * * ********************************************************************************************************/ -void BANDPASS::SetBandpassFreqs (RXA& rxa, double f_low, double f_high) +void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) { - BANDPASS *a = rxa.bp1; - - if ((f_low != a->f_low) || (f_high != a->f_high)) + if ((_f_low != f_low) || (_f_high != f_high)) { float* impulse = FIR::fir_bandpass ( - a->nc, - f_low, - f_high, - a->samplerate, - a->wintype, + nc, + _f_low, + _f_high, + samplerate, + wintype, 1, - a->gain / (double)(2 * a->size) + gain / (double)(2 * size) ); - FIRCORE::setImpulse_fircore (a->p, impulse, 0); + FIRCORE::setImpulse_fircore (fircore, impulse, 0); delete[] (impulse); - a->f_low = f_low; - a->f_high = f_high; - FIRCORE::setUpdate_fircore (a->p); + f_low = _f_low; + f_high = _f_high; + FIRCORE::setUpdate_fircore (fircore); } } -void BANDPASS::SetBandpassNC (RXA& rxa, int nc) +void BANDPASS::SetBandpassNC(int _nc) { // NOTE: 'nc' must be >= 'size' - BANDPASS *a; - a = rxa.bp1; - - if (nc != a->nc) + if (_nc != nc) { - a->nc = nc; + nc = _nc; float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, + nc, + f_low, + f_high, + samplerate, + wintype, 1, - a->gain / (double)(2 * a->size) + gain / (double)( 2 * size) ); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); + FIRCORE::setNc_fircore (fircore, nc, impulse); delete[] (impulse); } } -void BANDPASS::SetBandpassMP (RXA& rxa, int mp) +void BANDPASS::SetBandpassMP(int _mp) { - BANDPASS *a; - a = rxa.bp1; - - if (mp != a->mp) + if (_mp != mp) { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); + mp = _mp; + FIRCORE::setMp_fircore (fircore, mp); } } @@ -263,7 +252,7 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) // a->f_low = f_low; // a->f_high = f_high; // impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->p, impulse, 1); +// setImpulse_fircore (a->fircore, impulse, 1); // delete[] (impulse); // } // a = txa.bp1; @@ -272,7 +261,7 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) // a->f_low = f_low; // a->f_high = f_high; // impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->p, impulse, 1); +// setImpulse_fircore (a->fircore, impulse, 1); // delete[] (impulse); // } // a = txa.bp2; @@ -281,96 +270,9 @@ void BANDPASS::SetBandpassMP (RXA& rxa, int mp) // a->f_low = f_low; // a->f_high = f_high; // impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->p, impulse, 1); +// setImpulse_fircore (a->fircore, impulse, 1); // delete[] (impulse); // } //} -void BANDPASS::SetBandpassNC (TXA& txa, int nc) -{ - // NOTE: 'nc' must be >= 'size' - BANDPASS *a; - a = txa.bp0; - - if (a->nc != nc) - { - a->nc = nc; - float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, - 1, - a->gain / (double)(2 * a->size) - ); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } - - a = txa.bp1; - - if (a->nc != nc) - { - a->nc = nc; - float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, - 1, - a->gain / (double)(2 * a->size) - ); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } - - a = txa.bp2; - - if (a->nc != nc) - { - a->nc = nc; - float* impulse = FIR::fir_bandpass ( - a->nc, - a->f_low, - a->f_high, - a->samplerate, - a->wintype, - 1, - a->gain / (double)(2 * a->size) - ); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } -} - -void BANDPASS::SetBandpassMP (TXA& txa, int mp) -{ - BANDPASS *a; - a = txa.bp0; - - if (mp != a->mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } - - a = txa.bp1; - - if (mp != a->mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } - - a = txa.bp2; - - if (mp != a->mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } -} - } // namespace WDSP diff --git a/wdsp/bandpass.hpp b/wdsp/bandpass.hpp index c72ac3a8f..3af29ffe2 100644 --- a/wdsp/bandpass.hpp +++ b/wdsp/bandpass.hpp @@ -64,9 +64,9 @@ public: double samplerate; int wintype; double gain; - FIRCORE *p; + FIRCORE *fircore; - static BANDPASS *create_bandpass ( + BANDPASS( int run, int position, int size, @@ -80,21 +80,21 @@ public: int wintype, double gain ); - static void destroy_bandpass (BANDPASS *a); - static void flush_bandpass (BANDPASS *a); - static void xbandpass (BANDPASS *a, int pos); - static void setBuffers_bandpass (BANDPASS *a, float* in, float* out); - static void setSamplerate_bandpass (BANDPASS *a, int rate); - static void setSize_bandpass (BANDPASS *a, int size); - static void setGain_bandpass (BANDPASS *a, double gain, int update); - static void CalcBandpassFilter (BANDPASS *a, double f_low, double f_high, double gain); + BANDPASS(const BANDPASS&) = delete; + BANDPASS& operator=(const BANDPASS& other) = delete; + ~BANDPASS(); + + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setGain(double gain, int update); + void calcBandpassFilter(double f_low, double f_high, double gain); // RXA Prototypes - static void SetBandpassFreqs (RXA& rxa, double f_low, double f_high); - static void SetBandpassNC (RXA& rxa, int nc); - static void SetBandpassMP (RXA& rxa, int mp); - // TXA Prototypes - static void SetBandpassNC (TXA& txa, int nc); - static void SetBandpassMP (TXA& txa, int mp); + void setBandpassFreqs(double f_low, double f_high); + void SetBandpassNC(int nc); + void SetBandpassMP(int mp); }; } // namespace WDSP From 350117b9a9747fbcf9908b77bd8db01dc15e3ea7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 30 Jul 2024 22:52:21 +0200 Subject: [PATCH 24/46] WDSP: SIPHON rework --- wdsp/RXA.cpp | 14 +-- wdsp/TXA.cpp | 14 +-- wdsp/siphon.cpp | 258 ++++++++++++++++++++---------------------------- wdsp/siphon.hpp | 49 ++++----- 4 files changed, 148 insertions(+), 187 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 5caa85090..c0fc8f15a 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -459,7 +459,7 @@ RXA* RXA::create_rxa ( 1.0); // gain // Scope/phase display send - pull phase & scope display data - rxa->sip1 = SIPHON::create_siphon ( + rxa->sip1 = new SIPHON( 0, // run - needed only for phase display 0, // position 0, // mode @@ -569,7 +569,7 @@ void RXA::destroy_rxa (RXA *rxa) MPEAK::destroy_mpeak (rxa->mpeak); SPEAK::destroy_speak (rxa->speak); CBL::destroy_cbl (rxa->cbl); - SIPHON::destroy_siphon (rxa->sip1); + delete (rxa->sip1); delete (rxa->bp1); delete (rxa->agcmeter); delete (rxa->agc); @@ -624,7 +624,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->agc->flush(); rxa->agcmeter->flush(); rxa->bp1->flush(); - SIPHON::flush_siphon (rxa->sip1); + rxa->sip1->flush(); CBL::flush_cbl (rxa->cbl); SPEAK::flush_speak (rxa->speak); MPEAK::flush_mpeak (rxa->mpeak); @@ -663,7 +663,7 @@ void RXA::xrxa (RXA *rxa) rxa->emnr->execute(1); rxa->bp1->execute(1); rxa->agcmeter->execute(); - SIPHON::xsiphon (rxa->sip1, 0); + rxa->sip1->execute(0); CBL::xcbl (rxa->cbl); SPEAK::xspeak (rxa->speak); MPEAK::xmpeak (rxa->mpeak); @@ -770,7 +770,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->bp1->setSamplerate(rxa->dsp_rate); rxa->agc->setSamplerate(rxa->dsp_rate); rxa->agcmeter->setSamplerate(rxa->dsp_rate); - SIPHON::setSamplerate_siphon (rxa->sip1, rxa->dsp_rate); + rxa->sip1->setSamplerate(rxa->dsp_rate); CBL::setSamplerate_cbl (rxa->cbl, rxa->dsp_rate); SPEAK::setSamplerate_speak (rxa->speak, rxa->dsp_rate); MPEAK::setSamplerate_mpeak (rxa->mpeak, rxa->dsp_rate); @@ -849,8 +849,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->agc->setSize(rxa->dsp_size); rxa->agcmeter->setBuffers(rxa->midbuff); rxa->agcmeter->setSize(rxa->dsp_size); - SIPHON::setBuffers_siphon (rxa->sip1, rxa->midbuff); - SIPHON::setSize_siphon (rxa->sip1, rxa->dsp_size); + rxa->sip1->setBuffers(rxa->midbuff); + rxa->sip1->setSize(rxa->dsp_size); CBL::setBuffers_cbl (rxa->cbl, rxa->midbuff, rxa->midbuff); CBL::setSize_cbl (rxa->cbl, rxa->dsp_size); SPEAK::setBuffers_speak (rxa->speak, rxa->midbuff, rxa->midbuff); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 84d7e637d..03d637453 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -431,7 +431,7 @@ TXA* TXA::create_txa ( TXA_ALC_GAIN, // index for gain value &txa->alc->gain); // pointer for gain computation - txa->sip1 = SIPHON::create_siphon ( + txa->sip1 = new SIPHON( 1, // run 0, // position 0, // mode @@ -526,7 +526,7 @@ void TXA::destroy_txa (TXA *txa) CFIR::destroy_cfir(txa->cfir); // destroy_calcc (txa->calcc); IQC::destroy_iqc (txa->iqc.p0); - SIPHON::destroy_siphon (txa->sip1); + delete (txa->sip1); delete (txa->alcmeter); USLEW::destroy_uslew (txa->uslew); delete (txa->gen1); @@ -588,7 +588,7 @@ void TXA::flush_txa (TXA* txa) txa->gen1->flush(); USLEW::flush_uslew (txa->uslew); txa->alcmeter->flush (); - SIPHON::flush_siphon (txa->sip1); + txa->sip1->flush(); IQC::flush_iqc (txa->iqc.p0); CFIR::flush_cfir(txa->cfir); txa->rsmpout->flush(); @@ -624,7 +624,7 @@ void xtxa (TXA* txa) txa->gen1->execute(); // output signal generator (TUN and Two-tone) USLEW::xuslew (txa->uslew); // up-slew for AM, FM, and gens txa->alcmeter->execute (); // ALC Meter - SIPHON::xsiphon (txa->sip1, 0); // siphon data for display + txa->sip1->execute(0); // siphon data for display IQC::xiqc (txa->iqc.p0); // PureSignal correction CFIR::xcfir(txa->cfir); // compensating FIR filter (used Protocol_2 only) txa->rsmpout->execute(); // output resampler @@ -720,7 +720,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->gen1->setSamplerate(txa->dsp_rate); USLEW::setSamplerate_uslew (txa->uslew, txa->dsp_rate); txa->alcmeter->setSamplerate (txa->dsp_rate); - SIPHON::setSamplerate_siphon (txa->sip1, txa->dsp_rate); + txa->sip1->setSamplerate (txa->dsp_rate); IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate); CFIR::setSamplerate_cfir (txa->cfir, txa->dsp_rate); // output resampler @@ -804,8 +804,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) USLEW::setSize_uslew (txa->uslew, txa->dsp_size); txa->alcmeter->setBuffers (txa->midbuff); txa->alcmeter->setSize(txa->dsp_size); - SIPHON::setBuffers_siphon (txa->sip1, txa->midbuff); - SIPHON::setSize_siphon (txa->sip1, txa->dsp_size); + txa->sip1->setBuffers (txa->midbuff); + txa->sip1->setSize (txa->dsp_size); IQC::setBuffers_iqc (txa->iqc.p0, txa->midbuff, txa->midbuff); IQC::setSize_iqc (txa->iqc.p0, txa->dsp_size); CFIR::setBuffers_cfir (txa->cfir, txa->midbuff, txa->midbuff); diff --git a/wdsp/siphon.cpp b/wdsp/siphon.cpp index bbfe583cc..930e75a76 100644 --- a/wdsp/siphon.cpp +++ b/wdsp/siphon.cpp @@ -33,157 +33,150 @@ warren@wpratt.com namespace WDSP { -void SIPHON::build_window (SIPHON *a) +void SIPHON::build_window() { int i; - float arg0, cosphi; - float sum, scale; - arg0 = 2.0 * PI / ((float)a->fftsize - 1.0); + double arg0, cosphi; + double sum, scale; + arg0 = 2.0 * PI / ((double) fftsize - 1.0); sum = 0.0; - for (i = 0; i < a->fftsize; i++) + for (i = 0; i < fftsize; i++) { cosphi = cos (arg0 * (float)i); - a->window[i] = + 6.3964424114390378e-02 + window[i] = + 6.3964424114390378e-02 + cosphi * ( - 2.3993864599352804e-01 + cosphi * ( + 3.5015956323820469e-01 + cosphi * ( - 2.4774111897080783e-01 + cosphi * ( + 8.5438256055858031e-02 + cosphi * ( - 1.2320203369293225e-02 + cosphi * ( + 4.3778825791773474e-04 )))))); - sum += a->window[i]; + sum += window[i]; } scale = 1.0 / sum; - for (i = 0; i < a->fftsize; i++) - a->window[i] *= scale; + for (i = 0; i < fftsize; i++) + window[i] *= scale; } -SIPHON* SIPHON::create_siphon ( - int run, - int position, - int mode, - int disp, - int insize, - float* in, - int sipsize, - int fftsize, - int specmode +SIPHON::SIPHON( + int _run, + int _position, + int _mode, + int _disp, + int _insize, + float* _in, + int _sipsize, + int _fftsize, + int _specmode ) { - SIPHON *a = new SIPHON; - a->run = run; - a->position = position; - a->mode = mode; - a->disp = disp; - a->insize = insize; - a->in = in; - a->sipsize = sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!! - a->fftsize = fftsize; - a->specmode = specmode; - a->sipbuff = new float[a->sipsize * 2]; // (float *) malloc0 (a->sipsize * sizeof (complex)); - a->idx = 0; - a->sipout = new float[a->sipsize * 2]; // (float *) malloc0 (a->sipsize * sizeof (complex)); - a->specout = new float[a->fftsize * 2]; // (float *) malloc0 (a->fftsize * sizeof (complex)); - a->sipplan = fftwf_plan_dft_1d (a->fftsize, (fftwf_complex *)a->sipout, (fftwf_complex *)a->specout, FFTW_FORWARD, FFTW_PATIENT); - a->window = new float[a->fftsize * 2]; // (float *) malloc0 (a->fftsize * sizeof (complex)); - build_window (a); - return a; + run = _run; + position = _position; + mode = _mode; + disp = _disp; + insize = _insize; + in = _in; + sipsize = _sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!! + fftsize = _fftsize; + specmode = _specmode; + sipbuff.resize(sipsize * 2); // (float *) malloc0 (sipsize * sizeof (complex)); + idx = 0; + sipout.resize(sipsize * 2); // (float *) malloc0 (sipsize * sizeof (complex)); + specout.resize(fftsize * 2); // (float *) malloc0 (fftsize * sizeof (complex)); + sipplan = fftwf_plan_dft_1d (fftsize, (fftwf_complex *) sipout.data(), (fftwf_complex *) specout.data(), FFTW_FORWARD, FFTW_PATIENT); + window.resize(fftsize * 2); // (float *) malloc0 (fftsize * sizeof (complex)); + build_window(); } -void SIPHON::destroy_siphon (SIPHON *a) +SIPHON::~SIPHON() { - fftwf_destroy_plan (a->sipplan); - delete[] (a->window); - delete[] (a->specout); - delete[] (a->sipout); - delete[] (a->sipbuff); - delete (a); + fftwf_destroy_plan (sipplan); } -void SIPHON::flush_siphon (SIPHON *a) +void SIPHON::flush() { - std::fill(a->sipbuff, a->sipbuff + a->sipsize * 2, 0); - std::fill(a->sipout, a->sipout + a->sipsize * 2, 0); - std::fill(a->specout, a->specout + a->fftsize * 2, 0); - a->idx = 0; + std::fill(sipbuff.begin(), sipbuff.end(), 0); + std::fill(sipout.begin(), sipout.end(), 0); + std::fill(specout.begin(), specout.end(), 0); + idx = 0; } -void SIPHON::xsiphon (SIPHON *a, int pos) +void SIPHON::execute(int pos) { int first, second; - if (a->run && a->position == pos) + if (run && position == pos) { - switch (a->mode) + switch (mode) { case 0: - if (a->insize >= a->sipsize) - std::copy(&(a->in[2 * (a->insize - a->sipsize)]), &(a->in[2 * (a->insize - a->sipsize)]) + a->sipsize * 2, a->sipbuff); + if (insize >= sipsize) + std::copy(&(in[2 * (insize - sipsize)]), &(in[2 * (insize - sipsize)]) + sipsize * 2, sipbuff.begin()); else { - if (a->insize > (a->sipsize - a->idx)) + if (insize > (sipsize - idx)) { - first = a->sipsize - a->idx; - second = a->insize - first; + first = sipsize - idx; + second = insize - first; } else { - first = a->insize; + first = insize; second = 0; } - std::copy(a->in, a->in + first * 2, a->sipbuff + 2 * a->idx); - std::copy(a->in + 2 * first, a->in + 2 * first + second * 2, a->sipbuff); - if ((a->idx += a->insize) >= a->sipsize) a->idx -= a->sipsize; + std::copy(in, in + first * 2, sipbuff.begin() + 2 * idx); + std::copy(in + 2 * first, in + 2 * first + second * 2, sipbuff.begin()); + if ((idx += insize) >= sipsize) idx -= sipsize; } break; case 1: - // Spectrum0 (1, a->disp, 0, 0, a->in); + // Spectrum0 (1, disp, 0, 0, in); break; } } } -void SIPHON::setBuffers_siphon (SIPHON *a, float* in) +void SIPHON::setBuffers(float* _in) { - a->in = in; + in = _in; } -void SIPHON::setSamplerate_siphon (SIPHON *a, int) +void SIPHON::setSamplerate(int) { - flush_siphon (a); + flush(); } -void SIPHON::setSize_siphon (SIPHON *a, int size) +void SIPHON::setSize(int size) { - a->insize = size; - flush_siphon (a); + insize = size; + flush(); } -void SIPHON::suck (SIPHON *a) +void SIPHON::suck() { - if (a->outsize <= a->sipsize) + if (outsize <= sipsize) { - int mask = a->sipsize - 1; - int j = (a->idx - a->outsize) & mask; - int size = a->sipsize - j; - if (size >= a->outsize) - std::copy(&(a->sipbuff[2 * j]), &(a->sipbuff[2 * j]) + a->outsize * 2, a->sipout); + int mask = sipsize - 1; + int j = (idx - outsize) & mask; + int size = sipsize - j; + if (size >= outsize) + std::copy(&(sipbuff[2 * j]), &(sipbuff[2 * j]) + outsize * 2, sipout.begin()); else { - std::copy(&(a->sipbuff[2 * j]), &(a->sipbuff[2 * j]) + size * 2, a->sipout); - std::copy(a->sipbuff, a->sipbuff + (a->outsize - size) * 2, &(a->sipout[2 * size])); + std::copy(&(sipbuff[2 * j]), &(sipbuff[2 * j]) + size * 2, sipout.begin()); + std::copy(sipbuff.begin(), sipbuff.begin() + (outsize - size) * 2, &(sipout[2 * size])); } } } -void SIPHON::sip_spectrum (SIPHON *a) +void SIPHON::sip_spectrum() { int i; - for (i = 0; i < a->fftsize; i++) + for (i = 0; i < fftsize; i++) { - a->sipout[2 * i + 0] *= a->window[i]; - a->sipout[2 * i + 1] *= a->window[i]; + sipout[2 * i + 0] *= window[i]; + sipout[2 * i + 1] *= window[i]; } - fftwf_execute (a->sipplan); + fftwf_execute (sipplan); } /******************************************************************************************************** @@ -192,29 +185,25 @@ void SIPHON::sip_spectrum (SIPHON *a) * * ********************************************************************************************************/ -void SIPHON::GetaSipF (RXA& rxa, float* out, int size) +void SIPHON::getaSipF(float* _out, int _size) { // return raw samples as floats - SIPHON *a=rxa.sip1; - int i; - a->outsize = size; - suck (a); + outsize = _size; + suck (); - for (i = 0; i < size; i++) { - out[i] = (float)a->sipout[2 * i + 0]; + for (int i = 0; i < _size; i++) { + _out[i] = (float) sipout[2 * i + 0]; } } -void SIPHON::GetaSipF1 (RXA& rxa, float* out, int size) +void SIPHON::getaSipF1(float* _out, int _size) { // return raw samples as floats - SIPHON *a=rxa.sip1; - int i; - a->outsize = size; - suck (a); + outsize = _size; + suck(); - for (i = 0; i < size; i++) + for (int i = 0; i < _size; i++) { - out[2 * i + 0] = (float)a->sipout[2 * i + 0]; - out[2 * i + 1] = (float)a->sipout[2 * i + 1]; + _out[2 * i + 0] = (float) sipout[2 * i + 0]; + _out[2 * i + 1] = (float) sipout[2 * i + 1]; } } @@ -224,84 +213,53 @@ void SIPHON::GetaSipF1 (RXA& rxa, float* out, int size) * * ********************************************************************************************************/ -void SIPHON::SetSipPosition (TXA& txa, int pos) +void SIPHON::setSipPosition(int _pos) { - SIPHON *a = txa.sip1; - a->position = pos; + position = _pos; } -void SIPHON::SetSipMode (TXA& txa, int mode) +void SIPHON::setSipMode(int _mode) { - SIPHON *a = txa.sip1; - a->mode = mode; + mode = _mode; } -void SIPHON::SetSipDisplay (TXA& txa, int disp) +void SIPHON::setSipDisplay(int _disp) { - SIPHON *a = txa.sip1; - a->disp = disp; + disp = _disp; } -void SIPHON::GetaSipF (TXA& txa, float* out, int size) -{ // return raw samples as floats - SIPHON *a = txa.sip1; - int i; - a->outsize = size; - suck (a); - - for (i = 0; i < size; i++) { - out[i] = (float)a->sipout[2 * i + 0]; - } -} - -void SIPHON::GetaSipF1 (TXA& txa, float* out, int size) -{ // return raw samples as floats - SIPHON *a = txa.sip1; - int i; - a->outsize = size; - suck (a); - - for (i = 0; i < size; i++) - { - out[2 * i + 0] = (float)a->sipout[2 * i + 0]; - out[2 * i + 1] = (float)a->sipout[2 * i + 1]; - } -} - -void SIPHON::SetSipSpecmode (TXA& txa, int mode) +void SIPHON::setSipSpecmode(int _mode) { - SIPHON *a = txa.sip1; - if (mode == 0) - a->specmode = 0; + if (_mode == 0) + specmode = 0; else - a->specmode = 1; + specmode = 1; } -void SIPHON::GetSpecF1 (TXA& txa, float* out) +void SIPHON::getSpecF1(float* _out) { // return spectrum magnitudes in dB - SIPHON *a = txa.sip1; int i, j, mid, m, n; - a->outsize = a->fftsize; - suck (a); - sip_spectrum (a); - mid = a->fftsize / 2; + outsize = fftsize; + suck(); + sip_spectrum(); + mid = fftsize / 2; - if (a->specmode != 1) + if (specmode != 1) { // swap the halves of the spectrum for (i = 0, j = mid; i < mid; i++, j++) { - out[i] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60)); - out[j] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * i + 0] * a->specout[2 * i + 0] + a->specout[2 * i + 1] * a->specout[2 * i + 1] + 1.0e-60)); + _out[i] = (float)(10.0 * MemLog::mlog10 (specout[2 * j + 0] * specout[2 * j + 0] + specout[2 * j + 1] * specout[2 * j + 1] + 1.0e-60)); + _out[j] = (float)(10.0 * MemLog::mlog10 (specout[2 * i + 0] * specout[2 * i + 0] + specout[2 * i + 1] * specout[2 * i + 1] + 1.0e-60)); } } else { // mirror each half of the spectrum in-place - for (i = 0, j = mid - 1, m = mid, n = a->fftsize - 1; i < mid; i++, j--, m++, n--) + for (i = 0, j = mid - 1, m = mid, n = fftsize - 1; i < mid; i++, j--, m++, n--) { - out[i] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * j + 0] * a->specout[2 * j + 0] + a->specout[2 * j + 1] * a->specout[2 * j + 1] + 1.0e-60)); - out[m] = (float)(10.0 * MemLog::mlog10 (a->specout[2 * n + 0] * a->specout[2 * n + 0] + a->specout[2 * n + 1] * a->specout[2 * n + 1] + 1.0e-60)); + _out[i] = (float)(10.0 * MemLog::mlog10 (specout[2 * j + 0] * specout[2 * j + 0] + specout[2 * j + 1] * specout[2 * j + 1] + 1.0e-60)); + _out[m] = (float)(10.0 * MemLog::mlog10 (specout[2 * n + 0] * specout[2 * n + 0] + specout[2 * n + 1] * specout[2 * n + 1] + 1.0e-60)); } } } diff --git a/wdsp/siphon.hpp b/wdsp/siphon.hpp index 5af4176ea..f753ca581 100644 --- a/wdsp/siphon.hpp +++ b/wdsp/siphon.hpp @@ -31,6 +31,8 @@ warren@wpratt.com #ifndef wdsp_siphon_h #define wdsp_siphon_h +#include + #include "fftw3.h" #include "export.h" @@ -49,17 +51,17 @@ public: int insize; float* in; int sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!! - float* sipbuff; + std::vector sipbuff; int outsize; int idx; - float* sipout; + std::vector sipout; int fftsize; - float* specout; + std::vector specout; long specmode; fftwf_plan sipplan; - float* window; + std::vector window; - static SIPHON* create_siphon ( + SIPHON( int run, int position, int mode, @@ -70,23 +72,24 @@ public: int fftsize, int specmode ); - static void destroy_siphon (SIPHON *a); - static void flush_siphon (SIPHON *a); - static void xsiphon (SIPHON *a, int pos); - static void setBuffers_siphon (SIPHON *a, float* in); - static void setSamplerate_siphon (SIPHON *a, int rate); - static void setSize_siphon (SIPHON *a, int size); + SIPHON(const SIPHON&) = delete; + SIPHON& operator=(const SIPHON& other) = delete; + ~SIPHON(); + + void flush(); + void execute(int pos); + void setBuffers(float* in); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void GetaSipF (RXA& rxa, float* out, int size); - static void GetaSipF1 (RXA& rxa, float* out, int size); + void getaSipF (float* out, int size); + void getaSipF1 (float* out, int size); // TXA Properties - static void SetSipPosition (TXA& txa, int pos); - static void SetSipMode (TXA& txa, int mode); - static void SetSipDisplay (TXA& txa, int disp); - static void GetaSipF (TXA& txa, float* out, int size); - static void GetaSipF1 (TXA& txa, float* out, int size); - static void GetSpecF1 (TXA& txa, float* out); - static void SetSipSpecmode (TXA& txa, int mode); + void setSipPosition(int pos); + void setSipMode(int mode); + void setSipDisplay(int disp); + void getSpecF1(float* out); + void setSipSpecmode(int mode); // Calls for External Use // static void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode); // static void destroy_siphonEXT (int id); @@ -94,9 +97,9 @@ public: // static void SetSiphonInsize (int id, int size); private: - static void build_window (SIPHON *a); - static void suck (SIPHON *a); - static void sip_spectrum (SIPHON *a); + void build_window(); + void suck(); + void sip_spectrum(); }; } // namespace WDSP From 913d5bf7b014d2c9d7e0c3eb52bb3404558200a9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 30 Jul 2024 23:29:37 +0200 Subject: [PATCH 25/46] WDSP: CBL rework --- wdsp/RXA.cpp | 14 +++--- wdsp/cblock.cpp | 113 +++++++++++++++++++++++------------------------- wdsp/cblock.hpp | 23 +++++----- wdsp/ssql.cpp | 8 ++-- 4 files changed, 77 insertions(+), 81 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index c0fc8f15a..289e27091 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -471,7 +471,7 @@ RXA* RXA::create_rxa ( 0); // specmode // AM carrier block - rxa->cbl = CBL::create_cbl ( + rxa->cbl = new CBL( 0, // run - needed only if set to ON rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer @@ -568,7 +568,7 @@ void RXA::destroy_rxa (RXA *rxa) SSQL::destroy_ssql (rxa->ssql); MPEAK::destroy_mpeak (rxa->mpeak); SPEAK::destroy_speak (rxa->speak); - CBL::destroy_cbl (rxa->cbl); + delete (rxa->cbl); delete (rxa->sip1); delete (rxa->bp1); delete (rxa->agcmeter); @@ -625,7 +625,7 @@ void RXA::flush_rxa (RXA *rxa) rxa->agcmeter->flush(); rxa->bp1->flush(); rxa->sip1->flush(); - CBL::flush_cbl (rxa->cbl); + rxa->cbl->flush(); SPEAK::flush_speak (rxa->speak); MPEAK::flush_mpeak (rxa->mpeak); SSQL::flush_ssql (rxa->ssql); @@ -664,7 +664,7 @@ void RXA::xrxa (RXA *rxa) rxa->bp1->execute(1); rxa->agcmeter->execute(); rxa->sip1->execute(0); - CBL::xcbl (rxa->cbl); + rxa->cbl->execute(); SPEAK::xspeak (rxa->speak); MPEAK::xmpeak (rxa->mpeak); SSQL::xssql (rxa->ssql); @@ -771,7 +771,7 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->agc->setSamplerate(rxa->dsp_rate); rxa->agcmeter->setSamplerate(rxa->dsp_rate); rxa->sip1->setSamplerate(rxa->dsp_rate); - CBL::setSamplerate_cbl (rxa->cbl, rxa->dsp_rate); + rxa->cbl->setSamplerate(rxa->dsp_rate); SPEAK::setSamplerate_speak (rxa->speak, rxa->dsp_rate); MPEAK::setSamplerate_mpeak (rxa->mpeak, rxa->dsp_rate); SSQL::setSamplerate_ssql (rxa->ssql, rxa->dsp_rate); @@ -851,8 +851,8 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->agcmeter->setSize(rxa->dsp_size); rxa->sip1->setBuffers(rxa->midbuff); rxa->sip1->setSize(rxa->dsp_size); - CBL::setBuffers_cbl (rxa->cbl, rxa->midbuff, rxa->midbuff); - CBL::setSize_cbl (rxa->cbl, rxa->dsp_size); + rxa->cbl->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->cbl->setSize(rxa->dsp_size); SPEAK::setBuffers_speak (rxa->speak, rxa->midbuff, rxa->midbuff); SPEAK::setSize_speak (rxa->speak, rxa->dsp_size); MPEAK::setBuffers_mpeak (rxa->mpeak, rxa->midbuff, rxa->midbuff); diff --git a/wdsp/cblock.cpp b/wdsp/cblock.cpp index 04119bed8..6514ea9c8 100644 --- a/wdsp/cblock.cpp +++ b/wdsp/cblock.cpp @@ -31,95 +31,88 @@ warren@wpratt.com namespace WDSP { -void CBL::calc_cbl (CBL *a) +void CBL::calc() { - a->prevIin = 0.0; - a->prevQin = 0.0; - a->prevIout = 0.0; - a->prevQout = 0.0; - a->mtau = exp(-1.0 / (a->sample_rate * a->tau)); + prevIin = 0.0; + prevQin = 0.0; + prevIout = 0.0; + prevQout = 0.0; + mtau = exp(-1.0 / (sample_rate * tau)); } -CBL* CBL::create_cbl( - int run, - int buff_size, - float *in_buff, - float *out_buff, - int mode, - int sample_rate, - double tau +CBL::CBL( + int _run, + int _buff_size, + float *_in_buff, + float *_out_buff, + int _mode, + int _sample_rate, + double _tau ) { - CBL *a = new CBL; - a->run = run; - a->buff_size = buff_size; - a->in_buff = in_buff; - a->out_buff = out_buff; - a->mode = mode; - a->sample_rate = (double) sample_rate; - a->tau = tau; - calc_cbl (a); - return a; + run = _run; + buff_size = _buff_size; + in_buff = _in_buff; + out_buff = _out_buff; + mode = _mode; + sample_rate = (double) _sample_rate; + tau = _tau; + calc(); } -void CBL::destroy_cbl(CBL *a) +void CBL::flush() { - delete a; + prevIin = 0.0; + prevQin = 0.0; + prevIout = 0.0; + prevQout = 0.0; } -void CBL::flush_cbl (CBL *a) +void CBL::execute() { - a->prevIin = 0.0; - a->prevQin = 0.0; - a->prevIout = 0.0; - a->prevQout = 0.0; -} - -void CBL::xcbl (CBL *a) -{ - if (a->run) + if (run) { int i; double tempI, tempQ; - for (i = 0; i < a->buff_size; i++) + for (i = 0; i < buff_size; i++) { - tempI = a->in_buff[2 * i + 0]; - tempQ = a->in_buff[2 * i + 1]; - a->out_buff[2 * i + 0] = a->in_buff[2 * i + 0] - a->prevIin + a->mtau * a->prevIout; - a->out_buff[2 * i + 1] = a->in_buff[2 * i + 1] - a->prevQin + a->mtau * a->prevQout; - a->prevIin = tempI; - a->prevQin = tempQ; + tempI = in_buff[2 * i + 0]; + tempQ = in_buff[2 * i + 1]; + out_buff[2 * i + 0] = in_buff[2 * i + 0] - prevIin + mtau * prevIout; + out_buff[2 * i + 1] = in_buff[2 * i + 1] - prevQin + mtau * prevQout; + prevIin = tempI; + prevQin = tempQ; - if (fabs(a->prevIout = a->out_buff[2 * i + 0]) < 1.0e-20) - a->prevIout = 0.0; + if (fabs(prevIout = out_buff[2 * i + 0]) < 1.0e-20) + prevIout = 0.0; - if (fabs(a->prevQout = a->out_buff[2 * i + 1]) < 1.0e-20) - a->prevQout = 0.0; + if (fabs(prevQout = out_buff[2 * i + 1]) < 1.0e-20) + prevQout = 0.0; } } - else if (a->in_buff != a->out_buff) + else if (in_buff != out_buff) { - std::copy(a->in_buff, a->in_buff + a->buff_size * 2, a->out_buff); + std::copy(in_buff, in_buff + buff_size * 2, out_buff); } } -void CBL::setBuffers_cbl (CBL *a, float* in, float* out) +void CBL::setBuffers(float* _in, float* _out) { - a->in_buff = in; - a->out_buff = out; + in_buff = _in; + out_buff = _out; } -void CBL::setSamplerate_cbl (CBL *a, int rate) +void CBL::setSamplerate(int _rate) { - a->sample_rate = rate; - calc_cbl (a); + sample_rate = _rate; + calc(); } -void CBL::setSize_cbl (CBL *a, int size) +void CBL::setSize(int _size) { - a->buff_size = size; - flush_cbl (a); + buff_size = _size; + flush(); } /******************************************************************************************************** @@ -128,9 +121,9 @@ void CBL::setSize_cbl (CBL *a, int size) * * ********************************************************************************************************/ -void CBL::SetCBLRun(RXA& rxa, int setit) +void CBL::setRun(int setit) { - rxa.cbl->run = setit; + run = setit; } } // namespace WDSP diff --git a/wdsp/cblock.hpp b/wdsp/cblock.hpp index 167a2a3b9..2bd781b4a 100644 --- a/wdsp/cblock.hpp +++ b/wdsp/cblock.hpp @@ -50,7 +50,7 @@ public: double tau; //carrier removal time constant double mtau; //carrier removal multiplier - static CBL* create_cbl( + CBL( int run, int buff_size, float *in_buff, @@ -59,17 +59,20 @@ public: int sample_rate, double tau ); - static void destroy_cbl (CBL *a); - static void flush_cbl (CBL *a); - static void xcbl (CBL *a); - static void setBuffers_cbl (CBL *a, float* in, float* out); - static void setSamplerate_cbl (CBL *a, int rate); - static void setSize_cbl (CBL *a, int size); - // RXA Properties - static void SetCBLRun(RXA& rxa, int setit); + CBL(const CBL&) = delete; + CBL& operator=(CBL& other) = delete; + ~CBL() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // Public Properties + void setRun(int setit); private: - static void calc_cbl (CBL *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index f6213b445..0d6283585 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -140,7 +140,7 @@ void SSQL::compute_ssql_slews(SSQL *a) void SSQL::calc_ssql (SSQL *a) { a->b1 = new float[a->size * 2]; // (float*) malloc0 (a->size * sizeof (complex)); - a->dcbl = CBL::create_cbl (1, a->size, a->in, a->b1, 0, a->rate, 0.02); + a->dcbl = new CBL(1, a->size, a->in, a->b1, 0, a->rate, 0.02); a->ibuff = new float[a->size]; // (float*) malloc0 (a->size * sizeof (float)); a->ftovbuff = new float[a->size]; // (float*) malloc0(a->size * sizeof (float)); a->cvtr = FTOV::create_ftov (1, a->size, a->rate, a->ftov_rsize, a->ftov_fmax, a->ibuff, a->ftovbuff); @@ -175,7 +175,7 @@ void SSQL::decalc_ssql (SSQL *a) FTOV::destroy_ftov (a->cvtr); delete[] (a->ftovbuff); delete[] (a->ibuff); - CBL::destroy_cbl (a->dcbl); + delete (a->dcbl); delete[] (a->b1); delete[] (a->cdown); delete[] (a->cup); @@ -230,7 +230,7 @@ void SSQL::flush_ssql (SSQL *a) { std::fill(a->b1, a->b1 + a->size * 2, 0); - CBL::flush_cbl (a->dcbl); + a->dcbl->flush(); memset (a->ibuff, 0, a->size * sizeof (float)); memset (a->ftovbuff, 0, a->size * sizeof (float)); FTOV::flush_ftov (a->cvtr); @@ -252,7 +252,7 @@ void SSQL::xssql (SSQL *a) { if (a->run) { - CBL::xcbl (a->dcbl); // dc block the input signal + a->dcbl->execute(); // dc block the input signal for (int i = 0; i < a->size; i++) // extract 'I' component a->ibuff[i] = a->b1[2 * i]; FTOV::xftov (a->cvtr); // convert frequency to voltage, ignoring amplitude From b9e3b10a6b76732173910b48d41dfada7786ea93 Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 31 Jul 2024 01:37:17 +0200 Subject: [PATCH 26/46] WDSP: split iir source files --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 4 +- wdsp/CMakeLists.txt | 24 +- wdsp/RXA.cpp | 3 +- wdsp/TXA.cpp | 2 +- wdsp/TXA.hpp | 1 - wdsp/bqbp.cpp | 159 +++ wdsp/bqbp.hpp | 70 ++ wdsp/bqlp.cpp | 155 +++ wdsp/bqlp.hpp | 70 ++ wdsp/dbqbp.cpp | 155 +++ wdsp/dbqbp.hpp | 71 ++ wdsp/dbqlp.cpp | 151 +++ wdsp/dbqlp.hpp | 70 ++ wdsp/dsphp.cpp | 140 +++ wdsp/dsphp.hpp | 71 ++ wdsp/fmd.cpp | 2 +- wdsp/iir.cpp | 1387 ----------------------- wdsp/iir.hpp | 440 ------- wdsp/mpeak.cpp | 221 ++++ wdsp/mpeak.hpp | 96 ++ wdsp/phrot.cpp | 182 +++ wdsp/phrot.hpp | 78 ++ wdsp/snotch.cpp | 136 +++ wdsp/snotch.hpp | 70 ++ wdsp/speak.cpp | 260 +++++ wdsp/speak.hpp | 89 ++ wdsp/sphp.cpp | 141 +++ wdsp/sphp.hpp | 72 ++ wdsp/ssql.cpp | 2 +- wdsp/ssql.hpp | 4 +- 30 files changed, 2488 insertions(+), 1838 deletions(-) create mode 100644 wdsp/bqbp.cpp create mode 100644 wdsp/bqbp.hpp create mode 100644 wdsp/bqlp.cpp create mode 100644 wdsp/bqlp.hpp create mode 100644 wdsp/dbqbp.cpp create mode 100644 wdsp/dbqbp.hpp create mode 100644 wdsp/dbqlp.cpp create mode 100644 wdsp/dbqlp.hpp create mode 100644 wdsp/dsphp.cpp create mode 100644 wdsp/dsphp.hpp delete mode 100644 wdsp/iir.cpp delete mode 100644 wdsp/iir.hpp create mode 100644 wdsp/mpeak.cpp create mode 100644 wdsp/mpeak.hpp create mode 100644 wdsp/phrot.cpp create mode 100644 wdsp/phrot.hpp create mode 100644 wdsp/snotch.cpp create mode 100644 wdsp/snotch.hpp create mode 100644 wdsp/speak.cpp create mode 100644 wdsp/speak.hpp create mode 100644 wdsp/sphp.cpp create mode 100644 wdsp/sphp.hpp diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index c6e7593e9..9edf7c2b5 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -38,12 +38,12 @@ #include "nob.hpp" #include "amd.hpp" #include "fmd.hpp" -#include "iir.cpp" #include "ssql.hpp" #include "amsq.hpp" #include "fmsq.hpp" -#include "eq.hpp" +#include "eqp.hpp" #include "shift.hpp" +#include "speak.hpp" #include "wdsprxsink.h" diff --git a/wdsp/CMakeLists.txt b/wdsp/CMakeLists.txt index abb56e609..fea3b3a1c 100644 --- a/wdsp/CMakeLists.txt +++ b/wdsp/CMakeLists.txt @@ -12,12 +12,17 @@ set(wdsp_SOURCES bldr.cpp bps.cpp bpsnba.cpp + bqbp.cpp + bqlp.cpp calculus.cpp cblock.cpp cfcomp.cpp cfir.cpp compress.cpp + dbqbp.cpp + dbqlp.cpp delay.cpp + dsphp.cpp emnr.cpp emph.cpp eqp.cpp @@ -32,15 +37,17 @@ set(wdsp_SOURCES gain.cpp gen.cpp icfir.cpp - iir.cpp + # iir.cpp iqc.cpp lmath.cpp meter.cpp meterlog10.cpp + mpeak.cpp nbp.cpp nob.cpp osctrl.cpp patchpanel.cpp + phrot.cpp resample.cpp resamplef.cpp rmatch.cpp @@ -50,6 +57,9 @@ set(wdsp_SOURCES siphon.cpp slew.cpp snba.cpp + snotch.cpp + speak.cpp + sphp.cpp ssql.cpp TXA.cpp varsamp.cpp @@ -67,6 +77,8 @@ set(wdsp_HEADERS bldr.hpp bps.hpp bpsnba.hpp + bqbp.hpp + bqlp.hpp bufferprobe.hpp calculus.hpp cblock.hpp @@ -74,7 +86,10 @@ set(wdsp_HEADERS cfir.hpp comm.hpp compress.hpp + dbqbp.hpp + dbqlp.hpp delay.hpp + dsphp.hpp emnr.hpp emph.hpp eqp.hpp @@ -89,15 +104,17 @@ set(wdsp_HEADERS gain.hpp gen.hpp icfir.hpp - iir.hpp + # iir.hpp iqc.hpp lmath.hpp meter.hpp meterlog10.hpp + mpeak.hpp nbp.hpp nob.hpp osctrl.hpp patchpanel.hpp + phrot.hpp resample.hpp resamplef.hpp rmatch.hpp @@ -107,6 +124,9 @@ set(wdsp_HEADERS siphon.hpp slew.hpp snba.hpp + snotch.hpp + speak.hpp + sphp.hpp ssql.hpp TXA.hpp varsamp.hpp diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 289e27091..b898722e5 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -49,11 +49,12 @@ warren@wpratt.com #include "siphon.hpp" #include "cblock.hpp" #include "ssql.hpp" -#include "iir.hpp" #include "fircore.hpp" #include "wcpAGC.hpp" #include "anb.hpp" #include "nob.hpp" +#include "speak.hpp" +#include "mpeak.hpp" namespace WDSP { diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 03d637453..84191334f 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -33,7 +33,6 @@ warren@wpratt.com #include "patchpanel.hpp" #include "amsq.hpp" #include "eq.hpp" -#include "iir.hpp" #include "cfcomp.hpp" #include "compress.hpp" #include "bandpass.hpp" @@ -48,6 +47,7 @@ warren@wpratt.com #include "iqc.hpp" #include "cfir.hpp" #include "fircore.hpp" +#include "phrot.hpp" #include "fir.hpp" #include "TXA.hpp" diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index c7b5fa203..284f2a643 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -37,7 +37,6 @@ warren@wpratt.com #include "patchpanel.hpp" #include "amsq.hpp" #include "eqp.hpp" -#include "iir.hpp" #include "cfcomp.hpp" #include "compress.hpp" #include "bandpass.hpp" diff --git a/wdsp/bqbp.cpp b/wdsp/bqbp.cpp new file mode 100644 index 000000000..0026fb004 --- /dev/null +++ b/wdsp/bqbp.cpp @@ -0,0 +1,159 @@ +/* iir.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "bqbp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Complex Bi-Quad Band-Pass * +* * +********************************************************************************************************/ + +void BQBP::calc_bqbp(BQBP *a) +{ + double f0, w0, bw, q, sn, cs, c, den; + bw = a->f_high - a->f_low; + f0 = (a->f_high + a->f_low) / 2.0; + q = f0 / bw; + w0 = TWOPI * f0 / a->rate; + sn = sin(w0); + cs = cos(w0); + c = sn / (2.0 * q); + den = 1.0 + c; + a->a0 = +c / den; + a->a1 = 0.0; + a->a2 = -c / den; + a->b1 = 2.0 * cs / den; + a->b2 = (c - 1.0) / den; + flush_bqbp(a); +} + +BQBP* BQBP::create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) +{ + BQBP *a = new BQBP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->f_low = f_low; + a->f_high = f_high; + a->gain = gain; + a->nstages = nstages; + a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + calc_bqbp(a); + return a; +} + +void BQBP::destroy_bqbp(BQBP *a) +{ + delete[](a->y2); + delete[](a->y1); + delete[](a->y0); + delete[](a->x2); + delete[](a->x1); + delete[](a->x0); + delete(a); +} + +void BQBP::flush_bqbp(BQBP *a) +{ + int i; + for (i = 0; i < a->nstages; i++) + { + a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; + a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + } +} + +void BQBP::xbqbp(BQBP *a) +{ + if (a->run) + { + int i, j, n; + + for (i = 0; i < a->size; i++) + { + for (j = 0; j < 2; j++) + { + a->x0[j] = a->gain * a->in[2 * i + j]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + + a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] + + a->a1 * a->x1[2 * n + j] + + a->a2 * a->x2[2 * n + j] + + a->b1 * a->y1[2 * n + j] + + a->b2 * a->y2[2 * n + j]; + a->y2[2 * n + j] = a->y1[2 * n + j]; + a->y1[2 * n + j] = a->y0[2 * n + j]; + a->x2[2 * n + j] = a->x1[2 * n + j]; + a->x1[2 * n + j] = a->x0[2 * n + j]; + } + + a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + } + } + } + else if (a->out != a->in) + { + std::copy(a->in, a->in + a->size * 2, a->out); + } +} + +void BQBP::setBuffers_bqbp(BQBP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void BQBP::setSamplerate_bqbp(BQBP *a, int rate) +{ + a->rate = rate; + calc_bqbp(a); +} + +void BQBP::setSize_bqbp(BQBP *a, int size) +{ + a->size = size; + flush_bqbp(a); +} + +} // namespace WDSP diff --git a/wdsp/bqbp.hpp b/wdsp/bqbp.hpp new file mode 100644 index 000000000..a530e552a --- /dev/null +++ b/wdsp/bqbp.hpp @@ -0,0 +1,70 @@ +/* bqbp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Complex Bi-Quad Band-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_bqbp_h +#define wdsp_bqbp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API BQBP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double f_low; + double f_high; + double gain; + int nstages; + double a0, a1, a2, b1, b2; + double* x0, * x1, * x2, * y0, * y1, * y2; + + static BQBP* create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); + static void destroy_bqbp(BQBP *a); + static void flush_bqbp(BQBP *a); + static void xbqbp(BQBP *a); + static void setBuffers_bqbp(BQBP *a, float* in, float* out); + static void setSamplerate_bqbp(BQBP *a, int rate); + static void setSize_bqbp(BQBP *a, int size); + +private: + static void calc_bqbp(BQBP *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/bqlp.cpp b/wdsp/bqlp.cpp new file mode 100644 index 000000000..c18caae4f --- /dev/null +++ b/wdsp/bqlp.cpp @@ -0,0 +1,155 @@ +/* iir.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "bqlp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Complex Bi-Quad Low-Pass * +* * +********************************************************************************************************/ + +void BQLP::calc_bqlp(BQLP *a) +{ + double w0, cs, c, den; + w0 = TWOPI * a->fc / (double)a->rate; + cs = cos(w0); + c = sin(w0) / (2.0 * a->Q); + den = 1.0 + c; + a->a0 = 0.5 * (1.0 - cs) / den; + a->a1 = (1.0 - cs) / den; + a->a2 = 0.5 * (1.0 - cs) / den; + a->b1 = 2.0 * cs / den; + a->b2 = (c - 1.0) / den; + flush_bqlp(a); +} + +BQLP* BQLP::create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) +{ + BQLP *a = new BQLP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->fc = fc; + a->Q = Q; + a->gain = gain; + a->nstages = nstages; + a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + calc_bqlp(a); + return a; +} + +void BQLP::destroy_bqlp(BQLP *a) +{ + delete[](a->y2); + delete[](a->y1); + delete[](a->y0); + delete[](a->x2); + delete[](a->x1); + delete[](a->x0); + delete(a); +} + +void BQLP::flush_bqlp(BQLP *a) +{ + int i; + for (i = 0; i < a->nstages; i++) + { + a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; + a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + } +} + +void BQLP::xbqlp(BQLP *a) +{ + if (a->run) + { + int i, j, n; + + for (i = 0; i < a->size; i++) + { + for (j = 0; j < 2; j++) + { + a->x0[j] = a->gain * a->in[2 * i + j]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] + + a->a1 * a->x1[2 * n + j] + + a->a2 * a->x2[2 * n + j] + + a->b1 * a->y1[2 * n + j] + + a->b2 * a->y2[2 * n + j]; + a->y2[2 * n + j] = a->y1[2 * n + j]; + a->y1[2 * n + j] = a->y0[2 * n + j]; + a->x2[2 * n + j] = a->x1[2 * n + j]; + a->x1[2 * n + j] = a->x0[2 * n + j]; + } + + a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + } + } + } + else if (a->out != a->in) + { + std::copy(a->in, a->in + a->size * 2, a->out); + } +} + +void BQLP::setBuffers_bqlp(BQLP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void BQLP::setSamplerate_bqlp(BQLP *a, int rate) +{ + a->rate = rate; + calc_bqlp(a); +} + +void BQLP::setSize_bqlp(BQLP *a, int size) +{ + a->size = size; + flush_bqlp(a); +} + + +} // namespace WDSP diff --git a/wdsp/bqlp.hpp b/wdsp/bqlp.hpp new file mode 100644 index 000000000..49672c765 --- /dev/null +++ b/wdsp/bqlp.hpp @@ -0,0 +1,70 @@ +/* bqlp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Complex Bi-Quad Low-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_bqlp_h +#define wdsp_bqlp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API BQLP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double fc; + double Q; + double gain; + int nstages; + double a0, a1, a2, b1, b2; + double* x0, * x1, * x2, * y0, * y1, * y2; + + static BQLP* create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); + static void destroy_bqlp(BQLP *a); + static void flush_bqlp(BQLP *a); + static void xbqlp(BQLP *a); + static void setBuffers_bqlp(BQLP *a, float* in, float* out); + static void setSamplerate_bqlp(BQLP *a, int rate); + static void setSize_bqlp(BQLP *a, int size); + +private: + static void calc_bqlp(BQLP *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/dbqbp.cpp b/wdsp/dbqbp.cpp new file mode 100644 index 000000000..0404a80f6 --- /dev/null +++ b/wdsp/dbqbp.cpp @@ -0,0 +1,155 @@ +/* dbqbp.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "dbqbp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Double Bi-Quad Band-Pass * +* * +********************************************************************************************************/ + +void DBQBP::calc_dbqbp(DBQBP *a) +{ + double f0, w0, bw, q, sn, cs, c, den; + bw = a->f_high - a->f_low; + f0 = (a->f_high + a->f_low) / 2.0; + q = f0 / bw; + w0 = TWOPI * f0 / a->rate; + sn = sin(w0); + cs = cos(w0); + c = sn / (2.0 * q); + den = 1.0 + c; + a->a0 = +c / den; + a->a1 = 0.0; + a->a2 = -c / den; + a->b1 = 2.0 * cs / den; + a->b2 = (c - 1.0) / den; + flush_dbqbp(a); +} + +DBQBP* DBQBP::create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) +{ + DBQBP *a = new DBQBP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->f_low = f_low; + a->f_high = f_high; + a->gain = gain; + a->nstages = nstages; + a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + calc_dbqbp(a); + return a; +} + +void DBQBP::destroy_dbqbp(DBQBP *a) +{ + delete[](a->y2); + delete[](a->y1); + delete[](a->y0); + delete[](a->x2); + delete[](a->x1); + delete[](a->x0); + delete(a); +} + +void DBQBP::flush_dbqbp(DBQBP *a) +{ + int i; + for (i = 0; i < a->nstages; i++) + { + a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; + } +} + +void DBQBP::xdbqbp(DBQBP *a) +{ + if (a->run) + { + int i, n; + + for (i = 0; i < a->size; i++) + { + a->x0[0] = a->gain * a->in[i]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[n] = a->y0[n - 1]; + + a->y0[n] = a->a0 * a->x0[n] + + a->a1 * a->x1[n] + + a->a2 * a->x2[n] + + a->b1 * a->y1[n] + + a->b2 * a->y2[n]; + a->y2[n] = a->y1[n]; + a->y1[n] = a->y0[n]; + a->x2[n] = a->x1[n]; + a->x1[n] = a->x0[n]; + } + + a->out[i] = a->y0[a->nstages - 1]; + } + } + else if (a->out != a->in) + { + memcpy(a->out, a->in, a->size * sizeof(float)); + } +} + +void DBQBP::setBuffers_dbqbp(DBQBP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void DBQBP::setSamplerate_dbqbp(DBQBP *a, int rate) +{ + a->rate = rate; + calc_dbqbp(a); +} + +void DBQBP::setSize_dbqbp(DBQBP *a, int size) +{ + a->size = size; + flush_dbqbp(a); +} + +} // namespace WDSP diff --git a/wdsp/dbqbp.hpp b/wdsp/dbqbp.hpp new file mode 100644 index 000000000..36051020e --- /dev/null +++ b/wdsp/dbqbp.hpp @@ -0,0 +1,71 @@ +/* dbqbp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Complex Bi-Quad Band-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_dbqbp_h +#define wdsp_dbqbp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API DBQBP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double f_low; + double f_high; + double gain; + int nstages; + double a0, a1, a2, b1, b2; + double* x0, * x1, * x2, * y0, * y1, * y2; + + // Double Bi-Quad Band-Pass + static DBQBP* create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); + static void destroy_dbqbp(DBQBP *a); + static void flush_dbqbp(DBQBP *a); + static void xdbqbp(DBQBP *a); + static void setBuffers_dbqbp(DBQBP *a, float* in, float* out); + static void setSamplerate_dbqbp(DBQBP *a, int rate); + static void setSize_dbqbp(DBQBP *a, int size); + +private: + static void calc_dbqbp(DBQBP *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/dbqlp.cpp b/wdsp/dbqlp.cpp new file mode 100644 index 000000000..3062b8ab3 --- /dev/null +++ b/wdsp/dbqlp.cpp @@ -0,0 +1,151 @@ +/* dbqlp.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "dbqlp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Double Bi-Quad Low-Pass * +* * +********************************************************************************************************/ + +void DBQLP::calc_dbqlp(DBQLP *a) +{ + float w0, cs, c, den; + w0 = TWOPI * a->fc / (float)a->rate; + cs = cos(w0); + c = sin(w0) / (2.0 * a->Q); + den = 1.0 + c; + a->a0 = 0.5 * (1.0 - cs) / den; + a->a1 = (1.0 - cs) / den; + a->a2 = 0.5 * (1.0 - cs) / den; + a->b1 = 2.0 * cs / den; + a->b2 = (c - 1.0) / den; + flush_dbqlp(a); +} + +DBQLP* DBQLP::create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) +{ + DBQLP *a = new DBQLP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->fc = fc; + a->Q = Q; + a->gain = gain; + a->nstages = nstages; + a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + calc_dbqlp(a); + return a; +} + +void DBQLP::destroy_dbqlp(DBQLP *a) +{ + delete[](a->y2); + delete[](a->y1); + delete[](a->y0); + delete[](a->x2); + delete[](a->x1); + delete[](a->x0); + delete(a); +} + +void DBQLP::flush_dbqlp(DBQLP *a) +{ + int i; + for (i = 0; i < a->nstages; i++) + { + a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; + } +} + +void DBQLP::xdbqlp(DBQLP *a) +{ + if (a->run) + { + int i, n; + + for (i = 0; i < a->size; i++) + { + a->x0[0] = a->gain * a->in[i]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[n] = a->y0[n - 1]; + + a->y0[n] = a->a0 * a->x0[n] + + a->a1 * a->x1[n] + + a->a2 * a->x2[n] + + a->b1 * a->y1[n] + + a->b2 * a->y2[n]; + a->y2[n] = a->y1[n]; + a->y1[n] = a->y0[n]; + a->x2[n] = a->x1[n]; + a->x1[n] = a->x0[n]; + } + + a->out[i] = a->y0[a->nstages - 1]; + } + } + else if (a->out != a->in) + { + memcpy(a->out, a->in, a->size * sizeof(float)); + } +} + +void DBQLP::setBuffers_dbqlp(DBQLP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void DBQLP::setSamplerate_dbqlp(DBQLP *a, int rate) +{ + a->rate = rate; + calc_dbqlp(a); +} + +void DBQLP::setSize_dbqlp(DBQLP *a, int size) +{ + a->size = size; + flush_dbqlp(a); +} + +} // namespace WDSP diff --git a/wdsp/dbqlp.hpp b/wdsp/dbqlp.hpp new file mode 100644 index 000000000..c6673944e --- /dev/null +++ b/wdsp/dbqlp.hpp @@ -0,0 +1,70 @@ +/* dbqlp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Double Bi-Quad Low-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_dbqlp_h +#define wdsp_dbqlp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API DBQLP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double fc; + double Q; + double gain; + int nstages; + double a0, a1, a2, b1, b2; + double* x0, * x1, * x2, * y0, * y1, * y2; + + static DBQLP* create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); + static void destroy_dbqlp(DBQLP *a); + static void flush_dbqlp(DBQLP *a); + static void xdbqlp(DBQLP *a); + static void setBuffers_dbqlp(DBQLP *a, float* in, float* out); + static void setSamplerate_dbqlp(DBQLP *a, int rate); + static void setSize_dbqlp(DBQLP *a, int size); + +private: + static void calc_dbqlp(DBQLP *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/dsphp.cpp b/wdsp/dsphp.cpp new file mode 100644 index 000000000..c6ae2d527 --- /dev/null +++ b/wdsp/dsphp.cpp @@ -0,0 +1,140 @@ +/* iir.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "dsphp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Double Single-Pole High-Pass * +* * +********************************************************************************************************/ + +void DSPHP::calc_dsphp(DSPHP *a) +{ + double g; + a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); + g = exp(-TWOPI * a->fc / a->rate); + a->b0 = +0.5 * (1.0 + g); + a->b1 = -0.5 * (1.0 + g); + a->a1 = -g; +} + +DSPHP* DSPHP::create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) +{ + DSPHP *a = new DSPHP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->fc = fc; + a->nstages = nstages; + calc_dsphp(a); + return a; +} + +void DSPHP::decalc_dsphp(DSPHP *a) +{ + delete[](a->y1); + delete[](a->y0); + delete[](a->x1); + delete[](a->x0); +} + +void DSPHP::destroy_dsphp(DSPHP *a) +{ + decalc_dsphp(a); + delete(a); +} + +void DSPHP::flush_dsphp(DSPHP *a) +{ + memset(a->x0, 0, a->nstages * sizeof(float)); + memset(a->x1, 0, a->nstages * sizeof(float)); + memset(a->y0, 0, a->nstages * sizeof(float)); + memset(a->y1, 0, a->nstages * sizeof(float)); +} + +void DSPHP::xdsphp(DSPHP *a) +{ + if (a->run) + { + int i, n; + + for (i = 0; i < a->size; i++) + { + a->x0[0] = a->in[i]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[n] = a->y0[n - 1]; + + a->y0[n] = a->b0 * a->x0[n] + + a->b1 * a->x1[n] + - a->a1 * a->y1[n]; + a->y1[n] = a->y0[n]; + a->x1[n] = a->x0[n]; + } + + a->out[i] = a->y0[a->nstages - 1]; + } + } + else if (a->out != a->in) + { + memcpy(a->out, a->in, a->size * sizeof(float)); + } +} + +void DSPHP::setBuffers_dsphp(DSPHP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void DSPHP::setSamplerate_dsphp(DSPHP *a, int rate) +{ + decalc_dsphp(a); + a->rate = rate; + calc_dsphp(a); +} + +void DSPHP::setSize_dsphp(DSPHP *a, int size) +{ + a->size = size; + flush_dsphp(a); +} + +} // namespace WDSP diff --git a/wdsp/dsphp.hpp b/wdsp/dsphp.hpp new file mode 100644 index 000000000..4b5b93b19 --- /dev/null +++ b/wdsp/dsphp.hpp @@ -0,0 +1,71 @@ +/* sphp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Double Single-Pole High-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_dsphp_h +#define wdsp_dsphp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API DSPHP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double fc; + int nstages; + double a1, b0, b1; + double* x0, * x1, * y0, * y1; + + static DSPHP* create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); + static void destroy_dsphp(DSPHP *a); + static void flush_dsphp(DSPHP *a); + static void xdsphp(DSPHP *a); + static void setBuffers_dsphp(DSPHP *a, float* in, float* out); + static void setSamplerate_dsphp(DSPHP *a, int rate); + static void setSize_dsphp(DSPHP *a, int size); + +private: + static void calc_dsphp(DSPHP *a); + static void decalc_dsphp(DSPHP *a); +}; + +} // namespace WDSP + +#endif + + diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index bac132be5..e762dfcce 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -26,11 +26,11 @@ warren@wpratt.com */ #include "comm.hpp" -#include "iir.hpp" #include "fircore.hpp" #include "fcurve.hpp" #include "fir.hpp" #include "wcpAGC.hpp" +#include "snotch.hpp" #include "fmd.hpp" namespace WDSP { diff --git a/wdsp/iir.cpp b/wdsp/iir.cpp deleted file mode 100644 index 7080d5744..000000000 --- a/wdsp/iir.cpp +++ /dev/null @@ -1,1387 +0,0 @@ -/* iir.c - -This file is part of a program that implements a Software-Defined Radio. - -Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V -Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel - -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.hpp" -#include "iir.hpp" -#include "RXA.hpp" -#include "TXA.hpp" - -namespace WDSP { - -/******************************************************************************************************** -* * -* Bi-Quad Notch * -* * -********************************************************************************************************/ - -void SNOTCH::calc_snotch (SNOTCH *a) -{ - double fn, qk, qr, csn; - fn = a->f / (float)a->rate; - csn = cos (TWOPI * fn); - qr = 1.0 - 3.0 * a->bw; - qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); - a->a0 = + qk; - a->a1 = - 2.0 * qk * csn; - a->a2 = + qk; - a->b1 = + 2.0 * qr * csn; - a->b2 = - qr * qr; - flush_snotch (a); -} - -SNOTCH* SNOTCH::create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw) -{ - SNOTCH *a = new SNOTCH; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f = f; - a->bw = bw; - calc_snotch (a); - return a; -} - -void SNOTCH::destroy_snotch (SNOTCH *a) -{ - delete (a); -} - -void SNOTCH::flush_snotch (SNOTCH *a) -{ - a->x1 = a->x2 = a->y1 = a->y2 = 0.0; -} - -void SNOTCH::xsnotch (SNOTCH *a) -{ - if (a->run) - { - int i; - for (i = 0; i < a->size; i++) - { - a->x0 = a->in[2 * i + 0]; - a->out[2 * i + 0] = a->a0 * a->x0 + a->a1 * a->x1 + a->a2 * a->x2 + a->b1 * a->y1 + a->b2 * a->y2; - a->y2 = a->y1; - a->y1 = a->out[2 * i + 0]; - a->x2 = a->x1; - a->x1 = a->x0; - } - } - else if (a->out != a->in) - { - std::copy( a->in, a->in + a->size * 2, a->out); - } -} - -void SNOTCH::setBuffers_snotch (SNOTCH *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void SNOTCH::setSamplerate_snotch (SNOTCH *a, int rate) -{ - a->rate = rate; - calc_snotch (a); -} - -void SNOTCH::setSize_snotch (SNOTCH *a, int size) -{ - a->size = size; - flush_snotch (a); -} - -/******************************************************************************************************** -* * -* RXA Properties * -* * -********************************************************************************************************/ - -void SNOTCH::SetSNCTCSSFreq (SNOTCH *a, double freq) -{ - a->f = freq; - calc_snotch (a); -} - -void SNOTCH::SetSNCTCSSRun (SNOTCH *a, int run) -{ - a->run = run; -} - - -/******************************************************************************************************** -* * -* Complex Bi-Quad Peaking * -* * -********************************************************************************************************/ - -void SPEAK::calc_speak (SPEAK *a) -{ - double ratio; - double f_corr, g_corr, bw_corr, bw_parm, A, f_min; - - switch (a->design) - { - case 0: - ratio = a->bw / a->f; - switch (a->nstages) - { - case 4: - bw_parm = 2.4; - f_corr = 1.0 - 0.160 * ratio + 1.440 * ratio * ratio; - g_corr = 1.0 - 1.003 * ratio + 3.990 * ratio * ratio; - break; - default: - bw_parm = 1.0; - f_corr = 1.0; - g_corr = 1.0; - break; - } - { - double fn, qk, qr, csn; - a->fgain = a->gain / g_corr; - fn = a->f / (double)a->rate / f_corr; - csn = cos (TWOPI * fn); - qr = 1.0 - 3.0 * a->bw / (double)a->rate * bw_parm; - qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); - a->a0 = 1.0 - qk; - a->a1 = 2.0 * (qk - qr) * csn; - a->a2 = qr * qr - qk; - a->b1 = 2.0 * qr * csn; - a->b2 = - qr * qr; - } - break; - - case 1: - if (a->f < 200.0) a->f = 200.0; - ratio = a->bw / a->f; - switch (a->nstages) - { - case 4: - bw_parm = 5.0; - bw_corr = 1.13 * ratio - 0.956 * ratio * ratio; - A = 2.5; - f_min = 50.0; - break; - default: - bw_parm = 1.0; - bw_corr = 1.0; - g_corr = 1.0; - A = 2.5; - f_min = 50.0; - break; - } - { - double w0, sn, c, den; - if (a->f < f_min) a->f = f_min; - w0 = TWOPI * a->f / (double)a->rate; - sn = sin (w0); - a->cbw = bw_corr * a->f; - c = sn * sinh(0.5 * log((a->f + 0.5 * a->cbw * bw_parm) / (a->f - 0.5 * a->cbw * bw_parm)) * w0 / sn); - den = 1.0 + c / A; - a->a0 = (1.0 + c * A) / den; - a->a1 = - 2.0 * cos (w0) / den; - a->a2 = (1 - c * A) / den; - a->b1 = - a->a1; - a->b2 = - (1 - c / A ) / den; - a->fgain = a->gain / pow (A * A, (double)a->nstages); - } - break; - } - flush_speak (a); -} - -SPEAK* SPEAK::create_speak ( - int run, - int size, - float* in, - float* out, - int rate, - double f, - double bw, - double gain, - int nstages, - int design -) -{ - SPEAK *a = new SPEAK; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f = f; - a->bw = bw; - a->gain = gain; - a->nstages = nstages; - a->design = design; - a->x0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->x1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->x2 = new double[a->nstages * 2]; //(float *) malloc0 (a->nstages * sizeof (complex)); - a->y0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->y1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->y2 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - calc_speak (a); - return a; -} - -void SPEAK::destroy_speak (SPEAK *a) -{ - delete[] (a->y2); - delete[] (a->y1); - delete[] (a->y0); - delete[] (a->x2); - delete[] (a->x1); - delete[] (a->x0); - delete (a); -} - -void SPEAK::flush_speak (SPEAK *a) -{ - int i; - for (i = 0; i < a->nstages; i++) - { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; - } -} - -void SPEAK::xspeak (SPEAK *a) -{ - if (a->run) - { - int i, j, n; - - for (i = 0; i < a->size; i++) - { - for (j = 0; j < 2; j++) - { - a->x0[j] = a->fgain * a->in[2 * i + j]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; - } - - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; - } - } - } - else if (a->out != a->in) - { - std::copy( a->in, a->in + a->size * 2, a->out); - } -} - -void SPEAK::setBuffers_speak (SPEAK *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void SPEAK::setSamplerate_speak (SPEAK *a, int rate) -{ - a->rate = rate; - calc_speak (a); -} - -void SPEAK::setSize_speak (SPEAK *a, int size) -{ - a->size = size; - flush_speak (a); -} - -/******************************************************************************************************** -* * -* RXA Properties * -* * -********************************************************************************************************/ - -void SPEAK::SetSPCWRun (RXA& rxa, int run) -{ - SPEAK *a = rxa.speak; - a->run = run; -} - -void SPEAK::SetSPCWFreq (RXA& rxa, double freq) -{ - SPEAK *a = rxa.speak; - a->f = freq; - calc_speak (a); -} - -void SPEAK::SetSPCWBandwidth (RXA& rxa, double bw) -{ - SPEAK *a = rxa.speak; - a->bw = bw; - calc_speak (a); -} - -void SPEAK::SetSPCWGain (RXA& rxa, double gain) -{ - SPEAK *a = rxa.speak; - a->gain = gain; - calc_speak (a); -} - -/******************************************************************************************************** -* * -* Complex Multiple Peaking * -* * -********************************************************************************************************/ - -void MPEAK::calc_mpeak (MPEAK *a) -{ - int i; - a->tmp = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); - a->mix = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); - for (i = 0; i < a->npeaks; i++) - { - a->pfil[i] = SPEAK::create_speak ( - 1, - a->size, - a->in, - a->tmp, - a->rate, - a->f[i], - a->bw[i], - a->gain[i], - a->nstages, - 1 - ); - } -} - -void MPEAK::decalc_mpeak (MPEAK *a) -{ - int i; - for (i = 0; i < a->npeaks; i++) - SPEAK::destroy_speak (a->pfil[i]); - delete[] (a->mix); - delete[] (a->tmp); -} - -MPEAK* MPEAK::create_mpeak ( - int run, - int size, - float* in, - float* out, - int rate, - int npeaks, - int* enable, - double* f, - double* bw, - double* gain, - int nstages -) -{ - MPEAK *a = new MPEAK; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->npeaks = npeaks; - a->nstages = nstages; - a->enable = new int[a->npeaks]; // (int *) malloc0 (a->npeaks * sizeof (int)); - a->f = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - a->bw = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - a->gain = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - memcpy (a->enable, enable, a->npeaks * sizeof (int)); - memcpy (a->f, f, a->npeaks * sizeof (double)); - memcpy (a->bw, bw, a->npeaks * sizeof (double)); - memcpy (a->gain, gain, a->npeaks * sizeof (double)); - a->pfil = new SPEAK*[a->npeaks]; // (SPEAK *) malloc0 (a->npeaks * sizeof (SPEAK)); - calc_mpeak (a); - return a; -} - -void MPEAK::destroy_mpeak (MPEAK *a) -{ - decalc_mpeak (a); - delete[] (a->pfil); - delete[] (a->gain); - delete[] (a->bw); - delete[] (a->f); - delete[] (a->enable); - delete (a); -} - -void MPEAK::flush_mpeak (MPEAK *a) -{ - int i; - for (i = 0; i < a->npeaks; i++) - SPEAK::flush_speak (a->pfil[i]); -} - -void MPEAK::xmpeak (MPEAK *a) -{ - if (a->run) - { - int i, j; - std::fill(a->mix, a->mix + a->size * 2, 0); - - for (i = 0; i < a->npeaks; i++) - { - if (a->enable[i]) - { - SPEAK::xspeak (a->pfil[i]); - for (j = 0; j < 2 * a->size; j++) - a->mix[j] += a->tmp[j]; - } - } - - std::copy(a->mix, a->mix + a->size * 2, a->out); - } - else if (a->in != a->out) - { - std::copy( a->in, a->in + a->size * 2, a->out); - } -} - -void MPEAK::setBuffers_mpeak (MPEAK *a, float* in, float* out) -{ - decalc_mpeak (a); - a->in = in; - a->out = out; - calc_mpeak (a); -} - -void MPEAK::setSamplerate_mpeak (MPEAK *a, int rate) -{ - decalc_mpeak (a); - a->rate = rate; - calc_mpeak (a); -} - -void MPEAK::setSize_mpeak (MPEAK *a, int size) -{ - decalc_mpeak (a); - a->size = size; - calc_mpeak (a); -} - -/******************************************************************************************************** -* * -* RXA Properties * -* * -********************************************************************************************************/ - -void MPEAK::SetmpeakRun (RXA& rxa, int run) -{ - MPEAK *a = rxa.mpeak; - a->run = run; -} - -void MPEAK::SetmpeakNpeaks (RXA& rxa, int npeaks) -{ - MPEAK *a = rxa.mpeak; - a->npeaks = npeaks; -} - -void MPEAK::SetmpeakFilEnable (RXA& rxa, int fil, int enable) -{ - MPEAK *a = rxa.mpeak; - a->enable[fil] = enable; -} - -void MPEAK::SetmpeakFilFreq (RXA& rxa, int fil, double freq) -{ - MPEAK *a = rxa.mpeak; - a->f[fil] = freq; - a->pfil[fil]->f = freq; - SPEAK::calc_speak(a->pfil[fil]); -} - -void MPEAK::SetmpeakFilBw (RXA& rxa, int fil, double bw) -{ - MPEAK *a = rxa.mpeak; - a->bw[fil] = bw; - a->pfil[fil]->bw = bw; - SPEAK::calc_speak(a->pfil[fil]); -} - -void MPEAK::SetmpeakFilGain (RXA& rxa, int fil, double gain) -{ - MPEAK *a = rxa.mpeak; - a->gain[fil] = gain; - a->pfil[fil]->gain = gain; - SPEAK::calc_speak(a->pfil[fil]); -} - - -/******************************************************************************************************** -* * -* Phase Rotator * -* * -********************************************************************************************************/ - -void PHROT::calc_phrot (PHROT *a) -{ - double g; - a->x0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->x1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->y0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->y1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - g = tan (PI * a->fc / (float)a->rate); - a->b0 = (g - 1.0) / (g + 1.0); - a->b1 = 1.0; - a->a1 = a->b0; -} - -void PHROT::decalc_phrot (PHROT *a) -{ - delete[] (a->y1); - delete[] (a->y0); - delete[] (a->x1); - delete[] (a->x0); -} - -PHROT* PHROT::create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages) -{ - PHROT *a = new PHROT; - a->reverse = 0; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_phrot (a); - return a; -} - -void PHROT::destroy_phrot (PHROT *a) -{ - decalc_phrot (a); - delete (a); -} - -void PHROT::flush_phrot (PHROT *a) -{ - memset (a->x0, 0, a->nstages * sizeof (double)); - memset (a->x1, 0, a->nstages * sizeof (double)); - memset (a->y0, 0, a->nstages * sizeof (double)); - memset (a->y1, 0, a->nstages * sizeof (double)); -} - -void PHROT::xphrot (PHROT *a) -{ - if (a->reverse) - { - for (int i = 0; i < a->size; i++) - a->in[2 * i + 0] = -a->in[2 * i + 0]; - } - - if (a->run) - { - int i, n; - - for (i = 0; i < a->size; i++) - { - a->x0[0] = a->in[2 * i + 0]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) a->x0[n] = a->y0[n - 1]; - a->y0[n] = a->b0 * a->x0[n] - + a->b1 * a->x1[n] - - a->a1 * a->y1[n]; - a->y1[n] = a->y0[n]; - a->x1[n] = a->x0[n]; - } - - a->out[2 * i + 0] = a->y0[a->nstages - 1]; - } - } - else if (a->out != a->in) - { - std::copy( a->in, a->in + a->size * 2, a->out); - } -} - -void PHROT::setBuffers_phrot (PHROT *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void PHROT::setSamplerate_phrot (PHROT *a, int rate) -{ - decalc_phrot (a); - a->rate = rate; - calc_phrot (a); -} - -void PHROT::setSize_phrot (PHROT *a, int size) -{ - a->size = size; - flush_phrot (a); -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -void PHROT::SetPHROTRun (TXA& txa, int run) -{ - PHROT *a = txa.phrot; - a->run = run; - - if (a->run) - flush_phrot (a); -} - -void PHROT::SetPHROTCorner (TXA& txa, double corner) -{ - PHROT *a = txa.phrot; - decalc_phrot (a); - a->fc = corner; - calc_phrot (a); -} - -void PHROT::SetPHROTNstages (TXA& txa, int nstages) -{ - PHROT *a = txa.phrot; - decalc_phrot (a); - a->nstages = nstages; - calc_phrot (a); -} - -void PHROT::SetPHROTReverse (TXA& txa, int reverse) -{ - PHROT *a = txa.phrot; - a->reverse = reverse; -} - -/******************************************************************************************************** -* * -* Complex Bi-Quad Low-Pass * -* * -********************************************************************************************************/ - -void BQLP::calc_bqlp(BQLP *a) -{ - double w0, cs, c, den; - w0 = TWOPI * a->fc / (double)a->rate; - cs = cos(w0); - c = sin(w0) / (2.0 * a->Q); - den = 1.0 + c; - a->a0 = 0.5 * (1.0 - cs) / den; - a->a1 = (1.0 - cs) / den; - a->a2 = 0.5 * (1.0 - cs) / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_bqlp(a); -} - -BQLP* BQLP::create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) -{ - BQLP *a = new BQLP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->Q = Q; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - calc_bqlp(a); - return a; -} - -void BQLP::destroy_bqlp(BQLP *a) -{ - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void BQLP::flush_bqlp(BQLP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) - { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; - } -} - -void BQLP::xbqlp(BQLP *a) -{ - if (a->run) - { - int i, j, n; - - for (i = 0; i < a->size; i++) - { - for (j = 0; j < 2; j++) - { - a->x0[j] = a->gain * a->in[2 * i + j]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; - } - - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; - } - } - } - else if (a->out != a->in) - { - std::copy(a->in, a->in + a->size * 2, a->out); - } -} - -void BQLP::setBuffers_bqlp(BQLP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void BQLP::setSamplerate_bqlp(BQLP *a, int rate) -{ - a->rate = rate; - calc_bqlp(a); -} - -void BQLP::setSize_bqlp(BQLP *a, int size) -{ - a->size = size; - flush_bqlp(a); -} - -/******************************************************************************************************** -* * -* Double Bi-Quad Low-Pass * -* * -********************************************************************************************************/ - -void DBQLP::calc_dbqlp(BQLP *a) -{ - float w0, cs, c, den; - w0 = TWOPI * a->fc / (float)a->rate; - cs = cos(w0); - c = sin(w0) / (2.0 * a->Q); - den = 1.0 + c; - a->a0 = 0.5 * (1.0 - cs) / den; - a->a1 = (1.0 - cs) / den; - a->a2 = 0.5 * (1.0 - cs) / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_dbqlp(a); -} - -BQLP* DBQLP::create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) -{ - BQLP *a = new BQLP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->Q = Q; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - calc_dbqlp(a); - return a; -} - -void DBQLP::destroy_dbqlp(BQLP *a) -{ - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void DBQLP::flush_dbqlp(BQLP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) - { - a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; - } -} - -void DBQLP::xdbqlp(BQLP *a) -{ - if (a->run) - { - int i, n; - - for (i = 0; i < a->size; i++) - { - a->x0[0] = a->gain * a->in[i]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[n] = a->y0[n - 1]; - - a->y0[n] = a->a0 * a->x0[n] - + a->a1 * a->x1[n] - + a->a2 * a->x2[n] - + a->b1 * a->y1[n] - + a->b2 * a->y2[n]; - a->y2[n] = a->y1[n]; - a->y1[n] = a->y0[n]; - a->x2[n] = a->x1[n]; - a->x1[n] = a->x0[n]; - } - - a->out[i] = a->y0[a->nstages - 1]; - } - } - else if (a->out != a->in) - { - memcpy(a->out, a->in, a->size * sizeof(float)); - } -} - -void DBQLP::setBuffers_dbqlp(BQLP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void DBQLP::setSamplerate_dbqlp(BQLP *a, int rate) -{ - a->rate = rate; - calc_dbqlp(a); -} - -void DBQLP::setSize_dbqlp(BQLP *a, int size) -{ - a->size = size; - flush_dbqlp(a); -} - - -/******************************************************************************************************** -* * -* Complex Bi-Quad Band-Pass * -* * -********************************************************************************************************/ - -void BQBP::calc_bqbp(BQBP *a) -{ - double f0, w0, bw, q, sn, cs, c, den; - bw = a->f_high - a->f_low; - f0 = (a->f_high + a->f_low) / 2.0; - q = f0 / bw; - w0 = TWOPI * f0 / a->rate; - sn = sin(w0); - cs = cos(w0); - c = sn / (2.0 * q); - den = 1.0 + c; - a->a0 = +c / den; - a->a1 = 0.0; - a->a2 = -c / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_bqbp(a); -} - -BQBP* BQBP::create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) -{ - BQBP *a = new BQBP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f_low = f_low; - a->f_high = f_high; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - calc_bqbp(a); - return a; -} - -void BQBP::destroy_bqbp(BQBP *a) -{ - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void BQBP::flush_bqbp(BQBP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) - { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; - } -} - -void BQBP::xbqbp(BQBP *a) -{ - if (a->run) - { - int i, j, n; - - for (i = 0; i < a->size; i++) - { - for (j = 0; j < 2; j++) - { - a->x0[j] = a->gain * a->in[2 * i + j]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; - } - - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; - } - } - } - else if (a->out != a->in) - { - std::copy(a->in, a->in + a->size * 2, a->out); - } -} - -void BQBP::setBuffers_bqbp(BQBP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void BQBP::setSamplerate_bqbp(BQBP *a, int rate) -{ - a->rate = rate; - calc_bqbp(a); -} - -void BQBP::setSize_bqbp(BQBP *a, int size) -{ - a->size = size; - flush_bqbp(a); -} - -/******************************************************************************************************** -* * -* Double Bi-Quad Band-Pass * -* * -********************************************************************************************************/ - -void BQBP::calc_dbqbp(BQBP *a) -{ - double f0, w0, bw, q, sn, cs, c, den; - bw = a->f_high - a->f_low; - f0 = (a->f_high + a->f_low) / 2.0; - q = f0 / bw; - w0 = TWOPI * f0 / a->rate; - sn = sin(w0); - cs = cos(w0); - c = sn / (2.0 * q); - den = 1.0 + c; - a->a0 = +c / den; - a->a1 = 0.0; - a->a2 = -c / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_dbqbp(a); -} - -BQBP* BQBP::create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) -{ - BQBP *a = new BQBP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f_low = f_low; - a->f_high = f_high; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - calc_dbqbp(a); - return a; -} - -void BQBP::destroy_dbqbp(BQBP *a) -{ - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void BQBP::flush_dbqbp(BQBP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) - { - a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; - } -} - -void BQBP::xdbqbp(BQBP *a) -{ - if (a->run) - { - int i, n; - - for (i = 0; i < a->size; i++) - { - a->x0[0] = a->gain * a->in[i]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[n] = a->y0[n - 1]; - - a->y0[n] = a->a0 * a->x0[n] - + a->a1 * a->x1[n] - + a->a2 * a->x2[n] - + a->b1 * a->y1[n] - + a->b2 * a->y2[n]; - a->y2[n] = a->y1[n]; - a->y1[n] = a->y0[n]; - a->x2[n] = a->x1[n]; - a->x1[n] = a->x0[n]; - } - - a->out[i] = a->y0[a->nstages - 1]; - } - } - else if (a->out != a->in) - { - memcpy(a->out, a->in, a->size * sizeof(float)); - } -} - -void BQBP::setBuffers_dbqbp(BQBP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void BQBP::setSamplerate_dbqbp(BQBP *a, int rate) -{ - a->rate = rate; - calc_dbqbp(a); -} - -void BQBP::setSize_dbqbp(BQBP *a, int size) -{ - a->size = size; - flush_dbqbp(a); -} - -/******************************************************************************************************** -* * -* Complex Single-Pole High-Pass * -* * -********************************************************************************************************/ - -void SPHP::calc_sphp(SPHP *a) -{ - double g; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - g = exp(-TWOPI * a->fc / a->rate); - a->b0 = +0.5 * (1.0 + g); - a->b1 = -0.5 * (1.0 + g); - a->a1 = -g; -} - -SPHP* SPHP::create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) -{ - SPHP *a = new SPHP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_sphp(a); - return a; -} - -void SPHP::decalc_sphp(SPHP *a) -{ - delete[](a->y1); - delete[](a->y0); - delete[](a->x1); - delete[](a->x0); -} - -void SPHP::destroy_sphp(SPHP *a) -{ - decalc_sphp(a); - delete(a); -} - -void SPHP::flush_sphp(SPHP *a) -{ - std::fill(a->x0, a->x0 + a->nstages * 2, 0); - std::fill(a->x1, a->x0 + a->nstages * 2, 0); - std::fill(a->y0, a->x0 + a->nstages * 2, 0); - std::fill(a->y1, a->x0 + a->nstages * 2, 0); -} - -void SPHP::xsphp(SPHP *a) -{ - if (a->run) - { - int i, j, n; - for (i = 0; i < a->size; i++) - { - for (j = 0; j < 2; j++) - { - a->x0[j] = a->in[2 * i + j]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - - a->y0[2 * n + j] = a->b0 * a->x0[2 * n + j] - + a->b1 * a->x1[2 * n + j] - - a->a1 * a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; - } - - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; - } - } - } - else if (a->out != a->in) - { - std::copy(a->in, a->in + a->size * 2, a->out); - } -} - -void SPHP::setBuffers_sphp(SPHP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void SPHP::setSamplerate_sphp(SPHP *a, int rate) -{ - decalc_sphp(a); - a->rate = rate; - calc_sphp(a); -} - -void SPHP::setSize_sphp(SPHP *a, int size) -{ - a->size = size; - flush_sphp(a); -} - -/******************************************************************************************************** -* * -* Double Single-Pole High-Pass * -* * -********************************************************************************************************/ - -void SPHP::calc_dsphp(SPHP *a) -{ - double g; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - g = exp(-TWOPI * a->fc / a->rate); - a->b0 = +0.5 * (1.0 + g); - a->b1 = -0.5 * (1.0 + g); - a->a1 = -g; -} - -SPHP* SPHP::create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) -{ - SPHP *a = new SPHP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_dsphp(a); - return a; -} - -void SPHP::decalc_dsphp(SPHP *a) -{ - delete[](a->y1); - delete[](a->y0); - delete[](a->x1); - delete[](a->x0); -} - -void SPHP::destroy_dsphp(SPHP *a) -{ - decalc_dsphp(a); - delete(a); -} - -void SPHP::flush_dsphp(SPHP *a) -{ - memset(a->x0, 0, a->nstages * sizeof(float)); - memset(a->x1, 0, a->nstages * sizeof(float)); - memset(a->y0, 0, a->nstages * sizeof(float)); - memset(a->y1, 0, a->nstages * sizeof(float)); -} - -void SPHP::xdsphp(SPHP *a) -{ - if (a->run) - { - int i, n; - - for (i = 0; i < a->size; i++) - { - a->x0[0] = a->in[i]; - - for (n = 0; n < a->nstages; n++) - { - if (n > 0) - a->x0[n] = a->y0[n - 1]; - - a->y0[n] = a->b0 * a->x0[n] - + a->b1 * a->x1[n] - - a->a1 * a->y1[n]; - a->y1[n] = a->y0[n]; - a->x1[n] = a->x0[n]; - } - - a->out[i] = a->y0[a->nstages - 1]; - } - } - else if (a->out != a->in) - { - memcpy(a->out, a->in, a->size * sizeof(float)); - } -} - -void SPHP::setBuffers_dsphp(SPHP *a, float* in, float* out) -{ - a->in = in; - a->out = out; -} - -void SPHP::setSamplerate_dsphp(SPHP *a, int rate) -{ - decalc_dsphp(a); - a->rate = rate; - calc_dsphp(a); -} - -void SPHP::setSize_dsphp(SPHP *a, int size) -{ - a->size = size; - flush_dsphp(a); -} - -} // namespace WDSP diff --git a/wdsp/iir.hpp b/wdsp/iir.hpp deleted file mode 100644 index d6ba5cfce..000000000 --- a/wdsp/iir.hpp +++ /dev/null @@ -1,440 +0,0 @@ -/* iir.h - -This file is part of a program that implements a Software-Defined Radio. - -Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V -Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel - -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 - -*/ - -/******************************************************************************************************** -* * -* Bi-Quad Notch * -* * -********************************************************************************************************/ - -#ifndef wdsp_snotch_h -#define wdsp_snotch_h - -#include "export.h" - -namespace WDSP { - -class WDSP_API SNOTCH -{ -public: - int run; - int size; - float* in; - float* out; - double rate; - double f; - double bw; - double a0, a1, a2, b1, b2; - double x0, x1, x2, y1, y2; - - static SNOTCH* create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw); - static void destroy_snotch (SNOTCH *a); - static void flush_snotch (SNOTCH *a); - static void xsnotch (SNOTCH *a); - static void setBuffers_snotch (SNOTCH *a, float* in, float* out); - static void setSamplerate_snotch (SNOTCH *a, int rate); - static void setSize_snotch (SNOTCH *a, int size); - static void SetSNCTCSSFreq (SNOTCH *a, double freq); - static void SetSNCTCSSRun (SNOTCH *a, int run); - -private: - static void calc_snotch (SNOTCH *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Complex Bi-Quad Peaking * -* * -********************************************************************************************************/ - -#ifndef wdsp_speak_h -#define wdsp_speak_h - -#include "export.h" - -namespace WDSP { - -class RXA; - -class WDSP_API SPEAK -{ -public: - int run; - int size; - float* in; - float* out; - double rate; - double f; - double bw; - double cbw; - double gain; - double fgain; - int nstages; - int design; - double a0, a1, a2, b1, b2; - double *x0, *x1, *x2, *y0, *y1, *y2; - - static SPEAK* create_speak ( - int run, - int size, - float* in, - float* out, - int rate, - double f, - double bw, - double gain, - int nstages, - int design - ); - static void destroy_speak (SPEAK *a); - static void flush_speak (SPEAK *a); - static void xspeak (SPEAK *a); - static void setBuffers_speak (SPEAK *a, float* in, float* out); - static void setSamplerate_speak (SPEAK *a, int rate); - static void setSize_speak (SPEAK *a, int size); - // RXA - static void SetSPCWRun (RXA& rxa, int run); - static void SetSPCWFreq (RXA& rxa, double freq); - static void SetSPCWBandwidth (RXA& rxa, double bw); - static void SetSPCWGain (RXA& rxa, double gain); - static void calc_speak (SPEAK *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Complex Multiple Peaking * -* * -********************************************************************************************************/ - -#ifndef _mpeak_h -#define _mpeak_h - -#include "export.h" - -namespace WDSP { - -class RXA; - -class WDSP_API MPEAK -{ -public: - int run; - int size; - float* in; - float* out; - int rate; - int npeaks; - int* enable; - double* f; - double* bw; - double* gain; - int nstages; - SPEAK** pfil; - float* tmp; - float* mix; - - static MPEAK* create_mpeak ( - int run, - int size, - float* in, - float* out, - int rate, - int npeaks, - int* enable, - double* f, - double* bw, - double* gain, - int nstages - ); - static void destroy_mpeak (MPEAK *a); - static void flush_mpeak (MPEAK *a); - static void xmpeak (MPEAK *a); - static void setBuffers_mpeak (MPEAK *a, float* in, float* out); - static void setSamplerate_mpeak (MPEAK *a, int rate); - static void setSize_mpeak (MPEAK *a, int size); - // RXA - static void SetmpeakRun (RXA& rxa, int run); - static void SetmpeakNpeaks (RXA& rxa, int npeaks); - static void SetmpeakFilEnable (RXA& rxa, int fil, int enable); - static void SetmpeakFilFreq (RXA& rxa, int fil, double freq); - static void SetmpeakFilBw (RXA& rxa, int fil, double bw); - static void SetmpeakFilGain (RXA& rxa, int fil, double gain); - -private: - static void calc_mpeak (MPEAK *a); - static void decalc_mpeak (MPEAK *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Phase Rotator * -* * -********************************************************************************************************/ - -#ifndef wdsp_phrot_h -#define wdsp_phrot_h - -#include "export.h" - -namespace WDSP { - -class TXA; - -class WDSP_API PHROT -{ -public: - int reverse; - int run; - int size; - float* in; - float* out; - int rate; - double fc; - int nstages; - // normalized such that a0 = 1 - double a1, b0, b1; - double *x0, *x1, *y0, *y1; - - static PHROT* create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages); - static void destroy_phrot (PHROT *a); - static void flush_phrot (PHROT *a); - static void xphrot (PHROT *a); - static void setBuffers_phrot (PHROT *a, float* in, float* out); - static void setSamplerate_phrot (PHROT *a, int rate); - static void setSize_phrot (PHROT *a, int size); - // TXA Properties - static void SetPHROTRun (TXA& txa, int run); - static void SetPHROTCorner (TXA& txa, double corner); - static void SetPHROTNstages (TXA& txa, int nstages); - static void SetPHROTReverse (TXA& txa, int reverse); - -private: - static void calc_phrot (PHROT *a); - static void decalc_phrot (PHROT *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Complex Bi-Quad Low-Pass * -* * -********************************************************************************************************/ - -#ifndef wdsp_bqlp_h -#define wdsp_bqlp_h - -#include "export.h" - -namespace WDSP { - -class WDSP_API BQLP -{ -public: - int run; - int size; - float* in; - float* out; - double rate; - double fc; - double Q; - double gain; - int nstages; - double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; - - static BQLP* create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); - static void destroy_bqlp(BQLP *a); - static void flush_bqlp(BQLP *a); - static void xbqlp(BQLP *a); - static void setBuffers_bqlp(BQLP *a, float* in, float* out); - static void setSamplerate_bqlp(BQLP *a, int rate); - static void setSize_bqlp(BQLP *a, int size); - -private: - static void calc_bqlp(BQLP *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Double Bi-Quad Low-Pass * -* * -********************************************************************************************************/ - -#ifndef wdsp_dbqlp_h -#define wdsp_dbqlp_h - -#include "export.h" - -namespace WDSP { - -class WDSP_API DBQLP -{ -public: - static BQLP* create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); - static void destroy_dbqlp(BQLP *a); - static void flush_dbqlp(BQLP *a); - static void xdbqlp(BQLP *a); - static void setBuffers_dbqlp(BQLP *a, float* in, float* out); - static void setSamplerate_dbqlp(BQLP *a, int rate); - static void setSize_dbqlp(BQLP *a, int size); - -private: - static void calc_dbqlp(BQLP *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Complex Bi-Quad Band-Pass * -* * -********************************************************************************************************/ - -#ifndef wdsp_bqbp_h -#define wdsp_bqbp_h - -#include "export.h" - -namespace WDSP { - -class WDSP_API BQBP -{ -public: - int run; - int size; - float* in; - float* out; - double rate; - double f_low; - double f_high; - double gain; - int nstages; - double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; - - static BQBP* create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); - static void destroy_bqbp(BQBP *a); - static void flush_bqbp(BQBP *a); - static void xbqbp(BQBP *a); - static void setBuffers_bqbp(BQBP *a, float* in, float* out); - static void setSamplerate_bqbp(BQBP *a, int rate); - static void setSize_bqbp(BQBP *a, int size); - - // Double Bi-Quad Band-Pass - static BQBP* create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); - static void destroy_dbqbp(BQBP *a); - static void flush_dbqbp(BQBP *a); - static void xdbqbp(BQBP *a); - static void setBuffers_dbqbp(BQBP *a, float* in, float* out); - static void setSamplerate_dbqbp(BQBP *a, int rate); - static void setSize_dbqbp(BQBP *a, int size); - -private: - static void calc_bqbp(BQBP *a); - static void calc_dbqbp(BQBP *a); -}; - -} // namespace WDSP - -#endif - -/******************************************************************************************************** -* * -* Double Single-Pole High-Pass * -* * -********************************************************************************************************/ - -#ifndef wdsp_dsphp_h -#define wdsp_dsphp_h - -#include "export.h" - -namespace WDSP { - -class WDSP_API SPHP -{ -public: - int run; - int size; - float* in; - float* out; - double rate; - double fc; - int nstages; - double a1, b0, b1; - double* x0, * x1, * y0, * y1; - - static SPHP* create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); - static void destroy_dsphp(SPHP *a); - static void flush_dsphp(SPHP *a); - static void xdsphp(SPHP *a); - static void setBuffers_dsphp(SPHP *a, float* in, float* out); - static void setSamplerate_dsphp(SPHP *a, int rate); - static void setSize_dsphp(SPHP *a, int size); - - // Complex Single-Pole High-Pass - static SPHP* create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); - static void destroy_sphp(SPHP *a); - static void flush_sphp(SPHP *a); - static void xsphp(SPHP *a); - static void setBuffers_sphp(SPHP *a, float* in, float* out); - static void setSamplerate_sphp(SPHP *a, int rate); - static void setSize_sphp(SPHP *a, int size); - -private: - static void calc_sphp(SPHP *a); - static void decalc_sphp(SPHP *a); - static void calc_dsphp(SPHP *a); - static void decalc_dsphp(SPHP *a); -}; - -} // namespace WDSP - -#endif - - diff --git a/wdsp/mpeak.cpp b/wdsp/mpeak.cpp new file mode 100644 index 000000000..6c91e94c5 --- /dev/null +++ b/wdsp/mpeak.cpp @@ -0,0 +1,221 @@ +/* mpeak.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "mpeak.hpp" +#include "speak.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Complex Multiple Peaking * +* * +********************************************************************************************************/ + +void MPEAK::calc_mpeak (MPEAK *a) +{ + int i; + a->tmp = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); + a->mix = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); + for (i = 0; i < a->npeaks; i++) + { + a->pfil[i] = SPEAK::create_speak ( + 1, + a->size, + a->in, + a->tmp, + a->rate, + a->f[i], + a->bw[i], + a->gain[i], + a->nstages, + 1 + ); + } +} + +void MPEAK::decalc_mpeak (MPEAK *a) +{ + int i; + for (i = 0; i < a->npeaks; i++) + SPEAK::destroy_speak (a->pfil[i]); + delete[] (a->mix); + delete[] (a->tmp); +} + +MPEAK* MPEAK::create_mpeak ( + int run, + int size, + float* in, + float* out, + int rate, + int npeaks, + int* enable, + double* f, + double* bw, + double* gain, + int nstages +) +{ + MPEAK *a = new MPEAK; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->npeaks = npeaks; + a->nstages = nstages; + a->enable = new int[a->npeaks]; // (int *) malloc0 (a->npeaks * sizeof (int)); + a->f = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); + a->bw = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); + a->gain = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); + memcpy (a->enable, enable, a->npeaks * sizeof (int)); + memcpy (a->f, f, a->npeaks * sizeof (double)); + memcpy (a->bw, bw, a->npeaks * sizeof (double)); + memcpy (a->gain, gain, a->npeaks * sizeof (double)); + a->pfil = new SPEAK*[a->npeaks]; // (SPEAK *) malloc0 (a->npeaks * sizeof (SPEAK)); + calc_mpeak (a); + return a; +} + +void MPEAK::destroy_mpeak (MPEAK *a) +{ + decalc_mpeak (a); + delete[] (a->pfil); + delete[] (a->gain); + delete[] (a->bw); + delete[] (a->f); + delete[] (a->enable); + delete (a); +} + +void MPEAK::flush_mpeak (MPEAK *a) +{ + int i; + for (i = 0; i < a->npeaks; i++) + SPEAK::flush_speak (a->pfil[i]); +} + +void MPEAK::xmpeak (MPEAK *a) +{ + if (a->run) + { + int i, j; + std::fill(a->mix, a->mix + a->size * 2, 0); + + for (i = 0; i < a->npeaks; i++) + { + if (a->enable[i]) + { + SPEAK::xspeak (a->pfil[i]); + for (j = 0; j < 2 * a->size; j++) + a->mix[j] += a->tmp[j]; + } + } + + std::copy(a->mix, a->mix + a->size * 2, a->out); + } + else if (a->in != a->out) + { + std::copy( a->in, a->in + a->size * 2, a->out); + } +} + +void MPEAK::setBuffers_mpeak (MPEAK *a, float* in, float* out) +{ + decalc_mpeak (a); + a->in = in; + a->out = out; + calc_mpeak (a); +} + +void MPEAK::setSamplerate_mpeak (MPEAK *a, int rate) +{ + decalc_mpeak (a); + a->rate = rate; + calc_mpeak (a); +} + +void MPEAK::setSize_mpeak (MPEAK *a, int size) +{ + decalc_mpeak (a); + a->size = size; + calc_mpeak (a); +} + +/******************************************************************************************************** +* * +* RXA Properties * +* * +********************************************************************************************************/ + +void MPEAK::SetmpeakRun (RXA& rxa, int run) +{ + MPEAK *a = rxa.mpeak; + a->run = run; +} + +void MPEAK::SetmpeakNpeaks (RXA& rxa, int npeaks) +{ + MPEAK *a = rxa.mpeak; + a->npeaks = npeaks; +} + +void MPEAK::SetmpeakFilEnable (RXA& rxa, int fil, int enable) +{ + MPEAK *a = rxa.mpeak; + a->enable[fil] = enable; +} + +void MPEAK::SetmpeakFilFreq (RXA& rxa, int fil, double freq) +{ + MPEAK *a = rxa.mpeak; + a->f[fil] = freq; + a->pfil[fil]->f = freq; + SPEAK::calc_speak(a->pfil[fil]); +} + +void MPEAK::SetmpeakFilBw (RXA& rxa, int fil, double bw) +{ + MPEAK *a = rxa.mpeak; + a->bw[fil] = bw; + a->pfil[fil]->bw = bw; + SPEAK::calc_speak(a->pfil[fil]); +} + +void MPEAK::SetmpeakFilGain (RXA& rxa, int fil, double gain) +{ + MPEAK *a = rxa.mpeak; + a->gain[fil] = gain; + a->pfil[fil]->gain = gain; + SPEAK::calc_speak(a->pfil[fil]); +} + +} // namespace WDSP diff --git a/wdsp/mpeak.hpp b/wdsp/mpeak.hpp new file mode 100644 index 000000000..6a88f631a --- /dev/null +++ b/wdsp/mpeak.hpp @@ -0,0 +1,96 @@ +/* mpeak.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Complex Multiple Peaking * +* * +********************************************************************************************************/ + +#ifndef _mpeak_h +#define _mpeak_h + +#include "export.h" + +namespace WDSP { + +class RXA; +class SPEAK; + +class WDSP_API MPEAK +{ +public: + int run; + int size; + float* in; + float* out; + int rate; + int npeaks; + int* enable; + double* f; + double* bw; + double* gain; + int nstages; + SPEAK** pfil; + float* tmp; + float* mix; + + static MPEAK* create_mpeak ( + int run, + int size, + float* in, + float* out, + int rate, + int npeaks, + int* enable, + double* f, + double* bw, + double* gain, + int nstages + ); + static void destroy_mpeak (MPEAK *a); + static void flush_mpeak (MPEAK *a); + static void xmpeak (MPEAK *a); + static void setBuffers_mpeak (MPEAK *a, float* in, float* out); + static void setSamplerate_mpeak (MPEAK *a, int rate); + static void setSize_mpeak (MPEAK *a, int size); + // RXA + static void SetmpeakRun (RXA& rxa, int run); + static void SetmpeakNpeaks (RXA& rxa, int npeaks); + static void SetmpeakFilEnable (RXA& rxa, int fil, int enable); + static void SetmpeakFilFreq (RXA& rxa, int fil, double freq); + static void SetmpeakFilBw (RXA& rxa, int fil, double bw); + static void SetmpeakFilGain (RXA& rxa, int fil, double gain); + +private: + static void calc_mpeak (MPEAK *a); + static void decalc_mpeak (MPEAK *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/phrot.cpp b/wdsp/phrot.cpp new file mode 100644 index 000000000..a233db27d --- /dev/null +++ b/wdsp/phrot.cpp @@ -0,0 +1,182 @@ +/* phrot.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "phrot.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Phase Rotator * +* * +********************************************************************************************************/ + +void PHROT::calc_phrot (PHROT *a) +{ + double g; + a->x0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); + a->x1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); + a->y0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); + a->y1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); + g = tan (PI * a->fc / (float)a->rate); + a->b0 = (g - 1.0) / (g + 1.0); + a->b1 = 1.0; + a->a1 = a->b0; +} + +void PHROT::decalc_phrot (PHROT *a) +{ + delete[] (a->y1); + delete[] (a->y0); + delete[] (a->x1); + delete[] (a->x0); +} + +PHROT* PHROT::create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages) +{ + PHROT *a = new PHROT; + a->reverse = 0; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->fc = fc; + a->nstages = nstages; + calc_phrot (a); + return a; +} + +void PHROT::destroy_phrot (PHROT *a) +{ + decalc_phrot (a); + delete (a); +} + +void PHROT::flush_phrot (PHROT *a) +{ + memset (a->x0, 0, a->nstages * sizeof (double)); + memset (a->x1, 0, a->nstages * sizeof (double)); + memset (a->y0, 0, a->nstages * sizeof (double)); + memset (a->y1, 0, a->nstages * sizeof (double)); +} + +void PHROT::xphrot (PHROT *a) +{ + if (a->reverse) + { + for (int i = 0; i < a->size; i++) + a->in[2 * i + 0] = -a->in[2 * i + 0]; + } + + if (a->run) + { + int i, n; + + for (i = 0; i < a->size; i++) + { + a->x0[0] = a->in[2 * i + 0]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) a->x0[n] = a->y0[n - 1]; + a->y0[n] = a->b0 * a->x0[n] + + a->b1 * a->x1[n] + - a->a1 * a->y1[n]; + a->y1[n] = a->y0[n]; + a->x1[n] = a->x0[n]; + } + + a->out[2 * i + 0] = a->y0[a->nstages - 1]; + } + } + else if (a->out != a->in) + { + std::copy( a->in, a->in + a->size * 2, a->out); + } +} + +void PHROT::setBuffers_phrot (PHROT *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void PHROT::setSamplerate_phrot (PHROT *a, int rate) +{ + decalc_phrot (a); + a->rate = rate; + calc_phrot (a); +} + +void PHROT::setSize_phrot (PHROT *a, int size) +{ + a->size = size; + flush_phrot (a); +} + +/******************************************************************************************************** +* * +* TXA Properties * +* * +********************************************************************************************************/ + +void PHROT::SetPHROTRun (TXA& txa, int run) +{ + PHROT *a = txa.phrot; + a->run = run; + + if (a->run) + flush_phrot (a); +} + +void PHROT::SetPHROTCorner (TXA& txa, double corner) +{ + PHROT *a = txa.phrot; + decalc_phrot (a); + a->fc = corner; + calc_phrot (a); +} + +void PHROT::SetPHROTNstages (TXA& txa, int nstages) +{ + PHROT *a = txa.phrot; + decalc_phrot (a); + a->nstages = nstages; + calc_phrot (a); +} + +void PHROT::SetPHROTReverse (TXA& txa, int reverse) +{ + PHROT *a = txa.phrot; + a->reverse = reverse; +} + +} // namespace WDSP diff --git a/wdsp/phrot.hpp b/wdsp/phrot.hpp new file mode 100644 index 000000000..ef0f2dbf3 --- /dev/null +++ b/wdsp/phrot.hpp @@ -0,0 +1,78 @@ +/* phrot.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Phase Rotator * +* * +********************************************************************************************************/ + +#ifndef wdsp_phrot_h +#define wdsp_phrot_h + +#include "export.h" + +namespace WDSP { + +class TXA; + +class WDSP_API PHROT +{ +public: + int reverse; + int run; + int size; + float* in; + float* out; + int rate; + double fc; + int nstages; + // normalized such that a0 = 1 + double a1, b0, b1; + double *x0, *x1, *y0, *y1; + + static PHROT* create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages); + static void destroy_phrot (PHROT *a); + static void flush_phrot (PHROT *a); + static void xphrot (PHROT *a); + static void setBuffers_phrot (PHROT *a, float* in, float* out); + static void setSamplerate_phrot (PHROT *a, int rate); + static void setSize_phrot (PHROT *a, int size); + // TXA Properties + static void SetPHROTRun (TXA& txa, int run); + static void SetPHROTCorner (TXA& txa, double corner); + static void SetPHROTNstages (TXA& txa, int nstages); + static void SetPHROTReverse (TXA& txa, int reverse); + +private: + static void calc_phrot (PHROT *a); + static void decalc_phrot (PHROT *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/snotch.cpp b/wdsp/snotch.cpp new file mode 100644 index 000000000..8e799e6c4 --- /dev/null +++ b/wdsp/snotch.cpp @@ -0,0 +1,136 @@ +/* iir.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "snotch.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Bi-Quad Notch * +* * +********************************************************************************************************/ + +void SNOTCH::calc_snotch (SNOTCH *a) +{ + double fn, qk, qr, csn; + fn = a->f / (float)a->rate; + csn = cos (TWOPI * fn); + qr = 1.0 - 3.0 * a->bw; + qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); + a->a0 = + qk; + a->a1 = - 2.0 * qk * csn; + a->a2 = + qk; + a->b1 = + 2.0 * qr * csn; + a->b2 = - qr * qr; + flush_snotch (a); +} + +SNOTCH* SNOTCH::create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw) +{ + SNOTCH *a = new SNOTCH; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->f = f; + a->bw = bw; + calc_snotch (a); + return a; +} + +void SNOTCH::destroy_snotch (SNOTCH *a) +{ + delete (a); +} + +void SNOTCH::flush_snotch (SNOTCH *a) +{ + a->x1 = a->x2 = a->y1 = a->y2 = 0.0; +} + +void SNOTCH::xsnotch (SNOTCH *a) +{ + if (a->run) + { + int i; + for (i = 0; i < a->size; i++) + { + a->x0 = a->in[2 * i + 0]; + a->out[2 * i + 0] = a->a0 * a->x0 + a->a1 * a->x1 + a->a2 * a->x2 + a->b1 * a->y1 + a->b2 * a->y2; + a->y2 = a->y1; + a->y1 = a->out[2 * i + 0]; + a->x2 = a->x1; + a->x1 = a->x0; + } + } + else if (a->out != a->in) + { + std::copy( a->in, a->in + a->size * 2, a->out); + } +} + +void SNOTCH::setBuffers_snotch (SNOTCH *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void SNOTCH::setSamplerate_snotch (SNOTCH *a, int rate) +{ + a->rate = rate; + calc_snotch (a); +} + +void SNOTCH::setSize_snotch (SNOTCH *a, int size) +{ + a->size = size; + flush_snotch (a); +} + +/******************************************************************************************************** +* * +* RXA Properties * +* * +********************************************************************************************************/ + +void SNOTCH::SetSNCTCSSFreq (SNOTCH *a, double freq) +{ + a->f = freq; + calc_snotch (a); +} + +void SNOTCH::SetSNCTCSSRun (SNOTCH *a, int run) +{ + a->run = run; +} + +} // namespace WDSP diff --git a/wdsp/snotch.hpp b/wdsp/snotch.hpp new file mode 100644 index 000000000..39c917a8d --- /dev/null +++ b/wdsp/snotch.hpp @@ -0,0 +1,70 @@ +/* snotch.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Bi-Quad Notch * +* * +********************************************************************************************************/ + +#ifndef wdsp_snotch_h +#define wdsp_snotch_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API SNOTCH +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double f; + double bw; + double a0, a1, a2, b1, b2; + double x0, x1, x2, y1, y2; + + static SNOTCH* create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw); + static void destroy_snotch (SNOTCH *a); + static void flush_snotch (SNOTCH *a); + static void xsnotch (SNOTCH *a); + static void setBuffers_snotch (SNOTCH *a, float* in, float* out); + static void setSamplerate_snotch (SNOTCH *a, int rate); + static void setSize_snotch (SNOTCH *a, int size); + static void SetSNCTCSSFreq (SNOTCH *a, double freq); + static void SetSNCTCSSRun (SNOTCH *a, int run); + +private: + static void calc_snotch (SNOTCH *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/speak.cpp b/wdsp/speak.cpp new file mode 100644 index 000000000..a4b5a461a --- /dev/null +++ b/wdsp/speak.cpp @@ -0,0 +1,260 @@ +/* speak.cpp + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "speak.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Complex Bi-Quad Peaking * +* * +********************************************************************************************************/ + +void SPEAK::calc_speak (SPEAK *a) +{ + double ratio; + double f_corr, g_corr, bw_corr, bw_parm, A, f_min; + + switch (a->design) + { + case 0: + ratio = a->bw / a->f; + switch (a->nstages) + { + case 4: + bw_parm = 2.4; + f_corr = 1.0 - 0.160 * ratio + 1.440 * ratio * ratio; + g_corr = 1.0 - 1.003 * ratio + 3.990 * ratio * ratio; + break; + default: + bw_parm = 1.0; + f_corr = 1.0; + g_corr = 1.0; + break; + } + { + double fn, qk, qr, csn; + a->fgain = a->gain / g_corr; + fn = a->f / (double)a->rate / f_corr; + csn = cos (TWOPI * fn); + qr = 1.0 - 3.0 * a->bw / (double)a->rate * bw_parm; + qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); + a->a0 = 1.0 - qk; + a->a1 = 2.0 * (qk - qr) * csn; + a->a2 = qr * qr - qk; + a->b1 = 2.0 * qr * csn; + a->b2 = - qr * qr; + } + break; + + case 1: + if (a->f < 200.0) a->f = 200.0; + ratio = a->bw / a->f; + switch (a->nstages) + { + case 4: + bw_parm = 5.0; + bw_corr = 1.13 * ratio - 0.956 * ratio * ratio; + A = 2.5; + f_min = 50.0; + break; + default: + bw_parm = 1.0; + bw_corr = 1.0; + g_corr = 1.0; + A = 2.5; + f_min = 50.0; + break; + } + { + double w0, sn, c, den; + if (a->f < f_min) a->f = f_min; + w0 = TWOPI * a->f / (double)a->rate; + sn = sin (w0); + a->cbw = bw_corr * a->f; + c = sn * sinh(0.5 * log((a->f + 0.5 * a->cbw * bw_parm) / (a->f - 0.5 * a->cbw * bw_parm)) * w0 / sn); + den = 1.0 + c / A; + a->a0 = (1.0 + c * A) / den; + a->a1 = - 2.0 * cos (w0) / den; + a->a2 = (1 - c * A) / den; + a->b1 = - a->a1; + a->b2 = - (1 - c / A ) / den; + a->fgain = a->gain / pow (A * A, (double)a->nstages); + } + break; + } + flush_speak (a); +} + +SPEAK* SPEAK::create_speak ( + int run, + int size, + float* in, + float* out, + int rate, + double f, + double bw, + double gain, + int nstages, + int design +) +{ + SPEAK *a = new SPEAK; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->f = f; + a->bw = bw; + a->gain = gain; + a->nstages = nstages; + a->design = design; + a->x0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); + a->x1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); + a->x2 = new double[a->nstages * 2]; //(float *) malloc0 (a->nstages * sizeof (complex)); + a->y0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); + a->y1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); + a->y2 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); + calc_speak (a); + return a; +} + +void SPEAK::destroy_speak (SPEAK *a) +{ + delete[] (a->y2); + delete[] (a->y1); + delete[] (a->y0); + delete[] (a->x2); + delete[] (a->x1); + delete[] (a->x0); + delete (a); +} + +void SPEAK::flush_speak (SPEAK *a) +{ + int i; + for (i = 0; i < a->nstages; i++) + { + a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; + a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + } +} + +void SPEAK::xspeak (SPEAK *a) +{ + if (a->run) + { + int i, j, n; + + for (i = 0; i < a->size; i++) + { + for (j = 0; j < 2; j++) + { + a->x0[j] = a->fgain * a->in[2 * i + j]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] + + a->a1 * a->x1[2 * n + j] + + a->a2 * a->x2[2 * n + j] + + a->b1 * a->y1[2 * n + j] + + a->b2 * a->y2[2 * n + j]; + a->y2[2 * n + j] = a->y1[2 * n + j]; + a->y1[2 * n + j] = a->y0[2 * n + j]; + a->x2[2 * n + j] = a->x1[2 * n + j]; + a->x1[2 * n + j] = a->x0[2 * n + j]; + } + + a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + } + } + } + else if (a->out != a->in) + { + std::copy( a->in, a->in + a->size * 2, a->out); + } +} + +void SPEAK::setBuffers_speak (SPEAK *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void SPEAK::setSamplerate_speak (SPEAK *a, int rate) +{ + a->rate = rate; + calc_speak (a); +} + +void SPEAK::setSize_speak (SPEAK *a, int size) +{ + a->size = size; + flush_speak (a); +} + +/******************************************************************************************************** +* * +* RXA Properties * +* * +********************************************************************************************************/ + +void SPEAK::SetSPCWRun (RXA& rxa, int run) +{ + SPEAK *a = rxa.speak; + a->run = run; +} + +void SPEAK::SetSPCWFreq (RXA& rxa, double freq) +{ + SPEAK *a = rxa.speak; + a->f = freq; + calc_speak (a); +} + +void SPEAK::SetSPCWBandwidth (RXA& rxa, double bw) +{ + SPEAK *a = rxa.speak; + a->bw = bw; + calc_speak (a); +} + +void SPEAK::SetSPCWGain (RXA& rxa, double gain) +{ + SPEAK *a = rxa.speak; + a->gain = gain; + calc_speak (a); +} + +} // namespace WDSP diff --git a/wdsp/speak.hpp b/wdsp/speak.hpp new file mode 100644 index 000000000..9642d0ad1 --- /dev/null +++ b/wdsp/speak.hpp @@ -0,0 +1,89 @@ +/* speak.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Complex Bi-Quad Peaking * +* * +********************************************************************************************************/ + +#ifndef wdsp_speak_h +#define wdsp_speak_h + +#include "export.h" + +namespace WDSP { + +class RXA; + +class WDSP_API SPEAK +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double f; + double bw; + double cbw; + double gain; + double fgain; + int nstages; + int design; + double a0, a1, a2, b1, b2; + double *x0, *x1, *x2, *y0, *y1, *y2; + + static SPEAK* create_speak ( + int run, + int size, + float* in, + float* out, + int rate, + double f, + double bw, + double gain, + int nstages, + int design + ); + static void destroy_speak (SPEAK *a); + static void flush_speak (SPEAK *a); + static void xspeak (SPEAK *a); + static void setBuffers_speak (SPEAK *a, float* in, float* out); + static void setSamplerate_speak (SPEAK *a, int rate); + static void setSize_speak (SPEAK *a, int size); + // RXA + static void SetSPCWRun (RXA& rxa, int run); + static void SetSPCWFreq (RXA& rxa, double freq); + static void SetSPCWBandwidth (RXA& rxa, double bw); + static void SetSPCWGain (RXA& rxa, double gain); + static void calc_speak (SPEAK *a); +}; + +} // namespace WDSP + +#endif diff --git a/wdsp/sphp.cpp b/wdsp/sphp.cpp new file mode 100644 index 000000000..88045dfea --- /dev/null +++ b/wdsp/sphp.cpp @@ -0,0 +1,141 @@ +/* iir.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 "sphp.hpp" +#include "RXA.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Complex Single-Pole High-Pass * +* * +********************************************************************************************************/ + +void SPHP::calc_sphp(SPHP *a) +{ + double g; + a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); + g = exp(-TWOPI * a->fc / a->rate); + a->b0 = +0.5 * (1.0 + g); + a->b1 = -0.5 * (1.0 + g); + a->a1 = -g; +} + +SPHP* SPHP::create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) +{ + SPHP *a = new SPHP; + a->run = run; + a->size = size; + a->in = in; + a->out = out; + a->rate = rate; + a->fc = fc; + a->nstages = nstages; + calc_sphp(a); + return a; +} + +void SPHP::decalc_sphp(SPHP *a) +{ + delete[](a->y1); + delete[](a->y0); + delete[](a->x1); + delete[](a->x0); +} + +void SPHP::destroy_sphp(SPHP *a) +{ + decalc_sphp(a); + delete(a); +} + +void SPHP::flush_sphp(SPHP *a) +{ + std::fill(a->x0, a->x0 + a->nstages * 2, 0); + std::fill(a->x1, a->x0 + a->nstages * 2, 0); + std::fill(a->y0, a->x0 + a->nstages * 2, 0); + std::fill(a->y1, a->x0 + a->nstages * 2, 0); +} + +void SPHP::xsphp(SPHP *a) +{ + if (a->run) + { + int i, j, n; + for (i = 0; i < a->size; i++) + { + for (j = 0; j < 2; j++) + { + a->x0[j] = a->in[2 * i + j]; + + for (n = 0; n < a->nstages; n++) + { + if (n > 0) + a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + + a->y0[2 * n + j] = a->b0 * a->x0[2 * n + j] + + a->b1 * a->x1[2 * n + j] + - a->a1 * a->y1[2 * n + j]; + a->y1[2 * n + j] = a->y0[2 * n + j]; + a->x1[2 * n + j] = a->x0[2 * n + j]; + } + + a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + } + } + } + else if (a->out != a->in) + { + std::copy(a->in, a->in + a->size * 2, a->out); + } +} + +void SPHP::setBuffers_sphp(SPHP *a, float* in, float* out) +{ + a->in = in; + a->out = out; +} + +void SPHP::setSamplerate_sphp(SPHP *a, int rate) +{ + decalc_sphp(a); + a->rate = rate; + calc_sphp(a); +} + +void SPHP::setSize_sphp(SPHP *a, int size) +{ + a->size = size; + flush_sphp(a); +} + +} // namespace WDSP diff --git a/wdsp/sphp.hpp b/wdsp/sphp.hpp new file mode 100644 index 000000000..970e6e92e --- /dev/null +++ b/wdsp/sphp.hpp @@ -0,0 +1,72 @@ +/* sphp.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2022, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Double Single-Pole High-Pass * +* * +********************************************************************************************************/ + +#ifndef wdsp_sphp_h +#define wdsp_sphp_h + +#include "export.h" + +namespace WDSP { + +class WDSP_API SPHP +{ +public: + int run; + int size; + float* in; + float* out; + double rate; + double fc; + int nstages; + double a1, b0, b1; + double* x0, * x1, * y0, * y1; + + // Complex Single-Pole High-Pass + static SPHP* create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); + static void destroy_sphp(SPHP *a); + static void flush_sphp(SPHP *a); + static void xsphp(SPHP *a); + static void setBuffers_sphp(SPHP *a, float* in, float* out); + static void setSamplerate_sphp(SPHP *a, int rate); + static void setSize_sphp(SPHP *a, int size); + +private: + static void calc_sphp(SPHP *a); + static void decalc_sphp(SPHP *a); +}; + +} // namespace WDSP + +#endif + + diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index 0d6283585..b46af706e 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -28,7 +28,7 @@ warren@pratt.one #include "comm.hpp" #include "cblock.hpp" #include "ssql.hpp" -#include "iir.hpp" +#include "dbqlp.hpp" #include "RXA.hpp" namespace WDSP { diff --git a/wdsp/ssql.hpp b/wdsp/ssql.hpp index 1927957d1..6b353dc9a 100644 --- a/wdsp/ssql.hpp +++ b/wdsp/ssql.hpp @@ -57,7 +57,7 @@ public: class CBL; class FTDV; -class BQLP; +class DBQLP; class RXA; class WDSP_API SSQL // Syllabic Squelch @@ -85,7 +85,7 @@ public: int* wdbuff; // buffer containing output of window detector CBL *dcbl; // pointer to DC Blocker data structure FTOV *cvtr; // pointer to F to V Converter data structure - BQLP *filt; // pointer to Bi-Quad Low-Pass Filter data structure + DBQLP *filt; // pointer to Bi-Quad Low-Pass Filter data structure int ftov_rsize; // ring size for f_to_v converter double ftov_fmax; // fmax for f_to_v converter // window detector From 71fe079ee3aedd75b0ad9dc5c4dc26a483b38b56 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 1 Aug 2024 00:31:28 +0200 Subject: [PATCH 27/46] WDSP: rework former IIR classes --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 8 +- wdsp/RXA.cpp | 28 +-- wdsp/TXA.cpp | 14 +- wdsp/bqbp.cpp | 143 ++++++++-------- wdsp/bqbp.hpp | 33 +++- wdsp/bqlp.cpp | 142 ++++++++-------- wdsp/bqlp.hpp | 33 +++- wdsp/dbqbp.cpp | 141 ++++++++-------- wdsp/dbqbp.hpp | 33 +++- wdsp/dbqlp.cpp | 139 ++++++++------- wdsp/dbqlp.hpp | 33 +++- wdsp/dsphp.cpp | 118 ++++++------- wdsp/dsphp.hpp | 34 ++-- wdsp/fmd.cpp | 20 ++- wdsp/mpeak.cpp | 204 ++++++++++------------ wdsp/mpeak.hpp | 50 +++--- wdsp/phrot.cpp | 157 ++++++++--------- wdsp/phrot.hpp | 41 +++-- wdsp/snotch.cpp | 105 ++++++------ wdsp/snotch.hpp | 31 ++-- wdsp/speak.cpp | 216 +++++++++++------------- wdsp/speak.hpp | 31 ++-- wdsp/sphp.cpp | 117 ++++++------- wdsp/sphp.hpp | 33 ++-- wdsp/ssql.cpp | 114 ++++++------- wdsp/ssql.hpp | 27 ++- 26 files changed, 1044 insertions(+), 1001 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 9edf7c2b5..dd35a30ae 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -571,19 +571,19 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // CW Peaking if ((m_settings.m_cwPeaking != settings.m_cwPeaking) || force) { - WDSP::SPEAK::SetSPCWRun(*m_rxa, settings.m_cwPeaking ? 1 : 0); + m_rxa->speak->setRun(settings.m_cwPeaking ? 1 : 0); } if ((m_settings.m_cwPeakFrequency != settings.m_cwPeakFrequency) || force) { - WDSP::SPEAK::SetSPCWFreq(*m_rxa, settings.m_cwPeakFrequency); + m_rxa->speak->setFreq(settings.m_cwPeakFrequency); } if ((m_settings.m_cwBandwidth != settings.m_cwBandwidth) || force) { - WDSP::SPEAK::SetSPCWBandwidth(*m_rxa, settings.m_cwBandwidth); + m_rxa->speak->setBandwidth(settings.m_cwBandwidth); } if ((m_settings.m_cwGain != settings.m_cwGain) || force) { - WDSP::SPEAK::SetSPCWGain(*m_rxa, settings.m_cwGain); + m_rxa->speak->setGain(settings.m_cwGain); } // Noise Blanker diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index b898722e5..7976cba16 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -482,7 +482,7 @@ RXA* RXA::create_rxa ( 0.02); // tau // CW peaking filter - rxa->speak = SPEAK::create_speak ( + rxa->speak = new SPEAK( 0, // run rxa->dsp_size, // buffer size, rxa->midbuff, // pointer to input buffer @@ -500,7 +500,7 @@ RXA* RXA::create_rxa ( double def_freq[2] = {2125.0, 2295.0}; double def_bw[2] = {75.0, 75.0}; double def_gain[2] = {1.0, 1.0}; - rxa->mpeak = MPEAK::create_mpeak ( + rxa->mpeak = new MPEAK( 0, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -567,8 +567,8 @@ void RXA::destroy_rxa (RXA *rxa) delete (rxa->rsmpout); PANEL::destroy_panel (rxa->panel); SSQL::destroy_ssql (rxa->ssql); - MPEAK::destroy_mpeak (rxa->mpeak); - SPEAK::destroy_speak (rxa->speak); + delete (rxa->mpeak); + delete (rxa->speak); delete (rxa->cbl); delete (rxa->sip1); delete (rxa->bp1); @@ -627,8 +627,8 @@ void RXA::flush_rxa (RXA *rxa) rxa->bp1->flush(); rxa->sip1->flush(); rxa->cbl->flush(); - SPEAK::flush_speak (rxa->speak); - MPEAK::flush_mpeak (rxa->mpeak); + rxa->speak->flush(); + rxa->mpeak->flush(); SSQL::flush_ssql (rxa->ssql); PANEL::flush_panel (rxa->panel); rxa->rsmpout->flush(); @@ -666,8 +666,8 @@ void RXA::xrxa (RXA *rxa) rxa->agcmeter->execute(); rxa->sip1->execute(0); rxa->cbl->execute(); - SPEAK::xspeak (rxa->speak); - MPEAK::xmpeak (rxa->mpeak); + rxa->speak->execute(); + rxa->mpeak->execute(); SSQL::xssql (rxa->ssql); PANEL::xpanel (rxa->panel); rxa->amsq->execute(); @@ -773,8 +773,8 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->agcmeter->setSamplerate(rxa->dsp_rate); rxa->sip1->setSamplerate(rxa->dsp_rate); rxa->cbl->setSamplerate(rxa->dsp_rate); - SPEAK::setSamplerate_speak (rxa->speak, rxa->dsp_rate); - MPEAK::setSamplerate_mpeak (rxa->mpeak, rxa->dsp_rate); + rxa->speak->setSamplerate(rxa->dsp_rate); + rxa->mpeak->setSamplerate(rxa->dsp_rate); SSQL::setSamplerate_ssql (rxa->ssql, rxa->dsp_rate); PANEL::setSamplerate_panel (rxa->panel, rxa->dsp_rate); // output resampler @@ -854,10 +854,10 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->sip1->setSize(rxa->dsp_size); rxa->cbl->setBuffers(rxa->midbuff, rxa->midbuff); rxa->cbl->setSize(rxa->dsp_size); - SPEAK::setBuffers_speak (rxa->speak, rxa->midbuff, rxa->midbuff); - SPEAK::setSize_speak (rxa->speak, rxa->dsp_size); - MPEAK::setBuffers_mpeak (rxa->mpeak, rxa->midbuff, rxa->midbuff); - MPEAK::setSize_mpeak (rxa->mpeak, rxa->dsp_size); + rxa->speak->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->speak->setSize(rxa->dsp_size); + rxa->mpeak->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->mpeak->setSize(rxa->dsp_size); SSQL::setBuffers_ssql (rxa->ssql, rxa->midbuff, rxa->midbuff); SSQL::setSize_ssql (rxa->ssql, rxa->dsp_size); PANEL::setBuffers_panel (rxa->panel, rxa->midbuff, rxa->midbuff); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 84191334f..8f5b0b9c0 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -114,7 +114,7 @@ TXA* TXA::create_txa ( 2, // 1 to use Q, 2 to use I for input 0); // 0, no copy - txa->phrot = PHROT::create_phrot ( + txa->phrot = new PHROT( 0, // run txa->dsp_size, // size txa->midbuff, // input buffer @@ -548,7 +548,7 @@ void TXA::destroy_txa (TXA *txa) delete (txa->eqp); delete (txa->amsq); delete (txa->micmeter); - PHROT::destroy_phrot (txa->phrot); + delete (txa->phrot); PANEL::destroy_panel (txa->panel); delete (txa->gen0); delete (txa->rsmpin); @@ -566,7 +566,7 @@ void TXA::flush_txa (TXA* txa) txa->rsmpin->flush(); txa->gen0->flush(); PANEL::flush_panel (txa->panel); - PHROT::flush_phrot (txa->phrot); + txa->phrot->flush(); txa->micmeter->flush (); txa->amsq->flush (); txa->eqp->flush(); @@ -600,7 +600,7 @@ void xtxa (TXA* txa) txa->rsmpin->execute(); // input resampler txa->gen0->execute(); // input signal generator PANEL::xpanel (txa->panel); // includes MIC gain - PHROT::xphrot (txa->phrot); // phase rotator + txa->phrot->execute(); // phase rotator txa->micmeter->execute (); // MIC meter txa->amsq->xcap (); // downward expander capture txa->amsq->execute (); // downward expander action @@ -698,7 +698,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) // dsp_rate blocks txa->gen0->setSamplerate(txa->dsp_rate); PANEL::setSamplerate_panel (txa->panel, txa->dsp_rate); - PHROT::setSamplerate_phrot (txa->phrot, txa->dsp_rate); + txa->phrot->setSamplerate(txa->dsp_rate); txa->micmeter->setSamplerate (txa->dsp_rate); txa->amsq->setSamplerate (txa->dsp_rate); txa->eqp->setSamplerate (txa->dsp_rate); @@ -760,8 +760,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) txa->gen0->setSize(txa->dsp_size); PANEL::setBuffers_panel (txa->panel, txa->midbuff, txa->midbuff); PANEL::setSize_panel (txa->panel, txa->dsp_size); - PHROT::setBuffers_phrot (txa->phrot, txa->midbuff, txa->midbuff); - PHROT::setSize_phrot (txa->phrot, txa->dsp_size); + txa->phrot->setBuffers(txa->midbuff, txa->midbuff); + txa->phrot->setSize(txa->dsp_size); txa->micmeter->setBuffers (txa->midbuff); txa->micmeter->setSize (txa->dsp_size); txa->amsq->setBuffers (txa->midbuff, txa->midbuff, txa->midbuff); diff --git a/wdsp/bqbp.cpp b/wdsp/bqbp.cpp index 0026fb004..49d8fba14 100644 --- a/wdsp/bqbp.cpp +++ b/wdsp/bqbp.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "bqbp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { @@ -38,122 +36,119 @@ namespace WDSP { * * ********************************************************************************************************/ -void BQBP::calc_bqbp(BQBP *a) +void BQBP::calc() { double f0, w0, bw, q, sn, cs, c, den; - bw = a->f_high - a->f_low; - f0 = (a->f_high + a->f_low) / 2.0; + + bw = f_high - f_low; + f0 = (f_high + f_low) / 2.0; q = f0 / bw; - w0 = TWOPI * f0 / a->rate; + w0 = TWOPI * f0 / rate; sn = sin(w0); cs = cos(w0); c = sn / (2.0 * q); den = 1.0 + c; - a->a0 = +c / den; - a->a1 = 0.0; - a->a2 = -c / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_bqbp(a); + a0 = +c / den; + a1 = 0.0; + a2 = -c / den; + b1 = 2.0 * cs / den; + b2 = (c - 1.0) / den; + flush(); } -BQBP* BQBP::create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) +BQBP::BQBP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _f_low, + double _f_high, + double _gain, + int _nstages +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + f_low(_f_low), + f_high(_f_high), + gain(_gain), + nstages(_nstages) { - BQBP *a = new BQBP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f_low = f_low; - a->f_high = f_high; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - calc_bqbp(a); - return a; + x0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + x1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + x2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + calc(); } -void BQBP::destroy_bqbp(BQBP *a) +void BQBP::flush() { - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void BQBP::flush_bqbp(BQBP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) + for (int i = 0; i < nstages; i++) { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + x1[2 * i + 0] = x2[2 * i + 0] = y1[2 * i + 0] = y2[2 * i + 0] = 0.0; + x1[2 * i + 1] = x2[2 * i + 1] = y1[2 * i + 1] = y2[2 * i + 1] = 0.0; } } -void BQBP::xbqbp(BQBP *a) +void BQBP::execute() { - if (a->run) + if (run) { int i, j, n; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { for (j = 0; j < 2; j++) { - a->x0[j] = a->gain * a->in[2 * i + j]; + x0[j] = gain * in[2 * i + j]; - for (n = 0; n < a->nstages; n++) + for (n = 0; n < nstages; n++) { if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + x0[2 * n + j] = y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; + y0[2 * n + j] = a0 * x0[2 * n + j] + + a1 * x1[2 * n + j] + + a2 * x2[2 * n + j] + + b1 * y1[2 * n + j] + + b2 * y2[2 * n + j]; + y2[2 * n + j] = y1[2 * n + j]; + y1[2 * n + j] = y0[2 * n + j]; + x2[2 * n + j] = x1[2 * n + j]; + x1[2 * n + j] = x0[2 * n + j]; } - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + out[2 * i + j] = y0[2 * (nstages - 1) + j]; } } } - else if (a->out != a->in) + else if (out != in) { - std::copy(a->in, a->in + a->size * 2, a->out); + std::copy(in, in + size * 2, out); } } -void BQBP::setBuffers_bqbp(BQBP *a, float* in, float* out) +void BQBP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void BQBP::setSamplerate_bqbp(BQBP *a, int rate) +void BQBP::setSamplerate( int _rate) { - a->rate = rate; - calc_bqbp(a); + rate = _rate; + calc(); } -void BQBP::setSize_bqbp(BQBP *a, int size) +void BQBP::setSize(int _size) { - a->size = size; - flush_bqbp(a); + size = _size; + flush(); } } // namespace WDSP diff --git a/wdsp/bqbp.hpp b/wdsp/bqbp.hpp index a530e552a..d41c42ece 100644 --- a/wdsp/bqbp.hpp +++ b/wdsp/bqbp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_bqbp_h #define wdsp_bqbp_h +#include + #include "export.h" namespace WDSP { @@ -51,18 +53,31 @@ public: double gain; int nstages; double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; + std::vector x0, x1, x2, y0, y1, y2; - static BQBP* create_bqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); - static void destroy_bqbp(BQBP *a); - static void flush_bqbp(BQBP *a); - static void xbqbp(BQBP *a); - static void setBuffers_bqbp(BQBP *a, float* in, float* out); - static void setSamplerate_bqbp(BQBP *a, int rate); - static void setSize_bqbp(BQBP *a, int size); + BQBP( + int run, + int size, + float* in, + float* out, + double rate, + double f_low, + double f_high, + double gain, + int nstages + ); + BQBP(const BQBP&) = delete; + BQBP& operator=(BQBP& other) = delete; + ~BQBP() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_bqbp(BQBP *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/bqlp.cpp b/wdsp/bqlp.cpp index c18caae4f..f7ba6af5f 100644 --- a/wdsp/bqlp.cpp +++ b/wdsp/bqlp.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "bqlp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { @@ -38,118 +36,114 @@ namespace WDSP { * * ********************************************************************************************************/ -void BQLP::calc_bqlp(BQLP *a) +void BQLP::calc() { double w0, cs, c, den; - w0 = TWOPI * a->fc / (double)a->rate; + + w0 = TWOPI * fc / (double)rate; cs = cos(w0); - c = sin(w0) / (2.0 * a->Q); + c = sin(w0) / (2.0 * Q); den = 1.0 + c; - a->a0 = 0.5 * (1.0 - cs) / den; - a->a1 = (1.0 - cs) / den; - a->a2 = 0.5 * (1.0 - cs) / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_bqlp(a); + a0 = 0.5 * (1.0 - cs) / den; + a1 = (1.0 - cs) / den; + a2 = 0.5 * (1.0 - cs) / den; + b1 = 2.0 * cs / den; + b2 = (c - 1.0) / den; + flush(); } -BQLP* BQLP::create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) +BQLP::BQLP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _fc, + double _Q, + double _gain, + int _nstages +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + fc(_fc), + Q(_Q), + gain(_gain), + nstages(_nstages) { - BQLP *a = new BQLP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->Q = Q; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y2 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - calc_bqlp(a); - return a; + x0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + x1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + x2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y2.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + calc(); } -void BQLP::destroy_bqlp(BQLP *a) +void BQLP::flush() { - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void BQLP::flush_bqlp(BQLP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) + for (int i = 0; i < nstages; i++) { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + x1[2 * i + 0] = x2[2 * i + 0] = y1[2 * i + 0] = y2[2 * i + 0] = 0.0; + x1[2 * i + 1] = x2[2 * i + 1] = y1[2 * i + 1] = y2[2 * i + 1] = 0.0; } } -void BQLP::xbqlp(BQLP *a) +void BQLP::execute() { - if (a->run) + if (run) { int i, j, n; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { for (j = 0; j < 2; j++) { - a->x0[j] = a->gain * a->in[2 * i + j]; + x0[j] = gain * in[2 * i + j]; - for (n = 0; n < a->nstages; n++) + for (n = 0; n < nstages; n++) { if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; + x0[2 * n + j] = y0[2 * (n - 1) + j]; + y0[2 * n + j] = a0 * x0[2 * n + j] + + a1 * x1[2 * n + j] + + a2 * x2[2 * n + j] + + b1 * y1[2 * n + j] + + b2 * y2[2 * n + j]; + y2[2 * n + j] = y1[2 * n + j]; + y1[2 * n + j] = y0[2 * n + j]; + x2[2 * n + j] = x1[2 * n + j]; + x1[2 * n + j] = x0[2 * n + j]; } - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + out[2 * i + j] = y0[2 * (nstages - 1) + j]; } } } - else if (a->out != a->in) + else if (out != in) { - std::copy(a->in, a->in + a->size * 2, a->out); + std::copy(in, in + size * 2, out); } } -void BQLP::setBuffers_bqlp(BQLP *a, float* in, float* out) +void BQLP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void BQLP::setSamplerate_bqlp(BQLP *a, int rate) +void BQLP::setSamplerate(int _rate) { - a->rate = rate; - calc_bqlp(a); + rate = _rate; + calc(); } -void BQLP::setSize_bqlp(BQLP *a, int size) +void BQLP::setSize(int _size) { - a->size = size; - flush_bqlp(a); + size = _size; + flush(); } - } // namespace WDSP diff --git a/wdsp/bqlp.hpp b/wdsp/bqlp.hpp index 49672c765..5cebf3bb1 100644 --- a/wdsp/bqlp.hpp +++ b/wdsp/bqlp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_bqlp_h #define wdsp_bqlp_h +#include + #include "export.h" namespace WDSP { @@ -51,18 +53,31 @@ public: double gain; int nstages; double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; + std::vector x0, x1, x2, y0, y1, y2; - static BQLP* create_bqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); - static void destroy_bqlp(BQLP *a); - static void flush_bqlp(BQLP *a); - static void xbqlp(BQLP *a); - static void setBuffers_bqlp(BQLP *a, float* in, float* out); - static void setSamplerate_bqlp(BQLP *a, int rate); - static void setSize_bqlp(BQLP *a, int size); + BQLP( + int run, + int size, + float* in, + float* out, + double rate, + double fc, + double Q, + double gain, + int nstages + ); + BQLP(const BQLP&) = delete; + BQLP& operator=(BQLP& other) = delete; + ~BQLP() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_bqlp(BQLP *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/dbqbp.cpp b/wdsp/dbqbp.cpp index 0404a80f6..a600f6b83 100644 --- a/wdsp/dbqbp.cpp +++ b/wdsp/dbqbp.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "dbqbp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { @@ -38,118 +36,115 @@ namespace WDSP { * * ********************************************************************************************************/ -void DBQBP::calc_dbqbp(DBQBP *a) +void DBQBP::calc() { double f0, w0, bw, q, sn, cs, c, den; - bw = a->f_high - a->f_low; - f0 = (a->f_high + a->f_low) / 2.0; + + bw = f_high - f_low; + f0 = (f_high + f_low) / 2.0; q = f0 / bw; - w0 = TWOPI * f0 / a->rate; + w0 = TWOPI * f0 / rate; sn = sin(w0); cs = cos(w0); c = sn / (2.0 * q); den = 1.0 + c; - a->a0 = +c / den; - a->a1 = 0.0; - a->a2 = -c / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_dbqbp(a); + a0 = +c / den; + a1 = 0.0; + a2 = -c / den; + b1 = 2.0 * cs / den; + b2 = (c - 1.0) / den; + flush(); } -DBQBP* DBQBP::create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages) +DBQBP::DBQBP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _f_low, + double _f_high, + double _gain, + int _nstages +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + f_low(_f_low), + f_high(_f_high), + gain(_gain), + nstages(_nstages) { - DBQBP *a = new DBQBP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f_low = f_low; - a->f_high = f_high; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - calc_dbqbp(a); - return a; + x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + x2.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y2.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + calc(); } -void DBQBP::destroy_dbqbp(DBQBP *a) +void DBQBP::flush() { - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void DBQBP::flush_dbqbp(DBQBP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) + for (int i = 0; i < nstages; i++) { - a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; + x1[i] = x2[i] = y1[i] = y2[i] = 0.0; } } -void DBQBP::xdbqbp(DBQBP *a) +void DBQBP::execute() { - if (a->run) + if (run) { int i, n; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->x0[0] = a->gain * a->in[i]; + x0[0] = gain * in[i]; - for (n = 0; n < a->nstages; n++) + for (n = 0; n < nstages; n++) { if (n > 0) - a->x0[n] = a->y0[n - 1]; + x0[n] = y0[n - 1]; - a->y0[n] = a->a0 * a->x0[n] - + a->a1 * a->x1[n] - + a->a2 * a->x2[n] - + a->b1 * a->y1[n] - + a->b2 * a->y2[n]; - a->y2[n] = a->y1[n]; - a->y1[n] = a->y0[n]; - a->x2[n] = a->x1[n]; - a->x1[n] = a->x0[n]; + y0[n] = a0 * x0[n] + + a1 * x1[n] + + a2 * x2[n] + + b1 * y1[n] + + b2 * y2[n]; + y2[n] = y1[n]; + y1[n] = y0[n]; + x2[n] = x1[n]; + x1[n] = x0[n]; } - a->out[i] = a->y0[a->nstages - 1]; + out[i] = y0[nstages - 1]; } } - else if (a->out != a->in) + else if (out != in) { - memcpy(a->out, a->in, a->size * sizeof(float)); + std::copy(in, in + size, out); } } -void DBQBP::setBuffers_dbqbp(DBQBP *a, float* in, float* out) +void DBQBP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void DBQBP::setSamplerate_dbqbp(DBQBP *a, int rate) +void DBQBP::setSamplerate(int _rate) { - a->rate = rate; - calc_dbqbp(a); + rate = _rate; + calc(); } -void DBQBP::setSize_dbqbp(DBQBP *a, int size) +void DBQBP::setSize(int _size) { - a->size = size; - flush_dbqbp(a); + size = _size; + flush(); } } // namespace WDSP diff --git a/wdsp/dbqbp.hpp b/wdsp/dbqbp.hpp index 36051020e..e043426fb 100644 --- a/wdsp/dbqbp.hpp +++ b/wdsp/dbqbp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_dbqbp_h #define wdsp_dbqbp_h +#include + #include "export.h" namespace WDSP { @@ -51,19 +53,32 @@ public: double gain; int nstages; double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; + std::vector x0, x1, x2, y0, y1, y2; // Double Bi-Quad Band-Pass - static DBQBP* create_dbqbp(int run, int size, float* in, float* out, double rate, double f_low, double f_high, double gain, int nstages); - static void destroy_dbqbp(DBQBP *a); - static void flush_dbqbp(DBQBP *a); - static void xdbqbp(DBQBP *a); - static void setBuffers_dbqbp(DBQBP *a, float* in, float* out); - static void setSamplerate_dbqbp(DBQBP *a, int rate); - static void setSize_dbqbp(DBQBP *a, int size); + DBQBP( + int run, + int size, + float* in, + float* out, + double rate, + double f_low, + double f_high, + double gain, + int nstages + ); + DBQBP(const DBQBP&) = delete; + DBQBP& operator=(DBQBP& other) = delete; + ~DBQBP() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_dbqbp(DBQBP *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/dbqlp.cpp b/wdsp/dbqlp.cpp index 3062b8ab3..2e6fc5e5e 100644 --- a/wdsp/dbqlp.cpp +++ b/wdsp/dbqlp.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "dbqlp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { @@ -38,114 +36,111 @@ namespace WDSP { * * ********************************************************************************************************/ -void DBQLP::calc_dbqlp(DBQLP *a) +void DBQLP::calc() { float w0, cs, c, den; - w0 = TWOPI * a->fc / (float)a->rate; + + w0 = TWOPI * fc / (float)rate; cs = cos(w0); - c = sin(w0) / (2.0 * a->Q); + c = sin(w0) / (2.0 * Q); den = 1.0 + c; - a->a0 = 0.5 * (1.0 - cs) / den; - a->a1 = (1.0 - cs) / den; - a->a2 = 0.5 * (1.0 - cs) / den; - a->b1 = 2.0 * cs / den; - a->b2 = (c - 1.0) / den; - flush_dbqlp(a); + a0 = 0.5 * (1.0 - cs) / den; + a1 = (1.0 - cs) / den; + a2 = 0.5 * (1.0 - cs) / den; + b1 = 2.0 * cs / den; + b2 = (c - 1.0) / den; + flush(); } -DBQLP* DBQLP::create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages) +DBQLP::DBQLP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _fc, + double _Q, + double _gain, + int _nstages +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + fc(_fc), + Q(_Q), + gain(_gain), + nstages(_nstages) { - DBQLP *a = new DBQLP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->Q = Q; - a->gain = gain; - a->nstages = nstages; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y2 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - calc_dbqlp(a); - return a; + x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + x2.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y2.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + calc(); } -void DBQLP::destroy_dbqlp(DBQLP *a) +void DBQLP::flush() { - delete[](a->y2); - delete[](a->y1); - delete[](a->y0); - delete[](a->x2); - delete[](a->x1); - delete[](a->x0); - delete(a); -} - -void DBQLP::flush_dbqlp(DBQLP *a) -{ - int i; - for (i = 0; i < a->nstages; i++) + for (int i = 0; i < nstages; i++) { - a->x1[i] = a->x2[i] = a->y1[i] = a->y2[i] = 0.0; + x1[i] = x2[i] = y1[i] = y2[i] = 0.0; } } -void DBQLP::xdbqlp(DBQLP *a) +void DBQLP::execute() { - if (a->run) + if (run) { int i, n; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->x0[0] = a->gain * a->in[i]; + x0[0] = gain * in[i]; - for (n = 0; n < a->nstages; n++) + for (n = 0; n < nstages; n++) { if (n > 0) - a->x0[n] = a->y0[n - 1]; + x0[n] = y0[n - 1]; - a->y0[n] = a->a0 * a->x0[n] - + a->a1 * a->x1[n] - + a->a2 * a->x2[n] - + a->b1 * a->y1[n] - + a->b2 * a->y2[n]; - a->y2[n] = a->y1[n]; - a->y1[n] = a->y0[n]; - a->x2[n] = a->x1[n]; - a->x1[n] = a->x0[n]; + y0[n] = a0 * x0[n] + + a1 * x1[n] + + a2 * x2[n] + + b1 * y1[n] + + b2 * y2[n]; + y2[n] = y1[n]; + y1[n] = y0[n]; + x2[n] = x1[n]; + x1[n] = x0[n]; } - a->out[i] = a->y0[a->nstages - 1]; + out[i] = y0[nstages - 1]; } } - else if (a->out != a->in) + else if (out != in) { - memcpy(a->out, a->in, a->size * sizeof(float)); + std::copy(in, in + size, out); } } -void DBQLP::setBuffers_dbqlp(DBQLP *a, float* in, float* out) +void DBQLP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void DBQLP::setSamplerate_dbqlp(DBQLP *a, int rate) +void DBQLP::setSamplerate(int _rate) { - a->rate = rate; - calc_dbqlp(a); + rate = _rate; + calc(); } -void DBQLP::setSize_dbqlp(DBQLP *a, int size) +void DBQLP::setSize(int _size) { - a->size = size; - flush_dbqlp(a); + size = _size; + flush(); } } // namespace WDSP diff --git a/wdsp/dbqlp.hpp b/wdsp/dbqlp.hpp index c6673944e..a4dc1d889 100644 --- a/wdsp/dbqlp.hpp +++ b/wdsp/dbqlp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_dbqlp_h #define wdsp_dbqlp_h +#include + #include "export.h" namespace WDSP { @@ -51,18 +53,31 @@ public: double gain; int nstages; double a0, a1, a2, b1, b2; - double* x0, * x1, * x2, * y0, * y1, * y2; + std::vector x0, x1, x2, y0, y1, y2; - static DBQLP* create_dbqlp(int run, int size, float* in, float* out, double rate, double fc, double Q, double gain, int nstages); - static void destroy_dbqlp(DBQLP *a); - static void flush_dbqlp(DBQLP *a); - static void xdbqlp(DBQLP *a); - static void setBuffers_dbqlp(DBQLP *a, float* in, float* out); - static void setSamplerate_dbqlp(DBQLP *a, int rate); - static void setSize_dbqlp(DBQLP *a, int size); + DBQLP( + int run, + int size, + float* in, + float* out, + double rate, + double fc, + double Q, + double gain, + int nstages + ); + DBQLP(const DBQLP&) = delete; + DBQLP& operator=(DBQLP& other) = delete; + ~DBQLP() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_dbqlp(DBQLP *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/dsphp.cpp b/wdsp/dsphp.cpp index c6ae2d527..e08141968 100644 --- a/wdsp/dsphp.cpp +++ b/wdsp/dsphp.cpp @@ -38,103 +38,91 @@ namespace WDSP { * * ********************************************************************************************************/ -void DSPHP::calc_dsphp(DSPHP *a) +void DSPHP::calc() { double g; - a->x0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->x1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y0 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - a->y1 = new double[a->nstages]; // (float*)malloc0(a->nstages * sizeof(float)); - g = exp(-TWOPI * a->fc / a->rate); - a->b0 = +0.5 * (1.0 + g); - a->b1 = -0.5 * (1.0 + g); - a->a1 = -g; + x0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + x1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y0.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + y1.resize(nstages); // (float*)malloc0(nstages * sizeof(float)); + g = exp(-TWOPI * fc / rate); + b0 = +0.5 * (1.0 + g); + b1 = -0.5 * (1.0 + g); + a1 = -g; } -DSPHP* DSPHP::create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) +DSPHP::DSPHP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _fc, + int _nstages +) { - DSPHP *a = new DSPHP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_dsphp(a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + rate = _rate; + fc = _fc; + nstages = _nstages; + calc(); } -void DSPHP::decalc_dsphp(DSPHP *a) +void DSPHP::flush() { - delete[](a->y1); - delete[](a->y0); - delete[](a->x1); - delete[](a->x0); + std::fill(x0.begin(), x0.end(), 0); + std::fill(x1.begin(), x1.end(), 0); + std::fill(y0.begin(), y0.end(), 0); + std::fill(y1.begin(), y1.end(), 0); } -void DSPHP::destroy_dsphp(DSPHP *a) +void DSPHP::execute() { - decalc_dsphp(a); - delete(a); -} - -void DSPHP::flush_dsphp(DSPHP *a) -{ - memset(a->x0, 0, a->nstages * sizeof(float)); - memset(a->x1, 0, a->nstages * sizeof(float)); - memset(a->y0, 0, a->nstages * sizeof(float)); - memset(a->y1, 0, a->nstages * sizeof(float)); -} - -void DSPHP::xdsphp(DSPHP *a) -{ - if (a->run) + if (run) { - int i, n; - - for (i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - a->x0[0] = a->in[i]; + x0[0] = in[i]; - for (n = 0; n < a->nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) - a->x0[n] = a->y0[n - 1]; + x0[n] = y0[n - 1]; - a->y0[n] = a->b0 * a->x0[n] - + a->b1 * a->x1[n] - - a->a1 * a->y1[n]; - a->y1[n] = a->y0[n]; - a->x1[n] = a->x0[n]; + y0[n] = b0 * x0[n] + + b1 * x1[n] + - a1 * y1[n]; + y1[n] = y0[n]; + x1[n] = x0[n]; } - a->out[i] = a->y0[a->nstages - 1]; + out[i] = y0[nstages - 1]; } } - else if (a->out != a->in) + else if (out != in) { - memcpy(a->out, a->in, a->size * sizeof(float)); + std::copy(in, in + size, out); } } -void DSPHP::setBuffers_dsphp(DSPHP *a, float* in, float* out) +void DSPHP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void DSPHP::setSamplerate_dsphp(DSPHP *a, int rate) +void DSPHP::setSamplerate(int _rate) { - decalc_dsphp(a); - a->rate = rate; - calc_dsphp(a); + rate = _rate; + calc(); } -void DSPHP::setSize_dsphp(DSPHP *a, int size) +void DSPHP::setSize(int _size) { - a->size = size; - flush_dsphp(a); + size = _size; } } // namespace WDSP diff --git a/wdsp/dsphp.hpp b/wdsp/dsphp.hpp index 4b5b93b19..28e304f37 100644 --- a/wdsp/dsphp.hpp +++ b/wdsp/dsphp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_dsphp_h #define wdsp_dsphp_h +#include + #include "export.h" namespace WDSP { @@ -49,19 +51,31 @@ public: double fc; int nstages; double a1, b0, b1; - double* x0, * x1, * y0, * y1; + std::vector x0, x1, y0, y1; - static DSPHP* create_dsphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); - static void destroy_dsphp(DSPHP *a); - static void flush_dsphp(DSPHP *a); - static void xdsphp(DSPHP *a); - static void setBuffers_dsphp(DSPHP *a, float* in, float* out); - static void setSamplerate_dsphp(DSPHP *a, int rate); - static void setSize_dsphp(DSPHP *a, int size); + DSPHP( + int run, + int size, + float* in, + float* out, + double rate, + double fc, + int nstages + ); + DSPHP(const DSPHP&) = delete; + DSPHP& operator=(DSPHP& other) = delete; + ~DSPHP() = default; + + void destroy(); + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_dsphp(DSPHP *a); - static void decalc_dsphp(DSPHP *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index e762dfcce..da74781c5 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -53,7 +53,15 @@ void FMD::calc() // pll audio gain again = rate / (deviation * TWOPI); // CTCSS Removal - sntch = SNOTCH::create_snotch(1, size, out, out, (int)rate, ctcss_freq, 0.0002); + sntch = new SNOTCH( + 1, + size, + out, + out, + (int) rate, + ctcss_freq, + 0.0002) + ; // detector limiter plim = new WCPAGC( 1, // run - always ON @@ -84,7 +92,7 @@ void FMD::calc() void FMD::decalc() { delete (plim); - SNOTCH::destroy_snotch(sntch); + delete (sntch); } FMD::FMD( @@ -162,7 +170,7 @@ void FMD::flush() fil_out = 0.0; omega = 0.0; fmdc = 0.0; - SNOTCH::flush_snotch (sntch); + sntch->flush(); plim->flush(); } @@ -200,7 +208,7 @@ void FMD::execute() // audio filter FIRCORE::xfircore (paud); // CTCSS Removal - SNOTCH::xsnotch (sntch); + sntch->execute(); if (lim_run) { for (i = 0; i < 2 * size; i++) @@ -275,13 +283,13 @@ void FMD::setDeviation(double _deviation) void FMD::setCTCSSFreq(double freq) { ctcss_freq = freq; - SNOTCH::SetSNCTCSSFreq (sntch, ctcss_freq); + sntch->setFreq(ctcss_freq); } void FMD::setCTCSSRun(int run) { sntch_run = run; - SNOTCH::SetSNCTCSSRun (sntch, sntch_run); + sntch->setRun(sntch_run); } void FMD::setNCde(int nc) diff --git a/wdsp/mpeak.cpp b/wdsp/mpeak.cpp index 6c91e94c5..8541a4d98 100644 --- a/wdsp/mpeak.cpp +++ b/wdsp/mpeak.cpp @@ -39,135 +39,121 @@ namespace WDSP { * * ********************************************************************************************************/ -void MPEAK::calc_mpeak (MPEAK *a) +void MPEAK::calc() { - int i; - a->tmp = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); - a->mix = new float[a->size * 2]; // (float *) malloc0 (a->size * sizeof (complex)); - for (i = 0; i < a->npeaks; i++) + tmp.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); + mix.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); + for (int i = 0; i < npeaks; i++) { - a->pfil[i] = SPEAK::create_speak ( + pfil[i] = new SPEAK( 1, - a->size, - a->in, - a->tmp, - a->rate, - a->f[i], - a->bw[i], - a->gain[i], - a->nstages, + size, + in, + tmp.data(), + rate, + f[i], + bw[i], + gain[i], + nstages, 1 ); } } -void MPEAK::decalc_mpeak (MPEAK *a) +void MPEAK::decalc() { - int i; - for (i = 0; i < a->npeaks; i++) - SPEAK::destroy_speak (a->pfil[i]); - delete[] (a->mix); - delete[] (a->tmp); + for (int i = 0; i < npeaks; i++) + delete (pfil[i]); } -MPEAK* MPEAK::create_mpeak ( - int run, - int size, - float* in, - float* out, - int rate, - int npeaks, - int* enable, - double* f, - double* bw, - double* gain, - int nstages +MPEAK::MPEAK( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + int _npeaks, + int* _enable, + double* _f, + double* _bw, + double* _gain, + int _nstages ) { - MPEAK *a = new MPEAK; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->npeaks = npeaks; - a->nstages = nstages; - a->enable = new int[a->npeaks]; // (int *) malloc0 (a->npeaks * sizeof (int)); - a->f = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - a->bw = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - a->gain = new double[a->npeaks]; // (float *) malloc0 (a->npeaks * sizeof (float)); - memcpy (a->enable, enable, a->npeaks * sizeof (int)); - memcpy (a->f, f, a->npeaks * sizeof (double)); - memcpy (a->bw, bw, a->npeaks * sizeof (double)); - memcpy (a->gain, gain, a->npeaks * sizeof (double)); - a->pfil = new SPEAK*[a->npeaks]; // (SPEAK *) malloc0 (a->npeaks * sizeof (SPEAK)); - calc_mpeak (a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + rate = _rate; + npeaks = _npeaks; + nstages = _nstages; + enable.resize(npeaks); // (int *) malloc0 (npeaks * sizeof (int)); + f.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); + bw.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); + gain.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); + std::copy(_enable, _enable + npeaks, enable.begin()); + std::copy(_f, _f + npeaks, f.begin()); + std::copy(_bw, _bw + npeaks, bw.begin()); + std::copy(_gain, _gain + npeaks, gain.begin()); + pfil.resize(npeaks); // (SPEAK *) malloc0 (npeaks * sizeof (SPEAK)); + calc(); } -void MPEAK::destroy_mpeak (MPEAK *a) +MPEAK::~MPEAK() { - decalc_mpeak (a); - delete[] (a->pfil); - delete[] (a->gain); - delete[] (a->bw); - delete[] (a->f); - delete[] (a->enable); - delete (a); + decalc(); } -void MPEAK::flush_mpeak (MPEAK *a) +void MPEAK::flush() { - int i; - for (i = 0; i < a->npeaks; i++) - SPEAK::flush_speak (a->pfil[i]); + for (int i = 0; i < npeaks; i++) + pfil[i]->flush(); } -void MPEAK::xmpeak (MPEAK *a) +void MPEAK::execute() { - if (a->run) + if (run) { - int i, j; - std::fill(a->mix, a->mix + a->size * 2, 0); + std::fill(mix.begin(), mix.end(), 0); - for (i = 0; i < a->npeaks; i++) + for (int i = 0; i < npeaks; i++) { - if (a->enable[i]) + if (enable[i]) { - SPEAK::xspeak (a->pfil[i]); - for (j = 0; j < 2 * a->size; j++) - a->mix[j] += a->tmp[j]; + pfil[i]->execute(); + for (int j = 0; j < 2 * size; j++) + mix[j] += tmp[j]; } } - std::copy(a->mix, a->mix + a->size * 2, a->out); + std::copy(mix.begin(), mix.end(), out); } - else if (a->in != a->out) + else if (in != out) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void MPEAK::setBuffers_mpeak (MPEAK *a, float* in, float* out) +void MPEAK::setBuffers(float* _in, float* _out) { - decalc_mpeak (a); - a->in = in; - a->out = out; - calc_mpeak (a); + decalc(); + in = _in; + out = _out; + calc(); } -void MPEAK::setSamplerate_mpeak (MPEAK *a, int rate) +void MPEAK::setSamplerate(int _rate) { - decalc_mpeak (a); - a->rate = rate; - calc_mpeak (a); + decalc(); + rate = _rate; + calc(); } -void MPEAK::setSize_mpeak (MPEAK *a, int size) +void MPEAK::setSize(int _size) { - decalc_mpeak (a); - a->size = size; - calc_mpeak (a); + decalc(); + size = _size; + calc(); } /******************************************************************************************************** @@ -176,46 +162,40 @@ void MPEAK::setSize_mpeak (MPEAK *a, int size) * * ********************************************************************************************************/ -void MPEAK::SetmpeakRun (RXA& rxa, int run) +void MPEAK::setRun(int _run) { - MPEAK *a = rxa.mpeak; - a->run = run; + run = _run; } -void MPEAK::SetmpeakNpeaks (RXA& rxa, int npeaks) +void MPEAK::setNpeaks(int _npeaks) { - MPEAK *a = rxa.mpeak; - a->npeaks = npeaks; + npeaks = _npeaks; } -void MPEAK::SetmpeakFilEnable (RXA& rxa, int fil, int enable) +void MPEAK::setFilEnable(int _fil, int _enable) { - MPEAK *a = rxa.mpeak; - a->enable[fil] = enable; + enable[_fil] = _enable; } -void MPEAK::SetmpeakFilFreq (RXA& rxa, int fil, double freq) +void MPEAK::setFilFreq(int _fil, double _freq) { - MPEAK *a = rxa.mpeak; - a->f[fil] = freq; - a->pfil[fil]->f = freq; - SPEAK::calc_speak(a->pfil[fil]); + f[_fil] = _freq; + pfil[_fil]->f = _freq; + pfil[_fil]->calc(); } -void MPEAK::SetmpeakFilBw (RXA& rxa, int fil, double bw) +void MPEAK::setFilBw(int _fil, double _bw) { - MPEAK *a = rxa.mpeak; - a->bw[fil] = bw; - a->pfil[fil]->bw = bw; - SPEAK::calc_speak(a->pfil[fil]); + bw[_fil] = _bw; + pfil[_fil]->bw = _bw; + pfil[_fil]->calc(); } -void MPEAK::SetmpeakFilGain (RXA& rxa, int fil, double gain) +void MPEAK::setFilGain(int _fil, double _gain) { - MPEAK *a = rxa.mpeak; - a->gain[fil] = gain; - a->pfil[fil]->gain = gain; - SPEAK::calc_speak(a->pfil[fil]); + gain[_fil] = _gain; + pfil[_fil]->gain = _gain; + pfil[_fil]->calc(); } } // namespace WDSP diff --git a/wdsp/mpeak.hpp b/wdsp/mpeak.hpp index 6a88f631a..0b859df85 100644 --- a/wdsp/mpeak.hpp +++ b/wdsp/mpeak.hpp @@ -34,11 +34,12 @@ warren@wpratt.com #ifndef _mpeak_h #define _mpeak_h +#include + #include "export.h" namespace WDSP { -class RXA; class SPEAK; class WDSP_API MPEAK @@ -50,16 +51,16 @@ public: float* out; int rate; int npeaks; - int* enable; - double* f; - double* bw; - double* gain; + std::vector enable; + std::vector f; + std::vector bw; + std::vector gain; int nstages; - SPEAK** pfil; - float* tmp; - float* mix; + std::vector pfil; + std::vector tmp; + std::vector mix; - static MPEAK* create_mpeak ( + MPEAK( int run, int size, float* in, @@ -72,23 +73,26 @@ public: double* gain, int nstages ); - static void destroy_mpeak (MPEAK *a); - static void flush_mpeak (MPEAK *a); - static void xmpeak (MPEAK *a); - static void setBuffers_mpeak (MPEAK *a, float* in, float* out); - static void setSamplerate_mpeak (MPEAK *a, int rate); - static void setSize_mpeak (MPEAK *a, int size); + MPEAK(const MPEAK&) = delete; + MPEAK& operator=(const MPEAK& other) = delete; + ~MPEAK(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA - static void SetmpeakRun (RXA& rxa, int run); - static void SetmpeakNpeaks (RXA& rxa, int npeaks); - static void SetmpeakFilEnable (RXA& rxa, int fil, int enable); - static void SetmpeakFilFreq (RXA& rxa, int fil, double freq); - static void SetmpeakFilBw (RXA& rxa, int fil, double bw); - static void SetmpeakFilGain (RXA& rxa, int fil, double gain); + void setRun(int run); + void setNpeaks(int npeaks); + void setFilEnable(int fil, int enable); + void setFilFreq(int fil, double freq); + void setFilBw(int fil, double bw); + void setFilGain(int fil, double gain); private: - static void calc_mpeak (MPEAK *a); - static void decalc_mpeak (MPEAK *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/phrot.cpp b/wdsp/phrot.cpp index a233db27d..1dcbb0826 100644 --- a/wdsp/phrot.cpp +++ b/wdsp/phrot.cpp @@ -38,108 +38,97 @@ namespace WDSP { * * ********************************************************************************************************/ -void PHROT::calc_phrot (PHROT *a) +void PHROT::calc() { double g; - a->x0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->x1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->y0 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - a->y1 = new double[a->nstages]; // (float *) malloc0 (a->nstages * sizeof (float)); - g = tan (PI * a->fc / (float)a->rate); - a->b0 = (g - 1.0) / (g + 1.0); - a->b1 = 1.0; - a->a1 = a->b0; + x0.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); + x1.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); + y0.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); + y1.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); + g = tan (PI * fc / (float)rate); + b0 = (g - 1.0) / (g + 1.0); + b1 = 1.0; + a1 = b0; } -void PHROT::decalc_phrot (PHROT *a) +PHROT::PHROT( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _fc, + int _nstages +) : + reverse(0), + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + fc(_fc), + nstages(_nstages) { - delete[] (a->y1); - delete[] (a->y0); - delete[] (a->x1); - delete[] (a->x0); + calc(); } -PHROT* PHROT::create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages) +void PHROT::flush() { - PHROT *a = new PHROT; - a->reverse = 0; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_phrot (a); - return a; + std::fill (x0.begin(), x0.end(), 0); + std::fill (x1.begin(), x1.end(), 0); + std::fill (y0.begin(), y0.end(), 0); + std::fill (y1.begin(), y1.end(), 0); } -void PHROT::destroy_phrot (PHROT *a) +void PHROT::execute() { - decalc_phrot (a); - delete (a); -} - -void PHROT::flush_phrot (PHROT *a) -{ - memset (a->x0, 0, a->nstages * sizeof (double)); - memset (a->x1, 0, a->nstages * sizeof (double)); - memset (a->y0, 0, a->nstages * sizeof (double)); - memset (a->y1, 0, a->nstages * sizeof (double)); -} - -void PHROT::xphrot (PHROT *a) -{ - if (a->reverse) + if (reverse) { - for (int i = 0; i < a->size; i++) - a->in[2 * i + 0] = -a->in[2 * i + 0]; + for (int i = 0; i < size; i++) + in[2 * i + 0] = -in[2 * i + 0]; } - if (a->run) + if (run) { - int i, n; - - for (i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - a->x0[0] = a->in[2 * i + 0]; + x0[0] = in[2 * i + 0]; - for (n = 0; n < a->nstages; n++) + for (int n = 0; n < nstages; n++) { - if (n > 0) a->x0[n] = a->y0[n - 1]; - a->y0[n] = a->b0 * a->x0[n] - + a->b1 * a->x1[n] - - a->a1 * a->y1[n]; - a->y1[n] = a->y0[n]; - a->x1[n] = a->x0[n]; + if (n > 0) x0[n] = y0[n - 1]; + y0[n] = b0 * x0[n] + + b1 * x1[n] + - a1 * y1[n]; + y1[n] = y0[n]; + x1[n] = x0[n]; } - a->out[2 * i + 0] = a->y0[a->nstages - 1]; + out[2 * i + 0] = y0[nstages - 1]; } } - else if (a->out != a->in) + else if (out != in) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void PHROT::setBuffers_phrot (PHROT *a, float* in, float* out) +void PHROT::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void PHROT::setSamplerate_phrot (PHROT *a, int rate) +void PHROT::setSamplerate(int _rate) { - decalc_phrot (a); - a->rate = rate; - calc_phrot (a); + rate = _rate; + calc(); } -void PHROT::setSize_phrot (PHROT *a, int size) +void PHROT::setSize(int _size) { - a->size = size; - flush_phrot (a); + size = _size; + flush(); } /******************************************************************************************************** @@ -148,35 +137,29 @@ void PHROT::setSize_phrot (PHROT *a, int size) * * ********************************************************************************************************/ -void PHROT::SetPHROTRun (TXA& txa, int run) +void PHROT::setRun(int run) { - PHROT *a = txa.phrot; - a->run = run; + run = run; - if (a->run) - flush_phrot (a); + if (run) + flush(); } -void PHROT::SetPHROTCorner (TXA& txa, double corner) +void PHROT::setCorner(double corner) { - PHROT *a = txa.phrot; - decalc_phrot (a); - a->fc = corner; - calc_phrot (a); + fc = corner; + calc(); } -void PHROT::SetPHROTNstages (TXA& txa, int nstages) +void PHROT::setNstages(int _nstages) { - PHROT *a = txa.phrot; - decalc_phrot (a); - a->nstages = nstages; - calc_phrot (a); + nstages = _nstages; + calc(); } -void PHROT::SetPHROTReverse (TXA& txa, int reverse) +void PHROT::setReverse(int _reverse) { - PHROT *a = txa.phrot; - a->reverse = reverse; + reverse = _reverse; } } // namespace WDSP diff --git a/wdsp/phrot.hpp b/wdsp/phrot.hpp index ef0f2dbf3..79e4c9397 100644 --- a/wdsp/phrot.hpp +++ b/wdsp/phrot.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_phrot_h #define wdsp_phrot_h +#include + #include "export.h" namespace WDSP { @@ -53,24 +55,35 @@ public: int nstages; // normalized such that a0 = 1 double a1, b0, b1; - double *x0, *x1, *y0, *y1; + std::vector x0, x1, y0, y1; - static PHROT* create_phrot (int run, int size, float* in, float* out, int rate, double fc, int nstages); - static void destroy_phrot (PHROT *a); - static void flush_phrot (PHROT *a); - static void xphrot (PHROT *a); - static void setBuffers_phrot (PHROT *a, float* in, float* out); - static void setSamplerate_phrot (PHROT *a, int rate); - static void setSize_phrot (PHROT *a, int size); + PHROT( + int run, + int size, + float* in, + float* out, + int rate, + double fc, + int nstages + ); + PHROT(const PHROT&) = delete; + PHROT& operator=(const PHROT& other) = delete; + ~PHROT() = default; + + void destroy(); + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetPHROTRun (TXA& txa, int run); - static void SetPHROTCorner (TXA& txa, double corner); - static void SetPHROTNstages (TXA& txa, int nstages); - static void SetPHROTReverse (TXA& txa, int reverse); + void setRun(int run); + void setCorner(double corner); + void setNstages(int nstages); + void setReverse(int reverse); private: - static void calc_phrot (PHROT *a); - static void decalc_phrot (PHROT *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/snotch.cpp b/wdsp/snotch.cpp index 8e799e6c4..e2bdb5498 100644 --- a/wdsp/snotch.cpp +++ b/wdsp/snotch.cpp @@ -38,82 +38,83 @@ namespace WDSP { * * ********************************************************************************************************/ -void SNOTCH::calc_snotch (SNOTCH *a) +void SNOTCH::calc() { double fn, qk, qr, csn; - fn = a->f / (float)a->rate; + fn = f / (double) rate; csn = cos (TWOPI * fn); - qr = 1.0 - 3.0 * a->bw; + qr = 1.0 - 3.0 * bw; qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); - a->a0 = + qk; - a->a1 = - 2.0 * qk * csn; - a->a2 = + qk; - a->b1 = + 2.0 * qr * csn; - a->b2 = - qr * qr; - flush_snotch (a); + a0 = + qk; + a1 = - 2.0 * qk * csn; + a2 = + qk; + b1 = + 2.0 * qr * csn; + b2 = - qr * qr; + flush(); } -SNOTCH* SNOTCH::create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw) +SNOTCH::SNOTCH( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _f, + double _bw +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + f(_f), + bw(_bw) { - SNOTCH *a = new SNOTCH; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f = f; - a->bw = bw; - calc_snotch (a); - return a; + calc(); } -void SNOTCH::destroy_snotch (SNOTCH *a) +void SNOTCH::flush() { - delete (a); + x1 = x2 = y1 = y2 = 0.0; } -void SNOTCH::flush_snotch (SNOTCH *a) +void SNOTCH::execute() { - a->x1 = a->x2 = a->y1 = a->y2 = 0.0; -} - -void SNOTCH::xsnotch (SNOTCH *a) -{ - if (a->run) + if (run) { int i; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->x0 = a->in[2 * i + 0]; - a->out[2 * i + 0] = a->a0 * a->x0 + a->a1 * a->x1 + a->a2 * a->x2 + a->b1 * a->y1 + a->b2 * a->y2; - a->y2 = a->y1; - a->y1 = a->out[2 * i + 0]; - a->x2 = a->x1; - a->x1 = a->x0; + x0 = in[2 * i + 0]; + out[2 * i + 0] = a0 * x0 + a1 * x1 + a2 * x2 + b1 * y1 + b2 * y2; + y2 = y1; + y1 = out[2 * i + 0]; + x2 = x1; + x1 = x0; } } - else if (a->out != a->in) + else if (out != in) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void SNOTCH::setBuffers_snotch (SNOTCH *a, float* in, float* out) +void SNOTCH::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void SNOTCH::setSamplerate_snotch (SNOTCH *a, int rate) +void SNOTCH::setSamplerate(int _rate) { - a->rate = rate; - calc_snotch (a); + rate = _rate; + calc(); } -void SNOTCH::setSize_snotch (SNOTCH *a, int size) +void SNOTCH::setSize(int _size) { - a->size = size; - flush_snotch (a); + size = _size; + flush(); } /******************************************************************************************************** @@ -122,15 +123,15 @@ void SNOTCH::setSize_snotch (SNOTCH *a, int size) * * ********************************************************************************************************/ -void SNOTCH::SetSNCTCSSFreq (SNOTCH *a, double freq) +void SNOTCH::setFreq(double _freq) { - a->f = freq; - calc_snotch (a); + f = _freq; + calc(); } -void SNOTCH::SetSNCTCSSRun (SNOTCH *a, int run) +void SNOTCH::setRun(int _run) { - a->run = run; + run = _run; } } // namespace WDSP diff --git a/wdsp/snotch.hpp b/wdsp/snotch.hpp index 39c917a8d..bbed01d1c 100644 --- a/wdsp/snotch.hpp +++ b/wdsp/snotch.hpp @@ -51,18 +51,29 @@ public: double a0, a1, a2, b1, b2; double x0, x1, x2, y1, y2; - static SNOTCH* create_snotch (int run, int size, float* in, float* out, int rate, double f, double bw); - static void destroy_snotch (SNOTCH *a); - static void flush_snotch (SNOTCH *a); - static void xsnotch (SNOTCH *a); - static void setBuffers_snotch (SNOTCH *a, float* in, float* out); - static void setSamplerate_snotch (SNOTCH *a, int rate); - static void setSize_snotch (SNOTCH *a, int size); - static void SetSNCTCSSFreq (SNOTCH *a, double freq); - static void SetSNCTCSSRun (SNOTCH *a, int run); + SNOTCH( + int run, + int size, + float* in, + float* out, + int rate, + double f, + double bw + ); + SNOTCH(const SNOTCH&) = delete; + SNOTCH& operator=(SNOTCH& other) = delete; + ~SNOTCH() {} + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setFreq(double freq); + void setRun(int run); private: - static void calc_snotch (SNOTCH *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/speak.cpp b/wdsp/speak.cpp index a4b5a461a..97de8e14b 100644 --- a/wdsp/speak.cpp +++ b/wdsp/speak.cpp @@ -38,16 +38,16 @@ namespace WDSP { * * ********************************************************************************************************/ -void SPEAK::calc_speak (SPEAK *a) +void SPEAK::calc() { double ratio; double f_corr, g_corr, bw_corr, bw_parm, A, f_min; - switch (a->design) + switch (design) { case 0: - ratio = a->bw / a->f; - switch (a->nstages) + ratio = bw / f; + switch (nstages) { case 4: bw_parm = 2.4; @@ -62,23 +62,23 @@ void SPEAK::calc_speak (SPEAK *a) } { double fn, qk, qr, csn; - a->fgain = a->gain / g_corr; - fn = a->f / (double)a->rate / f_corr; + fgain = gain / g_corr; + fn = f / (double)rate / f_corr; csn = cos (TWOPI * fn); - qr = 1.0 - 3.0 * a->bw / (double)a->rate * bw_parm; + qr = 1.0 - 3.0 * bw / (double)rate * bw_parm; qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); - a->a0 = 1.0 - qk; - a->a1 = 2.0 * (qk - qr) * csn; - a->a2 = qr * qr - qk; - a->b1 = 2.0 * qr * csn; - a->b2 = - qr * qr; + a0 = 1.0 - qk; + a1 = 2.0 * (qk - qr) * csn; + a2 = qr * qr - qk; + b1 = 2.0 * qr * csn; + b2 = - qr * qr; } break; case 1: - if (a->f < 200.0) a->f = 200.0; - ratio = a->bw / a->f; - switch (a->nstages) + if (f < 200.0) f = 200.0; + ratio = bw / f; + switch (nstages) { case 4: bw_parm = 5.0; @@ -96,132 +96,116 @@ void SPEAK::calc_speak (SPEAK *a) } { double w0, sn, c, den; - if (a->f < f_min) a->f = f_min; - w0 = TWOPI * a->f / (double)a->rate; + if (f < f_min) f = f_min; + w0 = TWOPI * f / (double)rate; sn = sin (w0); - a->cbw = bw_corr * a->f; - c = sn * sinh(0.5 * log((a->f + 0.5 * a->cbw * bw_parm) / (a->f - 0.5 * a->cbw * bw_parm)) * w0 / sn); + cbw = bw_corr * f; + c = sn * sinh(0.5 * log((f + 0.5 * cbw * bw_parm) / (f - 0.5 * cbw * bw_parm)) * w0 / sn); den = 1.0 + c / A; - a->a0 = (1.0 + c * A) / den; - a->a1 = - 2.0 * cos (w0) / den; - a->a2 = (1 - c * A) / den; - a->b1 = - a->a1; - a->b2 = - (1 - c / A ) / den; - a->fgain = a->gain / pow (A * A, (double)a->nstages); + a0 = (1.0 + c * A) / den; + a1 = - 2.0 * cos (w0) / den; + a2 = (1 - c * A) / den; + b1 = - a1; + b2 = - (1 - c / A ) / den; + fgain = gain / pow (A * A, (double)nstages); } break; } - flush_speak (a); + flush(); } -SPEAK* SPEAK::create_speak ( - int run, - int size, - float* in, - float* out, - int rate, - double f, - double bw, - double gain, - int nstages, - int design -) +SPEAK::SPEAK( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _f, + double _bw, + double _gain, + int _nstages, + int _design +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + f(_f), + bw(_bw), + gain(_gain), + nstages(_nstages), + design(_design) { - SPEAK *a = new SPEAK; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->f = f; - a->bw = bw; - a->gain = gain; - a->nstages = nstages; - a->design = design; - a->x0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->x1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->x2 = new double[a->nstages * 2]; //(float *) malloc0 (a->nstages * sizeof (complex)); - a->y0 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->y1 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - a->y2 = new double[a->nstages * 2]; // (float *) malloc0 (a->nstages * sizeof (complex)); - calc_speak (a); - return a; + x0.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + x1.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + x2.resize(nstages * 2); //(float *) malloc0 (nstages * sizeof (complex)); + y0.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + y1.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + y2.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + calc(); } -void SPEAK::destroy_speak (SPEAK *a) +void SPEAK::flush() { - delete[] (a->y2); - delete[] (a->y1); - delete[] (a->y0); - delete[] (a->x2); - delete[] (a->x1); - delete[] (a->x0); - delete (a); -} - -void SPEAK::flush_speak (SPEAK *a) -{ - int i; - for (i = 0; i < a->nstages; i++) + for (int i = 0; i < nstages; i++) { - a->x1[2 * i + 0] = a->x2[2 * i + 0] = a->y1[2 * i + 0] = a->y2[2 * i + 0] = 0.0; - a->x1[2 * i + 1] = a->x2[2 * i + 1] = a->y1[2 * i + 1] = a->y2[2 * i + 1] = 0.0; + x1[2 * i + 0] = x2[2 * i + 0] = y1[2 * i + 0] = y2[2 * i + 0] = 0.0; + x1[2 * i + 1] = x2[2 * i + 1] = y1[2 * i + 1] = y2[2 * i + 1] = 0.0; } } -void SPEAK::xspeak (SPEAK *a) +void SPEAK::execute() { - if (a->run) + if (run) { - int i, j, n; - - for (i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - for (j = 0; j < 2; j++) + for (int j = 0; j < 2; j++) { - a->x0[j] = a->fgain * a->in[2 * i + j]; + x0[j] = fgain * in[2 * i + j]; - for (n = 0; n < a->nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->a0 * a->x0[2 * n + j] - + a->a1 * a->x1[2 * n + j] - + a->a2 * a->x2[2 * n + j] - + a->b1 * a->y1[2 * n + j] - + a->b2 * a->y2[2 * n + j]; - a->y2[2 * n + j] = a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x2[2 * n + j] = a->x1[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; + x0[2 * n + j] = y0[2 * (n - 1) + j]; + y0[2 * n + j] = a0 * x0[2 * n + j] + + a1 * x1[2 * n + j] + + a2 * x2[2 * n + j] + + b1 * y1[2 * n + j] + + b2 * y2[2 * n + j]; + y2[2 * n + j] = y1[2 * n + j]; + y1[2 * n + j] = y0[2 * n + j]; + x2[2 * n + j] = x1[2 * n + j]; + x1[2 * n + j] = x0[2 * n + j]; } - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + out[2 * i + j] = y0[2 * (nstages - 1) + j]; } } } - else if (a->out != a->in) + else if (out != in) { - std::copy( a->in, a->in + a->size * 2, a->out); + std::copy( in, in + size * 2, out); } } -void SPEAK::setBuffers_speak (SPEAK *a, float* in, float* out) +void SPEAK::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void SPEAK::setSamplerate_speak (SPEAK *a, int rate) +void SPEAK::setSamplerate(int _rate) { - a->rate = rate; - calc_speak (a); + rate = _rate; + calc(); } -void SPEAK::setSize_speak (SPEAK *a, int size) +void SPEAK::setSize(int _size) { - a->size = size; - flush_speak (a); + size = _size; + flush(); } /******************************************************************************************************** @@ -230,31 +214,27 @@ void SPEAK::setSize_speak (SPEAK *a, int size) * * ********************************************************************************************************/ -void SPEAK::SetSPCWRun (RXA& rxa, int run) +void SPEAK::setRun(int _run) { - SPEAK *a = rxa.speak; - a->run = run; + run = _run; } -void SPEAK::SetSPCWFreq (RXA& rxa, double freq) +void SPEAK::setFreq(double _freq) { - SPEAK *a = rxa.speak; - a->f = freq; - calc_speak (a); + f = _freq; + calc(); } -void SPEAK::SetSPCWBandwidth (RXA& rxa, double bw) +void SPEAK::setBandwidth(double _bw) { - SPEAK *a = rxa.speak; - a->bw = bw; - calc_speak (a); + bw = _bw; + calc(); } -void SPEAK::SetSPCWGain (RXA& rxa, double gain) +void SPEAK::setGain(double _gain) { - SPEAK *a = rxa.speak; - a->gain = gain; - calc_speak (a); + gain = _gain; + calc(); } } // namespace WDSP diff --git a/wdsp/speak.hpp b/wdsp/speak.hpp index 9642d0ad1..a6d18fcd3 100644 --- a/wdsp/speak.hpp +++ b/wdsp/speak.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_speak_h #define wdsp_speak_h +#include + #include "export.h" namespace WDSP { @@ -56,9 +58,9 @@ public: int nstages; int design; double a0, a1, a2, b1, b2; - double *x0, *x1, *x2, *y0, *y1, *y2; + std::vector x0, x1, x2, y0, y1, y2; - static SPEAK* create_speak ( + SPEAK( int run, int size, float* in, @@ -70,18 +72,21 @@ public: int nstages, int design ); - static void destroy_speak (SPEAK *a); - static void flush_speak (SPEAK *a); - static void xspeak (SPEAK *a); - static void setBuffers_speak (SPEAK *a, float* in, float* out); - static void setSamplerate_speak (SPEAK *a, int rate); - static void setSize_speak (SPEAK *a, int size); + SPEAK(const SPEAK&) = delete; + SPEAK& operator=(const SPEAK& other) = delete; + ~SPEAK() {} + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA - static void SetSPCWRun (RXA& rxa, int run); - static void SetSPCWFreq (RXA& rxa, double freq); - static void SetSPCWBandwidth (RXA& rxa, double bw); - static void SetSPCWGain (RXA& rxa, double gain); - static void calc_speak (SPEAK *a); + void setRun(int run); + void setFreq(double freq); + void setBandwidth(double bw); + void setGain(double gain); + void calc(); }; } // namespace WDSP diff --git a/wdsp/sphp.cpp b/wdsp/sphp.cpp index 88045dfea..af59c8488 100644 --- a/wdsp/sphp.cpp +++ b/wdsp/sphp.cpp @@ -37,105 +37,96 @@ namespace WDSP { * * ********************************************************************************************************/ -void SPHP::calc_sphp(SPHP *a) +void SPHP::calc() { double g; - a->x0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->x1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y0 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - a->y1 = new double[a->nstages * 2]; // (float*)malloc0(a->nstages * sizeof(complex)); - g = exp(-TWOPI * a->fc / a->rate); - a->b0 = +0.5 * (1.0 + g); - a->b1 = -0.5 * (1.0 + g); - a->a1 = -g; + x0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + x1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y0.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + y1.resize(nstages * 2); // (float*)malloc0(nstages * sizeof(complex)); + g = exp(-TWOPI * fc / rate); + b0 = +0.5 * (1.0 + g); + b1 = -0.5 * (1.0 + g); + a1 = -g; } -SPHP* SPHP::create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages) +SPHP::SPHP( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + double _fc, + int _nstages +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + fc(_fc), + nstages(_nstages) { - SPHP *a = new SPHP; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->fc = fc; - a->nstages = nstages; - calc_sphp(a); - return a; + calc(); } -void SPHP::decalc_sphp(SPHP *a) +void SPHP::flush() { - delete[](a->y1); - delete[](a->y0); - delete[](a->x1); - delete[](a->x0); + std::fill(x0.begin(), x0.end(), 0); + std::fill(x1.begin(), x0.end(), 0); + std::fill(y0.begin(), x0.end(), 0); + std::fill(y1.begin(), x0.end(), 0); } -void SPHP::destroy_sphp(SPHP *a) +void SPHP::execute() { - decalc_sphp(a); - delete(a); -} - -void SPHP::flush_sphp(SPHP *a) -{ - std::fill(a->x0, a->x0 + a->nstages * 2, 0); - std::fill(a->x1, a->x0 + a->nstages * 2, 0); - std::fill(a->y0, a->x0 + a->nstages * 2, 0); - std::fill(a->y1, a->x0 + a->nstages * 2, 0); -} - -void SPHP::xsphp(SPHP *a) -{ - if (a->run) + if (run) { int i, j, n; - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { for (j = 0; j < 2; j++) { - a->x0[j] = a->in[2 * i + j]; + x0[j] = in[2 * i + j]; - for (n = 0; n < a->nstages; n++) + for (n = 0; n < nstages; n++) { if (n > 0) - a->x0[2 * n + j] = a->y0[2 * (n - 1) + j]; + x0[2 * n + j] = y0[2 * (n - 1) + j]; - a->y0[2 * n + j] = a->b0 * a->x0[2 * n + j] - + a->b1 * a->x1[2 * n + j] - - a->a1 * a->y1[2 * n + j]; - a->y1[2 * n + j] = a->y0[2 * n + j]; - a->x1[2 * n + j] = a->x0[2 * n + j]; + y0[2 * n + j] = b0 * x0[2 * n + j] + + b1 * x1[2 * n + j] + - a1 * y1[2 * n + j]; + y1[2 * n + j] = y0[2 * n + j]; + x1[2 * n + j] = x0[2 * n + j]; } - a->out[2 * i + j] = a->y0[2 * (a->nstages - 1) + j]; + out[2 * i + j] = y0[2 * (nstages - 1) + j]; } } } - else if (a->out != a->in) + else if (out != in) { - std::copy(a->in, a->in + a->size * 2, a->out); + std::copy(in, in + size * 2, out); } } -void SPHP::setBuffers_sphp(SPHP *a, float* in, float* out) +void SPHP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void SPHP::setSamplerate_sphp(SPHP *a, int rate) +void SPHP::setSamplerate(int _rate) { - decalc_sphp(a); - a->rate = rate; - calc_sphp(a); + rate = _rate; + calc(); } -void SPHP::setSize_sphp(SPHP *a, int size) +void SPHP::setSize(int _size) { - a->size = size; - flush_sphp(a); + size = _size; + flush(); } } // namespace WDSP diff --git a/wdsp/sphp.hpp b/wdsp/sphp.hpp index 970e6e92e..8079d5ebe 100644 --- a/wdsp/sphp.hpp +++ b/wdsp/sphp.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_sphp_h #define wdsp_sphp_h +#include + #include "export.h" namespace WDSP { @@ -49,20 +51,31 @@ public: double fc; int nstages; double a1, b0, b1; - double* x0, * x1, * y0, * y1; + std::vector x0, x1, y0, y1; // Complex Single-Pole High-Pass - static SPHP* create_sphp(int run, int size, float* in, float* out, double rate, double fc, int nstages); - static void destroy_sphp(SPHP *a); - static void flush_sphp(SPHP *a); - static void xsphp(SPHP *a); - static void setBuffers_sphp(SPHP *a, float* in, float* out); - static void setSamplerate_sphp(SPHP *a, int rate); - static void setSize_sphp(SPHP *a, int size); + SPHP( + int run, + int size, + float* in, + float* out, + double rate, + double fc, + int nstages + ); + SPHP(const SPHP&) = delete; + SPHP& operator=(const SPHP& other) = delete; + ~SPHP() = default; + + void destroy(); + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_sphp(SPHP *a); - static void decalc_sphp(SPHP *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index b46af706e..08b60bf60 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -39,76 +39,76 @@ namespace WDSP { * * ********************************************************************************************************/ -FTOV* FTOV::create_ftov (int run, int size, int rate, int rsize, double fmax, float* in, float* out) +FTOV::FTOV( + int _run, + int _size, + int _rate, + int _rsize, + double _fmax, + float* _in, + float* _out +) { - FTOV *a = new FTOV; - a->run = run; - a->size = size; - a->rate = rate; - a->rsize = rsize; - a->fmax = fmax; - a->in = in; - a->out = out; - a->eps = 0.01; - a->ring = new int[a->rsize]; // (int*) malloc0 (a->rsize * sizeof (int)); - a->rptr = 0; - a->inlast = 0.0; - a->rcount = 0; - a->div = a->fmax * 2.0 * a->rsize / a->rate; // fmax * 2 = zero-crossings/sec + run = _run; + size = _size; + rate = _rate; + rsize = _rsize; + fmax = _fmax; + in = _in; + out = _out; + eps = 0.01; + ring.resize(rsize); // (int*) malloc0 (rsize * sizeof (int)); + rptr = 0; + inlast = 0.0; + rcount = 0; + div = fmax * 2.0 * rsize / rate; // fmax * 2 = zero-crossings/sec // rsize / rate = sec of data in ring // product is # zero-crossings in ring at fmax - return a; } -void FTOV::destroy_ftov (FTOV *a) +void FTOV::flush() { - delete[] (a->ring); - delete (a); + std::fill(ring.begin(), ring.end(), 0); + rptr = 0; + rcount = 0; + inlast = 0.0; } -void FTOV::flush_ftov (FTOV *a) -{ - memset (a->ring, 0, a->rsize * sizeof (int)); - a->rptr = 0; - a->rcount = 0; - a->inlast = 0.0; -} - -void FTOV::xftov (FTOV *a) +void FTOV::execute() { // 'ftov' does frequency to voltage conversion looking only at zero crossings of an // AC (DC blocked) signal, i.e., ignoring signal amplitude. - if (a->run) + if (run) { - if (a->ring[a->rptr] == 1) // if current ring location is a '1' ... + if (ring[rptr] == 1) // if current ring location is a '1' ... { - a->rcount--; // decrement the count - a->ring[a->rptr] = 0; // set the location to '0' + rcount--; // decrement the count + ring[rptr] = 0; // set the location to '0' } - if ((a->inlast * a->in[0] < 0.0) && // different signs mean zero-crossing - (fabs (a->inlast - a->in[0]) > a->eps)) + if ((inlast * in[0] < 0.0) && // different signs mean zero-crossing + (fabs (inlast - in[0]) > eps)) { - a->ring[a->rptr] = 1; // set the ring location to '1' - a->rcount++; // increment the count + ring[rptr] = 1; // set the ring location to '1' + rcount++; // increment the count } - if (++a->rptr == a->rsize) a->rptr = 0; // increment and wrap the pointer as needed - a->out[0] = std::min (1.0, (double)a->rcount / a->div); // calculate the output sample - a->inlast = a->in[a->size - 1]; // save the last input sample for next buffer - for (int i = 1; i < a->size; i++) + if (++rptr == rsize) rptr = 0; // increment and wrap the pointer as needed + out[0] = std::min (1.0, (double)rcount / div); // calculate the output sample + inlast = in[size - 1]; // save the last input sample for next buffer + for (int i = 1; i < size; i++) { - if (a->ring[a->rptr] == 1) // if current ring location is '1' ... + if (ring[rptr] == 1) // if current ring location is '1' ... { - a->rcount--; // decrement the count - a->ring[a->rptr] = 0; // set the location to '0' + rcount--; // decrement the count + ring[rptr] = 0; // set the location to '0' } - if ((a->in[i - 1] * a->in[i] < 0.0) && // different signs mean zero-crossing - (fabs (a->in[i - 1] - a->in[i]) > a->eps)) + if ((in[i - 1] * in[i] < 0.0) && // different signs mean zero-crossing + (fabs (in[i - 1] - in[i]) > eps)) { - a->ring[a->rptr] = 1; // set the ring location to '1' - a->rcount++; // increment the count + ring[rptr] = 1; // set the ring location to '1' + rcount++; // increment the count } - if (++a->rptr == a->rsize) a->rptr = 0; // increment and wrap the pointer as needed - a->out[i] = std::min(1.0, (double)a->rcount / a->div); // calculate the output sample + if (++rptr == rsize) rptr = 0; // increment and wrap the pointer as needed + out[i] = std::min(1.0, (double)rcount / div); // calculate the output sample } } } @@ -143,9 +143,9 @@ void SSQL::calc_ssql (SSQL *a) a->dcbl = new CBL(1, a->size, a->in, a->b1, 0, a->rate, 0.02); a->ibuff = new float[a->size]; // (float*) malloc0 (a->size * sizeof (float)); a->ftovbuff = new float[a->size]; // (float*) malloc0(a->size * sizeof (float)); - a->cvtr = FTOV::create_ftov (1, a->size, a->rate, a->ftov_rsize, a->ftov_fmax, a->ibuff, a->ftovbuff); + a->cvtr = new FTOV(1, a->size, a->rate, a->ftov_rsize, a->ftov_fmax, a->ibuff, a->ftovbuff); a->lpbuff = new float[a->size]; // (float*) malloc0 (a->size * sizeof (float)); - a->filt = DBQLP::create_dbqlp (1, a->size, a->ftovbuff, a->lpbuff, a->rate, 11.3, 1.0, 1.0, 1); + a->filt = new DBQLP(1, a->size, a->ftovbuff, a->lpbuff, a->rate, 11.3, 1.0, 1.0, 1); a->wdbuff = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int)); a->tr_signal = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int)); // window detector @@ -170,9 +170,9 @@ void SSQL::decalc_ssql (SSQL *a) { delete[] (a->tr_signal); delete[] (a->wdbuff); - DBQLP::destroy_dbqlp (a->filt); + delete (a->filt); delete[] (a->lpbuff); - FTOV::destroy_ftov (a->cvtr); + delete (a->cvtr); delete[] (a->ftovbuff); delete[] (a->ibuff); delete (a->dcbl); @@ -233,9 +233,9 @@ void SSQL::flush_ssql (SSQL *a) a->dcbl->flush(); memset (a->ibuff, 0, a->size * sizeof (float)); memset (a->ftovbuff, 0, a->size * sizeof (float)); - FTOV::flush_ftov (a->cvtr); + a->cvtr->flush(); memset (a->lpbuff, 0, a->size * sizeof (float)); - DBQLP::flush_dbqlp (a->filt); + a->filt->flush(); memset (a->wdbuff, 0, a->size * sizeof (int)); memset (a->tr_signal, 0, a->size * sizeof (int)); } @@ -255,9 +255,9 @@ void SSQL::xssql (SSQL *a) a->dcbl->execute(); // dc block the input signal for (int i = 0; i < a->size; i++) // extract 'I' component a->ibuff[i] = a->b1[2 * i]; - FTOV::xftov (a->cvtr); // convert frequency to voltage, ignoring amplitude + a->cvtr->execute(); // convert frequency to voltage, ignoring amplitude // WriteAudioWDSP(20.0, a->rate, a->size, a->ftovbuff, 4, 0.99); - DBQLP::xdbqlp (a->filt); // low-pass filter + a->filt->execute(); // low-pass filter // WriteAudioWDSP(20.0, a->rate, a->size, a->lpbuff, 4, 0.99); // calculate the output of the window detector for each sample for (int i = 0; i < a->size; i++) diff --git a/wdsp/ssql.hpp b/wdsp/ssql.hpp index 6b353dc9a..fe10ab6ff 100644 --- a/wdsp/ssql.hpp +++ b/wdsp/ssql.hpp @@ -28,6 +28,8 @@ warren@pratt.one #ifndef wdsp_ssql_h #define wdsp_ssql_h +#include + #include "export.h" namespace WDSP { @@ -40,19 +42,30 @@ public: int rate; // sample-rate int rsize; // rate * time_to_fill_ring, e.g., 48K/s * 50ms = 2400 double fmax; // frequency (Hz) for full output, e.g., 2000 (Hz) - float* in; // pointer to the intput buffer for ftov - float* out; // pointer to the output buffer for ftov - int* ring; // pointer to the base of the ring + float* in; // pointer to the intput buffer for ftov + float* out; // pointer to the output buffer for ftov + std::vector ring; // the ring int rptr; // index into the ring double inlast; // holds last sample from previous buffer int rcount; // count of zero-crossings currently in the ring double div; // divisor for 'rcount' to produce output of 1.0 at 'fmax' double eps; // minimum input change to count as a signal edge transition - static FTOV* create_ftov (int run, int size, int rate, int rsize, double fmax, float* in, float* out); - static void destroy_ftov (FTOV *a); - static void flush_ftov (FTOV *a); - static void xftov (FTOV *a); + FTOV( + int run, + int size, + int rate, + int rsize, + double fmax, + float* in, + float* out + ); + FTOV(const FTOV&) = delete; + FTOV& operator=(FTOV& other) = delete; + ~FTOV() = default; + + void flush(); + void execute(); }; class CBL; From cd38f356d0ecca9cb2372e1c0d4541a4ca3659ad Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 1 Aug 2024 02:01:09 +0200 Subject: [PATCH 28/46] WDSP: rework SSQL and PANEL classes --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 20 +- wdsp/RXA.cpp | 28 +-- wdsp/TXA.cpp | 14 +- wdsp/meter.hpp | 2 + wdsp/patchpanel.cpp | 166 ++++++------- wdsp/patchpanel.hpp | 35 +-- wdsp/ssql.cpp | 311 ++++++++++++------------ wdsp/ssql.hpp | 31 +-- 8 files changed, 293 insertions(+), 314 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index dd35a30ae..0114558dc 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -680,7 +680,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) || (m_settings.m_squelchThreshold != settings.m_squelchThreshold) || (m_settings.m_squelchMode != settings.m_squelchMode) || force) { - WDSP::SSQL::SetSSQLRun(*m_rxa, 0); + m_rxa->ssql->setRun(0); m_rxa->amsq->setRun(0); m_rxa->fmsq->setRun(0); @@ -690,9 +690,9 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) { case WDSPRxProfile::SquelchModeVoice: { - WDSP::SSQL::SetSSQLRun(*m_rxa, 1); + m_rxa->ssql->setRun(1); double threshold = 0.0075 * settings.m_squelchThreshold; - WDSP::SSQL::SetSSQLThreshold(*m_rxa, threshold); + m_rxa->ssql->setThreshold(threshold); } break; case WDSPRxProfile::SquelchModeAM: @@ -717,11 +717,11 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } if ((m_settings.m_ssqlTauMute != settings.m_ssqlTauMute) || force) { - WDSP::SSQL::SetSSQLTauMute(*m_rxa, settings.m_ssqlTauMute); + m_rxa->ssql->setTauMute(settings.m_ssqlTauMute); } if ((m_settings.m_ssqlTauUnmute != settings.m_ssqlTauUnmute) || force) { - WDSP::SSQL::SetSSQLTauUnMute(*m_rxa, settings.m_ssqlTauUnmute); + m_rxa->ssql->setTauUnMute(settings.m_ssqlTauUnmute); } if ((m_settings.m_amsqMaxTail != settings.m_amsqMaxTail) || force) { @@ -743,7 +743,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) // Audio panel if ((m_settings.m_volume != settings.m_volume) || force) { - WDSP::PANEL::SetPanelGain1(*m_rxa, settings.m_volume); + m_rxa->panel->setGain1(settings.m_volume); } if ((m_settings.m_audioBinaural != settings.m_audioBinaural) @@ -752,13 +752,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) { if (settings.m_audioBinaural) { - WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 3 : 0); - WDSP::PANEL::SetPanelPan(*m_rxa, settings.m_audioPan); + m_rxa->panel->setCopy(settings.m_audioFlipChannels ? 3 : 0); + m_rxa->panel->setPan(settings.m_audioPan); } else { - WDSP::PANEL::SetPanelCopy(*m_rxa, settings.m_audioFlipChannels ? 2 : 1); - WDSP::PANEL::SetPanelPan(*m_rxa, 0.5); + m_rxa->panel->setCopy(settings.m_audioFlipChannels ? 2 : 1); + m_rxa->panel->setPan(0.5); } } diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 7976cba16..e915b1e2d 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -515,7 +515,7 @@ RXA* RXA::create_rxa ( } // Syllabic squelch (Voice suelch) - Not in the block diagram - rxa->ssql = SSQL::create_ssql( + rxa->ssql = new SSQL( 0, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -532,7 +532,7 @@ RXA* RXA::create_rxa ( 2000.0); // max freq for f_to_v converter // PatchPanel - rxa->panel = PANEL::create_panel ( + rxa->panel = new PANEL( 1, // run rxa->dsp_size, // size rxa->midbuff, // pointer to input buffer @@ -565,8 +565,8 @@ RXA* RXA::create_rxa ( void RXA::destroy_rxa (RXA *rxa) { delete (rxa->rsmpout); - PANEL::destroy_panel (rxa->panel); - SSQL::destroy_ssql (rxa->ssql); + delete (rxa->panel); + delete (rxa->ssql); delete (rxa->mpeak); delete (rxa->speak); delete (rxa->cbl); @@ -629,8 +629,8 @@ void RXA::flush_rxa (RXA *rxa) rxa->cbl->flush(); rxa->speak->flush(); rxa->mpeak->flush(); - SSQL::flush_ssql (rxa->ssql); - PANEL::flush_panel (rxa->panel); + rxa->ssql->flush(); + rxa->panel->flush(); rxa->rsmpout->flush(); } @@ -668,8 +668,8 @@ void RXA::xrxa (RXA *rxa) rxa->cbl->execute(); rxa->speak->execute(); rxa->mpeak->execute(); - SSQL::xssql (rxa->ssql); - PANEL::xpanel (rxa->panel); + rxa->ssql->execute(); + rxa->panel->execute(); rxa->amsq->execute(); rxa->rsmpout->execute(); } @@ -775,8 +775,8 @@ void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) rxa->cbl->setSamplerate(rxa->dsp_rate); rxa->speak->setSamplerate(rxa->dsp_rate); rxa->mpeak->setSamplerate(rxa->dsp_rate); - SSQL::setSamplerate_ssql (rxa->ssql, rxa->dsp_rate); - PANEL::setSamplerate_panel (rxa->panel, rxa->dsp_rate); + rxa->ssql->setSamplerate(rxa->dsp_rate); + rxa->panel->setSamplerate(rxa->dsp_rate); // output resampler rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); rxa->rsmpout->setInRate(rxa->dsp_rate); @@ -858,10 +858,10 @@ void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) rxa->speak->setSize(rxa->dsp_size); rxa->mpeak->setBuffers(rxa->midbuff, rxa->midbuff); rxa->mpeak->setSize(rxa->dsp_size); - SSQL::setBuffers_ssql (rxa->ssql, rxa->midbuff, rxa->midbuff); - SSQL::setSize_ssql (rxa->ssql, rxa->dsp_size); - PANEL::setBuffers_panel (rxa->panel, rxa->midbuff, rxa->midbuff); - PANEL::setSize_panel (rxa->panel, rxa->dsp_size); + rxa->ssql->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->ssql->setSize(rxa->dsp_size); + rxa->panel->setBuffers(rxa->midbuff, rxa->midbuff); + rxa->panel->setSize(rxa->dsp_size); // output resampler rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); rxa->rsmpout->setSize(rxa->dsp_size); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 8f5b0b9c0..ac8f9285b 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -103,7 +103,7 @@ TXA* TXA::create_txa ( txa->dsp_rate, // sample rate 2); // mode - txa->panel = PANEL::create_panel ( + txa->panel = new PANEL( 1, // run txa->dsp_size, // size txa->midbuff, // pointer to input buffer @@ -549,7 +549,7 @@ void TXA::destroy_txa (TXA *txa) delete (txa->amsq); delete (txa->micmeter); delete (txa->phrot); - PANEL::destroy_panel (txa->panel); + delete (txa->panel); delete (txa->gen0); delete (txa->rsmpin); delete[] (txa->midbuff); @@ -565,7 +565,7 @@ void TXA::flush_txa (TXA* txa) std::fill(txa->midbuff, txa->midbuff + 2 * txa->dsp_size * 2, 0); txa->rsmpin->flush(); txa->gen0->flush(); - PANEL::flush_panel (txa->panel); + txa->panel->flush (); txa->phrot->flush(); txa->micmeter->flush (); txa->amsq->flush (); @@ -599,7 +599,7 @@ void xtxa (TXA* txa) { txa->rsmpin->execute(); // input resampler txa->gen0->execute(); // input signal generator - PANEL::xpanel (txa->panel); // includes MIC gain + txa->panel->execute(); // includes MIC gain txa->phrot->execute(); // phase rotator txa->micmeter->execute (); // MIC meter txa->amsq->xcap (); // downward expander capture @@ -697,7 +697,7 @@ void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) txa->rsmpin->setOutRate(txa->dsp_rate); // dsp_rate blocks txa->gen0->setSamplerate(txa->dsp_rate); - PANEL::setSamplerate_panel (txa->panel, txa->dsp_rate); + txa->panel->setSamplerate(txa->dsp_rate); txa->phrot->setSamplerate(txa->dsp_rate); txa->micmeter->setSamplerate (txa->dsp_rate); txa->amsq->setSamplerate (txa->dsp_rate); @@ -758,8 +758,8 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) // dsp_size blocks txa->gen0->setBuffers(txa->midbuff, txa->midbuff); txa->gen0->setSize(txa->dsp_size); - PANEL::setBuffers_panel (txa->panel, txa->midbuff, txa->midbuff); - PANEL::setSize_panel (txa->panel, txa->dsp_size); + txa->panel->setBuffers(txa->midbuff, txa->midbuff); + txa->panel->setSize(txa->dsp_size); txa->phrot->setBuffers(txa->midbuff, txa->midbuff); txa->phrot->setSize(txa->dsp_size); txa->micmeter->setBuffers (txa->midbuff); diff --git a/wdsp/meter.hpp b/wdsp/meter.hpp index 1cc8ab009..e84f07651 100644 --- a/wdsp/meter.hpp +++ b/wdsp/meter.hpp @@ -66,6 +66,8 @@ public: int enum_gain, double* pgain ); + METER(const METER&) = delete; + METER& operator=(const METER& other) = delete; ~METER() = default; void flush(); diff --git a/wdsp/patchpanel.cpp b/wdsp/patchpanel.cpp index b3d23f1dc..0aa051485 100644 --- a/wdsp/patchpanel.cpp +++ b/wdsp/patchpanel.cpp @@ -32,103 +32,94 @@ warren@wpratt.com namespace WDSP { -PANEL* PANEL::create_panel ( - int run, - int size, - float* in, - float* out, - double gain1, - double gain2I, - double gain2Q, - int inselect, - int copy -) +PANEL::PANEL( + int _run, + int _size, + float* _in, + float* _out, + double _gain1, + double _gain2I, + double _gain2Q, + int _inselect, + int _copy +) : + run(_run), + size(_size), + in(_in), + out(_out), + gain1(_gain1), + gain2I(_gain2I), + gain2Q(_gain2Q), + inselect(_inselect), + copy(_copy) { - PANEL* a = new PANEL; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->gain1 = gain1; - a->gain2I = gain2I; - a->gain2Q = gain2Q; - a->inselect = inselect; - a->copy = copy; - return a; } -void PANEL::destroy_panel (PANEL *a) +void PANEL::flush() { - delete (a); } -void PANEL::flush_panel (PANEL *) -{ - -} - -void PANEL::xpanel (PANEL *a) +void PANEL::execute() { int i; double I, Q; - double gainI = a->gain1 * a->gain2I; - double gainQ = a->gain1 * a->gain2Q; + double gainI = gain1 * gain2I; + double gainQ = gain1 * gain2Q; // inselect is either 0(neither), 1(Q), 2(I), or 3(both) - switch (a->copy) + switch (copy) { case 0: // no copy - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - I = a->in[2 * i + 0] * (a->inselect >> 1); - Q = a->in[2 * i + 1] * (a->inselect & 1); - a->out[2 * i + 0] = gainI * I; - a->out[2 * i + 1] = gainQ * Q; + I = in[2 * i + 0] * (inselect >> 1); + Q = in[2 * i + 1] * (inselect & 1); + out[2 * i + 0] = gainI * I; + out[2 * i + 1] = gainQ * Q; } break; case 1: // copy I to Q (then Q == I) - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - I = a->in[2 * i + 0] * (a->inselect >> 1); + I = in[2 * i + 0] * (inselect >> 1); Q = I; - a->out[2 * i + 0] = gainI * I; - a->out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = gainI * I; + out[2 * i + 1] = gainQ * Q; } break; case 2: // copy Q to I (then I == Q) - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - Q = a->in[2 * i + 1] * (a->inselect & 1); + Q = in[2 * i + 1] * (inselect & 1); I = Q; - a->out[2 * i + 0] = gainI * I; - a->out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = gainI * I; + out[2 * i + 1] = gainQ * Q; } break; case 3: // reverse (I=>Q and Q=>I) - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - Q = a->in[2 * i + 0] * (a->inselect >> 1); - I = a->in[2 * i + 1] * (a->inselect & 1); - a->out[2 * i + 0] = gainI * I; - a->out[2 * i + 1] = gainQ * Q; + Q = in[2 * i + 0] * (inselect >> 1); + I = in[2 * i + 1] * (inselect & 1); + out[2 * i + 0] = gainI * I; + out[2 * i + 1] = gainQ * Q; } break; } } -void PANEL::setBuffers_panel (PANEL *a, float* in, float* out) +void PANEL::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void PANEL::setSamplerate_panel (PANEL *, int) +void PANEL::setSamplerate(int) { - } -void PANEL::setSize_panel (PANEL *a, int size) +void PANEL::setSize(int _size) { - a->size = size; + size = _size; } /******************************************************************************************************** @@ -137,54 +128,54 @@ void PANEL::setSize_panel (PANEL *a, int size) * * ********************************************************************************************************/ -void PANEL::SetPanelRun (RXA& rxa, int run) +void PANEL::setRun(int _run) { - rxa.panel->run = run; + run = _run; } -void PANEL::SetPanelSelect (RXA& rxa, int select) +void PANEL::setSelect(int _select) { - rxa.panel->inselect = select; + inselect = _select; } -void PANEL::SetPanelGain1 (RXA& rxa, double gain) +void PANEL::setGain1(double _gain) { - rxa.panel->gain1 = gain; + gain1 = _gain; } -void PANEL::SetPanelGain2 (RXA& rxa, double gainI, double gainQ) +void PANEL::setGain2(double _gainI, double _gainQ) { - rxa.panel->gain2I = gainI; - rxa.panel->gain2Q = gainQ; + gain2I = _gainI; + gain2Q = _gainQ; } -void PANEL::SetPanelPan (RXA& rxa, double pan) +void PANEL::setPan(double _pan) { double gain1, gain2; - if (pan <= 0.5) + if (_pan <= 0.5) { gain1 = 1.0; - gain2 = sin (pan * PI); + gain2 = sin (_pan * PI); } else { - gain1 = sin (pan * PI); + gain1 = sin (_pan * PI); gain2 = 1.0; } - rxa.panel->gain2I = gain1; - rxa.panel->gain2Q = gain2; + gain2I = gain1; + gain2Q = gain2; } -void PANEL::SetPanelCopy (RXA& rxa, int copy) +void PANEL::setCopy(int _copy) { - rxa.panel->copy = copy; + copy = _copy; } -void PANEL::SetPanelBinaural (RXA& rxa, int bin) +void PANEL::setBinaural(int _bin) { - rxa.panel->copy = 1 - bin; + copy = 1 - _bin; } /******************************************************************************************************** @@ -193,25 +184,14 @@ void PANEL::SetPanelBinaural (RXA& rxa, int bin) * * ********************************************************************************************************/ -void PANEL::SetPanelRun (TXA& txa, int run) +void PANEL::setSelectTx(int _select) { - txa.panel->run = run; -} - -void PANEL::SetPanelGain1 (TXA& txa, double gain) -{ - txa.panel->gain1 = gain; - //print_message ("micgainset.txt", "Set MIC Gain to", (int)(100.0 * gain), 0, 0); -} - -void PANEL::SetPanelSelect (TXA& txa, int select) -{ - if (select == 1) - txa.panel->copy = 3; + if (_select == 1) + copy = 3; else - txa.panel->copy = 0; + copy = 0; - txa.panel->inselect = select; + inselect = _select; } } // namespace WDSP diff --git a/wdsp/patchpanel.hpp b/wdsp/patchpanel.hpp index 48cbcce7b..0c86ad122 100644 --- a/wdsp/patchpanel.hpp +++ b/wdsp/patchpanel.hpp @@ -48,7 +48,7 @@ public: int inselect; int copy; - static PANEL* create_panel ( + PANEL( int run, int size, float* in, @@ -59,24 +59,25 @@ public: int inselect, int copy ); - static void destroy_panel (PANEL *a); - static void flush_panel (PANEL *a); - static void xpanel (PANEL *a); - static void setBuffers_panel (PANEL *a, float* in, float* out); - static void setSamplerate_panel (PANEL *a, int rate); - static void setSize_panel (PANEL *a, int size); + PANEL(const PANEL&) = delete; + PANEL& operator=(const PANEL& other) = delete; + ~PANEL() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void SetPanelRun (RXA& rxa, int run); - static void SetPanelSelect (RXA& rxa, int select); - static void SetPanelGain1 (RXA& rxa, double gain); - static void SetPanelGain2 (RXA& rxa, double gainI, double gainQ); - static void SetPanelPan (RXA& rxa, double pan); - static void SetPanelCopy (RXA& rxa, int copy); - static void SetPanelBinaural (RXA& rxa, int bin); + void setRun(int run); + void setSelect(int select); + void setGain1(double gain); + void setGain2(double gainI, double gainQ); + void setPan(double pan); + void setCopy(int copy); + void setBinaural(int bin); // TXA Properties - static void SetPanelRun (TXA& txa, int run); - static void SetPanelGain1 (TXA& txa, double gain); - static void SetPanelSelect (TXA& txa, int select); + void setSelectTx(int select); }; } // namespace WDSP diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index 08b60bf60..3375b7e9f 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -117,127 +117,122 @@ void FTOV::execute() -void SSQL::compute_ssql_slews(SSQL *a) +void SSQL::compute_slews() { - int i; double delta, theta; - delta = PI / (double)a->ntup; + delta = PI / (double) ntup; theta = 0.0; - for (i = 0; i <= a->ntup; i++) + for (int i = 0; i <= ntup; i++) { - a->cup[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 - cos(theta)); + cup[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 - cos(theta)); theta += delta; } - delta = PI / (double)a->ntdown; + delta = PI / (double)ntdown; theta = 0.0; - for (i = 0; i <= a->ntdown; i++) + for (int i = 0; i <= ntdown; i++) { - a->cdown[i] = a->muted_gain + (1.0 - a->muted_gain) * 0.5 * (1.0 + cos(theta)); + cdown[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 + cos(theta)); theta += delta; } } -void SSQL::calc_ssql (SSQL *a) +void SSQL::calc() { - a->b1 = new float[a->size * 2]; // (float*) malloc0 (a->size * sizeof (complex)); - a->dcbl = new CBL(1, a->size, a->in, a->b1, 0, a->rate, 0.02); - a->ibuff = new float[a->size]; // (float*) malloc0 (a->size * sizeof (float)); - a->ftovbuff = new float[a->size]; // (float*) malloc0(a->size * sizeof (float)); - a->cvtr = new FTOV(1, a->size, a->rate, a->ftov_rsize, a->ftov_fmax, a->ibuff, a->ftovbuff); - a->lpbuff = new float[a->size]; // (float*) malloc0 (a->size * sizeof (float)); - a->filt = new DBQLP(1, a->size, a->ftovbuff, a->lpbuff, a->rate, 11.3, 1.0, 1.0, 1); - a->wdbuff = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int)); - a->tr_signal = new int[a->size]; // (int*) malloc0 (a->size * sizeof (int)); + b1 = new float[size * 2]; // (float*) malloc0 (size * sizeof (complex)); + dcbl = new CBL(1, size, in, b1, 0, rate, 0.02); + ibuff = new float[size]; // (float*) malloc0 (size * sizeof (float)); + ftovbuff = new float[size]; // (float*) malloc0(size * sizeof (float)); + cvtr = new FTOV(1, size, rate, ftov_rsize, ftov_fmax, ibuff, ftovbuff); + lpbuff = new float[size]; // (float*) malloc0 (size * sizeof (float)); + filt = new DBQLP(1, size, ftovbuff, lpbuff, rate, 11.3, 1.0, 1.0, 1); + wdbuff = new int[size]; // (int*) malloc0 (size * sizeof (int)); + tr_signal = new int[size]; // (int*) malloc0 (size * sizeof (int)); // window detector - a->wdmult = exp (-1.0 / (a->rate * a->wdtau)); - a->wdaverage = 0.0; + wdmult = exp (-1.0 / (rate * wdtau)); + wdaverage = 0.0; // trigger - a->tr_voltage = a->tr_thresh; - a->mute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_mute)); - a->unmute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_unmute)); + tr_voltage = tr_thresh; + mute_mult = 1.0 - exp (-1.0 / (rate * tr_tau_mute)); + unmute_mult = 1.0 - exp (-1.0 / (rate * tr_tau_unmute)); // level change - a->ntup = (int)(a->tup * a->rate); - a->ntdown = (int)(a->tdown * a->rate); - a->cup = new float[a->ntup + 1]; // (float*) malloc0 ((a->ntup + 1) * sizeof (float)); - a->cdown = new float[a->ntdown + 1]; // (float*) malloc0 ((a->ntdown + 1) * sizeof (float)); - compute_ssql_slews (a); + ntup = (int)(tup * rate); + ntdown = (int)(tdown * rate); + cup = new float[ntup + 1]; // (float*) malloc0 ((ntup + 1) * sizeof (float)); + cdown = new float[ntdown + 1]; // (float*) malloc0 ((ntdown + 1) * sizeof (float)); + compute_slews(); // control - a->state = 0; - a->count = 0; + state = 0; + count = 0; } -void SSQL::decalc_ssql (SSQL *a) +void SSQL::decalc() { - delete[] (a->tr_signal); - delete[] (a->wdbuff); - delete (a->filt); - delete[] (a->lpbuff); - delete (a->cvtr); - delete[] (a->ftovbuff); - delete[] (a->ibuff); - delete (a->dcbl); - delete[] (a->b1); - delete[] (a->cdown); - delete[] (a->cup); + delete[] (tr_signal); + delete[] (wdbuff); + delete (filt); + delete[] (lpbuff); + delete (cvtr); + delete[] (ftovbuff); + delete[] (ibuff); + delete (dcbl); + delete[] (b1); + delete[] (cdown); + delete[] (cup); } -SSQL* SSQL::create_ssql ( - int run, - int size, - float* in, - float* out, - int rate, - double tup, - double tdown, - double muted_gain, - double tau_mute, - double tau_unmute, - double wthresh, - double tr_thresh, - int rsize, - double fmax +SSQL::SSQL( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + double _tup, + double _tdown, + double _muted_gain, + double _tau_mute, + double _tau_unmute, + double _wthresh, + double _tr_thresh, + int _rsize, + double _fmax ) { - SSQL *a = new SSQL; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->tup = tup; - a->tdown = tdown; - a->muted_gain = muted_gain; - a->tr_tau_mute = tau_mute; - a->tr_tau_unmute = tau_unmute; - a->wthresh = wthresh; // PRIMARY SQUELCH THRESHOLD CONTROL - a->tr_thresh = tr_thresh; // value between tr_ss_unmute and tr_ss_mute, default = 0.8197 - a->tr_ss_mute = 1.0; - a->tr_ss_unmute = 0.3125; - a->wdtau = 0.5; - a->ftov_rsize = rsize; - a->ftov_fmax = fmax; - calc_ssql (a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + rate = _rate; + tup = _tup; + tdown = _tdown; + muted_gain = _muted_gain; + tr_tau_mute = _tau_mute; + tr_tau_unmute = _tau_unmute; + wthresh = _wthresh; // PRIMARY SQUELCH THRESHOLD CONTROL + tr_thresh = _tr_thresh; // value between tr_ss_unmute and tr_ss_mute, default = 0.8197 + tr_ss_mute = 1.0; + tr_ss_unmute = 0.3125; + wdtau = 0.5; + ftov_rsize = _rsize; + ftov_fmax = _fmax; + calc(); } -void SSQL::destroy_ssql (SSQL *a) +SSQL::~SSQL() { - decalc_ssql (a); - delete (a); + decalc(); } -void SSQL::flush_ssql (SSQL *a) +void SSQL::flush() { - - std::fill(a->b1, a->b1 + a->size * 2, 0); - a->dcbl->flush(); - memset (a->ibuff, 0, a->size * sizeof (float)); - memset (a->ftovbuff, 0, a->size * sizeof (float)); - a->cvtr->flush(); - memset (a->lpbuff, 0, a->size * sizeof (float)); - a->filt->flush(); - memset (a->wdbuff, 0, a->size * sizeof (int)); - memset (a->tr_signal, 0, a->size * sizeof (int)); + std::fill(b1, b1 + size * 2, 0); + dcbl->flush(); + memset (ibuff, 0, size * sizeof (float)); + memset (ftovbuff, 0, size * sizeof (float)); + cvtr->flush(); + memset (lpbuff, 0, size * sizeof (float)); + filt->flush(); + memset (wdbuff, 0, size * sizeof (int)); + memset (tr_signal, 0, size * sizeof (int)); } enum _ssqlstate @@ -248,98 +243,98 @@ enum _ssqlstate DECREASE }; -void SSQL::xssql (SSQL *a) +void SSQL::execute() { - if (a->run) + if (run) { - a->dcbl->execute(); // dc block the input signal - for (int i = 0; i < a->size; i++) // extract 'I' component - a->ibuff[i] = a->b1[2 * i]; - a->cvtr->execute(); // convert frequency to voltage, ignoring amplitude - // WriteAudioWDSP(20.0, a->rate, a->size, a->ftovbuff, 4, 0.99); - a->filt->execute(); // low-pass filter - // WriteAudioWDSP(20.0, a->rate, a->size, a->lpbuff, 4, 0.99); + dcbl->execute(); // dc block the input signal + for (int i = 0; i < size; i++) // extract 'I' component + ibuff[i] = b1[2 * i]; + cvtr->execute(); // convert frequency to voltage, ignoring amplitude + // WriteAudioWDSP(20.0, rate, size, ftovbuff, 4, 0.99); + filt->execute(); // low-pass filter + // WriteAudioWDSP(20.0, rate, size, lpbuff, 4, 0.99); // calculate the output of the window detector for each sample - for (int i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - a->wdaverage = a->wdmult * a->wdaverage + (1.0 - a->wdmult) * a->lpbuff[i]; - if ((a->lpbuff[i] - a->wdaverage) > a->wthresh || (a->wdaverage - a->lpbuff[i]) > a->wthresh) - a->wdbuff[i] = 0; // signal unmute + wdaverage = wdmult * wdaverage + (1.0 - wdmult) * lpbuff[i]; + if ((lpbuff[i] - wdaverage) > wthresh || (wdaverage - lpbuff[i]) > wthresh) + wdbuff[i] = 0; // signal unmute else - a->wdbuff[i] = 1; // signal mute + wdbuff[i] = 1; // signal mute } // calculate the trigger signal for each sample - for (int i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - if (a->wdbuff[i] == 0) - a->tr_voltage += (a->tr_ss_unmute - a->tr_voltage) * a->unmute_mult; - if (a->wdbuff[i] == 1) - a->tr_voltage += (a->tr_ss_mute - a->tr_voltage) * a->mute_mult; - if (a->tr_voltage > a->tr_thresh) a->tr_signal[i] = 0; // muted - else a->tr_signal[i] = 1; // unmuted + if (wdbuff[i] == 0) + tr_voltage += (tr_ss_unmute - tr_voltage) * unmute_mult; + if (wdbuff[i] == 1) + tr_voltage += (tr_ss_mute - tr_voltage) * mute_mult; + if (tr_voltage > tr_thresh) tr_signal[i] = 0; // muted + else tr_signal[i] = 1; // unmuted } // execute state machine; calculate audio output - for (int i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - switch (a->state) + switch (state) { case MUTED: - if (a->tr_signal[i] == 1) + if (tr_signal[i] == 1) { - a->state = INCREASE; - a->count = a->ntup; + state = INCREASE; + count = ntup; } - a->out[2 * i + 0] = a->muted_gain * a->in[2 * i + 0]; - a->out[2 * i + 1] = a->muted_gain * a->in[2 * i + 1]; + out[2 * i + 0] = muted_gain * in[2 * i + 0]; + out[2 * i + 1] = muted_gain * in[2 * i + 1]; break; case INCREASE: - a->out[2 * i + 0] = a->in[2 * i + 0] * a->cup[a->ntup - a->count]; - a->out[2 * i + 1] = a->in[2 * i + 1] * a->cup[a->ntup - a->count]; - if (a->count-- == 0) - a->state = UNMUTED; + out[2 * i + 0] = in[2 * i + 0] * cup[ntup - count]; + out[2 * i + 1] = in[2 * i + 1] * cup[ntup - count]; + if (count-- == 0) + state = UNMUTED; break; case UNMUTED: - if (a->tr_signal[i] == 0) + if (tr_signal[i] == 0) { - a->state = DECREASE; - a->count = a->ntdown; + state = DECREASE; + count = ntdown; } - a->out[2 * i + 0] = a->in[2 * i + 0]; - a->out[2 * i + 1] = a->in[2 * i + 1]; + out[2 * i + 0] = in[2 * i + 0]; + out[2 * i + 1] = in[2 * i + 1]; break; case DECREASE: - a->out[2 * i + 0] = a->in[2 * i + 0] * a->cdown[a->ntdown - a->count]; - a->out[2 * i + 1] = a->in[2 * i + 1] * a->cdown[a->ntdown - a->count]; - if (a->count-- == 0) - a->state = MUTED; + out[2 * i + 0] = in[2 * i + 0] * cdown[ntdown - count]; + out[2 * i + 1] = in[2 * i + 1] * cdown[ntdown - count]; + if (count-- == 0) + state = MUTED; break; } } } - else if (a->in != a->out) - std::copy(a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy(in, in + size * 2, out); } -void SSQL::setBuffers_ssql (SSQL *a, float* in, float* out) +void SSQL::setBuffers(float* _in, float* _out) { - decalc_ssql (a); - a->in = in; - a->out = out; - calc_ssql (a); + decalc(); + in = _in; + out = _out; + calc(); } -void SSQL::setSamplerate_ssql (SSQL *a, int rate) +void SSQL::setSamplerate(int _rate) { - decalc_ssql (a); - a->rate = rate; - calc_ssql (a); + decalc(); + rate = _rate; + calc(); } -void SSQL::setSize_ssql (SSQL *a, int size) +void SSQL::setSize(int _size) { - decalc_ssql (a); - a->size = size; - calc_ssql (a); + decalc(); + size = _size; + calc(); } /******************************************************************************************************** @@ -348,34 +343,32 @@ void SSQL::setSize_ssql (SSQL *a, int size) * * ********************************************************************************************************/ -void SSQL::SetSSQLRun (RXA& rxa, int run) +void SSQL::setRun(int _run) { - rxa.ssql->run = run; + run = _run; } -void SSQL::SetSSQLThreshold (RXA& rxa, double threshold) +void SSQL::setThreshold(double _threshold) { // 'threshold' should be between 0.0 and 1.0 // WU2O testing: 0.16 is a good default for 'threshold'; => 0.08 for 'wthresh' - rxa.ssql->wthresh = threshold / 2.0; + wthresh = _threshold / 2.0; } -void SSQL::SetSSQLTauMute (RXA& rxa, double tau_mute) +void SSQL::setTauMute(double _tau_mute) { // reasonable (wide) range is 0.1 to 2.0 // WU2O testing: 0.1 is good default value - SSQL *a = rxa.ssql; - a->tr_tau_mute = tau_mute; - a->mute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_mute)); + tr_tau_mute = _tau_mute; + mute_mult = 1.0 - exp (-1.0 / (rate * tr_tau_mute)); } -void SSQL::SetSSQLTauUnMute (RXA& rxa, double tau_unmute) +void SSQL::setTauUnMute(double _tau_unmute) { // reasonable (wide) range is 0.1 to 1.0 // WU2O testing: 0.1 is good default value - SSQL *a = rxa.ssql; - a->tr_tau_unmute = tau_unmute; - a->unmute_mult = 1.0 - exp (-1.0 / (a->rate * a->tr_tau_unmute)); + tr_tau_unmute = _tau_unmute; + unmute_mult = 1.0 - exp (-1.0 / (rate * tr_tau_unmute)); } } // namespace WDSP diff --git a/wdsp/ssql.hpp b/wdsp/ssql.hpp index fe10ab6ff..62fbaf4ff 100644 --- a/wdsp/ssql.hpp +++ b/wdsp/ssql.hpp @@ -117,7 +117,7 @@ public: double unmute_mult; // multiplier for successive voltage calcs when unmuted int* tr_signal; // trigger signal, 0 or 1 - static SSQL* create_ssql ( + SSQL( int run, int size, float* in, @@ -133,22 +133,25 @@ public: int rsize, double fmax ); - static void destroy_ssql (SSQL *a); - static void flush_ssql (SSQL *a); - static void xssql (SSQL *a); - static void setBuffers_ssql (SSQL *a, float* in, float* out); - static void setSamplerate_ssql (SSQL *a, int rate); - static void setSize_ssql (SSQL *a, int size); + SSQL(const SSQL&) = delete; + SSQL& operator=(const SSQL& other) = delete; + ~SSQL(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // RXA Properties - static void SetSSQLRun (RXA& rxa, int run); - static void SetSSQLThreshold (RXA& rxa, double threshold); - static void SetSSQLTauMute (RXA& rxa, double tau_mute); - static void SetSSQLTauUnMute (RXA& rxa, double tau_unmute); + void setRun(int run); + void setThreshold(double threshold); + void setTauMute(double tau_mute); + void setTauUnMute(double tau_unmute); private: - static void compute_ssql_slews(SSQL *a); - static void calc_ssql (SSQL *a); - static void decalc_ssql (SSQL *a); + void compute_slews(); + void calc(); + void decalc(); }; } // namespace WDSP From e52f1c0cea4b83640793c4f6643f630b0348768a Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 1 Aug 2024 02:12:28 +0200 Subject: [PATCH 29/46] Do not run CI/CD for commits on feature branches --- .github/workflows/sdrangel.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sdrangel.yml b/.github/workflows/sdrangel.yml index d2fb392e2..c468e7399 100644 --- a/.github/workflows/sdrangel.yml +++ b/.github/workflows/sdrangel.yml @@ -6,7 +6,6 @@ on: push: branches: - master - - feature-* - mac_ci tags: - 'v*' From 4ddb6dc9ffbd597813243a01265e5fd338b51e55 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 1 Aug 2024 02:12:58 +0200 Subject: [PATCH 30/46] WDSP: removed unnecessary references to RXA and TXA --- wdsp/amd.cpp | 1 - wdsp/amd.hpp | 2 -- wdsp/amsq.cpp | 2 -- wdsp/amsq.hpp | 3 --- wdsp/anf.cpp | 1 - wdsp/anf.hpp | 2 -- wdsp/anr.cpp | 1 - wdsp/anr.hpp | 2 -- wdsp/bandpass.cpp | 2 -- wdsp/bandpass.hpp | 2 -- wdsp/bpsnba.cpp | 1 - wdsp/bpsnba.hpp | 2 -- wdsp/cblock.cpp | 1 - wdsp/cblock.hpp | 2 -- wdsp/dsphp.cpp | 2 -- wdsp/emnr.cpp | 1 - wdsp/emnr.hpp | 2 -- wdsp/eqp.cpp | 2 -- wdsp/eqp.hpp | 2 -- wdsp/mpeak.cpp | 2 -- wdsp/patchpanel.cpp | 2 -- wdsp/patchpanel.hpp | 3 --- wdsp/phrot.cpp | 2 -- wdsp/sender.cpp | 1 - wdsp/sender.hpp | 1 - wdsp/siphon.cpp | 2 -- wdsp/siphon.hpp | 3 --- wdsp/snotch.cpp | 2 -- wdsp/speak.cpp | 2 -- wdsp/speak.hpp | 2 -- wdsp/sphp.cpp | 3 +-- wdsp/ssql.cpp | 1 - wdsp/ssql.hpp | 1 - wdsp/wcpAGC.cpp | 2 -- 34 files changed, 1 insertion(+), 61 deletions(-) diff --git a/wdsp/amd.cpp b/wdsp/amd.cpp index 7db5f2858..c4283700f 100644 --- a/wdsp/amd.cpp +++ b/wdsp/amd.cpp @@ -33,7 +33,6 @@ warren@wpratt.com #include "emnr.hpp" #include "anr.hpp" #include "snba.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/amd.hpp b/wdsp/amd.hpp index 3fb7a91a2..edff60deb 100644 --- a/wdsp/amd.hpp +++ b/wdsp/amd.hpp @@ -43,8 +43,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API AMD { public: int run; diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index 215646c12..39dcc9ae7 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "amsq.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index 02819475d..b2a74bf32 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -33,9 +33,6 @@ warren@wpratt.com namespace WDSP { -class RXA; -class TXA; - class WDSP_API AMSQ { public: diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index 81edd9578..f19cd7358 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -32,7 +32,6 @@ warren@wpratt.com #include "anr.hpp" #include "anf.hpp" #include "bandpass.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/anf.hpp b/wdsp/anf.hpp index 73fc1f052..8a914b462 100644 --- a/wdsp/anf.hpp +++ b/wdsp/anf.hpp @@ -36,8 +36,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API ANF { public: diff --git a/wdsp/anr.cpp b/wdsp/anr.cpp index de998264a..21ad450ca 100644 --- a/wdsp/anr.cpp +++ b/wdsp/anr.cpp @@ -32,7 +32,6 @@ warren@wpratt.com #include "emnr.hpp" #include "anf.hpp" #include "bandpass.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/anr.hpp b/wdsp/anr.hpp index 8adeebc16..f328afcb5 100644 --- a/wdsp/anr.hpp +++ b/wdsp/anr.hpp @@ -36,8 +36,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API ANR { public: diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index 78845ad1f..17dff842e 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -29,8 +29,6 @@ warren@wpratt.com #include "bandpass.hpp" #include "fir.hpp" #include "fircore.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/bandpass.hpp b/wdsp/bandpass.hpp index 3af29ffe2..1b784e85e 100644 --- a/wdsp/bandpass.hpp +++ b/wdsp/bandpass.hpp @@ -46,8 +46,6 @@ warren@wpratt.com namespace WDSP { class FIRCORE; -class RXA; -class TXA; class WDSP_API BANDPASS { diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index a271c0aa3..6a33911b5 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -35,7 +35,6 @@ warren@wpratt.com #include "anr.hpp" #include "emnr.hpp" #include "bpsnba.hpp" -#include "RXA.hpp" #define MAXIMP 256 diff --git a/wdsp/bpsnba.hpp b/wdsp/bpsnba.hpp index 28221eb63..0b1c97505 100644 --- a/wdsp/bpsnba.hpp +++ b/wdsp/bpsnba.hpp @@ -30,8 +30,6 @@ warren@wpratt.com namespace WDSP{ -class RXA; - class NOTCHDB; class NBP; diff --git a/wdsp/cblock.cpp b/wdsp/cblock.cpp index 6514ea9c8..ded8bc628 100644 --- a/wdsp/cblock.cpp +++ b/wdsp/cblock.cpp @@ -27,7 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "cblock.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/cblock.hpp b/wdsp/cblock.hpp index 2bd781b4a..b9e77c309 100644 --- a/wdsp/cblock.hpp +++ b/wdsp/cblock.hpp @@ -32,8 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API CBL { public: diff --git a/wdsp/dsphp.cpp b/wdsp/dsphp.cpp index e08141968..d0a78c398 100644 --- a/wdsp/dsphp.cpp +++ b/wdsp/dsphp.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "dsphp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index 11ecd1f55..2385b4548 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -34,7 +34,6 @@ warren@wpratt.com #include "anf.hpp" #include "snba.hpp" #include "bandpass.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/emnr.hpp b/wdsp/emnr.hpp index 58f99390e..79e1ea126 100644 --- a/wdsp/emnr.hpp +++ b/wdsp/emnr.hpp @@ -36,8 +36,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API EMNR { public: diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index 4dda77be2..a4c1fe5b1 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -29,8 +29,6 @@ warren@wpratt.com #include "eqp.hpp" #include "fircore.hpp" #include "fir.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp index 32423d0c7..fd844c434 100644 --- a/wdsp/eqp.hpp +++ b/wdsp/eqp.hpp @@ -41,8 +41,6 @@ warren@wpratt.com namespace WDSP { class FIRCORE; -class RXA; -class TXA; class WDSP_API EQP { diff --git a/wdsp/mpeak.cpp b/wdsp/mpeak.cpp index 8541a4d98..ea4318563 100644 --- a/wdsp/mpeak.cpp +++ b/wdsp/mpeak.cpp @@ -28,8 +28,6 @@ warren@wpratt.com #include "comm.hpp" #include "mpeak.hpp" #include "speak.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/patchpanel.cpp b/wdsp/patchpanel.cpp index 0aa051485..9499846a5 100644 --- a/wdsp/patchpanel.cpp +++ b/wdsp/patchpanel.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "patchpanel.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/patchpanel.hpp b/wdsp/patchpanel.hpp index 0c86ad122..4de9aaa4a 100644 --- a/wdsp/patchpanel.hpp +++ b/wdsp/patchpanel.hpp @@ -32,9 +32,6 @@ warren@wpratt.com namespace WDSP { -class RXA; -class TXA; - class WDSP_API PANEL { public: diff --git a/wdsp/phrot.cpp b/wdsp/phrot.cpp index 1dcbb0826..d7821554e 100644 --- a/wdsp/phrot.cpp +++ b/wdsp/phrot.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "phrot.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/sender.cpp b/wdsp/sender.cpp index e8aaf811d..cc55b31fd 100644 --- a/wdsp/sender.cpp +++ b/wdsp/sender.cpp @@ -27,7 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "sender.hpp" -#include "RXA.hpp" #include "bufferprobe.hpp" namespace WDSP { diff --git a/wdsp/sender.hpp b/wdsp/sender.hpp index a174690b2..200104794 100644 --- a/wdsp/sender.hpp +++ b/wdsp/sender.hpp @@ -37,7 +37,6 @@ warren@wpratt.com namespace WDSP { -class RXA; class BufferProbe; class WDSP_API SENDER diff --git a/wdsp/siphon.cpp b/wdsp/siphon.cpp index 930e75a76..c7ca1ec5f 100644 --- a/wdsp/siphon.cpp +++ b/wdsp/siphon.cpp @@ -28,8 +28,6 @@ warren@wpratt.com #include "comm.hpp" #include "meterlog10.hpp" #include "siphon.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/siphon.hpp b/wdsp/siphon.hpp index f753ca581..bad4065d3 100644 --- a/wdsp/siphon.hpp +++ b/wdsp/siphon.hpp @@ -38,9 +38,6 @@ warren@wpratt.com namespace WDSP { -class RXA; -class TXA; - class WDSP_API SIPHON { public: diff --git a/wdsp/snotch.cpp b/wdsp/snotch.cpp index e2bdb5498..d26e6a5ad 100644 --- a/wdsp/snotch.cpp +++ b/wdsp/snotch.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "snotch.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/speak.cpp b/wdsp/speak.cpp index 97de8e14b..b2a1f0a20 100644 --- a/wdsp/speak.cpp +++ b/wdsp/speak.cpp @@ -27,8 +27,6 @@ warren@wpratt.com #include "comm.hpp" #include "speak.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/speak.hpp b/wdsp/speak.hpp index a6d18fcd3..b5602a1f2 100644 --- a/wdsp/speak.hpp +++ b/wdsp/speak.hpp @@ -40,8 +40,6 @@ warren@wpratt.com namespace WDSP { -class RXA; - class WDSP_API SPEAK { public: diff --git a/wdsp/sphp.cpp b/wdsp/sphp.cpp index af59c8488..2b392b6fe 100644 --- a/wdsp/sphp.cpp +++ b/wdsp/sphp.cpp @@ -25,9 +25,8 @@ warren@wpratt.com */ +#include "comm.hpp" #include "sphp.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index 3375b7e9f..69ec65aa0 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -29,7 +29,6 @@ warren@pratt.one #include "cblock.hpp" #include "ssql.hpp" #include "dbqlp.hpp" -#include "RXA.hpp" namespace WDSP { diff --git a/wdsp/ssql.hpp b/wdsp/ssql.hpp index 62fbaf4ff..04261304e 100644 --- a/wdsp/ssql.hpp +++ b/wdsp/ssql.hpp @@ -71,7 +71,6 @@ public: class CBL; class FTDV; class DBQLP; -class RXA; class WDSP_API SSQL // Syllabic Squelch { diff --git a/wdsp/wcpAGC.cpp b/wdsp/wcpAGC.cpp index e1f9a8a42..455bc856d 100644 --- a/wdsp/wcpAGC.cpp +++ b/wdsp/wcpAGC.cpp @@ -34,8 +34,6 @@ Santa Cruz, CA 95060 #include "comm.hpp" #include "nbp.hpp" #include "wcpAGC.hpp" -#include "RXA.hpp" -#include "TXA.hpp" namespace WDSP { From 8941835466748db69f02be1763600221b0932838 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 2 Aug 2024 08:01:46 +0200 Subject: [PATCH 31/46] WDSP: Sonar lint fixes (1) --- wdsp/RXA.cpp | 4 +- wdsp/amd.cpp | 39 +++--- wdsp/amd.hpp | 14 +- wdsp/amsq.cpp | 65 ++++------ wdsp/amsq.hpp | 11 +- wdsp/anb.cpp | 55 +++++--- wdsp/anf.cpp | 60 +++++---- wdsp/anf.hpp | 3 +- wdsp/anr.cpp | 21 +-- wdsp/anr.hpp | 3 +- wdsp/bandpass.cpp | 72 +++-------- wdsp/bpsnba.cpp | 11 +- wdsp/bpsnba.hpp | 7 +- wdsp/cblock.cpp | 16 ++- wdsp/dsphp.cpp | 2 +- wdsp/dsphp.hpp | 9 +- wdsp/emnr.cpp | 316 ++++++++++++++++++++++++---------------------- wdsp/emnr.hpp | 13 +- wdsp/fir.cpp | 250 ++++++++++++++++++------------------ wdsp/fir.hpp | 4 +- 20 files changed, 501 insertions(+), 474 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index e915b1e2d..8053b8f98 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -354,7 +354,7 @@ RXA* RXA::create_rxa ( rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - ANF_DLINE_SIZE, // dline_size + ANF::ANF_DLINE_SIZE, // dline_size 64, // taps 16, // delay 0.0001, // two_mu @@ -374,7 +374,7 @@ RXA* RXA::create_rxa ( rxa->dsp_size, // buffer size rxa->midbuff, // pointer to input buffer rxa->midbuff, // pointer to output buffer - ANR_DLINE_SIZE, // dline_size + ANR::ANR_DLINE_SIZE, // dline_size 64, // taps 16, // delay 0.0001, // two_mu diff --git a/wdsp/amd.cpp b/wdsp/amd.cpp index c4283700f..6b1aefe3f 100644 --- a/wdsp/amd.cpp +++ b/wdsp/amd.cpp @@ -26,6 +26,7 @@ warren@wpratt.com */ #include +#include #include "comm.hpp" #include "amd.hpp" @@ -116,15 +117,19 @@ void AMD::flush() void AMD::execute() { - int i; double audio; - double vco[2]; - double corr[2]; + std::array vco; + std::array corr; double det; double del_out; - double ai, bi, aq, bq; - double ai_ps, bi_ps, aq_ps, bq_ps; - int j, k; + double ai; + double bi; + double aq; + double bq; + double ai_ps; + double bi_ps; + double aq_ps; + double bq_ps; if (run) { @@ -133,7 +138,7 @@ void AMD::execute() case 0: //AM Demodulator { - for (i = 0; i < buff_size; i++) + for (int i = 0; i < buff_size; i++) { double xr = in_buff[2 * i + 0]; double xi = in_buff[2 * i + 1]; @@ -146,8 +151,8 @@ void AMD::execute() audio += dc_insert - dc; } - out_buff[2 * i + 0] = audio; - out_buff[2 * i + 1] = audio; + out_buff[2 * i + 0] = (float) audio; + out_buff[2 * i + 1] = (float) audio; } break; @@ -155,7 +160,7 @@ void AMD::execute() case 1: //Synchronous AM Demodulator with Sideband Separation { - for (i = 0; i < buff_size; i++) + for (int i = 0; i < buff_size; i++) { vco[0] = cos(phs); vco[1] = sin(phs); @@ -174,9 +179,9 @@ void AMD::execute() dsI = ai; dsQ = bq; - for (j = 0; j < STAGES; j++) + for (int j = 0; j < STAGES; j++) { - k = 3 * j; + int k = 3 * j; a[k + 3] = c0[j] * (a[k] - a[k + 5]) + a[k + 2]; b[k + 3] = c1[j] * (b[k] - b[k + 5]) + b[k + 2]; c[k + 3] = c0[j] * (c[k] - c[k + 5]) + c[k + 2]; @@ -188,7 +193,7 @@ void AMD::execute() bq_ps = c[OUT_IDX]; aq_ps = d[OUT_IDX]; - for (j = OUT_IDX + 2; j > 0; j--) + for (int j = OUT_IDX + 2; j > 0; j--) { a[j] = a[j - 1]; b[j] = b[j - 1]; @@ -217,6 +222,8 @@ void AMD::execute() audio = (ai_ps + bi_ps) - (aq_ps - bq_ps); break; } + default: + break; } if (levelfade) @@ -226,8 +233,8 @@ void AMD::execute() audio += dc_insert - dc; } - out_buff[2 * i + 0] = audio; - out_buff[2 * i + 1] = audio; + out_buff[2 * i + 0] = (float) audio; + out_buff[2 * i + 1] = (float) audio; if ((corr[0] == 0.0) && (corr[1] == 0.0)) corr[0] = 1.0; @@ -254,6 +261,8 @@ void AMD::execute() break; } + default: + break; } } else if (in_buff != out_buff) diff --git a/wdsp/amd.hpp b/wdsp/amd.hpp index edff60deb..6114e0be5 100644 --- a/wdsp/amd.hpp +++ b/wdsp/amd.hpp @@ -28,15 +28,6 @@ warren@wpratt.com #ifndef wdsp_amd_hpp #define wdsp_amd_hpp -// ff defines for sbdemod -#ifndef STAGES -#define STAGES 7 -#endif - -#ifndef OUT_IDX -#define OUT_IDX (3 * STAGES) -#endif - #include #include "export.h" @@ -61,13 +52,16 @@ public: double phs; // pll - phase accumulator double omega; // pll - locked pll frequency double fil_out; // pll - filter output - double g1, g2; // pll - filter gain parameters + double g1; // pll - filter gain parameters + double g2; // pll - filter gain parameters double tauR; // carrier removal time constant double tauI; // carrier insertion time constant double mtauR; // carrier removal multiplier double onem_mtauR; // 1.0 - carrier_removal_multiplier double mtauI; // carrier insertion multiplier double onem_mtauI; // 1.0 - carrier_insertion_multiplier + static const int STAGES = 7; + static const int OUT_IDX = 3 * STAGES; std::array a; // Filter a variables std::array b; // Filter b variables std::array c; // Filter c variables diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index 39dcc9ae7..e4c145cdd 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -32,12 +32,12 @@ namespace WDSP { void AMSQ::compute_slews() { - int i; - double delta, theta; + double delta; + double theta; delta = PI / (double)ntup; theta = 0.0; - for (i = 0; i <= ntup; i++) + for (int i = 0; i <= ntup; i++) { cup[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 - cos (theta)); theta += delta; @@ -46,7 +46,7 @@ void AMSQ::compute_slews() delta = PI / (double)ntdown; theta = 0.0; - for (i = 0; i <= ntdown; i++) + for (int i = 0; i <= ntdown; i++) { cdown[i] = muted_gain + (1.0 - muted_gain) * 0.5 * (1.0 + cos (theta)); theta += delta; @@ -63,11 +63,11 @@ void AMSQ::calc() // level change ntup = (int)(tup * rate); ntdown = (int)(tdown * rate); - cup.resize((ntup + 1) * 2); // (float *)malloc0((ntup + 1) * sizeof(float)); - cdown.resize((ntdown + 1) * 2); // (float *)malloc0((ntdown + 1) * sizeof(float)); + cup.resize((ntup + 1) * 2); + cdown.resize((ntdown + 1) * 2); compute_slews(); // control - state = 0; + state = AMSQState::MUTED; } AMSQ::AMSQ ( @@ -108,26 +108,17 @@ void AMSQ::flush() { std::fill(trigsig.begin(), trigsig.end(), 0); avsig = 0.0; - state = 0; + state = AMSQState::MUTED; } -enum _amsqstate -{ - MUTED, - INCREASE, - UNMUTED, - TAIL, - DECREASE -}; - void AMSQ::execute() { if (run) { - int i; - double sig, siglimit; + double sig; + double siglimit; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { double trigr = trigsig[2 * i + 0]; double trigi = trigsig[2 * i + 1]; @@ -136,31 +127,31 @@ void AMSQ::execute() switch (state) { - case MUTED: + case AMSQState::MUTED: if (avsig > unmute_thresh) { - state = INCREASE; + state = AMSQState::INCREASE; count = ntup; } - out[2 * i + 0] = muted_gain * in[2 * i + 0]; - out[2 * i + 1] = muted_gain * in[2 * i + 1]; + out[2 * i + 0] = (float) (muted_gain * in[2 * i + 0]); + out[2 * i + 1] = (float) (muted_gain * in[2 * i + 1]); break; - case INCREASE: - out[2 * i + 0] = in[2 * i + 0] * cup[ntup - count]; - out[2 * i + 1] = in[2 * i + 1] * cup[ntup - count]; + case AMSQState::INCREASE: + out[2 * i + 0] = (float) (in[2 * i + 0] * cup[ntup - count]); + out[2 * i + 1] = (float) (in[2 * i + 1] * cup[ntup - count]); if (count-- == 0) - state = UNMUTED; + state = AMSQState::UNMUTED; break; - case UNMUTED: + case AMSQState::UNMUTED: if (avsig < tail_thresh) { - state = TAIL; + state = AMSQState::TAIL; if ((siglimit = avsig) > 1.0) siglimit = 1.0; @@ -173,28 +164,28 @@ void AMSQ::execute() break; - case TAIL: + case AMSQState::TAIL: out[2 * i + 0] = in[2 * i + 0]; out[2 * i + 1] = in[2 * i + 1]; if (avsig > unmute_thresh) { - state = UNMUTED; + state = AMSQState::UNMUTED; } else if (count-- == 0) { - state = DECREASE; + state = AMSQState::TAIL; count = ntdown; } break; - case DECREASE: - out[2 * i + 0] = in[2 * i + 0] * cdown[ntdown - count]; - out[2 * i + 1] = in[2 * i + 1] * cdown[ntdown - count]; + case AMSQState::DECREASE: + out[2 * i + 0] = (float) (in[2 * i + 0] * cdown[ntdown - count]); + out[2 * i + 1] = (float) (in[2 * i + 1] * cdown[ntdown - count]); if (count-- == 0) - state = MUTED; + state = AMSQState::MUTED; break; } diff --git a/wdsp/amsq.hpp b/wdsp/amsq.hpp index b2a74bf32..eca16dad4 100644 --- a/wdsp/amsq.hpp +++ b/wdsp/amsq.hpp @@ -36,6 +36,15 @@ namespace WDSP { class WDSP_API AMSQ { public: + enum class AMSQState + { + MUTED, + INCREASE, + UNMUTED, + TAIL, + DECREASE + }; + int run; // 0 if squelch system is OFF; 1 if it's ON int size; // size of input/output buffers float* in; // squelch input signal buffer @@ -47,7 +56,7 @@ public: double avm; double onem_avm; double avsig; - int state; // state machine control + AMSQState state; // state machine control int count; double tup; double tdown; diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 33f3d95e9..7b3183909 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -36,7 +36,6 @@ namespace WDSP { void ANB::initBlanker() { - int i; trans_count = (int)(tau * samplerate); if (trans_count < 2) @@ -54,7 +53,7 @@ void ANB::initBlanker() backmult = exp(-1.0 / (samplerate * backtau)); ombackmult = 1.0 - backmult; - for (i = 0; i <= trans_count; i++) + for (int i = 0; i <= trans_count; i++) wave[i] = 0.5 * cos(i * coef); std::fill(dline.begin(), dline.end(), 0); @@ -76,23 +75,44 @@ ANB::ANB ( buffsize(_buffsize), in(_in), out(_out), + dline_size((int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1), samplerate(_samplerate), tau(_tau), hangtime(_hangtime), advtime(_advtime), backtau(_backtau), - threshold(_threshold), - dtime(0), - htime(0), - itime(0), - atime(0) + threshold(_threshold) { - tau = tau < 0.0 ? 0.0 : (tau > MAX_TAU ? MAX_TAU : tau); - hangtime = hangtime < 0.0 ? 0.0 : (hangtime > MAX_ADVTIME ? MAX_ADVTIME : hangtime); - advtime = advtime < 0.0 ? 0.0 : (advtime > MAX_ADVTIME ? MAX_ADVTIME : advtime); - samplerate = samplerate < 0.0 ? 0.0 : (samplerate > MAX_SAMPLERATE ? MAX_SAMPLERATE : samplerate); + dtime = 0; + htime = 0; + itime = 0; + atime = 0; + + if (tau < 0.0) { + tau = 0.0; + } else if (tau > MAX_TAU) { + tau = MAX_TAU; + } + + if (hangtime < 0.0) { + hangtime = 0.0; + } else if (hangtime > MAX_ADVTIME) { + hangtime = MAX_ADVTIME; + } + + if (advtime < 0.0) { + advtime = 0.0; + } else if (advtime > MAX_ADVTIME) { + advtime = MAX_ADVTIME; + } + + if (samplerate < 0.0) { + samplerate = 0.0; + } else if (samplerate > MAX_SAMPLERATE) { + samplerate = MAX_SAMPLERATE; + } + wave.resize((int)(MAX_SAMPLERATE * MAX_TAU) + 1); - dline_size = (int)((MAX_TAU + MAX_ADVTIME) * MAX_SAMPLERATE) + 1; dline.resize(dline_size * 2); initBlanker(); } @@ -106,11 +126,10 @@ void ANB::execute() { double scale; double mag; - int i; if (run) { - for (i = 0; i < buffsize; i++) + for (int i = 0; i < buffsize; i++) { double xr = in[2 * i + 0]; double xi = in[2 * i + 1]; @@ -139,8 +158,8 @@ void ANB::execute() case 1: scale = power * (0.5 + wave[dtime]); - out[2 * i + 0] = dline[2 * out_idx + 0] * scale; - out[2 * i + 1] = dline[2 * out_idx + 1] * scale; + out[2 * i + 0] = (float) (dline[2 * out_idx + 0] * scale); + out[2 * i + 1] = (float) (dline[2 * out_idx + 1] * scale); if (++dtime > trans_count) { @@ -177,8 +196,8 @@ void ANB::execute() case 4: scale = 0.5 - wave[itime]; - out[2 * i + 0] = dline[2 * out_idx + 0] * scale; - out[2 * i + 1] = dline[2 * out_idx + 1] * scale; + out[2 * i + 0] = (float) (dline[2 * out_idx + 0] * scale); + out[2 * i + 1] = (float) (dline[2 * out_idx + 1] * scale); if (count > 0) { diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index f19cd7358..cee3e3341 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -53,49 +53,53 @@ ANF::ANF( double _den_mult, double _lincr, double _ldecr -) +) : + run(_run), + position(_position), + buff_size(_buff_size), + in_buff(_in_buff), + out_buff(_out_buff), + dline_size(_dline_size), + mask(_dline_size - 1), + n_taps(_n_taps), + delay(_delay), + two_mu(_two_mu), + gamma(_gamma), + lidx(_lidx), + lidx_min(_lidx_min), + lidx_max(_lidx_max), + ngamma(_ngamma), + den_mult(_den_mult), + lincr(_lincr), + ldecr(_ldecr) { - run = _run; - position = _position; - buff_size = _buff_size; - in_buff = _in_buff; - out_buff = _out_buff; - dline_size = _dline_size; - mask = _dline_size - 1; - n_taps = _n_taps; - delay = _delay; - two_mu = _two_mu; - gamma = _gamma; in_idx = 0; - lidx = _lidx; - lidx_min = _lidx_min; - lidx_max = _lidx_max; - ngamma = _ngamma; - den_mult = _den_mult; - lincr = _lincr; - ldecr = _ldecr; - std::fill(d.begin(), d.end(), 0); std::fill(w.begin(), w.end(), 0); } void ANF::execute(int _position) { - int i, j, idx; - double c0, c1; - double y, error, sigma, inv_sigp; - double nel, nev; + int idx; + double c0; + double c1; + double y; + double error; + double sigma; + double inv_sigp; + double nel; + double nev; if (run && (position == _position)) { - for (i = 0; i < buff_size; i++) + for (int i = 0; i < buff_size; i++) { d[in_idx] = in_buff[2 * i + 0]; y = 0; sigma = 0; - for (j = 0; j < n_taps; j++) + for (int j = 0; j < n_taps; j++) { idx = (in_idx + j + delay) & mask; y += w[j] * d[idx]; @@ -105,7 +109,7 @@ void ANF::execute(int _position) inv_sigp = 1.0 / (sigma + 1e-10); error = d[in_idx] - y; - out_buff[2 * i + 0] = error; + out_buff[2 * i + 0] = (float) error; out_buff[2 * i + 1] = 0.0; if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0) @@ -128,7 +132,7 @@ void ANF::execute(int _position) c0 = 1.0 - two_mu * ngamma; c1 = two_mu * error * inv_sigp; - for (j = 0; j < n_taps; j++) + for (int j = 0; j < n_taps; j++) { idx = (in_idx + j + delay) & mask; w[j] = c0 * w[j] + c1 * d[idx]; diff --git a/wdsp/anf.hpp b/wdsp/anf.hpp index 8a914b462..898baa985 100644 --- a/wdsp/anf.hpp +++ b/wdsp/anf.hpp @@ -32,8 +32,6 @@ warren@wpratt.com #include "export.h" -#define ANF_DLINE_SIZE 2048 - namespace WDSP { class WDSP_API ANF @@ -50,6 +48,7 @@ public: int delay; double two_mu; double gamma; + static const int ANF_DLINE_SIZE = 2048; std::array d; std::array w; int in_idx; diff --git a/wdsp/anr.cpp b/wdsp/anr.cpp index 21ad450ca..66028e814 100644 --- a/wdsp/anr.cpp +++ b/wdsp/anr.cpp @@ -80,21 +80,26 @@ ANR::ANR( void ANR::execute(int _position) { - int i, j, idx; - double c0, c1; - double y, error, sigma, inv_sigp; - double nel, nev; + int idx; + double c0; + double c1; + double y; + double error; + double sigma; + double inv_sigp; + double nel; + double nev; if (run && (position == _position)) { - for (i = 0; i < buff_size; i++) + for (int i = 0; i < buff_size; i++) { d[in_idx] = in_buff[2 * i + 0]; y = 0; sigma = 0; - for (j = 0; j < n_taps; j++) + for (int j = 0; j < n_taps; j++) { idx = (in_idx + j + delay) & mask; y += w[j] * d[idx]; @@ -104,7 +109,7 @@ void ANR::execute(int _position) inv_sigp = 1.0 / (sigma + 1e-10); error = d[in_idx] - y; - out_buff[2 * i + 0] = y; + out_buff[2 * i + 0] = (float) y; out_buff[2 * i + 1] = 0.0; if ((nel = error * (1.0 - two_mu * sigma * inv_sigp)) < 0.0) @@ -127,7 +132,7 @@ void ANR::execute(int _position) c0 = 1.0 - two_mu * ngamma; c1 = two_mu * error * inv_sigp; - for (j = 0; j < n_taps; j++) + for (int j = 0; j < n_taps; j++) { idx = (in_idx + j + delay) & mask; w[j] = c0 * w[j] + c1 * d[idx]; diff --git a/wdsp/anr.hpp b/wdsp/anr.hpp index f328afcb5..143010cf8 100644 --- a/wdsp/anr.hpp +++ b/wdsp/anr.hpp @@ -32,8 +32,6 @@ warren@wpratt.com #include "export.h" -#define ANR_DLINE_SIZE 2048 - namespace WDSP { class WDSP_API ANR @@ -50,6 +48,7 @@ public: int delay; double two_mu; double gamma; + static const int ANR_DLINE_SIZE = 2048; std::array d; std::array w; int in_idx; diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index 17dff842e..a730d2106 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -51,21 +51,21 @@ BANDPASS::BANDPASS( int _samplerate, int _wintype, double _gain -) -{ +) : // NOTE: 'nc' must be >= 'size' - run = _run; - position = _position; - size = _size; - nc = _nc; - mp = _mp; - in = _in; - out = _out; - f_low = _f_low; - f_high = _f_high; - samplerate = _samplerate; - wintype = _wintype; - gain = _gain; + run(_run), + position(_position), + size(_size), + nc(_nc), + mp(_mp), + in(_in), + out(_out), + f_low(_f_low), + f_high(_f_high), + samplerate(_samplerate), + wintype(_wintype), + gain(_gain) +{ float* impulse = FIR::fir_bandpass ( nc, f_low, @@ -136,7 +136,7 @@ void BANDPASS::setSize(int _size) gain / (double) (2 * size) ); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void BANDPASS::setGain(double _gain, int _update) @@ -152,7 +152,7 @@ void BANDPASS::setGain(double _gain, int _update) gain / (double) (2 * size) ); FIRCORE::setImpulse_fircore (fircore, impulse, _update); - delete[] (impulse); + delete[] impulse; } void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) @@ -172,7 +172,7 @@ void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) gain / (double)(2 * size) ); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } } @@ -197,7 +197,7 @@ void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) ); FIRCORE::setImpulse_fircore (fircore, impulse, 0); - delete[] (impulse); + delete[] impulse; f_low = _f_low; f_high = _f_high; FIRCORE::setUpdate_fircore (fircore); @@ -220,7 +220,7 @@ void BANDPASS::SetBandpassNC(int _nc) gain / (double)( 2 * size) ); FIRCORE::setNc_fircore (fircore, nc, impulse); - delete[] (impulse); + delete[] impulse; } } @@ -239,38 +239,4 @@ void BANDPASS::SetBandpassMP(int _mp) * * ********************************************************************************************************/ -//PORT -//void SetTXABandpassFreqs (int channel, float f_low, float f_high) -//{ -// float* impulse; -// BANDPASS a; -// a = txa.bp0; -// if ((f_low != a->f_low) || (f_high != a->f_high)) -// { -// a->f_low = f_low; -// a->f_high = f_high; -// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->fircore, impulse, 1); -// delete[] (impulse); -// } -// a = txa.bp1; -// if ((f_low != a->f_low) || (f_high != a->f_high)) -// { -// a->f_low = f_low; -// a->f_high = f_high; -// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->fircore, impulse, 1); -// delete[] (impulse); -// } -// a = txa.bp2; -// if ((f_low != a->f_low) || (f_high != a->f_high)) -// { -// a->f_low = f_low; -// a->f_high = f_high; -// impulse = fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain / (float)(2 * a->size)); -// setImpulse_fircore (a->fircore, impulse, 1); -// delete[] (impulse); -// } -//} - } // namespace WDSP diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index 6a33911b5..f7a3884f9 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -52,7 +52,7 @@ namespace WDSP { void BPSNBA::calc() { - buff = new float[size * 2]; // (double *) malloc0 (size * sizeof (complex)); + buff.resize(size * 2); bpsnba = new NBP ( 1, // run, always runs (use bpsnba 'run') run_notches, // run the notches @@ -60,7 +60,7 @@ void BPSNBA::calc() size, // buffer size nc, // number of filter coefficients mp, // minimum phase flag - buff, // pointer to input buffer + buff.data(), // pointer to input buffer out, // pointer to output buffer f_low, // lower filter frequency f_high, // upper filter frequency @@ -116,8 +116,7 @@ BPSNBA::BPSNBA( void BPSNBA::decalc() { - delete (bpsnba); - delete[] (buff); + delete bpsnba; } BPSNBA::~BPSNBA() @@ -127,7 +126,7 @@ BPSNBA::~BPSNBA() void BPSNBA::flush() { - std::fill(buff, buff + size * 2, 0); + std::fill(buff.begin(), buff.end(), 0); bpsnba->flush(); } @@ -156,7 +155,7 @@ void BPSNBA::setSize(int _size) void BPSNBA::exec_in(int _position) { if (run && position == _position) - std::copy(in, in + size * 2, buff); + std::copy(in, in + size * 2, buff.begin()); } void BPSNBA::exec_out(int _position) diff --git a/wdsp/bpsnba.hpp b/wdsp/bpsnba.hpp index 0b1c97505..5fe602799 100644 --- a/wdsp/bpsnba.hpp +++ b/wdsp/bpsnba.hpp @@ -28,12 +28,15 @@ warren@wpratt.com #ifndef wdsp_bpsnba_h #define wdsp_bpsnba_h +#include + +#include "export.h" namespace WDSP{ class NOTCHDB; class NBP; -class BPSNBA +class WDSP_API BPSNBA { public: int run; // run the filter @@ -49,7 +52,7 @@ public: double abs_high_freq; // highest positive freq supported by SNG double f_low; // low cutoff frequency double f_high; // high cutoff frequency - float* buff; // internal buffer + std::vector buff; // internal buffer int wintype; // filter window type double gain; // filter gain int autoincr; // use auto increment for notch width diff --git a/wdsp/cblock.cpp b/wdsp/cblock.cpp index ded8bc628..1548d1698 100644 --- a/wdsp/cblock.cpp +++ b/wdsp/cblock.cpp @@ -71,22 +71,24 @@ void CBL::execute() { if (run) { - int i; - double tempI, tempQ; + double tempI; + double tempQ; - for (i = 0; i < buff_size; i++) + for (int i = 0; i < buff_size; i++) { tempI = in_buff[2 * i + 0]; tempQ = in_buff[2 * i + 1]; - out_buff[2 * i + 0] = in_buff[2 * i + 0] - prevIin + mtau * prevIout; - out_buff[2 * i + 1] = in_buff[2 * i + 1] - prevQin + mtau * prevQout; + out_buff[2 * i + 0] = (float) (in_buff[2 * i + 0] - prevIin + mtau * prevIout); + out_buff[2 * i + 1] = (float) (in_buff[2 * i + 1] - prevQin + mtau * prevQout); prevIin = tempI; prevQin = tempQ; + prevIout = out_buff[2 * i + 0]; + prevQout = out_buff[2 * i + 1]; - if (fabs(prevIout = out_buff[2 * i + 0]) < 1.0e-20) + if (fabs(prevIout) < 1.0e-20) prevIout = 0.0; - if (fabs(prevQout = out_buff[2 * i + 1]) < 1.0e-20) + if (fabs(prevQout) < 1.0e-20) prevQout = 0.0; } } diff --git a/wdsp/dsphp.cpp b/wdsp/dsphp.cpp index d0a78c398..4c6ad9981 100644 --- a/wdsp/dsphp.cpp +++ b/wdsp/dsphp.cpp @@ -97,7 +97,7 @@ void DSPHP::execute() x1[n] = x0[n]; } - out[i] = y0[nstages - 1]; + out[i] = (float) y0[nstages - 1]; } } else if (out != in) diff --git a/wdsp/dsphp.hpp b/wdsp/dsphp.hpp index 28e304f37..8a30e8c54 100644 --- a/wdsp/dsphp.hpp +++ b/wdsp/dsphp.hpp @@ -50,8 +50,13 @@ public: double rate; double fc; int nstages; - double a1, b0, b1; - std::vector x0, x1, y0, y1; + double a1; + double b0; + double b1; + std::vector x0; + std::vector x1; + std::vector y0; + std::vector y1; DSPHP( int run, diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index 2385b4548..6baca2b2c 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -72,10 +72,10 @@ EMNR::NPS::NPS( epsH1(_epsH1) { epsH1r = epsH1 / (1.0 + epsH1); - sigma2N.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); - PH1y.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); - Pbar.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); - EN2y.resize(msize); // (float *)malloc0(nps.msize * sizeof(float)); + sigma2N.resize(msize); + PH1y.resize(msize); + Pbar.resize(msize); + EN2y.resize(msize); for (int i = 0; i < msize; i++) { @@ -86,9 +86,8 @@ EMNR::NPS::NPS( void EMNR::NPS::LambdaDs() { - int k; - for (k = 0; k < msize; k++) + for (int k = 0; k < msize; k++) { PH1y[k] = 1.0 / (1.0 + (1.0 + epsH1) * exp (- epsH1r * lambda_y[k] / sigma2N[k])); Pbar[k] = alpha_Pbar * Pbar[k] + (1.0 - alpha_Pbar) * PH1y[k]; @@ -122,31 +121,17 @@ EMNR::NP::NP( lambda_d(_lambda_d) { - { - double tau = -128.0 / 8000.0 / log(0.7); - alphaCsmooth = exp(-incr / rate / tau); - } - - { - double tau = -128.0 / 8000.0 / log(0.96); - alphaMax = exp(-incr / rate / tau); - } - - { - double tau = -128.0 / 8000.0 / log(0.7); - alphaCmin = exp(-incr / rate / tau); - } - - { - double tau = -128.0 / 8000.0 / log(0.3); - alphaMin_max_value = exp(-incr / rate / tau); - } - + double tau0 = -128.0 / 8000.0 / log(0.7); + alphaCsmooth = exp(-incr / rate / tau0); + double tau1 = -128.0 / 8000.0 / log(0.96); + alphaMax = exp(-incr / rate / tau1); + double tau2 = -128.0 / 8000.0 / log(0.7); + alphaCmin = exp(-incr / rate / tau2); + double tau3 = -128.0 / 8000.0 / log(0.3); + alphaMin_max_value = exp(-incr / rate / tau3); snrq = -incr / (0.064 * rate); - double tau4 = -128.0 / 8000.0 / log(0.8); betamax = exp(-incr / rate / tau4); - invQeqMax = 0.5; av = 2.12; Dtime = 8.0 * 12.0 * 128.0 / 8000.0; @@ -166,68 +151,63 @@ EMNR::NP::NP( invQbar_points[0] = 0.03; invQbar_points[1] = 0.05; invQbar_points[2] = 0.06; - invQbar_points[3] = std::numeric_limits::max();; + invQbar_points[3] = std::numeric_limits::max(); - { - double db; - db = 10.0 * log10(8.0) / (12.0 * 128 / 8000); - nsmax[0] = pow(10.0, db / 10.0 * V * incr / rate); - db = 10.0 * log10(4.0) / (12.0 * 128 / 8000); - nsmax[1] = pow(10.0, db / 10.0 * V * incr / rate); - db = 10.0 * log10(2.0) / (12.0 * 128 / 8000); - nsmax[2] = pow(10.0, db / 10.0 * V * incr / rate); - db = 10.0 * log10(1.2) / (12.0 * 128 / 8000); - nsmax[3] = pow(10.0, db / 10.0 * V * incr / rate); - } + double db; + db = 10.0 * log10(8.0) / (12.0 * 128 / 8000); + nsmax[0] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(4.0) / (12.0 * 128 / 8000); + nsmax[1] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(2.0) / (12.0 * 128 / 8000); + nsmax[2] = pow(10.0, db / 10.0 * V * incr / rate); + db = 10.0 * log10(1.2) / (12.0 * 128 / 8000); + nsmax[3] = pow(10.0, db / 10.0 * V * incr / rate); - p.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - alphaOptHat.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - alphaHat.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - sigma2N.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - pbar.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - p2bar.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - Qeq.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - bmin.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - bmin_sub.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - k_mod.resize(msize); // (int *)malloc0(np.msize * sizeof(int)); - actmin.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - actmin_sub.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - lmin_flag.resize(msize); // (int *)malloc0(np.msize * sizeof(int)); - pmin_u.resize(msize); // (float *)malloc0(np.msize * sizeof(float)); - actminbuff.resize(U); // (float**)malloc0(np.U * sizeof(float*)); + p.resize(msize); + alphaOptHat.resize(msize); + alphaHat.resize(msize); + sigma2N.resize(msize); + pbar.resize(msize); + p2bar.resize(msize); + Qeq.resize(msize); + bmin.resize(msize); + bmin_sub.resize(msize); + k_mod.resize(msize); + actmin.resize(msize); + actmin_sub.resize(msize); + lmin_flag.resize(msize); + pmin_u.resize(msize); + actminbuff.resize(U); for (int i = 0; i < U; i++) { - actminbuff[i].resize(msize); // (float *)malloc0(np.msize * sizeof(float)); + actminbuff[i].resize(msize); } + alphaC = 1.0; + subwc = V; + amb_idx = 0; + + for (int k = 0; k < msize; k++) { + lambda_y[k] = 0.5; + } + + std::copy(lambda_y.begin(), lambda_y.end(), p.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), sigma2N.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), pbar.begin()); + std::copy(lambda_y.begin(), lambda_y.end(), pmin_u.begin()); + + for (int k = 0; k < msize; k++) { - int k, ku; - alphaC = 1.0; - subwc = V; - amb_idx = 0; + p2bar[k] = lambda_y[k] * lambda_y[k]; + actmin[k] = std::numeric_limits::max(); + actmin_sub[k] = std::numeric_limits::max(); - for (k = 0; k < msize; k++) { - lambda_y[k] = 0.5; + for (int ku = 0; ku < U; ku++) { + actminbuff[ku][k] = std::numeric_limits::max(); } - - std::copy(lambda_y.begin(), lambda_y.end(), p.begin()); - std::copy(lambda_y.begin(), lambda_y.end(), sigma2N.begin()); - std::copy(lambda_y.begin(), lambda_y.end(), pbar.begin()); - std::copy(lambda_y.begin(), lambda_y.end(), pmin_u.begin()); - - for (k = 0; k < msize; k++) - { - p2bar[k] = lambda_y[k] * lambda_y[k]; - actmin[k] = std::numeric_limits::max(); - actmin_sub[k] = std::numeric_limits::max();; - - for (ku = 0; ku < U; ku++) { - actminbuff[ku][k] = std::numeric_limits::max();; - } - } - - std::fill(lmin_flag.begin(), lmin_flag.end(), 0); } + + std::fill(lmin_flag.begin(), lmin_flag.end(), 0); } void EMNR::NP::interpM ( @@ -249,7 +229,9 @@ void EMNR::NP::interpM ( else { int idx = 1; - double xllow, xlhigh, frac; + double xllow; + double xlhigh; + double frac; while ((x >= xvals[idx]) && (idx < nvals - 1)) idx++; @@ -264,16 +246,23 @@ void EMNR::NP::interpM ( void EMNR::NP::LambdaD() { int k; - double f0, f1, f2, f3; + double f0; + double f1; + double f2; + double f3; double sum_prev_p; double sum_lambda_y; double alphaCtilda; double sum_prev_sigma2N; - double alphaMin, SNR; - double beta, varHat, invQeq; + double alphaMin; + double SNR; + double beta; + double varHat; + double invQeq; double invQbar; double bc; - double QeqTilda, QeqTildaSub; + double QeqTilda; + double QeqTildaSub; double noise_slope_max; sum_prev_p = 0.0; @@ -369,7 +358,7 @@ void EMNR::NP::LambdaD() lmin_flag[k] = 0; actminbuff[amb_idx][k] = actmin[k]; - min = std::numeric_limits::max();; + min = std::numeric_limits::max(); for (ku = 0; ku < U; ku++) { @@ -389,8 +378,8 @@ void EMNR::NP::LambdaD() } lmin_flag[k] = 0; - actmin[k] = std::numeric_limits::max();; - actmin_sub[k] = std::numeric_limits::max();; + actmin[k] = std::numeric_limits::max(); + actmin_sub[k] = std::numeric_limits::max(); } if (++amb_idx == U) @@ -433,17 +422,15 @@ EMNR::G::G( y(_y) { - lambda_y.resize(msize); // (float *)malloc0(msize * sizeof(float)); - lambda_d.resize(msize); // (float *)malloc0(msize * sizeof(float)); - prev_gamma.resize(msize); // (float *)malloc0(msize * sizeof(float)); - prev_mask.resize(msize); // (float *)malloc0(msize * sizeof(float)); + lambda_y.resize(msize); + lambda_d.resize(msize); + prev_gamma.resize(msize); + prev_mask.resize(msize); gf1p5 = sqrt(PI) / 2.0; - { - double tau = -128.0 / 8000.0 / log(0.98); - alpha = exp(-incr / rate / tau); - } + double tau = -128.0 / 8000.0 / log(0.98); + alpha = exp(-incr / rate / tau); eps_floor = std::numeric_limits::min(); gamma_max = 1000.0; @@ -480,7 +467,9 @@ EMNR::G::G( void EMNR::G::calc_gamma0() { - double gamma, eps_hat, v; + double gamma; + double eps_hat; + double v; for (int k = 0; k < msize; k++) { @@ -490,20 +479,15 @@ void EMNR::G::calc_gamma0() v = (eps_hat / (1.0 + eps_hat)) * gamma; mask[k] = gf1p5 * sqrt (v) / gamma * exp (- 0.5 * v) * ((1.0 + v) * bessI0 (0.5 * v) + v * bessI1 (0.5 * v)); - { - double v2 = std::min (v, 700.0); - double eta = mask[k] * mask[k] * lambda_y[k] / lambda_d[k]; - double eps = eta / (1.0 - q); - double witchHat = (1.0 - q) / q * exp (v2) / (1.0 + eps); - mask[k] *= witchHat / (1.0 + witchHat); - } + double v2 = std::min (v, 700.0); + double eta = mask[k] * mask[k] * lambda_y[k] / lambda_d[k]; + double eps = eta / (1.0 - q); + double witchHat = (1.0 - q) / q * exp (v2) / (1.0 + eps); + mask[k] *= witchHat / (1.0 + witchHat); if (mask[k] > gmax) mask[k] = gmax; - if (mask[k] != mask[k]) - mask[k] = 0.01; - prev_gamma[k] = gamma; prev_mask[k] = mask[k]; } @@ -511,7 +495,10 @@ void EMNR::G::calc_gamma0() void EMNR::G::calc_gamma1() { - double gamma, eps_hat, v, ehr; + double gamma; + double eps_hat; + double v; + double ehr; for (int k = 0; k < msize; k++) { @@ -524,9 +511,6 @@ void EMNR::G::calc_gamma1() if ((mask[k] = ehr * exp (std::min (700.0, 0.5 * e1xb(v)))) > gmax) mask[k] = gmax; - if (mask[k] != mask[k]) - mask[k] = 0.01; - prev_gamma[k] = gamma; prev_mask[k] = mask[k]; } @@ -534,7 +518,9 @@ void EMNR::G::calc_gamma1() void EMNR::G::calc_gamma2() { - double gamma, eps_hat, eps_p; + double gamma; + double eps_hat; + double eps_p; for (int k = 0; k < msize; k++) { @@ -572,7 +558,8 @@ void EMNR::G::calc_lambda_y() double EMNR::G::bessI0 (double x) { - double res, p; + double res; + double p; if (x == 0.0) { @@ -616,7 +603,8 @@ double EMNR::G::bessI0 (double x) double EMNR::G::bessI1 (double x) { - double res, p; + double res; + double p; if (x == 0.0) { @@ -667,12 +655,17 @@ double EMNR::G::bessI1 (double x) double EMNR::G::e1xb (double x) { - double e1, ga, r, t, t0; - int k, m; + double e1; + double ga; + double r; + double t; + double t0; + int k; + int m; if (x == 0.0) { - e1 = std::numeric_limits::max();; + e1 = std::numeric_limits::max(); } else if (x <= 1.0) { @@ -715,26 +708,25 @@ double EMNR::G::e1xb (double x) void EMNR::calc_window() { int i; - double arg, sum, inv_coherent_gain; + double arg; + double sum; + double inv_coherent_gain; - switch (wintype) + if (wintype == 0) { - case 0: arg = 2.0 * PI / (double) fsize; sum = 0.0; for (i = 0; i < fsize; i++) { - window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg)); + window[i] = (float) (sqrt (0.54 - 0.46 * cos((float)i * arg))); sum += window[i]; } inv_coherent_gain = (double) fsize / sum; for (i = 0; i < fsize; i++) - window[i] *= inv_coherent_gain; - - break; + window[i] *= (float) inv_coherent_gain; } } @@ -771,20 +763,20 @@ void EMNR::calc() init_oainidx = oainidx; oaoutidx = 0; msize = fsize / 2 + 1; - window.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); - inaccum.resize(iasize); // (float *)malloc0(iasize * sizeof(float)); - forfftin.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); - forfftout.resize(msize * 2); // (float *)malloc0(msize * sizeof(complex)); - mask.resize(msize); // (float *)malloc0(msize * sizeof(float)); + window.resize(fsize); + inaccum.resize(iasize); + forfftin.resize(fsize); + forfftout.resize(msize * 2); + mask.resize(msize); std::fill(mask.begin(), mask.end(), 1.0); - revfftin.resize(msize * 2); // (float *)malloc0(msize * sizeof(complex)); - revfftout.resize(fsize); // (float *)malloc0(fsize * sizeof(float)); - save.resize(ovrlp); // (float **)malloc0(ovrlp * sizeof(float *)); + revfftin.resize(msize * 2); + revfftout.resize(fsize); + save.resize(ovrlp); for (int i = 0; i < ovrlp; i++) - save[i].resize(fsize); // (float *)malloc0(fsize * sizeof(float)); + save[i].resize(fsize); - outaccum.resize(oasize); // (float *)malloc0(oasize * sizeof(float)); + outaccum.resize(oasize); nsamps = 0; saveidx = 0; Rfor = fftwf_plan_dft_r2c_1d( @@ -853,10 +845,10 @@ void EMNR::calc() void EMNR::decalc() { - delete (ae); - delete (nps); - delete (np); - delete (g); + delete ae; + delete nps; + delete np; + delete g; fftwf_destroy_plan(Rrev); fftwf_destroy_plan(Rfor); @@ -896,10 +888,9 @@ EMNR::EMNR( void EMNR::flush() { - int i; std::fill(inaccum.begin(), inaccum.end(), 0); - for (i = 0; i < ovrlp; i++) + for (int i = 0; i < ovrlp; i++) std::fill(save[i].begin(), save[i].end(), 0); std::fill(outaccum.begin(), outaccum.end(), 0); @@ -918,9 +909,13 @@ EMNR::~EMNR() void EMNR::aepf() { - int k, m; - int N, n; - double sumPre, sumPost, zeta, zetaT; + int k; + int N; + int n; + double sumPre; + double sumPost; + double zeta; + double zetaT; sumPre = 0.0; sumPost = 0.0; @@ -947,7 +942,7 @@ void EMNR::aepf() for (k = n; k < (ae->msize - n); k++) { ae->nmask[k] = 0.0; - for (m = k - n; m <= (k + n); m++) + for (int m = k - n; m <= (k + n); m++) ae->nmask[k] += mask[m]; ae->nmask[k] /= (float)N; } @@ -957,8 +952,14 @@ void EMNR::aepf() double EMNR::G::getKey(const std::array& type, double gamma, double xi) { - int ngamma1, ngamma2, nxi1 = 0, nxi2 = 0; - double tg, tx, dg, dx; + int ngamma1; + int ngamma2; + int nxi1 = 0; + int nxi2 = 0; + double tg; + double tx; + double dg; + double dx; const double dmin = 0.001; const double dmax = 1000.0; @@ -1005,8 +1006,13 @@ double EMNR::G::getKey(const std::array& type, double gamma, do ix[2] = 241 * nxi1 + ngamma2; ix[3] = 241 * nxi2 + ngamma2; - for (auto& ixi : ix) { - ixi = ixi < 0 ? 0 : (ixi >= 241*241 ? 241*241 - 1 : ixi); + for (auto& ixi : ix) + { + if (ixi < 0) { + ixi = 0; + } else if (ixi >= 241*241) { + ixi = 241*241 - 1; + } } return (1.0 - dg) * (1.0 - dx) * type[ix[0]] @@ -1027,6 +1033,8 @@ void EMNR::calc_gain() case 1: nps->LambdaDs(); break; + default: + break; } switch (g->gain_method) @@ -1040,6 +1048,8 @@ void EMNR::calc_gain() case 2: g->calc_gamma2(); break; + default: + break; } if (g->ae_run) @@ -1050,7 +1060,11 @@ void EMNR::execute(int _pos) { if (run && _pos == position) { - int i, j, k, sbuff, sbegin; + int i; + int j; + int k; + int sbuff; + int sbegin; double g1; for (i = 0; i < 2 * bsize; i += 2) @@ -1074,8 +1088,8 @@ void EMNR::execute(int _pos) for (i = 0; i < msize; i++) { g1 = gain * mask[i]; - revfftin[2 * i + 0] = g1 * forfftout[2 * i + 0]; - revfftin[2 * i + 1] = g1 * forfftout[2 * i + 1]; + revfftin[2 * i + 0] = (float) (g1 * forfftout[2 * i + 0]); + revfftin[2 * i + 1] = (float) (g1 * forfftout[2 * i + 1]); } fftwf_execute (Rrev); diff --git a/wdsp/emnr.hpp b/wdsp/emnr.hpp index 79e1ea126..043dcc4ac 100644 --- a/wdsp/emnr.hpp +++ b/wdsp/emnr.hpp @@ -119,7 +119,8 @@ public: static double e1xb (double x); static double bessI0 (double x); static double bessI1 (double x); - } *g; + }; + G *g; struct NP { @@ -186,7 +187,8 @@ public: const std::array& xvals, const std::array& yvals ); - } *np; + }; + NP *np; struct NPS { @@ -221,8 +223,8 @@ public: ~NPS() = default; void LambdaDs(); - } *nps; - + }; + NPS *nps; struct AE { int msize; @@ -240,7 +242,8 @@ public: AE(const AE&) = delete; AE& operator=(const AE& other) = delete; ~AE() = default; - } *ae; + }; + AE *ae; EMNR( int run, diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index 269694c36..494e880db 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -27,6 +27,7 @@ warren@pratt.one #define _CRT_SECURE_NO_WARNINGS #include +#include #include "fftw3.h" #include "comm.hpp" @@ -36,132 +37,131 @@ namespace WDSP { float* FIR::fftcv_mults (int NM, float* c_impulse) { - float* mults = new float[NM * 2]; - float* cfft_impulse = new float[NM * 2]; + auto mults = new float[NM * 2]; + std::vector cfft_impulse(NM * 2); fftwf_plan ptmp = fftwf_plan_dft_1d( NM, - (fftwf_complex *) cfft_impulse, + (fftwf_complex *) cfft_impulse.data(), (fftwf_complex *) mults, FFTW_FORWARD, FFTW_PATIENT ); - std::fill(cfft_impulse, cfft_impulse + NM * 2, 0); + std::fill(cfft_impulse.begin(), cfft_impulse.end(), 0); // store complex coefs right-justified in the buffer std::copy(c_impulse, c_impulse + (NM / 2 + 1) * 2, &(cfft_impulse[NM - 2])); fftwf_execute (ptmp); fftwf_destroy_plan (ptmp); - delete[] cfft_impulse; return mults; } float* FIR::get_fsamp_window(int N, int wintype) { - int i; - double arg0, arg1; - float* window = new float[N]; // (float *) malloc0 (N * sizeof(float)); + double arg0; + double arg1; + auto window = new float[N]; switch (wintype) { case 0: arg0 = 2.0 * PI / ((double)N - 1.0); - for (i = 0; i < N; i++) + for (int i = 0; i < N; i++) { arg1 = cos(arg0 * (double)i); - window[i] = +0.21747 + double val = +0.21747 + arg1 * (-0.45325 + arg1 * (+0.28256 + arg1 * (-0.04672))); + window[i] = (float) val; } break; case 1: arg0 = 2.0 * PI / ((double)N - 1.0); - for (i = 0; i < N; ++i) + for (int i = 0; i < N; ++i) { arg1 = cos(arg0 * (double)i); - window[i] = +6.3964424114390378e-02 + double val = +6.3964424114390378e-02 + arg1 * (-2.3993864599352804e-01 + arg1 * (+3.5015956323820469e-01 + arg1 * (-2.4774111897080783e-01 + arg1 * (+8.5438256055858031e-02 + arg1 * (-1.2320203369293225e-02 + arg1 * (+4.3778825791773474e-04)))))); + window[i] = (float) val; } break; default: - for (i = 0; i < N; i++) + for (int i = 0; i < N; i++) window[i] = 1.0; } return window; } -float* FIR::fir_fsamp_odd (int N, float* A, int rtype, double scale, int wintype) +float* FIR::fir_fsamp_odd (int N, const float* A, int rtype, double scale, int wintype) { - int i, j; int mid = (N - 1) / 2; - double mag, phs; - float* window; - float *fcoef = new float[N * 2]; - float *c_impulse = new float[N * 2]; + double mag; + double phs; + std::vector fcoef(N * 2); + auto *c_impulse = new float[N * 2]; fftwf_plan ptmp = fftwf_plan_dft_1d( N, - (fftwf_complex *)fcoef, + (fftwf_complex *)fcoef.data(), (fftwf_complex *)c_impulse, FFTW_BACKWARD, FFTW_PATIENT ); double local_scale = 1.0 / (double) N; - for (i = 0; i <= mid; i++) + for (int i = 0; i <= mid; i++) { mag = A[i] * local_scale; phs = - (double)mid * TWOPI * (double)i / (double)N; - fcoef[2 * i + 0] = mag * cos (phs); - fcoef[2 * i + 1] = mag * sin (phs); + fcoef[2 * i + 0] = (float) (mag * cos (phs)); + fcoef[2 * i + 1] = (float) (mag * sin (phs)); } - for (i = mid + 1, j = 0; i < N; i++, j++) + for (int i = mid + 1, j = 0; i < N; i++, j++) { fcoef[2 * i + 0] = + fcoef[2 * (mid - j) + 0]; fcoef[2 * i + 1] = - fcoef[2 * (mid - j) + 1]; } fftwf_execute (ptmp); fftwf_destroy_plan (ptmp); - delete[] fcoef; - window = get_fsamp_window(N, wintype); + float* window = get_fsamp_window(N, wintype); switch (rtype) { case 0: - for (i = 0; i < N; i++) - c_impulse[i] = scale * c_impulse[2 * i] * window[i]; + for (int i = 0; i < N; i++) + c_impulse[i] = (float) (scale * c_impulse[2 * i] * window[i]); break; case 1: - for (i = 0; i < N; i++) + for (int i = 0; i < N; i++) { - c_impulse[2 * i + 0] *= scale * window[i]; + c_impulse[2 * i + 0] *= (float) (scale * window[i]); c_impulse[2 * i + 1] = 0.0; } break; + default: + break; } delete[] window; return c_impulse; } -float* FIR::fir_fsamp (int N, float* A, int rtype, double scale, int wintype) +float* FIR::fir_fsamp (int N, const float* A, int rtype, double scale, int wintype) { - int n, i, j, k; double sum; - float* window; - float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex)); + auto c_impulse = new float[N * 2]; if (N & 1) { int M = (N - 1) / 2; - for (n = 0; n < M + 1; n++) + for (int n = 0; n < M + 1; n++) { sum = 0.0; - for (k = 1; k < M + 1; k++) + for (int k = 1; k < M + 1; k++) sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N); - c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum); + c_impulse[2 * n + 0] = (float) ((1.0 / N) * (A[0] + sum)); c_impulse[2 * n + 1] = 0.0; } - for (n = M + 1, j = 1; n < N; n++, j++) + for (int n = M + 1, j = 1; n < N; n++, j++) { c_impulse[2 * n + 0] = c_impulse[2 * (M - j) + 0]; c_impulse[2 * n + 1] = 0.0; @@ -170,34 +170,36 @@ float* FIR::fir_fsamp (int N, float* A, int rtype, double scale, int wintype) else { double M = (double)(N - 1) / 2.0; - for (n = 0; n < N / 2; n++) + for (int n = 0; n < N / 2; n++) { sum = 0.0; - for (k = 1; k < N / 2; k++) + for (int k = 1; k < N / 2; k++) sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N); - c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum); + c_impulse[2 * n + 0] = (float) ((1.0 / N) * (A[0] + sum)); c_impulse[2 * n + 1] = 0.0; } - for (n = N / 2, j = 1; n < N; n++, j++) + for (int n = N / 2, j = 1; n < N; n++, j++) { c_impulse[2 * n + 0] = c_impulse[2 * (N / 2 - j) + 0]; c_impulse[2 * n + 1] = 0.0; } } - window = get_fsamp_window (N, wintype); + float* window = get_fsamp_window (N, wintype); switch (rtype) { case 0: - for (i = 0; i < N; i++) - c_impulse[i] = scale * c_impulse[2 * i] * window[i]; + for (int i = 0; i < N; i++) + c_impulse[i] = (float) (scale * c_impulse[2 * i] * window[i]); break; case 1: - for (i = 0; i < N; i++) + for (int i = 0; i < N; i++) { - c_impulse[2 * i + 0] *= scale * window[i]; + c_impulse[2 * i + 0] *= (float) (scale * window[i]); c_impulse[2 * i + 1] = 0.0; } break; + default: + break; } delete[] window; return c_impulse; @@ -205,45 +207,42 @@ float* FIR::fir_fsamp (int N, float* A, int rtype, double scale, int wintype) float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale) { - float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex)); + auto *c_impulse = new float[N * 2]; double ft = (f_high - f_low) / (2.0 * samplerate); double ft_rad = TWOPI * ft; double w_osc = PI * (f_high + f_low) / samplerate; - int i, j; double m = 0.5 * (double)(N - 1); double delta = PI / m; double cosphi; - double posi, posj; - double sinc, window, coef; + double posi; + double posj; + double sinc; + double window; + double coef; if (N & 1) { switch (rtype) { case 0: - c_impulse[N >> 1] = scale * 2.0 * ft; + c_impulse[N >> 1] = (float) (scale * 2.0 * ft); break; case 1: - c_impulse[N - 1] = scale * 2.0 * ft; + c_impulse[N - 1] = (float) (scale * 2.0 * ft); c_impulse[ N ] = 0.0; break; + default: + break; } } - for (i = (N + 1) / 2, j = N / 2 - 1; i < N; i++, j--) + for (int i = (N + 1) / 2, j = N / 2 - 1; i < N; i++, j--) { posi = (double)i - m; posj = (double)j - m; sinc = sin (ft_rad * posi) / (PI * posi); - switch (wintype) + + if (wintype == 1) // Blackman-Harris 7-term { - case 0: // Blackman-Harris 4-term - cosphi = cos (delta * i); - window = + 0.21747 - + cosphi * ( - 0.45325 - + cosphi * ( + 0.28256 - + cosphi * ( - 0.04672 ))); - break; - case 1: // Blackman-Harris 7-term cosphi = cos (delta * i); window = + 6.3964424114390378e-02 + cosphi * ( - 2.3993864599352804e-01 @@ -252,20 +251,31 @@ float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, + cosphi * ( + 8.5438256055858031e-02 + cosphi * ( - 1.2320203369293225e-02 + cosphi * ( + 4.3778825791773474e-04 )))))); - break; } + else // Blackman-Harris 4-term + { + cosphi = cos (delta * i); + window = + 0.21747 + + cosphi * ( - 0.45325 + + cosphi * ( + 0.28256 + + cosphi * ( - 0.04672 ))); + } + coef = scale * sinc * window; + switch (rtype) { case 0: - c_impulse[i] = + coef * cos (posi * w_osc); - c_impulse[j] = + coef * cos (posj * w_osc); + c_impulse[i] = (float) (+ coef * cos (posi * w_osc)); + c_impulse[j] = (float) (+ coef * cos (posj * w_osc)); break; case 1: - c_impulse[2 * i + 0] = + coef * cos (posi * w_osc); - c_impulse[2 * i + 1] = - coef * sin (posi * w_osc); - c_impulse[2 * j + 0] = + coef * cos (posj * w_osc); - c_impulse[2 * j + 1] = - coef * sin (posj * w_osc); + c_impulse[2 * i + 0] = (float) (+ coef * cos (posi * w_osc)); + c_impulse[2 * i + 1] = (float) (- coef * sin (posi * w_osc)); + c_impulse[2 * j + 0] = (float) (+ coef * cos (posj * w_osc)); + c_impulse[2 * j + 1] = (float) (- coef * sin (posj * w_osc)); + break; + default: break; } } @@ -282,11 +292,17 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale) // NOTE: The number of values in the file must NOT exceed those implied by N and rtype { FILE *file; - int i; - float I, Q; - float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex)); + float I; + float Q; + auto c_impulse = new float[N * 2]; + std::fill(c_impulse, c_impulse + N*2, 0); file = fopen (filename, "r"); - for (i = 0; i < N; i++) + + if (!file) { + return c_impulse; + } + + for (int i = 0; i < N; i++) { // read in the complex impulse response // NOTE: IF the freq response is symmetrical about 0, the imag coeffs will all be zero. @@ -309,6 +325,8 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale) c_impulse[2 * i + 1] = - scale * Q; break; } + default: + break; } } fclose (file); @@ -317,77 +335,74 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale) void FIR::analytic (int N, float* in, float* out) { - if (N < 1) { + if (N < 2) { return; } - int i; double inv_N = 1.0 / (double) N; double two_inv_N = 2.0 * inv_N; - float* x = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex)); + std::vector x(N * 2); fftwf_plan pfor = fftwf_plan_dft_1d ( N, (fftwf_complex *) in, - (fftwf_complex *) x, + (fftwf_complex *) x.data(), FFTW_FORWARD, FFTW_PATIENT ); fftwf_plan prev = fftwf_plan_dft_1d ( N, - (fftwf_complex *) x, + (fftwf_complex *) x.data(), (fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT ); fftwf_execute (pfor); - x[0] *= inv_N; - x[1] *= inv_N; + x[0] *= (float) inv_N; + x[1] *= (float) inv_N; - for (i = 1; i < N / 2; i++) + for (int i = 1; i < N / 2; i++) { - x[2 * i + 0] *= two_inv_N; - x[2 * i + 1] *= two_inv_N; + x[2 * i + 0] *= (float) two_inv_N; + x[2 * i + 1] *= (float) two_inv_N; } - x[N + 0] *= inv_N; - x[N + 1] *= inv_N; + x[N + 0] *= (float) inv_N; + x[N + 1] *= (float) inv_N; memset (&x[N + 2], 0, (N - 2) * sizeof (float)); fftwf_execute (prev); fftwf_destroy_plan (prev); fftwf_destroy_plan (pfor); - - delete[] x; } void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity) { int i; int size = N * pfactor; - double inv_PN = 1.0 / (float)size; - float* firpad = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); - float* firfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); - double* mag = new double[size]; // (float *) malloc0 (size * sizeof (float)); - float* ana = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); - float* impulse = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); - float* newfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex)); - std::copy(fir, fir + N * 2, firpad); + double inv_PN = 1.0 / (double)size; + std::vector firpad(size * 2); + std::vector firfreq(size * 2); + std::vector mag(size); + std::vector ana(size * 2); + std::vector impulse(size * 2); + std::vector newfreq(size * 2); + std::copy(fir, fir + N * 2, firpad.begin()); fftwf_plan pfor = fftwf_plan_dft_1d ( size, - (fftwf_complex *) firpad, - (fftwf_complex *) firfreq, + (fftwf_complex *) firpad.data(), + (fftwf_complex *) firfreq.data(), FFTW_FORWARD, FFTW_PATIENT); fftwf_plan prev = fftwf_plan_dft_1d ( size, - (fftwf_complex *) newfreq, - (fftwf_complex *) impulse, + (fftwf_complex *) newfreq.data(), + (fftwf_complex *) impulse.data(), FFTW_BACKWARD, FFTW_PATIENT ); - // print_impulse("orig_imp.txt", N, fir, 1, 0); + fftwf_execute (pfor); for (i = 0; i < size; i++) { @@ -395,33 +410,27 @@ void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity) double xi = firfreq[2 * i + 1]; mag[i] = sqrt (xr*xr + xi*xi) * inv_PN; if (mag[i] > 0.0) - ana[2 * i + 0] = log (mag[i]); + ana[2 * i + 0] = (float) log (mag[i]); else ana[2 * i + 0] = log (std::numeric_limits::min()); } - analytic (size, ana, ana); + analytic (size, ana.data(), ana.data()); for (i = 0; i < size; i++) { - newfreq[2 * i + 0] = + mag[i] * cos (ana[2 * i + 1]); + newfreq[2 * i + 0] = (float) (+ mag[i] * cos (ana[2 * i + 1])); if (polarity) - newfreq[2 * i + 1] = + mag[i] * sin (ana[2 * i + 1]); + newfreq[2 * i + 1] = (float) (+ mag[i] * sin (ana[2 * i + 1])); else - newfreq[2 * i + 1] = - mag[i] * sin (ana[2 * i + 1]); + newfreq[2 * i + 1] = (float) (- mag[i] * sin (ana[2 * i + 1])); } fftwf_execute (prev); if (polarity) std::copy(&impulse[2 * (pfactor - 1) * N], &impulse[2 * (pfactor - 1) * N] + N * 2, mpfir); else - std::copy(impulse, impulse + N * 2, mpfir); - // print_impulse("min_imp.txt", N, mpfir, 1, 0); + std::copy(impulse.begin(), impulse.end(), mpfir); + fftwf_destroy_plan (prev); fftwf_destroy_plan (pfor); - delete[] (newfreq); - delete[] (impulse); - delete[] (ana); - delete[] (mag); - delete[] (firfreq); - delete[] (firpad); } // impulse response of a zero frequency filter comprising a cascade of two resonators, @@ -432,15 +441,15 @@ float* FIR::zff_impulse(int nc, float scale) int n_resdet = nc / 2 - 1; // size of single zero-frequency resonator with detrender int n_dresdet = 2 * n_resdet - 1; // size of two cascaded units; when we convolve these we get 2 * n - 1 length // allocate the single and make the values - float* resdet = new float[n_resdet]; // (float*)malloc0 (n_resdet * sizeof(float)); + std::vector resdet(n_resdet); // (float*)malloc0 (n_resdet * sizeof(float)); for (int i = 1, j = 0, k = n_resdet - 1; i < nc / 4; i++, j++, k--) resdet[j] = resdet[k] = (float)(i * (i + 1) / 2); resdet[nc / 4 - 1] = (float)(nc / 4 * (nc / 4 + 1) / 2); // print_impulse ("resdet", n_resdet, resdet, 0, 0); // allocate the float and complex versions and make the values - float* dresdet = new float[n_dresdet]; // (float*)malloc0 (n_dresdet * sizeof(float)); - float div = (float)((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor - float* c_dresdet = new float[nc * 2]; // (float*)malloc0 (nc * sizeof(complex)); + std::vector dresdet(n_dresdet); + auto div = (float) ((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor + auto c_dresdet = new float[nc * 2]; for (int n = 0; n < n_dresdet; n++) // convolve to make the cascade { for (int k = 0; k < n_resdet; k++) @@ -450,10 +459,7 @@ float* FIR::zff_impulse(int nc, float scale) c_dresdet[2 * n + 0] = dresdet[n] * scale; c_dresdet[2 * n + 1] = 0.0; } - // print_impulse("dresdet", n_dresdet, dresdet, 0, 0); - // print_impulse("c_dresdet", nc, c_dresdet, 1, 0); - delete[] (dresdet); - delete[] (resdet); + return c_dresdet; } diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index 895c58d87..dd60523fa 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -35,8 +35,8 @@ class WDSP_API FIR { public: static float* fftcv_mults (int NM, float* c_impulse); - static float* fir_fsamp_odd (int N, float* A, int rtype, double scale, int wintype); - static float* fir_fsamp (int N, float* A, int rtype, double scale, int wintype); + static float* fir_fsamp_odd (int N, const float* A, int rtype, double scale, int wintype); + static float* fir_fsamp (int N, const float* A, int rtype, double scale, int wintype); static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); static void mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity); From d6159067a82b9cca67265300fae5e4e5e69d9630 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 3 Aug 2024 11:05:12 +0200 Subject: [PATCH 32/46] WDSP: Sonar lint fixes (2) --- .gitignore | 1 + wdsp/amsq.cpp | 4 +- wdsp/eq.cpp | 378 ++++++++++---------------------------------- wdsp/eq.hpp | 47 ++++-- wdsp/eqp.cpp | 94 +++++------ wdsp/eqp.hpp | 6 +- wdsp/fmd.cpp | 115 ++++++++++---- wdsp/fmsq.cpp | 68 ++++---- wdsp/fmsq.hpp | 11 +- wdsp/gen.cpp | 123 +++++++------- wdsp/gen.hpp | 133 +++++++++------- wdsp/meter.cpp | 39 +++-- wdsp/meter.hpp | 2 +- wdsp/mpeak.cpp | 14 +- wdsp/nbp.cpp | 85 +++++----- wdsp/nbp.hpp | 8 +- wdsp/nob.cpp | 39 ++--- wdsp/patchpanel.cpp | 40 ++--- wdsp/phrot.cpp | 16 +- wdsp/phrot.hpp | 9 +- wdsp/resample.cpp | 51 +++--- wdsp/resample.hpp | 8 +- wdsp/sender.cpp | 19 +-- wdsp/shift.cpp | 16 +- wdsp/siphon.cpp | 157 ++++++------------ wdsp/siphon.hpp | 5 - wdsp/snba.cpp | 359 +++++++++++++++++++++-------------------- wdsp/snba.hpp | 138 +++++++++------- wdsp/snotch.cpp | 12 +- wdsp/snotch.hpp | 14 +- wdsp/speak.cpp | 58 ++++--- wdsp/speak.hpp | 15 +- wdsp/sphp.cpp | 9 +- wdsp/ssql.cpp | 112 ++++++------- wdsp/ssql.hpp | 25 +-- wdsp/wcpAGC.cpp | 38 +++-- wdsp/wcpAGC.hpp | 6 +- 37 files changed, 1091 insertions(+), 1183 deletions(-) diff --git a/.gitignore b/.gitignore index 5650b32d4..49afebe63 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ obj-x86_64-linux-gnu/* **/venv*/ *.pyc .DS_Store +.sonarlint ### Go ### # Binaries for programs and plugins diff --git a/wdsp/amsq.cpp b/wdsp/amsq.cpp index e4c145cdd..c84189395 100644 --- a/wdsp/amsq.cpp +++ b/wdsp/amsq.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "amsq.hpp" @@ -174,7 +176,7 @@ void AMSQ::execute() } else if (count-- == 0) { - state = AMSQState::TAIL; + state = AMSQState::DECREASE; count = ntdown; } diff --git a/wdsp/eq.cpp b/wdsp/eq.cpp index d1450e4e2..73ed34ee9 100644 --- a/wdsp/eq.cpp +++ b/wdsp/eq.cpp @@ -42,332 +42,124 @@ namespace WDSP { ********************************************************************************************************/ -float* EQ::eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype) +void EQ::eq_mults (std::vector& mults, int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype) { float* impulse = EQP::eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); - float* mults = FIR::fftcv_mults(2 * size, impulse); - delete[] (impulse); - return mults; + float* _mults = FIR::fftcv_mults(2 * size, impulse); + mults.resize(2 * size * 2); + std::copy(_mults, _mults + 2*size*2, mults.begin()); + delete[] _mults; + delete[] impulse; } -void EQ::calc_eq (EQ *a) +void EQ::calc() { - a->scale = 1.0 / (float)(2 * a->size); - a->infilt = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); - a->product = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); - a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT); - a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT); - a->mults = EQ::eq_mults(a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); + scale = (float) (1.0 / (float)(2 * size)); + infilt.resize(2 * size * 2); + product.resize(2 * size * 2); + CFor = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *) infilt.data(), + (fftwf_complex *) product.data(), + FFTW_FORWARD, + FFTW_PATIENT + ); + CRev = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *) product.data(), + (fftwf_complex *) out, + FFTW_BACKWARD, + FFTW_PATIENT + ); + EQ::eq_mults(mults, size, nfreqs, F.data(), G.data(), samplerate, scale, ctfmode, wintype); } -void EQ::decalc_eq (EQ *a) +void EQ::decalc() { - fftwf_destroy_plan(a->CRev); - fftwf_destroy_plan(a->CFor); - delete[] (a->mults); - delete[] (a->product); - delete[] (a->infilt); + fftwf_destroy_plan(CRev); + fftwf_destroy_plan(CFor); } -EQ* EQ::create_eq (int run, int size, float *in, float *out, int nfreqs, float* F, float* G, int ctfmode, int wintype, int samplerate) +EQ::EQ( + int _run, + int _size, + float *_in, + float *_out, + int _nfreqs, + const float* _F, + const float* _G, + int _ctfmode, + int _wintype, + int _samplerate +) : + run(_run), + size(_size), + in(_in), + out(_out), + nfreqs(_nfreqs), + ctfmode(_ctfmode), + wintype(_wintype), + samplerate((float) _samplerate) { - EQ *a = new EQ; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->nfreqs = nfreqs; - a->F = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = new float[a->nfreqs + 1]; // (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - a->ctfmode = ctfmode; - a->wintype = wintype; - a->samplerate = (float)samplerate; - calc_eq (a); - return a; + F.resize(nfreqs + 1); + G.resize(nfreqs + 1); + std::copy(_F, _F + nfreqs + 1, F.begin()); + std::copy(_G, _G + nfreqs + 1, G.begin()); + calc(); } -void EQ::destroy_eq (EQ *a) +EQ::~EQ() { - decalc_eq (a); - delete[] (a->G); - delete[] (a->F); - delete[] (a); + decalc(); } -void EQ::flush_eq (EQ *a) +void EQ::flush() { - std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0); + std::fill(infilt.begin(), infilt.end(), 0); } -void EQ::xeq (EQ *a) +void EQ::execute() { - int i; - float I, Q; - if (a->run) + float I; + float Q; + if (run) { - std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size])); - fftwf_execute (a->CFor); - for (i = 0; i < 2 * a->size; i++) + std::copy(in, in + size * 2, &(infilt[2 * size])); + fftwf_execute (CFor); + for (int i = 0; i < 2 * size; i++) { - I = a->product[2 * i + 0]; - Q = a->product[2 * i + 1]; - a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1]; - a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0]; + I = product[2 * i + 0]; + Q = product[2 * i + 1]; + product[2 * i + 0] = I * mults[2 * i + 0] - Q * mults[2 * i + 1]; + product[2 * i + 1] = I * mults[2 * i + 1] + Q * mults[2 * i + 0]; } - fftwf_execute (a->CRev); - std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt); + fftwf_execute (CRev); + std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt); } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void EQ::setBuffers_eq (EQ *a, float* in, float* out) +void EQ::setBuffers(float* _in, float* _out) { - decalc_eq (a); - a->in = in; - a->out = out; - calc_eq (a); + decalc(); + in = _in; + out = _out; + calc(); } -void EQ::setSamplerate_eq (EQ *a, int rate) +void EQ::setSamplerate(int _rate) { - decalc_eq (a); - a->samplerate = rate; - calc_eq (a); + decalc(); + samplerate = (float) _rate; + calc(); } -void EQ::setSize_eq (EQ *a, int size) +void EQ::setSize(int _size) { - decalc_eq (a); - a->size = size; - calc_eq (a); + decalc(); + size = _size; + calc(); } -/******************************************************************************************************** -* * -* Overlap-Save Equalizer: RXA Properties * -* * -********************************************************************************************************/ -/* // UNCOMMENT properties when a pointer is in place in rxa -PORT -void SetRXAEQRun (int channel, int run) -{ - ch.csDSP.lock(); - rxa.eq->run = run; - ch.csDSP.unlock(); -} - -PORT -void SetRXAEQProfile (int channel, int nfreqs, float* F, float* G) -{ - EQ a; - ch.csDSP.lock(); - a = rxa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = nfreqs; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetRXAEQCtfmode (int channel, int mode) -{ - EQ a; - ch.csDSP.lock(); - a = rxa.eq; - a->ctfmode = mode; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetRXAEQWintype (int channel, int wintype) -{ - EQ a; - ch.csDSP.lock(); - a = rxa.eq; - a->wintype = wintype; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetRXAGrphEQ (int channel, int *rxeq) -{ // three band equalizer (legacy compatibility) - EQ a; - ch.csDSP.lock(); - a = rxa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 4; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 150.0; - a->F[2] = 400.0; - a->F[3] = 1500.0; - a->F[4] = 6000.0; - a->G[0] = (float)rxeq[0]; - a->G[1] = (float)rxeq[1]; - a->G[2] = (float)rxeq[1]; - a->G[3] = (float)rxeq[2]; - a->G[4] = (float)rxeq[3]; - a->ctfmode = 0; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetRXAGrphEQ10 (int channel, int *rxeq) -{ // ten band equalizer (legacy compatibility) - EQ a; - int i; - ch.csDSP.lock(); - a = rxa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 10; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 32.0; - a->F[2] = 63.0; - a->F[3] = 125.0; - a->F[4] = 250.0; - a->F[5] = 500.0; - a->F[6] = 1000.0; - a->F[7] = 2000.0; - a->F[8] = 4000.0; - a->F[9] = 8000.0; - a->F[10] = 16000.0; - for (i = 0; i <= a->nfreqs; i++) - a->G[i] = (float)rxeq[i]; - a->ctfmode = 0; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} -*/ -/******************************************************************************************************** -* * -* Overlap-Save Equalizer: TXA Properties * -* * -********************************************************************************************************/ -/* // UNCOMMENT properties when a pointer is in place in rxa -PORT -void SetTXAEQRun (int channel, int run) -{ - ch.csDSP.lock(); - txa.eq->run = run; - ch.csDSP.unlock(); -} - -PORT -void SetTXAEQProfile (int channel, int nfreqs, float* F, float* G) -{ - EQ a; - ch.csDSP.lock(); - a = txa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = nfreqs; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - memcpy (a->F, F, (nfreqs + 1) * sizeof (float)); - memcpy (a->G, G, (nfreqs + 1) * sizeof (float)); - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetTXAEQCtfmode (int channel, int mode) -{ - EQ a; - ch.csDSP.lock(); - a = txa.eq; - a->ctfmode = mode; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetTXAEQMethod (int channel, int wintype) -{ - EQ a; - ch.csDSP.lock(); - a = txa.eq; - a->wintype = wintype; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetTXAGrphEQ (int channel, int *txeq) -{ // three band equalizer (legacy compatibility) - EQ a; - ch.csDSP.lock(); - a = txa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 4; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 150.0; - a->F[2] = 400.0; - a->F[3] = 1500.0; - a->F[4] = 6000.0; - a->G[0] = (float)txeq[0]; - a->G[1] = (float)txeq[1]; - a->G[2] = (float)txeq[1]; - a->G[3] = (float)txeq[2]; - a->G[4] = (float)txeq[3]; - a->ctfmode = 0; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} - -PORT -void SetTXAGrphEQ10 (int channel, int *txeq) -{ // ten band equalizer (legacy compatibility) - EQ a; - int i; - ch.csDSP.lock(); - a = txa.eq; - delete[] (a->G); - delete[] (a->F); - a->nfreqs = 10; - a->F = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->G = (float *) malloc0 ((a->nfreqs + 1) * sizeof (float)); - a->F[1] = 32.0; - a->F[2] = 63.0; - a->F[3] = 125.0; - a->F[4] = 250.0; - a->F[5] = 500.0; - a->F[6] = 1000.0; - a->F[7] = 2000.0; - a->F[8] = 4000.0; - a->F[9] = 8000.0; - a->F[10] = 16000.0; - for (i = 0; i <= a->nfreqs; i++) - a->G[i] = (float)txeq[i]; - a->ctfmode = 0; - delete[] (a->mults); - a->mults = eq_mults (a->size, a->nfreqs, a->F, a->G, a->samplerate, a->scale, a->ctfmode, a->wintype); - ch.csDSP.unlock(); -} -*/ - } // namespace WDSP diff --git a/wdsp/eq.hpp b/wdsp/eq.hpp index 66748cb55..473e66b1c 100644 --- a/wdsp/eq.hpp +++ b/wdsp/eq.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_eq_h #define wdsp_eq_h +#include + #include "fftw3.h" #include "export.h" @@ -47,11 +49,11 @@ public: float* in; float* out; int nfreqs; - float* F; - float* G; - float* infilt; - float* product; - float* mults; + std::vector F; + std::vector G; + std::vector infilt; + std::vector product; + std::vector mults; float scale; int ctfmode; int wintype; @@ -59,19 +61,32 @@ public: fftwf_plan CFor; fftwf_plan CRev; - static EQ* create_eq (int run, int size, float *in, float *out, int nfreqs, float* F, float* G, int ctfmode, int wintype, int samplerate); - // static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype); - static void destroy_eq (EQ *a); - static void flush_eq (EQ *a); - static void xeq (EQ *a); - static void setBuffers_eq (EQ *a, float* in, float* out); - static void setSamplerate_eq (EQ *a, int rate); - static void setSize_eq (EQ *a, int size); + EQ( + int run, + int size, + float *in, + float *out, + int nfreqs, + const float* F, + const float* G, + int ctfmode, + int wintype, + int samplerate + ); + EQ(const EQ&) = delete; + EQ& operator=(EQ& other) = delete; + ~EQ() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static float* eq_mults (int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype); - static void calc_eq (EQ *a); - static void decalc_eq (EQ *a); + static void eq_mults (std::vector& mults, int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index a4c1fe5b1..2edf81b5a 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -42,22 +42,27 @@ int EQP::fEQcompare (const void * a, const void * b) return 1; } -float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype) +float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, double samplerate, double scale, int ctfmode, int wintype) { - float* fp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); - float* gp = new float[nfreqs + 2]; // (float *) malloc0 ((nfreqs + 2) * sizeof (float)); - float* A = new float[N / 2 + 1]; // (float *) malloc0 ((N / 2 + 1) * sizeof (float)); - float* sary = new float[2 * nfreqs]; // (float *) malloc0 (2 * nfreqs * sizeof (float)); - double gpreamp, f, frac; + std::vector fp(nfreqs + 2); + std::vector gp(nfreqs + 2); + std::vector A(N / 2 + 1); + float* sary = new float[2 * nfreqs]; + + double gpreamp; + double f; + double frac; float* impulse; - int i, j, mid; + int i; + int j; + int mid; fp[0] = 0.0; fp[nfreqs + 1] = 1.0; gpreamp = G[0]; for (i = 1; i <= nfreqs; i++) { - fp[i] = 2.0 * F[i] / samplerate; + fp[i] = (float) (2.0 * F[i] / samplerate); if (fp[i] < 0.0) fp[i] = 0.0; @@ -97,7 +102,7 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate j++; frac = (f - fp[j]) / (fp[j + 1] - fp[j]); - A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; + A[i] = (float) (pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale); } } else @@ -110,14 +115,19 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate j++; frac = (f - fp[j]) / (fp[j + 1] - fp[j]); - A[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale; + A[i] = (float) (pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j] + gpreamp)) * scale); } } if (ctfmode == 0) { - int k, low, high; - double lowmag, highmag, flow4, fhigh4; + int k; + int low; + int high; + double lowmag; + double highmag; + double flow4; + double fhigh4; if (N & 1) { @@ -134,7 +144,7 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate f = (double)k / (double)mid; lowmag *= (f * f * f * f) / flow4; if (lowmag < 1.0e-20) lowmag = 1.0e-20; - A[k] = lowmag; + A[k] = (float) lowmag; } k = high; @@ -144,7 +154,7 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate f = (double)k / (double)mid; highmag *= fhigh4 / (f * f * f * f); if (highmag < 1.0e-20) highmag = 1.0e-20; - A[k] = highmag; + A[k] = (float) highmag; } } else @@ -162,7 +172,7 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate f = (double)k / (double)mid; lowmag *= (f * f * f * f) / flow4; if (lowmag < 1.0e-20) lowmag = 1.0e-20; - A[k] = lowmag; + A[k] = (float) lowmag; } k = high; @@ -172,21 +182,17 @@ float* EQP::eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate f = (double)k / (double)mid; highmag *= fhigh4 / (f * f * f * f); if (highmag < 1.0e-20) highmag = 1.0e-20; - A[k] = highmag; + A[k] = (float) highmag; } } } if (N & 1) - impulse = FIR::fir_fsamp_odd(N, A, 1, 1.0, wintype); + impulse = FIR::fir_fsamp_odd(N, A.data(), 1, 1.0, wintype); else - impulse = FIR::fir_fsamp(N, A, 1, 1.0, wintype); + impulse = FIR::fir_fsamp(N, A.data(), 1, 1.0, wintype); - // print_impulse("eq.txt", N, impulse, 1, 0); - delete[] (sary); - delete[] (A); - delete[] (gp); - delete[] (fp); + delete[] sary; return impulse; } @@ -220,8 +226,8 @@ EQP::EQP( in = _in; out = _out; nfreqs = _nfreqs; - F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); + G.resize(nfreqs + 1); std::copy(_F, _F + (_nfreqs + 1), F.begin()); std::copy(_G, _G + (_nfreqs + 1), G.begin()); ctfmode = _ctfmode; @@ -229,7 +235,7 @@ EQP::EQP( samplerate = (double) _samplerate; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); - delete[] (impulse); + delete[] impulse; } EQP::~EQP() @@ -263,7 +269,7 @@ void EQP::setSamplerate(int rate) samplerate = rate; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void EQP::setSize(int _size) @@ -273,7 +279,7 @@ void EQP::setSize(int _size) FIRCORE::setSize_fircore (fircore, size); impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } /******************************************************************************************************** @@ -296,7 +302,7 @@ void EQP::setNC(int _nc) nc = _nc; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setNc_fircore (fircore, nc, impulse); - delete[] (impulse); + delete[] impulse; } } @@ -313,13 +319,13 @@ void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) { float* impulse; nfreqs = _nfreqs; - F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); + G.resize(nfreqs + 1); std::copy(_F, _F + (_nfreqs + 1), F.begin()); std::copy(_G, _G + (_nfreqs + 1), G.begin()); impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void EQP::setCtfmode(int _mode) @@ -328,7 +334,7 @@ void EQP::setCtfmode(int _mode) ctfmode = _mode; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void EQP::setWintype(int _wintype) @@ -337,15 +343,15 @@ void EQP::setWintype(int _wintype) wintype = _wintype; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } -void EQP::setGrphEQ(int *rxeq) +void EQP::setGrphEQ(const int *rxeq) { // three band equalizer (legacy compatibility) float* impulse; nfreqs = 4; - F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); + G.resize(nfreqs + 1); F[1] = 150.0; F[2] = 400.0; F[3] = 1500.0; @@ -358,16 +364,15 @@ void EQP::setGrphEQ(int *rxeq) ctfmode = 0; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } -void EQP::setGrphEQ10(int *rxeq) +void EQP::setGrphEQ10(const int *rxeq) { // ten band equalizer (legacy compatibility) float* impulse; - int i; nfreqs = 10; - F.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); - G.resize(nfreqs + 1); // (float *) malloc0 ((nfreqs + 1) * sizeof (float)); + F.resize(nfreqs + 1); + G.resize(nfreqs + 1); F[1] = 32.0; F[2] = 63.0; F[3] = 125.0; @@ -378,13 +383,12 @@ void EQP::setGrphEQ10(int *rxeq) F[8] = 4000.0; F[9] = 8000.0; F[10] = 16000.0; - for (i = 0; i <= nfreqs; i++) + for (int i = 0; i <= nfreqs; i++) G[i] = (float)rxeq[i]; ctfmode = 0; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - // print_impulse ("rxeq.txt", nc, impulse, 1, 0); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } } // namespace WDSP diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp index fd844c434..df4167355 100644 --- a/wdsp/eqp.hpp +++ b/wdsp/eqp.hpp @@ -89,10 +89,10 @@ public: void setProfile(int nfreqs, const float* F, const float* G); void setCtfmode(int mode); void setWintype(int wintype); - void setGrphEQ(int *rxeq); - void setGrphEQ10(int *rxeq); + void setGrphEQ(const int *rxeq); + void setGrphEQ10(const int *rxeq); - static float* eq_impulse (int N, int nfreqs, float* F, float* G, double samplerate, double scale, int ctfmode, int wintype); + static float* eq_impulse (int N, int nfreqs, const float* F, const float* G, double samplerate, double scale, int ctfmode, int wintype); private: static int fEQcompare (const void * a, const void * b); diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index da74781c5..5dd043fae 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "fircore.hpp" #include "fcurve.hpp" @@ -91,8 +93,8 @@ void FMD::calc() void FMD::decalc() { - delete (plim); - delete (sntch); + delete plim; + delete sntch; } FMD::FMD( @@ -144,14 +146,24 @@ FMD::FMD( float* impulse; calc(); // de-emphasis filter - audio.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); - impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + audio.resize(size * 2); + impulse = FCurve::fc_impulse ( + nc_de, + (float) f_low, + (float) f_high, + (float) (+20.0 * log10(f_high / f_low)), + 0.0, 1, + (float) rate, + (float) (1.0 / (2.0 * size)), + 0, + 0 + ); pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); - delete[] (impulse); + delete[] impulse; // audio filter impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); - delete[] (impulse); + delete[] impulse; } FMD::~FMD() @@ -179,8 +191,11 @@ void FMD::execute() if (run) { int i; - double det, del_out; - double vco[2], corr[2]; + double det; + double del_out; + std::array vco; + std::array corr; + for (i = 0; i < size; i++) { // pll @@ -200,7 +215,7 @@ void FMD::execute() while (phs < 0.0) phs += TWOPI; // dc removal, gain, & demod output fmdc = mtau * fmdc + onem_mtau * fil_out; - audio[2 * i + 0] = again * (fil_out - fmdc); + audio[2 * i + 0] = (float) (again * (fil_out - fmdc)); audio[2 * i + 1] = audio[2 * i + 0]; } // de-emphasis @@ -212,7 +227,7 @@ void FMD::execute() if (lim_run) { for (i = 0; i < 2 * size; i++) - out[i] *= lim_pre_gain; + out[i] *= (float) lim_pre_gain; plim->execute(); } } @@ -238,13 +253,24 @@ void FMD::setSamplerate(int _rate) rate = _rate; calc(); // de-emphasis filter - impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + impulse = FCurve::fc_impulse ( + nc_de, + (float) f_low, + (float) f_high, + (float) (+20.0 * log10(f_high / f_low)), + 0.0, + 1, + (float) rate, + (float) (1.0 / (2.0 * size)), + 0, + 0 + ); FIRCORE::setImpulse_fircore (pde, impulse, 1); - delete[] (impulse); + delete[] impulse; // audio filter impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); FIRCORE::setImpulse_fircore (paud, impulse, 1); - delete[] (impulse); + delete[] impulse; plim->setSamplerate((int) rate); } @@ -254,17 +280,28 @@ void FMD::setSize(int _size) decalc(); size = _size; calc(); - audio.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); + audio.resize(size * 2); // de-emphasis filter FIRCORE::destroy_fircore (pde); - impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + impulse = FCurve::fc_impulse ( + nc_de, + (float) f_low, + (float) f_high, + (float) (+20.0 * log10(f_high / f_low)), + 0.0, + 1, + (float) rate, + (float) (1.0 / (2.0 * size)), + 0, + 0 + ); pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); - delete[] (impulse); + delete[] impulse; // audio filter FIRCORE::destroy_fircore (paud); impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); - delete[] (impulse); + delete[] impulse; plim->setSize(size); } @@ -286,9 +323,9 @@ void FMD::setCTCSSFreq(double freq) sntch->setFreq(ctcss_freq); } -void FMD::setCTCSSRun(int run) +void FMD::setCTCSSRun(int _run) { - sntch_run = run; + sntch_run = _run; sntch->setRun(sntch_run); } @@ -299,9 +336,20 @@ void FMD::setNCde(int nc) if (nc_de != nc) { nc_de = nc; - impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + impulse = FCurve::fc_impulse ( + nc_de, + (float) f_low, + (float) f_high, + (float) (+20.0 * log10(f_high / f_low)), + 0.0, + 1, + (float) rate, + (float) (1.0 / (2.0 * size)), + 0, + 0 + ); FIRCORE::setNc_fircore (pde, nc_de, impulse); - delete[] (impulse); + delete[] impulse; } } @@ -323,7 +371,7 @@ void FMD::setNCaud(int nc) nc_aud = nc; impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); FIRCORE::setNc_fircore (paud, nc_aud, impulse); - delete[] (impulse); + delete[] impulse; } } @@ -336,10 +384,10 @@ void FMD::setMPaud(int mp) } } -void FMD::setLimRun(int run) +void FMD::setLimRun(int _run) { - if (lim_run != run) { - lim_run = run; + if (lim_run != _run) { + lim_run = _run; } } @@ -364,13 +412,24 @@ void FMD::setAFFilter(double low, double high) f_low = low; f_high = high; // de-emphasis filter - impulse = FCurve::fc_impulse (nc_de, f_low, f_high, +20.0 * log10(f_high / f_low), 0.0, 1, rate, 1.0 / (2.0 * size), 0, 0); + impulse = FCurve::fc_impulse ( + nc_de, + (float) f_low, + (float) f_high, + (float) (+20.0 * log10(f_high / f_low)), + 0.0, + 1, + (float) rate, + (float) (1.0 / (2.0 * size)), + 0, + 0 + ); FIRCORE::setImpulse_fircore (pde, impulse, 1); - delete[] (impulse); + delete[] impulse; // audio filter impulse = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); FIRCORE::setImpulse_fircore (paud, impulse, 1); - delete[] (impulse); + delete[] impulse; } } diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 74a9891e9..41980f20c 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -34,22 +34,23 @@ namespace WDSP { void FMSQ::calc() { - double delta, theta; + double delta; + double theta; float* impulse; int i; // noise filter - noise.resize(2 * size * 2); // (float *)malloc0(2 * size * sizeof(complex)); + noise.resize(2 * size * 2); F[0] = 0.0; - F[1] = fc; - F[2] = *pllpole; + F[1] = (float) fc; + F[2] = (float) *pllpole; F[3] = 20000.0; G[0] = 0.0; G[1] = 0.0; G[2] = 3.0; - G[3] = +20.0 * log10(20000.0 / *pllpole); + G[3] = (float) (+20.0 * log10(20000.0 / *pllpole)); impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); p = FIRCORE::create_fircore (size, trigger, noise.data(), nc, mp, impulse); - delete[] (impulse); + delete[] impulse; // noise averaging avm = exp(-1.0 / (rate * avtau)); onem_avm = 1.0 - avm; @@ -60,8 +61,8 @@ void FMSQ::calc() // level change ntup = (int)(tup * rate); ntdown = (int)(tdown * rate); - cup.resize(ntup + 1); // (float *)malloc0 ((ntup + 1) * sizeof(float)); - cdown.resize(ntdown + 1); //(float *)malloc0 ((ntdown + 1) * sizeof(float)); + cup.resize(ntup + 1); + cdown.resize(ntdown + 1); delta = PI / (double) ntup; theta = 0.0; @@ -80,7 +81,7 @@ void FMSQ::calc() theta += delta; } // control - state = 0; + state = FMSQState::MUTED; ready = 0; ramp = 0.0; rstep = 1.0 / rate; @@ -145,29 +146,20 @@ void FMSQ::flush() FIRCORE::flush_fircore (p); avnoise = 100.0; longnoise = 1.0; - state = 0; + state = FMSQState::MUTED; ready = 0; ramp = 0.0; } -enum _fmsqstate -{ - MUTED, - INCREASE, - UNMUTED, - TAIL, - DECREASE -}; - void FMSQ::execute() { if (run) { - int i; - double _noise, lnlimit; + double _noise; + double lnlimit; FIRCORE::xfircore (p); - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { double noise0 = noise[2 * i + 0]; double noise1 = noise[2 * i + 1]; @@ -183,10 +175,10 @@ void FMSQ::execute() switch (state) { - case MUTED: + case FMSQState::MUTED: if (avnoise < unmute_thresh && ready) { - state = INCREASE; + state = FMSQState::INCREASE; count = ntup; } @@ -195,19 +187,19 @@ void FMSQ::execute() break; - case INCREASE: - outsig[2 * i + 0] = insig[2 * i + 0] * cup[ntup - count]; - outsig[2 * i + 1] = insig[2 * i + 1] * cup[ntup - count]; + case FMSQState::INCREASE: + outsig[2 * i + 0] = (float) (insig[2 * i + 0] * cup[ntup - count]); + outsig[2 * i + 1] = (float) (insig[2 * i + 1] * cup[ntup - count]); if (count-- == 0) - state = UNMUTED; + state = FMSQState::UNMUTED; break; - case UNMUTED: + case FMSQState::UNMUTED: if (avnoise > tail_thresh) { - state = TAIL; + state = FMSQState::TAIL; if ((lnlimit = longnoise) > 1.0) lnlimit = 1.0; @@ -220,28 +212,28 @@ void FMSQ::execute() break; - case TAIL: + case FMSQState::TAIL: outsig[2 * i + 0] = insig[2 * i + 0]; outsig[2 * i + 1] = insig[2 * i + 1]; if (avnoise < unmute_thresh) { - state = UNMUTED; + state = FMSQState::UNMUTED; } else if (count-- == 0) { - state = DECREASE; + state = FMSQState::DECREASE; count = ntdown; } break; - case DECREASE: - outsig[2 * i + 0] = insig[2 * i + 0] * cdown[ntdown - count]; - outsig[2 * i + 1] = insig[2 * i + 1] * cdown[ntdown - count]; + case FMSQState::DECREASE: + outsig[2 * i + 0] = (float) (insig[2 * i + 0] * cdown[ntdown - count]); + outsig[2 * i + 1] = (float) (insig[2 * i + 1] * cdown[ntdown - count]); if (count-- == 0) - state = MUTED; + state = FMSQState::MUTED; break; } @@ -301,7 +293,7 @@ void FMSQ::setNC(int _nc) nc = _nc; impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); FIRCORE::setNc_fircore (p, nc, impulse); - delete[] (impulse); + delete[] impulse; } } diff --git a/wdsp/fmsq.hpp b/wdsp/fmsq.hpp index 1d5b8f614..288a5c0a7 100644 --- a/wdsp/fmsq.hpp +++ b/wdsp/fmsq.hpp @@ -40,6 +40,15 @@ class FIRCORE; class WDSP_API FMSQ { public: + enum class FMSQState + { + MUTED, + INCREASE, + UNMUTED, + TAIL, + DECREASE + }; + int run; // 0 if squelch system is OFF; 1 if it's ON int size; // size of input/output buffers float* insig; // squelch input signal buffer @@ -59,7 +68,7 @@ public: double longavm; double onem_longavm; double longnoise; - int state; // state machine control + FMSQState state; // state machine control int count; double tup; double tdown; diff --git a/wdsp/gen.cpp b/wdsp/gen.cpp index 76f56927d..d7029889a 100644 --- a/wdsp/gen.cpp +++ b/wdsp/gen.cpp @@ -78,8 +78,8 @@ void GEN::calc_triangle() void GEN::calc_pulse () { - int i; - float delta, theta; + double delta; + double theta; pulse.pperiod = 1.0 / pulse.pf; pulse.tphs = 0.0; pulse.tdelta = TWOPI * pulse.tf / rate; @@ -93,11 +93,11 @@ void GEN::calc_pulse () pulse.pnoff = 0; pulse.pcount = pulse.pnoff; - pulse.state = 0; - pulse.ctrans = new float[pulse.pntrans + 1]; // (float *) malloc0 ((pulse.pntrans + 1) * sizeof (float)); + pulse.state = PState::OFF; + pulse.ctrans = new double[pulse.pntrans + 1]; delta = PI / (float)pulse.pntrans; theta = 0.0; - for (i = 0; i <= pulse.pntrans; i++) + for (int i = 0; i <= pulse.pntrans; i++) { pulse.ctrans[i] = 0.5 * (1.0 - cos (theta)); theta += delta; @@ -143,7 +143,7 @@ GEN::GEN( tt.f1 = + 900.0; tt.f2 = + 1700.0; // noise - srand ((unsigned int) time (0)); + srand ((unsigned int) time (nullptr)); noise.mag = 1.0; // sweep sweep.mag = 1.0; @@ -172,17 +172,9 @@ GEN::~GEN() void GEN::flush() { - pulse.state = 0; + pulse.state = PState::OFF; } -enum pstate -{ - OFF, - UP, - ON, - DOWN -}; - void GEN::execute() { if (run) @@ -191,14 +183,14 @@ void GEN::execute() { case 0: // tone { - int i; - float t1, t2; - float cosphase = cos (tone.phs); - float sinphase = sin (tone.phs); - for (i = 0; i < size; i++) + double t1; + double t2; + double cosphase = cos (tone.phs); + double sinphase = sin (tone.phs); + for (int i = 0; i < size; i++) { - out[2 * i + 0] = + tone.mag * cosphase; - out[2 * i + 1] = - tone.mag * sinphase; + out[2 * i + 0] = (float) (+ tone.mag * cosphase); + out[2 * i + 1] = (float) (- tone.mag * sinphase); t1 = cosphase; t2 = sinphase; cosphase = t1 * tone.cosdelta - t2 * tone.sindelta; @@ -211,16 +203,16 @@ void GEN::execute() } case 1: // two-tone { - int i; - float tcos, tsin; - float cosphs1 = cos (tt.phs1); - float sinphs1 = sin (tt.phs1); - float cosphs2 = cos (tt.phs2); - float sinphs2 = sin (tt.phs2); - for (i = 0; i < size; i++) + double tcos; + double tsin; + double cosphs1 = cos (tt.phs1); + double sinphs1 = sin (tt.phs1); + double cosphs2 = cos (tt.phs2); + double sinphs2 = sin (tt.phs2); + for (int i = 0; i < size; i++) { - out[2 * i + 0] = + tt.mag1 * cosphs1 + tt.mag2 * cosphs2; - out[2 * i + 1] = - tt.mag1 * sinphs1 - tt.mag2 * sinphs2; + out[2 * i + 0] = (float) (+ tt.mag1 * cosphs1 + tt.mag2 * cosphs2); + out[2 * i + 1] = (float) (- tt.mag1 * sinphs1 - tt.mag2 * sinphs2); tcos = cosphs1; tsin = sinphs1; cosphs1 = tcos * tt.cosdelta1 - tsin * tt.sindelta1; @@ -240,29 +232,30 @@ void GEN::execute() } case 2: // noise { - int i; - float r1, r2, c, rad; - for (i = 0; i < size; i++) + double r1; + double r2; + double c; + double rad; + for (int i = 0; i < size; i++) { do { - r1 = 2.0 * (float)rand() / (float)RAND_MAX - 1.0; - r2 = 2.0 * (float)rand() / (float)RAND_MAX - 1.0; + r1 = 2.0 * (double)rand() / (double)RAND_MAX - 1.0; + r2 = 2.0 * (double)rand() / (double)RAND_MAX - 1.0; c = r1 * r1 + r2 * r2; } while (c >= 1.0); rad = sqrt (-2.0 * log (c) / c); - out[2 * i + 0] = noise.mag * rad * r1; - out[2 * i + 1] = noise.mag * rad * r2; + out[2 * i + 0] = (float) (noise.mag * rad * r1); + out[2 * i + 1] = (float) (noise.mag * rad * r2); } break; } case 3: // sweep { - int i; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { - out[2 * i + 0] = + sweep.mag * cos(sweep.phs); - out[2 * i + 1] = - sweep.mag * sin(sweep.phs); + out[2 * i + 0] = (float) (+ sweep.mag * cos(sweep.phs)); + out[2 * i + 1] = (float) (- sweep.mag * sin(sweep.phs)); sweep.phs += sweep.dphs; sweep.dphs += sweep.d2phs; if (sweep.phs >= TWOPI) sweep.phs -= TWOPI; @@ -274,11 +267,10 @@ void GEN::execute() } case 4: // sawtooth (audio only) { - int i; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { if (saw.t > saw.period) saw.t -= saw.period; - out[2 * i + 0] = saw.mag * (saw.t * saw.f - 1.0); + out[2 * i + 0] = (float) (saw.mag * (saw.t * saw.f - 1.0)); out[2 * i + 1] = 0.0; saw.t += saw.delta; } @@ -286,13 +278,12 @@ void GEN::execute() break; case 5: // triangle (audio only) { - int i; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { if (tri.t > tri.period) tri.t1 = tri.t -= tri.period; if (tri.t > tri.half) tri.t1 -= tri.delta; else tri.t1 += tri.delta; - out[2 * i + 0] = tri.mag * (4.0 * tri.t1 * tri.f - 1.0); + out[2 * i + 0] = (float) (tri.mag * (4.0 * tri.t1 * tri.f - 1.0)); out[2 * i + 1] = 0.0; tri.t += tri.delta; } @@ -300,44 +291,44 @@ void GEN::execute() break; case 6: // pulse (audio only) { - int i; - float t1, t2; - float cosphase = cos (pulse.tphs); - float sinphase = sin (pulse.tphs); - for (i = 0; i < size; i++) + double t1; + double t2; + double cosphase = cos (pulse.tphs); + double sinphase = sin (pulse.tphs); + for (int i = 0; i < size; i++) { if (pulse.pnoff != 0) switch (pulse.state) { - case OFF: + case PState::OFF: out[2 * i + 0] = 0.0; if (--pulse.pcount == 0) { - pulse.state = UP; + pulse.state = PState::UP; pulse.pcount = pulse.pntrans; } break; - case UP: - out[2 * i + 0] = pulse.mag * cosphase * pulse.ctrans[pulse.pntrans - pulse.pcount]; + case PState::UP: + out[2 * i + 0] = (float) (pulse.mag * cosphase * pulse.ctrans[pulse.pntrans - pulse.pcount]); if (--pulse.pcount == 0) { - pulse.state = ON; + pulse.state = PState::ON; pulse.pcount = pulse.pnon; } break; - case ON: - out[2 * i + 0] = pulse.mag * cosphase; + case PState::ON: + out[2 * i + 0] = (float) (pulse.mag * cosphase); if (--pulse.pcount == 0) { - pulse.state = DOWN; + pulse.state = PState::DOWN; pulse.pcount = pulse.pntrans; } break; - case DOWN: - out[2 * i + 0] = pulse.mag * cosphase * pulse.ctrans[pulse.pcount]; + case PState::DOWN: + out[2 * i + 0] = (float) (pulse.mag * cosphase * pulse.ctrans[pulse.pcount]); if (--pulse.pcount == 0) { - pulse.state = OFF; + pulse.state = PState::OFF; pulse.pcount = pulse.pnoff; } break; @@ -432,9 +423,9 @@ void GEN::SetPreSweepFreq(float freq1, float freq2) calc_sweep(); } -void GEN::SetPreSweepRate(float rate) +void GEN::SetPreSweepRate(float _rate) { - sweep.sweeprate = rate; + sweep.sweeprate = _rate; calc_sweep(); } diff --git a/wdsp/gen.hpp b/wdsp/gen.hpp index d4703374e..8c2ee9225 100644 --- a/wdsp/gen.hpp +++ b/wdsp/gen.hpp @@ -37,88 +37,103 @@ class TXA; class WDSP_API GEN { public: + enum class PState + { + OFF, + UP, + ON, + DOWN + }; + int run; // run int size; // number of samples per buffer float* in; // input buffer (retained in case I want to mix in a generated signal) float* out; // output buffer - float rate; // sample rate + double rate; // sample rate int mode; struct _tone { - float mag; - float freq; - float phs; - float delta; - float cosdelta; - float sindelta; - } tone; + double mag; + double freq; + double phs; + double delta; + double cosdelta; + double sindelta; + }; + _tone tone; struct _tt { - float mag1; - float mag2; - float f1; - float f2; - float phs1; - float phs2; - float delta1; - float delta2; - float cosdelta1; - float cosdelta2; - float sindelta1; - float sindelta2; - } tt; + double mag1; + double mag2; + double f1; + double f2; + double phs1; + double phs2; + double delta1; + double delta2; + double cosdelta1; + double cosdelta2; + double sindelta1; + double sindelta2; + }; + _tt tt; struct _noise { - float mag; - } noise; + double mag; + }; + _noise noise; struct _sweep { - float mag; - float f1; - float f2; - float sweeprate; - float phs; - float dphs; - float d2phs; - float dphsmax; - } sweep; + double mag; + double f1; + double f2; + double sweeprate; + double phs; + double dphs; + double d2phs; + double dphsmax; + }; + _sweep sweep; struct _saw { - float mag; - float f; - float period; - float delta; - float t; - } saw; + double mag; + double f; + double period; + double delta; + double t; + }; + _saw saw; struct _tri { - float mag; - float f; - float period; - float half; - float delta; - float t; - float t1; - } tri; + double mag; + double f; + double period; + double half; + double delta; + double t; + double t1; + }; + _tri tri; struct _pulse { - float mag; - float pf; - float pdutycycle; - float ptranstime; - float* ctrans; + double mag; + double pf; + double pdutycycle; + double ptranstime; + double* ctrans; int pcount; int pnon; int pntrans; int pnoff; - float pperiod; - float tf; - float tphs; - float tdelta; - float tcosdelta; - float tsindelta; - int state; - } pulse; + double pperiod; + double tf; + double tphs; + double tdelta; + double tcosdelta; + double tsindelta; + PState state; + }; + _pulse pulse; GEN( int run, diff --git a/wdsp/meter.cpp b/wdsp/meter.cpp index 5ad168920..09797f3dc 100644 --- a/wdsp/meter.cpp +++ b/wdsp/meter.cpp @@ -51,20 +51,20 @@ METER::METER( int _enum_pk, int _enum_gain, double* _pgain -) +) : + run(_run), + prun(_prun), + size(_size), + buff(_buff), + rate((double) _rate), + tau_average(_tau_av), + tau_peak_decay(_tau_decay), + result(_result), + enum_av(_enum_av), + enum_pk(_enum_pk), + enum_gain(_enum_gain), + pgain(_pgain) { - run = _run; - prun = _prun; - size = _size; - buff = _buff; - rate = (double) _rate; - tau_average = _tau_av; - tau_peak_decay = _tau_decay; - result = _result; - enum_av = _enum_av; - enum_pk = _enum_pk; - enum_gain = _enum_gain; - pgain = _pgain; calc(); } @@ -75,7 +75,7 @@ void METER::flush() result[enum_av] = -100.0; result[enum_pk] = -100.0; - if ((pgain != 0) && (enum_gain >= 0)) + if ((pgain != nullptr) && (enum_gain >= 0)) result[enum_gain] = 0.0; } @@ -83,18 +83,17 @@ void METER::execute() { int srun; - if (prun != 0) - srun = *(prun); + if (prun != nullptr) + srun = *prun; else srun = 1; if (run && srun) { - int i; double smag; double np = 0.0; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { double xr = buff[2 * i + 0]; double xi = buff[2 * i + 1]; @@ -112,7 +111,7 @@ void METER::execute() result[enum_av] = 10.0 * MemLog::mlog10 (avg <= 0 ? 1.0e-20 : avg); result[enum_pk] = 10.0 * MemLog::mlog10 (peak <= 0 ? 1.0e-20 : peak); - if ((pgain != 0) && (enum_gain >= 0)) + if ((pgain != nullptr) && (enum_gain >= 0)) result[enum_gain] = 20.0 * MemLog::mlog10 (*pgain <= 0 ? 1.0e-20 : *pgain); } else @@ -150,7 +149,7 @@ void METER::setSize(int _size) * * ********************************************************************************************************/ -double METER::getMeter(int mt) +double METER::getMeter(int mt) const { return result[mt]; } diff --git a/wdsp/meter.hpp b/wdsp/meter.hpp index e84f07651..2bea6d4c4 100644 --- a/wdsp/meter.hpp +++ b/wdsp/meter.hpp @@ -75,7 +75,7 @@ public: void setBuffers(float* in); void setSamplerate(int rate); void setSize(int size); - double getMeter(int mt); + double getMeter(int mt) const; private: void calc(); diff --git a/wdsp/mpeak.cpp b/wdsp/mpeak.cpp index ea4318563..7e551a848 100644 --- a/wdsp/mpeak.cpp +++ b/wdsp/mpeak.cpp @@ -39,8 +39,8 @@ namespace WDSP { void MPEAK::calc() { - tmp.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); - mix.resize(size * 2); // (float *) malloc0 (size * sizeof (complex)); + tmp.resize(size * 2); + mix.resize(size * 2); for (int i = 0; i < npeaks; i++) { pfil[i] = new SPEAK( @@ -85,15 +85,15 @@ MPEAK::MPEAK( rate = _rate; npeaks = _npeaks; nstages = _nstages; - enable.resize(npeaks); // (int *) malloc0 (npeaks * sizeof (int)); - f.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); - bw.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); - gain.resize(npeaks); // (float *) malloc0 (npeaks * sizeof (float)); + enable.resize(npeaks); + f.resize(npeaks); + bw.resize(npeaks); + gain.resize(npeaks); std::copy(_enable, _enable + npeaks, enable.begin()); std::copy(_f, _f + npeaks, f.begin()); std::copy(_bw, _bw + npeaks, bw.begin()); std::copy(_gain, _gain + npeaks, gain.begin()); - pfil.resize(npeaks); // (SPEAK *) malloc0 (npeaks * sizeof (SPEAK)); + pfil.resize(npeaks); calc(); } diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index 37bc29bc5..b9784ecb2 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "fir.hpp" #include "fircore.hpp" @@ -44,16 +46,17 @@ NOTCHDB::NOTCHDB(int _master_run, int _maxnotches) master_run = _master_run; maxnotches = _maxnotches; nn = 0; - fcenter.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); - fwidth.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); - nlow.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); - nhigh.resize(maxnotches); // (float *) malloc0 (maxnotches * sizeof (float)); - active.resize(maxnotches); // (int *) malloc0 (maxnotches * sizeof (int )); + fcenter.resize(maxnotches); + fwidth.resize(maxnotches); + nlow.resize(maxnotches); + nhigh.resize(maxnotches); + active.resize(maxnotches); } int NOTCHDB::addNotch(int notch, double _fcenter, double _fwidth, int _active) { - int i, j; + int i; + int j; int rval; if (notch <= nn && nn < maxnotches) @@ -104,7 +107,8 @@ int NOTCHDB::getNotch(int _notch, double* _fcenter, double* _fwidth, int* _activ int NOTCHDB::deleteNotch(int _notch) { - int i, j; + int i; + int j; int rval; if (_notch < nn) @@ -143,7 +147,7 @@ int NOTCHDB::editNotch(int _notch, double _fcenter, double _fwidth, int _active) return rval; } -void NOTCHDB::getNumNotches(int* _nnotches) +void NOTCHDB::getNumNotches(int* _nnotches) const { *_nnotches = nn; } @@ -154,25 +158,24 @@ void NOTCHDB::getNumNotches(int* _nnotches) * * ********************************************************************************************************/ -float* NBP::fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double rate, double scale, int wintype) +float* NBP::fir_mbandpass (int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype) { - int i, k; - float* impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex)); - float* imp; - for (k = 0; k < nbp; k++) + auto* impulse = new float[N * 2]; + std::fill(impulse, impulse + N*2, 0); + for (int k = 0; k < nbp; k++) { - imp = FIR::fir_bandpass (N, flow[k], fhigh[k], rate, wintype, 1, scale); - for (i = 0; i < N; i++) + float* imp = FIR::fir_bandpass (N, flow[k], fhigh[k], rate, wintype, 1, scale); + for (int i = 0; i < N; i++) { impulse[2 * i + 0] += imp[2 * i + 0]; impulse[2 * i + 1] += imp[2 * i + 1]; } - delete[] (imp); + delete[] imp; } return impulse; } -double NBP::min_notch_width() +double NBP::min_notch_width() const { double min_width; switch (wintype) @@ -183,6 +186,8 @@ double NBP::min_notch_width() case 1: min_width = 2200.0 / (nc / 256) * (rate / 48000); break; + default: + min_width = 0; } return min_width; } @@ -204,10 +209,12 @@ int NBP::make_nbp ( ) { int nbp; - int nnbp, adds; - int i, j, k; - double nl, nh; - int* del = new int[1024]; // (int *) malloc0 (1024 * sizeof (int)); + int nnbp; + int adds; + int i; + double nl; + double nh; + std::array del; if (fhigh > flow) { bplow[0] = flow; @@ -217,11 +224,10 @@ int NBP::make_nbp ( else { nbp = 0; - delete[] del; return nbp; } *havnotch = 0; - for (k = 0; k < nn; k++) + for (int k = 0; k < nn; k++) { if (autoincr && width[k] < minwidth) { @@ -270,7 +276,7 @@ int NBP::make_nbp ( if (del[i] == 1) { nnbp--; - for (j = i; j < nnbp; j++) + for (int j = i; j < nnbp; j++) { bplow[j] = bplow[j + 1]; bphigh[j] = bphigh[j + 1]; @@ -281,14 +287,13 @@ int NBP::make_nbp ( nbp = nnbp; } } - delete[] (del); return nbp; } void NBP::calc_lightweight() { // calculate and set new impulse response; used when changing tune freq or shift freq - int i; - double fl, fh; + double fl; + double fh; double offset; NOTCHDB *b = notchdb; if (fnfrun) @@ -314,7 +319,7 @@ void NBP::calc_lightweight() // when tuning, no need to recalc filter if there were not and are not any notches in passband if (hadnotch || havnotch) { - for (i = 0; i < numpb; i++) + for (int i = 0; i < numpb; i++) { bplow[i] -= offset; bphigh[i] -= offset; @@ -330,7 +335,7 @@ void NBP::calc_lightweight() ); FIRCORE::setImpulse_fircore (fircore, impulse, 1); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); - delete[](impulse); + delete[] impulse; } hadnotch = havnotch; } @@ -340,8 +345,8 @@ void NBP::calc_lightweight() void NBP::calc_impulse () { // calculates impulse response; for create_fircore() and parameter changes - int i; - float fl, fh; + double fl; + double fh; double offset; NOTCHDB *b = notchdb; @@ -365,7 +370,7 @@ void NBP::calc_impulse () bphigh, &havnotch ); - for (i = 0; i < numpb; i++) + for (int i = 0; i < numpb; i++) { bplow[i] -= offset; bphigh[i] -= offset; @@ -429,12 +434,12 @@ NBP::NBP( maxpb(_maxpb), notchdb(_notchdb) { - bplow.resize(maxpb); // (float *) malloc0 (maxpb * sizeof (float)); - bphigh.resize(maxpb); // (float *) malloc0 (maxpb * sizeof (float)); + bplow.resize(maxpb); + bphigh.resize(maxpb); calc_impulse (); fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); - delete[](impulse); + delete[]impulse; } NBP::~NBP() @@ -467,7 +472,7 @@ void NBP::setSamplerate(int _rate) rate = _rate; calc_impulse (); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void NBP::setSize(int _size) @@ -477,14 +482,14 @@ void NBP::setSize(int _size) FIRCORE::setSize_fircore (fircore, size); calc_impulse (); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } void NBP::setNc() { calc_impulse(); FIRCORE::setNc_fircore (fircore, nc, impulse); - delete[] (impulse); + delete[] impulse; } void NBP::setMp() @@ -513,7 +518,7 @@ void NBP::SetFreqs(double _flow, double _fhigh) fhigh = _fhigh; calc_impulse(); FIRCORE::setImpulse_fircore (fircore, impulse, 1); - delete[] (impulse); + delete[] impulse; } } @@ -536,7 +541,7 @@ void NBP::SetMP(int _mp) } } -void NBP::GetMinNotchWidth(double* minwidth) +void NBP::GetMinNotchWidth(double* minwidth) const { *minwidth = min_notch_width(); } diff --git a/wdsp/nbp.hpp b/wdsp/nbp.hpp index 4bb04429f..c56f71225 100644 --- a/wdsp/nbp.hpp +++ b/wdsp/nbp.hpp @@ -59,7 +59,7 @@ public: int getNotch (int notch, double* fcenter, double* fwidth, int* active); int deleteNotch (int notch); int editNotch (int notch, double fcenter, double fwidth, int active); - void getNumNotches (int* nnotches); + void getNumNotches (int* nnotches) const; }; @@ -125,12 +125,12 @@ public: void SetFreqs(double flow, double fhigh); void SetNC(int nc); void SetMP(int mp); - void GetMinNotchWidth(double* minwidth); + void GetMinNotchWidth(double* minwidth) const; void calc_lightweight(); private: - static float* fir_mbandpass (int N, int nbp, double* flow, double* fhigh, double rate, double scale, int wintype); - double min_notch_width (); + static float* fir_mbandpass (int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype); + double min_notch_width () const; static int make_nbp ( int nn, std::vector& active, diff --git a/wdsp/nob.cpp b/wdsp/nob.cpp index 5b7834315..ac3eb9cfb 100644 --- a/wdsp/nob.cpp +++ b/wdsp/nob.cpp @@ -150,8 +150,10 @@ void NOB::execute() double mag; int bf_idx; int ff_idx; - int lidx, tidx; - int i, j, k; + int lidx; + int tidx; + int j; + int k; int bfboutidx; int ffboutidx; int hcount; @@ -161,7 +163,7 @@ void NOB::execute() if (run) { - for (i = 0; i < buffsize; i++) + for (int i = 0; i < buffsize; i++) { dline[2 * in_idx + 0] = in[2 * i + 0]; dline[2 * in_idx + 1] = in[2 * i + 1]; @@ -189,8 +191,8 @@ void NOB::execute() { case 0: // normal output & impulse setup { - out[2 * i + 0] = dline[2 * out_idx + 0]; - out[2 * i + 1] = dline[2 * out_idx + 1]; + out[2 * i + 0] = (float) (dline[2 * out_idx + 0]); + out[2 * i + 1] = (float) (dline[2 * out_idx + 1]); Ilast = dline[2 * out_idx + 0]; Qlast = dline[2 * out_idx + 1]; @@ -210,7 +212,6 @@ void NOB::execute() do { - len = 0; hcount = 0; while ((imp[tidx] > 0 || hcount > 0) && blank_count < max_imp_seq) @@ -314,7 +315,7 @@ void NOB::execute() switch (mode) { - case 0: // zero + default: // zero deltaI = 0.0; deltaQ = 0.0; I = 0.0; @@ -367,8 +368,8 @@ void NOB::execute() case 1: // slew output in advance of blanking period { scale = 0.5 + awave[time]; - out[2 * i + 0] = Ilast * scale + (1.0 - scale) * I; - out[2 * i + 1] = Qlast * scale + (1.0 - scale) * Q; + out[2 * i + 0] = (float) (Ilast * scale + (1.0 - scale) * I); + out[2 * i + 1] = (float) (Qlast * scale + (1.0 - scale) * Q); if (++time == adv_slew_count) { @@ -385,8 +386,8 @@ void NOB::execute() case 2: // initial advance period { - out[2 * i + 0] = I; - out[2 * i + 1] = Q; + out[2 * i + 0] = (float) I; + out[2 * i + 1] = (float) Q; I += deltaI; Q += deltaQ; @@ -401,8 +402,8 @@ void NOB::execute() case 3: // impulse & hang period { - out[2 * i + 0] = I; - out[2 * i + 1] = Q; + out[2 * i + 0] = (float) I; + out[2 * i + 1] = (float) Q; I += deltaI; Q += deltaQ; @@ -425,8 +426,8 @@ void NOB::execute() case 4: // slew output after blanking period { scale = 0.5 - hwave[time]; - out[2 * i + 0] = Inext * scale + (1.0 - scale) * I; - out[2 * i + 1] = Qnext * scale + (1.0 - scale) * Q; + out[2 * i + 0] = (float) (Inext * scale + (1.0 - scale) * I); + out[2 * i + 1] = (float) (Qnext * scale + (1.0 - scale) * Q); if (++time == hang_slew_count) state = 0; @@ -437,8 +438,8 @@ void NOB::execute() case 5: { scale = 0.5 + awave[time]; - out[2 * i + 0] = Ilast * scale; - out[2 * i + 1] = Qlast * scale; + out[2 * i + 0] = (float) (Ilast * scale); + out[2 * i + 1] = (float) (Qlast * scale); if (++time == adv_slew_count) { @@ -542,8 +543,8 @@ void NOB::execute() case 9: { scale = 0.5 - hwave[time]; - out[2 * i + 0] = Inext * scale; - out[2 * i + 1] = Qnext * scale; + out[2 * i + 0] = (float) (Inext * scale); + out[2 * i + 1] = (float) (Qnext * scale); if (++time >= hang_slew_count) { diff --git a/wdsp/patchpanel.cpp b/wdsp/patchpanel.cpp index 9499846a5..a8b9b8a01 100644 --- a/wdsp/patchpanel.cpp +++ b/wdsp/patchpanel.cpp @@ -55,24 +55,26 @@ PANEL::PANEL( void PANEL::flush() { + // There is no data to be reset internally } void PANEL::execute() { int i; - double I, Q; + double I; + double Q; double gainI = gain1 * gain2I; double gainQ = gain1 * gain2Q; // inselect is either 0(neither), 1(Q), 2(I), or 3(both) switch (copy) { - case 0: // no copy + default: // 0 (default) no copy for (i = 0; i < size; i++) { I = in[2 * i + 0] * (inselect >> 1); Q = in[2 * i + 1] * (inselect & 1); - out[2 * i + 0] = gainI * I; - out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = (float) (gainI * I); + out[2 * i + 1] = (float) (gainQ * Q); } break; case 1: // copy I to Q (then Q == I) @@ -80,17 +82,17 @@ void PANEL::execute() { I = in[2 * i + 0] * (inselect >> 1); Q = I; - out[2 * i + 0] = gainI * I; - out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = (float) (gainI * I); + out[2 * i + 1] = (float) (gainQ * Q); } break; case 2: // copy Q to I (then I == Q) for (i = 0; i < size; i++) { - Q = in[2 * i + 1] * (inselect & 1); + Q = in[2 * i + 1] * (inselect & 1); I = Q; - out[2 * i + 0] = gainI * I; - out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = (float) (gainI * I); + out[2 * i + 1] = (float) (gainQ * Q); } break; case 3: // reverse (I=>Q and Q=>I) @@ -98,8 +100,8 @@ void PANEL::execute() { Q = in[2 * i + 0] * (inselect >> 1); I = in[2 * i + 1] * (inselect & 1); - out[2 * i + 0] = gainI * I; - out[2 * i + 1] = gainQ * Q; + out[2 * i + 0] = (float) (gainI * I); + out[2 * i + 1] = (float) (gainQ * Q); } break; } @@ -113,6 +115,7 @@ void PANEL::setBuffers(float* _in, float* _out) void PANEL::setSamplerate(int) { + // There is no sample rate to be set for this component } void PANEL::setSize(int _size) @@ -149,21 +152,22 @@ void PANEL::setGain2(double _gainI, double _gainQ) void PANEL::setPan(double _pan) { - double gain1, gain2; + double _gain1; + double _gain2; if (_pan <= 0.5) { - gain1 = 1.0; - gain2 = sin (_pan * PI); + _gain1 = 1.0; + _gain2 = sin (_pan * PI); } else { - gain1 = sin (_pan * PI); - gain2 = 1.0; + _gain1 = sin (_pan * PI); + _gain2 = 1.0; } - gain2I = gain1; - gain2Q = gain2; + gain2I = _gain1; + gain2Q = _gain2; } void PANEL::setCopy(int _copy) diff --git a/wdsp/phrot.cpp b/wdsp/phrot.cpp index d7821554e..f4812b322 100644 --- a/wdsp/phrot.cpp +++ b/wdsp/phrot.cpp @@ -39,10 +39,10 @@ namespace WDSP { void PHROT::calc() { double g; - x0.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); - x1.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); - y0.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); - y1.resize(nstages); // (float *) malloc0 (nstages * sizeof (float)); + x0.resize(nstages); + x1.resize(nstages); + y0.resize(nstages); + y1.resize(nstages); g = tan (PI * fc / (float)rate); b0 = (g - 1.0) / (g + 1.0); b1 = 1.0; @@ -58,7 +58,6 @@ PHROT::PHROT( double _fc, int _nstages ) : - reverse(0), run(_run), size(_size), in(_in), @@ -67,6 +66,7 @@ PHROT::PHROT( fc(_fc), nstages(_nstages) { + reverse = 0; calc(); } @@ -102,7 +102,7 @@ void PHROT::execute() x1[n] = x0[n]; } - out[2 * i + 0] = y0[nstages - 1]; + out[2 * i + 0] = (float) y0[nstages - 1]; } } else if (out != in) @@ -135,9 +135,9 @@ void PHROT::setSize(int _size) * * ********************************************************************************************************/ -void PHROT::setRun(int run) +void PHROT::setRun(int _run) { - run = run; + run = _run; if (run) flush(); diff --git a/wdsp/phrot.hpp b/wdsp/phrot.hpp index 79e4c9397..c71deb43e 100644 --- a/wdsp/phrot.hpp +++ b/wdsp/phrot.hpp @@ -54,8 +54,13 @@ public: double fc; int nstages; // normalized such that a0 = 1 - double a1, b0, b1; - std::vector x0, x1, y0, y1; + double a1; + double b0; + double b1; + std::vector x0; + std::vector x1; + std::vector y0; + std::vector y1; PHROT( int run, diff --git a/wdsp/resample.cpp b/wdsp/resample.cpp index e4f24309c..19d16e3b3 100644 --- a/wdsp/resample.cpp +++ b/wdsp/resample.cpp @@ -39,11 +39,14 @@ namespace WDSP { void RESAMPLE::calc() { - int x, y, z; - int i, j, k; + int x; + int y; + int z; + int i; int min_rate; double full_rate; - double fc_norm_high, fc_norm_low; + double fc_norm_high; + double fc_norm_low; float* impulse; fc = fcin; ncoef = ncoefin; @@ -84,22 +87,22 @@ void RESAMPLE::calc() ncoef = (ncoef / L + 1) * L; cpp = ncoef / L; - h.resize(ncoef); // (float *)malloc0(ncoef * sizeof(float)); + h.resize(ncoef); impulse = FIR::fir_bandpass(ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, gain * (double)L); i = 0; - for (j = 0; j < L; j++) + for (int j = 0; j < L; j++) { - for (k = 0; k < ncoef; k += L) + for (int k = 0; k < ncoef; k += L) h[i++] = impulse[j + k]; } ringsize = cpp; - ring.resize(ringsize); // (float *)malloc0(ringsize * sizeof(complex)); + ring.resize(ringsize); idx_in = ringsize - 1; phnum = 0; - delete[] (impulse); + delete[] impulse; } RESAMPLE::RESAMPLE ( @@ -141,11 +144,12 @@ int RESAMPLE::execute() if (run) { - int i, j, n; + int n; int idx_out; - double I, Q; + double I; + double Q; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { ring[2 * idx_in + 0] = in[2 * i + 0]; ring[2 * idx_in + 1] = in[2 * i + 1]; @@ -156,7 +160,7 @@ int RESAMPLE::execute() Q = 0.0; n = cpp * phnum; - for (j = 0; j < cpp; j++) + for (int j = 0; j < cpp; j++) { if ((idx_out = idx_in + j) >= ringsize) idx_out -= ringsize; @@ -165,8 +169,8 @@ int RESAMPLE::execute() Q += h[n + j] * ring[2 * idx_out + 1]; } - out[2 * outsamps + 0] = I; - out[2 * outsamps + 1] = Q; + out[2 * outsamps + 0] = (float) I; + out[2 * outsamps + 1] = (float) Q; outsamps++; phnum += M; } @@ -231,25 +235,24 @@ void RESAMPLE::setBandwidth(double _fc_low, double _fc_high) // exported calls -void* RESAMPLE::createV (int in_rate, int out_rate) +RESAMPLE* RESAMPLE::Create(int in_rate, int out_rate) { - return (void *) new RESAMPLE(1, 0, 0, 0, in_rate, out_rate, 0.0, 0, 1.0); + return new RESAMPLE(1, 0, nullptr, nullptr, in_rate, out_rate, 0.0, 0, 1.0); } -void RESAMPLE::executeV (float* input, float* output, int numsamps, int* outsamps, void* ptr) +void RESAMPLE::Execute(float* input, float* output, int numsamps, int* outsamps, RESAMPLE* ptr) { - RESAMPLE *a = (RESAMPLE*) ptr; - a->in = input; - a->out = output; - a->size = numsamps; - *outsamps = a->execute(); + ptr->in = input; + ptr->out = output; + ptr->size = numsamps; + *outsamps = ptr->execute(); } -void RESAMPLE::destroyV (void* ptr) +void RESAMPLE::Destroy(RESAMPLE* ptr) { - delete ( (RESAMPLE*) ptr ); + delete ptr; } } // namespace WDSP diff --git a/wdsp/resample.hpp b/wdsp/resample.hpp index 5b4a12357..a1450b55c 100644 --- a/wdsp/resample.hpp +++ b/wdsp/resample.hpp @@ -87,10 +87,10 @@ public: void setOutRate(int rate); void setFCLow(double fc_low); void setBandwidth(double fc_low, double fc_high); - // Exported calls - static void* createV (int in_rate, int out_rate); - static void executeV (float* input, float* output, int numsamps, int* outsamps, void* ptr); - static void destroyV (void* ptr); + // Static methods + static RESAMPLE* Create (int in_rate, int out_rate); + static void Execute (float* input, float* output, int numsamps, int* outsamps, RESAMPLE* ptr); + static void Destroy (RESAMPLE* ptr); private: void calc(); diff --git a/wdsp/sender.cpp b/wdsp/sender.cpp index cc55b31fd..c7e1c7bd7 100644 --- a/wdsp/sender.cpp +++ b/wdsp/sender.cpp @@ -36,29 +36,20 @@ SENDER::SENDER(int _run, int _flag, int _mode, int _size, float* _in) : flag(_flag), mode(_mode), size(_size), - in(_in) + in(_in), + spectrumProbe(nullptr) { - spectrumProbe = nullptr; } void SENDER::flush() { + // There is no internal data to be reset } void SENDER::execute() { - if (run && flag) - { - switch (mode) - { - case 0: - { - if (spectrumProbe) { - spectrumProbe->proceed(in, size); - } - break; - } - } + if (run && flag && (mode == 0) && spectrumProbe) { + spectrumProbe->proceed(in, size); } } diff --git a/wdsp/shift.cpp b/wdsp/shift.cpp index 80cdb0d8f..72fe18f3e 100644 --- a/wdsp/shift.cpp +++ b/wdsp/shift.cpp @@ -50,9 +50,9 @@ SHIFT::SHIFT ( in(_in), out(_out), rate((double) _rate), - shift(_fshift) + shift(_fshift), + phase(0.0) { - phase = 0.0; calc(); } @@ -65,17 +65,19 @@ void SHIFT::execute() { if (run) { - int i; - double I1, Q1, t1, t2; + double I1; + double Q1; + double t1; + double t2; double cos_phase = cos (phase); double sin_phase = sin (phase); - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { I1 = in[2 * i + 0]; Q1 = in[2 * i + 1]; - out[2 * i + 0] = I1 * cos_phase - Q1 * sin_phase; - out[2 * i + 1] = I1 * sin_phase + Q1 * cos_phase; + out[2 * i + 0] = (float) (I1 * cos_phase - Q1 * sin_phase); + out[2 * i + 1] = (float) (I1 * sin_phase + Q1 * cos_phase); t1 = cos_phase; t2 = sin_phase; cos_phase = t1 * cos_delta - t2 * sin_delta; diff --git a/wdsp/siphon.cpp b/wdsp/siphon.cpp index c7ca1ec5f..b04f56af3 100644 --- a/wdsp/siphon.cpp +++ b/wdsp/siphon.cpp @@ -34,23 +34,25 @@ namespace WDSP { void SIPHON::build_window() { int i; - double arg0, cosphi; - double sum, scale; + double arg0; + double cosphi; + double sum; + float scale; arg0 = 2.0 * PI / ((double) fftsize - 1.0); sum = 0.0; for (i = 0; i < fftsize; i++) { cosphi = cos (arg0 * (float)i); - window[i] = + 6.3964424114390378e-02 + window[i] = (float) (+ 6.3964424114390378e-02 + cosphi * ( - 2.3993864599352804e-01 + cosphi * ( + 3.5015956323820469e-01 + cosphi * ( - 2.4774111897080783e-01 + cosphi * ( + 8.5438256055858031e-02 + cosphi * ( - 1.2320203369293225e-02 - + cosphi * ( + 4.3778825791773474e-04 )))))); + + cosphi * ( + 4.3778825791773474e-04 ))))))); sum += window[i]; } - scale = 1.0 / sum; + scale = 1.0f / (float) sum; for (i = 0; i < fftsize; i++) window[i] *= scale; } @@ -65,23 +67,23 @@ SIPHON::SIPHON( int _sipsize, int _fftsize, int _specmode -) +) : + run(_run), + position(_position), + mode(_mode), + disp(_disp), + insize(_insize), + in(_in), + sipsize(_sipsize), // NOTE: sipsize MUST BE A POWER OF TWO!! + fftsize(_fftsize), + specmode(_specmode) { - run = _run; - position = _position; - mode = _mode; - disp = _disp; - insize = _insize; - in = _in; - sipsize = _sipsize; // NOTE: sipsize MUST BE A POWER OF TWO!! - fftsize = _fftsize; - specmode = _specmode; - sipbuff.resize(sipsize * 2); // (float *) malloc0 (sipsize * sizeof (complex)); + sipbuff.resize(sipsize * 2); idx = 0; - sipout.resize(sipsize * 2); // (float *) malloc0 (sipsize * sizeof (complex)); - specout.resize(fftsize * 2); // (float *) malloc0 (fftsize * sizeof (complex)); + sipout.resize(sipsize * 2); + specout.resize(fftsize * 2); sipplan = fftwf_plan_dft_1d (fftsize, (fftwf_complex *) sipout.data(), (fftwf_complex *) specout.data(), FFTW_FORWARD, FFTW_PATIENT); - window.resize(fftsize * 2); // (float *) malloc0 (fftsize * sizeof (complex)); + window.resize(fftsize * 2); build_window(); } @@ -100,35 +102,28 @@ void SIPHON::flush() void SIPHON::execute(int pos) { - int first, second; + int first; + int second; - if (run && position == pos) + if (run && (position == pos) && (mode == 0)) { - switch (mode) + if (insize >= sipsize) + std::copy(&(in[2 * (insize - sipsize)]), &(in[2 * (insize - sipsize)]) + sipsize * 2, sipbuff.begin()); + else { - case 0: - if (insize >= sipsize) - std::copy(&(in[2 * (insize - sipsize)]), &(in[2 * (insize - sipsize)]) + sipsize * 2, sipbuff.begin()); + if (insize > (sipsize - idx)) + { + first = sipsize - idx; + second = insize - first; + } else { - if (insize > (sipsize - idx)) - { - first = sipsize - idx; - second = insize - first; - } - else - { - first = insize; - second = 0; - } - std::copy(in, in + first * 2, sipbuff.begin() + 2 * idx); - std::copy(in + 2 * first, in + 2 * first + second * 2, sipbuff.begin()); - if ((idx += insize) >= sipsize) idx -= sipsize; + first = insize; + second = 0; } - break; - case 1: - // Spectrum0 (1, disp, 0, 0, in); - break; + std::copy(in, in + first * 2, sipbuff.begin() + 2 * idx); + std::copy(in + 2 * first, in + 2 * first + second * 2, sipbuff.begin()); + if ((idx += insize) >= sipsize) idx -= sipsize; } } } @@ -168,8 +163,7 @@ void SIPHON::suck() void SIPHON::sip_spectrum() { - int i; - for (i = 0; i < fftsize; i++) + for (int i = 0; i < fftsize; i++) { sipout[2 * i + 0] *= window[i]; sipout[2 * i + 1] *= window[i]; @@ -189,7 +183,7 @@ void SIPHON::getaSipF(float* _out, int _size) suck (); for (int i = 0; i < _size; i++) { - _out[i] = (float) sipout[2 * i + 0]; + _out[i] = sipout[2 * i + 0]; } } @@ -200,8 +194,8 @@ void SIPHON::getaSipF1(float* _out, int _size) for (int i = 0; i < _size; i++) { - _out[2 * i + 0] = (float) sipout[2 * i + 0]; - _out[2 * i + 1] = (float) sipout[2 * i + 1]; + _out[2 * i + 0] = sipout[2 * i + 0]; + _out[2 * i + 1] = sipout[2 * i + 1]; } } @@ -236,7 +230,11 @@ void SIPHON::setSipSpecmode(int _mode) void SIPHON::getSpecF1(float* _out) { // return spectrum magnitudes in dB - int i, j, mid, m, n; + int i; + int j; + int mid; + int m; + int n; outsize = fftsize; suck(); sip_spectrum(); @@ -262,68 +260,5 @@ void SIPHON::getSpecF1(float* _out) } } -/******************************************************************************************************** -* * -* CALLS FOR EXTERNAL USE * -* * -********************************************************************************************************/ - -/* -#define MAX_EXT_SIPHONS (2) // maximum number of Siphons called from outside wdsp -__declspec (align (16)) SIPHON psiphon[MAX_EXT_SIPHONS]; // array of pointers for Siphons used EXTERNAL to wdsp - - -PORT -void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode) -{ - psiphon[id] = create_siphon (run, 0, 0, 0, insize, 0, sipsize, fftsize, specmode); -} - -PORT -void destroy_siphonEXT (int id) -{ - destroy_siphon (psiphon[id]); -} - -PORT -void flush_siphonEXT (int id) -{ - flush_siphon (psiphon[id]); -} - -PORT -void xsiphonEXT (int id, float* buff) -{ - SIPHON a = psiphon[id]; - a->in = buff; - xsiphon (a, 0); -} - -PORT -void GetaSipF1EXT (int id, float* out, int size) -{ // return raw samples as floats - SIPHON a = psiphon[id]; - int i; - a->update.lock(); - a->outsize = size; - suck (a); - a->update.unlock(); - for (i = 0; i < size; i++) - { - out[2 * i + 0] = (float)a->sipout[2 * i + 0]; - out[2 * i + 1] = (float)a->sipout[2 * i + 1]; - } -} - -PORT -void SetSiphonInsize (int id, int size) -{ - SIPHON a = psiphon[id]; - a->update.lock(); - a->insize = size; - a->update.unlock(); -} -*/ - } // namespace WDSP diff --git a/wdsp/siphon.hpp b/wdsp/siphon.hpp index bad4065d3..4517223ae 100644 --- a/wdsp/siphon.hpp +++ b/wdsp/siphon.hpp @@ -87,11 +87,6 @@ public: void setSipDisplay(int disp); void getSpecF1(float* out); void setSipSpecmode(int mode); - // Calls for External Use - // static void create_siphonEXT (int id, int run, int insize, int sipsize, int fftsize, int specmode); - // static void destroy_siphonEXT (int id); - // static void xsiphonEXT (int id, float* buff); - // static void SetSiphonInsize (int id, int size); private: void build_window(); diff --git a/wdsp/snba.cpp b/wdsp/snba.cpp index 1783066c5..c212aa3ca 100644 --- a/wdsp/snba.cpp +++ b/wdsp/snba.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "resample.hpp" #include "lmath.hpp" @@ -36,10 +38,75 @@ warren@wpratt.com #include "emnr.hpp" #include "snba.hpp" -#define MAXIMP 256 - namespace WDSP { +SNBA::Exec::Exec(int xsize, int _asize, int _npasses) : + asize(_asize), + npasses(_npasses) +{ + a.resize(xsize); + v.resize(xsize); + detout.resize(xsize); + savex.resize(xsize); + xHout.resize(xsize); + unfixed.resize(xsize); +} + +void SNBA::Exec::fluxh() +{ + std::fill (a.begin(), a.end(), 0); + std::fill (v.begin(), v.end(), 0); + std::fill (detout.begin(), detout.end(), 0); + std::fill (savex.begin(), savex.end(), 0); + std::fill (xHout.begin(), xHout.end(), 0); + std::fill (unfixed.begin(), unfixed.end(), 0); +} + +SNBA::Det::Det( + int _xsize, + double _k1, + double _k2, + int _b, + int _pre, + int _post +) : + k1(_k1), + k2(_k2), + b(_b), + pre(_pre), + post(_post) +{ + vp.resize(_xsize); + vpwr.resize(_xsize); +} + +void SNBA::Det::flush() +{ + std::fill(vp.begin(), vp.end(), 0); + std::fill(vpwr.begin(), vpwr.end(), 0); +} + +SNBA::Wrk::Wrk( + int xsize, + int asize +) : + xHat_a1rows_max(xsize + asize), + xHat_a2cols_max(xsize + 2 * asize) +{ + xHat_r.resize(xsize); + xHat_ATAI.resize(xsize * xsize); + xHat_A1.resize(xHat_a1rows_max * xsize); + xHat_A2.resize(xHat_a1rows_max * xHat_a2cols_max); + xHat_P1.resize(xsize * xHat_a2cols_max); + xHat_P2.resize(xsize); + trI_y.resize(xsize - 1); + trI_v.resize(xsize - 1); + dR_z.resize(xsize - 2); + asolve_r.resize(asize + 1); + asolve_z.resize(asize + 1); + +} + void SNBA::calc() { if (inrate >= internalrate) @@ -47,8 +114,8 @@ void SNBA::calc() else isize = bsize * (internalrate / inrate); - inbuff = new float[isize * 2]; // (double *) malloc0 (isize * sizeof (complex)); - outbuff = new float[isize * 2]; // (double *) malloc0 (isize * sizeof (complex)); + inbuff = new float[isize * 2]; + outbuff = new float[isize * 2]; if (inrate != internalrate) resamprun = 1; @@ -88,7 +155,7 @@ void SNBA::calc() iainidx = 0; iaoutidx = 0; - inaccum = new double[iasize * 2]; // (double *) malloc0 (iasize * sizeof (double)); + inaccum.resize(iasize * 2); nsamps = 0; if (incr > isize) @@ -105,7 +172,7 @@ void SNBA::calc() } init_oaoutidx = oaoutidx; - outaccum = new double[oasize * 2]; // (double *) malloc0 (oasize * sizeof (double)); + outaccum.resize(oasize * 2); } SNBA::SNBA( @@ -140,15 +207,12 @@ SNBA::SNBA( iasize(0), iainidx(0), iaoutidx(0), - inaccum(nullptr), - xbase(nullptr), xaux(nullptr), nsamps(0), oasize(0), oainidx(0), oaoutidx(0), init_oaoutidx(0), - outaccum(nullptr), resamprun(0), isize(0), inresamp(nullptr), @@ -156,80 +220,29 @@ SNBA::SNBA( inbuff(nullptr), outbuff(nullptr), out_low_cut(_out_low_cut), - out_high_cut(_out_high_cut) + out_high_cut(_out_high_cut), + exec(_xsize, _asize, _npasses), + sdet(_xsize, _k1, _k2, _b, _pre, _post), + wrk(_xsize, _asize) { - exec.asize = _asize; - exec.npasses = _npasses; - sdet.k1 = _k1; - sdet.k2 = _k2; - sdet.b = _b; - sdet.pre = _pre; - sdet.post = _post; scan.pmultmin = _pmultmin; calc(); - xbase = new double[2 * xsize]; // (double *) malloc0 (2 * xsize * sizeof (double)); - xaux = xbase + xsize; - exec.a = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - exec.v = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - exec.detout = new int[xsize]; //(int *) malloc0 (xsize * sizeof (int)); - exec.savex = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - exec.xHout = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - exec.unfixed = new int[xsize]; //(int *) malloc0 (xsize * sizeof (int)); - sdet.vp = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - sdet.vpwr = new double[xsize]; //(double *) malloc0 (xsize * sizeof (double)); - - wrk.xHat_a1rows_max = xsize + exec.asize; - wrk.xHat_a2cols_max = xsize + 2 * exec.asize; - wrk.xHat_r = new double[xsize]; // (double *) malloc0 (xsize * sizeof(double)); - wrk.xHat_ATAI = new double[xsize * xsize]; // (double *) malloc0 (xsize * xsize * sizeof(double)); - wrk.xHat_A1 = new double[wrk.xHat_a1rows_max * xsize]; // (double *) malloc0 (wrk.xHat_a1rows_max * xsize * sizeof(double)); - wrk.xHat_A2 = new double[wrk.xHat_a1rows_max * wrk.xHat_a2cols_max]; // (double *) malloc0 (wrk.xHat_a1rows_max * wrk.xHat_a2cols_max * sizeof(double)); - wrk.xHat_P1 = new double[xsize * wrk.xHat_a2cols_max]; // (double *) malloc0 (xsize * wrk.xHat_a2cols_max * sizeof(double)); - wrk.xHat_P2 = new double[xsize]; // (double *) malloc0 (xsize * sizeof(double)); - wrk.trI_y = new double[xsize - 1]; // (double *) malloc0 ((xsize - 1) * sizeof(double)); - wrk.trI_v = new double[xsize - 1]; // (double *) malloc0 ((xsize - 1) * sizeof(double)); - wrk.dR_z = new double[xsize - 2]; // (double *) malloc0 ((xsize - 2) * sizeof(double)); - wrk.asolve_r = new double[exec.asize + 1]; // (double *) malloc0 ((exec.asize + 1) * sizeof(double)); - wrk.asolve_z = new double[exec.asize + 1]; // (double *) malloc0 ((exec.asize + 1) * sizeof(double)); + xbase.resize(2 * xsize); + xaux = &xbase[xsize]; } void SNBA::decalc() { - delete (outresamp); - delete (inresamp); - delete[] (outbuff); - delete[] (inbuff); - delete[] (outaccum); - delete[] (inaccum); + delete outresamp; + delete inresamp; + delete[] outbuff; + delete[] inbuff; } SNBA::~SNBA() { - delete[] (wrk.xHat_r); - delete[] (wrk.xHat_ATAI); - delete[] (wrk.xHat_A1); - delete[] (wrk.xHat_A2); - delete[] (wrk.xHat_P1); - delete[] (wrk.xHat_P2); - delete[] (wrk.trI_y); - delete[] (wrk.trI_v); - delete[] (wrk.dR_z); - delete[] (wrk.asolve_r); - delete[] (wrk.asolve_z); - - delete[] (sdet.vpwr); - delete[] (sdet.vp); - delete[] (exec.unfixed); - delete[] (exec.xHout); - delete[] (exec.savex); - delete[] (exec.detout); - delete[] (exec.v); - delete[] (exec.a); - - delete[] (xbase); - decalc(); } @@ -241,20 +254,13 @@ void SNBA::flush() oainidx = 0; oaoutidx = init_oaoutidx; - memset (inaccum, 0, iasize * sizeof (double)); - memset (outaccum, 0, oasize * sizeof (double)); - memset (xaux, 0, xsize * sizeof (double)); - memset (exec.a, 0, xsize * sizeof (double)); - memset (exec.v, 0, xsize * sizeof (double)); - memset (exec.detout, 0, xsize * sizeof (int)); - memset (exec.savex, 0, xsize * sizeof (double)); - memset (exec.xHout, 0, xsize * sizeof (double)); - memset (exec.unfixed, 0, xsize * sizeof (int)); - memset (sdet.vp, 0, xsize * sizeof (double)); - memset (sdet.vpwr, 0, xsize * sizeof (double)); - - std::fill(inbuff, inbuff + isize * 2, 0); - std::fill(outbuff, outbuff + isize * 2, 0); + exec.fluxh(); + sdet.flush(); + std::fill(inaccum.begin(), inaccum.end(), 0); + std::fill(outaccum.begin(), outaccum.end(), 0); + std::fill(xaux, xaux + xsize, 0); + std::fill(inbuff, inbuff + isize * 2, 0); + std::fill(outbuff, outbuff + isize * 2, 0); inresamp->flush(); outresamp->flush(); @@ -282,27 +288,26 @@ void SNBA::setSize(int size) calc(); } -void SNBA::ATAc0 (int n, int nr, double* A, double* r) +void SNBA::ATAc0 (int n, int nr, std::vector& A, std::vector& r) { - int i, j; - memset(r, 0, n * sizeof (double)); + std::fill(r.begin(), r.begin() + n, 0); - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) { - for (j = 0; j < nr; j++) + for (int j = 0; j < nr; j++) r[i] += A[j * n + i] * A[j * n + 0]; } } -void SNBA::multA1TA2(double* a1, double* a2, int m, int n, int q, double* c) +void SNBA::multA1TA2(std::vector& a1, std::vector& a2, int m, int n, int q, std::vector& c) { - int i, j, k; + int k; int p = q - m; - memset (c, 0, m * n * sizeof (double)); + std::fill(c.begin(), c.begin() + m*n, 0); - for (i = 0; i < m; i++) + for (int i = 0; i < m; i++) { - for (j = 0; j < n; j++) + for (int j = 0; j < n; j++) { if (j < p) { @@ -318,12 +323,12 @@ void SNBA::multA1TA2(double* a1, double* a2, int m, int n, int q, double* c) } } -void SNBA::multXKE(double* a, double* xk, int m, int q, int p, double* vout) +void SNBA::multXKE(std::vector& a, const double* xk, int m, int q, int p, std::vector& vout) { - int i, k; - memset (vout, 0, m * sizeof (double)); + int k; + std::fill(vout.begin(), vout.begin() + m, 0); - for (i = 0; i < m; i++) + for (int i = 0; i < m; i++) { for (k = i; k < p; k++) vout[i] += a[i * q + k] * xk[k]; @@ -332,14 +337,13 @@ void SNBA::multXKE(double* a, double* xk, int m, int q, int p, double* vout) } } -void SNBA::multAv(double* a, double* v, int m, int q, double* vout) +void SNBA::multAv(std::vector& a, std::vector& v, int m, int q, std::vector& vout) { - int i, k; - memset (vout, 0, m * sizeof (double)); + std::fill(vout.begin(), vout.begin() + m, 0); - for (i = 0; i < m; i++) + for (int i = 0; i < m; i++) { - for (k = 0; k < q; k++) + for (int k = 0; k < q; k++) vout[i] += a[i * q + k] * v[k]; } } @@ -347,29 +351,31 @@ void SNBA::multAv(double* a, double* v, int m, int q, double* vout) void SNBA::xHat( int xusize, int asize, - double* xk, - double* a, - double* xout, - double* r, - double* ATAI, - double* A1, - double* A2, - double* P1, - double* P2, - double* trI_y, - double* trI_v, - double* dR_z + const double* xk, + std::vector& a, + std::vector& xout, + std::vector& r, + std::vector& ATAI, + std::vector& A1, + std::vector& A2, + std::vector& P1, + std::vector& P2, + std::vector& trI_y, + std::vector& trI_v, + std::vector& dR_z ) { - int i, j, k; + int i; + int j; + int k; int a1rows = xusize + asize; int a2cols = xusize + 2 * asize; - memset (r, 0, xusize * sizeof(double)); // work space - memset (ATAI, 0, xusize * xusize * sizeof(double)); // work space - memset (A1, 0, a1rows * xusize * sizeof(double)); // work space - memset (A2, 0, a1rows * a2cols * sizeof(double)); // work space - memset (P1, 0, xusize * a2cols * sizeof(double)); // work space - memset (P2, 0, xusize * sizeof(double)); // work space + std::fill (r.begin(), r.begin() + xusize, 0); // work space + std::fill (ATAI.begin(), ATAI.begin() + xusize * xusize, 0); // work space + std::fill (A1.begin(), A1.begin() + a1rows * xusize, 0); // work space + std::fill (A2.begin(), A2.begin() + a1rows * a2cols, 0); // work space + std::fill (P1.begin(), P1.begin() + xusize * a2cols, 0); // work space + std::fill (P2.begin(), P2.begin() + xusize, 0); // work space for (i = 0; i < xusize; i++) { @@ -380,28 +386,29 @@ void SNBA::xHat( } for (i = 0; i < asize; i++) - { - for (k = asize - i - 1, j = 0; k < asize; k++, j++) - A2[j * a2cols + i] = a[k]; - } + { + for (k = asize - i - 1, j = 0; k < asize; k++, j++) + A2[j * a2cols + i] = a[k]; + } for (i = asize + xusize; i < 2 * asize + xusize; i++) - { - A2[(i - asize) * a2cols + i] = - 1.0; - for (j = i - asize + 1, k = 0; j < xusize + asize; j++, k++) - A2[j * a2cols + i] = a[k]; - } + { + A2[(i - asize) * a2cols + i] = - 1.0; + for (j = i - asize + 1, k = 0; j < xusize + asize; j++, k++) + A2[j * a2cols + i] = a[k]; + } ATAc0(xusize, xusize + asize, A1, r); - LMathd::trI(xusize, r, ATAI, trI_y, trI_v, dR_z); + LMathd::trI(xusize, r.data(), ATAI.data(), trI_y.data(), trI_v.data(), dR_z.data()); multA1TA2(A1, A2, xusize, 2 * asize + xusize, xusize + asize, P1); multXKE(P1, xk, xusize, xusize + 2 * asize, asize, P2); multAv(ATAI, P2, xusize, xusize, xout); } -void SNBA::invf(int xsize, int asize, double* a, double* x, double* v) +void SNBA::invf(int xsize, int asize, std::vector& a, const double* x, std::vector& v) { - int i, j; - memset (v, 0, xsize * sizeof (double)); + int i; + int j; + std::fill(v.begin(), v.begin() + xsize, 0); for (i = asize; i < xsize - asize; i++) { @@ -417,12 +424,16 @@ void SNBA::invf(int xsize, int asize, double* a, double* x, double* v) } } -void SNBA::det(int asize, double* v, int* detout) +void SNBA::det(int asize, std::vector& v, std::vector& detout) { - int i, j; + int i; + int j; double medpwr; - double t1, t2; - int bstate, bcount, bsamp; + double t1; + double t2; + int bstate; + int bcount; + int bsamp; for (i = asize, j = 0; i < xsize; i++, j++) { @@ -430,7 +441,7 @@ void SNBA::det(int asize, double* v, int* detout) sdet.vp[j] = sdet.vpwr[i]; } - LMathd::median(xsize - asize, sdet.vp, &medpwr); + LMathd::median(xsize - asize, sdet.vp.data(), &medpwr); t1 = sdet.k1 * medpwr; t2 = 0.0; @@ -492,6 +503,8 @@ void SNBA::det(int asize, double* v, int* detout) bstate = 1; } + break; + default: break; } } @@ -525,24 +538,26 @@ int SNBA::scanFrame( int xsize, int pval, double pmultmin, - int* det, - int* bimp, - int* limp, - int* befimp, - int* aftimp, - int* p_opt, + std::vector& det, + std::array& bimp, + std::array& limp, + std::array& befimp, + std::array& aftimp, + std::array& p_opt, int* next ) { int inflag = 0; - int i = 0, j = 0, k = 0; + int i = 0; + int j = 0; + int k = 0; int nimp = 0; double td; int ti; - double merit[MAXIMP] = { 0 }; - int nextlist[MAXIMP]; - memset (befimp, 0, MAXIMP * sizeof (int)); - memset (aftimp, 0, MAXIMP * sizeof (int)); + std::array merit = { 0 }; + std::array nextlist; + std::fill(befimp.begin(), befimp.end(), 0); + std::fill(aftimp.begin(), aftimp.end(), 0); while (i < xsize && nimp < MAXIMP) { @@ -555,7 +570,8 @@ int SNBA::scanFrame( } else if (det[i] == 1) { - limp[nimp - 1]++; + if (nimp > 0) + limp[nimp - 1]++; } else { @@ -634,22 +650,20 @@ int SNBA::scanFrame( void SNBA::execFrame(double* x) { - int i, k; - int pass; int nimp; - int bimp[MAXIMP]; - int limp[MAXIMP]; - int befimp[MAXIMP]; - int aftimp[MAXIMP]; - int p_opt[MAXIMP]; + std::array bimp; + std::array limp; + std::array befimp; + std::array aftimp; + std::array p_opt; int next = 0; int p; - memcpy (exec.savex, x, xsize * sizeof (double)); - LMathd::asolve(xsize, exec.asize, x, exec.a, wrk.asolve_r, wrk.asolve_z); + std::copy(x, x + xsize, exec.savex.begin()); + LMathd::asolve(xsize, exec.asize, x, exec.a.data(), wrk.asolve_r.data(), wrk.asolve_z.data()); invf(xsize, exec.asize, exec.a, x, exec.v); det(exec.asize, exec.v, exec.detout); - for (i = 0; i < xsize; i++) + for (int i = 0; i < xsize; i++) { if (exec.detout[i] != 0) x[i] = 0.0; @@ -657,18 +671,18 @@ void SNBA::execFrame(double* x) nimp = scanFrame(xsize, exec.asize, scan.pmultmin, exec.detout, bimp, limp, befimp, aftimp, p_opt, &next); - for (pass = 0; pass < exec.npasses; pass++) + for (int pass = 0; pass < exec.npasses; pass++) { - memcpy (exec.unfixed, exec.detout, xsize * sizeof (int)); + std::copy(exec.detout.begin(), exec.detout.end(), exec.unfixed.begin()); - for (k = 0; k < nimp; k++) + for (int k = 0; k < nimp; k++) { if (k > 0) scanFrame(xsize, exec.asize, scan.pmultmin, exec.unfixed, bimp, limp, befimp, aftimp, p_opt, &next); if ((p = p_opt[next]) > 0) { - LMathd::asolve(xsize, p, x, exec.a, wrk.asolve_r, wrk.asolve_z); + LMathd::asolve(xsize, p, x, exec.a.data(), wrk.asolve_r.data(), wrk.asolve_z.data()); xHat( limp[next], p, @@ -685,7 +699,7 @@ void SNBA::execFrame(double* x) wrk.trI_v, wrk.dR_z ); - memcpy (&x[bimp[next]], exec.xHout, limp[next] * sizeof (double)); + std::copy(exec.xHout.begin(), exec.xHout.begin() + limp[next], &x[bimp[next]]); memset (&exec.unfixed[bimp[next]], 0, limp[next] * sizeof (int)); } else @@ -719,12 +733,12 @@ void SNBA::execute() nsamps -= incr; memcpy (&outaccum[oainidx], xaux, incr * sizeof (double)); oainidx = (oainidx + incr) % oasize; - memmove (xbase, &xbase[incr], (2 * xsize - incr) * sizeof (double)); + std::copy(&xbase[incr], &xbase[incr] + (2 * xsize - incr), xbase.begin()); } for (i = 0; i < isize; i++) { - outbuff[2 * i + 0] = outaccum[oaoutidx]; + outbuff[2 * i + 0] = (float) outaccum[oaoutidx]; outbuff[2 * i + 1] = 0.0; oaoutidx = (oaoutidx + 1) % oasize; } @@ -792,7 +806,8 @@ void SNBA::setPmultmin(double pmultmin) void SNBA::setOutputBandwidth(double flow, double fhigh) { - double f_low, f_high; + double f_low = 0; + double f_high = 0; if (flow >= 0 && fhigh >= 0) { @@ -802,7 +817,7 @@ void SNBA::setOutputBandwidth(double flow, double fhigh) if (flow > out_high_cut) flow = out_high_cut; - f_low = std::max ( out_low_cut, flow); + f_low = std::max (out_low_cut, flow); f_high = std::min (out_high_cut, fhigh); } else if (flow <= 0 && fhigh <= 0) @@ -813,7 +828,7 @@ void SNBA::setOutputBandwidth(double flow, double fhigh) if (fhigh < -out_high_cut) fhigh = -out_high_cut; - f_low = std::max ( out_low_cut, -fhigh); + f_low = std::max (out_low_cut, -fhigh); f_high = std::min (out_high_cut, -flow); } else if (flow < 0 && fhigh > 0) diff --git a/wdsp/snba.hpp b/wdsp/snba.hpp index e356c02a6..ab12d871c 100644 --- a/wdsp/snba.hpp +++ b/wdsp/snba.hpp @@ -28,11 +28,15 @@ warren@wpratt.com #ifndef wdsp_snba_h #define wdsp_snba_h +#include + +#include "export.h" + namespace WDSP{ class RESAMPLE; -class SNBA +class WDSP_API SNBA { public: int run; @@ -47,15 +51,15 @@ public: int iasize; int iainidx; int iaoutidx; - double* inaccum; - double* xbase; + std::vector inaccum; + std::vector xbase; double* xaux; int nsamps; int oasize; int oainidx; int oaoutidx; int init_oaoutidx; - double* outaccum; + std::vector outaccum; int resamprun; int isize; RESAMPLE *inresamp; @@ -64,48 +68,72 @@ public: float* outbuff; double out_low_cut; double out_high_cut; + static const int MAXIMP = 256; - struct _exec + struct Exec { int asize; - double* a; - double* v; - int* detout; - double* savex; - double* xHout; - int* unfixed; + std::vector a; + std::vector v; + std::vector detout; + std::vector savex; + std::vector xHout; + std::vector unfixed; int npasses; - } exec; - struct _det + + Exec(int xsize, int _asize, int _npasses); + void fluxh(); + }; + Exec exec; + struct Det { double k1; double k2; int b; int pre; int post; - double* vp; - double* vpwr; - } sdet; - struct _scan + std::vector vp; + std::vector vpwr; + + Det( + int xsize, + double k1, + double k2, + int b, + int pre, + int post + ); + void flush(); + }; + Det sdet; + struct Scan { double pmultmin; - } scan; - struct _wrk + }; + Scan scan; + struct Wrk { int xHat_a1rows_max; int xHat_a2cols_max; - double* xHat_r; - double* xHat_ATAI; - double* xHat_A1; - double* xHat_A2; - double* xHat_P1; - double* xHat_P2; - double* trI_y; - double* trI_v; - double* dR_z; - double* asolve_r; - double* asolve_z; - } wrk; + std::vector xHat_r; + std::vector xHat_ATAI; + std::vector xHat_A1; + std::vector xHat_A2; + std::vector xHat_P1; + std::vector xHat_P2; + std::vector trI_y; + std::vector trI_v; + std::vector dR_z; + std::vector asolve_r; + std::vector asolve_z; + + Wrk( + int xsize, + int asize + ); + void flush(); + }; + Wrk wrk; SNBA( int run, @@ -149,40 +177,40 @@ public: private: void calc(); void decalc(); - static void ATAc0 (int n, int nr, double* A, double* r); - static void multA1TA2(double* a1, double* a2, int m, int n, int q, double* c); - static void multXKE(double* a, double* xk, int m, int q, int p, double* vout); - static void multAv(double* a, double* v, int m, int q, double* vout); + static void ATAc0 (int n, int nr, std::vector& A, std::vector& r); + static void multA1TA2(std::vector& a1, std::vector& a2, int m, int n, int q, std::vector& c); + static void multXKE(std::vector& a, const double* xk, int m, int q, int p, std::vector& vout); + static void multAv(std::vector& a, std::vector& v, int m, int q, std::vector& vout); static void xHat( int xusize, int asize, - double* xk, - double* a, - double* xout, - double* r, - double* ATAI, - double* A1, - double* A2, - double* P1, - double* P2, - double* trI_y, - double* trI_v, - double* dR_z + const double* xk, + std::vector& a, + std::vector& xout, + std::vector& r, + std::vector& ATAI, + std::vector& A1, + std::vector& A2, + std::vector& P1, + std::vector& P2, + std::vector& trI_y, + std::vector& trI_v, + std::vector& dR_z ); - static void invf(int xsize, int asize, double* a, double* x, double* v); + static void invf(int xsize, int asize, std::vector& a, const double* x, std::vector& v); static int scanFrame( int xsize, int pval, double pmultmin, - int* det, - int* bimp, - int* limp, - int* befimp, - int* aftimp, - int* p_opt, + std::vector& det, + std::array& bimp, + std::array& limp, + std::array& befimp, + std::array& aftimp, + std::array& p_opt, int* next ); - void det(int asize, double* v, int* detout); + void det(int asize, std::vector& v, std::vector& detout); void execFrame(double* x); }; diff --git a/wdsp/snotch.cpp b/wdsp/snotch.cpp index d26e6a5ad..f2d532c0b 100644 --- a/wdsp/snotch.cpp +++ b/wdsp/snotch.cpp @@ -38,8 +38,11 @@ namespace WDSP { void SNOTCH::calc() { - double fn, qk, qr, csn; - fn = f / (double) rate; + double fn; + double qk; + double qr; + double csn; + fn = f / rate; csn = cos (TWOPI * fn); qr = 1.0 - 3.0 * bw; qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); @@ -80,11 +83,10 @@ void SNOTCH::execute() { if (run) { - int i; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { x0 = in[2 * i + 0]; - out[2 * i + 0] = a0 * x0 + a1 * x1 + a2 * x2 + b1 * y1 + b2 * y2; + out[2 * i + 0] = (float) (a0 * x0 + a1 * x1 + a2 * x2 + b1 * y1 + b2 * y2); y2 = y1; y1 = out[2 * i + 0]; x2 = x1; diff --git a/wdsp/snotch.hpp b/wdsp/snotch.hpp index bbed01d1c..027e02bbb 100644 --- a/wdsp/snotch.hpp +++ b/wdsp/snotch.hpp @@ -48,8 +48,16 @@ public: double rate; double f; double bw; - double a0, a1, a2, b1, b2; - double x0, x1, x2, y1, y2; + double a0; + double a1; + double a2; + double b1; + double b2; + double x0; + double x1; + double x2; + double y1; + double y2; SNOTCH( int run, @@ -62,7 +70,7 @@ public: ); SNOTCH(const SNOTCH&) = delete; SNOTCH& operator=(SNOTCH& other) = delete; - ~SNOTCH() {} + ~SNOTCH() = default; void flush(); void execute(); diff --git a/wdsp/speak.cpp b/wdsp/speak.cpp index b2a1f0a20..e07e5a168 100644 --- a/wdsp/speak.cpp +++ b/wdsp/speak.cpp @@ -39,31 +39,38 @@ namespace WDSP { void SPEAK::calc() { double ratio; - double f_corr, g_corr, bw_corr, bw_parm, A, f_min; + double f_corr; + double g_corr; + double bw_corr; + double bw_parm; + double A; + double f_min; switch (design) { case 0: ratio = bw / f; - switch (nstages) + if (nstages == 4) { - case 4: bw_parm = 2.4; f_corr = 1.0 - 0.160 * ratio + 1.440 * ratio * ratio; g_corr = 1.0 - 1.003 * ratio + 3.990 * ratio * ratio; - break; - default: + } + else + { bw_parm = 1.0; f_corr = 1.0; g_corr = 1.0; - break; } { - double fn, qk, qr, csn; + double fn; + double qk; + double qr; + double csn; fgain = gain / g_corr; - fn = f / (double)rate / f_corr; + fn = f / rate / f_corr; csn = cos (TWOPI * fn); - qr = 1.0 - 3.0 * bw / (double)rate * bw_parm; + qr = 1.0 - 3.0 * bw / rate * bw_parm; qk = (1.0 - 2.0 * qr * csn + qr * qr) / (2.0 * (1.0 - csn)); a0 = 1.0 - qk; a1 = 2.0 * (qk - qr) * csn; @@ -76,26 +83,27 @@ void SPEAK::calc() case 1: if (f < 200.0) f = 200.0; ratio = bw / f; - switch (nstages) + if (nstages == 4) { - case 4: bw_parm = 5.0; bw_corr = 1.13 * ratio - 0.956 * ratio * ratio; A = 2.5; f_min = 50.0; - break; - default: + } + else + { bw_parm = 1.0; bw_corr = 1.0; - g_corr = 1.0; A = 2.5; f_min = 50.0; - break; } { - double w0, sn, c, den; + double w0; + double sn; + double c; + double den; if (f < f_min) f = f_min; - w0 = TWOPI * f / (double)rate; + w0 = TWOPI * f / rate; sn = sin (w0); cbw = bw_corr * f; c = sn * sinh(0.5 * log((f + 0.5 * cbw * bw_parm) / (f - 0.5 * cbw * bw_parm)) * w0 / sn); @@ -108,6 +116,8 @@ void SPEAK::calc() fgain = gain / pow (A * A, (double)nstages); } break; + default: + break; } flush(); } @@ -135,12 +145,12 @@ SPEAK::SPEAK( nstages(_nstages), design(_design) { - x0.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); - x1.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); - x2.resize(nstages * 2); //(float *) malloc0 (nstages * sizeof (complex)); - y0.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); - y1.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); - y2.resize(nstages * 2); // (float *) malloc0 (nstages * sizeof (complex)); + x0.resize(nstages * 2); + x1.resize(nstages * 2); + x2.resize(nstages * 2); + y0.resize(nstages * 2); + y1.resize(nstages * 2); + y2.resize(nstages * 2); calc(); } @@ -178,7 +188,7 @@ void SPEAK::execute() x1[2 * n + j] = x0[2 * n + j]; } - out[2 * i + j] = y0[2 * (nstages - 1) + j]; + out[2 * i + j] = (float) y0[2 * (nstages - 1) + j]; } } } diff --git a/wdsp/speak.hpp b/wdsp/speak.hpp index b5602a1f2..b216b91ac 100644 --- a/wdsp/speak.hpp +++ b/wdsp/speak.hpp @@ -55,8 +55,17 @@ public: double fgain; int nstages; int design; - double a0, a1, a2, b1, b2; - std::vector x0, x1, x2, y0, y1, y2; + double a0; + double a1; + double a2; + double b1; + double b2; + std::vector x0; + std::vector x1; + std::vector x2; + std::vector y0; + std::vector y1; + std::vector y2; SPEAK( int run, @@ -72,7 +81,7 @@ public: ); SPEAK(const SPEAK&) = delete; SPEAK& operator=(const SPEAK& other) = delete; - ~SPEAK() {} + ~SPEAK() = default; void flush(); void execute(); diff --git a/wdsp/sphp.cpp b/wdsp/sphp.cpp index 2b392b6fe..b17b7d248 100644 --- a/wdsp/sphp.cpp +++ b/wdsp/sphp.cpp @@ -81,14 +81,13 @@ void SPHP::execute() { if (run) { - int i, j, n; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { - for (j = 0; j < 2; j++) + for (int j = 0; j < 2; j++) { x0[j] = in[2 * i + j]; - for (n = 0; n < nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) x0[2 * n + j] = y0[2 * (n - 1) + j]; @@ -100,7 +99,7 @@ void SPHP::execute() x1[2 * n + j] = x0[2 * n + j]; } - out[2 * i + j] = y0[2 * (nstages - 1) + j]; + out[2 * i + j] = (float) y0[2 * (nstages - 1) + j]; } } } diff --git a/wdsp/ssql.cpp b/wdsp/ssql.cpp index 69ec65aa0..98410fc3e 100644 --- a/wdsp/ssql.cpp +++ b/wdsp/ssql.cpp @@ -56,7 +56,7 @@ FTOV::FTOV( in = _in; out = _out; eps = 0.01; - ring.resize(rsize); // (int*) malloc0 (rsize * sizeof (int)); + ring.resize(rsize); rptr = 0; inlast = 0.0; rcount = 0; @@ -91,7 +91,7 @@ void FTOV::execute() rcount++; // increment the count } if (++rptr == rsize) rptr = 0; // increment and wrap the pointer as needed - out[0] = std::min (1.0, (double)rcount / div); // calculate the output sample + out[0] = (float) std::min (1.0, (double)rcount / div); // calculate the output sample inlast = in[size - 1]; // save the last input sample for next buffer for (int i = 1; i < size; i++) { @@ -107,7 +107,7 @@ void FTOV::execute() rcount++; // increment the count } if (++rptr == rsize) rptr = 0; // increment and wrap the pointer as needed - out[i] = std::min(1.0, (double)rcount / div); // calculate the output sample + out[i] = (float) std::min(1.0, (double)rcount / div); // calculate the output sample } } } @@ -118,7 +118,8 @@ void FTOV::execute() void SSQL::compute_slews() { - double delta, theta; + double delta; + double theta; delta = PI / (double) ntup; theta = 0.0; for (int i = 0; i <= ntup; i++) @@ -137,15 +138,33 @@ void SSQL::compute_slews() void SSQL::calc() { - b1 = new float[size * 2]; // (float*) malloc0 (size * sizeof (complex)); - dcbl = new CBL(1, size, in, b1, 0, rate, 0.02); - ibuff = new float[size]; // (float*) malloc0 (size * sizeof (float)); - ftovbuff = new float[size]; // (float*) malloc0(size * sizeof (float)); - cvtr = new FTOV(1, size, rate, ftov_rsize, ftov_fmax, ibuff, ftovbuff); - lpbuff = new float[size]; // (float*) malloc0 (size * sizeof (float)); - filt = new DBQLP(1, size, ftovbuff, lpbuff, rate, 11.3, 1.0, 1.0, 1); - wdbuff = new int[size]; // (int*) malloc0 (size * sizeof (int)); - tr_signal = new int[size]; // (int*) malloc0 (size * sizeof (int)); + b1.resize(size * 2); + dcbl = new CBL(1, size, in, b1.data(), 0, rate, 0.02); + ibuff.resize(size); + ftovbuff.resize(size); + cvtr = new FTOV( + 1, + size, + rate, + ftov_rsize, + ftov_fmax, + ibuff.data(), + ftovbuff.data() + ); + lpbuff.resize(size); + filt = new DBQLP( + 1, + size, + ftovbuff.data(), + lpbuff.data(), + rate, + 11.3, + 1.0, + 1.0, + 1 + ); + wdbuff.resize(size); + tr_signal.resize(size); // window detector wdmult = exp (-1.0 / (rate * wdtau)); wdaverage = 0.0; @@ -156,27 +175,19 @@ void SSQL::calc() // level change ntup = (int)(tup * rate); ntdown = (int)(tdown * rate); - cup = new float[ntup + 1]; // (float*) malloc0 ((ntup + 1) * sizeof (float)); - cdown = new float[ntdown + 1]; // (float*) malloc0 ((ntdown + 1) * sizeof (float)); + cup.resize(ntup + 1); + cdown.resize(ntdown + 1); compute_slews(); // control - state = 0; + state = SSQLState::MUTED; count = 0; } void SSQL::decalc() { - delete[] (tr_signal); - delete[] (wdbuff); - delete (filt); - delete[] (lpbuff); - delete (cvtr); - delete[] (ftovbuff); - delete[] (ibuff); - delete (dcbl); - delete[] (b1); - delete[] (cdown); - delete[] (cup); + delete filt; + delete cvtr; + delete dcbl; } SSQL::SSQL( @@ -223,24 +234,17 @@ SSQL::~SSQL() void SSQL::flush() { - std::fill(b1, b1 + size * 2, 0); + std::fill(b1.begin(), b1.end(), 0); dcbl->flush(); - memset (ibuff, 0, size * sizeof (float)); - memset (ftovbuff, 0, size * sizeof (float)); + std::fill(ibuff.begin(), ibuff.end(), 0); + std::fill(ftovbuff.begin(), ftovbuff.end(), 0); cvtr->flush(); - memset (lpbuff, 0, size * sizeof (float)); + std::fill(lpbuff.begin(), lpbuff.end(), 0); filt->flush(); - memset (wdbuff, 0, size * sizeof (int)); - memset (tr_signal, 0, size * sizeof (int)); + std::fill(wdbuff.begin(), wdbuff.end(), 0); + std::fill(tr_signal.begin(), tr_signal.end(), 0); } -enum _ssqlstate -{ - MUTED, - INCREASE, - UNMUTED, - DECREASE -}; void SSQL::execute() { @@ -277,35 +281,35 @@ void SSQL::execute() { switch (state) { - case MUTED: + case SSQLState::MUTED: if (tr_signal[i] == 1) { - state = INCREASE; + state = SSQLState::INCREASE; count = ntup; } - out[2 * i + 0] = muted_gain * in[2 * i + 0]; - out[2 * i + 1] = muted_gain * in[2 * i + 1]; + out[2 * i + 0] = (float) (muted_gain * in[2 * i + 0]); + out[2 * i + 1] = (float) (muted_gain * in[2 * i + 1]); break; - case INCREASE: - out[2 * i + 0] = in[2 * i + 0] * cup[ntup - count]; - out[2 * i + 1] = in[2 * i + 1] * cup[ntup - count]; + case SSQLState::INCREASE: + out[2 * i + 0] = (float) (in[2 * i + 0] * cup[ntup - count]); + out[2 * i + 1] = (float) (in[2 * i + 1] * cup[ntup - count]); if (count-- == 0) - state = UNMUTED; + state = SSQLState::UNMUTED; break; - case UNMUTED: + case SSQLState::UNMUTED: if (tr_signal[i] == 0) { - state = DECREASE; + state = SSQLState::DECREASE; count = ntdown; } out[2 * i + 0] = in[2 * i + 0]; out[2 * i + 1] = in[2 * i + 1]; break; - case DECREASE: - out[2 * i + 0] = in[2 * i + 0] * cdown[ntdown - count]; - out[2 * i + 1] = in[2 * i + 1] * cdown[ntdown - count]; + case SSQLState::DECREASE: + out[2 * i + 0] = (float) (in[2 * i + 0] * cdown[ntdown - count]); + out[2 * i + 1] = (float) (in[2 * i + 1] * cdown[ntdown - count]); if (count-- == 0) - state = MUTED; + state = SSQLState::MUTED; break; } } diff --git a/wdsp/ssql.hpp b/wdsp/ssql.hpp index 04261304e..e5bc8590b 100644 --- a/wdsp/ssql.hpp +++ b/wdsp/ssql.hpp @@ -75,26 +75,33 @@ class DBQLP; class WDSP_API SSQL // Syllabic Squelch { public: + enum class SSQLState + { + MUTED, + INCREASE, + UNMUTED, + DECREASE + }; int run; // 0 if squelch system is OFF; 1 if it's ON int size; // size of input/output buffers float* in; // squelch input signal buffer float* out; // squelch output signal buffer int rate; // sample rate - int state; // state machine control + SSQLState state; // state machine control int count; // count variable for raised cosine transitions double tup; // time for turn-on transition double tdown; // time for turn-off transition int ntup; // number of samples for turn-on transition int ntdown; // number of samples for turn-off transition - float* cup; // coefficients for up-slew - float* cdown; // coefficients for down-slew + std::vector cup; // coefficients for up-slew + std::vector cdown; // coefficients for down-slew double muted_gain; // audio gain while muted; 0.0 for complete silence - float* b1; // buffer to hold output of dc-block function - float* ibuff; // buffer containing only 'I' component - float* ftovbuff; // buffer containing output of f to v converter - float* lpbuff; // buffer containing output of low-pass filter - int* wdbuff; // buffer containing output of window detector + std::vector b1; // buffer to hold output of dc-block function + std::vector ibuff; // buffer containing only 'I' component + std::vector ftovbuff; // buffer containing output of f to v converter + std::vector lpbuff; // buffer containing output of low-pass filter + std::vector wdbuff; // buffer containing output of window detector CBL *dcbl; // pointer to DC Blocker data structure FTOV *cvtr; // pointer to F to V Converter data structure DBQLP *filt; // pointer to Bi-Quad Low-Pass Filter data structure @@ -114,7 +121,7 @@ public: double tr_voltage; // trigger voltage double mute_mult; // multiplier for successive voltage calcs when muted double unmute_mult; // multiplier for successive voltage calcs when unmuted - int* tr_signal; // trigger signal, 0 or 1 + std::vector tr_signal; // trigger signal, 0 or 1 SSQL( int run, diff --git a/wdsp/wcpAGC.cpp b/wdsp/wcpAGC.cpp index 455bc856d..ae7aac1f5 100644 --- a/wdsp/wcpAGC.cpp +++ b/wdsp/wcpAGC.cpp @@ -146,7 +146,8 @@ void WCPAGC::flush() void WCPAGC::execute() { - int i, j, k; + int i; + int k; double mult; if (run) @@ -155,8 +156,8 @@ void WCPAGC::execute() { for (i = 0; i < io_buffsize; i++) { - out[2 * i + 0] = fixed_gain * in[2 * i + 0]; - out[2 * i + 1] = fixed_gain * in[2 * i + 1]; + out[2 * i + 0] = (float) (fixed_gain * in[2 * i + 0]); + out[2 * i + 1] = (float) (fixed_gain * in[2 * i + 1]); } return; @@ -173,8 +174,10 @@ void WCPAGC::execute() out_sample[0] = ring[2 * out_index + 0]; out_sample[1] = ring[2 * out_index + 1]; abs_out_sample = abs_ring[out_index]; - double xr = ring[2 * in_index + 0] = in[2 * i + 0]; - double xi = ring[2 * in_index + 1] = in[2 * i + 1]; + ring[2 * in_index + 0] = in[2 * i + 0]; + ring[2 * in_index + 1] = in[2 * i + 1]; + double xr = ring[2 * in_index + 0]; + double xi = ring[2 * in_index + 1]; if (pmode == 0) abs_ring[in_index] = std::max(fabs(xr), fabs(xi)); @@ -189,7 +192,7 @@ void WCPAGC::execute() ring_max = 0.0; k = out_index; - for (j = 0; j < attack_buffsize; j++) + for (int j = 0; j < attack_buffsize; j++) { if (++k == ring_buffsize) k = 0; @@ -323,6 +326,8 @@ void WCPAGC::execute() } break; } + default: + break; } if (volts < min_volts) @@ -330,8 +335,8 @@ void WCPAGC::execute() gain = volts * inv_out_target; mult = (out_target - slope_constant * std::min (0.0, log10(inv_max_input * volts))) / volts; - out[2 * i + 0] = out_sample[0] * mult; - out[2 * i + 1] = out_sample[1] * mult; + out[2 * i + 0] = (float) (out_sample[0] * mult); + out[2 * i + 1] = (float) (out_sample[1] * mult); } } else if (out != in) @@ -406,7 +411,7 @@ void WCPAGC::setMode(int _mode) void WCPAGC::setFixed(double _fixed_agc) { - fixed_gain = pow (10.0, (double) _fixed_agc / 20.0); + fixed_gain = pow (10.0, _fixed_agc / 20.0); loadWcpAGC(); } @@ -428,7 +433,7 @@ void WCPAGC::setHang(int _hang) loadWcpAGC(); } -void WCPAGC::getHangLevel(double *hangLevel) +void WCPAGC::getHangLevel(double *hangLevel) const //for line on bandscope { *hangLevel = 20.0 * log10(hang_level / 0.637); @@ -437,7 +442,8 @@ void WCPAGC::getHangLevel(double *hangLevel) void WCPAGC::setHangLevel(double _hangLevel) //for line on bandscope { - double convert, tmp; + double convert; + double tmp; if (max_input > min_volts) { @@ -451,7 +457,7 @@ void WCPAGC::setHangLevel(double _hangLevel) loadWcpAGC(); } -void WCPAGC::getHangThreshold(int *hangthreshold) +void WCPAGC::getHangThreshold(int *hangthreshold) const //for slider in setup { *hangthreshold = (int) (100.0 * hang_thresh); @@ -464,7 +470,7 @@ void WCPAGC::setHangThreshold(int _hangthreshold) loadWcpAGC(); } -void WCPAGC::getTop(double *max_agc) +void WCPAGC::getTop(double *max_agc) const //for AGC Max Gain in setup { *max_agc = 20 * log10 (max_gain); @@ -473,7 +479,7 @@ void WCPAGC::getTop(double *max_agc) void WCPAGC::setTop(double _max_agc) //for AGC Max Gain in setup { - max_gain = pow (10.0, (double) _max_agc / 20.0); + max_gain = pow (10.0, _max_agc / 20.0); loadWcpAGC(); } @@ -489,9 +495,9 @@ void WCPAGC::setMaxInputLevel(double _level) loadWcpAGC(); } -void WCPAGC::setRun(int state) +void WCPAGC::setRun(int _state) { - run = state; + run = _state; } } // namespace WDSP diff --git a/wdsp/wcpAGC.hpp b/wdsp/wcpAGC.hpp index a52e9620d..c643062b5 100644 --- a/wdsp/wcpAGC.hpp +++ b/wdsp/wcpAGC.hpp @@ -145,11 +145,11 @@ public: void setAttack(int attack); void setDecay(int decay); void setHang(int hang); - void getHangLevel(double *hangLevel); + void getHangLevel(double *hangLevel) const; void setHangLevel(double hangLevel); - void getHangThreshold(int *hangthreshold); + void getHangThreshold(int *hangthreshold) const; void setHangThreshold(int hangthreshold); - void getTop(double *max_agc); + void getTop(double *max_agc) const; void setTop(double max_agc); void setSlope(int slope); void setMaxInputLevel(double level); From de756413e8740f9fde6fda9cd48ab7a381aabfd7 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 3 Aug 2024 13:54:42 +0200 Subject: [PATCH 33/46] WDSP: RXA and TXA rework --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 44 +- wdsp/CMakeLists.txt | 2 + wdsp/RXA.cpp | 1262 +++++++++++------------ wdsp/RXA.hpp | 94 +- wdsp/TXA.cpp | 1116 ++++++++++---------- wdsp/TXA.hpp | 55 +- wdsp/compress.cpp | 2 +- wdsp/osctrl.cpp | 2 +- wdsp/slew.cpp | 2 +- wdsp/unit.cpp | 148 +++ wdsp/unit.hpp | 19 + 11 files changed, 1377 insertions(+), 1369 deletions(-) create mode 100644 wdsp/unit.cpp diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 0114558dc..3ba83b5ff 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -125,14 +125,14 @@ WDSPRxSink::WDSPRxSink() : m_sPeak = 0.0; m_sCount = m_wdspBufSize; - m_rxa = WDSP::RXA::create_rxa( + m_rxa = new WDSP::RXA( m_wdspSampleRate, // input samplerate m_wdspSampleRate, // output samplerate m_wdspSampleRate, // sample rate for mainstream dsp processing (dsp) m_wdspBufSize // number complex samples processed per buffer in mainstream dsp processing ); m_rxa->setSpectrumProbe(&m_spectrumProbe); - WDSP::RXA::SetPassband(*m_rxa, 0, m_Bandwidth); + m_rxa->setPassband(0, m_Bandwidth); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); applySettings(m_settings, true); @@ -140,7 +140,7 @@ WDSPRxSink::WDSPRxSink() : WDSPRxSink::~WDSPRxSink() { - WDSP::RXA::destroy_rxa(m_rxa); + delete m_rxa; } void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) @@ -189,7 +189,7 @@ void WDSPRxSink::processOneSample(Complex &ci) if (++m_inCount == m_rxa->get_insize()) { - WDSP::RXA::xrxa(m_rxa); + m_rxa->execute(); m_sCount = m_wdspBufSize; m_sAvg = m_rxa->smeter->getMeter(WDSP::RXA::RXA_S_AV); @@ -306,7 +306,7 @@ void WDSPRxSink::applyAudioSampleRate(int sampleRate) m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_wdspSampleRate; - WDSP::RXA::setOutputSamplerate(m_rxa, sampleRate); + m_rxa->setOutputSamplerate(sampleRate); m_audioFifo.setSize(sampleRate); m_audioSampleRate = sampleRate; @@ -446,24 +446,24 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) m_interpolatorDistanceRemain = 0; m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; - WDSP::RXA::SetPassband(*m_rxa, fLow, fHigh); - WDSP::RXA::NBPSetWindow(*m_rxa, m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow); + m_rxa->setPassband(fLow, fHigh); + m_rxa->nbpSetWindow(m_settings.m_profiles[m_settings.m_profileIndex].m_fftWindow); if (settings.m_demod == WDSPRxProfile::DemodSSB) { if (dsb) { - WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_DSB); + m_rxa->setMode(WDSP::RXA::RXA_DSB); } else { - WDSP::RXA::SetMode(*m_rxa, usb ? WDSP::RXA::RXA_USB : WDSP::RXA::RXA_LSB); + m_rxa->setMode(usb ? WDSP::RXA::RXA_USB : WDSP::RXA::RXA_LSB); } } else if (settings.m_demod == WDSPRxProfile::DemodAM) { - WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_AM); + m_rxa->setMode(WDSP::RXA::RXA_AM); } else if (settings.m_demod == WDSPRxProfile::DemodSAM) { - WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_SAM); + m_rxa->setMode(WDSP::RXA::RXA_SAM); if (dsb) { m_rxa->amd->setSBMode(0); @@ -473,7 +473,7 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } else if (settings.m_demod == WDSPRxProfile::DemodFMN) { - WDSP::RXA::SetMode(*m_rxa, WDSP::RXA::RXA_FM); + m_rxa->setMode(WDSP::RXA::RXA_FM); } } @@ -486,18 +486,18 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) if ((m_settings.m_dnr != settings.m_dnr) || (m_settings.m_nrScheme != settings.m_nrScheme) || force) { - WDSP::RXA::SetANRRun(*m_rxa, 0); - WDSP::RXA::SetEMNRRun(*m_rxa, 0); + m_rxa->setANRRun(0); + m_rxa->setEMNRRun(0); if (settings.m_dnr) { switch (settings.m_nrScheme) { case WDSPRxProfile::NRSchemeNR: - WDSP::RXA::SetANRRun(*m_rxa, 1); + m_rxa->setANRRun(1); break; case WDSPRxProfile::NRSchemeNR2: - WDSP::RXA::SetEMNRRun(*m_rxa, 1); + m_rxa->setEMNRRun(1); break; default: break; @@ -510,12 +510,12 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) switch (settings.m_nrPosition) { case WDSPRxProfile::NRPositionPreAGC: - WDSP::RXA::SetANRPosition(*m_rxa, 0); - WDSP::RXA::SetEMNRPosition(*m_rxa, 0); + m_rxa->setANRPosition(0); + m_rxa->setEMNRPosition(0); break; case WDSPRxProfile::NRPositionPostAGC: - WDSP::RXA::SetANRPosition(*m_rxa, 1); - WDSP::RXA::SetEMNRPosition(*m_rxa, 1); + m_rxa->setANRPosition(1); + m_rxa->setEMNRPosition(1); break; default: break; @@ -560,12 +560,12 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) } if ((m_settings.m_anf != settings.m_anf) || force) { - WDSP::RXA::SetANFRun(*m_rxa, settings.m_anf ? 1 : 0); + m_rxa->setANFRun(settings.m_anf ? 1 : 0); } // Caution: Causes corruption if ((m_settings.m_snb != settings.m_snb) || force) { - WDSP::RXA::SetSNBARun(*m_rxa, settings.m_snb ? 1 : 0); + m_rxa->setSNBARun(settings.m_snb ? 1 : 0); } // CW Peaking diff --git a/wdsp/CMakeLists.txt b/wdsp/CMakeLists.txt index fea3b3a1c..59f76a89b 100644 --- a/wdsp/CMakeLists.txt +++ b/wdsp/CMakeLists.txt @@ -62,6 +62,7 @@ set(wdsp_SOURCES sphp.cpp ssql.cpp TXA.cpp + unit.cpp varsamp.cpp wcpAGC.cpp ) @@ -129,6 +130,7 @@ set(wdsp_HEADERS sphp.hpp ssql.hpp TXA.hpp + unit.hpp varsamp.hpp wcpAGC.hpp ) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 8053b8f98..940911ea3 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -58,43 +58,28 @@ warren@wpratt.com namespace WDSP { -RXA* RXA::create_rxa ( - int in_rate, // input samplerate - int out_rate, // output samplerate - int dsp_rate, // sample rate for mainstream dsp processing - int dsp_size // number complex samples processed per buffer in mainstream dsp processing +RXA::RXA( + int _in_rate, // input samplerate + int _out_rate, // output samplerate + int _dsp_rate, // sample rate for mainstream dsp processing + int _dsp_size // number complex samples processed per buffer in mainstream dsp processing +) : Unit( + _in_rate, + _out_rate, + _dsp_rate, + _dsp_size ) { - RXA* rxa = new RXA; - - rxa->in_rate = in_rate; - rxa->out_rate = out_rate; - rxa->dsp_rate = dsp_rate; - rxa->dsp_size = dsp_size; - - if (in_rate >= dsp_rate) - rxa->dsp_insize = dsp_size * (in_rate / dsp_rate); - else - rxa->dsp_insize = dsp_size / (dsp_rate / in_rate); - - if (out_rate >= dsp_rate) - rxa->dsp_outsize = dsp_size * (out_rate / dsp_rate); - else - rxa->dsp_outsize = dsp_size / (dsp_rate / out_rate); - - rxa->mode = RXA_LSB; - rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *) malloc0 (1 * ch.dsp_insize * sizeof (complex)); - rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *) malloc0 (1 * ch.dsp_outsize * sizeof (complex)); - rxa->midbuff = new float[2 * rxa->dsp_size * 2]; // (float *) malloc0 (2 * ch.dsp_size * sizeof (complex)); - std::fill(rxa->meter, rxa->meter + RXA_METERTYPE_LAST, 0); + mode = RXA::RXA_LSB; + std::fill(meter, meter + RXA::RXA_METERTYPE_LAST, 0); // Noise blanker (ANB or "NB") - rxa->anb = new ANB( + anb = new ANB( 0, // run - rxa->dsp_insize, // input buffer size - rxa->inbuff, // pointer to input buffer - rxa->inbuff, // pointer to output buffer - rxa->in_rate, // samplerate + dsp_insize, // input buffer size + inbuff, // pointer to input buffer + inbuff, // pointer to output buffer + in_rate, // samplerate 0.0001, // tau 0.0001, // hang time 0.0001, // advance time @@ -102,12 +87,12 @@ RXA* RXA::create_rxa ( 30 // thershold ); // Noise blanker (NOB or "NB2") - rxa->nob = new NOB( + nob = new NOB( 0, // run - rxa->dsp_insize, // input buffer size - rxa->inbuff, // pointer to input buffer - rxa->inbuff, // pointer to output buffer - rxa->in_rate, // samplerate + dsp_insize, // input buffer size + inbuff, // pointer to input buffer + inbuff, // pointer to output buffer + in_rate, // samplerate 0, // mode (zero) 0.0001, // advance slew time 0.0001, // advance time @@ -119,36 +104,36 @@ RXA* RXA::create_rxa ( ); // Ftequency shifter - shift to select a slice of spectrum - rxa->shift = new SHIFT( + shift = new SHIFT( 0, // run - rxa->dsp_insize, // input buffer size - rxa->inbuff, // pointer to input buffer - rxa->inbuff, // pointer to output buffer - rxa->in_rate, // samplerate + dsp_insize, // input buffer size + inbuff, // pointer to input buffer + inbuff, // pointer to output buffer + in_rate, // samplerate 0.0); // amount to shift (Hz) // Input resampler - resample to dsp rate for main processing - rxa->rsmpin = new RESAMPLE( + rsmpin = new RESAMPLE( 0, // run - will be turned ON below if needed - rxa->dsp_insize, // input buffer size - rxa->inbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->in_rate, // input samplerate - rxa->dsp_rate, // output samplerate + dsp_insize, // input buffer size + inbuff, // pointer to input buffer + midbuff, // pointer to output buffer + in_rate, // input samplerate + dsp_rate, // output samplerate 0.0, // select cutoff automatically 0, // select ncoef automatically 1.0); // gain // Input meter - ADC - rxa->adcmeter = new METER( + adcmeter = new METER( 0, // run 0, // optional pointer to another 'run' - rxa->dsp_size, // size - rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - rxa->meter, // result vector + meter, // result vector RXA_ADC_AV, // index for average value RXA_ADC_PK, // index for peak value -1, // index for gain value - disabled @@ -157,40 +142,40 @@ RXA* RXA::create_rxa ( // Notched bandpass section // notch database - rxa->ndb = new NOTCHDB ( + ndb = new NOTCHDB ( 0, // master run for all nbp's 1024); // max number of notches // notched bandpass - rxa->nbp0 = new NBP ( + nbp0 = new NBP ( 1, // run, always runs 0, // run the notches 0, // position - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of coefficients + dsp_size, // buffer size + std::max(2048, dsp_size), // number of coefficients 0, // minimum phase flag - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer -4150.0, // lower filter frequency -150.0, // upper filter frequency - rxa->dsp_rate, // sample rate + dsp_rate, // sample rate 0, // wintype 1.0, // gain 1, // auto-increase notch width 1025, // max number of passbands - rxa->ndb); // addr of database pointer + ndb); // addr of database pointer // bandpass for snba - rxa->bpsnba = new BPSNBA ( + bpsnba = new BPSNBA ( 0, // bpsnba run flag 0, // run the notches 0, // position - rxa->dsp_size, // size - std::max(2048, rxa->dsp_size), // number of filter coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of filter coefficients 0, // minimum phase flag - rxa->midbuff, // input buffer - rxa->midbuff, // output buffer - rxa->dsp_rate, // samplerate + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // samplerate + 250.0, // abs value of cutoff nearest zero + 5700.0, // abs value of cutoff farthest zero - 5700.0, // current low frequency @@ -199,42 +184,42 @@ RXA* RXA::create_rxa ( 1.0, // gain 1, // auto-increase notch width 1025, // max number of passbands - rxa->ndb); // addr of database pointer + ndb); // addr of database pointer // Post filter display send - send spectrum display (after S-meter in the block diagram) - rxa->sender = new SENDER ( + sender = new SENDER ( 0, // run 0, // flag 0, // mode - rxa->dsp_size, // size - rxa->midbuff // pointer to input buffer + dsp_size, // size + midbuff // pointer to input buffer ); // End notched bandpass section // S-meter - rxa->smeter = new METER( + smeter = new METER( 1, // run 0, // optional pointer to another 'run' - rxa->dsp_size, // size - rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - rxa->meter, // result vector + meter, // result vector RXA_S_AV, // index for average value RXA_S_PK, // index for peak value -1, // index for gain value - disabled 0); // pointer for gain computation // AM squelch capture (for other modes than FM) - rxa->amsq = new AMSQ( + amsq = new AMSQ( 0, // run - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to signal input buffer used by xamsq - rxa->midbuff, // pointer to signal output buffer used by xamsq - rxa->midbuff, // pointer to trigger buffer that xamsqcap will capture - rxa->dsp_rate, // sample rate + dsp_size, // buffer size + midbuff, // pointer to signal input buffer used by xamsq + midbuff, // pointer to signal output buffer used by xamsq + midbuff, // pointer to trigger buffer that xamsqcap will capture + dsp_rate, // sample rate 0.010, // time constant for averaging signal level 0.070, // signal up transition time 0.070, // signal down transition time @@ -245,15 +230,15 @@ RXA* RXA::create_rxa ( 0.0); // muted gain // AM/SAM demodulator - rxa->amd = new AMD( + amd = new AMD( 0, // run - OFF by default - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + dsp_size, // buffer size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 0, // mode: 0->AM, 1->SAM 1, // levelfade: 0->OFF, 1->ON 0, // sideband mode: 0->OFF - rxa->dsp_rate, // sample rate + dsp_rate, // sample rate -2000.0, // minimum lock frequency +2000.0, // maximum lock frequency 1.0, // zeta @@ -262,12 +247,12 @@ RXA* RXA::create_rxa ( 1.4); // tauI // FM demodulator - rxa->fmd = new FMD( + fmd = new FMD( 0, // run - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate + dsp_size, // buffer size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + dsp_rate, // sample rate 5000.0, // deviation 300.0, // f_low 3000.0, // f_high @@ -279,21 +264,21 @@ RXA* RXA::create_rxa ( 0.5, // audio gain 1, // run tone filter 254.1, // ctcss frequency - std::max(2048, rxa->dsp_size), // # coefs for de-emphasis filter + std::max(2048, dsp_size), // # coefs for de-emphasis filter 0, // min phase flag for de-emphasis filter - std::max(2048, rxa->dsp_size), // # coefs for audio cutoff filter + std::max(2048, dsp_size), // # coefs for audio cutoff filter 0); // min phase flag for audio cutoff filter // FM squelch apply - rxa->fmsq = new FMSQ( + fmsq = new FMSQ( 0, // run - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input signal buffer - rxa->midbuff, // pointer to output signal buffer - rxa->fmd->audio.data(), // pointer to trigger buffer - rxa->dsp_rate, // sample rate + dsp_size, // buffer size + midbuff, // pointer to input signal buffer + midbuff, // pointer to output signal buffer + fmd->audio.data(), // pointer to trigger buffer + dsp_rate, // sample rate 5000.0, // cutoff freq for noise filter (Hz) - &rxa->fmd->pllpole, // pointer to pole frequency of the fmd pll (Hz) + &fmd->pllpole, // pointer to pole frequency of the fmd pll (Hz) 0.100, // delay time after channel flush 0.001, // tau for noise averaging 0.100, // tau for long noise averaging @@ -303,17 +288,17 @@ RXA* RXA::create_rxa ( 0.562, // noise level to initiate unmute 0.000, // minimum tail time 0.100, // maximum tail time - std::max(2048, rxa->dsp_size), // number of coefficients for noise filter + std::max(2048, dsp_size), // number of coefficients for noise filter 0); // minimum phase flag // Spectral noise blanker (SNB) - rxa->snba = new SNBA( + snba = new SNBA( 0, // run - rxa->midbuff, // input buffer - rxa->midbuff, // output buffer - rxa->dsp_rate, // input / output sample rate + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // input / output sample rate 12000, // internal processing sample rate - rxa->dsp_size, // buffer size + dsp_size, // buffer size 4, // overlap factor to use 256, // frame size to use; sized for 12K rate 64, // asize @@ -329,31 +314,30 @@ RXA* RXA::create_rxa ( // Equalizer { - float default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; - //float default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; - float default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - rxa->eqp = new EQP( + std::array default_F = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; + std::array default_G = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + eqp = new EQP( 0, // run - OFF by default - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of filter coefficients + dsp_size, // buffer size + std::max(2048, dsp_size), // number of filter coefficients 0, // minimum phase flag - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 10, // number of frequencies - default_F, // frequency vector - default_G, // gain vector + default_F.data(), // frequency vector + default_G.data(), // gain vector 0, // cutoff mode 0, // wintype - rxa->dsp_rate); // sample rate + dsp_rate); // sample rate } // Auto notch filter - rxa->anf = new ANF( + anf = new ANF( 0, // run - OFF by default 0, // position - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + dsp_size, // buffer size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer ANF::ANF_DLINE_SIZE, // dline_size 64, // taps 16, // delay @@ -368,12 +352,12 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // LMS noise reduction (ANR or "NR") - rxa->anr = new ANR( + anr = new ANR( 0, // run - OFF by default 0, // position - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + dsp_size, // buffer size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer ANR::ANR_DLINE_SIZE, // dline_size 64, // taps 16, // delay @@ -388,15 +372,15 @@ RXA* RXA::create_rxa ( 3.0); // ldecr // Spectral noise reduyction (EMNR or "NR2") - rxa->emnr = new EMNR( + emnr = new EMNR( 0, // run 0, // position - rxa->dsp_size, // buffer size - rxa->midbuff, // input buffer - rxa->midbuff, // output buffer + dsp_size, // buffer size + midbuff, // input buffer + midbuff, // output buffer 4096, // FFT size 4, // overlap - rxa->dsp_rate, // samplerate + dsp_rate, // samplerate 0, // window type 1.0, // gain 2, // gain method @@ -404,14 +388,14 @@ RXA* RXA::create_rxa ( 1); // ae_run // AGC - rxa->agc = new WCPAGC( + agc = new WCPAGC( 1, // run 3, // mode 1, // peakmode = envelope - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->dsp_size, // buffer size - rxa->dsp_rate, // sample rate + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + dsp_size, // buffer size + dsp_rate, // sample rate 0.001, // tau_attack 0.250, // tau_decay 4, // n_tau @@ -430,64 +414,64 @@ RXA* RXA::create_rxa ( 0.100); // tau_hang_decay // AGC meter - rxa->agcmeter = new METER( + agcmeter = new METER( 0, // run 0, // optional pointer to another 'run' - rxa->dsp_size, // size - rxa->midbuff, // pointer to buffer - rxa->dsp_rate, // samplerate + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - rxa->meter, // result vector + meter, // result vector RXA_AGC_AV, // index for average value RXA_AGC_PK, // index for peak value RXA_AGC_GAIN, // index for gain value - &rxa->agc->gain); // pointer for gain computation + &agc->gain); // pointer for gain computation // Bandpass filter - After spectral noise reduction in the block diagram - rxa->bp1 = new BANDPASS ( + bp1 = new BANDPASS ( 1, // run - used only with ( AM || ANF || ANR || EMNR) 0, // position - rxa->dsp_size, // buffer size - std::max(2048, rxa->dsp_size), // number of coefficients + dsp_size, // buffer size + std::max(2048, dsp_size), // number of coefficients 0, // flag for minimum phase - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer -4150.0, // lower filter frequency -150.0, // upper filter frequency - rxa->dsp_rate, // sample rate + dsp_rate, // sample rate 1, // wintype 1.0); // gain // Scope/phase display send - pull phase & scope display data - rxa->sip1 = new SIPHON( + sip1 = new SIPHON( 0, // run - needed only for phase display 0, // position 0, // mode 0, // disp - rxa->dsp_size, // size of input buffer - rxa->midbuff, // input buffer + dsp_size, // size of input buffer + midbuff, // input buffer 4096, // number of samples to store 4096, // fft size for spectrum 0); // specmode // AM carrier block - rxa->cbl = new CBL( + cbl = new CBL( 0, // run - needed only if set to ON - rxa->dsp_size, // buffer size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + dsp_size, // buffer size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 0, // mode - rxa->dsp_rate, // sample rate + dsp_rate, // sample rate 0.02); // tau // CW peaking filter - rxa->speak = new SPEAK( + speak = new SPEAK( 0, // run - rxa->dsp_size, // buffer size, - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate + dsp_size, // buffer size, + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + dsp_rate, // sample rate 600.0, // center frequency 100.0, // bandwidth 2.0, // gain @@ -496,31 +480,31 @@ RXA* RXA::create_rxa ( // Dolly filter (multiple peak filter) - default is 2 for RTTY { - int def_enable[2] = {1, 1}; - double def_freq[2] = {2125.0, 2295.0}; - double def_bw[2] = {75.0, 75.0}; - double def_gain[2] = {1.0, 1.0}; - rxa->mpeak = new MPEAK( + std::array def_enable = { 1, 1}; + std::array def_freq = {2125.0, 2295.0}; + std::array def_bw = { 75.0, 75.0}; + std::array def_gain = { 1.0, 1.0}; + mpeak = new MPEAK( 0, // run - rxa->dsp_size, // size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + dsp_rate, // sample rate 2, // number of peaking filters - def_enable, // enable vector - def_freq, // frequency vector - def_bw, // bandwidth vector - def_gain, // gain vector + def_enable.data(), // enable vector + def_freq.data(), // frequency vector + def_bw.data(), // bandwidth vector + def_gain.data(), // gain vector 4 ); // number of stages } // Syllabic squelch (Voice suelch) - Not in the block diagram - rxa->ssql = new SSQL( + ssql = new SSQL( 0, // run - rxa->dsp_size, // size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer - rxa->dsp_rate, // sample rate + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + dsp_rate, // sample rate 0.070, // signal up transition time 0.070, // signal down transition time 0.0, // muted gain @@ -532,11 +516,11 @@ RXA* RXA::create_rxa ( 2000.0); // max freq for f_to_v converter // PatchPanel - rxa->panel = new PANEL( + panel = new PANEL( 1, // run - rxa->dsp_size, // size - rxa->midbuff, // pointer to input buffer - rxa->midbuff, // pointer to output buffer + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 4.0, // gain1 1.0, // gain2I 1.0, // gain2Q @@ -546,325 +530,270 @@ RXA* RXA::create_rxa ( // AM squelch apply - absent but in the block diagram // Output resampler - rxa->rsmpout = new RESAMPLE( + rsmpout = new RESAMPLE( 0, // run - will be turned ON below if needed - rxa->dsp_size, // input buffer size - rxa->midbuff, // pointer to input buffer - rxa->outbuff, // pointer to output buffer - rxa->dsp_rate, // input sample rate - rxa->out_rate, // output sample rate + dsp_size, // input buffer size + midbuff, // pointer to input buffer + outbuff, // pointer to output buffer + dsp_rate, // input sample rate + out_rate, // output sample rate 0.0, // select cutoff automatically 0, // select ncoef automatically 1.0); // gain // turn OFF / ON resamplers as needed - ResCheck (*rxa); - return rxa; + resCheck(); } -void RXA::destroy_rxa (RXA *rxa) +RXA::~RXA() { - delete (rxa->rsmpout); - delete (rxa->panel); - delete (rxa->ssql); - delete (rxa->mpeak); - delete (rxa->speak); - delete (rxa->cbl); - delete (rxa->sip1); - delete (rxa->bp1); - delete (rxa->agcmeter); - delete (rxa->agc); - delete (rxa->emnr); - delete (rxa->anr); - delete (rxa->anf); - delete (rxa->eqp); - delete (rxa->snba); - delete (rxa->fmsq); - delete (rxa->fmd); - delete (rxa->amd); - delete (rxa->amsq); - delete (rxa->smeter); - delete (rxa->sender); - delete (rxa->bpsnba); - delete (rxa->nbp0); - delete (rxa->ndb); - delete (rxa->adcmeter); - delete (rxa->rsmpin); - delete (rxa->shift); - delete (rxa->nob); - delete (rxa->anb); - delete[] (rxa->midbuff); - delete[] (rxa->outbuff); - delete[] (rxa->inbuff); - delete rxa; + delete rsmpout; + delete panel; + delete ssql; + delete mpeak; + delete speak; + delete cbl; + delete sip1; + delete bp1; + delete agcmeter; + delete agc; + delete emnr; + delete anr; + delete anf; + delete eqp; + delete snba; + delete fmsq; + delete fmd; + delete amd; + delete amsq; + delete smeter; + delete sender; + delete bpsnba; + delete nbp0; + delete ndb; + delete adcmeter; + delete rsmpin; + delete shift; + delete nob; + delete anb; } -void RXA::flush_rxa (RXA *rxa) +void RXA::flush() { - std::fill(rxa->inbuff, rxa->inbuff + 1 * rxa->dsp_insize * 2, 0); - std::fill(rxa->outbuff, rxa->outbuff + 1 * rxa->dsp_outsize * 2, 0); - std::fill(rxa->midbuff, rxa->midbuff + 2 * rxa->dsp_size * 2, 0); - rxa->anb->flush(); - rxa->nob->flush(); - rxa->shift->flush(); - rxa->rsmpin->flush(); - rxa->adcmeter->flush(); - rxa->nbp0->flush(); - rxa->bpsnba->flush(); - rxa->sender->flush(); - rxa->smeter->flush(); - rxa->amsq->flush(); - rxa->amd->flush(); - rxa->fmd->flush(); - rxa->fmsq->flush(); - rxa->snba->flush(); - rxa->eqp->flush(); - rxa->anf->flush(); - rxa->anr->flush(); - rxa->emnr->flush(); - rxa->agc->flush(); - rxa->agcmeter->flush(); - rxa->bp1->flush(); - rxa->sip1->flush(); - rxa->cbl->flush(); - rxa->speak->flush(); - rxa->mpeak->flush(); - rxa->ssql->flush(); - rxa->panel->flush(); - rxa->rsmpout->flush(); + Unit::flushBuffers(); + anb->flush(); + nob->flush(); + shift->flush(); + rsmpin->flush(); + adcmeter->flush(); + nbp0->flush(); + bpsnba->flush(); + sender->flush(); + smeter->flush(); + amsq->flush(); + amd->flush(); + fmd->flush(); + fmsq->flush(); + snba->flush(); + eqp->flush(); + anf->flush(); + anr->flush(); + emnr->flush(); + agc->flush(); + agcmeter->flush(); + bp1->flush(); + sip1->flush(); + cbl->flush(); + speak->flush(); + mpeak->flush(); + ssql->flush(); + panel->flush(); + rsmpout->flush(); } -void RXA::xrxa (RXA *rxa) +void RXA::execute() { - rxa->anb->execute(); - rxa->nob->execute(); - rxa->shift->execute(); - rxa->rsmpin->execute(); - rxa->adcmeter->execute(); - rxa->bpsnba->exec_in(0); - rxa->nbp0->execute(0); - rxa->smeter->execute(); - rxa->sender->execute(); - rxa->amsq->xcap(); - rxa->bpsnba->exec_out(0); - rxa->amd->execute(); - rxa->fmd->execute(); - rxa->fmsq->execute(); - rxa->bpsnba->exec_in(1); - rxa->bpsnba->exec_out(1); - rxa->snba->execute(); - rxa->eqp->execute(); - rxa->anf->execute(0); - rxa->anr->ANR::execute(0); - rxa->emnr->execute(0); - rxa->bp1->BANDPASS::execute(0); - rxa->agc->execute(); - rxa->anf->execute(1); - rxa->anr->execute(1); - rxa->emnr->execute(1); - rxa->bp1->execute(1); - rxa->agcmeter->execute(); - rxa->sip1->execute(0); - rxa->cbl->execute(); - rxa->speak->execute(); - rxa->mpeak->execute(); - rxa->ssql->execute(); - rxa->panel->execute(); - rxa->amsq->execute(); - rxa->rsmpout->execute(); + anb->execute(); + nob->execute(); + shift->execute(); + rsmpin->execute(); + adcmeter->execute(); + bpsnba->exec_in(0); + nbp0->execute(0); + smeter->execute(); + sender->execute(); + amsq->xcap(); + bpsnba->exec_out(0); + amd->execute(); + fmd->execute(); + fmsq->execute(); + bpsnba->exec_in(1); + bpsnba->exec_out(1); + snba->execute(); + eqp->execute(); + anf->execute(0); + anr->ANR::execute(0); + emnr->execute(0); + bp1->BANDPASS::execute(0); + agc->execute(); + anf->execute(1); + anr->execute(1); + emnr->execute(1); + bp1->execute(1); + agcmeter->execute(); + sip1->execute(0); + cbl->execute(); + speak->execute(); + mpeak->execute(); + ssql->execute(); + panel->execute(); + amsq->execute(); + rsmpout->execute(); } -void RXA::setInputSamplerate (RXA *rxa, int in_rate) +void RXA::setInputSamplerate(int _in_rate) { - if (in_rate >= rxa->dsp_rate) - rxa->dsp_insize = rxa->dsp_size * (in_rate / rxa->dsp_rate); - else - rxa->dsp_insize = rxa->dsp_size / (rxa->dsp_rate / in_rate); - - rxa->in_rate = in_rate; - // buffers - delete[] (rxa->inbuff); - rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *)malloc0(1 * ch.dsp_insize * sizeof(complex)); + Unit::setBuffersInputSamplerate(_in_rate); // anb - rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->anb->setSize(rxa->dsp_insize); - rxa->anb->setSamplerate(rxa->in_rate); + anb->setBuffers(inbuff, inbuff); + anb->setSize(dsp_insize); + anb->setSamplerate(in_rate); // nob - rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->nob->setSize(rxa->dsp_insize); - rxa->nob->setSamplerate(rxa->in_rate); + nob->setBuffers(inbuff, inbuff); + nob->setSize(dsp_insize); + nob->setSamplerate(in_rate); // shift - rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->shift->setSize(rxa->dsp_insize); - rxa->shift->setSamplerate(rxa->in_rate); + shift->setBuffers(inbuff, inbuff); + shift->setSize(dsp_insize); + shift->setSamplerate(in_rate); // input resampler - rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); - rxa->rsmpin->setSize(rxa->dsp_insize); - rxa->rsmpin->setInRate(rxa->in_rate); - ResCheck (*rxa); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); + rsmpin->setInRate(in_rate); + resCheck(); } -void RXA::setOutputSamplerate (RXA *rxa, int out_rate) +void RXA::setOutputSamplerate(int _out_rate) { - if (out_rate >= rxa->dsp_rate) - rxa->dsp_outsize = rxa->dsp_size * (out_rate / rxa->dsp_rate); - else - rxa->dsp_outsize = rxa->dsp_size / (rxa->dsp_rate / out_rate); - - rxa->out_rate = out_rate; - // buffers - delete[] (rxa->outbuff); - rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * ch.dsp_outsize * sizeof(complex)); + Unit::setBuffersOutputSamplerate(_out_rate); // output resampler - rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); - rxa->rsmpout->setOutRate(rxa->out_rate); - ResCheck (*rxa); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setOutRate(out_rate); + resCheck(); } -void RXA::setDSPSamplerate (RXA *rxa, int dsp_rate) +void RXA::setDSPSamplerate(int _dsp_rate) { - if (rxa->in_rate >= dsp_rate) - rxa->dsp_insize = rxa->dsp_size * (rxa->in_rate / dsp_rate); - else - rxa->dsp_insize = rxa->dsp_size / (dsp_rate / rxa->in_rate); - - if (rxa->out_rate >= dsp_rate) - rxa->dsp_outsize = rxa->dsp_size * (rxa->out_rate / dsp_rate); - else - rxa->dsp_outsize = rxa->dsp_size / (dsp_rate / rxa->out_rate); - - rxa->dsp_rate = dsp_rate; - // buffers - delete[] (rxa->inbuff); - rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *)malloc0(1 * rxa->dsp_insize * sizeof(complex)); - delete[] (rxa->outbuff); - rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); + Unit::setBuffersDSPSamplerate(_dsp_rate); // anb - rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->anb->setSize(rxa->dsp_insize); + anb->setBuffers(inbuff, inbuff); + anb->setSize(dsp_insize); // nob - rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->nob->setSize(rxa->dsp_insize); + nob->setBuffers(inbuff, inbuff); + nob->setSize(dsp_insize); // shift - rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->shift->setSize(rxa->dsp_insize); + shift->setBuffers(inbuff, inbuff); + shift->setSize(dsp_insize); // input resampler - rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); - rxa->rsmpin->setSize(rxa->dsp_insize); - rxa->rsmpin->setOutRate(rxa->dsp_rate); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); + rsmpin->setOutRate(dsp_rate); // dsp_rate blocks - rxa->adcmeter->setSamplerate(rxa->dsp_rate); - rxa->nbp0->setSamplerate(rxa->dsp_rate); - rxa->bpsnba->setSamplerate(rxa->dsp_rate); - rxa->smeter->setSamplerate(rxa->dsp_rate); - rxa->sender->setSamplerate(rxa->dsp_rate); - rxa->amsq->setSamplerate(rxa->dsp_rate); - rxa->amd->setSamplerate(rxa->dsp_rate); - rxa->fmd->setSamplerate(rxa->dsp_rate); - rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio.data()); - rxa->fmsq->setSamplerate(rxa->dsp_rate); - // rxa->snba->setSamplerate(rxa->dsp_rate); SMBA removed - rxa->eqp->setSamplerate(rxa->dsp_rate); - rxa->anf->setSamplerate(rxa->dsp_rate); - rxa->anr->setSamplerate(rxa->dsp_rate); - rxa->emnr->setSamplerate(rxa->dsp_rate); - rxa->bp1->setSamplerate(rxa->dsp_rate); - rxa->agc->setSamplerate(rxa->dsp_rate); - rxa->agcmeter->setSamplerate(rxa->dsp_rate); - rxa->sip1->setSamplerate(rxa->dsp_rate); - rxa->cbl->setSamplerate(rxa->dsp_rate); - rxa->speak->setSamplerate(rxa->dsp_rate); - rxa->mpeak->setSamplerate(rxa->dsp_rate); - rxa->ssql->setSamplerate(rxa->dsp_rate); - rxa->panel->setSamplerate(rxa->dsp_rate); + adcmeter->setSamplerate(dsp_rate); + nbp0->setSamplerate(dsp_rate); + bpsnba->setSamplerate(dsp_rate); + smeter->setSamplerate(dsp_rate); + sender->setSamplerate(dsp_rate); + amsq->setSamplerate(dsp_rate); + amd->setSamplerate(dsp_rate); + fmd->setSamplerate(dsp_rate); + fmsq->setBuffers(midbuff, midbuff, fmd->audio.data()); + fmsq->setSamplerate(dsp_rate); + // snba->setSamplerate(dsp_rate); SMBA removed + eqp->setSamplerate(dsp_rate); + anf->setSamplerate(dsp_rate); + anr->setSamplerate(dsp_rate); + emnr->setSamplerate(dsp_rate); + bp1->setSamplerate(dsp_rate); + agc->setSamplerate(dsp_rate); + agcmeter->setSamplerate(dsp_rate); + sip1->setSamplerate(dsp_rate); + cbl->setSamplerate(dsp_rate); + speak->setSamplerate(dsp_rate); + mpeak->setSamplerate(dsp_rate); + ssql->setSamplerate(dsp_rate); + panel->setSamplerate(dsp_rate); // output resampler - rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); - rxa->rsmpout->setInRate(rxa->dsp_rate); - ResCheck (*rxa); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setInRate(dsp_rate); + resCheck(); } -void RXA::setDSPBuffsize (RXA *rxa, int dsp_size) +void RXA::setDSPBuffsize(int _dsp_size) { - if (rxa->in_rate >= rxa->dsp_rate) - rxa->dsp_insize = dsp_size * (rxa->in_rate / rxa->dsp_rate); - else - rxa->dsp_insize = dsp_size / (rxa->dsp_rate / rxa->in_rate); - - if (rxa->out_rate >= rxa->dsp_rate) - rxa->dsp_outsize = dsp_size * (rxa->out_rate / rxa->dsp_rate); - else - rxa->dsp_outsize = dsp_size / (rxa->dsp_rate / rxa->out_rate); - - rxa->dsp_size = dsp_size; - // buffers - delete[](rxa->inbuff); - rxa->inbuff = new float[1 * rxa->dsp_insize * 2]; // (float *)malloc0(1 * rxa->dsp_insize * sizeof(complex)); - delete[] (rxa->midbuff); - rxa->midbuff = new float[2 * rxa->dsp_size * 2]; // (float *)malloc0(2 * rxa->dsp_size * sizeof(complex)); - delete[] (rxa->outbuff); - rxa->outbuff = new float[1 * rxa->dsp_outsize * 2]; // (float *)malloc0(1 * rxa->dsp_outsize * sizeof(complex)); + Unit::setBuffersDSPBuffsize(_dsp_size); // anb - rxa->anb->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->anb->setSize(rxa->dsp_insize); + anb->setBuffers(inbuff, inbuff); + anb->setSize(dsp_insize); // nob - rxa->nob->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->nob->setSize(rxa->dsp_insize); + nob->setBuffers(inbuff, inbuff); + nob->setSize(dsp_insize); // shift - rxa->shift->setBuffers(rxa->inbuff, rxa->inbuff); - rxa->shift->setSize(rxa->dsp_insize); + shift->setBuffers(inbuff, inbuff); + shift->setSize(dsp_insize); // input resampler - rxa->rsmpin->setBuffers(rxa->inbuff, rxa->midbuff); - rxa->rsmpin->setSize(rxa->dsp_insize); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); // dsp_size blocks - rxa->adcmeter->setBuffers(rxa->midbuff); - rxa->adcmeter->setSize(rxa->dsp_size); - rxa->nbp0->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->nbp0->setSize(rxa->dsp_size); - rxa->bpsnba->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->bpsnba->setSize(rxa->dsp_size); - rxa->smeter->setBuffers(rxa->midbuff); - rxa->smeter->setSize(rxa->dsp_size); - rxa->sender->setBuffers(rxa->midbuff); - rxa->sender->setSize(rxa->dsp_size); - rxa->amsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->midbuff); - rxa->amsq->setSize(rxa->dsp_size); - rxa->amd->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->amd->setSize(rxa->dsp_size); - rxa->fmd->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->fmd->setSize(rxa->dsp_size); - rxa->fmsq->setBuffers(rxa->midbuff, rxa->midbuff, rxa->fmd->audio.data()); - rxa->fmsq->setSize(rxa->dsp_size); - rxa->snba->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->snba->setSize(rxa->dsp_size); - rxa->eqp->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->eqp->setSize(rxa->dsp_size); - rxa->anf->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->anf->setSize(rxa->dsp_size); - rxa->anr->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->anr->setSize(rxa->dsp_size); - rxa->emnr->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->emnr->setSize(rxa->dsp_size); - rxa->bp1->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->bp1->setSize(rxa->dsp_size); - rxa->agc->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->agc->setSize(rxa->dsp_size); - rxa->agcmeter->setBuffers(rxa->midbuff); - rxa->agcmeter->setSize(rxa->dsp_size); - rxa->sip1->setBuffers(rxa->midbuff); - rxa->sip1->setSize(rxa->dsp_size); - rxa->cbl->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->cbl->setSize(rxa->dsp_size); - rxa->speak->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->speak->setSize(rxa->dsp_size); - rxa->mpeak->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->mpeak->setSize(rxa->dsp_size); - rxa->ssql->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->ssql->setSize(rxa->dsp_size); - rxa->panel->setBuffers(rxa->midbuff, rxa->midbuff); - rxa->panel->setSize(rxa->dsp_size); + adcmeter->setBuffers(midbuff); + adcmeter->setSize(dsp_size); + nbp0->setBuffers(midbuff, midbuff); + nbp0->setSize(dsp_size); + bpsnba->setBuffers(midbuff, midbuff); + bpsnba->setSize(dsp_size); + smeter->setBuffers(midbuff); + smeter->setSize(dsp_size); + sender->setBuffers(midbuff); + sender->setSize(dsp_size); + amsq->setBuffers(midbuff, midbuff, midbuff); + amsq->setSize(dsp_size); + amd->setBuffers(midbuff, midbuff); + amd->setSize(dsp_size); + fmd->setBuffers(midbuff, midbuff); + fmd->setSize(dsp_size); + fmsq->setBuffers(midbuff, midbuff, fmd->audio.data()); + fmsq->setSize(dsp_size); + snba->setBuffers(midbuff, midbuff); + snba->setSize(dsp_size); + eqp->setBuffers(midbuff, midbuff); + eqp->setSize(dsp_size); + anf->setBuffers(midbuff, midbuff); + anf->setSize(dsp_size); + anr->setBuffers(midbuff, midbuff); + anr->setSize(dsp_size); + emnr->setBuffers(midbuff, midbuff); + emnr->setSize(dsp_size); + bp1->setBuffers(midbuff, midbuff); + bp1->setSize(dsp_size); + agc->setBuffers(midbuff, midbuff); + agc->setSize(dsp_size); + agcmeter->setBuffers(midbuff); + agcmeter->setSize(dsp_size); + sip1->setBuffers(midbuff); + sip1->setSize(dsp_size); + cbl->setBuffers(midbuff, midbuff); + cbl->setSize(dsp_size); + speak->setBuffers(midbuff, midbuff); + speak->setSize(dsp_size); + mpeak->setBuffers(midbuff, midbuff); + mpeak->setSize(dsp_size); + ssql->setBuffers(midbuff, midbuff); + ssql->setSize(dsp_size); + panel->setBuffers(midbuff, midbuff); + panel->setSize(dsp_size); // output resampler - rxa->rsmpout->setBuffers(rxa->midbuff, rxa->outbuff); - rxa->rsmpout->setSize(rxa->dsp_size); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setSize(dsp_size); } void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) @@ -879,66 +808,64 @@ void RXA::setSpectrumProbe(BufferProbe *spectrumProbe) * * ********************************************************************************************************/ -void RXA::SetMode (RXA& rxa, int mode) +void RXA::setMode(int _mode) { - if (rxa.mode != mode) + if (mode != _mode) { - int amd_run = (mode == RXA_AM) || (mode == RXA_SAM); - bpsnbaCheck (rxa, mode, rxa.ndb->master_run); + int amd_run = (_mode == RXA_AM) || (_mode == RXA_SAM); + bpsnbaCheck (_mode, ndb->master_run); bp1Check ( - rxa, amd_run, - rxa.snba->run, - rxa.emnr->run, - rxa.anf->run, - rxa.anr->run + snba->run, + emnr->run, + anf->run, + anr->run ); - rxa.mode = mode; - rxa.amd->run = 0; - rxa.fmd->run = 0; + mode = _mode; + amd->run = 0; + fmd->run = 0; - switch (mode) + switch (_mode) { case RXA_AM: - rxa.amd->run = 1; - rxa.amd->mode = 0; + amd->run = 1; + amd->mode = 0; break; case RXA_SAM: - rxa.amd->run = 1; - rxa.amd->mode = 1; + amd->run = 1; + amd->mode = 1; break; case RXA_DSB: break; case RXA_FM: - rxa.fmd->run = 1; + fmd->run = 1; break; default: break; } - bp1Set (rxa); - bpsnbaSet (rxa); // update variables + bp1Set(); + bpsnbaSet(); // update variables } } -void RXA::ResCheck (RXA& rxa) +void RXA::resCheck() { // turn OFF/ON resamplers depending upon whether they're needed - RESAMPLE *a = rxa.rsmpin; - if (rxa.in_rate != rxa.dsp_rate) + RESAMPLE *a = rsmpin; + if (in_rate != dsp_rate) a->run = 1; else a->run = 0; - a = rxa.rsmpout; - if (rxa.dsp_rate != rxa.out_rate) + a = rsmpout; + if (dsp_rate != out_rate) a->run = 1; else a->run = 0; } void RXA::bp1Check ( - RXA& rxa, int amd_run, int snba_run, int emnr_run, @@ -946,7 +873,7 @@ void RXA::bp1Check ( int anr_run ) { - BANDPASS *a = rxa.bp1; + BANDPASS *a = bp1; float gain; if (amd_run || snba_run || @@ -961,15 +888,15 @@ void RXA::bp1Check ( a->setGain(gain, 0); } -void RXA::bp1Set (RXA& rxa) +void RXA::bp1Set () { - BANDPASS *a = rxa.bp1; + BANDPASS *a = bp1; int old = a->run; - if ((rxa.amd->run == 1) || - (rxa.snba->run == 1) || - (rxa.emnr->run == 1) || - (rxa.anf->run == 1) || - (rxa.anr->run == 1) + if ((amd->run == 1) || + (snba->run == 1) || + (emnr->run == 1) || + (anf->run == 1) || + (anr->run == 1) ) a->run = 1; else @@ -979,36 +906,33 @@ void RXA::bp1Set (RXA& rxa) FIRCORE::setUpdate_fircore (a->fircore); } -void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) +void RXA::bpsnbaCheck(int _mode, int _notch_run) { // for BPSNBA: set run, position, freqs, run_notches // call this upon change in RXA_mode, snba_run, notch_master_run - BPSNBA *a = rxa.bpsnba; - float f_low = 0.0, f_high = 0.0; + BPSNBA *a = bpsnba; + double f_low = 0.0; + double f_high = 0.0; int run_notches = 0; - switch (mode) + switch (_mode) { case RXA_LSB: case RXA_CWL: case RXA_DIGL: f_low = -a->abs_high_freq; f_high = -a->abs_low_freq; - run_notches = notch_run; + run_notches = _notch_run; break; case RXA_USB: case RXA_CWU: case RXA_DIGU: f_low = +a->abs_low_freq; f_high = +a->abs_high_freq; - run_notches = notch_run; + run_notches = _notch_run; break; case RXA_AM: case RXA_SAM: case RXA_DSB: - f_low = +a->abs_low_freq; - f_high = +a->abs_high_freq; - run_notches = 0; - break; case RXA_FM: f_low = +a->abs_low_freq; f_high = +a->abs_high_freq; @@ -1016,7 +940,8 @@ void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) break; case RXA_DRM: case RXA_SPEC: - + break; + default: break; } // 'run' and 'position' are examined at run time; no filter changes required. @@ -1029,57 +954,53 @@ void RXA::bpsnbaCheck (RXA& rxa, int mode, int notch_run) a->f_high = f_high; a->run_notches = run_notches; // f_low, f_high, run_notches are needed for the filter recalculation - rxa.bpsnba->recalc_bpsnba_filter(0); + bpsnba->recalc_bpsnba_filter(0); } } -void RXA::bpsnbaSet (RXA& rxa) +void RXA::bpsnbaSet() { // for BPSNBA: set run, position, freqs, run_notches // call this upon change in RXA_mode, snba_run, notch_master_run - BPSNBA *a = rxa.bpsnba; - switch (rxa.mode) + BPSNBA *a = bpsnba; + switch (mode) { case RXA_LSB: case RXA_CWL: case RXA_DIGL: - a->run = rxa.snba->run; - a->position = 0; - break; case RXA_USB: case RXA_CWU: case RXA_DIGU: - a->run = rxa.snba->run; + a->run = snba->run; a->position = 0; break; - case RXA_AM: - case RXA_SAM: case RXA_DSB: - a->run = rxa.snba->run; - a->position = 1; - break; + case RXA_AM: case RXA_FM: - a->run = rxa.snba->run; + a->run = snba->run; a->position = 1; break; + case RXA_SAM: case RXA_DRM: case RXA_SPEC: a->run = 0; break; + default: + break; } FIRCORE::setUpdate_fircore (a->bpsnba->fircore); } -void RXA::UpdateNBPFiltersLightWeight (RXA& rxa) +void RXA::updateNBPFiltersLightWeight() { // called when setting tune freq or shift freq - rxa.nbp0->calc_lightweight(); - rxa.bpsnba->bpsnba->calc_lightweight(); + nbp0->calc_lightweight(); + bpsnba->bpsnba->calc_lightweight(); } -void RXA::UpdateNBPFilters(RXA& rxa) +void RXA::updateNBPFilters() { - NBP *a = rxa.nbp0; - BPSNBA *b = rxa.bpsnba; + NBP *a = nbp0; + BPSNBA *b = bpsnba; if (a->fnfrun) { a->calc_impulse(); @@ -1092,273 +1013,268 @@ void RXA::UpdateNBPFilters(RXA& rxa) } } -int RXA::NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) +int RXA::nbpAddNotch(int _notch, double _fcenter, double _fwidth, int _active) { - NOTCHDB *b = rxa.ndb; - int rval = b->addNotch(notch, fcenter, fwidth, active); + NOTCHDB *b = ndb; + int rval = b->addNotch(_notch, _fcenter, _fwidth, _active); if (rval == 0) { - RXA::UpdateNBPFilters (rxa); + updateNBPFilters(); } return rval; } -int RXA::NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active) +int RXA::nbpGetNotch(int _notch, double* _fcenter, double* _fwidth, int* _active) { - NOTCHDB *a = rxa.ndb; - int rval = a->getNotch(notch, fcenter, fwidth, active); + NOTCHDB *a = ndb; + int rval = a->getNotch(_notch, _fcenter, _fwidth, _active); return rval; } -int RXA::NBPDeleteNotch (RXA& rxa, int notch) +int RXA::nbpDeleteNotch(int _notch) { - NOTCHDB *a = rxa.ndb; - int rval = a->deleteNotch(notch); + NOTCHDB *a = ndb; + int rval = a->deleteNotch(_notch); if (rval == 0) { - RXA::UpdateNBPFilters (rxa); + updateNBPFilters(); } return rval; } -int RXA::NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active) +int RXA::nbpEditNotch(int _notch, double _fcenter, double _fwidth, int _active) { - NOTCHDB *a = rxa.ndb; - int rval = a->editNotch(notch, fcenter, fwidth, active); + NOTCHDB *a = ndb; + int rval = a->editNotch(_notch, _fcenter, _fwidth, _active); if (rval == 0) { - RXA::UpdateNBPFilters (rxa); + updateNBPFilters(); } return rval; } -void RXA::NBPGetNumNotches (RXA& rxa, int* nnotches) +void RXA::nbpGetNumNotches(int* _nnotches) { - NOTCHDB *a = rxa.ndb; - a->getNumNotches(nnotches); + const NOTCHDB *a = ndb; + a->getNumNotches(_nnotches); } -void RXA::NBPSetTuneFrequency (RXA& rxa, double tunefreq) +void RXA::nbpSetTuneFrequency(double _tunefreq) { NOTCHDB *a; - a = rxa.ndb; + a = ndb; - if (tunefreq != a->tunefreq) + if (_tunefreq != a->tunefreq) { - a->tunefreq = tunefreq; - RXA::UpdateNBPFiltersLightWeight (rxa); + a->tunefreq = _tunefreq; + updateNBPFiltersLightWeight(); } } -void RXA::NBPSetShiftFrequency (RXA& rxa, double shift) +void RXA::nbpSetShiftFrequency(double _shift) { NOTCHDB *a; - a = rxa.ndb; - if (shift != a->shift) + a = ndb; + if (_shift != a->shift) { - a->shift = shift; - RXA::UpdateNBPFiltersLightWeight (rxa); + a->shift = _shift; + updateNBPFiltersLightWeight(); } } -void RXA::NBPSetNotchesRun (RXA& rxa, int run) +void RXA::nbpSetNotchesRun(int _run) { - NOTCHDB *a = rxa.ndb; - NBP *b = rxa.nbp0; + NOTCHDB *a = ndb; + NBP *b = nbp0; - if ( run != a->master_run) + if ( _run != a->master_run) { - a->master_run = run; // update variables + a->master_run = _run; // update variables b->fnfrun = a->master_run; - RXA::bpsnbaCheck (rxa, rxa.mode, run); + bpsnbaCheck(mode, _run); b->calc_impulse(); // recalc nbp impulse response FIRCORE::setImpulse_fircore (b->fircore, b->impulse, 0); // calculate new filter masks delete[] (b->impulse); - RXA::bpsnbaSet (rxa); + bpsnbaSet(); FIRCORE::setUpdate_fircore (b->fircore); // apply new filter masks } } -void RXA::NBPSetWindow (RXA& rxa, int wintype) +void RXA::nbpSetWindow(int _wintype) { NBP *a; BPSNBA *b; - a = rxa.nbp0; - b = rxa.bpsnba; + a = nbp0; + b = bpsnba; - if ((a->wintype != wintype)) + if ((a->wintype != _wintype)) { - a->wintype = wintype; + a->wintype = _wintype; a->calc_impulse(); FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); delete[] (a->impulse); } - if ((b->wintype != wintype)) + if ((b->wintype != _wintype)) { - b->wintype = wintype; + b->wintype = _wintype; b->recalc_bpsnba_filter(1); } } -void RXA::NBPSetAutoIncrease (RXA& rxa, int autoincr) +void RXA::nbpSetAutoIncrease(int _autoincr) { NBP *a; BPSNBA *b; - a = rxa.nbp0; - b = rxa.bpsnba; + a = nbp0; + b = bpsnba; - if ((a->autoincr != autoincr)) + if ((a->autoincr != _autoincr)) { - a->autoincr = autoincr; + a->autoincr = _autoincr; a->calc_impulse(); FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); delete[] (a->impulse); } - if ((b->autoincr != autoincr)) + if ((b->autoincr != _autoincr)) { - b->autoincr = autoincr; + b->autoincr = _autoincr; b->recalc_bpsnba_filter(1); } } -void RXA::SetAMDRun(RXA& rxa, int run) +void RXA::setAMDRun(int _run) { - if (rxa.amd->run != run) + if (amd->run != _run) { - RXA::bp1Check ( - rxa, - run, - rxa.snba->run, - rxa.emnr->run, - rxa.anf->run, - rxa.anr->run + bp1Check ( + _run, + snba->run, + emnr->run, + anf->run, + anr->run ); - rxa.amd->run = run; - RXA::bp1Set (rxa); + amd->run = _run; + bp1Set(); } } -void RXA::SetSNBARun (RXA& rxa, int run) +void RXA::setSNBARun(int _run) { - SNBA *a = rxa.snba; + SNBA *a = snba; - if (a->run != run) + if (a->run != _run) { - RXA::bpsnbaCheck (rxa, rxa.mode, rxa.ndb->master_run); - RXA::bp1Check ( - rxa, - rxa.amd->run, - run, - rxa.emnr->run, - rxa.anf->run, - rxa.anr->run + bpsnbaCheck(mode, ndb->master_run); + bp1Check( + amd->run, + _run, + emnr->run, + anf->run, + anr->run ); - a->run = run; - RXA::bp1Set (rxa); - RXA::bpsnbaSet (rxa); + a->run = _run; + bp1Set(); + bpsnbaSet(); } } -void RXA::SetANFRun (RXA& rxa, int run) +void RXA::setANFRun(int _run) { - ANF *a = rxa.anf; + ANF *a = anf; - if (a->run != run) + if (a->run != _run) { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - rxa.emnr->run, - run, - rxa.anr->run + bp1Check ( + amd->run, + snba->run, + emnr->run, + _run, + anr->run ); - a->run = run; - RXA::bp1Set (rxa); + a->run = _run; + bp1Set(); a->flush(); } } -void RXA::SetANFPosition (RXA& rxa, int position) +void RXA::setANFPosition(int _position) { - rxa.anf->position = position; - rxa.bp1->position = position; - rxa.anf->flush(); + anf->position = _position; + bp1->position = _position; + anf->flush(); } -void RXA::SetANRRun (RXA& rxa, int run) +void RXA::setANRRun(int _run) { - ANR *a = rxa.anr; + ANR *a = anr; - if (a->run != run) + if (a->run != _run) { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - rxa.emnr->run, - rxa.anf->run, - run + bp1Check ( + amd->run, + snba->run, + emnr->run, + anf->run, + _run ); - a->run = run; - RXA::bp1Set (rxa); + a->run = _run; + bp1Set(); a->flush(); } } -void RXA::SetANRPosition (RXA& rxa, int position) +void RXA::setANRPosition(int _position) { - rxa.anr->position = position; - rxa.bp1->position = position; - rxa.anr->flush(); + anr->position = _position; + bp1->position = _position; + anr->flush(); } -void RXA::SetEMNRRun (RXA& rxa, int run) +void RXA::setEMNRRun(int _run) { - EMNR *a = rxa.emnr; + EMNR *a = emnr; - if (a->run != run) + if (a->run != _run) { - RXA::bp1Check ( - rxa, - rxa.amd->run, - rxa.snba->run, - run, - rxa.anf->run, - rxa.anr->run + bp1Check ( + amd->run, + snba->run, + _run, + anf->run, + anr->run ); - a->run = run; - RXA::bp1Set (rxa); + a->run = _run; + bp1Set(); } } -void RXA::SetEMNRPosition (RXA& rxa, int position) +void RXA::setEMNRPosition(int _position) { - rxa.emnr->position = position; - rxa.bp1->position = position; + emnr->position = _position; + bp1->position = _position; } -void RXA::GetAGCThresh(RXA& rxa, double *thresh, double size, double rate) +void RXA::getAGCThresh(double *_thresh, double _size, double _rate) //for line on bandscope. { double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); - *thresh = 20.0 * log10( rxa.agc->min_volts ) - noise_offset; + noise_offset = 10.0 * log10((nbp0->fhigh - nbp0->flow) * _size / _rate); + *_thresh = 20.0 * log10( agc->min_volts ) - noise_offset; } -void RXA::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) +void RXA::setAGCThresh(double _thresh, double _size, double _rate) //for line on bandscope { double noise_offset; - noise_offset = 10.0 * log10((rxa.nbp0->fhigh - rxa.nbp0->flow) * size / rate); - rxa.agc->max_gain = rxa.agc->out_target / (rxa.agc->var_gain * pow (10.0, (thresh + noise_offset) / 20.0)); - rxa.agc->loadWcpAGC(); + noise_offset = 10.0 * log10((nbp0->fhigh - nbp0->flow) * _size / _rate); + agc->max_gain = agc->out_target / (agc->var_gain * pow (10.0, (_thresh + noise_offset) / 20.0)); + agc->loadWcpAGC(); } /******************************************************************************************************** @@ -1367,35 +1283,35 @@ void RXA::SetAGCThresh(RXA& rxa, double thresh, double size, double rate) * * ********************************************************************************************************/ -void RXA::SetPassband (RXA& rxa, float f_low, float f_high) +void RXA::setPassband(float _f_low, float _f_high) { - rxa.bp1->setBandpassFreqs (f_low, f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) - rxa.snba->setOutputBandwidth (f_low, f_high); // Spectral noise blanker (SNB) - rxa.nbp0->SetFreqs (f_low, f_high); // Notched bandpass + bp1->setBandpassFreqs (_f_low, _f_high); // After spectral noise reduction ( AM || ANF || ANR || EMNR) + snba->setOutputBandwidth (_f_low, _f_high); // Spectral noise blanker (SNB) + nbp0->SetFreqs (_f_low, _f_high); // Notched bandpass } -void RXA::SetNC (RXA& rxa, int nc) +void RXA::setNC(int _nc) { - int oldstate = rxa.state; - rxa.nbp0->SetNC (nc); - rxa.bpsnba->SetNC (nc); - rxa.bp1->SetBandpassNC (nc); - rxa.eqp->setNC (nc); - rxa.fmsq->setNC (nc); - rxa.fmd->setNCde (nc); - rxa.fmd->setNCaud (nc); - rxa.state = oldstate; + int oldstate = state; + nbp0->SetNC (_nc); + bpsnba->SetNC (_nc); + bp1->SetBandpassNC (_nc); + eqp->setNC (_nc); + fmsq->setNC (_nc); + fmd->setNCde (_nc); + fmd->setNCaud (_nc); + state = oldstate; } -void RXA::SetMP (RXA& rxa, int mp) +void RXA::setMP(int _mp) { - rxa.nbp0->SetMP (mp); - rxa.bpsnba->SetMP (mp); - rxa.bp1->SetBandpassMP (mp); - rxa.eqp->setMP (mp); - rxa.fmsq->setMP (mp); - rxa.fmd->setMPde (mp); - rxa.fmd->setMPaud (mp); + nbp0->SetMP (_mp); + bpsnba->SetMP (_mp); + bp1->SetBandpassMP (_mp); + eqp->setMP (_mp); + fmsq->setMP (_mp); + fmd->setMPde (_mp); + fmd->setMPaud (_mp); } } // namespace WDSP diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 629a9374e..1f1fc6b0a 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -128,70 +128,68 @@ public: PANEL *panel; RESAMPLE *rsmpout; - static RXA* create_rxa ( + RXA( int in_rate, // input samplerate int out_rate, // output samplerate int dsp_rate, // sample rate for mainstream dsp processing int dsp_size // number complex samples processed per buffer in mainstream dsp processing ); - static void destroy_rxa (RXA *rxa); - static void flush_rxa (RXA *rxa); - static void xrxa (RXA *rxa); - int get_insize() const { return dsp_insize; } - int get_outsize() const { return dsp_outsize; } - float *get_inbuff() { return inbuff; } - float *get_outbuff() { return outbuff; } + RXA(const RXA&) = delete; + RXA& operator=(const RXA& other) = delete; + ~RXA(); + + void flush(); + void execute(); + void setInputSamplerate(int _in_rate); + void setOutputSamplerate(int _out_rate); + void setDSPSamplerate(int _dsp_rate); + void setDSPBuffsize(int _dsp_size); + int get_insize() const { return Unit::dsp_insize; } + int get_outsize() const { return Unit::dsp_outsize; } + float *get_inbuff() { return Unit::inbuff; } + float *get_outbuff() { return Unit::outbuff; } void setSpectrumProbe(BufferProbe *_spectrumProbe); - static void setInputSamplerate (RXA *rxa, int in_rate); - static void setOutputSamplerate (RXA *rxa, int out_rate); - static void setDSPSamplerate (RXA *rxa, int dsp_rate); - static void setDSPBuffsize (RXA *rxa, int dsp_size); // RXA Properties - static void SetMode (RXA& rxa, int mode); - static void ResCheck (RXA& rxa); - static void bp1Check (RXA& rxa, int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run); - static void bp1Set (RXA& rxa); - static void bpsnbaCheck (RXA& rxa, int mode, int notch_run); - static void bpsnbaSet (RXA& rxa); + void setMode (int mode); + void resCheck (); + void bp1Check (int amd_run, int snba_run, int emnr_run, int anf_run, int anr_run); + void bp1Set (); + void bpsnbaCheck (int mode, int notch_run); + void bpsnbaSet (); // NOTCHDB, NBP, SNBA - static void UpdateNBPFiltersLightWeight (RXA& rxa); - static void UpdateNBPFilters(RXA& rxa); - static int NBPAddNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); - static int NBPGetNotch (RXA& rxa, int notch, double* fcenter, double* fwidth, int* active); - static int NBPDeleteNotch (RXA& rxa, int notch); - static int NBPEditNotch (RXA& rxa, int notch, double fcenter, double fwidth, int active); - static void NBPGetNumNotches (RXA& rxa, int* nnotches); - static void NBPSetTuneFrequency (RXA& rxa, double tunefreq); - static void NBPSetShiftFrequency (RXA& rxa, double shift); - static void NBPSetNotchesRun (RXA& rxa, int run); - static void NBPSetWindow (RXA& rxa, int wintype); - static void NBPSetAutoIncrease (RXA& rxa, int autoincr); + void updateNBPFiltersLightWeight(); + void updateNBPFilters(); + int nbpAddNotch(int notch, double fcenter, double fwidth, int active); + int nbpGetNotch(int notch, double* fcenter, double* fwidth, int* active); + int nbpDeleteNotch(int notch); + int nbpEditNotch(int notch, double fcenter, double fwidth, int active); + void nbpGetNumNotches(int* nnotches); + void nbpSetTuneFrequency(double tunefreq); + void nbpSetShiftFrequency(double shift); + void nbpSetNotchesRun(int run); + void nbpSetWindow(int wintype); + void nbpSetAutoIncrease(int autoincr); // AMD - static void SetAMDRun(RXA& rxa, int run); + void setAMDRun(int run); // SNBA - static void SetSNBARun (RXA& rxa, int run); + void setSNBARun(int run); // ANF - static void SetANFRun (RXA& rxa, int run); - static void SetANFPosition (RXA& rxa, int position); + void setANFRun(int run); + void setANFPosition(int position); // ANR - static void SetANRRun (RXA& rxa, int run); - static void SetANRPosition (RXA& rxa, int position); + void setANRRun(int run); + void setANRPosition(int position); // EMNR - static void SetEMNRRun (RXA& rxa, int run); - static void SetEMNRPosition (RXA& rxa, int position); + void setEMNRRun(int run); + void setEMNRPosition(int position); // WCPAGC - static void SetAGCThresh(RXA& rxa, double thresh, double size, double rate); - static void GetAGCThresh(RXA& rxa, double *thresh, double size, double rate); + void setAGCThresh(double thresh, double size, double rate); + void getAGCThresh(double *thresh, double size, double rate); // Collectives - static void SetPassband (RXA& rxa, float f_low, float f_high); - static void SetNC (RXA& rxa, int nc); - static void SetMP (RXA& rxa, int mp); - -private: - float* inbuff; - float* midbuff; - float* outbuff; + void setPassband(float f_low, float f_high); + void setNC(int nc); + void setMP(int mp); }; } // namespace WDSP diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index ac8f9285b..8532a0c6f 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -53,97 +53,82 @@ warren@wpratt.com namespace WDSP { -TXA* TXA::create_txa ( - int in_rate, // input samplerate - int out_rate, // output samplerate - int dsp_rate, // sample rate for mainstream dsp processing - int dsp_size // number complex samples processed per buffer in mainstream dsp processing +TXA::TXA( + int _in_rate, // input samplerate + int _out_rate, // output samplerate + int _dsp_rate, // sample rate for mainstream dsp processing + int _dsp_size // number complex samples processed per buffer in mainstream dsp processing +) : Unit( + _in_rate, + _out_rate, + _dsp_rate, + _dsp_size ) { - TXA *txa = new TXA; + mode = TXA_LSB; + f_low = -5000.0; + f_high = - 100.0; - txa->in_rate = in_rate; - txa->out_rate = out_rate; - txa->dsp_rate = dsp_rate; - txa->dsp_size = dsp_size; - - if (in_rate >= dsp_rate) - txa->dsp_insize = dsp_size * (in_rate / dsp_rate); - else - txa->dsp_insize = dsp_size / (dsp_rate / in_rate); - - if (out_rate >= dsp_rate) - txa->dsp_outsize = dsp_size * (out_rate / dsp_rate); - else - txa->dsp_outsize = dsp_size / (dsp_rate / out_rate); - - txa->mode = TXA_LSB; - txa->f_low = -5000.0; - txa->f_high = - 100.0; - txa->inbuff = new float[1 * txa->dsp_insize * 2]; // (float *) malloc0 (1 * txa->dsp_insize * sizeof (complex)); - txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *) malloc0 (1 * txa->dsp_outsize * sizeof (complex)); - txa->midbuff = new float[3 * txa->dsp_size * 2]; //(float *) malloc0 (2 * txa->dsp_size * sizeof (complex)); - - txa->rsmpin = new RESAMPLE( + rsmpin = new RESAMPLE( 0, // run - will be turned on below if needed - txa->dsp_insize, // input buffer size - txa->inbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer - txa->in_rate, // input sample rate - txa->dsp_rate, // output sample rate + dsp_insize, // input buffer size + inbuff, // pointer to input buffer + midbuff, // pointer to output buffer + in_rate, // input sample rate + dsp_rate, // output sample rate 0.0, // select cutoff automatically 0, // select ncoef automatically 1.0); // gain - txa->gen0 = new GEN( + gen0 = new GEN( 0, // run - txa->dsp_size, // buffer size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // sample rate + dsp_size, // buffer size + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // sample rate 2); // mode - txa->panel = new PANEL( + panel = new PANEL( 1, // run - txa->dsp_size, // size - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 1.0, // gain1 1.0, // gain2I 1.0, // gain2Q 2, // 1 to use Q, 2 to use I for input 0); // 0, no copy - txa->phrot = new PHROT( + phrot = new PHROT( 0, // run - txa->dsp_size, // size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // samplerate + dsp_size, // size + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // samplerate 338.0, // 1/2 of phase frequency 8); // number of stages - txa->micmeter = new METER( + micmeter = new METER( 1, // run - 0, // optional pointer to another 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + nullptr, // optional pointer to another 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_MIC_AV, // index for average value TXA_MIC_PK, // index for peak value -1, // index for gain value - 0); // pointer for gain computation + nullptr); // pointer for gain computation - txa->amsq = new AMSQ( + amsq = new AMSQ( 0, // run - txa->dsp_size, // size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->midbuff, // trigger buffer - txa->dsp_rate, // sample rate + dsp_size, // size + midbuff, // input buffer + midbuff, // output buffer + midbuff, // trigger buffer + dsp_rate, // sample rate 0.010, // time constant for averaging signal 0.004, // up-slew time 0.004, // down-slew time @@ -154,59 +139,59 @@ TXA* TXA::create_txa ( 0.200); // muted gain { - float default_F[11] = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; - float default_G[11] = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; + std::array default_F = {0.0, 32.0, 63.0, 125.0, 250.0, 500.0, 1000.0, 2000.0, 4000.0, 8000.0, 16000.0}; + std::array default_G = {0.0, -12.0, -12.0, -12.0, -1.0, +1.0, +4.0, +9.0, +12.0, -10.0, -10.0}; //float default_G[11] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; - txa->eqp = new EQP ( + eqp = new EQP ( 0, // run - OFF by default - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of filter coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of filter coefficients 0, // minimum phase flag - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 10, // nfreqs - default_F, // vector of frequencies - default_G, // vector of gain values + default_F.data(), // vector of frequencies + default_G.data(), // vector of gain values 0, // cutoff mode 0, // wintype - txa->dsp_rate); // samplerate + dsp_rate); // samplerate } - txa->eqmeter = new METER( + eqmeter = new METER( 1, // run - &(txa->eqp->run), // pointer to eqp 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + &(eqp->run), // pointer to eqp 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_EQ_AV, // index for average value TXA_EQ_PK, // index for peak value -1, // index for gain value - 0); // pointer for gain computation + nullptr); // pointer for gain computation - txa->preemph = EMPHP::create_emphp ( + preemph = EMPHP::create_emphp ( 0, // run 1, // position - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of filter coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of filter coefficients 0, // minimum phase flag - txa->midbuff, // input buffer - txa->midbuff, // output buffer, - txa->dsp_rate, // sample rate + midbuff, // input buffer + midbuff, // output buffer, + dsp_rate, // sample rate 0, // pre-emphasis type 300.0, // f_low 3000.0); // f_high - txa->leveler = new WCPAGC( + leveler = new WCPAGC( 0, // run - OFF by default 5, // mode 0, // 0 for max(I,Q), 1 for envelope - txa->midbuff, // input buff pointer - txa->midbuff, // output buff pointer - txa->dsp_size, // io_buffsize - txa->dsp_rate, // sample rate + midbuff, // input buff pointer + midbuff, // output buff pointer + dsp_size, // io_buffsize + dsp_rate, // sample rate 0.001, // tau_attack 0.500, // tau_decay 6, // n_tau @@ -224,139 +209,139 @@ TXA* TXA::create_txa ( 2.000, // hang_thresh 0.100); // tau_hang_decay - txa->lvlrmeter = new METER( + lvlrmeter = new METER( 1, // run - &(txa->leveler->run), // pointer to leveler 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + &(leveler->run), // pointer to leveler 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_LVLR_AV, // index for average value TXA_LVLR_PK, // index for peak value TXA_LVLR_GAIN, // index for gain value - &txa->leveler->gain); // pointer for gain computation + &leveler->gain); // pointer for gain computation { - float default_F[5] = {200.0, 1000.0, 2000.0, 3000.0, 4000.0}; - float default_G[5] = {0.0, 5.0, 10.0, 10.0, 5.0}; - float default_E[5] = {7.0, 7.0, 7.0, 7.0, 7.0}; - txa->cfcomp = CFCOMP::create_cfcomp( + std::array default_F = {200.0, 1000.0, 2000.0, 3000.0, 4000.0}; + std::array default_G = { 0.0, 5.0, 10.0, 10.0, 5.0}; + std::array default_E = { 7.0, 7.0, 7.0, 7.0, 7.0}; + cfcomp = CFCOMP::create_cfcomp( 0, // run 0, // position 0, // post-equalizer run - txa->dsp_size, // size - txa->midbuff, // input buffer - txa->midbuff, // output buffer + dsp_size, // size + midbuff, // input buffer + midbuff, // output buffer 2048, // fft size 4, // overlap - txa->dsp_rate, // samplerate + dsp_rate, // samplerate 1, // window type 0, // compression method 5, // nfreqs 0.0, // pre-compression 0.0, // pre-postequalization - default_F, // frequency array - default_G, // compression array - default_E, // eq array + default_F.data(), // frequency array + default_G.data(), // compression array + default_E.data(), // eq array 0.25, // metering time constant 0.50); // display time constant } - txa->cfcmeter = new METER( + cfcmeter = new METER( 1, // run - &(txa->cfcomp->run), // pointer to eqp 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + &(cfcomp->run), // pointer to eqp 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_CFC_AV, // index for average value TXA_CFC_PK, // index for peak value TXA_CFC_GAIN, // index for gain value - (double*) &txa->cfcomp->gain); // pointer for gain computation + (double*) &cfcomp->gain); // pointer for gain computation - txa->bp0 = new BANDPASS( + bp0 = new BANDPASS( 1, // always runs 0, // position - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of coefficients 0, // flag for minimum phase - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer - txa->f_low, // low freq cutoff - txa->f_high, // high freq cutoff - txa->dsp_rate, // samplerate + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + f_low, // low freq cutoff + f_high, // high freq cutoff + dsp_rate, // samplerate 1, // wintype 2.0); // gain - txa->compressor = COMPRESSOR::create_compressor ( + compressor = COMPRESSOR::create_compressor ( 0, // run - OFF by default - txa->dsp_size, // size - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 3.0); // gain - txa->bp1 = new BANDPASS( + bp1 = new BANDPASS( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of coefficients 0, // flag for minimum phase - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer - txa->f_low, // low freq cutoff - txa->f_high, // high freq cutoff - txa->dsp_rate, // samplerate + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + f_low, // low freq cutoff + f_high, // high freq cutoff + dsp_rate, // samplerate 1, // wintype 2.0); // gain - txa->osctrl = OSCTRL::create_osctrl ( + osctrl = OSCTRL::create_osctrl ( 0, // run - txa->dsp_size, // size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // sample rate - 1.95); // gain for clippings + dsp_size, // size + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // sample rate + 1.95f); // gain for clippings - txa->bp2 = new BANDPASS( + bp2 = new BANDPASS( 0, // ONLY RUNS WHEN COMPRESSOR IS USED 0, // position - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of coefficients 0, // flag for minimum phase - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer - txa->f_low, // low freq cutoff - txa->f_high, // high freq cutoff - txa->dsp_rate, // samplerate + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer + f_low, // low freq cutoff + f_high, // high freq cutoff + dsp_rate, // samplerate 1, // wintype 1.0); // gain - txa->compmeter = new METER( + compmeter = new METER( 1, // run - &(txa->compressor->run), // pointer to compressor 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + &(compressor->run), // pointer to compressor 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_COMP_AV, // index for average value TXA_COMP_PK, // index for peak value -1, // index for gain value - 0); // pointer for gain computation + nullptr); // pointer for gain computation - txa->alc = new WCPAGC( + alc = new WCPAGC( 1, // run - always ON 5, // mode 1, // 0 for max(I,Q), 1 for envelope - txa->midbuff, // input buff pointer - txa->midbuff, // output buff pointer - txa->dsp_size, // io_buffsize - txa->dsp_rate, // sample rate + midbuff, // input buff pointer + midbuff, // output buff pointer + dsp_size, // io_buffsize + dsp_rate, // sample rate 0.001, // tau_attack 0.010, // tau_decay 6, // n_tau @@ -374,70 +359,70 @@ TXA* TXA::create_txa ( 2.000, // hang_thresh 0.100); // tau_hang_decay - txa->ammod = AMMOD::create_ammod ( + ammod = AMMOD::create_ammod ( 0, // run - OFF by default 0, // mode: 0=>AM, 1=>DSB - txa->dsp_size, // size - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to output buffer + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to output buffer 0.5); // carrier level - txa->fmmod = FMMOD::create_fmmod ( + fmmod = FMMOD::create_fmmod ( 0, // run - OFF by default - txa->dsp_size, // size - txa->midbuff, // pointer to input buffer - txa->midbuff, // pointer to input buffer - txa->dsp_rate, // samplerate + dsp_size, // size + midbuff, // pointer to input buffer + midbuff, // pointer to input buffer + dsp_rate, // samplerate 5000.0, // deviation 300.0, // low cutoff frequency 3000.0, // high cutoff frequency 1, // ctcss run control - 0.10, // ctcss level + 0.10f, // ctcss level 100.0, // ctcss frequency 1, // run bandpass filter - std::max(2048, txa->dsp_size), // number coefficients for bandpass filter + std::max(2048, dsp_size), // number coefficients for bandpass filter 0); // minimum phase flag - txa->gen1 = new GEN( + gen1 = new GEN( 0, // run - txa->dsp_size, // buffer size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // sample rate + dsp_size, // buffer size + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // sample rate 0); // mode - txa->uslew = USLEW::create_uslew ( - txa, - &(txa->upslew), // pointer to channel upslew flag - txa->dsp_size, // buffer size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // sample rate + uslew = USLEW::create_uslew ( + this, + &upslew, // pointer to channel upslew flag + dsp_size, // buffer size + midbuff, // input buffer + midbuff, // output buffer + (float) dsp_rate, // sample rate 0.000, // delay time - 0.005); // upslew time + 0.005f); // upslew time - txa->alcmeter = new METER( + alcmeter = new METER( 1, // run - 0, // optional pointer to a 'run' - txa->dsp_size, // size - txa->midbuff, // pointer to buffer - txa->dsp_rate, // samplerate + nullptr, // optional pointer to a 'run' + dsp_size, // size + midbuff, // pointer to buffer + dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_ALC_AV, // index for average value TXA_ALC_PK, // index for peak value TXA_ALC_GAIN, // index for gain value - &txa->alc->gain); // pointer for gain computation + &alc->gain); // pointer for gain computation - txa->sip1 = new SIPHON( + sip1 = new SIPHON( 1, // run 0, // position 0, // mode 0, // disp - txa->dsp_size, // input buffer size - txa->midbuff, // input buffer + dsp_size, // input buffer size + midbuff, // input buffer 16384, // number of samples to buffer 16384, // fft size for spectrum 1); // specmode @@ -446,7 +431,7 @@ TXA* TXA::create_txa ( // channel, // channel number // 1, // run calibration // 1024, // input buffer size - // txa->in_rate, // samplerate + // in_rate, // samplerate // 16, // ints // 256, // spi // (1.0 / 0.4072), // hw_scale @@ -461,25 +446,25 @@ TXA* TXA::create_txa ( // 256, // pin samples // 0.9); // alpha - txa->iqc.p0 = txa->iqc.p1 = IQC::create_iqc ( + iqc.p0 = iqc.p1 = IQC::create_iqc ( 0, // run - txa->dsp_size, // size - txa->midbuff, // input buffer - txa->midbuff, // output buffer - (float)txa->dsp_rate, // sample rate + dsp_size, // size + midbuff, // input buffer + midbuff, // output buffer + (float)dsp_rate, // sample rate 16, // ints - 0.005, // changeover time + 0.005f, // changeover time 256); // spi - txa->cfir = CFIR::create_cfir( + cfir = CFIR::create_cfir( 0, // run - txa->dsp_size, // size - std::max(2048, txa->dsp_size), // number of filter coefficients + dsp_size, // size + std::max(2048, dsp_size), // number of filter coefficients 0, // minimum phase flag - txa->midbuff, // input buffer - txa->midbuff, // output buffer - txa->dsp_rate, // input sample rate - txa->out_rate, // CIC input sample rate + midbuff, // input buffer + midbuff, // output buffer + dsp_rate, // input sample rate + out_rate, // CIC input sample rate 1, // CIC differential delay 640, // CIC interpolation factor 5, // CIC integrator-comb pairs @@ -488,334 +473,277 @@ TXA* TXA::create_txa ( 0.0, // raised-cosine transition width 0); // window type - txa->rsmpout = new RESAMPLE( + rsmpout = new RESAMPLE( 0, // run - will be turned ON below if needed - txa->dsp_size, // input size - txa->midbuff, // pointer to input buffer - txa->outbuff, // pointer to output buffer - txa->dsp_rate, // input sample rate - txa->out_rate, // output sample rate + dsp_size, // input size + midbuff, // pointer to input buffer + outbuff, // pointer to output buffer + dsp_rate, // input sample rate + out_rate, // output sample rate 0.0, // select cutoff automatically 0, // select ncoef automatically 0.980); // gain - txa->outmeter = new METER( + outmeter = new METER( 1, // run - 0, // optional pointer to another 'run' - txa->dsp_outsize, // size - txa->outbuff, // pointer to buffer - txa->out_rate, // samplerate + nullptr, // optional pointer to another 'run' + dsp_outsize, // size + outbuff, // pointer to buffer + out_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - txa->meter, // result vector + meter, // result vector TXA_OUT_AV, // index for average value TXA_OUT_PK, // index for peak value -1, // index for gain value - 0); // pointer for gain computation + nullptr); // pointer for gain computation // turn OFF / ON resamplers as needed - ResCheck (*txa); - return txa; + resCheck(); } -void TXA::destroy_txa (TXA *txa) +TXA::~TXA() { // in reverse order, free each item we created - delete (txa->outmeter); - delete (txa->rsmpout); - CFIR::destroy_cfir(txa->cfir); - // destroy_calcc (txa->calcc); - IQC::destroy_iqc (txa->iqc.p0); - delete (txa->sip1); - delete (txa->alcmeter); - USLEW::destroy_uslew (txa->uslew); - delete (txa->gen1); - FMMOD::destroy_fmmod (txa->fmmod); - AMMOD::destroy_ammod (txa->ammod); - delete (txa->alc); - delete (txa->compmeter); - delete (txa->bp2); - OSCTRL::destroy_osctrl (txa->osctrl); - delete (txa->bp1); - COMPRESSOR::destroy_compressor (txa->compressor); - delete (txa->bp0); - delete (txa->cfcmeter); - CFCOMP::destroy_cfcomp (txa->cfcomp); - delete (txa->lvlrmeter); - delete (txa->leveler); - EMPHP::destroy_emphp (txa->preemph); - delete (txa->eqmeter); - delete (txa->eqp); - delete (txa->amsq); - delete (txa->micmeter); - delete (txa->phrot); - delete (txa->panel); - delete (txa->gen0); - delete (txa->rsmpin); - delete[] (txa->midbuff); - delete[] (txa->outbuff); - delete[] (txa->inbuff); - delete txa; + delete outmeter; + delete rsmpout; + CFIR::destroy_cfir(cfir); + IQC::destroy_iqc (iqc.p0); + delete sip1; + delete alcmeter; + USLEW::destroy_uslew (uslew); + delete gen1; + FMMOD::destroy_fmmod (fmmod); + AMMOD::destroy_ammod (ammod); + delete alc; + delete compmeter; + delete bp2; + OSCTRL::destroy_osctrl (osctrl); + delete bp1; + COMPRESSOR::destroy_compressor (compressor); + delete bp0; + delete cfcmeter; + CFCOMP::destroy_cfcomp (cfcomp); + delete lvlrmeter; + delete leveler; + EMPHP::destroy_emphp (preemph); + delete eqmeter; + delete eqp; + delete amsq; + delete micmeter; + delete phrot; + delete panel; + delete gen0; + delete rsmpin; } -void TXA::flush_txa (TXA* txa) +void TXA::flush() { - std::fill(txa->inbuff, txa->inbuff + 1 * txa->dsp_insize * 2, 0); - std::fill(txa->outbuff, txa->outbuff + 1 * txa->dsp_outsize * 2, 0); - std::fill(txa->midbuff, txa->midbuff + 2 * txa->dsp_size * 2, 0); - txa->rsmpin->flush(); - txa->gen0->flush(); - txa->panel->flush (); - txa->phrot->flush(); - txa->micmeter->flush (); - txa->amsq->flush (); - txa->eqp->flush(); - txa->eqmeter->flush (); - EMPHP::flush_emphp (txa->preemph); - txa->leveler->flush(); - txa->lvlrmeter->flush (); - CFCOMP::flush_cfcomp (txa->cfcomp); - txa->cfcmeter->flush (); - txa->bp0->flush (); - COMPRESSOR::flush_compressor (txa->compressor); - txa->bp1->flush (); - OSCTRL::flush_osctrl (txa->osctrl); - txa->bp2->flush (); - txa->compmeter->flush (); - txa->alc->flush (); - AMMOD::flush_ammod (txa->ammod); - FMMOD::flush_fmmod (txa->fmmod); - txa->gen1->flush(); - USLEW::flush_uslew (txa->uslew); - txa->alcmeter->flush (); - txa->sip1->flush(); - IQC::flush_iqc (txa->iqc.p0); - CFIR::flush_cfir(txa->cfir); - txa->rsmpout->flush(); - txa->outmeter->flush (); + Unit::flushBuffers(); + rsmpin->flush(); + gen0->flush(); + panel->flush (); + phrot->flush(); + micmeter->flush (); + amsq->flush (); + eqp->flush(); + eqmeter->flush (); + EMPHP::flush_emphp (preemph); + leveler->flush(); + lvlrmeter->flush (); + CFCOMP::flush_cfcomp (cfcomp); + cfcmeter->flush (); + bp0->flush (); + COMPRESSOR::flush_compressor (compressor); + bp1->flush (); + OSCTRL::flush_osctrl (osctrl); + bp2->flush (); + compmeter->flush (); + alc->flush (); + AMMOD::flush_ammod (ammod); + FMMOD::flush_fmmod (fmmod); + gen1->flush(); + USLEW::flush_uslew (uslew); + alcmeter->flush (); + sip1->flush(); + IQC::flush_iqc (iqc.p0); + CFIR::flush_cfir(cfir); + rsmpout->flush(); + outmeter->flush (); } -void xtxa (TXA* txa) +void TXA::execute() { - txa->rsmpin->execute(); // input resampler - txa->gen0->execute(); // input signal generator - txa->panel->execute(); // includes MIC gain - txa->phrot->execute(); // phase rotator - txa->micmeter->execute (); // MIC meter - txa->amsq->xcap (); // downward expander capture - txa->amsq->execute (); // downward expander action - txa->eqp->execute (); // pre-EQ - txa->eqmeter->execute (); // EQ meter - EMPHP::xemphp (txa->preemph, 0); // FM pre-emphasis (first option) - txa->leveler->execute (); // Leveler - txa->lvlrmeter->execute (); // Leveler Meter - CFCOMP::xcfcomp (txa->cfcomp, 0); // Continuous Frequency Compressor with post-EQ - txa->cfcmeter->execute (); // CFC+PostEQ Meter - txa->bp0->execute (0); // primary bandpass filter - COMPRESSOR::xcompressor (txa->compressor); // COMP compressor - txa->bp1->execute (0); // aux bandpass (runs if COMP) - OSCTRL::xosctrl (txa->osctrl); // CESSB Overshoot Control - txa->bp2->execute (0); // aux bandpass (runs if CESSB) - txa->compmeter->execute (); // COMP meter - txa->alc->execute (); // ALC - AMMOD::xammod (txa->ammod); // AM Modulator - EMPHP::xemphp (txa->preemph, 1); // FM pre-emphasis (second option) - FMMOD::xfmmod (txa->fmmod); // FM Modulator - txa->gen1->execute(); // output signal generator (TUN and Two-tone) - USLEW::xuslew (txa->uslew); // up-slew for AM, FM, and gens - txa->alcmeter->execute (); // ALC Meter - txa->sip1->execute(0); // siphon data for display - IQC::xiqc (txa->iqc.p0); // PureSignal correction - CFIR::xcfir(txa->cfir); // compensating FIR filter (used Protocol_2 only) - txa->rsmpout->execute(); // output resampler - txa->outmeter->execute (); // output meter - // print_peak_env ("env_exception.txt", txa->dsp_outsize, txa->outbuff, 0.7); + rsmpin->execute(); // input resampler + gen0->execute(); // input signal generator + panel->execute(); // includes MIC gain + phrot->execute(); // phase rotator + micmeter->execute (); // MIC meter + amsq->xcap (); // downward expander capture + amsq->execute (); // downward expander action + eqp->execute (); // pre-EQ + eqmeter->execute (); // EQ meter + EMPHP::xemphp (preemph, 0); // FM pre-emphasis (first option) + leveler->execute (); // Leveler + lvlrmeter->execute (); // Leveler Meter + CFCOMP::xcfcomp (cfcomp, 0); // Continuous Frequency Compressor with post-EQ + cfcmeter->execute (); // CFC+PostEQ Meter + bp0->execute (0); // primary bandpass filter + COMPRESSOR::xcompressor (compressor); // COMP compressor + bp1->execute (0); // aux bandpass (runs if COMP) + OSCTRL::xosctrl (osctrl); // CESSB Overshoot Control + bp2->execute (0); // aux bandpass (runs if CESSB) + compmeter->execute (); // COMP meter + alc->execute (); // ALC + AMMOD::xammod (ammod); // AM Modulator + EMPHP::xemphp (preemph, 1); // FM pre-emphasis (second option) + FMMOD::xfmmod (fmmod); // FM Modulator + gen1->execute(); // output signal generator (TUN and Two-tone) + USLEW::xuslew (uslew); // up-slew for AM, FM, and gens + alcmeter->execute (); // ALC Meter + sip1->execute(0); // siphon data for display + IQC::xiqc (iqc.p0); // PureSignal correction + CFIR::xcfir(cfir); // compensating FIR filter (used Protocol_2 only) + rsmpout->execute(); // output resampler + outmeter->execute (); // output meter } -void TXA::setInputSamplerate (TXA *txa, int in_rate) +void TXA::setInputSamplerate(int in_rate) { - if (in_rate >= txa->dsp_rate) - txa->dsp_insize = txa->dsp_size * (in_rate / txa->dsp_rate); - else - txa->dsp_insize = txa->dsp_size / (txa->dsp_rate / in_rate); - - txa->in_rate = in_rate; - // buffers - delete[] (txa->inbuff); - txa->inbuff = new float[1 * txa->dsp_insize * 2]; //(float *)malloc0(1 * txa->dsp_insize * sizeof(complex)); + Unit::setBuffersInputSamplerate(in_rate); // input resampler - txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin->setSize(txa->dsp_insize); - txa->rsmpin->setInRate(txa->in_rate); - ResCheck (*txa); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); + rsmpin->setInRate(in_rate); + resCheck(); } -void TXA::setOutputSamplerate (TXA* txa, int out_rate) +void TXA::setOutputSamplerate(int out_rate) { - if (out_rate >= txa->dsp_rate) - txa->dsp_outsize = txa->dsp_size * (out_rate / txa->dsp_rate); - else - txa->dsp_outsize = txa->dsp_size / (txa->dsp_rate / out_rate); - - txa->out_rate = out_rate; - // buffers - delete[] (txa->outbuff); - txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); + Unit::setBuffersOutputSamplerate(out_rate); // cfir - needs to know input rate of firmware CIC - CFIR::setOutRate_cfir (txa->cfir, txa->out_rate); + CFIR::setOutRate_cfir (cfir, out_rate); // output resampler - txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout->setOutRate(txa->out_rate); - ResCheck (*txa); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setOutRate(out_rate); + resCheck(); // output meter - txa->outmeter->setBuffers(txa->outbuff); - txa->outmeter->setSize(txa->dsp_outsize); - txa->outmeter->setSamplerate (txa->out_rate); + outmeter->setBuffers(outbuff); + outmeter->setSize(dsp_outsize); + outmeter->setSamplerate (out_rate); } -void TXA::setDSPSamplerate (TXA *txa, int dsp_rate) +void TXA::setDSPSamplerate(int dsp_rate) { - if (txa->in_rate >= dsp_rate) - txa->dsp_insize = txa->dsp_size * (txa->in_rate / dsp_rate); - else - txa->dsp_insize = txa->dsp_size / (dsp_rate / txa->in_rate); - - if (txa->out_rate >= dsp_rate) - txa->dsp_outsize = txa->dsp_size * (txa->out_rate / dsp_rate); - else - txa->dsp_outsize = txa->dsp_size / (dsp_rate / txa->out_rate); - - txa->dsp_rate = dsp_rate; - // buffers - delete[] (txa->inbuff); - txa->inbuff = new float[1 * txa->dsp_insize * 2]; // (float *)malloc0(1 * txa->dsp_insize * sizeof(complex)); - delete[] (txa->outbuff); - txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); + Unit::setBuffersDSPSamplerate(dsp_rate); // input resampler - txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin->setSize(txa->dsp_insize); - txa->rsmpin->setOutRate(txa->dsp_rate); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); + rsmpin->setOutRate(dsp_rate); // dsp_rate blocks - txa->gen0->setSamplerate(txa->dsp_rate); - txa->panel->setSamplerate(txa->dsp_rate); - txa->phrot->setSamplerate(txa->dsp_rate); - txa->micmeter->setSamplerate (txa->dsp_rate); - txa->amsq->setSamplerate (txa->dsp_rate); - txa->eqp->setSamplerate (txa->dsp_rate); - txa->eqmeter->setSamplerate (txa->dsp_rate); - EMPHP::setSamplerate_emphp (txa->preemph, txa->dsp_rate); - txa->leveler->setSamplerate (txa->dsp_rate); - txa->lvlrmeter->setSamplerate (txa->dsp_rate); - CFCOMP::setSamplerate_cfcomp (txa->cfcomp, txa->dsp_rate); - txa->cfcmeter->setSamplerate (txa->dsp_rate); - txa->bp0->setSamplerate (txa->dsp_rate); - COMPRESSOR::setSamplerate_compressor (txa->compressor, txa->dsp_rate); - txa->bp1->setSamplerate (txa->dsp_rate); - OSCTRL::setSamplerate_osctrl (txa->osctrl, txa->dsp_rate); - txa->bp2->setSamplerate (txa->dsp_rate); - txa->compmeter->setSamplerate (txa->dsp_rate); - txa->alc->setSamplerate (txa->dsp_rate); - AMMOD::setSamplerate_ammod (txa->ammod, txa->dsp_rate); - FMMOD::setSamplerate_fmmod (txa->fmmod, txa->dsp_rate); - txa->gen1->setSamplerate(txa->dsp_rate); - USLEW::setSamplerate_uslew (txa->uslew, txa->dsp_rate); - txa->alcmeter->setSamplerate (txa->dsp_rate); - txa->sip1->setSamplerate (txa->dsp_rate); - IQC::setSamplerate_iqc (txa->iqc.p0, txa->dsp_rate); - CFIR::setSamplerate_cfir (txa->cfir, txa->dsp_rate); + gen0->setSamplerate(dsp_rate); + panel->setSamplerate(dsp_rate); + phrot->setSamplerate(dsp_rate); + micmeter->setSamplerate (dsp_rate); + amsq->setSamplerate (dsp_rate); + eqp->setSamplerate (dsp_rate); + eqmeter->setSamplerate (dsp_rate); + EMPHP::setSamplerate_emphp (preemph, dsp_rate); + leveler->setSamplerate (dsp_rate); + lvlrmeter->setSamplerate (dsp_rate); + CFCOMP::setSamplerate_cfcomp (cfcomp, dsp_rate); + cfcmeter->setSamplerate (dsp_rate); + bp0->setSamplerate (dsp_rate); + COMPRESSOR::setSamplerate_compressor (compressor, dsp_rate); + bp1->setSamplerate (dsp_rate); + OSCTRL::setSamplerate_osctrl (osctrl, dsp_rate); + bp2->setSamplerate (dsp_rate); + compmeter->setSamplerate (dsp_rate); + alc->setSamplerate (dsp_rate); + AMMOD::setSamplerate_ammod (ammod, dsp_rate); + FMMOD::setSamplerate_fmmod (fmmod, dsp_rate); + gen1->setSamplerate(dsp_rate); + USLEW::setSamplerate_uslew (uslew, dsp_rate); + alcmeter->setSamplerate (dsp_rate); + sip1->setSamplerate (dsp_rate); + IQC::setSamplerate_iqc (iqc.p0, dsp_rate); + CFIR::setSamplerate_cfir (cfir, dsp_rate); // output resampler - txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout->setInRate(txa->dsp_rate); - ResCheck (*txa); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setInRate(dsp_rate); + resCheck(); // output meter - txa->outmeter->setBuffers(txa->outbuff); - txa->outmeter->setSize (txa->dsp_outsize); + outmeter->setBuffers(outbuff); + outmeter->setSize (dsp_outsize); } -void TXA::setDSPBuffsize (TXA *txa, int dsp_size) +void TXA::setDSPBuffsize(int dsp_size) { - if (txa->in_rate >= txa->dsp_rate) - txa->dsp_insize = dsp_size * (txa->in_rate / txa->dsp_rate); - else - txa->dsp_insize = dsp_size / (txa->dsp_rate / txa->in_rate); - - if (txa->out_rate >= txa->dsp_rate) - txa->dsp_outsize = dsp_size * (txa->out_rate / txa->dsp_rate); - else - txa->dsp_outsize = dsp_size / (txa->dsp_rate / txa->out_rate); - - txa->dsp_size = dsp_size; - // buffers - delete[] (txa->inbuff); - txa->inbuff = new float[1 * txa->dsp_insize * 2]; // (float *)malloc0(1 * txa->dsp_insize * sizeof(complex)); - delete[] (txa->midbuff); - txa->midbuff = new float[2 * txa->dsp_size * 2]; // (float *)malloc0(2 * txa->dsp_size * sizeof(complex)); - delete[] (txa->outbuff); - txa->outbuff = new float[1 * txa->dsp_outsize * 2]; // (float *)malloc0(1 * txa->dsp_outsize * sizeof(complex)); + Unit::setBuffersDSPBuffsize(dsp_size); // input resampler - txa->rsmpin->setBuffers(txa->inbuff, txa->midbuff); - txa->rsmpin->setSize(txa->dsp_insize); + rsmpin->setBuffers(inbuff, midbuff); + rsmpin->setSize(dsp_insize); // dsp_size blocks - txa->gen0->setBuffers(txa->midbuff, txa->midbuff); - txa->gen0->setSize(txa->dsp_size); - txa->panel->setBuffers(txa->midbuff, txa->midbuff); - txa->panel->setSize(txa->dsp_size); - txa->phrot->setBuffers(txa->midbuff, txa->midbuff); - txa->phrot->setSize(txa->dsp_size); - txa->micmeter->setBuffers (txa->midbuff); - txa->micmeter->setSize (txa->dsp_size); - txa->amsq->setBuffers (txa->midbuff, txa->midbuff, txa->midbuff); - txa->amsq->setSize (txa->dsp_size); - txa->eqp->setBuffers (txa->midbuff, txa->midbuff); - txa->eqp->setSize (txa->dsp_size); - txa->eqmeter->setBuffers (txa->midbuff); - txa->eqmeter->setSize (txa->dsp_size); - EMPHP::setBuffers_emphp (txa->preemph, txa->midbuff, txa->midbuff); - EMPHP::setSize_emphp (txa->preemph, txa->dsp_size); - txa->leveler->setBuffers(txa->midbuff, txa->midbuff); - txa->leveler->setSize(txa->dsp_size); - txa->lvlrmeter->setBuffers(txa->midbuff); - txa->lvlrmeter->setSize(txa->dsp_size); - CFCOMP::setBuffers_cfcomp (txa->cfcomp, txa->midbuff, txa->midbuff); - CFCOMP::setSize_cfcomp (txa->cfcomp, txa->dsp_size); - txa->cfcmeter->setBuffers(txa->midbuff); - txa->cfcmeter->setSize(txa->dsp_size); - txa->bp0->setBuffers (txa->midbuff, txa->midbuff); - txa->bp0->setSize (txa->dsp_size); - COMPRESSOR::setBuffers_compressor (txa->compressor, txa->midbuff, txa->midbuff); - COMPRESSOR::setSize_compressor (txa->compressor, txa->dsp_size); - txa->bp1->setBuffers (txa->midbuff, txa->midbuff); - txa->bp1->setSize (txa->dsp_size); - OSCTRL::setBuffers_osctrl (txa->osctrl, txa->midbuff, txa->midbuff); - OSCTRL::setSize_osctrl (txa->osctrl, txa->dsp_size); - txa->bp2->setBuffers (txa->midbuff, txa->midbuff); - txa->bp2->setSize (txa->dsp_size); - txa->compmeter->setBuffers(txa->midbuff); - txa->compmeter->setSize(txa->dsp_size); - txa->alc->setBuffers(txa->midbuff, txa->midbuff); - txa->alc->setSize( txa->dsp_size); - AMMOD::setBuffers_ammod (txa->ammod, txa->midbuff, txa->midbuff); - AMMOD::setSize_ammod (txa->ammod, txa->dsp_size); - FMMOD::setBuffers_fmmod (txa->fmmod, txa->midbuff, txa->midbuff); - FMMOD::setSize_fmmod (txa->fmmod, txa->dsp_size); - txa->gen1->setBuffers(txa->midbuff, txa->midbuff); - txa->gen1->setSize(txa->dsp_size); - USLEW::setBuffers_uslew (txa->uslew, txa->midbuff, txa->midbuff); - USLEW::setSize_uslew (txa->uslew, txa->dsp_size); - txa->alcmeter->setBuffers (txa->midbuff); - txa->alcmeter->setSize(txa->dsp_size); - txa->sip1->setBuffers (txa->midbuff); - txa->sip1->setSize (txa->dsp_size); - IQC::setBuffers_iqc (txa->iqc.p0, txa->midbuff, txa->midbuff); - IQC::setSize_iqc (txa->iqc.p0, txa->dsp_size); - CFIR::setBuffers_cfir (txa->cfir, txa->midbuff, txa->midbuff); - CFIR::setSize_cfir (txa->cfir, txa->dsp_size); + gen0->setBuffers(midbuff, midbuff); + gen0->setSize(dsp_size); + panel->setBuffers(midbuff, midbuff); + panel->setSize(dsp_size); + phrot->setBuffers(midbuff, midbuff); + phrot->setSize(dsp_size); + micmeter->setBuffers (midbuff); + micmeter->setSize (dsp_size); + amsq->setBuffers (midbuff, midbuff, midbuff); + amsq->setSize (dsp_size); + eqp->setBuffers (midbuff, midbuff); + eqp->setSize (dsp_size); + eqmeter->setBuffers (midbuff); + eqmeter->setSize (dsp_size); + EMPHP::setBuffers_emphp (preemph, midbuff, midbuff); + EMPHP::setSize_emphp (preemph, dsp_size); + leveler->setBuffers(midbuff, midbuff); + leveler->setSize(dsp_size); + lvlrmeter->setBuffers(midbuff); + lvlrmeter->setSize(dsp_size); + CFCOMP::setBuffers_cfcomp (cfcomp, midbuff, midbuff); + CFCOMP::setSize_cfcomp (cfcomp, dsp_size); + cfcmeter->setBuffers(midbuff); + cfcmeter->setSize(dsp_size); + bp0->setBuffers (midbuff, midbuff); + bp0->setSize (dsp_size); + COMPRESSOR::setBuffers_compressor (compressor, midbuff, midbuff); + COMPRESSOR::setSize_compressor (compressor, dsp_size); + bp1->setBuffers (midbuff, midbuff); + bp1->setSize (dsp_size); + OSCTRL::setBuffers_osctrl (osctrl, midbuff, midbuff); + OSCTRL::setSize_osctrl (osctrl, dsp_size); + bp2->setBuffers (midbuff, midbuff); + bp2->setSize (dsp_size); + compmeter->setBuffers(midbuff); + compmeter->setSize(dsp_size); + alc->setBuffers(midbuff, midbuff); + alc->setSize( dsp_size); + AMMOD::setBuffers_ammod (ammod, midbuff, midbuff); + AMMOD::setSize_ammod (ammod, dsp_size); + FMMOD::setBuffers_fmmod (fmmod, midbuff, midbuff); + FMMOD::setSize_fmmod (fmmod, dsp_size); + gen1->setBuffers(midbuff, midbuff); + gen1->setSize(dsp_size); + USLEW::setBuffers_uslew (uslew, midbuff, midbuff); + USLEW::setSize_uslew (uslew, dsp_size); + alcmeter->setBuffers (midbuff); + alcmeter->setSize(dsp_size); + sip1->setBuffers (midbuff); + sip1->setSize (dsp_size); + IQC::setBuffers_iqc (iqc.p0, midbuff, midbuff); + IQC::setSize_iqc (iqc.p0, dsp_size); + CFIR::setBuffers_cfir (cfir, midbuff, midbuff); + CFIR::setSize_cfir (cfir, dsp_size); // output resampler - txa->rsmpout->setBuffers(txa->midbuff, txa->outbuff); - txa->rsmpout->setSize(txa->dsp_size); + rsmpout->setBuffers(midbuff, outbuff); + rsmpout->setSize(dsp_size); // output meter - txa->outmeter->setBuffers(txa->outbuff); - txa->outmeter->setSize(txa->dsp_outsize); + outmeter->setBuffers(outbuff); + outmeter->setSize(dsp_outsize); } /******************************************************************************************************** @@ -824,51 +752,51 @@ void TXA::setDSPBuffsize (TXA *txa, int dsp_size) * * ********************************************************************************************************/ -void TXA::SetMode (TXA& txa, int mode) +void TXA::setMode(int _mode) { - if (txa.mode != mode) + if (mode != _mode) { - txa.mode = mode; - txa.ammod->run = 0; - txa.fmmod->run = 0; - txa.preemph->run = 0; + mode = _mode; + ammod->run = 0; + fmmod->run = 0; + preemph->run = 0; - switch (mode) + switch (_mode) { case TXA_AM: case TXA_SAM: - txa.ammod->run = 1; - txa.ammod->mode = 0; + ammod->run = 1; + ammod->mode = 0; break; case TXA_DSB: - txa.ammod->run = 1; - txa.ammod->mode = 1; + ammod->run = 1; + ammod->mode = 1; break; case TXA_AM_LSB: case TXA_AM_USB: - txa.ammod->run = 1; - txa.ammod->mode = 2; + ammod->run = 1; + ammod->mode = 2; break; case TXA_FM: - txa.fmmod->run = 1; - txa.preemph->run = 1; + fmmod->run = 1; + preemph->run = 1; break; default: break; } - SetupBPFilters (txa); + setupBPFilters(); } } -void TXA::SetBandpassFreqs (TXA& txa, float f_low, float f_high) +void TXA::setBandpassFreqs(float _f_low, float _f_high) { - if ((txa.f_low != f_low) || (txa.f_high != f_high)) + if ((f_low != _f_low) || (f_high != _f_high)) { - txa.f_low = f_low; - txa.f_high = f_high; - SetupBPFilters (txa); + f_low = _f_low; + f_high = _f_high; + setupBPFilters(); } } @@ -879,34 +807,34 @@ void TXA::SetBandpassFreqs (TXA& txa, float f_low, float f_high) * * ********************************************************************************************************/ -void TXA::ResCheck (TXA& txa) +void TXA::resCheck() { - RESAMPLE *a = txa.rsmpin; - if (txa.in_rate != txa.dsp_rate) + RESAMPLE *a = rsmpin; + if (in_rate != dsp_rate) a->run = 1; else a->run = 0; - a = txa.rsmpout; - if (txa.dsp_rate != txa.out_rate) + a = rsmpout; + if (dsp_rate != out_rate) a->run = 1; else a->run = 0; } -int TXA::UslewCheck (TXA& txa) +int TXA::uslewCheck() { - return (txa.ammod->run == 1) || - (txa.fmmod->run == 1) || - (txa.gen0->run == 1) || - (txa.gen1->run == 1); + return (ammod->run == 1) || + (fmmod->run == 1) || + (gen0->run == 1) || + (gen1->run == 1); } -void TXA::SetupBPFilters (TXA& txa) +void TXA::setupBPFilters() { - txa.bp0->run = 1; - txa.bp1->run = 0; - txa.bp2->run = 0; - switch (txa.mode) + bp0->run = 1; + bp1->run = 0; + bp2->run = 0; + switch (mode) { case TXA_LSB: case TXA_USB: @@ -916,15 +844,15 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_DIGU: case TXA_SPEC: case TXA_DRM: - txa.bp0->calcBandpassFilter (txa.f_low, txa.f_high, 2.0); - if (txa.compressor->run) + bp0->calcBandpassFilter (f_low, f_high, 2.0); + if (compressor->run) { - txa.bp1->calcBandpassFilter (txa.f_low, txa.f_high, 2.0); - txa.bp1->run = 1; - if (txa.osctrl->run) + bp1->calcBandpassFilter (f_low, f_high, 2.0); + bp1->run = 1; + if (osctrl->run) { - txa.bp2->calcBandpassFilter (txa.f_low, txa.f_high, 1.0); - txa.bp2->run = 1; + bp2->calcBandpassFilter (f_low, f_high, 1.0); + bp2->run = 1; } } break; @@ -932,60 +860,62 @@ void TXA::SetupBPFilters (TXA& txa) case TXA_AM: case TXA_SAM: case TXA_FM: - if (txa.compressor->run) + if (compressor->run) { - txa.bp0->calcBandpassFilter (0.0, txa.f_high, 2.0); - txa.bp1->calcBandpassFilter (0.0, txa.f_high, 2.0); - txa.bp1->run = 1; - if (txa.osctrl->run) + bp0->calcBandpassFilter (0.0, f_high, 2.0); + bp1->calcBandpassFilter (0.0, f_high, 2.0); + bp1->run = 1; + if (osctrl->run) { - txa.bp2->calcBandpassFilter (0.0, txa.f_high, 1.0); - txa.bp2->run = 1; + bp2->calcBandpassFilter (0.0, f_high, 1.0); + bp2->run = 1; } } else { - txa.bp0->calcBandpassFilter (txa.f_low, txa.f_high, 1.0); + bp0->calcBandpassFilter (f_low, f_high, 1.0); } break; case TXA_AM_LSB: - txa.bp0->calcBandpassFilter (-txa.f_high, 0.0, 2.0); - if (txa.compressor->run) + bp0->calcBandpassFilter (-f_high, 0.0, 2.0); + if (compressor->run) { - txa.bp1->calcBandpassFilter (-txa.f_high, 0.0, 2.0); - txa.bp1->run = 1; - if (txa.osctrl->run) + bp1->calcBandpassFilter (-f_high, 0.0, 2.0); + bp1->run = 1; + if (osctrl->run) { - txa.bp2->calcBandpassFilter (-txa.f_high, 0.0, 1.0); - txa.bp2->run = 1; + bp2->calcBandpassFilter (-f_high, 0.0, 1.0); + bp2->run = 1; } } break; case TXA_AM_USB: - txa.bp0->calcBandpassFilter (0.0, txa.f_high, 2.0); - if (txa.compressor->run) + bp0->calcBandpassFilter (0.0, f_high, 2.0); + if (compressor->run) { - txa.bp1->calcBandpassFilter (0.0, txa.f_high, 2.0); - txa.bp1->run = 1; - if (txa.osctrl->run) + bp1->calcBandpassFilter (0.0, f_high, 2.0); + bp1->run = 1; + if (osctrl->run) { - txa.bp2->calcBandpassFilter(0.0, txa.f_high, 1.0); - txa.bp2->run = 1; + bp2->calcBandpassFilter(0.0, f_high, 1.0); + bp2->run = 1; } } break; + default: + break; } } -void TXA::SetBandpassNC (TXA& txa, int nc) +void TXA::setBandpassNC(int _nc) { // NOTE: 'nc' must be >= 'size' BANDPASS *a; - a = txa.bp0; + a = bp0; - if (a->nc != nc) + if (a->nc != _nc) { - a->nc = nc; + a->nc = _nc; float* impulse = FIR::fir_bandpass ( a->nc, a->f_low, @@ -996,14 +926,14 @@ void TXA::SetBandpassNC (TXA& txa, int nc) a->gain / (double)(2 * a->size) ); FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); - delete[] (impulse); + delete[] impulse; } - a = txa.bp1; + a = bp1; - if (a->nc != nc) + if (a->nc != _nc) { - a->nc = nc; + a->nc = _nc; float* impulse = FIR::fir_bandpass ( a->nc, a->f_low, @@ -1014,14 +944,14 @@ void TXA::SetBandpassNC (TXA& txa, int nc) a->gain / (double)(2 * a->size) ); FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); - delete[] (impulse); + delete[] impulse; } - a = txa.bp2; + a = bp2; - if (a->nc != nc) + if (a->nc != _nc) { - a->nc = nc; + a->nc = _nc; float* impulse = FIR::fir_bandpass ( a->nc, a->f_low, @@ -1032,34 +962,34 @@ void TXA::SetBandpassNC (TXA& txa, int nc) a->gain / (double)(2 * a->size) ); FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); - delete[] (impulse); + delete[] impulse; } } -void TXA::SetBandpassMP (TXA& txa, int mp) +void TXA::setBandpassMP(int _mp) { BANDPASS *a; - a = txa.bp0; + a = bp0; - if (mp != a->mp) + if (_mp != a->mp) { - a->mp = mp; + a->mp = _mp; FIRCORE::setMp_fircore (a->fircore, a->mp); } - a = txa.bp1; + a = bp1; - if (mp != a->mp) + if (_mp != a->mp) { - a->mp = mp; + a->mp = _mp; FIRCORE::setMp_fircore (a->fircore, a->mp); } - a = txa.bp2; + a = bp2; - if (mp != a->mp) + if (_mp != a->mp) { - a->mp = mp; + a->mp = _mp; FIRCORE::setMp_fircore (a->fircore, a->mp); } } @@ -1070,30 +1000,30 @@ void TXA::SetBandpassMP (TXA& txa, int mp) * * ********************************************************************************************************/ -void TXA::SetNC (TXA& txa, int nc) +void TXA::setNC(int _nc) { - int oldstate = txa.state; + int oldstate = state; - SetBandpassNC (txa, nc); - EMPHP::SetFMEmphNC (txa, nc); - txa.eqp->setNC (nc); - FMMOD::SetFMNC (txa, nc); - CFIR::SetCFIRNC (txa, nc); - txa.state = oldstate; + setBandpassNC (_nc); + EMPHP::SetFMEmphNC (*this, _nc); + eqp->setNC (_nc); + FMMOD::SetFMNC (*this, _nc); + CFIR::SetCFIRNC (*this, _nc); + state = oldstate; } -void TXA::SetMP (TXA& txa, int mp) +void TXA::setMP(int _mp) { - SetBandpassMP (txa, mp); - EMPHP::SetFMEmphMP (txa, mp); - txa.eqp->setMP (mp); - FMMOD::SetFMMP (txa, mp); + setBandpassMP (_mp); + EMPHP::SetFMEmphMP (*this, _mp); + eqp->setMP (_mp); + FMMOD::SetFMMP (*this, _mp); } -void TXA::SetFMAFFilter (TXA& txa, float low, float high) +void TXA::setFMAFFilter(float _low, float _high) { - EMPHP::SetFMPreEmphFreqs (txa, low, high); - FMMOD::SetFMAFFreqs (txa, low, high); + EMPHP::SetFMPreEmphFreqs (*this, _low, _high); + FMMOD::SetFMAFFreqs (*this, _low, _high); } } // namespace WDSP diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index 284f2a643..19e5a5771 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -157,11 +157,6 @@ public: GEN *gen0; GEN *gen1; USLEW *uslew; - // struct - // { - // CALCC *p; - // CRITICAL_SECTION cs_update; - // } calcc; struct { IQC *p0, *p1; @@ -169,42 +164,42 @@ public: } iqc; CFIR *cfir; - static TXA* create_txa ( + TXA( int in_rate, // input samplerate int out_rate, // output samplerate int dsp_rate, // sample rate for mainstream dsp processing int dsp_size // number complex samples processed per buffer in mainstream dsp processing ); - static void destroy_txa (TXA *txa); - static void flush_txa (TXA *txa); - static void xtxa (TXA *txa); - int get_insize() const { return dsp_insize; } - int get_outsize() const { return dsp_outsize; } - float *get_inbuff() { return inbuff; } - float *get_outbuff() { return outbuff; } - static void setInputSamplerate (TXA *txa, int in_rate); - static void setOutputSamplerate (TXA *txa, int out_rate); - static void setDSPSamplerate (TXA *txa, int dsp_rate); - static void setDSPBuffsize (TXA *txa, int dsp_size); + TXA(const TXA&) = delete; + TXA& operator=(const TXA& other) = delete; + ~TXA(); + + void flush(); + void execute(); + void setInputSamplerate(int _in_rate); + void setOutputSamplerate(int _out_rate); + void setDSPSamplerate(int _dsp_rate); + void setDSPBuffsize(int _dsp_size); + int get_insize() const { return Unit::dsp_insize; } + int get_outsize() const { return Unit::dsp_outsize; } + float *get_inbuff() { return Unit::inbuff; } + float *get_outbuff() { return Unit::outbuff; } // TXA Properties - static void SetMode (TXA& txa, int mode); - static void SetBandpassFreqs (TXA& txa, float f_low, float f_high); - static void SetBandpassNC (TXA& txa, int nc); - static void SetBandpassMP (TXA& txa, int mp); + void setMode(int mode); + void setBandpassFreqs(float f_low, float f_high); + void setBandpassNC(int nc); + void setBandpassMP(int mp); // Collectives - static void SetNC (TXA& txa, int nc); - static void SetMP (TXA& txa, int mp); - static void SetFMAFFilter (TXA& txa, float low, float high); - static void SetupBPFilters (TXA& txa); - static int UslewCheck (TXA& txa); + void setNC(int nc); + void setMP(int mp); + void setFMAFFilter(float low, float high); + void setupBPFilters(); + int uslewCheck(); private: - static void ResCheck (TXA& txa); - float* inbuff; - float* midbuff; - float* outbuff; + void resCheck(); }; } // namespace WDSP diff --git a/wdsp/compress.cpp b/wdsp/compress.cpp index 949dcf854..5a8d2a556 100644 --- a/wdsp/compress.cpp +++ b/wdsp/compress.cpp @@ -103,7 +103,7 @@ void COMPRESSOR::SetCompressorRun (TXA& txa, int run) if (txa.compressor->run != run) { txa.compressor->run = run; - TXA::SetupBPFilters (txa); + txa.setupBPFilters(); } } diff --git a/wdsp/osctrl.cpp b/wdsp/osctrl.cpp index 30876fcd1..084f495f7 100644 --- a/wdsp/osctrl.cpp +++ b/wdsp/osctrl.cpp @@ -149,7 +149,7 @@ void OSCTRL::SetosctrlRun (TXA& txa, int run) if (txa.osctrl->run != run) { txa.osctrl->run = run; - TXA::SetupBPFilters (txa); + txa.setupBPFilters(); } } diff --git a/wdsp/slew.cpp b/wdsp/slew.cpp index 04b47eeda..50651bfd1 100644 --- a/wdsp/slew.cpp +++ b/wdsp/slew.cpp @@ -103,7 +103,7 @@ void USLEW::flush_uslew (USLEW *a) void USLEW::xuslew (USLEW *a) { - if (!a->runmode && TXA::UslewCheck (*a->txa)) + if (!a->runmode && a->txa->uslewCheck()) a->runmode = 1; long upslew = *a->ch_upslew; diff --git a/wdsp/unit.cpp b/wdsp/unit.cpp new file mode 100644 index 000000000..b6d33ad55 --- /dev/null +++ b/wdsp/unit.cpp @@ -0,0 +1,148 @@ +/* unit.hpp + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2013 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +#include "unit.hpp" + +namespace WDSP { + +Unit::Unit( + int _in_rate, // input samplerate + int _out_rate, // output samplerate + int _dsp_rate, // sample rate for mainstream dsp processing + int _dsp_size // number complex samples processed per buffer in mainstream dsp processing +) : + in_rate{_in_rate}, + out_rate(_out_rate), + dsp_rate(_dsp_rate), + dsp_size(_dsp_size) +{ + if (_in_rate >= _dsp_rate) + dsp_insize = _dsp_size * (_in_rate / _dsp_rate); + else + dsp_insize = _dsp_size / (_dsp_rate / _in_rate); + + if (_out_rate >= _dsp_rate) + dsp_outsize = _dsp_size * (_out_rate / _dsp_rate); + else + dsp_outsize = _dsp_size / (_dsp_rate / _out_rate); + + // buffers + inbuff = new float[1 * dsp_insize * 2]; + outbuff = new float[1 * dsp_outsize * 2]; + midbuff = new float[2 * dsp_size * 2]; +} + +Unit::~Unit() +{ + delete[] inbuff; + delete[] outbuff; + delete[] midbuff; +} + +void Unit::flushBuffers() +{ + std::fill(inbuff, inbuff + 1 * dsp_insize * 2, 0); + std::fill(outbuff, outbuff + 1 * dsp_outsize * 2, 0); + std::fill(midbuff, midbuff + 2 * dsp_size * 2, 0); +} + +void Unit::setBuffersInputSamplerate(int _in_rate) +{ + if (_in_rate >= dsp_rate) + dsp_insize = dsp_size * (_in_rate / dsp_rate); + else + dsp_insize = dsp_size / (dsp_rate / _in_rate); + + in_rate = _in_rate; + + // buffers + delete[] (inbuff); + inbuff = new float[1 * dsp_insize * 2]; +} + +void Unit::setBuffersOutputSamplerate(int _out_rate) +{ + if (_out_rate >= dsp_rate) + dsp_outsize = dsp_size * (_out_rate / dsp_rate); + else + dsp_outsize = dsp_size / (dsp_rate / _out_rate); + + out_rate = _out_rate; + + // buffers + delete[] outbuff; + outbuff = new float[1 * dsp_outsize * 2]; +} + +void Unit::setBuffersDSPSamplerate(int _dsp_rate) +{ + if (in_rate >= _dsp_rate) + dsp_insize = dsp_size * (in_rate / _dsp_rate); + else + dsp_insize = dsp_size / (_dsp_rate / in_rate); + + if (out_rate >= _dsp_rate) + dsp_outsize = dsp_size * (out_rate / _dsp_rate); + else + dsp_outsize = dsp_size / (_dsp_rate / out_rate); + + dsp_rate = _dsp_rate; + + // buffers + delete[] inbuff; + inbuff = new float[1 * dsp_insize * 2]; + delete[] outbuff; + outbuff = new float[1 * dsp_outsize * 2]; +} + +void Unit::setBuffersDSPBuffsize(int _dsp_size) +{ + if (in_rate >= dsp_rate) + dsp_insize = _dsp_size * (in_rate / dsp_rate); + else + dsp_insize = _dsp_size / (dsp_rate / in_rate); + + if (out_rate >= dsp_rate) + dsp_outsize = _dsp_size * (out_rate / dsp_rate); + else + dsp_outsize = _dsp_size / (dsp_rate / out_rate); + + dsp_size = _dsp_size; + + // buffers + delete[]inbuff; + inbuff = new float[1 * dsp_insize * 2]; + delete[] midbuff; + midbuff = new float[2 * dsp_size * 2]; + delete[] outbuff; + outbuff = new float[1 * dsp_outsize * 2]; +} + + +} // namespace diff --git a/wdsp/unit.hpp b/wdsp/unit.hpp index 09000f0c3..a60844ddc 100644 --- a/wdsp/unit.hpp +++ b/wdsp/unit.hpp @@ -42,6 +42,25 @@ public: int dsp_insize; // size (complex samples) of the input buffer int dsp_outsize; // size (complex samples) of the output buffer int state; // 0 for unit OFF; 1 for unit ON + + Unit( + int _in_rate, // input samplerate + int _out_rate, // output samplerate + int _dsp_rate, // sample rate for mainstream dsp processing + int _dsp_size // number complex samples processed per buffer in mainstream dsp processing + ); + ~Unit(); + + void flushBuffers(); + void setBuffersInputSamplerate(int _in_rate); + void setBuffersOutputSamplerate(int _out_rate); + void setBuffersDSPSamplerate(int _dsp_rate); + void setBuffersDSPBuffsize(int _dsp_size); + +protected: + float* inbuff; + float* midbuff; + float* outbuff; }; } // namespace WDSP From 34917a0b214b74a90e5b9e8c19deba2aee188a95 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 5 Aug 2024 20:05:59 +0200 Subject: [PATCH 34/46] WDSP: more rework --- plugins/channelrx/wdsprx/wdsprxsink.cpp | 78 +-- plugins/channelrx/wdsprx/wdsprxsink.h | 9 +- wdsp/CMakeLists.txt | 2 + wdsp/RXA.cpp | 47 +- wdsp/RXA.hpp | 14 +- wdsp/TXA.cpp | 157 ++++-- wdsp/TXA.hpp | 6 +- wdsp/amd.cpp | 30 +- wdsp/ammod.cpp | 82 +-- wdsp/ammod.hpp | 32 +- wdsp/anb.cpp | 11 +- wdsp/anf.cpp | 2 +- wdsp/bldr.cpp | 351 ++++++------ wdsp/bldr.hpp | 79 +-- wdsp/bps.cpp | 282 +++------- wdsp/bps.hpp | 52 +- wdsp/bqbp.cpp | 19 +- wdsp/bqlp.cpp | 16 +- wdsp/bqlp.hpp | 13 +- wdsp/cblock.cpp | 16 +- wdsp/cfcomp.cpp | 676 ++++++++++++------------ wdsp/cfcomp.hpp | 127 ++--- wdsp/compress.cpp | 16 +- wdsp/compress.hpp | 4 +- wdsp/dbqbp.cpp | 16 +- wdsp/dbqbp.hpp | 13 +- wdsp/dsphp.cpp | 16 +- wdsp/emnr.cpp | 6 +- wdsp/emph.cpp | 274 +++------- wdsp/emph.hpp | 89 +--- wdsp/emphp.cpp | 217 ++++++++ wdsp/emphp.hpp | 91 ++++ 32 files changed, 1483 insertions(+), 1360 deletions(-) create mode 100644 wdsp/emphp.cpp create mode 100644 wdsp/emphp.hpp diff --git a/plugins/channelrx/wdsprx/wdsprxsink.cpp b/plugins/channelrx/wdsprx/wdsprxsink.cpp index 3ba83b5ff..3c198ad47 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsink.cpp @@ -26,7 +26,6 @@ #include "util/messagequeue.h" #include "maincore.h" #include "RXA.hpp" -#include "nbp.hpp" #include "meter.hpp" #include "patchpanel.hpp" #include "wcpAGC.hpp" @@ -83,10 +82,10 @@ void WDSPRxSink::SpectrumProbe::proceed(const float *in, int nb_samples) { if (!(m_undersampleCount++ & decim_mask)) { - float avgr = m_sum.real() / decim; - float avgi = m_sum.imag() / decim; + float avgr = m_sum.real() / (float) decim; + float avgi = m_sum.imag() / (float) decim; - if (!m_dsb & !m_usb) + if (!m_dsb && !m_usb) { // invert spectrum for LSB m_sampleVector.push_back(Sample(avgi*SDR_RX_SCALEF, avgr*SDR_RX_SCALEF)); } @@ -175,14 +174,14 @@ void WDSPRxSink::feed(const SampleVector::const_iterator& begin, const SampleVec } } -void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples) +void WDSPRxSink::getMagSqLevels(double& avg, double& peak, int& nbSamples) const { avg = m_sAvg; peak = m_sPeak; nbSamples = m_sCount; } -void WDSPRxSink::processOneSample(Complex &ci) +void WDSPRxSink::processOneSample(const Complex &ci) { m_rxa->get_inbuff()[2*m_inCount] = ci.imag() / SDR_RX_SCALEF; m_rxa->get_inbuff()[2*m_inCount+1] = ci.real() / SDR_RX_SCALEF; @@ -204,10 +203,10 @@ void WDSPRxSink::processOneSample(Complex &ci) } else { - const double& cr = m_rxa->get_outbuff()[2*i+1]; - const double& ci = m_rxa->get_outbuff()[2*i]; - qint16 zr = cr * 32768.0; - qint16 zi = ci * 32768.0; + const double& dr = m_rxa->get_outbuff()[2*i+1]; + const double& di = m_rxa->get_outbuff()[2*i]; + qint16 zr = dr * 32768.0; + qint16 zi = di * 32768.0; m_audioBuffer[m_audioBufferFill].r = zr; m_audioBuffer[m_audioBufferFill].l = zi; @@ -219,7 +218,7 @@ void WDSPRxSink::processOneSample(Complex &ci) else { Real demod = (zr + zi) * 0.7; - qint16 sample = (qint16)(demod); + auto sample = (qint16)(demod); m_demodBuffer[m_demodBufferFill++] = sample; } @@ -228,13 +227,11 @@ void WDSPRxSink::processOneSample(Complex &ci) QList dataPipes; MainCore::instance()->getDataPipes().getDataPipes(m_channel, "demod", dataPipes); - if (dataPipes.size() > 0) + if (!dataPipes.empty()) { - QList::iterator it = dataPipes.begin(); - - for (; it != dataPipes.end(); ++it) + for (auto dataPipe : dataPipes) { - DataFifo *fifo = qobject_cast((*it)->m_element); + DataFifo *fifo = qobject_cast(dataPipe->m_element); if (fifo) { @@ -245,7 +242,7 @@ void WDSPRxSink::processOneSample(Complex &ci) ); } } - } + } m_demodBufferFill = 0; } @@ -316,7 +313,7 @@ void WDSPRxSink::applyAudioSampleRate(int sampleRate) QList pipes; MainCore::instance()->getMessagePipes().getMessagePipes(m_channel, "reportdemod", pipes); - if (pipes.size() > 0) + if (!pipes.empty()) { for (const auto& pipe : pipes) { @@ -385,8 +382,13 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) (m_settings.m_demod != settings.m_demod) || (m_settings.m_dsb != settings.m_dsb) || force) { - float band, low, high, fLow, fHigh; - bool usb, dsb; + float band; + float low; + float high; + float fLow; + float fHigh; + bool usb; + bool dsb; band = settings.m_profiles[settings.m_profileIndex].m_highCutoff; high = band; @@ -770,8 +772,8 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) || (m_settings.m_agcHangThreshold != settings.m_agcHangThreshold) || (m_settings.m_agcGain != settings.m_agcGain) || force) { - m_rxa->agc->setSlope(settings.m_agcSlope); // SetRXAAGCSlope(id, rx->agc_slope); - m_rxa->agc->setTop((float) settings.m_agcGain); // SetRXAAGCTop(id, rx->agc_gain); + m_rxa->agc->setSlope(settings.m_agcSlope); + m_rxa->agc->setTop((float) settings.m_agcGain); if (settings.m_agc) { @@ -779,31 +781,31 @@ void WDSPRxSink::applySettings(const WDSPRxSettings& settings, bool force) { case WDSPRxProfile::WDSPRxAGCMode::AGCLong: m_rxa->agc->setMode(1); - m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); - m_rxa->agc->setHang(2000); // SetRXAAGCHang(id, 2000); - m_rxa->agc->setDecay(2000); // SetRXAAGCDecay(id, 2000); - m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); + m_rxa->agc->setAttack(2); + m_rxa->agc->setHang(2000); + m_rxa->agc->setDecay(2000); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); break; case WDSPRxProfile::WDSPRxAGCMode::AGCSlow: m_rxa->agc->setMode(2); - m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); - m_rxa->agc->setHang(1000); // SetRXAAGCHang(id, 1000); - m_rxa->agc->setDecay(500); // SetRXAAGCDecay(id, 500); - m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, (int)rx->agc_hang_threshold); + m_rxa->agc->setAttack(2); + m_rxa->agc->setHang(1000); + m_rxa->agc->setDecay(500); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); break; case WDSPRxProfile::WDSPRxAGCMode::AGCMedium: m_rxa->agc->setMode(3); - m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); - m_rxa->agc->setHang(0); // SetRXAAGCHang(id, 0); - m_rxa->agc->setDecay(250); // SetRXAAGCDecay(id, 250); - m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); + m_rxa->agc->setAttack(2); + m_rxa->agc->setHang(0); + m_rxa->agc->setDecay(250); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); break; case WDSPRxProfile::WDSPRxAGCMode::AGCFast: m_rxa->agc->setMode(4); - m_rxa->agc->setAttack(2); // SetRXAAGCAttack(id, 2); - m_rxa->agc->setHang(0); // SetRXAAGCHang(id, 0); - m_rxa->agc->setDecay(50); // SetRXAAGCDecay(id, 50); - m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); // SetRXAAGCHangThreshold(id, 100); + m_rxa->agc->setAttack(2); + m_rxa->agc->setHang(0); + m_rxa->agc->setDecay(50); + m_rxa->agc->setHangThreshold(settings.m_agcHangThreshold); break; } } diff --git a/plugins/channelrx/wdsprx/wdsprxsink.h b/plugins/channelrx/wdsprx/wdsprxsink.h index 8e0a73206..22624cc49 100644 --- a/plugins/channelrx/wdsprx/wdsprxsink.h +++ b/plugins/channelrx/wdsprx/wdsprxsink.h @@ -55,13 +55,14 @@ public: bool getAudioActive() const { return m_audioActive; } void setChannel(ChannelAPI *channel) { m_channel = channel; } void setAudioFifoLabel(const QString& label) { m_audioFifo.setLabel(label); } - void getMagSqLevels(double& avg, double& peak, int& nbSamples); + void getMagSqLevels(double& avg, double& peak, int& nbSamples) const; private: class SpectrumProbe : public WDSP::BufferProbe { public: - SpectrumProbe(SampleVector& sampleVector); + explicit SpectrumProbe(SampleVector& sampleVector); + virtual ~SpectrumProbe() = default; virtual void proceed(const float *in, int nbSamples); void setSpanLog2(int spanLog2); void setDSB(bool dsb) { m_dsb = dsb; } @@ -102,8 +103,6 @@ private: Interpolator m_interpolator; Real m_interpolatorDistance; Real m_interpolatorDistanceRemain; - // fftfilt* SSBFilter; - // fftfilt* DSBFilter; SpectrumVis* m_spectrumSink; SampleVector m_sampleBuffer; @@ -123,7 +122,7 @@ private: static const int m_wdspSampleRate; static const int m_wdspBufSize; - void processOneSample(Complex &ci); + void processOneSample(const Complex &ci); }; #endif // INCLUDE_SSBDEMODSINK_H diff --git a/wdsp/CMakeLists.txt b/wdsp/CMakeLists.txt index 59f76a89b..3a277b7fc 100644 --- a/wdsp/CMakeLists.txt +++ b/wdsp/CMakeLists.txt @@ -25,6 +25,7 @@ set(wdsp_SOURCES dsphp.cpp emnr.cpp emph.cpp + emphp.cpp eqp.cpp fcurve.cpp fir.cpp @@ -93,6 +94,7 @@ set(wdsp_HEADERS dsphp.hpp emnr.hpp emph.hpp + emphp.hpp eqp.hpp fcurve.hpp fir.hpp diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 940911ea3..91fef39d9 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -55,6 +55,7 @@ warren@wpratt.com #include "nob.hpp" #include "speak.hpp" #include "mpeak.hpp" +#include "fir.hpp" namespace WDSP { @@ -71,7 +72,7 @@ RXA::RXA( ) { mode = RXA::RXA_LSB; - std::fill(meter, meter + RXA::RXA_METERTYPE_LAST, 0); + std::fill(meter.begin(), meter.end(), 0); // Noise blanker (ANB or "NB") anb = new ANB( @@ -127,17 +128,17 @@ RXA::RXA( // Input meter - ADC adcmeter = new METER( 0, // run - 0, // optional pointer to another 'run' + nullptr, // optional pointer to another 'run' dsp_size, // size midbuff, // pointer to buffer dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - meter, // result vector + meter.data(), // result vector RXA_ADC_AV, // index for average value RXA_ADC_PK, // index for peak value -1, // index for gain value - disabled - 0); // pointer for gain computation + nullptr); // pointer for gain computation // Notched bandpass section @@ -200,17 +201,17 @@ RXA::RXA( // S-meter smeter = new METER( 1, // run - 0, // optional pointer to another 'run' + nullptr, // optional pointer to another 'run' dsp_size, // size midbuff, // pointer to buffer dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - meter, // result vector + meter.data(), // result vector RXA_S_AV, // index for average value RXA_S_PK, // index for peak value -1, // index for gain value - disabled - 0); // pointer for gain computation + nullptr); // pointer for gain computation // AM squelch capture (for other modes than FM) amsq = new AMSQ( @@ -416,13 +417,13 @@ RXA::RXA( // AGC meter agcmeter = new METER( 0, // run - 0, // optional pointer to another 'run' + nullptr, // optional pointer to another 'run' dsp_size, // size midbuff, // pointer to buffer dsp_rate, // samplerate 0.100, // averaging time constant 0.100, // peak decay time constant - meter, // result vector + meter.data(), // result vector RXA_AGC_AV, // index for average value RXA_AGC_PK, // index for peak value RXA_AGC_GAIN, // index for gain value @@ -903,7 +904,7 @@ void RXA::bp1Set () a->run = 0; if (!old && a->run) a->flush(); - FIRCORE::setUpdate_fircore (a->fircore); + FIRCORE::setUpdate_fircore(a->fircore); } void RXA::bpsnbaCheck(int _mode, int _notch_run) @@ -988,7 +989,7 @@ void RXA::bpsnbaSet() default: break; } - FIRCORE::setUpdate_fircore (a->bpsnba->fircore); + FIRCORE::setUpdate_fircore(a->bpsnba->fircore); } void RXA::updateNBPFiltersLightWeight() @@ -1004,7 +1005,7 @@ void RXA::updateNBPFilters() if (a->fnfrun) { a->calc_impulse(); - FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); delete[] (a->impulse); } if (b->bpsnba->fnfrun) @@ -1025,7 +1026,7 @@ int RXA::nbpAddNotch(int _notch, double _fcenter, double _fwidth, int _active) return rval; } -int RXA::nbpGetNotch(int _notch, double* _fcenter, double* _fwidth, int* _active) +int RXA::nbpGetNotch(int _notch, double* _fcenter, double* _fwidth, int* _active) const { NOTCHDB *a = ndb; int rval = a->getNotch(_notch, _fcenter, _fwidth, _active); @@ -1056,7 +1057,7 @@ int RXA::nbpEditNotch(int _notch, double _fcenter, double _fwidth, int _active) return rval; } -void RXA::nbpGetNumNotches(int* _nnotches) +void RXA::nbpGetNumNotches(int* _nnotches) const { const NOTCHDB *a = ndb; a->getNumNotches(_nnotches); @@ -1096,10 +1097,10 @@ void RXA::nbpSetNotchesRun(int _run) b->fnfrun = a->master_run; bpsnbaCheck(mode, _run); b->calc_impulse(); // recalc nbp impulse response - FIRCORE::setImpulse_fircore (b->fircore, b->impulse, 0); // calculate new filter masks + FIRCORE::setImpulse_fircore(b->fircore, b->impulse, 0); // calculate new filter masks delete[] (b->impulse); bpsnbaSet(); - FIRCORE::setUpdate_fircore (b->fircore); // apply new filter masks + FIRCORE::setUpdate_fircore(b->fircore); // apply new filter masks } } @@ -1110,15 +1111,15 @@ void RXA::nbpSetWindow(int _wintype) a = nbp0; b = bpsnba; - if ((a->wintype != _wintype)) + if (a->wintype != _wintype) { a->wintype = _wintype; a->calc_impulse(); - FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); delete[] (a->impulse); } - if ((b->wintype != _wintype)) + if (b->wintype != _wintype) { b->wintype = _wintype; b->recalc_bpsnba_filter(1); @@ -1132,15 +1133,15 @@ void RXA::nbpSetAutoIncrease(int _autoincr) a = nbp0; b = bpsnba; - if ((a->autoincr != _autoincr)) + if (a->autoincr != _autoincr) { a->autoincr = _autoincr; a->calc_impulse(); - FIRCORE::setImpulse_fircore (a->fircore, a->impulse, 1); + FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); delete[] (a->impulse); } - if ((b->autoincr != _autoincr)) + if (b->autoincr != _autoincr) { b->autoincr = _autoincr; b->recalc_bpsnba_filter(1); @@ -1260,7 +1261,7 @@ void RXA::setEMNRPosition(int _position) bp1->position = _position; } -void RXA::getAGCThresh(double *_thresh, double _size, double _rate) +void RXA::getAGCThresh(double *_thresh, double _size, double _rate) const //for line on bandscope. { double noise_offset; diff --git a/wdsp/RXA.hpp b/wdsp/RXA.hpp index 1f1fc6b0a..8578eded2 100644 --- a/wdsp/RXA.hpp +++ b/wdsp/RXA.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_rxa_h #define wdsp_rxa_h +#include + #include "comm.hpp" #include "unit.hpp" #include "export.h" @@ -95,7 +97,7 @@ public: }; int mode; - double meter[RXA_METERTYPE_LAST]; + std::array meter; ANB *anb; NOB *nob; @@ -119,7 +121,6 @@ public: WCPAGC *agc; METER *agcmeter; BANDPASS *bp1; - BPS *bps1; SIPHON *sip1; CBL *cbl; SPEAK *speak; @@ -136,7 +137,7 @@ public: ); RXA(const RXA&) = delete; RXA& operator=(const RXA& other) = delete; - ~RXA(); + virtual ~RXA(); void flush(); void execute(); @@ -161,10 +162,10 @@ public: void updateNBPFiltersLightWeight(); void updateNBPFilters(); int nbpAddNotch(int notch, double fcenter, double fwidth, int active); - int nbpGetNotch(int notch, double* fcenter, double* fwidth, int* active); + int nbpGetNotch(int notch, double* fcenter, double* fwidth, int* active) const; int nbpDeleteNotch(int notch); int nbpEditNotch(int notch, double fcenter, double fwidth, int active); - void nbpGetNumNotches(int* nnotches); + void nbpGetNumNotches(int* nnotches) const; void nbpSetTuneFrequency(double tunefreq); void nbpSetShiftFrequency(double shift); void nbpSetNotchesRun(int run); @@ -185,7 +186,8 @@ public: void setEMNRPosition(int position); // WCPAGC void setAGCThresh(double thresh, double size, double rate); - void getAGCThresh(double *thresh, double size, double rate); + void getAGCThresh(double *thresh, double size, double rate) const; + // Collectives void setPassband(float f_low, float f_high); void setNC(int nc); diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 8532a0c6f..9521d88a3 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -39,7 +39,7 @@ warren@wpratt.com #include "bps.hpp" #include "osctrl.hpp" #include "wcpAGC.hpp" -#include "emph.hpp" +#include "emphp.hpp" #include "fmmod.hpp" #include "siphon.hpp" #include "gen.hpp" @@ -171,7 +171,7 @@ TXA::TXA( -1, // index for gain value nullptr); // pointer for gain computation - preemph = EMPHP::create_emphp ( + preemph = new EMPHP( 0, // run 1, // position dsp_size, // size @@ -224,10 +224,10 @@ TXA::TXA( &leveler->gain); // pointer for gain computation { - std::array default_F = {200.0, 1000.0, 2000.0, 3000.0, 4000.0}; - std::array default_G = { 0.0, 5.0, 10.0, 10.0, 5.0}; - std::array default_E = { 7.0, 7.0, 7.0, 7.0, 7.0}; - cfcomp = CFCOMP::create_cfcomp( + std::array default_F = {200.0, 1000.0, 2000.0, 3000.0, 4000.0}; + std::array default_G = { 0.0, 5.0, 10.0, 10.0, 5.0}; + std::array default_E = { 7.0, 7.0, 7.0, 7.0, 7.0}; + cfcomp = new CFCOMP( 0, // run 0, // position 0, // post-equalizer run @@ -359,7 +359,7 @@ TXA::TXA( 2.000, // hang_thresh 0.100); // tau_hang_decay - ammod = AMMOD::create_ammod ( + ammod = new AMMOD( 0, // run - OFF by default 0, // mode: 0=>AM, 1=>DSB dsp_size, // size @@ -514,7 +514,7 @@ TXA::~TXA() USLEW::destroy_uslew (uslew); delete gen1; FMMOD::destroy_fmmod (fmmod); - AMMOD::destroy_ammod (ammod); + delete ammod; delete alc; delete compmeter; delete bp2; @@ -523,10 +523,10 @@ TXA::~TXA() COMPRESSOR::destroy_compressor (compressor); delete bp0; delete cfcmeter; - CFCOMP::destroy_cfcomp (cfcomp); + delete cfcomp; delete lvlrmeter; delete leveler; - EMPHP::destroy_emphp (preemph); + delete preemph; delete eqmeter; delete eqp; delete amsq; @@ -548,10 +548,10 @@ void TXA::flush() amsq->flush (); eqp->flush(); eqmeter->flush (); - EMPHP::flush_emphp (preemph); + preemph->flush(); leveler->flush(); lvlrmeter->flush (); - CFCOMP::flush_cfcomp (cfcomp); + cfcomp->flush(); cfcmeter->flush (); bp0->flush (); COMPRESSOR::flush_compressor (compressor); @@ -560,7 +560,7 @@ void TXA::flush() bp2->flush (); compmeter->flush (); alc->flush (); - AMMOD::flush_ammod (ammod); + ammod->flush(); FMMOD::flush_fmmod (fmmod); gen1->flush(); USLEW::flush_uslew (uslew); @@ -583,10 +583,10 @@ void TXA::execute() amsq->execute (); // downward expander action eqp->execute (); // pre-EQ eqmeter->execute (); // EQ meter - EMPHP::xemphp (preemph, 0); // FM pre-emphasis (first option) + preemph->execute(0); // FM pre-emphasis (first option) leveler->execute (); // Leveler lvlrmeter->execute (); // Leveler Meter - CFCOMP::xcfcomp (cfcomp, 0); // Continuous Frequency Compressor with post-EQ + cfcomp->execute(0); // Continuous Frequency Compressor with post-EQ cfcmeter->execute (); // CFC+PostEQ Meter bp0->execute (0); // primary bandpass filter COMPRESSOR::xcompressor (compressor); // COMP compressor @@ -595,8 +595,8 @@ void TXA::execute() bp2->execute (0); // aux bandpass (runs if CESSB) compmeter->execute (); // COMP meter alc->execute (); // ALC - AMMOD::xammod (ammod); // AM Modulator - EMPHP::xemphp (preemph, 1); // FM pre-emphasis (second option) + ammod->execute(); // AM Modulator + preemph->execute(1); // FM pre-emphasis (second option) FMMOD::xfmmod (fmmod); // FM Modulator gen1->execute(); // output signal generator (TUN and Two-tone) USLEW::xuslew (uslew); // up-slew for AM, FM, and gens @@ -648,10 +648,10 @@ void TXA::setDSPSamplerate(int dsp_rate) amsq->setSamplerate (dsp_rate); eqp->setSamplerate (dsp_rate); eqmeter->setSamplerate (dsp_rate); - EMPHP::setSamplerate_emphp (preemph, dsp_rate); + preemph->setSamplerate(dsp_rate); leveler->setSamplerate (dsp_rate); lvlrmeter->setSamplerate (dsp_rate); - CFCOMP::setSamplerate_cfcomp (cfcomp, dsp_rate); + cfcomp->setSamplerate(dsp_rate); cfcmeter->setSamplerate (dsp_rate); bp0->setSamplerate (dsp_rate); COMPRESSOR::setSamplerate_compressor (compressor, dsp_rate); @@ -660,7 +660,7 @@ void TXA::setDSPSamplerate(int dsp_rate) bp2->setSamplerate (dsp_rate); compmeter->setSamplerate (dsp_rate); alc->setSamplerate (dsp_rate); - AMMOD::setSamplerate_ammod (ammod, dsp_rate); + ammod->setSamplerate(dsp_rate); FMMOD::setSamplerate_fmmod (fmmod, dsp_rate); gen1->setSamplerate(dsp_rate); USLEW::setSamplerate_uslew (uslew, dsp_rate); @@ -698,14 +698,14 @@ void TXA::setDSPBuffsize(int dsp_size) eqp->setSize (dsp_size); eqmeter->setBuffers (midbuff); eqmeter->setSize (dsp_size); - EMPHP::setBuffers_emphp (preemph, midbuff, midbuff); - EMPHP::setSize_emphp (preemph, dsp_size); + preemph->setBuffers(midbuff, midbuff); + preemph->setSize(dsp_size); leveler->setBuffers(midbuff, midbuff); leveler->setSize(dsp_size); lvlrmeter->setBuffers(midbuff); lvlrmeter->setSize(dsp_size); - CFCOMP::setBuffers_cfcomp (cfcomp, midbuff, midbuff); - CFCOMP::setSize_cfcomp (cfcomp, dsp_size); + cfcomp->setBuffers(midbuff, midbuff); + cfcomp->setSize(dsp_size); cfcmeter->setBuffers(midbuff); cfcmeter->setSize(dsp_size); bp0->setBuffers (midbuff, midbuff); @@ -722,8 +722,8 @@ void TXA::setDSPBuffsize(int dsp_size) compmeter->setSize(dsp_size); alc->setBuffers(midbuff, midbuff); alc->setSize( dsp_size); - AMMOD::setBuffers_ammod (ammod, midbuff, midbuff); - AMMOD::setSize_ammod (ammod, dsp_size); + ammod->setBuffers(midbuff, midbuff); + ammod->setSize(dsp_size); FMMOD::setBuffers_fmmod (fmmod, midbuff, midbuff); FMMOD::setSize_fmmod (fmmod, dsp_size); gen1->setBuffers(midbuff, midbuff); @@ -925,7 +925,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); delete[] impulse; } @@ -943,7 +943,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); delete[] impulse; } @@ -961,7 +961,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore (a->fircore, a->nc, impulse); + FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); delete[] impulse; } } @@ -974,7 +974,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore (a->fircore, a->mp); + FIRCORE::setMp_fircore(a->fircore, a->mp); } a = bp1; @@ -982,7 +982,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore (a->fircore, a->mp); + FIRCORE::setMp_fircore(a->fircore, a->mp); } a = bp2; @@ -990,7 +990,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore (a->fircore, a->mp); + FIRCORE::setMp_fircore(a->fircore, a->mp); } } @@ -1005,7 +1005,7 @@ void TXA::setNC(int _nc) int oldstate = state; setBandpassNC (_nc); - EMPHP::SetFMEmphNC (*this, _nc); + preemph->setNC (_nc); eqp->setNC (_nc); FMMOD::SetFMNC (*this, _nc); CFIR::SetCFIRNC (*this, _nc); @@ -1015,15 +1015,100 @@ void TXA::setNC(int _nc) void TXA::setMP(int _mp) { setBandpassMP (_mp); - EMPHP::SetFMEmphMP (*this, _mp); + preemph->setMP (_mp); eqp->setMP (_mp); FMMOD::SetFMMP (*this, _mp); } void TXA::setFMAFFilter(float _low, float _high) { - EMPHP::SetFMPreEmphFreqs (*this, _low, _high); - FMMOD::SetFMAFFreqs (*this, _low, _high); + preemph->setFreqs (_low, _high); + FMMOD::SetFMAFFreqs(*this, _low, _high); } +void TXA::SetBPSRun (TXA& txa, int _run) +{ + txa.bp1->run = _run; +} + +void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) +{ + float* impulse; + BPS *a; + a = txa.bps0; + + if ((_f_low != a->f_low) || (_f_high != a->f_high)) + { + a->f_low = _f_low; + a->f_high = _f_high; + delete[] (a->mults); + impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } + + a = txa.bps1; + + if ((_f_low != a->f_low) || (_f_high != a->f_high)) + { + a->f_low = _f_low; + a->f_high = _f_high; + delete[] (a->mults); + impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } + + a = txa.bps2; + + if ((_f_low != a->f_low) || (_f_high != a->f_high)) + { + a->f_low = _f_low; + a->f_high = _f_high; + delete[] (a->mults); + impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } +} + +void TXA::SetBPSWindow (TXA& txa, int _wintype) +{ + float* impulse; + BPS *a; + a = txa.bps0; + + if (a->wintype != _wintype) + { + a->wintype = _wintype; + delete[] (a->mults); + impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } + + a = txa.bps1; + + if (a->wintype != _wintype) + { + a->wintype = _wintype; + delete[] (a->mults); + impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } + + a = txa.bps2; + + if (a->wintype != _wintype) + { + a->wintype = _wintype; + delete[] (a->mults); + impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + a->mults = FIR::fftcv_mults (2 * a->size, impulse); + delete[] (impulse); + } +} + + } // namespace WDSP diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index 19e5a5771..55da102fb 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -172,7 +172,7 @@ public: ); TXA(const TXA&) = delete; TXA& operator=(const TXA& other) = delete; - ~TXA(); + virtual ~TXA(); void flush(); void execute(); @@ -190,6 +190,10 @@ public: void setBandpassFreqs(float f_low, float f_high); void setBandpassNC(int nc); void setBandpassMP(int mp); + // BPS + static void SetBPSRun (TXA& txa, int run); + static void SetBPSFreqs (TXA& txa, double low, double high); + static void SetBPSWindow (TXA& txa, int wintype); // Collectives void setNC(int nc); diff --git a/wdsp/amd.cpp b/wdsp/amd.cpp index 6b1aefe3f..820e1b4b7 100644 --- a/wdsp/amd.cpp +++ b/wdsp/amd.cpp @@ -53,22 +53,22 @@ AMD::AMD double _omegaN, double _tauR, double _tauI -) +) : + run(_run), + buff_size(_buff_size), + in_buff(_in_buff), + out_buff(_out_buff), + mode(_mode), + sample_rate((double) _sample_rate), + fmin(_fmin), + fmax(_fmax), + zeta(_zeta), + omegaN(_omegaN), + tauR(_tauR), + tauI(_tauI), + sbmode(_sbmode), + levelfade(_levelfade) { - run = _run; - buff_size = _buff_size; - in_buff = _in_buff; - out_buff = _out_buff; - mode = _mode; - levelfade = _levelfade; - sbmode = _sbmode; - sample_rate = (double) _sample_rate; - fmin = _fmin; - fmax = _fmax; - zeta = _zeta; - omegaN = _omegaN; - tauR = _tauR; - tauI = _tauI; init(); } diff --git a/wdsp/ammod.cpp b/wdsp/ammod.cpp index b1301e351..e884143a4 100644 --- a/wdsp/ammod.cpp +++ b/wdsp/ammod.cpp @@ -33,72 +33,74 @@ warren@wpratt.com namespace WDSP { -AMMOD* AMMOD::create_ammod (int run, int mode, int size, float* in, float* out, float c_level) +AMMOD::AMMOD( + int _run, + int _mode, + int _size, + float* _in, + float* _out, + double _c_level +) { - AMMOD *a = new AMMOD; - a->run = run; - a->mode = mode; - a->size = size; - a->in = in; - a->out = out; - a->c_level = c_level; - a->a_level = 1.0 - a->c_level; - a->mult = 1.0 / sqrt (2.0); - return a; + run = _run; + mode = _mode; + size = _size; + in = _in; + out = _out; + c_level = _c_level; + a_level = 1.0 - c_level; + mult = 1.0 / sqrt (2.0); } -void AMMOD::destroy_ammod(AMMOD *a) +void AMMOD::flush() { - delete a; + // Nothing to flush } -void AMMOD::flush_ammod(AMMOD *) +void AMMOD::execute() { - -} - -void AMMOD::xammod(AMMOD *a) -{ - if (a->run) + if (run) { int i; - switch (a->mode) + switch (mode) { case 0: // AM - for (i = 0; i < a->size; i++) - a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * (a->c_level + a->a_level * a->in[2 * i + 0]); + for (i = 0; i < size; i++) + out[2 * i + 0] = out[2 * i + 1] = (float) (mult * (c_level + a_level * in[2 * i + 0])); break; case 1: // DSB - for (i = 0; i < a->size; i++) - a->out[2 * i + 0] = a->out[2 * i + 1] = a->mult * a->in[2 * i + 0]; + for (i = 0; i < size; i++) + out[2 * i + 0] = out[2 * i + 1] = (float) (mult * in[2 * i + 0]); break; case 2: // SSB w/Carrier - for (i = 0; i < a->size; i++) + for (i = 0; i < size; i++) { - a->out[2 * i + 0] = a->mult * a->c_level + a->a_level * a->in[2 * i + 0]; - a->out[2 * i + 1] = a->mult * a->c_level + a->a_level * a->in[2 * i + 1]; + out[2 * i + 0] = (float) (mult * c_level + a_level * in[2 * i + 0]); + out[2 * i + 1] = (float) (mult * c_level + a_level * in[2 * i + 1]); } break; + default: + break; } } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void AMMOD::setBuffers_ammod(AMMOD *a, float* in, float* out) +void AMMOD::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void AMMOD::setSamplerate_ammod(AMMOD *, int) +void AMMOD::setSamplerate(int) { - + // Nothing to do } -void AMMOD::setSize_ammod(AMMOD *a, int size) +void AMMOD::setSize(int _size) { - a->size = size; + size = _size; } /******************************************************************************************************** @@ -107,10 +109,10 @@ void AMMOD::setSize_ammod(AMMOD *a, int size) * * ********************************************************************************************************/ -void AMMOD::SetAMCarrierLevel (TXA& txa, float c_level) +void AMMOD::setAMCarrierLevel(double _c_level) { - txa.ammod->c_level = c_level; - txa.ammod->a_level = 1.0 - c_level; + c_level = _c_level; + a_level = 1.0 - _c_level; } } // namespace WDSP diff --git a/wdsp/ammod.hpp b/wdsp/ammod.hpp index 09f724fe9..e5ba7ad51 100644 --- a/wdsp/ammod.hpp +++ b/wdsp/ammod.hpp @@ -42,19 +42,29 @@ public: int size; float* in; float* out; - float c_level; - float a_level; - float mult; + double c_level; + double a_level; + double mult; - static AMMOD* create_ammod(int run, int mode, int size, float* in, float* out, float c_level); - static void destroy_ammod (AMMOD *a); - static void flush_ammod (AMMOD *a); - static void xammod (AMMOD *a); - static void setBuffers_ammod (AMMOD *a, float* in, float* out); - static void setSamplerate_ammod (AMMOD *a, int rate); - static void setSize_ammod (AMMOD *a, int size); + AMMOD( + int run, + int mode, + int size, + float* in, + float* out, + double c_level + ); + AMMOD(const AMMOD&) = delete; + AMMOD& operator=(const AMMOD& other) = delete; + ~AMMOD() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetAMCarrierLevel (TXA& txa, float c_level); + void setAMCarrierLevel(double c_level); }; } // namespace WDSP diff --git a/wdsp/anb.cpp b/wdsp/anb.cpp index 7b3183909..f9717d399 100644 --- a/wdsp/anb.cpp +++ b/wdsp/anb.cpp @@ -81,13 +81,12 @@ ANB::ANB ( hangtime(_hangtime), advtime(_advtime), backtau(_backtau), - threshold(_threshold) + threshold(_threshold), + dtime(0), + htime(0), + itime(0), + atime(0) { - dtime = 0; - htime = 0; - itime = 0; - atime = 0; - if (tau < 0.0) { tau = 0.0; } else if (tau > MAX_TAU) { diff --git a/wdsp/anf.cpp b/wdsp/anf.cpp index cee3e3341..af13b1635 100644 --- a/wdsp/anf.cpp +++ b/wdsp/anf.cpp @@ -65,6 +65,7 @@ ANF::ANF( delay(_delay), two_mu(_two_mu), gamma(_gamma), + in_idx(0), lidx(_lidx), lidx_min(_lidx_min), lidx_max(_lidx_max), @@ -73,7 +74,6 @@ ANF::ANF( lincr(_lincr), ldecr(_ldecr) { - in_idx = 0; std::fill(d.begin(), d.end(), 0); std::fill(w.begin(), w.end(), 0); } diff --git a/wdsp/bldr.cpp b/wdsp/bldr.cpp index ddbbb9c16..a234dd1c2 100644 --- a/wdsp/bldr.cpp +++ b/wdsp/bldr.cpp @@ -30,138 +30,102 @@ warren@wpratt.com namespace WDSP { -BLDR* BLDR::create_builder(int points, int ints) +BLDR::BLDR(int points, int ints) { // for the create function, 'points' and 'ints' are the MAXIMUM values that will be encountered - BLDR *a = new BLDR; - a->catxy = new float[2 * points]; // (float*)malloc0(2 * points * sizeof(float)); - a->sx = new float[points]; // (float*)malloc0( points * sizeof(float)); - a->sy = new float[points]; // (float*)malloc0( points * sizeof(float)); - a->h = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->p = new int[ints]; // (int*) malloc0( ints * sizeof(int)); - a->np = new int[ints]; // (int*) malloc0( ints * sizeof(int)); - a->taa = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tab = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tag = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tad = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tbb = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tbg = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tbd = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tgg = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tgd = new float[ints]; // (float*)malloc0( ints * sizeof(float)); - a->tdd = new float[ints]; // (float*)malloc0( ints * sizeof(float)); + catxy = new double[2 * points]; + sx.resize(points); + sy.resize(points); + h .resize(ints); + p.resize(ints); + np.resize(ints); + taa.resize(ints); + tab.resize(ints); + tag.resize(ints); + tad.resize(ints); + tbb.resize(ints); + tbg.resize(ints); + tbd.resize(ints); + tgg.resize(ints); + tgd.resize(ints); + tdd.resize(ints); int nsize = 3 * ints + 1; int intp1 = ints + 1; int intm1 = ints - 1; - a->A = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float)); - a->B = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float)); - a->C = new float[intp1 * intp1]; // (float*)malloc0(intm1 * intp1 * sizeof(float)); - a->D = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float)); - a->E = new float[intp1 * intp1]; // (float*)malloc0(intp1 * intp1 * sizeof(float)); - a->F = new float[intm1 * intp1]; // (float*)malloc0(intm1 * intp1 * sizeof(float)); - a->G = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float)); - a->MAT = new float[nsize * nsize]; // (float*)malloc0(nsize * nsize * sizeof(float)); - a->RHS = new float[nsize]; // (float*)malloc0(nsize * sizeof(float)); - a->SLN = new float[nsize]; // (float*)malloc0(nsize * sizeof(float)); - a->z = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float)); - a->zp = new float[intp1]; // (float*)malloc0(intp1 * sizeof(float)); - a->wrk = new float[nsize]; // (float*)malloc0(nsize * sizeof(float)); - a->ipiv = new int[nsize]; // (int*) malloc0(nsize * sizeof(int)); - return a; + A .resize(intp1 * intp1); + B .resize(intp1 * intp1); + C .resize(intp1 * intp1); + D .resize(intp1); + E .resize(intp1 * intp1); + F .resize(intm1 * intp1); + G .resize(intp1); + MAT.resize(nsize * nsize); + RHS.resize(nsize); + SLN.resize(nsize); + z .resize(intp1); + zp.resize(intp1); + wrk.resize(nsize); + ipiv.resize(nsize); } -void BLDR::destroy_builder(BLDR *a) +BLDR::~BLDR() { - delete[](a->ipiv); - delete[](a->wrk); - delete[](a->catxy); - delete[](a->sx); - delete[](a->sy); - delete[](a->h); - delete[](a->p); - delete[](a->np); - - delete[](a->taa); - delete[](a->tab); - delete[](a->tag); - delete[](a->tad); - delete[](a->tbb); - delete[](a->tbg); - delete[](a->tbd); - delete[](a->tgg); - delete[](a->tgd); - delete[](a->tdd); - - delete[](a->A); - delete[](a->B); - delete[](a->C); - delete[](a->D); - delete[](a->E); - delete[](a->F); - delete[](a->G); - - delete[](a->MAT); - delete[](a->RHS); - delete[](a->SLN); - - delete[](a->z); - delete[](a->zp); - - delete(a); + delete[]catxy; } -void BLDR::flush_builder(BLDR *a, int points, int ints) +void BLDR::flush(int points) { - memset(a->catxy, 0, 2 * points * sizeof(float)); - memset(a->sx, 0, points * sizeof(float)); - memset(a->sy, 0, points * sizeof(float)); - memset(a->h, 0, ints * sizeof(float)); - memset(a->p, 0, ints * sizeof(int)); - memset(a->np, 0, ints * sizeof(int)); - memset(a->taa, 0, ints * sizeof(float)); - memset(a->tab, 0, ints * sizeof(float)); - memset(a->tag, 0, ints * sizeof(float)); - memset(a->tad, 0, ints * sizeof(float)); - memset(a->tbb, 0, ints * sizeof(float)); - memset(a->tbg, 0, ints * sizeof(float)); - memset(a->tbd, 0, ints * sizeof(float)); - memset(a->tgg, 0, ints * sizeof(float)); - memset(a->tgd, 0, ints * sizeof(float)); - memset(a->tdd, 0, ints * sizeof(float)); - int nsize = 3 * ints + 1; - int intp1 = ints + 1; - int intm1 = ints - 1; - memset(a->A, 0, intp1 * intp1 * sizeof(float)); - memset(a->B, 0, intp1 * intp1 * sizeof(float)); - memset(a->C, 0, intm1 * intp1 * sizeof(float)); - memset(a->D, 0, intp1 * sizeof(float)); - memset(a->E, 0, intp1 * intp1 * sizeof(float)); - memset(a->F, 0, intm1 * intp1 * sizeof(float)); - memset(a->G, 0, intp1 * sizeof(float)); - memset(a->MAT, 0, nsize * nsize * sizeof(float)); - memset(a->RHS, 0, nsize * sizeof(float)); - memset(a->SLN, 0, nsize * sizeof(float)); - memset(a->z, 0, intp1 * sizeof(float)); - memset(a->zp, 0, intp1 * sizeof(float)); - memset(a->wrk, 0, nsize * sizeof(float)); - memset(a->ipiv, 0, nsize * sizeof(int)); + memset(catxy, 0, 2 * points * sizeof(double)); + std::fill(sx.begin(), sx.end(), 0); + std::fill(sy.begin(), sy.end(), 0); + std::fill(h.begin(), h.end(), 0); + std::fill(p.begin(), p.end(), 0); + std::fill(np.begin(), np.end(), 0); + std::fill(taa.begin(), taa.end(), 0); + std::fill(tab.begin(), tab.end(), 0); + std::fill(tag.begin(), tag.end(), 0); + std::fill(tad.begin(), tad.end(), 0); + std::fill(tbb.begin(), tbb.end(), 0); + std::fill(tbg.begin(), tbg.end(), 0); + std::fill(tbd.begin(), tbd.end(), 0); + std::fill(tgg.begin(), tgg.end(), 0); + std::fill(tgd.begin(), tgd.end(), 0); + std::fill(tdd.begin(), tdd.end(), 0); + std::fill(A.begin(), A.end(), 0); + std::fill(B.begin(), B.end(), 0); + std::fill(C.begin(), C.end(), 0); + std::fill(D.begin(), D.end(), 0); + std::fill(E.begin(), E.end(), 0); + std::fill(F.begin(), F.end(), 0); + std::fill(G.begin(), G.end(), 0); + std::fill(MAT.begin(), MAT.end(), 0); + std::fill(RHS.begin(), RHS.end(), 0); + std::fill(SLN.begin(), SLN.end(), 0); + std::fill(z.begin(), z.end(), 0); + std::fill(zp.begin(), zp.end(), 0); + std::fill(wrk.begin(), wrk.end(), 0); + std::fill(ipiv.begin(), ipiv.end(), 0); } int BLDR::fcompare(const void* a, const void* b) { - if (*(float*)a < *(float*)b) + if (*(double*)a < *(double*)b) return -1; - else if (*(float*)a == *(float*)b) + else if (*(double*)a == *(double*)b) return 0; else return 1; } -void BLDR::decomp(int n, float* a, int* piv, int* info, float* wrk) +void BLDR::decomp(int n, std::vector& a, std::vector& piv, int* info, std::vector& wrk) { - int i, j, k; + int i; + int j; int t_piv; - float m_row, mt_row, m_col, mt_col; + double m_row; + double mt_row; + double m_col; + double mt_col; *info = 0; for (i = 0; i < n; i++) { @@ -180,7 +144,7 @@ void BLDR::decomp(int n, float* a, int* piv, int* info, float* wrk) } wrk[i] = m_row; } - for (k = 0; k < n - 1; k++) + for (int k = 0; k < n - 1; k++) { j = k; m_col = a[n * piv[k] + k] / wrk[piv[k]]; @@ -216,10 +180,11 @@ cleanup: return; } -void BLDR::dsolve(int n, float* a, int* piv, float* b, float* x) +void BLDR::dsolve(int n, std::vector& a, std::vector& piv, std::vector& b, std::vector& x) { - int j, k; - float sum; + int j; + int k; + double sum; for (k = 0; k < n; k++) { @@ -238,7 +203,7 @@ void BLDR::dsolve(int n, float* a, int* piv, float* b, float* x) } } -void BLDR::cull(int* n, int ints, float* x, float* t, float ptol) +void BLDR::cull(int* n, int ints, std::vector& x, const double* t, double ptol) { int k = 0; int i = *n; @@ -255,28 +220,36 @@ void BLDR::cull(int* n, int ints, float* x, float* t, float ptol) *n -= k; } -void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, int* info, float* c, float ptol) +void BLDR::execute(int points, const double* x, const double* y, int ints, const double* t, int* info, double* c, double ptol) { - float u, v, alpha, beta, gamma, delta; + double u; + double v; + double alpha; + double beta; + double gamma; + double delta; int nsize = 3 * ints + 1; int intp1 = ints + 1; int intm1 = ints - 1; - int i, j, k, m; + int i; + int j; + int k; + int m; int dinfo; - flush_builder(a, points, ints); + flush(points); for (i = 0; i < points; i++) { - a->catxy[2 * i + 0] = x[i]; - a->catxy[2 * i + 1] = y[i]; + catxy[2 * i + 0] = x[i]; + catxy[2 * i + 1] = y[i]; } - qsort(a->catxy, points, 2 * sizeof(float), fcompare); + qsort(catxy, points, 2 * sizeof(double), fcompare); for (i = 0; i < points; i++) { - a->sx[i] = a->catxy[2 * i + 0]; - a->sy[i] = a->catxy[2 * i + 1]; + sx[i] = catxy[2 * i + 0]; + sy[i] = catxy[2 * i + 1]; } - cull(&points, ints, a->sx, t, ptol); - if (points <= 0 || a->sx[points - 1] > t[ints]) + cull(&points, ints, sx, t, ptol); + if (points <= 0 || sx[points - 1] > t[ints]) { *info = -1000; goto cleanup; @@ -284,101 +257,101 @@ void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, else *info = 0; for (j = 0; j < ints; j++) - a->h[j] = t[j + 1] - t[j]; - a->p[0] = 0; + h[j] = t[j + 1] - t[j]; + p[0] = 0; j = 0; for (i = 0; i < points; i++) { - if (a->sx[i] <= t[j + 1]) - a->np[j]++; + if (sx[i] <= t[j + 1]) + np[j]++; else { - a->p[++j] = i; - while (a->sx[i] > t[j + 1]) - a->p[++j] = i; - a->np[j] = 1; + p[++j] = i; + while (sx[i] > t[j + 1]) + p[++j] = i; + np[j] = 1; } } for (i = 0; i < ints; i++) - for (j = a->p[i]; j < a->p[i] + a->np[i]; j++) + for (j = p[i]; j < p[i] + np[i]; j++) { - u = (a->sx[j] - t[i]) / a->h[i]; + u = (sx[j] - t[i]) / h[i]; v = u - 1.0; alpha = (2.0 * u + 1.0) * v * v; beta = u * u * (1.0 - 2.0 * v); - gamma = a->h[i] * u * v * v; - delta = a->h[i] * u * u * v; - a->taa[i] += alpha * alpha; - a->tab[i] += alpha * beta; - a->tag[i] += alpha * gamma; - a->tad[i] += alpha * delta; - a->tbb[i] += beta * beta; - a->tbg[i] += beta * gamma; - a->tbd[i] += beta * delta; - a->tgg[i] += gamma * gamma; - a->tgd[i] += gamma * delta; - a->tdd[i] += delta * delta; - a->D[i + 0] += 2.0 * a->sy[j] * alpha; - a->D[i + 1] += 2.0 * a->sy[j] * beta; - a->G[i + 0] += 2.0 * a->sy[j] * gamma; - a->G[i + 1] += 2.0 * a->sy[j] * delta; + gamma = h[i] * u * v * v; + delta = h[i] * u * u * v; + taa[i] += alpha * alpha; + tab[i] += alpha * beta; + tag[i] += alpha * gamma; + tad[i] += alpha * delta; + tbb[i] += beta * beta; + tbg[i] += beta * gamma; + tbd[i] += beta * delta; + tgg[i] += gamma * gamma; + tgd[i] += gamma * delta; + tdd[i] += delta * delta; + D[i + 0] += 2.0 * sy[j] * alpha; + D[i + 1] += 2.0 * sy[j] * beta; + G[i + 0] += 2.0 * sy[j] * gamma; + G[i + 1] += 2.0 * sy[j] * delta; } for (i = 0; i < ints; i++) { - a->A[(i + 0) * intp1 + (i + 0)] += 2.0 * a->taa[i]; - a->A[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbb[i]; - a->A[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tab[i]; - a->A[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tab[i]; - a->B[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tag[i]; - a->B[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tbd[i]; - a->B[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tbg[i]; - a->B[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tad[i]; - a->E[(i + 0) * intp1 + (i + 0)] += 2.0 * a->tgg[i]; - a->E[(i + 1) * intp1 + (i + 1)] = 2.0 * a->tdd[i]; - a->E[(i + 0) * intp1 + (i + 1)] = 2.0 * a->tgd[i]; - a->E[(i + 1) * intp1 + (i + 0)] = 2.0 * a->tgd[i]; + A[(i + 0) * intp1 + (i + 0)] += 2.0 * taa[i]; + A[(i + 1) * intp1 + (i + 1)] = 2.0 * tbb[i]; + A[(i + 0) * intp1 + (i + 1)] = 2.0 * tab[i]; + A[(i + 1) * intp1 + (i + 0)] = 2.0 * tab[i]; + B[(i + 0) * intp1 + (i + 0)] += 2.0 * tag[i]; + B[(i + 1) * intp1 + (i + 1)] = 2.0 * tbd[i]; + B[(i + 0) * intp1 + (i + 1)] = 2.0 * tbg[i]; + B[(i + 1) * intp1 + (i + 0)] = 2.0 * tad[i]; + E[(i + 0) * intp1 + (i + 0)] += 2.0 * tgg[i]; + E[(i + 1) * intp1 + (i + 1)] = 2.0 * tdd[i]; + E[(i + 0) * intp1 + (i + 1)] = 2.0 * tgd[i]; + E[(i + 1) * intp1 + (i + 0)] = 2.0 * tgd[i]; } for (i = 0; i < intm1; i++) { - a->C[i * intp1 + (i + 0)] = +3.0 * a->h[i + 1] / a->h[i]; - a->C[i * intp1 + (i + 2)] = -3.0 * a->h[i] / a->h[i + 1]; - a->C[i * intp1 + (i + 1)] = -a->C[i * intp1 + (i + 0)] - a->C[i * intp1 + (i + 2)]; - a->F[i * intp1 + (i + 0)] = a->h[i + 1]; - a->F[i * intp1 + (i + 1)] = 2.0 * (a->h[i] + a->h[i + 1]); - a->F[i * intp1 + (i + 2)] = a->h[i]; + C[i * intp1 + (i + 0)] = +3.0 * h[i + 1] / h[i]; + C[i * intp1 + (i + 2)] = -3.0 * h[i] / h[i + 1]; + C[i * intp1 + (i + 1)] = -C[i * intp1 + (i + 0)] - C[i * intp1 + (i + 2)]; + F[i * intp1 + (i + 0)] = h[i + 1]; + F[i * intp1 + (i + 1)] = 2.0 * (h[i] + h[i + 1]); + F[i * intp1 + (i + 2)] = h[i]; } for (i = 0, k = 0; i < intp1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->A[i * intp1 + j]; + MAT[k * nsize + m] = A[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->B[j * intp1 + i]; + MAT[k * nsize + m] = B[j * intp1 + i]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) - a->MAT[k * nsize + m] = a->C[j * intp1 + i]; - a->RHS[k] = a->D[i]; + MAT[k * nsize + m] = C[j * intp1 + i]; + RHS[k] = D[i]; } for (i = 0, k = intp1; i < intp1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->B[i * intp1 + j]; + MAT[k * nsize + m] = B[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->E[i * intp1 + j]; + MAT[k * nsize + m] = E[i * intp1 + j]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) - a->MAT[k * nsize + m] = a->F[j * intp1 + i]; - a->RHS[k] = a->G[i]; + MAT[k * nsize + m] = F[j * intp1 + i]; + RHS[k] = G[i]; } for (i = 0, k = 2 * intp1; i < intm1; i++, k++) { for (j = 0, m = 0; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->C[i * intp1 + j]; + MAT[k * nsize + m] = C[i * intp1 + j]; for (j = 0, m = intp1; j < intp1; j++, m++) - a->MAT[k * nsize + m] = a->F[i * intp1 + j]; + MAT[k * nsize + m] = F[i * intp1 + j]; for (j = 0, m = 2 * intp1; j < intm1; j++, m++) - a->MAT[k * nsize + m] = 0.0; - a->RHS[k] = 0.0; + MAT[k * nsize + m] = 0.0; + RHS[k] = 0.0; } - decomp(nsize, a->MAT, a->ipiv, &dinfo, a->wrk); - dsolve(nsize, a->MAT, a->ipiv, a->RHS, a->SLN); + decomp(nsize, MAT, ipiv, &dinfo, wrk); + dsolve(nsize, MAT, ipiv, RHS, SLN); if (dinfo != 0) { *info = dinfo; @@ -387,15 +360,15 @@ void BLDR::xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, for (i = 0; i <= ints; i++) { - a->z[i] = a->SLN[i]; - a->zp[i] = a->SLN[i + ints + 1]; + z[i] = SLN[i]; + zp[i] = SLN[i + ints + 1]; } for (i = 0; i < ints; i++) { - c[4 * i + 0] = a->z[i]; - c[4 * i + 1] = a->zp[i]; - c[4 * i + 2] = -3.0 / (a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) - 1.0 / a->h[i] * (2.0 * a->zp[i] + a->zp[i + 1]); - c[4 * i + 3] = 2.0 / (a->h[i] * a->h[i] * a->h[i]) * (a->z[i] - a->z[i + 1]) + 1.0 / (a->h[i] * a->h[i]) * (a->zp[i] + a->zp[i + 1]); + c[4 * i + 0] = z[i]; + c[4 * i + 1] = zp[i]; + c[4 * i + 2] = -3.0 / (h[i] * h[i]) * (z[i] - z[i + 1]) - 1.0 / h[i] * (2.0 * zp[i] + zp[i + 1]); + c[4 * i + 3] = 2.0 / (h[i] * h[i] * h[i]) * (z[i] - z[i + 1]) + 1.0 / (h[i] * h[i]) * (zp[i] + zp[i + 1]); } cleanup: return; diff --git a/wdsp/bldr.hpp b/wdsp/bldr.hpp index 9c643ddd9..09d2d00b5 100644 --- a/wdsp/bldr.hpp +++ b/wdsp/bldr.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_bldr_h #define wdsp_bldr_h +#include + #include "export.h" namespace WDSP { @@ -35,47 +37,50 @@ namespace WDSP { class WDSP_API BLDR { public: - float* catxy; - float* sx; - float* sy; - float* h; - int* p; - int* np; - float* taa; - float* tab; - float* tag; - float* tad; - float* tbb; - float* tbg; - float* tbd; - float* tgg; - float* tgd; - float* tdd; - float* A; - float* B; - float* C; - float* D; - float* E; - float* F; - float* G; - float* MAT; - float* RHS; - float* SLN; - float* z; - float* zp; - float* wrk; - int* ipiv; + double* catxy; + std::vector sx; + std::vector sy; + std::vector h; + std::vector p; + std::vector np; + std::vector taa; + std::vector tab; + std::vector tag; + std::vector tad; + std::vector tbb; + std::vector tbg; + std::vector tbd; + std::vector tgg; + std::vector tgd; + std::vector tdd; + std::vector A; + std::vector B; + std::vector C; + std::vector D; + std::vector E; + std::vector F; + std::vector G; + std::vector MAT; + std::vector RHS; + std::vector SLN; + std::vector z; + std::vector zp; + std::vector wrk; + std::vector ipiv; - static BLDR* create_builder(int points, int ints); - static void destroy_builder(BLDR *a); - static void flush_builder(BLDR *a, int points, int ints); - static void xbuilder(BLDR *a, int points, float* x, float* y, int ints, float* t, int* info, float* c, float ptol); + BLDR(int points, int ints); + BLDR(const BLDR&) = delete; + BLDR& operator=(const BLDR& other) = delete; + ~BLDR(); + + void flush(int points); + void execute(int points, const double* x, const double* y, int ints, const double* t, int* info, double* c, double ptol); private: static int fcompare(const void* a, const void* b); - static void decomp(int n, float* a, int* piv, int* info, float* wrk); - static void dsolve(int n, float* a, int* piv, float* b, float* x); - static void cull(int* n, int ints, float* x, float* t, float ptol); + static void decomp(int n, std::vector& a, std::vector& piv, int* info, std::vector& wrk); + static void dsolve(int n, std::vector& a, std::vector& piv, std::vector& b, std::vector& x); + static void cull(int* n, int ints, std::vector& x, const double* t, double ptol); }; } // namespace WDSP diff --git a/wdsp/bps.cpp b/wdsp/bps.cpp index 168cf95d0..af99c0e19 100644 --- a/wdsp/bps.cpp +++ b/wdsp/bps.cpp @@ -40,116 +40,116 @@ namespace WDSP { * * ********************************************************************************************************/ -void BPS::calc_bps (BPS *a) +void BPS::calc() { float* impulse; - a->infilt = new float[2 * a->size * 2]; - a->product = new float[2 * a->size * 2]; - impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults(2 * a->size, impulse); - a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT); - a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT); - delete[](impulse); + infilt.resize(2 * size * 2); + product.resize(2 * size * 2); + impulse = FIR::fir_bandpass(size + 1, f_low, f_high, samplerate, wintype, 1, 1.0 / (float)(2 * size)); + mults = FIR::fftcv_mults(2 * size, impulse); + CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) infilt.data(), (fftwf_complex *) product.data(), FFTW_FORWARD, FFTW_PATIENT); + CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) product.data(), (fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT); + delete[]impulse; } -void BPS::decalc_bps (BPS *a) +void BPS::decalc() { - fftwf_destroy_plan(a->CRev); - fftwf_destroy_plan(a->CFor); - delete[] (a->mults); - delete[] (a->product); - delete[] (a->infilt); + fftwf_destroy_plan(CRev); + fftwf_destroy_plan(CFor); + delete[] mults; } -BPS* BPS::create_bps ( - int run, - int position, - int size, - float* in, - float* out, - float f_low, - float f_high, - int samplerate, - int wintype, - float gain -) +BPS::BPS( + int _run, + int _position, + int _size, + float* _in, + float* _out, + double _f_low, + double _f_high, + int _samplerate, + int _wintype, + double _gain +) : + run(_run), + position(_position), + size(_size), + in(_in), + out(_out), + f_low(_f_low), + f_high(_f_high), + samplerate((double) _samplerate), + wintype(_wintype), + gain(_gain) { - BPS *a = new BPS; - a->run = run; - a->position = position; - a->size = size; - a->samplerate = (float)samplerate; - a->wintype = wintype; - a->gain = gain; - a->in = in; - a->out = out; - a->f_low = f_low; - a->f_high = f_high; - calc_bps (a); - return a; + calc(); } -void BPS::destroy_bps (BPS *a) +BPS::~BPS() { - decalc_bps (a); - delete a; + decalc(); } -void BPS::flush_bps (BPS *a) +void BPS::flush() { - std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0); + std::fill(infilt.begin(), infilt.end(), 0); } -void BPS::xbps (BPS *a, int pos) +void BPS::execute(int pos) { - int i; - float I, Q; - if (a->run && pos == a->position) + double I; + double Q; + if (run && pos == position) { - std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size])); - fftwf_execute (a->CFor); - for (i = 0; i < 2 * a->size; i++) + std::copy(in, in + size * 2, &(infilt[2 * size])); + fftwf_execute (CFor); + for (int i = 0; i < 2 * size; i++) { - I = a->gain * a->product[2 * i + 0]; - Q = a->gain * a->product[2 * i + 1]; - a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1]; - a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0]; + I = gain * product[2 * i + 0]; + Q = gain * product[2 * i + 1]; + product[2 * i + 0] = (float) (I * mults[2 * i + 0] - Q * mults[2 * i + 1]); + product[2 * i + 1] = (float) (I * mults[2 * i + 1] + Q * mults[2 * i + 0]); } - fftwf_execute (a->CRev); - std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt); + fftwf_execute (CRev); + std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt.begin()); } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void BPS::setBuffers_bps (BPS *a, float* in, float* out) +void BPS::setBuffers(float* _in, float* _out) { - decalc_bps (a); - a->in = in; - a->out = out; - calc_bps (a); + decalc(); + in = _in; + out = _out; + calc(); } -void BPS::setSamplerate_bps (BPS *a, int rate) +void BPS::setSamplerate(int rate) { - decalc_bps (a); - a->samplerate = rate; - calc_bps (a); + decalc(); + samplerate = rate; + calc(); } -void BPS::setSize_bps (BPS *a, int size) +void BPS::setSize(int _size) { - decalc_bps (a); - a->size = size; - calc_bps (a); + decalc(); + size = _size; + calc(); } -void BPS::setFreqs_bps (BPS *a, float f_low, float f_high) +void BPS::setFreqs(double _f_low, double _f_high) { - decalc_bps (a); - a->f_low = f_low; - a->f_high = f_high; - calc_bps (a); + decalc(); + f_low = _f_low; + f_high = _f_high; + calc(); +} + +void BPS::setRun(int _run) +{ + run = _run; } /******************************************************************************************************** @@ -158,132 +158,4 @@ void BPS::setFreqs_bps (BPS *a, float f_low, float f_high) * * ********************************************************************************************************/ -void BPS::SetBPSRun (RXA& rxa, int run) -{ - rxa.bp1->run = run; -} - -void BPS::SetBPSFreqs (RXA& rxa, float f_low, float f_high) -{ - float* impulse; - BPS *a1; - a1 = rxa.bps1; - - if ((f_low != a1->f_low) || (f_high != a1->f_high)) - { - a1->f_low = f_low; - a1->f_high = f_high; - delete[] (a1->mults); - impulse = FIR::fir_bandpass(a1->size + 1, f_low, f_high, a1->samplerate, a1->wintype, 1, 1.0 / (float)(2 * a1->size)); - a1->mults = FIR::fftcv_mults (2 * a1->size, impulse); - delete[] (impulse); - } -} - -void BPS::SetBPSWindow (RXA& rxa, int wintype) -{ - float* impulse; - BPS *a1; - a1 = rxa.bps1; - - if ((a1->wintype != wintype)) - { - a1->wintype = wintype; - delete[] (a1->mults); - impulse = FIR::fir_bandpass(a1->size + 1, a1->f_low, a1->f_high, a1->samplerate, a1->wintype, 1, 1.0 / (float)(2 * a1->size)); - a1->mults = FIR::fftcv_mults (2 * a1->size, impulse); - delete[] (impulse); - } -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ -// UNCOMMENT properties when pointers in place in txa -void BPS::SetBPSRun (TXA& txa, int run) -{ - txa.bp1->run = run; -} - -void BPS::SetBPSFreqs (TXA& txa, float f_low, float f_high) -{ - float* impulse; - BPS *a; - a = txa.bps0; - - if ((f_low != a->f_low) || (f_high != a->f_high)) - { - a->f_low = f_low; - a->f_high = f_high; - delete[] (a->mults); - impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } - - a = txa.bps1; - - if ((f_low != a->f_low) || (f_high != a->f_high)) - { - a->f_low = f_low; - a->f_high = f_high; - delete[] (a->mults); - impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } - - a = txa.bps2; - - if ((f_low != a->f_low) || (f_high != a->f_high)) - { - a->f_low = f_low; - a->f_high = f_high; - delete[] (a->mults); - impulse = FIR::fir_bandpass(a->size + 1, f_low, f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } -} - -void BPS::SetBPSWindow (TXA& txa, int wintype) -{ - float* impulse; - BPS *a; - a = txa.bps0; - - if (a->wintype != wintype) - { - a->wintype = wintype; - delete[] (a->mults); - impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } - - a = txa.bps1; - - if (a->wintype != wintype) - { - a->wintype = wintype; - delete[] (a->mults); - impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } - - a = txa.bps2; - - if (a->wintype != wintype) - { - a->wintype = wintype; - delete[] (a->mults); - impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); - } -} - } // namespace WDSP diff --git a/wdsp/bps.hpp b/wdsp/bps.hpp index b4ceb3cee..8133bef58 100644 --- a/wdsp/bps.hpp +++ b/wdsp/bps.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_bps_h #define wdsp_bps_h +#include + #include "fftw3.h" #include "export.h" @@ -50,48 +52,44 @@ public: int size; float* in; float* out; - float f_low; - float f_high; - float* infilt; - float* product; + double f_low; + double f_high; + std::vector infilt; + std::vector product; float* mults; - float samplerate; + double samplerate; int wintype; - float gain; + double gain; fftwf_plan CFor; fftwf_plan CRev; - static BPS* create_bps ( + BPS( int run, int position, int size, float* in, float* out, - float f_low, - float f_high, + double f_low, + double f_high, int samplerate, int wintype, - float gain + double gain ); - static void destroy_bps (BPS *a); - static void flush_bps (BPS *a); - static void xbps (BPS *a, int pos); - static void setBuffers_bps (BPS *a, float* in, float* out); - static void setSamplerate_bps (BPS *a, int rate); - static void setSize_bps (BPS *a, int size); - static void setFreqs_bps (BPS *a, float f_low, float f_high); - // RXA Prototypes - static void SetBPSRun (RXA& rxa, int run); - static void SetBPSFreqs (RXA& rxa, float low, float high); - static void SetBPSWindow (RXA& rxa, int wintype); - // TXA Prototypes - static void SetBPSRun (TXA& txa, int run); - static void SetBPSFreqs (TXA& txa, float low, float high); - static void SetBPSWindow (TXA& txa, int wintype); + BPS(const BPS&) = delete; + BPS& operator=(const BPS& other) = delete; + ~BPS(); + + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setFreqs(double f_low, double f_high); + void setRun(int run); private: - static void calc_bps (BPS *a); - static void decalc_bps (BPS *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/bqbp.cpp b/wdsp/bqbp.cpp index 49d8fba14..8a60e285a 100644 --- a/wdsp/bqbp.cpp +++ b/wdsp/bqbp.cpp @@ -38,7 +38,14 @@ namespace WDSP { void BQBP::calc() { - double f0, w0, bw, q, sn, cs, c, den; + double f0; + double w0; + double bw; + double q; + double sn; + double cs; + double c; + double den; bw = f_high - f_low; f0 = (f_high + f_low) / 2.0; @@ -99,15 +106,13 @@ void BQBP::execute() { if (run) { - int i, j, n; - - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { - for (j = 0; j < 2; j++) + for (int j = 0; j < 2; j++) { x0[j] = gain * in[2 * i + j]; - for (n = 0; n < nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) x0[2 * n + j] = y0[2 * (n - 1) + j]; @@ -123,7 +128,7 @@ void BQBP::execute() x1[2 * n + j] = x0[2 * n + j]; } - out[2 * i + j] = y0[2 * (nstages - 1) + j]; + out[2 * i + j] = (float) y0[2 * (nstages - 1) + j]; } } } diff --git a/wdsp/bqlp.cpp b/wdsp/bqlp.cpp index f7ba6af5f..a062af522 100644 --- a/wdsp/bqlp.cpp +++ b/wdsp/bqlp.cpp @@ -38,9 +38,12 @@ namespace WDSP { void BQLP::calc() { - double w0, cs, c, den; + double w0; + double cs; + double c; + double den; - w0 = TWOPI * fc / (double)rate; + w0 = TWOPI * fc / rate; cs = cos(w0); c = sin(w0) / (2.0 * Q); den = 1.0 + c; @@ -95,15 +98,14 @@ void BQLP::execute() { if (run) { - int i, j, n; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { - for (j = 0; j < 2; j++) + for (int j = 0; j < 2; j++) { x0[j] = gain * in[2 * i + j]; - for (n = 0; n < nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) x0[2 * n + j] = y0[2 * (n - 1) + j]; @@ -118,7 +120,7 @@ void BQLP::execute() x1[2 * n + j] = x0[2 * n + j]; } - out[2 * i + j] = y0[2 * (nstages - 1) + j]; + out[2 * i + j] = (float) y0[2 * (nstages - 1) + j]; } } } diff --git a/wdsp/bqlp.hpp b/wdsp/bqlp.hpp index 5cebf3bb1..235876b48 100644 --- a/wdsp/bqlp.hpp +++ b/wdsp/bqlp.hpp @@ -52,8 +52,17 @@ public: double Q; double gain; int nstages; - double a0, a1, a2, b1, b2; - std::vector x0, x1, x2, y0, y1, y2; + double a0; + double a1; + double a2; + double b1; + double b2; + std::vector x0; + std::vector x1; + std::vector x2; + std::vector y0; + std::vector y1; + std::vector y2; BQLP( int run, diff --git a/wdsp/cblock.cpp b/wdsp/cblock.cpp index 1548d1698..0a5ce4fac 100644 --- a/wdsp/cblock.cpp +++ b/wdsp/cblock.cpp @@ -47,15 +47,15 @@ CBL::CBL( int _mode, int _sample_rate, double _tau -) +) : + run(_run), + buff_size(_buff_size), + in_buff(_in_buff), + out_buff(_out_buff), + mode(_mode), + sample_rate((double) _sample_rate), + tau(_tau) { - run = _run; - buff_size = _buff_size; - in_buff = _in_buff; - out_buff = _out_buff; - mode = _mode; - sample_rate = (double) _sample_rate; - tau = _tau; calc(); } diff --git a/wdsp/cfcomp.cpp b/wdsp/cfcomp.cpp index 4a7e67533..c05569682 100644 --- a/wdsp/cfcomp.cpp +++ b/wdsp/cfcomp.cpp @@ -32,381 +32,384 @@ warren@wpratt.com namespace WDSP { -void CFCOMP::calc_cfcwindow (CFCOMP *a) +void CFCOMP::calc_cfcwindow() { int i; - float arg0, arg1, cgsum, igsum, coherent_gain, inherent_power_gain, wmult; - switch (a->wintype) + double arg0; + double arg1; + double cgsum; + double igsum; + double coherent_gain; + double inherent_power_gain; + double wmult; + switch (wintype) { case 0: - arg0 = 2.0 * PI / (float)a->fsize; + arg0 = 2.0 * PI / (float)fsize; cgsum = 0.0; igsum = 0.0; - for (i = 0; i < a->fsize; i++) + for (i = 0; i < fsize; i++) { - a->window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg0)); - cgsum += a->window[i]; - igsum += a->window[i] * a->window[i]; + window[i] = sqrt (0.54 - 0.46 * cos((float)i * arg0)); + cgsum += window[i]; + igsum += window[i] * window[i]; } - coherent_gain = cgsum / (float)a->fsize; - inherent_power_gain = igsum / (float)a->fsize; + coherent_gain = cgsum / (float)fsize; + inherent_power_gain = igsum / (float)fsize; wmult = 1.0 / sqrt (inherent_power_gain); - for (i = 0; i < a->fsize; i++) - a->window[i] *= wmult; - a->winfudge = sqrt (1.0 / coherent_gain); + for (i = 0; i < fsize; i++) + window[i] *= wmult; + winfudge = sqrt (1.0 / coherent_gain); break; case 1: - arg0 = 2.0 * PI / (float)a->fsize; + arg0 = 2.0 * PI / (float)fsize; cgsum = 0.0; igsum = 0.0; - for (i = 0; i < a->fsize; i++) + for (i = 0; i < fsize; i++) { arg1 = cos(arg0 * (float)i); - a->window[i] = sqrt (+0.21747 + window[i] = sqrt (+0.21747 + arg1 * (-0.45325 + arg1 * (+0.28256 + arg1 * (-0.04672)))); - cgsum += a->window[i]; - igsum += a->window[i] * a->window[i]; + cgsum += window[i]; + igsum += window[i] * window[i]; } - coherent_gain = cgsum / (float)a->fsize; - inherent_power_gain = igsum / (float)a->fsize; + coherent_gain = cgsum / (float)fsize; + inherent_power_gain = igsum / (float)fsize; wmult = 1.0 / sqrt (inherent_power_gain); - for (i = 0; i < a->fsize; i++) - a->window[i] *= wmult; - a->winfudge = sqrt (1.0 / coherent_gain); + for (i = 0; i < fsize; i++) + window[i] *= wmult; + winfudge = sqrt (1.0 / coherent_gain); + break; + default: break; } } int CFCOMP::fCOMPcompare (const void *a, const void *b) { - if (*(float*)a < *(float*)b) + if (*(double*)a < *(double*)b) return -1; - else if (*(float*)a == *(float*)b) + else if (*(double*)a == *(double*)b) return 0; else return 1; } -void CFCOMP::calc_comp (CFCOMP *a) +void CFCOMP::calc_comp() { - int i, j; - float f, frac, fincr, fmax; - float* sary; - a->precomplin = pow (10.0, 0.05 * a->precomp); - a->prepeqlin = pow (10.0, 0.05 * a->prepeq); - fmax = 0.5 * a->rate; - for (i = 0; i < a->nfreqs; i++) + int i; + int j; + double f; + double frac; + double fincr; + double fmax; + double* sary; + precomplin = pow (10.0, 0.05 * precomp); + prepeqlin = pow (10.0, 0.05 * prepeq); + fmax = 0.5 * rate; + for (i = 0; i < nfreqs; i++) { - a->F[i] = std::max (a->F[i], 0.0f); - a->F[i] = std::min (a->F[i], fmax); - a->G[i] = std::max (a->G[i], 0.0f); + F[i] = std::max (F[i], 0.0); + F[i] = std::min (F[i], fmax); + G[i] = std::max (G[i], 0.0); } - sary = new float[3 * a->nfreqs]; // (float *)malloc0 (3 * a->nfreqs * sizeof (float)); - for (i = 0; i < a->nfreqs; i++) + sary = new double[3 * nfreqs]; + for (i = 0; i < nfreqs; i++) { - sary[3 * i + 0] = a->F[i]; - sary[3 * i + 1] = a->G[i]; - sary[3 * i + 2] = a->E[i]; + sary[3 * i + 0] = F[i]; + sary[3 * i + 1] = G[i]; + sary[3 * i + 2] = E[i]; } - qsort (sary, a->nfreqs, 3 * sizeof (float), fCOMPcompare); - for (i = 0; i < a->nfreqs; i++) + qsort (sary, nfreqs, 3 * sizeof (float), fCOMPcompare); + for (i = 0; i < nfreqs; i++) { - a->F[i] = sary[3 * i + 0]; - a->G[i] = sary[3 * i + 1]; - a->E[i] = sary[3 * i + 2]; + F[i] = sary[3 * i + 0]; + G[i] = sary[3 * i + 1]; + E[i] = sary[3 * i + 2]; } - a->fp[0] = 0.0; - a->fp[a->nfreqs + 1] = fmax; - a->gp[0] = a->G[0]; - a->gp[a->nfreqs + 1] = a->G[a->nfreqs - 1]; - a->ep[0] = a->E[0]; // cutoff? - a->ep[a->nfreqs + 1] = a->E[a->nfreqs - 1]; // cutoff? - for (i = 0, j = 1; i < a->nfreqs; i++, j++) + fp[0] = 0.0; + fp[nfreqs + 1] = fmax; + gp[0] = G[0]; + gp[nfreqs + 1] = G[nfreqs - 1]; + ep[0] = E[0]; // cutoff? + ep[nfreqs + 1] = E[nfreqs - 1]; // cutoff? + for (i = 0, j = 1; i < nfreqs; i++, j++) { - a->fp[j] = a->F[i]; - a->gp[j] = a->G[i]; - a->ep[j] = a->E[i]; + fp[j] = F[i]; + gp[j] = G[i]; + ep[j] = E[i]; } - fincr = a->rate / (float)a->fsize; + fincr = rate / (float)fsize; j = 0; - // print_impulse ("gp.txt", a->nfreqs+2, a->gp, 0, 0); - for (i = 0; i < a->msize; i++) + + for (i = 0; i < msize; i++) { f = fincr * (float)i; - while (f >= a->fp[j + 1] && j < a->nfreqs) j++; - frac = (f - a->fp[j]) / (a->fp[j + 1] - a->fp[j]); - a->comp[i] = pow (10.0, 0.05 * (frac * a->gp[j + 1] + (1.0 - frac) * a->gp[j])); - a->peq[i] = pow (10.0, 0.05 * (frac * a->ep[j + 1] + (1.0 - frac) * a->ep[j])); - a->cfc_gain[i] = a->precomplin * a->comp[i]; + while (f >= fp[j + 1] && j < nfreqs) j++; + frac = (f - fp[j]) / (fp[j + 1] - fp[j]); + comp[i] = pow (10.0, 0.05 * (frac * gp[j + 1] + (1.0 - frac) * gp[j])); + peq[i] = pow (10.0, 0.05 * (frac * ep[j + 1] + (1.0 - frac) * ep[j])); + cfc_gain[i] = precomplin * comp[i]; } - // print_impulse ("comp.txt", a->msize, a->comp, 0, 0); + delete[] sary; } -void CFCOMP::calc_cfcomp(CFCOMP *a) +void CFCOMP::calc_cfcomp() { - int i; - a->incr = a->fsize / a->ovrlp; - if (a->fsize > a->bsize) - a->iasize = a->fsize; + incr = fsize / ovrlp; + if (fsize > bsize) + iasize = fsize; else - a->iasize = a->bsize + a->fsize - a->incr; - a->iainidx = 0; - a->iaoutidx = 0; - if (a->fsize > a->bsize) + iasize = bsize + fsize - incr; + iainidx = 0; + iaoutidx = 0; + if (fsize > bsize) { - if (a->bsize > a->incr) a->oasize = a->bsize; - else a->oasize = a->incr; - a->oainidx = (a->fsize - a->bsize - a->incr) % a->oasize; + if (bsize > incr) oasize = bsize; + else oasize = incr; + oainidx = (fsize - bsize - incr) % oasize; } else { - a->oasize = a->bsize; - a->oainidx = a->fsize - a->incr; + oasize = bsize; + oainidx = fsize - incr; } - a->init_oainidx = a->oainidx; - a->oaoutidx = 0; - a->msize = a->fsize / 2 + 1; - a->window = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float)); - a->inaccum = new float[a->iasize]; // (float *)malloc0 (a->iasize * sizeof(float)); - a->forfftin = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float)); - a->forfftout = new float[a->msize * 2]; // (float *)malloc0 (a->msize * sizeof(complex)); - a->cmask = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float)); - a->mask = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float)); - a->cfc_gain = new float[a->msize]; // (float *)malloc0 (a->msize * sizeof(float)); - a->revfftin = new float[a->msize * 2]; // (float *)malloc0 (a->msize * sizeof(complex)); - a->revfftout = new float[a->fsize]; // (float *)malloc0 (a->fsize * sizeof(float)); - a->save = new float*[a->ovrlp]; // (float **)malloc0(a->ovrlp * sizeof(float *)); - for (i = 0; i < a->ovrlp; i++) - a->save[i] = new float[a->fsize]; // (float *)malloc0(a->fsize * sizeof(float)); - a->outaccum = new float[a->oasize]; // (float *)malloc0(a->oasize * sizeof(float)); - a->nsamps = 0; - a->saveidx = 0; - a->Rfor = fftwf_plan_dft_r2c_1d(a->fsize, a->forfftin, (fftwf_complex *)a->forfftout, FFTW_ESTIMATE); - a->Rrev = fftwf_plan_dft_c2r_1d(a->fsize, (fftwf_complex *)a->revfftin, a->revfftout, FFTW_ESTIMATE); - calc_cfcwindow(a); + init_oainidx = oainidx; + oaoutidx = 0; + msize = fsize / 2 + 1; + window.resize(fsize); + inaccum.resize(iasize); + forfftin.resize(fsize); + forfftout.resize(msize * 2); + cmask.resize(msize); + mask.resize(msize); + cfc_gain.resize(msize); + revfftin.resize(msize * 2); + revfftout.resize(fsize); + save.resize(ovrlp); + for (int i = 0; i < ovrlp; i++) + save[i].resize(fsize); + outaccum.resize(oasize); + nsamps = 0; + saveidx = 0; + Rfor = fftwf_plan_dft_r2c_1d(fsize, forfftin.data(), (fftwf_complex *)forfftout.data(), FFTW_ESTIMATE); + Rrev = fftwf_plan_dft_c2r_1d(fsize, (fftwf_complex *)revfftin.data(), revfftout.data(), FFTW_ESTIMATE); + calc_cfcwindow(); - a->pregain = (2.0 * a->winfudge) / (float)a->fsize; - a->postgain = 0.5 / ((float)a->ovrlp * a->winfudge); + pregain = (2.0 * winfudge) / (double)fsize; + postgain = 0.5 / ((double)ovrlp * winfudge); - a->fp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - a->gp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - a->ep = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - a->comp = new float[a->msize]; // (float *) malloc0 (a->msize * sizeof (float)); - a->peq = new float[a->msize]; // (float *) malloc0 (a->msize * sizeof (float)); - calc_comp (a); + fp.resize(nfreqs + 2); + gp.resize(nfreqs + 2); + ep.resize(nfreqs + 2); + comp.resize(msize); + peq.resize(msize); + calc_comp(); - a->gain = 0.0; - a->mmult = exp (-1.0 / (a->rate * a->ovrlp * a->mtau)); - a->dmult = exp (-(float)a->fsize / (a->rate * a->ovrlp * a->dtau)); + gain = 0.0; + mmult = exp (-1.0 / (rate * ovrlp * mtau)); + dmult = exp (-(float)fsize / (rate * ovrlp * dtau)); - a->delta = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float)); - a->delta_copy = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float)); - a->cfc_gain_copy = new float[a->msize]; // (float*)malloc0 (a->msize * sizeof(float)); + delta.resize(msize); + delta_copy.resize(msize); + cfc_gain_copy.resize(msize); } -void CFCOMP::decalc_cfcomp(CFCOMP *a) +void CFCOMP::decalc_cfcomp() +{ + fftwf_destroy_plan(Rrev); + fftwf_destroy_plan(Rfor); +} + +CFCOMP::CFCOMP( + int _run, + int _position, + int _peq_run, + int _size, + float* _in, + float* _out, + int _fsize, + int _ovrlp, + int _rate, + int _wintype, + int _comp_method, + int _nfreqs, + double _precomp, + double _prepeq, + const double* _F, + const double* _G, + const double* _E, + double _mtau, + double _dtau +) : + run (_run), + position(_position), + bsize(_size), + in(_in), + out(_out), + fsize(_fsize), + ovrlp(_ovrlp), + rate(_rate), + wintype(_wintype), + comp_method(_comp_method), + nfreqs(_nfreqs), + precomp(_precomp), + peq_run(_peq_run), + prepeq(_prepeq), + mtau(_mtau), // compression metering time constant + dtau(_dtau) // compression display time constant +{ + F.resize(nfreqs); + G.resize(nfreqs); + E.resize(nfreqs); + std::copy(_F, _F + nfreqs, F.begin()); + std::copy(_G, _G + nfreqs, G.begin()); + std::copy(_E, _E + nfreqs, E.begin()); + calc_cfcomp(); +} + +CFCOMP::~CFCOMP() +{ + decalc_cfcomp(); +} + +void CFCOMP::flush() +{ + std::fill(inaccum.begin(), inaccum.end(), 0); + for (int i = 0; i < ovrlp; i++) + std::fill(save[i].begin(), save[i].end(), 0); + std::fill(outaccum.begin(), outaccum.end(), 0); + nsamps = 0; + iainidx = 0; + iaoutidx = 0; + oainidx = init_oainidx; + oaoutidx = 0; + saveidx = 0; + gain = 0.0; + std::fill(delta.begin(), delta.end(), 0); +} + + + +void CFCOMP::calc_mask() { int i; - delete[] (a->cfc_gain_copy); - delete[] (a->delta_copy); - delete[] (a->delta); - delete[] (a->peq); - delete[] (a->comp); - delete[] (a->ep); - delete[] (a->gp); - delete[] (a->fp); - - fftwf_destroy_plan(a->Rrev); - fftwf_destroy_plan(a->Rfor); - delete[](a->outaccum); - for (i = 0; i < a->ovrlp; i++) - delete[](a->save[i]); - delete[](a->save); - delete[](a->revfftout); - delete[](a->revfftin); - delete[](a->cfc_gain); - delete[](a->mask); - delete[](a->cmask); - delete[](a->forfftout); - delete[](a->forfftin); - delete[](a->inaccum); - delete[](a->window); -} - -CFCOMP* CFCOMP::create_cfcomp (int run, int position, int peq_run, int size, float* in, float* out, int fsize, int ovrlp, - int rate, int wintype, int comp_method, int nfreqs, float precomp, float prepeq, float* F, float* G, float* E, float mtau, float dtau) -{ - CFCOMP *a = new CFCOMP; - a->run = run; - a->position = position; - a->peq_run = peq_run; - a->bsize = size; - a->in = in; - a->out = out; - a->fsize = fsize; - a->ovrlp = ovrlp; - a->rate = rate; - a->wintype = wintype; - a->comp_method = comp_method; - a->nfreqs = nfreqs; - a->precomp = precomp; - a->prepeq = prepeq; - a->mtau = mtau; // compression metering time constant - a->dtau = dtau; // compression display time constant - a->F = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - a->G = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - a->E = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - memcpy (a->F, F, a->nfreqs * sizeof (float)); - memcpy (a->G, G, a->nfreqs * sizeof (float)); - memcpy (a->E, E, a->nfreqs * sizeof (float)); - calc_cfcomp (a); - return a; -} - -void CFCOMP::flush_cfcomp (CFCOMP *a) -{ - int i; - memset (a->inaccum, 0, a->iasize * sizeof (float)); - for (i = 0; i < a->ovrlp; i++) - memset (a->save[i], 0, a->fsize * sizeof (float)); - memset (a->outaccum, 0, a->oasize * sizeof (float)); - a->nsamps = 0; - a->iainidx = 0; - a->iaoutidx = 0; - a->oainidx = a->init_oainidx; - a->oaoutidx = 0; - a->saveidx = 0; - a->gain = 0.0; - memset(a->delta, 0, a->msize * sizeof(float)); -} - -void CFCOMP::destroy_cfcomp (CFCOMP *a) -{ - decalc_cfcomp (a); - delete[] (a->E); - delete[] (a->G); - delete[] (a->F); - delete (a); -} - - -void CFCOMP::calc_mask (CFCOMP *a) -{ - int i; - float comp, mask, delta; - switch (a->comp_method) + double _comp; + double _mask; + double _delta; + if (comp_method == 0) { - case 0: + double mag; + double test; + for (i = 0; i < msize; i++) { - float mag, test; - for (i = 0; i < a->msize; i++) - { - mag = sqrt (a->forfftout[2 * i + 0] * a->forfftout[2 * i + 0] - + a->forfftout[2 * i + 1] * a->forfftout[2 * i + 1]); - comp = a->cfc_gain[i]; - test = comp * mag; - if (test > 1.0) - mask = 1.0 / mag; - else - mask = comp; - a->cmask[i] = mask; - if (test > a->gain) a->gain = test; - else a->gain = a->mmult * a->gain; + mag = sqrt (forfftout[2 * i + 0] * forfftout[2 * i + 0] + + forfftout[2 * i + 1] * forfftout[2 * i + 1]); + _comp = cfc_gain[i]; + test = _comp * mag; + if (test > 1.0) + _mask = 1.0 / mag; + else + _mask = _comp; + cmask[i] = _mask; + if (test > gain) gain = test; + else gain = mmult * gain; - delta = a->cfc_gain[i] - a->cmask[i]; - if (delta > a->delta[i]) a->delta[i] = delta; - else a->delta[i] *= a->dmult; + _delta = cfc_gain[i] - cmask[i]; + if (_delta > delta[i]) delta[i] = _delta; + else delta[i] *= dmult; + } + } + if (peq_run) + { + for (i = 0; i < msize; i++) + { + mask[i] = cmask[i] * prepeqlin * peq[i]; + } + } + else + std::copy(cmask.begin(), cmask.end(), mask.begin()); + mask_ready = 1; +} + +void CFCOMP::execute(int pos) +{ + if (run && pos == position) + { + int i; + int j; + int k; + int sbuff; + int sbegin; + for (i = 0; i < 2 * bsize; i += 2) + { + inaccum[iainidx] = in[i]; + iainidx = (iainidx + 1) % iasize; + } + nsamps += bsize; + while (nsamps >= fsize) + { + for (i = 0, j = iaoutidx; i < fsize; i++, j = (j + 1) % iasize) + forfftin[i] = (float) (pregain * window[i] * inaccum[j]); + iaoutidx = (iaoutidx + incr) % iasize; + nsamps -= incr; + fftwf_execute (Rfor); + calc_mask(); + for (i = 0; i < msize; i++) + { + revfftin[2 * i + 0] = (float) (mask[i] * forfftout[2 * i + 0]); + revfftin[2 * i + 1] = (float) (mask[i] * forfftout[2 * i + 1]); } - break; - } - } - if (a->peq_run) - { - for (i = 0; i < a->msize; i++) - { - a->mask[i] = a->cmask[i] * a->prepeqlin * a->peq[i]; - } - } - else - memcpy (a->mask, a->cmask, a->msize * sizeof (float)); - // print_impulse ("mask.txt", a->msize, a->mask, 0, 0); - a->mask_ready = 1; -} - -void CFCOMP::xcfcomp (CFCOMP *a, int pos) -{ - if (a->run && pos == a->position) - { - int i, j, k, sbuff, sbegin; - for (i = 0; i < 2 * a->bsize; i += 2) - { - a->inaccum[a->iainidx] = a->in[i]; - a->iainidx = (a->iainidx + 1) % a->iasize; - } - a->nsamps += a->bsize; - while (a->nsamps >= a->fsize) - { - for (i = 0, j = a->iaoutidx; i < a->fsize; i++, j = (j + 1) % a->iasize) - a->forfftin[i] = a->pregain * a->window[i] * a->inaccum[j]; - a->iaoutidx = (a->iaoutidx + a->incr) % a->iasize; - a->nsamps -= a->incr; - fftwf_execute (a->Rfor); - calc_mask(a); - for (i = 0; i < a->msize; i++) + fftwf_execute (Rrev); + for (i = 0; i < fsize; i++) + save[saveidx][i] = postgain * window[i] * revfftout[i]; + for (i = ovrlp; i > 0; i--) { - a->revfftin[2 * i + 0] = a->mask[i] * a->forfftout[2 * i + 0]; - a->revfftin[2 * i + 1] = a->mask[i] * a->forfftout[2 * i + 1]; - } - fftwf_execute (a->Rrev); - for (i = 0; i < a->fsize; i++) - a->save[a->saveidx][i] = a->postgain * a->window[i] * a->revfftout[i]; - for (i = a->ovrlp; i > 0; i--) - { - sbuff = (a->saveidx + i) % a->ovrlp; - sbegin = a->incr * (a->ovrlp - i); - for (j = sbegin, k = a->oainidx; j < a->incr + sbegin; j++, k = (k + 1) % a->oasize) + sbuff = (saveidx + i) % ovrlp; + sbegin = incr * (ovrlp - i); + for (j = sbegin, k = oainidx; j < incr + sbegin; j++, k = (k + 1) % oasize) { - if ( i == a->ovrlp) - a->outaccum[k] = a->save[sbuff][j]; + if ( i == ovrlp) + outaccum[k] = save[sbuff][j]; else - a->outaccum[k] += a->save[sbuff][j]; + outaccum[k] += save[sbuff][j]; } } - a->saveidx = (a->saveidx + 1) % a->ovrlp; - a->oainidx = (a->oainidx + a->incr) % a->oasize; + saveidx = (saveidx + 1) % ovrlp; + oainidx = (oainidx + incr) % oasize; } - for (i = 0; i < a->bsize; i++) + for (i = 0; i < bsize; i++) { - a->out[2 * i + 0] = a->outaccum[a->oaoutidx]; - a->out[2 * i + 1] = 0.0; - a->oaoutidx = (a->oaoutidx + 1) % a->oasize; + out[2 * i + 0] = (float) (outaccum[oaoutidx]); + out[2 * i + 1] = 0.0; + oaoutidx = (oaoutidx + 1) % oasize; } } - else if (a->out != a->in) - std::copy(a->in, a->in + a->bsize * 2, a->out); + else if (out != in) + std::copy(in, in + bsize * 2, out); } -void CFCOMP::setBuffers_cfcomp (CFCOMP *a, float* in, float* out) +void CFCOMP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void CFCOMP::setSamplerate_cfcomp (CFCOMP *a, int rate) +void CFCOMP::setSamplerate(int _rate) { - decalc_cfcomp (a); - a->rate = rate; - calc_cfcomp (a); + decalc_cfcomp(); + rate = _rate; + calc_cfcomp(); } -void CFCOMP::setSize_cfcomp (CFCOMP *a, int size) +void CFCOMP::setSize(int size) { - decalc_cfcomp (a); - a->bsize = size; - calc_cfcomp (a); + decalc_cfcomp(); + bsize = size; + calc_cfcomp(); } /******************************************************************************************************** @@ -415,94 +418,75 @@ void CFCOMP::setSize_cfcomp (CFCOMP *a, int size) * * ********************************************************************************************************/ -void CFCOMP::SetCFCOMPRun (TXA& txa, int run) +void CFCOMP::setRun(int _run) { - CFCOMP *a = txa.cfcomp; - - if (a->run != run) { - a->run = run; + if (run != _run) { + run = _run; } } -void CFCOMP::SetCFCOMPPosition (TXA& txa, int pos) +void CFCOMP::setPosition(int pos) { - CFCOMP *a = txa.cfcomp; - - if (a->position != pos) { - a->position = pos; + if (position != pos) { + position = pos; } } -void CFCOMP::SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float *E) +void CFCOMP::setProfile(int _nfreqs, const double* _F, const double* _G, const double* _E) { - CFCOMP *a = txa.cfcomp; - a->nfreqs = nfreqs < 1 ? 1 : nfreqs; - delete[] (a->E); - delete[] (a->F); - delete[] (a->G); - a->F = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - a->G = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - a->E = new float[a->nfreqs]; // (float *)malloc0 (a->nfreqs * sizeof (float)); - memcpy (a->F, F, a->nfreqs * sizeof (float)); - memcpy (a->G, G, a->nfreqs * sizeof (float)); - memcpy (a->E, E, a->nfreqs * sizeof (float)); - delete[] (a->ep); - delete[] (a->gp); - delete[] (a->fp); - a->fp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - a->gp = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - a->ep = new float[a->nfreqs + 2]; // (float *) malloc0 ((a->nfreqs + 2) * sizeof (float)); - calc_comp(a); + nfreqs = _nfreqs < 1 ? 1 : _nfreqs; + F.resize(nfreqs); + G.resize(nfreqs); + E.resize(nfreqs); + std::copy(_F, _F + nfreqs, F.begin()); + std::copy(_G, _G + nfreqs, G.begin()); + std::copy(_E, _E + nfreqs, E.begin()); + fp.resize(nfreqs + 2); + gp.resize(nfreqs + 2); + ep.resize(nfreqs + 2); + calc_comp(); } -void CFCOMP::SetCFCOMPPrecomp (TXA& txa, float precomp) +void CFCOMP::setPrecomp(double _precomp) { - CFCOMP *a = txa.cfcomp; - - if (a->precomp != precomp) + if (precomp != _precomp) { - a->precomp = precomp; - a->precomplin = pow (10.0, 0.05 * a->precomp); + precomp = _precomp; + precomplin = pow (10.0, 0.05 * precomp); - for (int i = 0; i < a->msize; i++) + for (int i = 0; i < msize; i++) { - a->cfc_gain[i] = a->precomplin * a->comp[i]; + cfc_gain[i] = precomplin * comp[i]; } } } -void CFCOMP::SetCFCOMPPeqRun (TXA& txa, int run) +void CFCOMP::setPeqRun(int _run) { - CFCOMP *a = txa.cfcomp; - - if (a->peq_run != run) { - a->peq_run = run; + if (peq_run != _run) { + peq_run = _run; } } -void CFCOMP::SetCFCOMPPrePeq (TXA& txa, float prepeq) +void CFCOMP::setPrePeq(double _prepeq) { - CFCOMP *a = txa.cfcomp; - a->prepeq = prepeq; - a->prepeqlin = pow (10.0, 0.05 * a->prepeq); + prepeq = _prepeq; + prepeqlin = pow (10.0, 0.05 * prepeq); } -void CFCOMP::GetCFCOMPDisplayCompression (TXA& txa, float* comp_values, int* ready) +void CFCOMP::getDisplayCompression(double* comp_values, int* ready) { - int i; - CFCOMP *a = txa.cfcomp; - - if ((*ready = a->mask_ready)) + if ((*ready = mask_ready)) { - memcpy(a->delta_copy, a->delta, a->msize * sizeof(float)); - memcpy(a->cfc_gain_copy, a->cfc_gain, a->msize * sizeof(float)); - a->mask_ready = 0; + std::copy(delta.begin(), delta.end(), delta_copy.begin()); + std::copy(cfc_gain.begin(), cfc_gain.end(), cfc_gain_copy.begin()); + mask_ready = 0; } if (*ready) { - for (i = 0; i < a->msize; i++) - comp_values[i] = 20.0 * MemLog::mlog10 (a->cfc_gain_copy[i] / (a->cfc_gain_copy[i] - a->delta_copy[i])); + for (int i = 0; i < msize; i++) + comp_values[i] = 20.0 * MemLog::mlog10 (cfc_gain_copy[i] / (cfc_gain_copy[i] - delta_copy[i])); } } diff --git a/wdsp/cfcomp.hpp b/wdsp/cfcomp.hpp index a0e043b4c..1f917f929 100644 --- a/wdsp/cfcomp.hpp +++ b/wdsp/cfcomp.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_cfcomp_h #define wdsp_cfcomp_h +#include + #include "fftw3.h" #include "export.h" @@ -46,25 +48,25 @@ public: int fsize; int ovrlp; int incr; - float* window; + std::vector window; int iasize; - float* inaccum; - float* forfftin; - float* forfftout; + std::vector inaccum; + std::vector forfftin; + std::vector forfftout; int msize; - float* cmask; - float* mask; + std::vector cmask; + std::vector mask; int mask_ready; - float* cfc_gain; - float* revfftin; - float* revfftout; - float** save; + std::vector cfc_gain; + std::vector revfftin; + std::vector revfftout; + std::vector> save; int oasize; - float* outaccum; - float rate; + std::vector outaccum; + double rate; int wintype; - float pregain; - float postgain; + double pregain; + double postgain; int nsamps; int iainidx; int iaoutidx; @@ -77,32 +79,32 @@ public: int comp_method; int nfreqs; - float* F; - float* G; - float* E; - float* fp; - float* gp; - float* ep; - float* comp; - float precomp; - float precomplin; - float* peq; + std::vector F; + std::vector G; + std::vector E; + std::vector fp; + std::vector gp; + std::vector ep; + std::vector comp; + double precomp; + double precomplin; + std::vector peq; int peq_run; - float prepeq; - float prepeqlin; - float winfudge; + double prepeq; + double prepeqlin; + double winfudge; - float gain; - float mtau; - float mmult; + double gain; + double mtau; + double mmult; // display stuff - float dtau; - float dmult; - float* delta; - float* delta_copy; - float* cfc_gain_copy; + double dtau; + double dmult; + std::vector delta; + std::vector delta_copy; + std::vector cfc_gain_copy; - static CFCOMP* create_cfcomp ( + CFCOMP( int run, int position, int peq_run, @@ -115,36 +117,39 @@ public: int wintype, int comp_method, int nfreqs, - float precomp, - float prepeq, - float* F, - float* G, - float* E, - float mtau, - float dtau + double precomp, + double prepeq, + const double* F, + const double* G, + const double* E, + double mtau, + double dtau ); - static void destroy_cfcomp (CFCOMP *a); - static void flush_cfcomp (CFCOMP *a); - static void xcfcomp (CFCOMP *a, int pos); - static void setBuffers_cfcomp (CFCOMP *a, float* in, float* out); - static void setSamplerate_cfcomp (CFCOMP *a, int rate); - static void setSize_cfcomp (CFCOMP *a, int size); + CFCOMP(const CFCOMP&) = delete; + CFCOMP& operator=(CFCOMP& other) = delete; + ~CFCOMP(); + + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetCFCOMPRun (TXA& txa, int run); - static void SetCFCOMPPosition (TXA& txa, int pos); - static void SetCFCOMPprofile (TXA& txa, int nfreqs, float* F, float* G, float *E); - static void SetCFCOMPPrecomp (TXA& txa, float precomp); - static void SetCFCOMPPeqRun (TXA& txa, int run); - static void SetCFCOMPPrePeq (TXA& txa, float prepeq); - static void GetCFCOMPDisplayCompression (TXA& txa, float* comp_values, int* ready); + void setRun(int run); + void setPosition(int pos); + void setProfile(int nfreqs, const double* F, const double* G, const double *E); + void setPrecomp(double precomp); + void setPeqRun(int run); + void setPrePeq(double prepeq); + void getDisplayCompression(double* comp_values, int* ready); private: - static void calc_cfcwindow (CFCOMP *a); + void calc_cfcwindow(); static int fCOMPcompare (const void *a, const void *b); - static void calc_comp (CFCOMP *a); - static void calc_cfcomp(CFCOMP *a); - static void decalc_cfcomp(CFCOMP *a); - static void calc_mask (CFCOMP *a); + void calc_comp(); + void calc_cfcomp(); + void decalc_cfcomp(); + void calc_mask(); }; } // namespace WDSP diff --git a/wdsp/compress.cpp b/wdsp/compress.cpp index 5a8d2a556..a36e32927 100644 --- a/wdsp/compress.cpp +++ b/wdsp/compress.cpp @@ -35,13 +35,14 @@ in the January 2010 issue of RadCom magazine. namespace WDSP { COMPRESSOR* COMPRESSOR::create_compressor ( - int run, - int buffsize, - float* inbuff, - float* outbuff, - float gain ) + int run, + int buffsize, + float* inbuff, + float* outbuff, + double gain +) { - COMPRESSOR *a = new COMPRESSOR; + auto *a = new COMPRESSOR; a->run = run; a->inbuff = inbuff; a->outbuff = outbuff; @@ -61,10 +62,9 @@ void COMPRESSOR::flush_compressor (COMPRESSOR *) void COMPRESSOR::xcompressor (COMPRESSOR *a) { - int i; float mag; if (a->run) - for (i = 0; i < a->buffsize; i++) + for (int i = 0; i < a->buffsize; i++) { mag = sqrt(a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] + a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]); if (a->gain * mag > 1.0) diff --git a/wdsp/compress.hpp b/wdsp/compress.hpp index 392072094..dc134bb58 100644 --- a/wdsp/compress.hpp +++ b/wdsp/compress.hpp @@ -41,14 +41,14 @@ public: int buffsize; float *inbuff; float *outbuff; - float gain; + double gain; static COMPRESSOR* create_compressor ( int run, int buffsize, float* inbuff, float* outbuff, - float gain + double gain ); static void destroy_compressor (COMPRESSOR *a); static void flush_compressor (COMPRESSOR *a); diff --git a/wdsp/dbqbp.cpp b/wdsp/dbqbp.cpp index a600f6b83..86856acd5 100644 --- a/wdsp/dbqbp.cpp +++ b/wdsp/dbqbp.cpp @@ -38,7 +38,14 @@ namespace WDSP { void DBQBP::calc() { - double f0, w0, bw, q, sn, cs, c, den; + double f0; + double w0; + double bw; + double q; + double sn; + double cs; + double c; + double den; bw = f_high - f_low; f0 = (f_high + f_low) / 2.0; @@ -98,13 +105,12 @@ void DBQBP::execute() { if (run) { - int i, n; - for (i = 0; i < size; i++) + for (int i = 0; i < size; i++) { x0[0] = gain * in[i]; - for (n = 0; n < nstages; n++) + for (int n = 0; n < nstages; n++) { if (n > 0) x0[n] = y0[n - 1]; @@ -120,7 +126,7 @@ void DBQBP::execute() x1[n] = x0[n]; } - out[i] = y0[nstages - 1]; + out[i] = (float) y0[nstages - 1]; } } else if (out != in) diff --git a/wdsp/dbqbp.hpp b/wdsp/dbqbp.hpp index e043426fb..0f1083f40 100644 --- a/wdsp/dbqbp.hpp +++ b/wdsp/dbqbp.hpp @@ -52,8 +52,17 @@ public: double f_high; double gain; int nstages; - double a0, a1, a2, b1, b2; - std::vector x0, x1, x2, y0, y1, y2; + double a0; + double a1; + double a2; + double b1; + double b2; + std::vector x0; + std::vector x1; + std::vector x2; + std::vector y0; + std::vector y1; + std::vector y2; // Double Bi-Quad Band-Pass DBQBP( diff --git a/wdsp/dsphp.cpp b/wdsp/dsphp.cpp index 4c6ad9981..62b74dfda 100644 --- a/wdsp/dsphp.cpp +++ b/wdsp/dsphp.cpp @@ -57,15 +57,15 @@ DSPHP::DSPHP( double _rate, double _fc, int _nstages -) +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + fc(_fc), + nstages(_nstages) { - run = _run; - size = _size; - in = _in; - out = _out; - rate = _rate; - fc = _fc; - nstages = _nstages; calc(); } diff --git a/wdsp/emnr.cpp b/wdsp/emnr.cpp index 6baca2b2c..27c656710 100644 --- a/wdsp/emnr.cpp +++ b/wdsp/emnr.cpp @@ -118,7 +118,9 @@ EMNR::NP::NP( rate(_rate), msize(_msize), lambda_y(_lambda_y), - lambda_d(_lambda_d) + lambda_d(_lambda_d), + invQeqMax(0.5), + av(2.12) { double tau0 = -128.0 / 8000.0 / log(0.7); @@ -132,8 +134,6 @@ EMNR::NP::NP( snrq = -incr / (0.064 * rate); double tau4 = -128.0 / 8000.0 / log(0.8); betamax = exp(-incr / rate / tau4); - invQeqMax = 0.5; - av = 2.12; Dtime = 8.0 * 12.0 * 128.0 / 8000.0; U = 8; V = (int)(0.5 + (Dtime * rate / (U * incr))); diff --git a/wdsp/emph.cpp b/wdsp/emph.cpp index 90498c1e7..552ac1539 100644 --- a/wdsp/emph.cpp +++ b/wdsp/emph.cpp @@ -33,239 +33,117 @@ warren@wpratt.com namespace WDSP { -/******************************************************************************************************** -* * -* Partitioned Overlap-Save FM Pre-Emphasis * -* * -********************************************************************************************************/ - -EMPHP* EMPHP::create_emphp (int run, int position, int size, int nc, int mp, float* in, float* out, int rate, int ctype, float f_low, float f_high) -{ - EMPHP *a = new EMPHP; - float* impulse; - a->run = run; - a->position = position; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->rate = rate; - a->ctype = ctype; - a->f_low = f_low; - a->f_high = f_high; - impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse); - delete[] (impulse); - return a; -} - -void EMPHP::destroy_emphp (EMPHP *a) -{ - FIRCORE::destroy_fircore (a->p); - delete (a); -} - -void EMPHP::flush_emphp (EMPHP *a) -{ - FIRCORE::flush_fircore (a->p); -} - -void EMPHP::xemphp (EMPHP *a, int position) -{ - if (a->run && a->position == position) - FIRCORE::xfircore (a->p); - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); -} - -void EMPHP::setBuffers_emphp (EMPHP *a, float* in, float* out) -{ - a->in = in; - a->out = out; - FIRCORE::setBuffers_fircore (a->p, a->in, a->out); -} - -void EMPHP::setSamplerate_emphp (EMPHP *a, int rate) -{ - float* impulse; - a->rate = rate; - impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -void EMPHP::setSize_emphp (EMPHP *a, int size) -{ - float* impulse; - a->size = size; - FIRCORE::setSize_fircore (a->p, a->size); - impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); -} - -/******************************************************************************************************** -* * -* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties * -* * -********************************************************************************************************/ - -void EMPHP::SetFMEmphPosition (TXA& txa, int position) -{ - txa.preemph->position = position; -} - -void EMPHP::SetFMEmphMP (TXA& txa, int mp) -{ - EMPHP *a; - a = txa.preemph; - if (a->mp != mp) - { - a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); - } -} - -void EMPHP::SetFMEmphNC (TXA& txa, int nc) -{ - EMPHP *a; - float* impulse; - a = txa.preemph; - - if (a->nc != nc) - { - a->nc = nc; - impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); - delete[] (impulse); - } -} - -void EMPHP::SetFMPreEmphFreqs (TXA& txa, float low, float high) -{ - EMPHP *a; - float* impulse; - a = txa.preemph; - - if (a->f_low != low || a->f_high != high) - { - a->f_low = low; - a->f_high = high; - impulse = FCurve::fc_impulse (a->nc, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); - delete[] (impulse); - } -} - /******************************************************************************************************** * * * Overlap-Save FM Pre-Emphasis * * * ********************************************************************************************************/ -void EMPH::calc_emph (EMPH *a) +void EMPH::calc() { - a->infilt = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); - a->product = new float[2 * a->size * 2]; // (float *)malloc0(2 * a->size * sizeof(complex)); - a->mults = FCurve::fc_mults(a->size, a->f_low, a->f_high, -20.0 * log10(a->f_high / a->f_low), 0.0, a->ctype, a->rate, 1.0 / (2.0 * a->size), 0, 0); - a->CFor = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->infilt, (fftwf_complex *)a->product, FFTW_FORWARD, FFTW_PATIENT); - a->CRev = fftwf_plan_dft_1d(2 * a->size, (fftwf_complex *)a->product, (fftwf_complex *)a->out, FFTW_BACKWARD, FFTW_PATIENT); + infilt = new float[2 * size * 2]; + product = new float[2 * size * 2]; + mults = FCurve::fc_mults( + size, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, + 0 + ); + CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *)infilt, (fftwf_complex *)product, FFTW_FORWARD, FFTW_PATIENT); + CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *)product, (fftwf_complex *)out, FFTW_BACKWARD, FFTW_PATIENT); } -void EMPH::decalc_emph (EMPH *a) +void EMPH::decalc() { - fftwf_destroy_plan(a->CRev); - fftwf_destroy_plan(a->CFor); - delete[] (a->mults); - delete[] (a->product); - delete[] (a->infilt); + fftwf_destroy_plan(CRev); + fftwf_destroy_plan(CFor); + delete[] mults; + delete[] product; + delete[] infilt; } -EMPH* EMPH::create_emph (int run, int position, int size, float* in, float* out, int rate, int ctype, float f_low, float f_high) +EMPH::EMPH( + int _run, + int _position, + int _size, + float* _in, + float* _out, + int _rate, + int _ctype, + double _f_low, + double _f_high +) : + run(_run), + position(_position), + size(_size), + in(_in), + out(_out), + ctype(_ctype), + f_low(_f_low), + f_high(_f_high), + rate((double) _rate) { - EMPH *a = new EMPH; - a->run = run; - a->position = position; - a->size = size; - a->in = in; - a->out = out; - a->rate = (float)rate; - a->ctype = ctype; - a->f_low = f_low; - a->f_high = f_high; - calc_emph (a); - return a; + calc(); } -void EMPH::destroy_emph (EMPH *a) +EMPH::~EMPH() { - decalc_emph (a); - delete (a); + decalc(); } -void EMPH::flush_emph (EMPH *a) +void EMPH::flush() { - std::fill(a->infilt, a->infilt + 2 * a->size * 2, 0); + std::fill(infilt, infilt + 2 * size * 2, 0); } -void EMPH::xemph (EMPH *a, int position) +void EMPH::execute(int _position) { - int i; - float I, Q; - if (a->run && a->position == position) + double I; + double Q; + if (run && position == _position) { - std::copy(a->in, a->in + a->size * 2, &(a->infilt[2 * a->size])); - fftwf_execute (a->CFor); - for (i = 0; i < 2 * a->size; i++) + std::copy(in, in + size * 2, &(infilt[2 * size])); + fftwf_execute (CFor); + for (int i = 0; i < 2 * size; i++) { - I = a->product[2 * i + 0]; - Q = a->product[2 * i + 1]; - a->product[2 * i + 0] = I * a->mults[2 * i + 0] - Q * a->mults[2 * i + 1]; - a->product[2 * i + 1] = I * a->mults[2 * i + 1] + Q * a->mults[2 * i + 0]; + I = product[2 * i + 0]; + Q = product[2 * i + 1]; + product[2 * i + 0] = (float) (I * mults[2 * i + 0] - Q * mults[2 * i + 1]); + product[2 * i + 1] = (float) (I * mults[2 * i + 1] + Q * mults[2 * i + 0]); } - fftwf_execute (a->CRev); - std::copy(&(a->infilt[2 * a->size]), &(a->infilt[2 * a->size]) + a->size * 2, a->infilt); + fftwf_execute (CRev); + std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt); } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void EMPH::setBuffers_emph (EMPH *a, float* in, float* out) +void EMPH::setBuffers(float* _in, float* _out) { - decalc_emph (a); - a->in = in; - a->out = out; - calc_emph (a); + decalc(); + in = _in; + out = _out; + calc(); } -void EMPH::setSamplerate_emph (EMPH *a, int rate) +void EMPH::setSamplerate(int _rate) { - decalc_emph (a); - a->rate = rate; - calc_emph (a); + decalc(); + rate = _rate; + calc(); } -void EMPH::setSize_emph (EMPH *a, int size) +void EMPH::setSize(int _size) { - decalc_emph(a); - a->size = size; - calc_emph(a); + decalc(); + size = _size; + calc(); } -/******************************************************************************************************** -* * -* Overlap-Save FM Pre-Emphasis: TXA Properties * -* * -********************************************************************************************************/ -/* // Uncomment when needed -PORT -void SetTXAFMEmphPosition (int channel, int position) -{ - ch.csDSP.lock(); - txa.preemph->position = position; - ch.csDSP.unlock(); -} -*/ - } // namespace WDSP diff --git a/wdsp/emph.hpp b/wdsp/emph.hpp index e64c107a9..21b9f8fc1 100644 --- a/wdsp/emph.hpp +++ b/wdsp/emph.hpp @@ -25,57 +25,6 @@ warren@wpratt.com */ -/******************************************************************************************************** -* * -* Partitioned Overlap-Save FM Pre-Emphasis * -* * -********************************************************************************************************/ - -#ifndef wdsp_emphp_h -#define wdsp_emphp_h - -#include "export.h" - -namespace WDSP { - -class FIRCORE; -class TXA; - -class WDSP_API EMPHP -{ -public: - int run; - int position; - int size; - int nc; - int mp; - float* in; - float* out; - int ctype; - float f_low; - float f_high; - float rate; - FIRCORE *p; - - static EMPHP* create_emphp (int run, int position, int size, int nc, int mp, - float* in, float* out, int rate, int ctype, float f_low, float f_high); - static void destroy_emphp (EMPHP *a); - static void flush_emphp (EMPHP *a); - static void xemphp (EMPHP *a, int position); - static void setBuffers_emphp (EMPHP *a, float* in, float* out); - static void setSamplerate_emphp (EMPHP *a, int rate); - static void setSize_emphp (EMPHP *a, int size); - // TXA Properties - static void SetFMEmphPosition (TXA& txa, int position); - static void SetFMEmphMP (TXA& txa, int mp); - static void SetFMEmphNC (TXA& txa, int nc); - static void SetFMPreEmphFreqs(TXA& txa, float low, float high); -}; - -} // namespace WDSP - -#endif - /******************************************************************************************************** * * * Overlap-Save FM Pre-Emphasis * @@ -92,32 +41,46 @@ namespace WDSP { class WDSP_API EMPH { +public: int run; int position; int size; float* in; float* out; int ctype; - float f_low; - float f_high; + double f_low; + double f_high; float* infilt; float* product; float* mults; - float rate; + double rate; fftwf_plan CFor; fftwf_plan CRev; - static EMPH* create_emph (int run, int position, int size, float* in, float* out, int rate, int ctype, float f_low, float f_high); - static void destroy_emph (EMPH *a); - static void flush_emph (EMPH *a); - static void xemph (EMPH *a, int position); - static void setBuffers_emph (EMPH *a, float* in, float* out); - static void setSamplerate_emph (EMPH *a, int rate); - static void setSize_emph (EMPH *a, int size); + EMPH( + int run, + int position, + int size, + float* in, + float* out, + int rate, + int ctype, + double f_low, + double f_high + ); + EMPH(const EMPH&) = delete; + EMPH& operator=(const EMPH& other) = delete; + ~EMPH(); + + void flush(); + void execute(int position); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_emph (EMPH *a); - static void decalc_emph (EMPH *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/emphp.cpp b/wdsp/emphp.cpp new file mode 100644 index 000000000..cb1fb8abd --- /dev/null +++ b/wdsp/emphp.cpp @@ -0,0 +1,217 @@ +/* emph.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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.hpp" +#include "emphp.hpp" +#include "fcurve.hpp" +#include "fircore.hpp" +#include "TXA.hpp" + +namespace WDSP { + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save FM Pre-Emphasis * +* * +********************************************************************************************************/ + +EMPHP::EMPHP( + int _run, + int _position, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + int _rate, + int _ctype, + double _f_low, + double _f_high +) +{ + float* impulse; + run = _run; + position = _position; + size = _size; + nc = _nc; + mp = _mp; + in = _in; + out = _out; + rate = _rate; + ctype = _ctype; + f_low = _f_low; + f_high = _f_high; + impulse = FCurve::fc_impulse ( + nc, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, 0 + ); + p = FIRCORE::create_fircore(size, in, out, nc, mp, impulse); + delete[] (impulse); +} + +EMPHP::~EMPHP() +{ + delete (p); +} + +void EMPHP::flush() +{ + FIRCORE::flush_fircore(p); +} + +void EMPHP::execute(int _position) +{ + if (run && position == _position) + FIRCORE::xfircore(p); + else if (in != out) + std::copy( in, in + size * 2, out); +} + +void EMPHP::setBuffers(float* _in, float* _out) +{ + in = _in; + out = _out; + FIRCORE::setBuffers_fircore(p, in, out); +} + +void EMPHP::setSamplerate(int _rate) +{ + float* impulse; + rate = _rate; + impulse = FCurve::fc_impulse ( + nc, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, 0 + ); + FIRCORE::setImpulse_fircore(p, impulse, 1); + delete[] (impulse); +} + +void EMPHP::setSize(int _size) +{ + float* impulse; + size = _size; + FIRCORE::setSize_fircore(p, size); + impulse = FCurve::fc_impulse ( + nc, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, + 0 + ); + FIRCORE::setImpulse_fircore(p, impulse, 1); + delete[] (impulse); +} + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save FM Pre-Emphasis: TXA Properties * +* * +********************************************************************************************************/ + +void EMPHP::setPosition(int _position) +{ + position = _position; +} + +void EMPHP::setMP(int _mp) +{ + if (mp != _mp) + { + mp = _mp; + FIRCORE::setMp_fircore(p, mp); + } +} + +void EMPHP::setNC(int _nc) +{ + float* impulse; + + if (nc != _nc) + { + nc = _nc; + impulse = FCurve::fc_impulse ( + nc, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, + 0 + ); + FIRCORE::setNc_fircore(p, nc, impulse); + delete[] (impulse); + } +} + +void EMPHP::setFreqs(double low, double high) +{ + float* impulse; + + if (f_low != low || f_high != high) + { + f_low = low; + f_high = high; + impulse = FCurve::fc_impulse ( + nc, + f_low, + f_high, + -20.0 * log10(f_high / f_low), + 0.0, + ctype, + rate, + 1.0 / (2.0 * size), + 0, + 0 + ); + FIRCORE::setImpulse_fircore(p, impulse, 1); + delete[] (impulse); + } +} + +} // namespace WDSP diff --git a/wdsp/emphp.hpp b/wdsp/emphp.hpp new file mode 100644 index 000000000..b887b0444 --- /dev/null +++ b/wdsp/emphp.hpp @@ -0,0 +1,91 @@ +/* emph.h + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2014, 2016, 2023 Warren Pratt, NR0V +Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel + +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 + +*/ + +/******************************************************************************************************** +* * +* Partitioned Overlap-Save FM Pre-Emphasis * +* * +********************************************************************************************************/ + +#ifndef wdsp_emphp_h +#define wdsp_emphp_h + +#include "export.h" + +namespace WDSP { + +class FIRCORE; +class TXA; + +class WDSP_API EMPHP +{ +public: + int run; + int position; + int size; + int nc; + int mp; + float* in; + float* out; + int ctype; + double f_low; + double f_high; + double rate; + FIRCORE *p; + + EMPHP( + int run, + int position, + int size, + int nc, + int mp, + float* in, + float* out, + int rate, + int ctype, + double f_low, + double f_high + ); + EMPHP(const EMPHP&) = delete; + EMPHP& operator=(const EMPHP& other) = delete; + ~EMPHP(); + + void flush(); + void execute(int position); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + // TXA Properties + void setPosition(int position); + void setMP(int mp); + void setNC(int nc); + void setFreqs(double low, double high); +}; + +} // namespace WDSP + +#endif From fe08cd4a789df9bb446eefc8b706a7f37d8f4124 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 6 Aug 2024 06:26:53 +0200 Subject: [PATCH 35/46] WDSP: FIRCORE rework --- wdsp/RXA.cpp | 14 +-- wdsp/TXA.cpp | 12 +-- wdsp/bandpass.cpp | 28 ++--- wdsp/bpsnba.cpp | 2 +- wdsp/cfir.cpp | 8 +- wdsp/emphp.cpp | 20 ++-- wdsp/eqp.cpp | 30 +++--- wdsp/fir.cpp | 8 +- wdsp/fir.hpp | 4 +- wdsp/fircore.cpp | 261 +++++++++++++++++++++------------------------- wdsp/fircore.hpp | 59 +++++++---- wdsp/fmd.cpp | 44 ++++---- wdsp/fmmod.cpp | 24 ++--- wdsp/fmsq.cpp | 14 +-- wdsp/icfir.cpp | 43 +++----- wdsp/nbp.cpp | 24 ++--- 16 files changed, 290 insertions(+), 305 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 91fef39d9..c030899db 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -904,7 +904,7 @@ void RXA::bp1Set () a->run = 0; if (!old && a->run) a->flush(); - FIRCORE::setUpdate_fircore(a->fircore); + a->fircore->setUpdate(); } void RXA::bpsnbaCheck(int _mode, int _notch_run) @@ -989,7 +989,7 @@ void RXA::bpsnbaSet() default: break; } - FIRCORE::setUpdate_fircore(a->bpsnba->fircore); + a->bpsnba->fircore->setUpdate(); } void RXA::updateNBPFiltersLightWeight() @@ -1005,7 +1005,7 @@ void RXA::updateNBPFilters() if (a->fnfrun) { a->calc_impulse(); - FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); + a->fircore->setImpulse(a->impulse, 1); delete[] (a->impulse); } if (b->bpsnba->fnfrun) @@ -1097,10 +1097,10 @@ void RXA::nbpSetNotchesRun(int _run) b->fnfrun = a->master_run; bpsnbaCheck(mode, _run); b->calc_impulse(); // recalc nbp impulse response - FIRCORE::setImpulse_fircore(b->fircore, b->impulse, 0); // calculate new filter masks + b->fircore->setImpulse(b->impulse, 0); // calculate new filter masks delete[] (b->impulse); bpsnbaSet(); - FIRCORE::setUpdate_fircore(b->fircore); // apply new filter masks + b->fircore->setUpdate(); // apply new filter masks } } @@ -1115,7 +1115,7 @@ void RXA::nbpSetWindow(int _wintype) { a->wintype = _wintype; a->calc_impulse(); - FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); + a->fircore->setImpulse(a->impulse, 1); delete[] (a->impulse); } @@ -1137,7 +1137,7 @@ void RXA::nbpSetAutoIncrease(int _autoincr) { a->autoincr = _autoincr; a->calc_impulse(); - FIRCORE::setImpulse_fircore(a->fircore, a->impulse, 1); + a->fircore->setImpulse(a->impulse, 1); delete[] (a->impulse); } diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 9521d88a3..f57c1426d 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -925,7 +925,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); + a->fircore->setNc(a->nc, impulse); delete[] impulse; } @@ -943,7 +943,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); + a->fircore->setNc(a->nc, impulse); delete[] impulse; } @@ -961,7 +961,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - FIRCORE::setNc_fircore(a->fircore, a->nc, impulse); + a->fircore->setNc(a->nc, impulse); delete[] impulse; } } @@ -974,7 +974,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore(a->fircore, a->mp); + a->fircore->setMp(a->mp); } a = bp1; @@ -982,7 +982,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore(a->fircore, a->mp); + a->fircore->setMp(a->mp); } a = bp2; @@ -990,7 +990,7 @@ void TXA::setBandpassMP(int _mp) if (_mp != a->mp) { a->mp = _mp; - FIRCORE::setMp_fircore(a->fircore, a->mp); + a->fircore->setMp(a->mp); } } diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index a730d2106..3bdc00ece 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -75,24 +75,24 @@ BANDPASS::BANDPASS( 1, gain / (double)(2 * size) ); - fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); + fircore = new FIRCORE(size, in, out, nc, mp, impulse); delete[] impulse; } BANDPASS::~BANDPASS() { - FIRCORE::destroy_fircore (fircore); + delete (fircore); } void BANDPASS::flush() { - FIRCORE::flush_fircore(fircore); + fircore->flush(); } void BANDPASS::execute(int pos) { if (run && position == pos) - FIRCORE::xfircore(fircore); + fircore->execute(); else if (out != in) std::copy(in, in + size * 2, out); } @@ -101,7 +101,7 @@ void BANDPASS::setBuffers(float* _in, float* _out) { in = _in; out = _out; - FIRCORE::setBuffers_fircore(fircore, in, out); + fircore->setBuffers(in, out); } void BANDPASS::setSamplerate(int _rate) @@ -116,7 +116,7 @@ void BANDPASS::setSamplerate(int _rate) 1, gain / (double) (2 * size) ); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -124,7 +124,7 @@ void BANDPASS::setSize(int _size) { // NOTE: 'size' must be <= 'nc' size = _size; - FIRCORE::setSize_fircore (fircore, size); + fircore->setSize(size); // recalc impulse because scale factor is a function of size float* impulse = FIR::fir_bandpass ( nc, @@ -135,7 +135,7 @@ void BANDPASS::setSize(int _size) 1, gain / (double) (2 * size) ); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -151,7 +151,7 @@ void BANDPASS::setGain(double _gain, int _update) 1, gain / (double) (2 * size) ); - FIRCORE::setImpulse_fircore (fircore, impulse, _update); + fircore->setImpulse(impulse, _update); delete[] impulse; } @@ -171,7 +171,7 @@ void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) 1, gain / (double)(2 * size) ); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } } @@ -196,11 +196,11 @@ void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) gain / (double)(2 * size) ); - FIRCORE::setImpulse_fircore (fircore, impulse, 0); + fircore->setImpulse(impulse, 0); delete[] impulse; f_low = _f_low; f_high = _f_high; - FIRCORE::setUpdate_fircore (fircore); + fircore->setUpdate(); } } @@ -219,7 +219,7 @@ void BANDPASS::SetBandpassNC(int _nc) 1, gain / (double)( 2 * size) ); - FIRCORE::setNc_fircore (fircore, nc, impulse); + fircore->setNc(nc, impulse); delete[] impulse; } } @@ -229,7 +229,7 @@ void BANDPASS::SetBandpassMP(int _mp) if (_mp != mp) { mp = _mp; - FIRCORE::setMp_fircore (fircore, mp); + fircore->setMp(mp); } } diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index f7a3884f9..6720871ed 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -176,7 +176,7 @@ void BPSNBA::recalc_bpsnba_filter(int update) b->gain = gain; b->autoincr = autoincr; b->calc_impulse(); - FIRCORE::setImpulse_fircore (b->fircore, b->impulse, update); + b->fircore->setImpulse(b->impulse, update); delete[] (b->impulse); } diff --git a/wdsp/cfir.cpp b/wdsp/cfir.cpp index e4def1b4c..1e0b05bf7 100644 --- a/wdsp/cfir.cpp +++ b/wdsp/cfir.cpp @@ -37,13 +37,13 @@ void CFIR::calc_cfir (CFIR *a) float* impulse; a->scale = 1.0 / (float)(2 * a->size); impulse = cfir_impulse (a->nc, a->DD, a->R, a->Pairs, a->runrate, a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse); + a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse); delete[] (impulse); } void CFIR::decalc_cfir (CFIR *a) { - FIRCORE::destroy_fircore (a->p); + delete (a->p); } CFIR* CFIR::create_cfir ( @@ -105,13 +105,13 @@ void CFIR::destroy_cfir (CFIR *a) void CFIR::flush_cfir (CFIR *a) { - FIRCORE::flush_fircore (a->p); + a->p->flush(); } void CFIR::xcfir (CFIR *a) { if (a->run) - FIRCORE::xfircore (a->p); + a->p->execute(); else if (a->in != a->out) std::copy( a->in, a->in + a->size * 2, a->out); } diff --git a/wdsp/emphp.cpp b/wdsp/emphp.cpp index cb1fb8abd..33c7c28d3 100644 --- a/wdsp/emphp.cpp +++ b/wdsp/emphp.cpp @@ -76,7 +76,7 @@ EMPHP::EMPHP( 1.0 / (2.0 * size), 0, 0 ); - p = FIRCORE::create_fircore(size, in, out, nc, mp, impulse); + p = new FIRCORE(size, in, out, nc, mp, impulse); delete[] (impulse); } @@ -87,13 +87,13 @@ EMPHP::~EMPHP() void EMPHP::flush() { - FIRCORE::flush_fircore(p); + p->flush(); } void EMPHP::execute(int _position) { if (run && position == _position) - FIRCORE::xfircore(p); + p->execute(); else if (in != out) std::copy( in, in + size * 2, out); } @@ -102,7 +102,7 @@ void EMPHP::setBuffers(float* _in, float* _out) { in = _in; out = _out; - FIRCORE::setBuffers_fircore(p, in, out); + p->setBuffers(in, out); } void EMPHP::setSamplerate(int _rate) @@ -120,7 +120,7 @@ void EMPHP::setSamplerate(int _rate) 1.0 / (2.0 * size), 0, 0 ); - FIRCORE::setImpulse_fircore(p, impulse, 1); + p->setImpulse(impulse, 1); delete[] (impulse); } @@ -128,7 +128,7 @@ void EMPHP::setSize(int _size) { float* impulse; size = _size; - FIRCORE::setSize_fircore(p, size); + p->setSize(size); impulse = FCurve::fc_impulse ( nc, f_low, @@ -141,7 +141,7 @@ void EMPHP::setSize(int _size) 0, 0 ); - FIRCORE::setImpulse_fircore(p, impulse, 1); + p->setImpulse(impulse, 1); delete[] (impulse); } @@ -161,7 +161,7 @@ void EMPHP::setMP(int _mp) if (mp != _mp) { mp = _mp; - FIRCORE::setMp_fircore(p, mp); + p->setMp(mp); } } @@ -184,7 +184,7 @@ void EMPHP::setNC(int _nc) 0, 0 ); - FIRCORE::setNc_fircore(p, nc, impulse); + p->setNc(nc, impulse); delete[] (impulse); } } @@ -209,7 +209,7 @@ void EMPHP::setFreqs(double low, double high) 0, 0 ); - FIRCORE::setImpulse_fircore(p, impulse, 1); + p->setImpulse(impulse, 1); delete[] (impulse); } } diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index 2edf81b5a..c211e5a2e 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -234,24 +234,24 @@ EQP::EQP( wintype = _wintype; samplerate = (double) _samplerate; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); + fircore = new FIRCORE(size, in, out, nc, mp, impulse); delete[] impulse; } EQP::~EQP() { - FIRCORE::destroy_fircore (fircore); + delete (fircore); } void EQP::flush() { - FIRCORE::flush_fircore (fircore); + fircore->flush(); } void EQP::execute() { if (run) - FIRCORE::xfircore (fircore); + fircore->execute(); else std::copy(in, in + size * 2, out); } @@ -260,7 +260,7 @@ void EQP::setBuffers(float* _in, float* _out) { in = _in; out = _out; - FIRCORE::setBuffers_fircore (fircore, in, out); + fircore->setBuffers(in, out); } void EQP::setSamplerate(int rate) @@ -268,7 +268,7 @@ void EQP::setSamplerate(int rate) float* impulse; samplerate = rate; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -276,9 +276,9 @@ void EQP::setSize(int _size) { float* impulse; size = _size; - FIRCORE::setSize_fircore (fircore, size); + fircore->setSize(size); impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -301,7 +301,7 @@ void EQP::setNC(int _nc) { nc = _nc; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setNc_fircore (fircore, nc, impulse); + fircore->setNc(nc, impulse); delete[] impulse; } } @@ -311,7 +311,7 @@ void EQP::setMP(int _mp) if (mp != _mp) { mp = _mp; - FIRCORE::setMp_fircore (fircore, mp); + fircore->setMp(mp); } } @@ -324,7 +324,7 @@ void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) std::copy(_F, _F + (_nfreqs + 1), F.begin()); std::copy(_G, _G + (_nfreqs + 1), G.begin()); impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -333,7 +333,7 @@ void EQP::setCtfmode(int _mode) float* impulse; ctfmode = _mode; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -342,7 +342,7 @@ void EQP::setWintype(int _wintype) float* impulse; wintype = _wintype; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -363,7 +363,7 @@ void EQP::setGrphEQ(const int *rxeq) G[4] = (float)rxeq[3]; ctfmode = 0; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -387,7 +387,7 @@ void EQP::setGrphEQ10(const int *rxeq) G[i] = (float)rxeq[i]; ctfmode = 0; impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index 494e880db..9fd5075a5 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -377,7 +377,7 @@ void FIR::analytic (int N, float* in, float* out) fftwf_destroy_plan (pfor); } -void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity) +void FIR::mp_imp (int N, std::vector& fir, std::vector& mpfir, int pfactor, int polarity) { int i; int size = N * pfactor; @@ -388,7 +388,7 @@ void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity) std::vector ana(size * 2); std::vector impulse(size * 2); std::vector newfreq(size * 2); - std::copy(fir, fir + N * 2, firpad.begin()); + std::copy(fir.begin(), fir.begin() + N * 2, firpad.begin()); fftwf_plan pfor = fftwf_plan_dft_1d ( size, (fftwf_complex *) firpad.data(), @@ -425,9 +425,9 @@ void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity) } fftwf_execute (prev); if (polarity) - std::copy(&impulse[2 * (pfactor - 1) * N], &impulse[2 * (pfactor - 1) * N] + N * 2, mpfir); + std::copy(&impulse[2 * (pfactor - 1) * N], &impulse[2 * (pfactor - 1) * N] + N * 2, mpfir.begin()); else - std::copy(impulse.begin(), impulse.end(), mpfir); + std::copy(impulse.begin(), impulse.end(), mpfir.begin()); fftwf_destroy_plan (prev); fftwf_destroy_plan (pfor); diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index dd60523fa..e852d0eb5 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -27,6 +27,8 @@ warren@pratt.one #ifndef wdsp_fir_h #define wdsp_fir_h +#include + #include "export.h" namespace WDSP { @@ -38,7 +40,7 @@ public: static float* fir_fsamp_odd (int N, const float* A, int rtype, double scale, int wintype); static float* fir_fsamp (int N, const float* A, int rtype, double scale, int wintype); static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); - static void mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity); + static void mp_imp (int N, std::vector& fir, std::vector& mpfir, int pfactor, int polarity); private: static void analytic (int N, float* in, float* out); diff --git a/wdsp/fircore.cpp b/wdsp/fircore.cpp index 866e3dd9c..5bcf996f5 100644 --- a/wdsp/fircore.cpp +++ b/wdsp/fircore.cpp @@ -38,223 +38,202 @@ namespace WDSP { ********************************************************************************************************/ -void FIRCORE::plan_fircore (FIRCORE *a) +void FIRCORE::plan() { // must call for change in 'nc', 'size', 'out' - int i; - a->nfor = a->nc / a->size; - a->cset = 0; - a->buffidx = 0; - a->idxmask = a->nfor - 1; - a->fftin = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->fftout = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *)); - a->fmask = new float**[2]; // (float ***) malloc0 (2 * sizeof (float **)); - a->fmask[0] = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *)); - a->fmask[1] = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *)); - a->maskgen = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->pcfor = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan)); - a->maskplan = new fftwf_plan*[2]; // (fftwf_plan **) malloc0 (2 * sizeof (fftwf_plan *)); - a->maskplan[0] = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan)); - a->maskplan[1] = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan)); - for (i = 0; i < a->nfor; i++) + nfor = nc / size; + cset = 0; + buffidx = 0; + idxmask = nfor - 1; + fftin.resize(2 * size * 2); + fftout.resize(nfor); + fmask[0].resize(nfor); + fmask[1].resize(nfor); + maskgen.resize(2 * size * 2); + pcfor.resize(nfor); + maskplan[0].resize(nfor); + maskplan[1].resize(nfor); + for (int i = 0; i < nfor; i++) { - a->fftout[i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->fmask[0][i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->fmask[1][i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->pcfor[i] = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->fftin, - (fftwf_complex *)a->fftout[i], + fftout[i].resize(2 * size * 2); + fmask[0][i].resize(2 * size * 2); + fmask[1][i].resize(2 * size * 2); + pcfor[i] = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)fftin.data(), + (fftwf_complex *)fftout[i].data(), FFTW_FORWARD, FFTW_PATIENT ); - a->maskplan[0][i] = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->maskgen, - (fftwf_complex *)a->fmask[0][i], + maskplan[0][i] = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)maskgen.data(), + (fftwf_complex *)fmask[0][i].data(), FFTW_FORWARD, FFTW_PATIENT ); - a->maskplan[1][i] = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->maskgen, - (fftwf_complex *)a->fmask[1][i], + maskplan[1][i] = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)maskgen.data(), + (fftwf_complex *)fmask[1][i].data(), FFTW_FORWARD, FFTW_PATIENT ); } - a->accum = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->crev = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->accum, - (fftwf_complex *)a->out, + accum.resize(2 * size * 2); + crev = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)accum.data(), + (fftwf_complex *)out, FFTW_BACKWARD, FFTW_PATIENT ); - a->masks_ready = 0; + masks_ready = 0; } -void FIRCORE::calc_fircore (FIRCORE *a, int flip) +void FIRCORE::calc(int _flip) { // call for change in frequency, rate, wintype, gain // must also call after a call to plan_firopt() - int i; - if (a->mp) - FIR::mp_imp (a->nc, a->impulse, a->imp, 16, 0); + if (mp) + FIR::mp_imp (nc, impulse, imp, 16, 0); else - std::copy(a->impulse, a->impulse + a->nc * 2, a->imp); + std::copy(impulse.begin(), impulse.begin() + nc * 2, imp.begin()); - for (i = 0; i < a->nfor; i++) + for (int i = 0; i < nfor; i++) { // I right-justified the impulse response => take output from left side of output buff, discard right side // Be careful about flipping an asymmetrical impulse response. - std::copy(&(a->imp[2 * a->size * i]), &(a->imp[2 * a->size * i]) + a->size * 2, &(a->maskgen[2 * a->size])); - fftwf_execute (a->maskplan[1 - a->cset][i]); + std::copy(&(imp[2 * size * i]), &(imp[2 * size * i]) + size * 2, &(maskgen[2 * size])); + fftwf_execute (maskplan[1 - cset][i]); } - a->masks_ready = 1; + masks_ready = 1; - if (flip) + if (_flip) { - a->cset = 1 - a->cset; - a->masks_ready = 0; + cset = 1 - cset; + masks_ready = 0; } } -FIRCORE* FIRCORE::create_fircore (int size, float* in, float* out, int nc, int mp, float* impulse) +FIRCORE::FIRCORE( + int _size, + float* _in, + float* _out, + int _nc, + int _mp, + float* _impulse +) { - FIRCORE *a = new FIRCORE; - a->size = size; - a->in = in; - a->out = out; - a->nc = nc; - a->mp = mp; - // InitializeCriticalSectionAndSpinCount (&a->update, 2500); - plan_fircore (a); - a->impulse = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex)); - a->imp = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex)); - std::copy(impulse, impulse + a->nc * 2, a->impulse); - calc_fircore (a, 1); - return a; + size = _size; + in = _in; + out = _out; + nc = _nc; + mp = _mp; + plan(); + impulse.resize(nc * 2); + imp.resize(nc * 2); + std::copy(_impulse, _impulse + nc * 2, impulse.begin()); + calc(1); } -void FIRCORE::deplan_fircore (FIRCORE *a) +void FIRCORE::deplan() { - int i; - fftwf_destroy_plan (a->crev); - delete[] (a->accum); - for (i = 0; i < a->nfor; i++) + fftwf_destroy_plan (crev); + for (int i = 0; i < nfor; i++) { - delete[] (a->fftout[i]); - delete[] (a->fmask[0][i]); - delete[] (a->fmask[1][i]); - fftwf_destroy_plan (a->pcfor[i]); - fftwf_destroy_plan (a->maskplan[0][i]); - fftwf_destroy_plan (a->maskplan[1][i]); + fftwf_destroy_plan (pcfor[i]); + fftwf_destroy_plan (maskplan[0][i]); + fftwf_destroy_plan (maskplan[1][i]); } - delete[] (a->maskplan[0]); - delete[] (a->maskplan[1]); - delete[] (a->maskplan); - delete[] (a->pcfor); - delete[] (a->maskgen); - delete[] (a->fmask[0]); - delete[] (a->fmask[1]); - delete[] (a->fmask); - delete[] (a->fftout); - delete[] (a->fftin); } -void FIRCORE::destroy_fircore (FIRCORE *a) +FIRCORE::~FIRCORE() { - deplan_fircore (a); - delete[] (a->imp); - delete[] (a->impulse); - delete (a); + deplan(); } -void FIRCORE::flush_fircore (FIRCORE *a) +void FIRCORE::flush() { - int i; - std::fill(a->fftin, a->fftin + 2 * a->size * 2, 0); - for (i = 0; i < a->nfor; i++) - std::fill(a->fftout[i], a->fftout[i] + 2 * a->size * 2, 0); - a->buffidx = 0; + std::fill(fftin.begin(), fftin.end(), 0); + for (int i = 0; i < nfor; i++) + std::fill(fftout[i].begin(), fftout[i].end(), 0); + buffidx = 0; } -void FIRCORE::xfircore (FIRCORE *a) +void FIRCORE::execute() { - int i, j, k; - std::copy(a->in, a->in + a->size * 2, &(a->fftin[2 * a->size])); - fftwf_execute (a->pcfor[a->buffidx]); - k = a->buffidx; - std::fill(a->accum, a->accum + 2 * a->size * 2, 0); + int k; + std::copy(in, in + size * 2, &(fftin[2 * size])); + fftwf_execute (pcfor[buffidx]); + k = buffidx; + std::fill(accum.begin(), accum.end(), 0); - for (j = 0; j < a->nfor; j++) + for (int j = 0; j < nfor; j++) { - for (i = 0; i < 2 * a->size; i++) + for (int i = 0; i < 2 * size; i++) { - a->accum[2 * i + 0] += a->fftout[k][2 * i + 0] * a->fmask[a->cset][j][2 * i + 0] - a->fftout[k][2 * i + 1] * a->fmask[a->cset][j][2 * i + 1]; - a->accum[2 * i + 1] += a->fftout[k][2 * i + 0] * a->fmask[a->cset][j][2 * i + 1] + a->fftout[k][2 * i + 1] * a->fmask[a->cset][j][2 * i + 0]; + accum[2 * i + 0] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 0] - fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 1]; + accum[2 * i + 1] += fftout[k][2 * i + 0] * fmask[cset][j][2 * i + 1] + fftout[k][2 * i + 1] * fmask[cset][j][2 * i + 0]; } - k = (k + a->idxmask) & a->idxmask; + k = (k + idxmask) & idxmask; } - a->buffidx = (a->buffidx + 1) & a->idxmask; - fftwf_execute (a->crev); - std::copy(&(a->fftin[2 * a->size]), &(a->fftin[2 * a->size]) + a->size * 2, a->fftin); + buffidx = (buffidx + 1) & idxmask; + fftwf_execute (crev); + std::copy(&(fftin[2 * size]), &(fftin[2 * size]) + size * 2, fftin.begin()); } -void FIRCORE::setBuffers_fircore (FIRCORE *a, float* in, float* out) +void FIRCORE::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; - deplan_fircore (a); - plan_fircore (a); - calc_fircore (a, 1); + in = _in; + out = _out; + deplan(); + plan(); + calc(1); } -void FIRCORE::setSize_fircore (FIRCORE *a, int size) +void FIRCORE::setSize(int _size) { - a->size = size; - deplan_fircore (a); - plan_fircore (a); - calc_fircore (a, 1); + size = _size; + deplan(); + plan(); + calc(1); } -void FIRCORE::setImpulse_fircore (FIRCORE *a, float* impulse, int update) +void FIRCORE::setImpulse(float* _impulse, int _update) { - std::copy(impulse, impulse + a->nc * 2, a->impulse); - calc_fircore (a, update); + std::copy(_impulse, _impulse + nc * 2, impulse.begin()); + calc(_update); } -void FIRCORE::setNc_fircore (FIRCORE *a, int nc, float* impulse) +void FIRCORE::setNc(int _nc, float* _impulse) { // because of FFT planning, this will probably cause a glitch in audio if done during dataflow - deplan_fircore (a); - delete[] (a->impulse); - delete[] (a->imp); - a->nc = nc; - plan_fircore (a); - a->imp = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex)); - a->impulse = new float[a->nc * 2]; // (float *) malloc0 (a->nc * sizeof (complex)); - std::copy(impulse, impulse + a->nc * 2, a->impulse); - calc_fircore (a, 1); + deplan(); + nc = _nc; + plan(); + imp.resize(nc * 2); + impulse.resize(nc * 2); + std::copy(_impulse, _impulse + nc * 2, impulse.begin()); + calc(1); } -void FIRCORE::setMp_fircore (FIRCORE *a, int mp) +void FIRCORE::setMp(int _mp) { - a->mp = mp; - calc_fircore (a, 1); + mp = _mp; + calc(1); } -void FIRCORE::setUpdate_fircore (FIRCORE *a) +void FIRCORE::setUpdate() { - if (a->masks_ready) + if (masks_ready) { - a->cset = 1 - a->cset; - a->masks_ready = 0; + cset = 1 - cset; + masks_ready = 0; } } diff --git a/wdsp/fircore.hpp b/wdsp/fircore.hpp index 3dbb82334..66e2d4832 100644 --- a/wdsp/fircore.hpp +++ b/wdsp/fircore.hpp @@ -34,6 +34,9 @@ warren@wpratt.com #ifndef wdsp_fircore_h #define wdsp_fircore_h +#include +#include + #include "fftw3.h" #include "export.h" @@ -46,39 +49,49 @@ public: float* in; // input buffer float* out; // output buffer, can be same as input int nc; // number of filter coefficients, power of two, >= size - float* impulse; // impulse response of filter - float* imp; + std::vector impulse; // impulse response of filter + std::vector imp; int nfor; // number of buffers in delay line - float* fftin; // fft input buffer - float*** fmask; // frequency domain masks - float** fftout; // fftout delay line - float* accum; // frequency domain accumulator + std::vector fftin; // fft input buffer + std::array>, 2> fmask; // frequency domain masks + std::vector> fftout; // fftout delay line + std::vector accum; // frequency domain accumulator int buffidx; // fft out buffer index int idxmask; // mask for index computations - float* maskgen; // input for mask generation FFT - fftwf_plan* pcfor; // array of forward FFT plans + std::vector maskgen; // input for mask generation FFT + std::vector pcfor; // array of forward FFT plans fftwf_plan crev; // reverse fft plan - fftwf_plan** maskplan; // plans for frequency domain masks + std::array, 2> maskplan; // plans for frequency domain masks int cset; int mp; int masks_ready; - static FIRCORE* create_fircore (int size, float* in, float* out, - int nc, int mp, float* impulse); - static void xfircore (FIRCORE *a); - static void destroy_fircore (FIRCORE *a); - static void flush_fircore (FIRCORE *a); - static void setBuffers_fircore (FIRCORE *a, float* in, float* out); - static void setSize_fircore (FIRCORE *a, int size); - static void setImpulse_fircore (FIRCORE *a, float* impulse, int update); - static void setNc_fircore (FIRCORE *a, int nc, float* impulse); - static void setMp_fircore (FIRCORE *a, int mp); - static void setUpdate_fircore (FIRCORE *a); + FIRCORE( + int size, + float* in, + float* out, + int nc, + int mp, + float* + impulse + ); + FIRCORE(const FIRCORE&) = delete; + FIRCORE& operator=(const FIRCORE& other) = delete; + ~FIRCORE(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSize(int size); + void setImpulse(float* impulse, int update); + void setNc(int nc, float* impulse); + void setMp(int mp); + void setUpdate(); private: - static void plan_fircore (FIRCORE *a); - static void calc_fircore (FIRCORE *a, int flip); - static void deplan_fircore (FIRCORE *a); + void plan(); + void calc(int flip); + void deplan(); }; } // namespace WDSP diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 5dd043fae..14eafe841 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -158,26 +158,26 @@ FMD::FMD( 0, 0 ); - pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); + pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse); delete[] impulse; // audio filter impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulse); delete[] impulse; } FMD::~FMD() { - FIRCORE::destroy_fircore (paud); - FIRCORE::destroy_fircore (pde); + delete (paud); + delete (pde); decalc(); } void FMD::flush() { std::fill(audio.begin(), audio.end(), 0); - FIRCORE::flush_fircore (pde); - FIRCORE::flush_fircore (paud); + pde->flush(); + paud->flush(); phs = 0.0; fil_out = 0.0; omega = 0.0; @@ -219,9 +219,9 @@ void FMD::execute() audio[2 * i + 1] = audio[2 * i + 0]; } // de-emphasis - FIRCORE::xfircore (pde); + pde->execute(); // audio filter - FIRCORE::xfircore (paud); + paud->execute(); // CTCSS Removal sntch->execute(); if (lim_run) @@ -241,8 +241,8 @@ void FMD::setBuffers(float* _in, float* _out) in = _in; out = _out; calc(); - FIRCORE::setBuffers_fircore (pde, audio.data(), out); - FIRCORE::setBuffers_fircore (paud, out, out); + pde->setBuffers(audio.data(), out); + paud->setBuffers(out, out); plim->setBuffers(out, out); } @@ -265,11 +265,11 @@ void FMD::setSamplerate(int _rate) 0, 0 ); - FIRCORE::setImpulse_fircore (pde, impulse, 1); + pde->setImpulse(impulse, 1); delete[] impulse; // audio filter impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - FIRCORE::setImpulse_fircore (paud, impulse, 1); + paud->setImpulse(impulse, 1); delete[] impulse; plim->setSamplerate((int) rate); } @@ -282,7 +282,7 @@ void FMD::setSize(int _size) calc(); audio.resize(size * 2); // de-emphasis filter - FIRCORE::destroy_fircore (pde); + delete (pde); impulse = FCurve::fc_impulse ( nc_de, (float) f_low, @@ -295,12 +295,12 @@ void FMD::setSize(int _size) 0, 0 ); - pde = FIRCORE::create_fircore (size, audio.data(), out, nc_de, mp_de, impulse); + pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse); delete[] impulse; // audio filter - FIRCORE::destroy_fircore (paud); + delete (paud); impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = FIRCORE::create_fircore (size, out, out, nc_aud, mp_aud, impulse); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulse); delete[] impulse; plim->setSize(size); } @@ -348,7 +348,7 @@ void FMD::setNCde(int nc) 0, 0 ); - FIRCORE::setNc_fircore (pde, nc_de, impulse); + pde->setNc(nc_de, impulse); delete[] impulse; } } @@ -358,7 +358,7 @@ void FMD::setMPde(int mp) if (mp_de != mp) { mp_de = mp; - FIRCORE::setMp_fircore (pde, mp_de); + pde->setMp(mp_de); } } @@ -370,7 +370,7 @@ void FMD::setNCaud(int nc) { nc_aud = nc; impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - FIRCORE::setNc_fircore (paud, nc_aud, impulse); + paud->setNc(nc_aud, impulse); delete[] impulse; } } @@ -380,7 +380,7 @@ void FMD::setMPaud(int mp) if (mp_aud != mp) { mp_aud = mp; - FIRCORE::setMp_fircore (paud, mp_aud); + paud->setMp(mp_aud); } } @@ -424,11 +424,11 @@ void FMD::setAFFilter(double low, double high) 0, 0 ); - FIRCORE::setImpulse_fircore (pde, impulse, 1); + pde->setImpulse(impulse, 1); delete[] impulse; // audio filter impulse = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - FIRCORE::setImpulse_fircore (paud, impulse, 1); + paud->setImpulse(impulse, 1); delete[] impulse; } } diff --git a/wdsp/fmmod.cpp b/wdsp/fmmod.cpp index 59bdaaacf..81eed4486 100644 --- a/wdsp/fmmod.cpp +++ b/wdsp/fmmod.cpp @@ -81,14 +81,14 @@ FMMOD* FMMOD::create_fmmod ( a->mp = mp; calc_fmmod (a); impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p = FIRCORE::create_fircore (a->size, a->out, a->out, a->nc, a->mp, impulse); + a->p = new FIRCORE(a->size, a->out, a->out, a->nc, a->mp, impulse); delete[] (impulse); return a; } void FMMOD::destroy_fmmod (FMMOD *a) { - FIRCORE::destroy_fircore (a->p); + delete (a->p); delete (a); } @@ -124,7 +124,7 @@ void FMMOD::xfmmod (FMMOD *a) } //print_deviation ("peakdev.txt", peak, a->samplerate); if (a->bp_run) - FIRCORE::xfircore (a->p); + a->p->execute(); } else if (a->in != a->out) std::copy( a->in, a->in + a->size * 2, a->out); @@ -135,7 +135,7 @@ void FMMOD::setBuffers_fmmod (FMMOD *a, float* in, float* out) a->in = in; a->out = out; calc_fmmod (a); - FIRCORE::setBuffers_fircore (a->p, a->out, a->out); + a->p->setBuffers(a->out, a->out); } void FMMOD::setSamplerate_fmmod (FMMOD *a, int rate) @@ -144,7 +144,7 @@ void FMMOD::setSamplerate_fmmod (FMMOD *a, int rate) a->samplerate = rate; calc_fmmod (a); impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + a->p->setImpulse(impulse, 1); delete[] (impulse); } @@ -153,9 +153,9 @@ void FMMOD::setSize_fmmod (FMMOD *a, int size) float* impulse; a->size = size; calc_fmmod (a); - FIRCORE::setSize_fircore (a->p, a->size); + a->p->setSize(a->size); impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + a->p->setImpulse(impulse, 1); delete[] (impulse); } @@ -170,7 +170,7 @@ void FMMOD::SetFMDeviation (TXA& txa, float deviation) FMMOD *a = txa.fmmod; float bp_fc = a->f_high + deviation; float* impulse = FIR::fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - FIRCORE::setImpulse_fircore (a->p, impulse, 0); + a->p->setImpulse(impulse, 0); delete[] (impulse); a->deviation = deviation; // mod @@ -178,7 +178,7 @@ void FMMOD::SetFMDeviation (TXA& txa, float deviation) a->sdelta = TWOPI * a->deviation / a->samplerate; // bandpass a->bp_fc = bp_fc; - FIRCORE::setUpdate_fircore (a->p); + a->p->setUpdate(); } void FMMOD::SetCTCSSFreq (TXA& txa, float freq) @@ -205,7 +205,7 @@ void FMMOD::SetFMNC (TXA& txa, int nc) { a->nc = nc; impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - FIRCORE::setNc_fircore (a->p, a->nc, impulse); + a->p->setNc(a->nc, impulse); delete[] (impulse); } } @@ -217,7 +217,7 @@ void FMMOD::SetFMMP (TXA& txa, int mp) if (a->mp != mp) { a->mp = mp; - FIRCORE::setMp_fircore (a->p, a->mp); + a->p->setMp(a->mp); } } @@ -233,7 +233,7 @@ void FMMOD::SetFMAFFreqs (TXA& txa, float low, float high) a->f_high = high; a->bp_fc = a->deviation + a->f_high; impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - FIRCORE::setImpulse_fircore (a->p, impulse, 1); + a->p->setImpulse(impulse, 1); delete[] (impulse); } } diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 41980f20c..5f83a4675 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -49,7 +49,7 @@ void FMSQ::calc() G[2] = 3.0; G[3] = (float) (+20.0 * log10(20000.0 / *pllpole)); impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - p = FIRCORE::create_fircore (size, trigger, noise.data(), nc, mp, impulse); + p = new FIRCORE(size, trigger, noise.data(), nc, mp, impulse); delete[] impulse; // noise averaging avm = exp(-1.0 / (rate * avtau)); @@ -89,7 +89,7 @@ void FMSQ::calc() void FMSQ::decalc() { - FIRCORE::destroy_fircore (p); + delete (p); } FMSQ::FMSQ( @@ -143,7 +143,7 @@ FMSQ::~FMSQ() void FMSQ::flush() { - FIRCORE::flush_fircore (p); + p->flush(); avnoise = 100.0; longnoise = 1.0; state = FMSQState::MUTED; @@ -157,7 +157,7 @@ void FMSQ::execute() { double _noise; double lnlimit; - FIRCORE::xfircore (p); + p->execute(); for (int i = 0; i < size; i++) { @@ -250,7 +250,7 @@ void FMSQ::setBuffers(float* in, float* out, float* trig) insig = in; outsig = out; trigger = trig; - FIRCORE::setBuffers_fircore (p, trigger, noise.data()); + p->setBuffers(trigger, noise.data()); } void FMSQ::setSamplerate(int _rate) @@ -292,7 +292,7 @@ void FMSQ::setNC(int _nc) { nc = _nc; impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - FIRCORE::setNc_fircore (p, nc, impulse); + p->setNc(nc, impulse); delete[] impulse; } } @@ -302,7 +302,7 @@ void FMSQ::setMP(int _mp) if (mp != _mp) { mp = _mp; - FIRCORE::setMp_fircore (p, mp); + p->setMp(mp); } } diff --git a/wdsp/icfir.cpp b/wdsp/icfir.cpp index dd7b8546c..f22043a77 100644 --- a/wdsp/icfir.cpp +++ b/wdsp/icfir.cpp @@ -35,15 +35,15 @@ namespace WDSP { void ICFIR::calc_icfir (ICFIR *a) { float* impulse; - a->scale = 1.0 / (float)(2 * a->size); - impulse = icfir_impulse (a->nc, a->DD, a->R, a->Pairs, a->runrate, a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = FIRCORE::create_fircore (a->size, a->in, a->out, a->nc, a->mp, impulse); + a->scale = 1.0f / (float)(2 * a->size); + impulse = icfir_impulse (a->nc, a->DD, a->R, a->Pairs, (float) a->runrate, (float) a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); + a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse); delete[] (impulse); } void ICFIR::decalc_icfir (ICFIR *a) { - FIRCORE::destroy_fircore (a->p); + delete (a->p); } ICFIR* ICFIR::create_icfir ( @@ -105,13 +105,13 @@ void ICFIR::destroy_icfir (ICFIR *a) void ICFIR::flush_icfir (ICFIR *a) { - FIRCORE::flush_fircore (a->p); + a->p->flush(); } void ICFIR::xicfir (ICFIR *a) { if (a->run) - FIRCORE::xfircore (a->p); + a->p->execute(); else if (a->in != a->out) std::copy( a->in, a->in + a->size * 2, a->out); } @@ -171,16 +171,21 @@ float* ICFIR::icfir_impulse ( // xbw: transition bandwidth for raised cosine // rtype: 0 for real output, 1 for complex output // scale: scale factor to be applied to the output - int i, j; - float tmp, local_scale, ri, mag, fn; + int i; + int j; + float tmp; + float local_scale; + float ri; + float mag; + float fn; float* impulse; - float* A = new float[N]; // (float *) malloc0 (N * sizeof (float)); + auto* A = new float[N]; float ft = cutoff / cicrate; // normalized cutoff frequency int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N int c_samps = (int)(cutoff / runrate * N) + (N + 1) / 2 - N / 2; // number of unique samples within bandpass, OK for odd or even N - int x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N - float offset = 0.5 - 0.5 * (float)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N - float* xistion = new float[x_samps + 1]; // (float *) malloc0 ((x_samps + 1) * sizeof (float)); + auto x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N + float offset = 0.5f - 0.5f * (float)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N + auto* xistion = new float[x_samps + 1]; float delta = PI / (float)x_samps; float L = cicrate / runrate; float phs = 0.0; @@ -235,24 +240,10 @@ float* ICFIR::icfir_impulse ( for (i = u_samps, j = 1; i < N; i++, j++) A[i] = A[u_samps - j]; impulse = FIR::fir_fsamp (N, A, rtype, 1.0, wintype); - // print_impulse ("icfirImpulse.txt", N, impulse, 1, 0); delete[] (A); delete[] xistion; return impulse; } -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -//PORT void -//SetTXAICFIRRun (int channel, int run) -//{ -// EnterCriticalSection(&ch[channel].csDSP); -// txa[channel].icfir.p->run = run; -// LeaveCriticalSection(&ch[channel].csDSP); -//} } // namespace WDSP diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index b9784ecb2..29c2befc7 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -333,7 +333,7 @@ void NBP::calc_lightweight() gain / (float)(2 * size), wintype ); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); delete[] impulse; } @@ -437,25 +437,25 @@ NBP::NBP( bplow.resize(maxpb); bphigh.resize(maxpb); calc_impulse (); - fircore = FIRCORE::create_fircore (size, in, out, nc, mp, impulse); + fircore = new FIRCORE(size, in, out, nc, mp, impulse); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); delete[]impulse; } NBP::~NBP() { - FIRCORE::destroy_fircore (fircore); + delete (fircore); } void NBP::flush() { - FIRCORE::flush_fircore (fircore); + fircore->flush(); } void NBP::execute (int pos) { if (run && pos == position) - FIRCORE::xfircore (fircore); + fircore->execute(); else if (in != out) std::copy( in, in + size * 2, out); } @@ -464,14 +464,14 @@ void NBP::setBuffers(float* _in, float* _out) { in = _in; out = _out; - FIRCORE::setBuffers_fircore (fircore, in, out); + fircore->setBuffers(in, out); } void NBP::setSamplerate(int _rate) { rate = _rate; calc_impulse (); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } @@ -479,22 +479,22 @@ void NBP::setSize(int _size) { // NOTE: 'size' must be <= 'nc' size = _size; - FIRCORE::setSize_fircore (fircore, size); + fircore->setSize(size); calc_impulse (); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } void NBP::setNc() { calc_impulse(); - FIRCORE::setNc_fircore (fircore, nc, impulse); + fircore->setNc(nc, impulse); delete[] impulse; } void NBP::setMp() { - FIRCORE::setMp_fircore (fircore, mp); + fircore->setMp(mp); } /******************************************************************************************************** @@ -517,7 +517,7 @@ void NBP::SetFreqs(double _flow, double _fhigh) flow = _flow; fhigh = _fhigh; calc_impulse(); - FIRCORE::setImpulse_fircore (fircore, impulse, 1); + fircore->setImpulse(impulse, 1); delete[] impulse; } } From 130d40c218bc8e59f990257724d37cbbe7f29bdf Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 7 Aug 2024 21:14:09 +0200 Subject: [PATCH 36/46] WDSP: more rework --- wdsp/TXA.cpp | 193 +++++++++++++++++++-------- wdsp/TXA.hpp | 12 ++ wdsp/cfir.cpp | 253 ++++++++++++++++++----------------- wdsp/cfir.hpp | 27 ++-- wdsp/compress.cpp | 87 +++++------- wdsp/compress.hpp | 20 +-- wdsp/fmmod.cpp | 247 +++++++++++++++++----------------- wdsp/fmmod.hpp | 65 ++++----- wdsp/iqc.cpp | 328 ++++++++++++++++------------------------------ wdsp/iqc.hpp | 70 ++++++---- wdsp/nob.hpp | 18 ++- wdsp/osctrl.cpp | 149 +++++++++------------ wdsp/osctrl.hpp | 38 +++--- wdsp/slew.cpp | 211 ++++++++++++++--------------- wdsp/slew.hpp | 55 +++++--- 15 files changed, 866 insertions(+), 907 deletions(-) diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index f57c1426d..c1f160ef7 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -277,7 +277,7 @@ TXA::TXA( 1, // wintype 2.0); // gain - compressor = COMPRESSOR::create_compressor ( + compressor = new COMPRESSOR( 0, // run - OFF by default dsp_size, // size midbuff, // pointer to input buffer @@ -298,7 +298,7 @@ TXA::TXA( 1, // wintype 2.0); // gain - osctrl = OSCTRL::create_osctrl ( + osctrl = new OSCTRL( 0, // run dsp_size, // size midbuff, // input buffer @@ -368,7 +368,7 @@ TXA::TXA( 0.5); // carrier level - fmmod = FMMOD::create_fmmod ( + fmmod = new FMMOD( 0, // run - OFF by default dsp_size, // size midbuff, // pointer to input buffer @@ -392,15 +392,14 @@ TXA::TXA( dsp_rate, // sample rate 0); // mode - uslew = USLEW::create_uslew ( - this, + uslew = new USLEW( &upslew, // pointer to channel upslew flag dsp_size, // buffer size midbuff, // input buffer midbuff, // output buffer - (float) dsp_rate, // sample rate + (double) dsp_rate, // sample rate 0.000, // delay time - 0.005f); // upslew time + 0.005); // upslew time alcmeter = new METER( 1, // run @@ -446,17 +445,17 @@ TXA::TXA( // 256, // pin samples // 0.9); // alpha - iqc.p0 = iqc.p1 = IQC::create_iqc ( + iqc.p0 = iqc.p1 = new IQC( 0, // run dsp_size, // size midbuff, // input buffer midbuff, // output buffer - (float)dsp_rate, // sample rate + (double) dsp_rate, // sample rate 16, // ints - 0.005f, // changeover time + 0.005, // changeover time 256); // spi - cfir = CFIR::create_cfir( + cfir = new CFIR( 0, // run dsp_size, // size std::max(2048, dsp_size), // number of filter coefficients @@ -507,20 +506,20 @@ TXA::~TXA() // in reverse order, free each item we created delete outmeter; delete rsmpout; - CFIR::destroy_cfir(cfir); - IQC::destroy_iqc (iqc.p0); + delete cfir; + delete iqc.p0; delete sip1; delete alcmeter; - USLEW::destroy_uslew (uslew); + delete uslew; delete gen1; - FMMOD::destroy_fmmod (fmmod); + delete fmmod; delete ammod; delete alc; delete compmeter; delete bp2; - OSCTRL::destroy_osctrl (osctrl); + delete osctrl; delete bp1; - COMPRESSOR::destroy_compressor (compressor); + delete compressor; delete bp0; delete cfcmeter; delete cfcomp; @@ -554,20 +553,20 @@ void TXA::flush() cfcomp->flush(); cfcmeter->flush (); bp0->flush (); - COMPRESSOR::flush_compressor (compressor); + compressor->flush(); bp1->flush (); - OSCTRL::flush_osctrl (osctrl); + osctrl->flush(); bp2->flush (); compmeter->flush (); alc->flush (); ammod->flush(); - FMMOD::flush_fmmod (fmmod); + fmmod->flush(); gen1->flush(); - USLEW::flush_uslew (uslew); + uslew->flush(); alcmeter->flush (); sip1->flush(); - IQC::flush_iqc (iqc.p0); - CFIR::flush_cfir(cfir); + iqc.p0->flush(); + cfir->flush(); rsmpout->flush(); outmeter->flush (); } @@ -589,21 +588,21 @@ void TXA::execute() cfcomp->execute(0); // Continuous Frequency Compressor with post-EQ cfcmeter->execute (); // CFC+PostEQ Meter bp0->execute (0); // primary bandpass filter - COMPRESSOR::xcompressor (compressor); // COMP compressor + compressor->execute(); // COMP compressor bp1->execute (0); // aux bandpass (runs if COMP) - OSCTRL::xosctrl (osctrl); // CESSB Overshoot Control + osctrl->execute(); // CESSB Overshoot Control bp2->execute (0); // aux bandpass (runs if CESSB) compmeter->execute (); // COMP meter alc->execute (); // ALC ammod->execute(); // AM Modulator preemph->execute(1); // FM pre-emphasis (second option) - FMMOD::xfmmod (fmmod); // FM Modulator + fmmod->execute(); // FM Modulator gen1->execute(); // output signal generator (TUN and Two-tone) - USLEW::xuslew (uslew); // up-slew for AM, FM, and gens + uslew->execute(uslewCheck()); // up-slew for AM, FM, and gens alcmeter->execute (); // ALC Meter sip1->execute(0); // siphon data for display - IQC::xiqc (iqc.p0); // PureSignal correction - CFIR::xcfir(cfir); // compensating FIR filter (used Protocol_2 only) + iqc.p0->execute(); // PureSignal correction + cfir->execute(); // compensating FIR filter (used Protocol_2 only) rsmpout->execute(); // output resampler outmeter->execute (); // output meter } @@ -622,7 +621,7 @@ void TXA::setOutputSamplerate(int out_rate) { Unit::setBuffersOutputSamplerate(out_rate); // cfir - needs to know input rate of firmware CIC - CFIR::setOutRate_cfir (cfir, out_rate); + cfir->setOutRate(out_rate); // output resampler rsmpout->setBuffers(midbuff, outbuff); rsmpout->setOutRate(out_rate); @@ -654,20 +653,20 @@ void TXA::setDSPSamplerate(int dsp_rate) cfcomp->setSamplerate(dsp_rate); cfcmeter->setSamplerate (dsp_rate); bp0->setSamplerate (dsp_rate); - COMPRESSOR::setSamplerate_compressor (compressor, dsp_rate); + compressor->setSamplerate(dsp_rate); bp1->setSamplerate (dsp_rate); - OSCTRL::setSamplerate_osctrl (osctrl, dsp_rate); + osctrl->setSamplerate(dsp_rate); bp2->setSamplerate (dsp_rate); compmeter->setSamplerate (dsp_rate); alc->setSamplerate (dsp_rate); ammod->setSamplerate(dsp_rate); - FMMOD::setSamplerate_fmmod (fmmod, dsp_rate); + fmmod->setSamplerate(dsp_rate); gen1->setSamplerate(dsp_rate); - USLEW::setSamplerate_uslew (uslew, dsp_rate); + uslew->setSamplerate(dsp_rate); alcmeter->setSamplerate (dsp_rate); sip1->setSamplerate (dsp_rate); - IQC::setSamplerate_iqc (iqc.p0, dsp_rate); - CFIR::setSamplerate_cfir (cfir, dsp_rate); + iqc.p0->setSamplerate(dsp_rate); + cfir->setSamplerate(dsp_rate); // output resampler rsmpout->setBuffers(midbuff, outbuff); rsmpout->setInRate(dsp_rate); @@ -710,12 +709,12 @@ void TXA::setDSPBuffsize(int dsp_size) cfcmeter->setSize(dsp_size); bp0->setBuffers (midbuff, midbuff); bp0->setSize (dsp_size); - COMPRESSOR::setBuffers_compressor (compressor, midbuff, midbuff); - COMPRESSOR::setSize_compressor (compressor, dsp_size); + compressor->setBuffers(midbuff, midbuff); + compressor->setSize(dsp_size); bp1->setBuffers (midbuff, midbuff); bp1->setSize (dsp_size); - OSCTRL::setBuffers_osctrl (osctrl, midbuff, midbuff); - OSCTRL::setSize_osctrl (osctrl, dsp_size); + osctrl->setBuffers(midbuff, midbuff); + osctrl->setSize(dsp_size); bp2->setBuffers (midbuff, midbuff); bp2->setSize (dsp_size); compmeter->setBuffers(midbuff); @@ -724,20 +723,20 @@ void TXA::setDSPBuffsize(int dsp_size) alc->setSize( dsp_size); ammod->setBuffers(midbuff, midbuff); ammod->setSize(dsp_size); - FMMOD::setBuffers_fmmod (fmmod, midbuff, midbuff); - FMMOD::setSize_fmmod (fmmod, dsp_size); + fmmod->setBuffers(midbuff, midbuff); + fmmod->setSize(dsp_size); gen1->setBuffers(midbuff, midbuff); gen1->setSize(dsp_size); - USLEW::setBuffers_uslew (uslew, midbuff, midbuff); - USLEW::setSize_uslew (uslew, dsp_size); + uslew->setBuffers(midbuff, midbuff); + uslew->setSize(dsp_size); alcmeter->setBuffers (midbuff); alcmeter->setSize(dsp_size); sip1->setBuffers (midbuff); sip1->setSize (dsp_size); - IQC::setBuffers_iqc (iqc.p0, midbuff, midbuff); - IQC::setSize_iqc (iqc.p0, dsp_size); - CFIR::setBuffers_cfir (cfir, midbuff, midbuff); - CFIR::setSize_cfir (cfir, dsp_size); + iqc.p0->IQC::setBuffers(midbuff, midbuff); + iqc.p0->IQC::setSize(dsp_size); + cfir->setBuffers(midbuff, midbuff); + cfir->setSize(dsp_size); // output resampler rsmpout->setBuffers(midbuff, outbuff); rsmpout->setSize(dsp_size); @@ -1007,8 +1006,8 @@ void TXA::setNC(int _nc) setBandpassNC (_nc); preemph->setNC (_nc); eqp->setNC (_nc); - FMMOD::SetFMNC (*this, _nc); - CFIR::SetCFIRNC (*this, _nc); + fmmod->setNC (_nc); + cfir->setNC (_nc); state = oldstate; } @@ -1017,13 +1016,13 @@ void TXA::setMP(int _mp) setBandpassMP (_mp); preemph->setMP (_mp); eqp->setMP (_mp); - FMMOD::SetFMMP (*this, _mp); + fmmod->setMP (_mp); } void TXA::setFMAFFilter(float _low, float _high) { preemph->setFreqs (_low, _high); - FMMOD::SetFMAFFreqs(*this, _low, _high); + fmmod->setAFFreqs (_low, _high); } void TXA::SetBPSRun (TXA& txa, int _run) @@ -1095,7 +1094,7 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); + delete[] impulse; } a = txa.bps2; @@ -1106,9 +1105,95 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) delete[] (a->mults); impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); a->mults = FIR::fftcv_mults (2 * a->size, impulse); - delete[] (impulse); + delete[] impulse; } } +void TXA::SetCompressorRun (TXA& txa, int _run) +{ + if (txa.compressor->run != _run) + { + txa.compressor->run = _run; + txa.setupBPFilters(); + } +} + +void TXA::SetosctrlRun (TXA& txa, int run) +{ + if (txa.osctrl->run != run) + { + txa.osctrl->run = run; + txa.setupBPFilters(); + } +} + +void TXA::GetiqcValues (TXA& txa, std::vector& cm, std::vector& cc, std::vector& cs) +{ + IQC *a; + a = txa.iqc.p0; + cm.resize(a->ints * 4); + cc.resize(a->ints * 4); + cs.resize(a->ints * 4); + std::copy(a->cm[a->cset].begin(), a->cm[a->cset].begin() + a->ints * 4, cm.begin()); + std::copy(a->cc[a->cset].begin(), a->cc[a->cset].begin() + a->ints * 4, cc.begin()); + std::copy(a->cs[a->cset].begin(), a->cs[a->cset].begin() + a->ints * 4, cs.begin()); +} + +void TXA::SetiqcValues (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs) +{ + IQC *a; + a = txa.iqc.p0; + a->cset = 1 - a->cset; + std::copy(cm.begin(), cm.begin() + a->ints * 4, a->cm[a->cset].begin()); + std::copy(cc.begin(), cc.begin() + a->ints * 4, a->cc[a->cset].begin()); + std::copy(cs.begin(), cs.begin() + a->ints * 4, a->cs[a->cset].begin()); + a->state = IQC::IQCSTATE::RUN; +} + +void TXA::SetiqcSwap (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs) +{ + IQC *a = txa.iqc.p1; + a->cset = 1 - a->cset; + std::copy(cm.begin(), cm.begin() + a->ints * 4, a->cm[a->cset].begin()); + std::copy(cc.begin(), cc.begin() + a->ints * 4, a->cc[a->cset].begin()); + std::copy(cs.begin(), cs.begin() + a->ints * 4, a->cs[a->cset].begin()); + a->busy = 1; + a->state = IQC::IQCSTATE::SWAP; + a->count = 0; +} + +void TXA::SetiqcStart (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs) +{ + IQC *a = txa.iqc.p1; + a->cset = 0; + std::copy(cm.begin(), cm.begin() + a->ints * 4, a->cm[a->cset].begin()); + std::copy(cc.begin(), cc.begin() + a->ints * 4, a->cc[a->cset].begin()); + std::copy(cs.begin(), cs.begin() + a->ints * 4, a->cs[a->cset].begin()); + a->busy = 1; + a->state = IQC::IQCSTATE::BEGIN; + a->count = 0; + txa.iqc.p1->run = 1; +} + +void TXA::SetiqcEnd (TXA& txa) +{ + IQC *a = txa.iqc.p1; + a->busy = 1; + a->state = IQC::IQCSTATE::END; + a->count = 0; + txa.iqc.p1->run = 0; +} + +void TXA::GetiqcDogCount (TXA& txa, int* count) +{ + IQC *a = txa.iqc.p1; + *count = a->dog.count; +} + +void TXA::SetiqcDogCount (TXA& txa, int count) +{ + IQC *a = txa.iqc.p1; + a->dog.count = count; +} } // namespace WDSP diff --git a/wdsp/TXA.hpp b/wdsp/TXA.hpp index 55da102fb..6c3501411 100644 --- a/wdsp/TXA.hpp +++ b/wdsp/TXA.hpp @@ -194,6 +194,18 @@ public: static void SetBPSRun (TXA& txa, int run); static void SetBPSFreqs (TXA& txa, double low, double high); static void SetBPSWindow (TXA& txa, int wintype); + // COMPRESSOR + static void SetCompressorRun (TXA& txa, int run); + // OSCTRL + static void SetosctrlRun (TXA& txa, int run); + // IQC + static void GetiqcValues (TXA& txa, std::vector& cm, std::vector& cc, std::vector& cs); + static void SetiqcValues (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs); + static void SetiqcSwap (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs); + static void SetiqcStart (TXA& txa, const std::vector& cm, const std::vector& cc, const std::vector& cs); + static void SetiqcEnd (TXA& txa); + static void GetiqcDogCount (TXA& txa, int* count); + static void SetiqcDogCount (TXA& txa, int count); // Collectives void setNC(int nc); diff --git a/wdsp/cfir.cpp b/wdsp/cfir.cpp index 1e0b05bf7..456565df5 100644 --- a/wdsp/cfir.cpp +++ b/wdsp/cfir.cpp @@ -32,36 +32,36 @@ warren@wpratt.com namespace WDSP { -void CFIR::calc_cfir (CFIR *a) +void CFIR::calc() { float* impulse; - a->scale = 1.0 / (float)(2 * a->size); - impulse = cfir_impulse (a->nc, a->DD, a->R, a->Pairs, a->runrate, a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse); - delete[] (impulse); + scale = 1.0 / (float)(2 * size); + impulse = cfir_impulse (nc, DD, R, Pairs, runrate, cicrate, cutoff, xtype, xbw, 1, scale, wintype); + p = new FIRCORE(size, in, out, nc, mp, impulse); + delete[] impulse; } -void CFIR::decalc_cfir (CFIR *a) +void CFIR::decalc() { - delete (a->p); + delete p; } -CFIR* CFIR::create_cfir ( - int run, - int size, - int nc, - int mp, - float* in, - float* out, - int runrate, - int cicrate, - int DD, - int R, - int Pairs, - double cutoff, - int xtype, - double xbw, - int wintype +CFIR::CFIR( + int _run, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + int _runrate, + int _cicrate, + int _DD, + int _R, + int _Pairs, + double _cutoff, + int _xtype, + double _xbw, + int _wintype ) // run: 0 - no action; 1 - operate // size: number of complex samples in an input buffer to the CFIR filter @@ -77,87 +77,84 @@ CFIR* CFIR::create_cfir ( // xtype: 0 - fourth power transition; 1 - raised cosine transition; 2 - brick wall // xbw: width of raised cosine transition { - CFIR *a = new CFIR; - a->run = run; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->runrate = runrate; - a->cicrate = cicrate; - a->DD = DD; - a->R = R; - a->Pairs = Pairs; - a->cutoff = cutoff; - a->xtype = xtype; - a->xbw = xbw; - a->wintype = wintype; - calc_cfir (a); - return a; + run = _run; + size = _size; + nc = _nc; + mp = _mp; + in = _in; + out = _out; + runrate = _runrate; + cicrate = _cicrate; + DD = _DD; + R = _R; + Pairs = _Pairs; + cutoff = _cutoff; + xtype = _xtype; + xbw = _xbw; + wintype = _wintype; + calc(); } -void CFIR::destroy_cfir (CFIR *a) +CFIR::~CFIR() { - decalc_cfir (a); - delete (a); + decalc(); } -void CFIR::flush_cfir (CFIR *a) +void CFIR::flush() { - a->p->flush(); + p->flush(); } -void CFIR::xcfir (CFIR *a) +void CFIR::execute() { - if (a->run) - a->p->execute(); - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + if (run) + p->execute(); + else if (in != out) + std::copy( in, in + size * 2, out); } -void CFIR::setBuffers_cfir (CFIR *a, float* in, float* out) +void CFIR::setBuffers(float* _in, float* _out) { - decalc_cfir (a); - a->in = in; - a->out = out; - calc_cfir (a); + decalc(); + in = _in; + out = _out; + calc(); } -void CFIR::setSamplerate_cfir (CFIR *a, int rate) +void CFIR::setSamplerate(int rate) { - decalc_cfir (a); - a->runrate = rate; - calc_cfir (a); + decalc(); + runrate = rate; + calc(); } -void CFIR::setSize_cfir (CFIR *a, int size) +void CFIR::setSize(int _size) { - decalc_cfir (a); - a->size = size; - calc_cfir (a); + decalc(); + size = _size; + calc(); } -void CFIR::setOutRate_cfir (CFIR *a, int rate) +void CFIR::setOutRate(int rate) { - decalc_cfir (a); - a->cicrate = rate; - calc_cfir (a); + decalc(); + cicrate = rate; + calc(); } float* CFIR::cfir_impulse ( - int N, - int DD, - int R, - int Pairs, - double runrate, - double cicrate, - double cutoff, - int xtype, - double xbw, - int rtype, - double scale, - int wintype + int _N, + int _DD, + int _R, + int _Pairs, + double _runrate, + double _cicrate, + double _cutoff, + int _xtype, + double _xbw, + int _rtype, + double _scale, + int _wintype ) { // N: number of impulse response samples @@ -171,91 +168,93 @@ float* CFIR::cfir_impulse ( // xbw: transition bandwidth for raised cosine // rtype: 0 for real output, 1 for complex output // scale: scale factor to be applied to the output - int i, j; - double tmp, local_scale, ri, mag, fn; + int i; + int j; + double tmp; + double local_scale; + double ri; + double mag = 0; + double fn; float* impulse; - float* A = new float[N]; // (float *) malloc0 (N * sizeof (float)); - double ft = cutoff / cicrate; // normalized cutoff frequency - int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N - int c_samps = (int)(cutoff / runrate * N) + (N + 1) / 2 - N / 2; // number of unique samples within bandpass, OK for odd or even N - int x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N - double offset = 0.5 - 0.5 * (float)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N - double* xistion = new double[x_samps + 1]; // (float *) malloc0 ((x_samps + 1) * sizeof (float)); - double delta = PI / (float)x_samps; - double L = cicrate / runrate; - double phs = 0.0; + std::vector A(_N); + double ft = _cutoff / _cicrate; // normalized cutoff frequency + int u_samps = (_N + 1) / 2; // number of unique samples, OK for odd or even N + int c_samps = (int)(_cutoff / _runrate * _N) + (_N + 1) / 2 - _N / 2; // number of unique samples within bandpass, OK for odd or even N + auto x_samps = (int)(_xbw / _runrate * _N); // number of unique samples in transition region, OK for odd or even N + double offset = 0.5 - 0.5 * (double)((_N + 1) / 2 - _N / 2); // sample offset from center, OK for odd or even N + std::vector xistion(x_samps + 1); + double delta = PI / (double)x_samps; + double L = _cicrate / _runrate; + double _phs = 0.0; for (i = 0; i <= x_samps; i++) { - xistion[i] = 0.5 * (cos (phs) + 1.0); - phs += delta; + xistion[i] = 0.5 * (cos (_phs) + 1.0); + _phs += delta; } - if ((tmp = DD * R * sin (PI * ft / R) / sin (PI * DD * ft)) < 0.0) //normalize by peak gain + if ((tmp = _DD * _R * sin (PI * ft / _R) / sin (PI * _DD * ft)) < 0.0) //normalize by peak gain tmp = -tmp; - local_scale = scale / pow (tmp, Pairs); - if (xtype == 0) + local_scale = _scale / pow (tmp, _Pairs); + if (_xtype == 0) { for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0) { - fn = ri / (L * (float)N); + fn = ri / (L * (double) _N); if (fn <= ft) { if (fn == 0.0) tmp = 1.0; - else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0) + else if ((tmp = _DD * _R * sin (PI * fn / _R) / sin (PI * _DD * fn)) < 0.0) tmp = -tmp; - mag = pow (tmp, Pairs) * local_scale; + mag = pow (tmp, _Pairs) * local_scale; } else mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn); - A[i] = mag; + A[i] = (float) mag; } } - else if (xtype == 1) + else if (_xtype == 1) { for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0) { - fn = ri / (L *(float)N); + fn = ri / (L *(double) _N); if (i < c_samps) { if (fn == 0.0) tmp = 1.0; - else if ((tmp = DD * R * sin (PI * fn / R) / sin (PI * DD * fn)) < 0.0) + else if ((tmp = _DD * _R * sin (PI * fn / _R) / sin (PI * _DD * fn)) < 0.0) tmp = -tmp; - mag = pow (tmp, Pairs) * local_scale; - A[i] = mag; + mag = pow (tmp, _Pairs) * local_scale; + A[i] = (float) mag; } else if ( i >= c_samps && i <= c_samps + x_samps) - A[i] = mag * xistion[i - c_samps]; + A[i] = (float) (mag * xistion[i - c_samps]); else A[i] = 0.0; } } - else if (xtype == 2) + else if (_xtype == 2) { for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0) { - fn = ri / (L * (float)N); + fn = ri / (L * (double) _N); if (fn <= ft) { if (fn == 0.0) tmp = 1.0; - else if ((tmp = DD * R * sin(PI * fn / R) / sin(PI * DD * fn)) < 0.0) + else if ((tmp = _DD * _R * sin(PI * fn / _R) / sin(PI * _DD * fn)) < 0.0) tmp = -tmp; - mag = pow (tmp, Pairs) * local_scale; + mag = pow (tmp, _Pairs) * local_scale; } else mag = 0.0; - A[i] = mag; + A[i] = (float) mag; } } - if (N & 1) - for (i = u_samps, j = 2; i < N; i++, j++) + if (_N & 1) + for (i = u_samps, j = 2; i < _N; i++, j++) A[i] = A[u_samps - j]; else - for (i = u_samps, j = 1; i < N; i++, j++) + for (i = u_samps, j = 1; i < _N; i++, j++) A[i] = A[u_samps - j]; - impulse = FIR::fir_fsamp (N, A, rtype, 1.0, wintype); - // print_impulse ("cfirImpulse.txt", N, impulse, 1, 0); - delete[] A; - delete[] xistion; + impulse = FIR::fir_fsamp (_N, A.data(), _rtype, 1.0, _wintype); return impulse; } @@ -265,22 +264,20 @@ float* CFIR::cfir_impulse ( * * ********************************************************************************************************/ -void CFIR::SetCFIRRun (TXA& txa, int run) +void CFIR::setRun(int _run) { - txa.cfir->run = run; + run = _run; } -void CFIR::SetCFIRNC(TXA& txa, int nc) +void CFIR::setNC(int _nc) { // NOTE: 'nc' must be >= 'size' - CFIR *a; - a = txa.cfir; - if (a->nc != nc) + if (nc != _nc) { - a->nc = nc; - decalc_cfir(a); - calc_cfir(a); + nc = _nc; + decalc(); + calc(); } } diff --git a/wdsp/cfir.hpp b/wdsp/cfir.hpp index ebd3c027e..155a24286 100644 --- a/wdsp/cfir.hpp +++ b/wdsp/cfir.hpp @@ -56,7 +56,7 @@ public: int wintype; FIRCORE *p; - static CFIR* create_cfir ( + CFIR( int run, int size, int nc, @@ -73,13 +73,16 @@ public: double xbw, int wintype ); - static void destroy_cfir (CFIR *a); - static void flush_cfir (CFIR *a); - static void xcfir (CFIR *a); - static void setBuffers_cfir (CFIR *a, float* in, float* out); - static void setSamplerate_cfir (CFIR *a, int rate); - static void setSize_cfir (CFIR *a, int size); - static void setOutRate_cfir (CFIR *a, int rate); + CFIR(const CFIR&) = delete; + CFIR& operator=(CFIR& other) = delete; + ~CFIR(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setOutRate(int rate); static float* cfir_impulse ( int N, int DD, @@ -95,12 +98,12 @@ public: int wintype ); // TXA Properties - static void SetCFIRRun(TXA& txa, int run); - static void SetCFIRNC(TXA& txa, int nc); + void setRun(int run); + void setNC(int nc); private: - static void calc_cfir (CFIR *a); - static void decalc_cfir (CFIR *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/compress.cpp b/wdsp/compress.cpp index a36e32927..a816760d8 100644 --- a/wdsp/compress.cpp +++ b/wdsp/compress.cpp @@ -34,62 +34,56 @@ in the January 2010 issue of RadCom magazine. namespace WDSP { -COMPRESSOR* COMPRESSOR::create_compressor ( - int run, - int buffsize, - float* inbuff, - float* outbuff, - double gain -) +COMPRESSOR::COMPRESSOR( + int _run, + int _buffsize, + float* _inbuff, + float* _outbuff, + double _gain +) : + run(_run), + buffsize(_buffsize), + inbuff(_inbuff), + outbuff(_outbuff), + gain(_gain) +{} + +void COMPRESSOR::flush() { - auto *a = new COMPRESSOR; - a->run = run; - a->inbuff = inbuff; - a->outbuff = outbuff; - a->buffsize = buffsize; - a->gain = gain; - return a; + // Nothing to do } -void COMPRESSOR::destroy_compressor (COMPRESSOR *a) +void COMPRESSOR::execute() { - delete (a); -} - -void COMPRESSOR::flush_compressor (COMPRESSOR *) -{ -} - -void COMPRESSOR::xcompressor (COMPRESSOR *a) -{ - float mag; - if (a->run) - for (int i = 0; i < a->buffsize; i++) + double mag; + if (run) + for (int i = 0; i < buffsize; i++) { - mag = sqrt(a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] + a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]); - if (a->gain * mag > 1.0) - a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] / mag; + mag = sqrt(inbuff[2 * i + 0] * inbuff[2 * i + 0] + inbuff[2 * i + 1] * inbuff[2 * i + 1]); + if (gain * mag > 1.0) + outbuff[2 * i + 0] = (float) (inbuff[2 * i + 0] / mag); else - a->outbuff[2 * i + 0] = a->inbuff[2 * i + 0] * a->gain; - a->outbuff[2 * i + 1] = 0.0; + outbuff[2 * i + 0] = (float) (inbuff[2 * i + 0] * gain); + outbuff[2 * i + 1] = 0.0; } - else if (a->inbuff != a->outbuff) - std::copy(a->inbuff, a->inbuff + a->buffsize * 2, a->outbuff); + else if (inbuff != outbuff) + std::copy(inbuff, inbuff + buffsize * 2, outbuff); } -void COMPRESSOR::setBuffers_compressor (COMPRESSOR *a, float* in, float* out) +void COMPRESSOR::setBuffers(float* _in, float* _out) { - a->inbuff = in; - a->outbuff = out; + inbuff = _in; + outbuff = _out; } -void COMPRESSOR::setSamplerate_compressor (COMPRESSOR *, int) +void COMPRESSOR::setSamplerate(int) { + // Nothing to do } -void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size) +void COMPRESSOR::setSize(int _size) { - a->buffsize = size; + buffsize = _size; } /******************************************************************************************************** @@ -98,18 +92,9 @@ void COMPRESSOR::setSize_compressor (COMPRESSOR *a, int size) * * ********************************************************************************************************/ -void COMPRESSOR::SetCompressorRun (TXA& txa, int run) +void COMPRESSOR::setGain(float _gain) { - if (txa.compressor->run != run) - { - txa.compressor->run = run; - txa.setupBPFilters(); - } -} - -void COMPRESSOR::SetCompressorGain (TXA& txa, float gain) -{ - txa.compressor->gain = pow (10.0, gain / 20.0); + gain = pow (10.0, _gain / 20.0); } } // namespace WDSP diff --git a/wdsp/compress.hpp b/wdsp/compress.hpp index dc134bb58..37d3cfdc6 100644 --- a/wdsp/compress.hpp +++ b/wdsp/compress.hpp @@ -43,22 +43,24 @@ public: float *outbuff; double gain; - static COMPRESSOR* create_compressor ( + COMPRESSOR( int run, int buffsize, float* inbuff, float* outbuff, double gain ); - static void destroy_compressor (COMPRESSOR *a); - static void flush_compressor (COMPRESSOR *a); - static void xcompressor (COMPRESSOR *a); - static void setBuffers_compressor (COMPRESSOR *a, float* in, float* out); - static void setSamplerate_compressor (COMPRESSOR *a, int rate); - static void setSize_compressor (COMPRESSOR *a, int size); + COMPRESSOR(const COMPRESSOR&) = delete; + COMPRESSOR& operator=(COMPRESSOR& other) = delete; + ~COMPRESSOR() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetCompressorRun (TXA& txa, int run); - static void SetCompressorGain (TXA& txa, float gain); + void setGain(float gain); }; } // namespace WDSP diff --git a/wdsp/fmmod.cpp b/wdsp/fmmod.cpp index 81eed4486..ee000b4db 100644 --- a/wdsp/fmmod.cpp +++ b/wdsp/fmmod.cpp @@ -33,130 +33,128 @@ warren@wpratt.com namespace WDSP { -void FMMOD::calc_fmmod (FMMOD *a) +void FMMOD::calc() { // ctcss gen - a->tscale = 1.0 / (1.0 + a->ctcss_level); - a->tphase = 0.0; - a->tdelta = TWOPI * a->ctcss_freq / a->samplerate; + tscale = 1.0 / (1.0 + ctcss_level); + tphase = 0.0; + tdelta = TWOPI * ctcss_freq / samplerate; // mod - a->sphase = 0.0; - a->sdelta = TWOPI * a->deviation / a->samplerate; + sphase = 0.0; + sdelta = TWOPI * deviation / samplerate; // bandpass - a->bp_fc = a->deviation + a->f_high; + bp_fc = deviation + f_high; } -FMMOD* FMMOD::create_fmmod ( - int run, - int size, - float* in, - float* out, - int rate, - float dev, - float f_low, - float f_high, - int ctcss_run, - float ctcss_level, - float ctcss_freq, - int bp_run, - int nc, - int mp +FMMOD::FMMOD( + int _run, + int _size, + float* _in, + float* _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 = new FMMOD; float* impulse; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->samplerate = (float)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::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p = new FIRCORE(a->size, a->out, a->out, a->nc, a->mp, impulse); - delete[] (impulse); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + samplerate = (float) _rate; + deviation = _dev; + f_low = _f_low; + f_high = _f_high; + ctcss_run = _ctcss_run; + ctcss_level = _ctcss_level; + ctcss_freq = _ctcss_freq; + bp_run = _bp_run; + nc = _nc; + mp = _mp; + calc(); + impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p = new FIRCORE(size, out, out, nc, mp, impulse); + delete[] impulse; } -void FMMOD::destroy_fmmod (FMMOD *a) +FMMOD::~FMMOD() { - delete (a->p); - delete (a); + delete p; } -void FMMOD::flush_fmmod (FMMOD *a) +void FMMOD::flush() { - a->tphase = 0.0; - a->sphase = 0.0; + tphase = 0.0; + sphase = 0.0; } -void FMMOD::xfmmod (FMMOD *a) +void FMMOD::execute() { - int i; - float dp, magdp, peak; - if (a->run) + double dp; + double magdp; + double peak; + if (run) { peak = 0.0; - for (i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - if (a->ctcss_run) + if (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)); + tphase += tdelta; + if (tphase >= TWOPI) tphase -= TWOPI; + out[2 * i + 0] = (float) (tscale * (in[2 * i + 0] + ctcss_level * cos (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); + dp = out[2 * i + 0] * sdelta; + sphase += dp; + if (sphase >= TWOPI) sphase -= TWOPI; + if (sphase < 0.0 ) sphase += TWOPI; + out[2 * i + 0] = (float) (0.7071 * cos (sphase)); + out[2 * i + 1] = (float) (0.7071 * sin (sphase)); if ((magdp = dp) < 0.0) magdp = - magdp; if (magdp > peak) peak = magdp; } - //print_deviation ("peakdev.txt", peak, a->samplerate); - if (a->bp_run) - a->p->execute(); + + if (bp_run) + p->execute(); } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void FMMOD::setBuffers_fmmod (FMMOD *a, float* in, float* out) +void FMMOD::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; - calc_fmmod (a); - a->p->setBuffers(a->out, a->out); + in = _in; + out = _out; + calc(); + p->setBuffers(out, out); } -void FMMOD::setSamplerate_fmmod (FMMOD *a, int rate) +void FMMOD::setSamplerate(int _rate) { float* impulse; - a->samplerate = rate; - calc_fmmod (a); - impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p->setImpulse(impulse, 1); - delete[] (impulse); + samplerate = _rate; + calc(); + impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse, 1); + delete[] impulse; } -void FMMOD::setSize_fmmod (FMMOD *a, int size) +void FMMOD::setSize(int _size) { float* impulse; - a->size = size; - calc_fmmod (a); - a->p->setSize(a->size); - impulse = FIR::fir_bandpass(a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p->setImpulse(impulse, 1); - delete[] (impulse); + size = _size; + calc(); + p->setSize(size); + impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse, 1); + delete[] impulse; } /******************************************************************************************************** @@ -165,76 +163,67 @@ void FMMOD::setSize_fmmod (FMMOD *a, int size) * * ********************************************************************************************************/ -void FMMOD::SetFMDeviation (TXA& txa, float deviation) +void FMMOD::setDeviation(float _deviation) { - FMMOD *a = txa.fmmod; - float bp_fc = a->f_high + deviation; - float* impulse = FIR::fir_bandpass (a->nc, -bp_fc, +bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p->setImpulse(impulse, 0); - delete[] (impulse); - a->deviation = deviation; + double _bp_fc = f_high + _deviation; + float* impulse = FIR::fir_bandpass (nc, -_bp_fc, +_bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse, 0); + delete[] impulse; + deviation = _deviation; // mod - a->sphase = 0.0; - a->sdelta = TWOPI * a->deviation / a->samplerate; + sphase = 0.0; + sdelta = TWOPI * deviation / samplerate; // bandpass - a->bp_fc = bp_fc; - a->p->setUpdate(); + bp_fc = _bp_fc; + p->setUpdate(); } -void FMMOD::SetCTCSSFreq (TXA& txa, float freq) +void FMMOD::setCTCSSFreq (float _freq) { - FMMOD *a; - a = txa.fmmod; - a->ctcss_freq = freq; - a->tphase = 0.0; - a->tdelta = TWOPI * a->ctcss_freq / a->samplerate; + ctcss_freq = _freq; + tphase = 0.0; + tdelta = TWOPI * ctcss_freq / samplerate; } -void FMMOD::SetCTCSSRun (TXA& txa, int run) +void FMMOD::setCTCSSRun (int _run) { - txa.fmmod->ctcss_run = run; + ctcss_run = _run; } -void FMMOD::SetFMNC (TXA& txa, int nc) +void FMMOD::setNC(int _nc) { - FMMOD *a; float* impulse; - a = txa.fmmod; - if (a->nc != nc) + if (nc != _nc) { - a->nc = nc; - impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p->setNc(a->nc, impulse); - delete[] (impulse); + nc = _nc; + impulse = FIR::fir_bandpass (nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setNc(nc, impulse); + delete[] impulse; } } -void FMMOD::SetFMMP (TXA& txa, int mp) +void FMMOD::setMP(int _mp) { - FMMOD *a; - a = txa.fmmod; - if (a->mp != mp) + if (mp != _mp) { - a->mp = mp; - a->p->setMp(a->mp); + mp = _mp; + p->setMp(mp); } } -void FMMOD::SetFMAFFreqs (TXA& txa, float low, float high) +void FMMOD::setAFFreqs(float _low, float _high) { - FMMOD *a; float* impulse; - a = txa.fmmod; - if (a->f_low != low || a->f_high != high) + if (f_low != _low || f_high != _high) { - a->f_low = low; - a->f_high = high; - a->bp_fc = a->deviation + a->f_high; - impulse = FIR::fir_bandpass (a->nc, -a->bp_fc, +a->bp_fc, a->samplerate, 0, 1, 1.0 / (2 * a->size)); - a->p->setImpulse(impulse, 1); - delete[] (impulse); + f_low = _low; + f_high = _high; + bp_fc = deviation + f_high; + impulse = FIR::fir_bandpass (nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse, 1); + delete[] impulse; } } diff --git a/wdsp/fmmod.hpp b/wdsp/fmmod.hpp index 30dfc87ac..411d3f447 100644 --- a/wdsp/fmmod.hpp +++ b/wdsp/fmmod.hpp @@ -42,59 +42,62 @@ public: int size; float* in; float* out; - float samplerate; - float deviation; - float f_low; - float f_high; + double samplerate; + double deviation; + double f_low; + double f_high; int ctcss_run; - float ctcss_level; - float ctcss_freq; + double ctcss_level; + double ctcss_freq; // for ctcss gen - float tscale; - float tphase; - float tdelta; + double tscale; + double tphase; + double tdelta; // mod - float sphase; - float sdelta; + double sphase; + double sdelta; // bandpass int bp_run; - float bp_fc; + double bp_fc; int nc; int mp; FIRCORE *p; - static FMMOD* create_fmmod ( + FMMOD( int run, int size, float* in, float* out, int rate, - float dev, - float f_low, - float f_high, + double dev, + double f_low, + double f_high, int ctcss_run, - float ctcss_level, - float ctcss_freq, + double ctcss_level, + double ctcss_freq, int bp_run, int nc, int mp ); - static void destroy_fmmod (FMMOD *a); - static void flush_fmmod (FMMOD *a); - static void xfmmod (FMMOD *a); - static void setBuffers_fmmod (FMMOD *a, float* in, float* out); - static void setSamplerate_fmmod (FMMOD *a, int rate); - static void setSize_fmmod (FMMOD *a, int size); + FMMOD(const FMMOD&) = delete; + FMMOD& operator=(const FMMOD& other) = delete; + ~FMMOD(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetFMDeviation (TXA& txa, float deviation); - static void SetCTCSSFreq (TXA& txa, float freq); - static void SetCTCSSRun (TXA& txa, int run); - static void SetFMMP (TXA& txa, int mp); - static void SetFMNC (TXA& txa, int nc); - static void SetFMAFFreqs (TXA& txa, float low, float high); + void setDeviation(float deviation); + void setCTCSSFreq(float freq); + void setCTCSSRun(int run); + void setMP(int mp); + void setNC(int nc); + void setAFFreqs(float low, float high); private: - static void calc_fmmod (FMMOD *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/iqc.cpp b/wdsp/iqc.cpp index 4f273ad24..3ad055a80 100644 --- a/wdsp/iqc.cpp +++ b/wdsp/iqc.cpp @@ -34,278 +34,174 @@ warren@wpratt.com namespace WDSP { -void IQC::size_iqc (IQC *a) +void IQC::size_iqc() { int i; - a->t = new float[a->ints + 1]; // (float *) malloc0 ((a->ints + 1) * sizeof(float)); - for (i = 0; i <= a->ints; i++) - a->t[i] = (float)i / (float)a->ints; + t.resize(ints + 1); + for (i = 0; i <= ints; i++) + t[i] = (double)i / (double)ints; for (i = 0; i < 2; i++) { - a->cm[i] = new float[a->ints * 4]; // (float *) malloc0 (a->ints * 4 * sizeof(float)); - a->cc[i] = new float[a->ints * 4]; // (float *) malloc0 (a->ints * 4 * sizeof(float)); - a->cs[i] = new float[a->ints * 4]; // (float *) malloc0 (a->ints * 4 * sizeof(float)); + cm[i].resize(ints * 4); + cc[i].resize(ints * 4); + cs[i].resize(ints * 4); } - a->dog.cpi = new int[a->ints]; // (int *) malloc0 (a->ints * sizeof (int)); - a->dog.count = 0; - a->dog.full_ints = 0; + dog.cpi.resize(ints); + dog.count = 0; + dog.full_ints = 0; } -void IQC::desize_iqc (IQC *a) +void IQC::calc() { - int i; - delete[] (a->dog.cpi); - for (i = 0; i < 2; i++) - { - delete[] (a->cm[i]); - delete[] (a->cc[i]); - delete[] (a->cs[i]); - } - delete[] (a->t); -} - -void IQC::calc_iqc (IQC *a) -{ - int i; - float delta, theta; - a->cset = 0; - a->count = 0; - a->state = 0; - a->busy = 0; - a->ntup = (int)(a->tup * a->rate); - a->cup = new float[a->ntup + 1]; // (float *) malloc0 ((a->ntup + 1) * sizeof (float)); - delta = PI / (float)a->ntup; + double delta; + double theta; + cset = 0; + count = 0; + state = IQCSTATE::RUN; + busy = 0; + ntup = (int)(tup * rate); + cup.resize(ntup + 1); + delta = PI / (double)ntup; theta = 0.0; - for (i = 0; i <= a->ntup; i++) + for (int i = 0; i <= ntup; i++) { - a->cup[i] = 0.5 * (1.0 - cos (theta)); + cup[i] = 0.5 * (1.0 - cos (theta)); theta += delta; } - size_iqc (a); + size_iqc(); } -void IQC::decalc_iqc (IQC *a) +IQC::IQC( + int _run, + int _size, + float* _in, + float* _out, + double _rate, + int _ints, + double _tup, + int _spi +) : + run(_run), + size(_size), + in(_in), + out(_out), + rate(_rate), + ints(_ints), + tup(_tup) { - desize_iqc (a); - delete[] (a->cup); + dog.spi = _spi; + calc(); } -IQC* IQC::create_iqc (int run, int size, float* in, float* out, float rate, int ints, float tup, int spi) +void IQC::flush() { - IQC *a = new IQC; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->ints = ints; - a->tup = tup; - a->dog.spi = spi; - calc_iqc (a); - return a; + // Nothing to do } -void IQC::destroy_iqc (IQC *a) +void IQC::execute() { - decalc_iqc (a); - delete (a); -} - -void IQC::flush_iqc (IQC *) -{ - -} - -enum _iqcstate -{ - RUN = 0, - BEGIN, - SWAP, - END, - DONE -}; - -void IQC::xiqc (IQC *a) -{ - if (a->run == 1) + if (run == 1) { - int i, k, cset, mset; - float I, Q, env, dx, ym, yc, ys, PRE0, PRE1; - for (i = 0; i < a->size; i++) + int k; + int icset; + int mset; + double I; + double Q; + double env; + double dx; + double ym; + double yc; + double ys; + double PRE0; + double PRE1; + for (int i = 0; i < size; i++) { - I = a->in[2 * i + 0]; - Q = a->in[2 * i + 1]; + I = in[2 * i + 0]; + Q = in[2 * i + 1]; env = sqrt (I * I + Q * Q); - if ((k = (int)(env * a->ints)) > a->ints - 1) k = a->ints - 1; - dx = env - a->t[k]; - cset = a->cset; - ym = a->cm[cset][4 * k + 0] + dx * (a->cm[cset][4 * k + 1] + dx * (a->cm[cset][4 * k + 2] + dx * a->cm[cset][4 * k + 3])); - yc = a->cc[cset][4 * k + 0] + dx * (a->cc[cset][4 * k + 1] + dx * (a->cc[cset][4 * k + 2] + dx * a->cc[cset][4 * k + 3])); - ys = a->cs[cset][4 * k + 0] + dx * (a->cs[cset][4 * k + 1] + dx * (a->cs[cset][4 * k + 2] + dx * a->cs[cset][4 * k + 3])); + if ((k = (int)(env * ints)) > ints - 1) k = ints - 1; + dx = env - t[k]; + icset = cset; + ym = cm[icset][4 * k + 0] + dx * (cm[icset][4 * k + 1] + dx * (cm[icset][4 * k + 2] + dx * cm[icset][4 * k + 3])); + yc = cc[icset][4 * k + 0] + dx * (cc[icset][4 * k + 1] + dx * (cc[icset][4 * k + 2] + dx * cc[icset][4 * k + 3])); + ys = cs[icset][4 * k + 0] + dx * (cs[icset][4 * k + 1] + dx * (cs[icset][4 * k + 2] + dx * cs[icset][4 * k + 3])); PRE0 = ym * (I * yc - Q * ys); PRE1 = ym * (I * ys + Q * yc); - switch (a->state) + switch (state) { - case RUN: - if (a->dog.cpi[k] != a->dog.spi) - if (++a->dog.cpi[k] == a->dog.spi) - a->dog.full_ints++; - if (a->dog.full_ints == a->ints) + case IQCSTATE::RUN: + if ((dog.cpi[k] != dog.spi) && (++dog.cpi[k] == dog.spi)) + dog.full_ints++; + if (dog.full_ints == ints) { - ++a->dog.count; - a->dog.full_ints = 0; - memset (a->dog.cpi, 0, a->ints * sizeof (int)); + ++dog.count; + dog.full_ints = 0; + std::fill(dog.cpi.begin(), dog.cpi.end(), 0); } break; - case BEGIN: - PRE0 = (1.0 - a->cup[a->count]) * I + a->cup[a->count] * PRE0; - PRE1 = (1.0 - a->cup[a->count]) * Q + a->cup[a->count] * PRE1; - if (a->count++ == a->ntup) + case IQCSTATE::BEGIN: + PRE0 = (1.0 - cup[count]) * I + cup[count] * PRE0; + PRE1 = (1.0 - cup[count]) * Q + cup[count] * PRE1; + if (count++ == ntup) { - a->state = RUN; - a->count = 0; - a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0); + state = IQCSTATE::RUN; + count = 0; + busy = 0; } break; - case SWAP: + case IQCSTATE::SWAP: mset = 1 - cset; - ym = a->cm[mset][4 * k + 0] + dx * (a->cm[mset][4 * k + 1] + dx * (a->cm[mset][4 * k + 2] + dx * a->cm[mset][4 * k + 3])); - yc = a->cc[mset][4 * k + 0] + dx * (a->cc[mset][4 * k + 1] + dx * (a->cc[mset][4 * k + 2] + dx * a->cc[mset][4 * k + 3])); - ys = a->cs[mset][4 * k + 0] + dx * (a->cs[mset][4 * k + 1] + dx * (a->cs[mset][4 * k + 2] + dx * a->cs[mset][4 * k + 3])); - PRE0 = (1.0 - a->cup[a->count]) * ym * (I * yc - Q * ys) + a->cup[a->count] * PRE0; - PRE1 = (1.0 - a->cup[a->count]) * ym * (I * ys + Q * yc) + a->cup[a->count] * PRE1; - if (a->count++ == a->ntup) + ym = cm[mset][4 * k + 0] + dx * (cm[mset][4 * k + 1] + dx * (cm[mset][4 * k + 2] + dx * cm[mset][4 * k + 3])); + yc = cc[mset][4 * k + 0] + dx * (cc[mset][4 * k + 1] + dx * (cc[mset][4 * k + 2] + dx * cc[mset][4 * k + 3])); + ys = cs[mset][4 * k + 0] + dx * (cs[mset][4 * k + 1] + dx * (cs[mset][4 * k + 2] + dx * cs[mset][4 * k + 3])); + PRE0 = (1.0 - cup[count]) * ym * (I * yc - Q * ys) + cup[count] * PRE0; + PRE1 = (1.0 - cup[count]) * ym * (I * ys + Q * yc) + cup[count] * PRE1; + if (count++ == ntup) { - a->state = RUN; - a->count = 0; - a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0); + state = IQCSTATE::RUN; + count = 0; + busy = 0; } break; - case END: - PRE0 = (1.0 - a->cup[a->count]) * PRE0 + a->cup[a->count] * I; - PRE1 = (1.0 - a->cup[a->count]) * PRE1 + a->cup[a->count] * Q; - if (a->count++ == a->ntup) + case IQCSTATE::END: + PRE0 = (1.0 - cup[count]) * PRE0 + cup[count] * I; + PRE1 = (1.0 - cup[count]) * PRE1 + cup[count] * Q; + if (count++ == ntup) { - a->state = DONE; - a->count = 0; - a->busy = 0; // InterlockedBitTestAndReset (&a->busy, 0); + state = IQCSTATE::DONE; + count = 0; + busy = 0; } break; - case DONE: + case IQCSTATE::DONE: PRE0 = I; PRE1 = Q; break; } - a->out[2 * i + 0] = PRE0; - a->out[2 * i + 1] = PRE1; - // print_iqc_values("iqc.txt", a->state, env, PRE0, PRE1, ym, yc, ys, 1.1); + out[2 * i + 0] = (float) PRE0; + out[2 * i + 1] = (float) PRE1; } } - else if (a->out != a->in) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (out != in) + std::copy( in, in + size * 2, out); } -void IQC::setBuffers_iqc (IQC *a, float* in, float* out) +void IQC::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void IQC::setSamplerate_iqc (IQC *a, int rate) +void IQC::setSamplerate(int _rate) { - decalc_iqc (a); - a->rate = rate; - calc_iqc (a); + rate = _rate; + calc(); } -void IQC::setSize_iqc (IQC *a, int size) +void IQC::setSize(int _size) { - a->size = size; -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -void IQC::GetiqcValues (TXA& txa, float* cm, float* cc, float* cs) -{ - IQC *a; - a = txa.iqc.p0; - memcpy (cm, a->cm[a->cset], a->ints * 4 * sizeof (float)); - memcpy (cc, a->cc[a->cset], a->ints * 4 * sizeof (float)); - memcpy (cs, a->cs[a->cset], a->ints * 4 * sizeof (float)); -} - -void IQC::SetiqcValues (TXA& txa, float* cm, float* cc, float* cs) -{ - IQC *a; - a = txa.iqc.p0; - a->cset = 1 - a->cset; - memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (float)); - memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (float)); - memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (float)); - a->state = RUN; -} - -void IQC::SetiqcSwap (TXA& txa, float* cm, float* cc, float* cs) -{ - IQC *a = txa.iqc.p1; - a->cset = 1 - a->cset; - memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (float)); - memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (float)); - memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (float)); - a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0); - a->state = SWAP; - a->count = 0; - // while (_InterlockedAnd (&a->busy, 1)) Sleep(1); - while (a->busy == 1) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} - -void IQC::SetiqcStart (TXA& txa, float* cm, float* cc, float* cs) -{ - IQC *a = txa.iqc.p1; - a->cset = 0; - memcpy (a->cm[a->cset], cm, a->ints * 4 * sizeof (float)); - memcpy (a->cc[a->cset], cc, a->ints * 4 * sizeof (float)); - memcpy (a->cs[a->cset], cs, a->ints * 4 * sizeof (float)); - a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0); - a->state = BEGIN; - a->count = 0; - txa.iqc.p1->run = 1; //InterlockedBitTestAndSet (&txa.iqc.p1->run, 0); - // while (_InterlockedAnd (&a->busy, 1)) Sleep(1); - while (a->busy == 1) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } -} - -void IQC::SetiqcEnd (TXA& txa) -{ - IQC *a = txa.iqc.p1; - a->busy = 1; // InterlockedBitTestAndSet (&a->busy, 0); - a->state = END; - a->count = 0; - // while (_InterlockedAnd (&a->busy, 1)) Sleep(1); - while (a->busy == 1) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - txa.iqc.p1->run = 0; //InterlockedBitTestAndReset (&txa.iqc.p1->run, 0); -} - -void IQC::GetiqcDogCount (TXA& txa, int* count) -{ - IQC *a = txa.iqc.p1; - *count = a->dog.count; -} - -void IQC::SetiqcDogCount (TXA& txa, int count) -{ - IQC *a = txa.iqc.p1; - a->dog.count = count; + size = _size; } } // namespace WDSP diff --git a/wdsp/iqc.hpp b/wdsp/iqc.hpp index bf8bac161..4f58283c4 100644 --- a/wdsp/iqc.hpp +++ b/wdsp/iqc.hpp @@ -28,6 +28,9 @@ warren@wpratt.com #ifndef wdsp_iqc_h #define wdsp_iqc_h +#include +#include + #include "export.h" namespace WDSP { @@ -37,52 +40,63 @@ class TXA; class WDSP_API IQC { public: + enum class IQCSTATE + { + RUN = 0, + BEGIN, + SWAP, + END, + DONE + }; + long run; long busy; int size; float* in; float* out; - float rate; + double rate; int ints; - float* t; + std::vector t; int cset; - float* cm[2]; - float* cc[2]; - float* cs[2]; - float tup; - float* cup; + std::array, 2> cm; + std::array, 2> cc; + std::array, 2> cs; + double tup; + std::vector cup; int count; int ntup; - int state; + IQCSTATE state; struct { int spi; - int* cpi; + std::vector cpi; int full_ints; int count; } dog; - static IQC* create_iqc (int run, int size, float* in, float* out, float rate, int ints, float tup, int spi); - static void destroy_iqc (IQC *a); - static void flush_iqc (IQC *a); - static void xiqc (IQC *a); - static void setBuffers_iqc (IQC *a, float* in, float* out); - static void setSamplerate_iqc (IQC *a, int rate); - static void setSize_iqc (IQC *a, int size); - // TXA Properties - static void GetiqcValues (TXA& txa, float* cm, float* cc, float* cs); - static void SetiqcValues (TXA& txa, float* cm, float* cc, float* cs); - static void SetiqcSwap (TXA& txa, float* cm, float* cc, float* cs); - static void SetiqcStart (TXA& txa, float* cm, float* cc, float* cs); - static void SetiqcEnd (TXA& txa); - static void GetiqcDogCount (TXA& txa, int* count); - static void SetiqcDogCount (TXA& txa, int count); + IQC( + int run, + int size, + float* in, + float* out, + double rate, + int ints, + double tup, + int spi + ); + IQC(const IQC&) = delete; + IQC& operator=(const IQC& other) = delete; + ~IQC() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void size_iqc (IQC *a); - static void desize_iqc (IQC *a); - static void calc_iqc (IQC *a); - static void decalc_iqc (IQC *a); + void size_iqc(); + void calc(); }; } // namespace WDSP diff --git a/wdsp/nob.hpp b/wdsp/nob.hpp index 6264d00be..d8850536b 100644 --- a/wdsp/nob.hpp +++ b/wdsp/nob.hpp @@ -75,12 +75,18 @@ public: int out_idx; // ring buffer position from which delayed samples are pulled double backmult; // multiplier for waveform averaging double ombackmult; // multiplier for waveform averaging - double I1, Q1; - double I2, Q2; - double I, Q; - double Ilast, Qlast; - double deltaI, deltaQ; - double Inext, Qnext; + double I1; + double Q1; + double I2; + double Q2; + double I; + double Q; + double Ilast; + double Qlast; + double deltaI; + double deltaQ; + double Inext; + double Qnext; int overflow; NOB( diff --git a/wdsp/osctrl.cpp b/wdsp/osctrl.cpp index 084f495f7..de2cfee84 100644 --- a/wdsp/osctrl.cpp +++ b/wdsp/osctrl.cpp @@ -35,122 +35,91 @@ warren@wpratt.com namespace WDSP { -void OSCTRL::calc_osctrl (OSCTRL *a) +void OSCTRL::calc() { - a->pn = (int)((0.3 / a->bw) * a->rate + 0.5); - if ((a->pn & 1) == 0) a->pn += 1; - if (a->pn < 3) a->pn = 3; - a->dl_len = a->pn >> 1; - a->dl = new float[a->pn * 2]; // (float *) malloc0 (a->pn * sizeof (complex)); - a->dlenv = new float[a->pn]; // (float *) malloc0 (a->pn * sizeof (float)); - a->in_idx = 0; - a->out_idx = a->in_idx + a->dl_len; - a->max_env = 0.0; + pn = (int)((0.3 / bw) * rate + 0.5); + if ((pn & 1) == 0) pn += 1; + if (pn < 3) pn = 3; + dl_len = pn >> 1; + dl.resize(pn * 2); + dlenv.resize(pn); + in_idx = 0; + out_idx = in_idx + dl_len; + max_env = 0.0; } -void OSCTRL::decalc_osctrl (OSCTRL *a) +OSCTRL::OSCTRL( + int _run, + int _size, + float* _inbuff, + float* _outbuff, + int _rate, + double _osgain +) : + run(_run), + size(_size), + inbuff(_inbuff), + outbuff(_outbuff), + rate(_rate), + osgain(_osgain) { - delete[] (a->dlenv); - delete[] (a->dl); + bw = 3000.0; + calc(); } -OSCTRL* OSCTRL::create_osctrl ( - int run, - int size, - float* inbuff, - float* outbuff, - int rate, - float osgain -) +void OSCTRL::flush() { - OSCTRL *a = new OSCTRL; - a->run = run; - a->size = size; - a->inbuff = inbuff; - a->outbuff = outbuff; - a->rate = rate; - a->osgain = osgain; - a->bw = 3000.0; - calc_osctrl (a); - return a; + std::fill(dl.begin(), dl.end(), 0); + std::fill(dlenv.begin(), dlenv.end(), 0); } -void OSCTRL::destroy_osctrl (OSCTRL *a) +void OSCTRL::execute() { - decalc_osctrl (a); - delete (a); -} - -void OSCTRL::flush_osctrl (OSCTRL *a) -{ - std::fill(a->dl, a->dl + a->dl_len * 2, 0); - std::fill(a->dlenv, a->dlenv + a->pn, 0); -} - -void OSCTRL::xosctrl (OSCTRL *a) -{ - if (a->run) + if (run) { - int i, j; - float divisor; - for (i = 0; i < a->size; i++) + double divisor; + for (int i = 0; i < size; i++) { - a->dl[2 * a->in_idx + 0] = a->inbuff[2 * i + 0]; // put sample in delay line - a->dl[2 * a->in_idx + 1] = a->inbuff[2 * i + 1]; - a->env_out = a->dlenv[a->in_idx]; // take env out of delay line - a->dlenv[a->in_idx] = sqrt (a->inbuff[2 * i + 0] * a->inbuff[2 * i + 0] // put env in delay line - + a->inbuff[2 * i + 1] * a->inbuff[2 * i + 1]); - if (a->dlenv[a->in_idx] > a->max_env) a->max_env = a->dlenv[a->in_idx]; - if (a->env_out >= a->max_env && a->env_out > 0.0) // run the buffer + dl[2 * in_idx + 0] = inbuff[2 * i + 0]; // put sample in delay line + dl[2 * in_idx + 1] = inbuff[2 * i + 1]; + env_out = dlenv[in_idx]; // take env out of delay line + dlenv[in_idx] = sqrt (inbuff[2 * i + 0] * inbuff[2 * i + 0] // put env in delay line + + inbuff[2 * i + 1] * inbuff[2 * i + 1]); + if (dlenv[in_idx] > max_env) max_env = dlenv[in_idx]; + if (env_out >= max_env && env_out > 0.0) // run the buffer { - a->max_env = 0.0; - for (j = 0; j < a->pn; j++) - if (a->dlenv[j] > a->max_env) a->max_env = a->dlenv[j]; + max_env = 0.0; + for (int j = 0; j < pn; j++) + if (dlenv[j] > max_env) max_env = dlenv[j]; } - if (a->max_env > 1.0) divisor = 1.0 + a->osgain * (a->max_env - 1.0); + if (max_env > 1.0) divisor = 1.0 + osgain * (max_env - 1.0); else divisor = 1.0; - a->outbuff[2 * i + 0] = a->dl[2 * a->out_idx + 0] / divisor; // output sample - a->outbuff[2 * i + 1] = a->dl[2 * a->out_idx + 1] / divisor; - if (--a->in_idx < 0) a->in_idx += a->pn; - if (--a->out_idx < 0) a->out_idx += a->pn; + outbuff[2 * i + 0] = (float) (dl[2 * out_idx + 0] / divisor); // output sample + outbuff[2 * i + 1] = (float) (dl[2 * out_idx + 1] / divisor); + if (--in_idx < 0) in_idx += pn; + if (--out_idx < 0) out_idx += pn; } } - else if (a->inbuff != a->outbuff) - std::copy(a->inbuff, a->inbuff + a->size * 2, a->outbuff); + else if (inbuff != outbuff) + std::copy(inbuff, inbuff + size * 2, outbuff); } -void OSCTRL::setBuffers_osctrl (OSCTRL *a, float* in, float* out) +void OSCTRL::setBuffers(float* _in, float* _out) { - a->inbuff = in; - a->outbuff = out; + inbuff = _in; + outbuff = _out; } -void OSCTRL::setSamplerate_osctrl (OSCTRL *a, int rate) +void OSCTRL::setSamplerate(int _rate) { - decalc_osctrl (a); - a->rate = rate; - calc_osctrl (a); + rate = _rate; + calc(); } -void OSCTRL::setSize_osctrl (OSCTRL *a, int size) +void OSCTRL::setSize(int _size) { - a->size = size; - flush_osctrl (a); -} - -/******************************************************************************************************** -* * -* TXA Properties * -* * -********************************************************************************************************/ - -void OSCTRL::SetosctrlRun (TXA& txa, int run) -{ - if (txa.osctrl->run != run) - { - txa.osctrl->run = run; - txa.setupBPFilters(); - } + size = _size; + flush(); } } // namespace WDSP diff --git a/wdsp/osctrl.hpp b/wdsp/osctrl.hpp index 14a31d51c..a5b49ffb7 100644 --- a/wdsp/osctrl.hpp +++ b/wdsp/osctrl.hpp @@ -32,6 +32,8 @@ warren@wpratt.com #ifndef wdsp_osctrl_h #define wdsp_osctrl_h +#include + #include "export.h" namespace WDSP { @@ -46,37 +48,37 @@ public: float *inbuff; // input buffer float *outbuff; // output buffer int rate; // sample rate - float osgain; // gain applied to overshoot "clippings" - float bw; // bandwidth + double osgain; // gain applied to overshoot "clippings" + double bw; // bandwidth int pn; // "peak stretcher" window, samples int dl_len; // delay line length, samples - float* dl; // delay line for complex samples - float* dlenv; // delay line for envelope values + std::vector dl; // delay line for complex samples + std::vector dlenv; // delay line for envelope values int in_idx; // input index for dl int out_idx; // output index for dl - float max_env; // maximum env value in env delay line - float env_out; + double max_env; // maximum env value in env delay line + double env_out; - static void xosctrl (OSCTRL *a); - static OSCTRL* create_osctrl ( + OSCTRL( int run, int size, float* inbuff, float* outbuff, int rate, - float osgain + double osgain ); - static void destroy_osctrl (OSCTRL *a); - static void flush_osctrl (OSCTRL *a); - static void setBuffers_osctrl (OSCTRL *a, float* in, float* out); - static void setSamplerate_osctrl (OSCTRL *a, int rate); - static void setSize_osctrl (OSCTRL *a, int size); - // TXA Properties - static void SetosctrlRun (TXA& txa, int run); + OSCTRL(const OSCTRL&) = delete; + OSCTRL& operator=(const OSCTRL& other) = delete; + ~OSCTRL() = default; + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); private: - static void calc_osctrl (OSCTRL *a); - static void decalc_osctrl (OSCTRL *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/slew.cpp b/wdsp/slew.cpp index 50651bfd1..b66a2d8e9 100644 --- a/wdsp/slew.cpp +++ b/wdsp/slew.cpp @@ -31,162 +31,141 @@ warren@wpratt.com namespace WDSP { -enum _USLEW +void USLEW::calc() { - BEGIN, - WAIT, - UP, - ON -}; - -void USLEW::calc_uslew (USLEW *a) -{ - int i; - float delta, theta; - a->runmode = 0; - a->state = BEGIN; - a->count = 0; - a->ndelup = (int)(a->tdelay * a->rate); - a->ntup = (int)(a->tupslew * a->rate); - a->cup = new float[a->ntup + 1]; // (float *) malloc0 ((a->ntup + 1) * sizeof (float)); - delta = PI / (float)a->ntup; + double delta; + double theta; + runmode = 0; + state = _USLEW::BEGIN; + count = 0; + ndelup = (int)(tdelay * rate); + ntup = (int)(tupslew * rate); + cup.resize(ntup + 1); + delta = PI / (float)ntup; theta = 0.0; - for (i = 0; i <= a->ntup; i++) + for (int i = 0; i <= ntup; i++) { - a->cup[i] = 0.5 * (1.0 - cos (theta)); + cup[i] = 0.5 * (1.0 - cos (theta)); theta += delta; } - *a->ch_upslew &= ~((long)1); // InterlockedBitTestAndReset (a->ch_upslew, 0); + *ch_upslew &= ~((long)1); } -void USLEW::decalc_uslew (USLEW *a) +USLEW::USLEW( + long *_ch_upslew, + int _size, + float* _in, + float* _out, + double _rate, + double _tdelay, + double _tupslew +) : + ch_upslew(_ch_upslew), + size(_size), + in(_in), + out(_out), + rate(_rate), + tdelay(_tdelay), + tupslew(_tupslew) { - delete[] (a->cup); + calc(); } -USLEW* USLEW::create_uslew ( - TXA *txa, - long *ch_upslew, - int size, - float* in, - float* out, - float rate, - float tdelay, - float tupslew -) +void USLEW::flush() { - USLEW *a = new USLEW; - a->txa = txa; - a->ch_upslew = ch_upslew; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->tdelay = tdelay; - a->tupslew = tupslew; - calc_uslew (a); - return a; + state = _USLEW::BEGIN; + runmode = 0; + *ch_upslew &= ~1L; } -void USLEW::destroy_uslew (USLEW *a) +void USLEW::execute (int check) { - decalc_uslew (a); - delete (a); -} + if (!runmode && check) + runmode = 1; -void USLEW::flush_uslew (USLEW *a) -{ - a->state = BEGIN; - a->runmode = 0; - *a->ch_upslew &= ~1L; //InterlockedBitTestAndReset (a->ch_upslew, 0); -} - -void USLEW::xuslew (USLEW *a) -{ - if (!a->runmode && a->txa->uslewCheck()) - a->runmode = 1; - - long upslew = *a->ch_upslew; - *a->ch_upslew = 1L; - if (a->runmode && upslew) //_InterlockedAnd (a->ch_upslew, 1)) + long upslew = *ch_upslew; + *ch_upslew = 1L; + if (runmode && upslew) //_InterlockedAnd (ch_upslew, 1)) { - int i; - float I, Q; - for (i = 0; i < a->size; i++) + double I; + double Q; + for (int i = 0; i < size; i++) { - I = a->in[2 * i + 0]; - Q = a->in[2 * i + 1]; - switch (a->state) + I = in[2 * i + 0]; + Q = in[2 * i + 1]; + switch (state) { - case BEGIN: - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; + case _USLEW::BEGIN: + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; if ((I != 0.0) || (Q != 0.0)) { - if (a->ndelup > 0) + if (ndelup > 0) { - a->state = WAIT; - a->count = a->ndelup; + state = _USLEW::WAIT; + count = ndelup; } - else if (a->ntup > 0) + else if (ntup > 0) { - a->state = UP; - a->count = a->ntup; + state = _USLEW::UP; + count = ntup; } else - a->state = ON; + state = _USLEW::ON; } break; - case WAIT: - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; - if (a->count-- == 0) + case _USLEW::WAIT: + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; + if (count-- == 0) { - if (a->ntup > 0) + if (ntup > 0) { - a->state = UP; - a->count = a->ntup; + state = _USLEW::UP; + count = ntup; } else - a->state = ON; + state = _USLEW::ON; } break; - case UP: - a->out[2 * i + 0] = I * a->cup[a->ntup - a->count]; - a->out[2 * i + 1] = Q * a->cup[a->ntup - a->count]; - if (a->count-- == 0) - a->state = ON; + case _USLEW::UP: + out[2 * i + 0] = (float) (I * cup[ntup - count]); + out[2 * i + 1] = (float) (Q * cup[ntup - count]); + if (count-- == 0) + state = _USLEW::ON; break; - case ON: - a->out[2 * i + 0] = I; - a->out[2 * i + 1] = Q; - *a->ch_upslew &= ~((long)1); // InterlockedBitTestAndReset (a->ch_upslew, 0); - a->runmode = 0; + case _USLEW::ON: + out[2 * i + 0] = (float) I; + out[2 * i + 1] = (float) Q; + *ch_upslew &= ~((long)1); + runmode = 0; + break; + default: break; } } } - else if (a->out != a->in) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (out != in) + std::copy( in, in + size * 2, out); } -void USLEW::setBuffers_uslew (USLEW *a, float* in, float* out) +void USLEW::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void USLEW::setSamplerate_uslew (USLEW *a, int rate) +void USLEW::setSamplerate(int _rate) { - decalc_uslew (a); - a->rate = rate; - calc_uslew (a); + decalc(); + rate = _rate; + calc(); } -void USLEW::setSize_uslew (USLEW *a, int size) +void USLEW::setSize(int _size) { - a->size = size; - flush_uslew (a); + size = _size; + flush(); } /******************************************************************************************************** @@ -195,13 +174,17 @@ void USLEW::setSize_uslew (USLEW *a, int size) * * ********************************************************************************************************/ -void USLEW::SetuSlewTime (TXA& txa, float time) +void USLEW::setuSlewTime(double _time) { // NOTE: 'time' is in seconds - USLEW *a = txa.uslew; - decalc_uslew (a); - a->tupslew = time; - calc_uslew (a); + decalc(); + tupslew = _time; + calc(); +} + +void USLEW::setRun(int run) +{ + runmode = run; } } // namespace WDSP diff --git a/wdsp/slew.hpp b/wdsp/slew.hpp index 3569701b1..61f71b856 100644 --- a/wdsp/slew.hpp +++ b/wdsp/slew.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_slew_h #define wdsp_slew_h +#include + #include "export.h" namespace WDSP { @@ -37,42 +39,53 @@ class TXA; class WDSP_API USLEW { public: - TXA *txa; + enum class _USLEW + { + BEGIN, + WAIT, + UP, + ON + }; + long *ch_upslew; int size; float* in; float* out; - float rate; - float tdelay; - float tupslew; + double rate; + double tdelay; + double tupslew; int runmode; - int state; + _USLEW state; int count; int ndelup; int ntup; - float* cup; + std::vector cup; - static USLEW* create_uslew ( - TXA *txa, + USLEW( long *ch_upslew, - int size, float* in, + int size, + float* in, float* out, - float rate, - float tdelay, - float tupslew + double rate, + double tdelay, + double tupslew ); - static void destroy_uslew (USLEW *a); - static void flush_uslew (USLEW *a); - static void xuslew (USLEW *a); - static void setBuffers_uslew (USLEW *a, float* in, float* out); - static void setSamplerate_uslew (USLEW *a, int rate); - static void setSize_uslew (USLEW *a, int size); + USLEW(const USLEW&) = delete; + USLEW& operator=(const USLEW& other) = delete; + ~USLEW() = default; + + void flush(); + void execute (int check); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); // TXA Properties - static void SetuSlewTime (TXA& txa, float time); + void setuSlewTime(double time); + void setRun(int run); private: - static void calc_uslew (USLEW *a); - static void decalc_uslew (USLEW *a); + void calc(); + void decalc(); }; } // namespace WDSP From 62f05b3706a5b604a9892fca2ef0f3c7518fa78a Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 8 Aug 2024 09:09:40 +0200 Subject: [PATCH 37/46] WDSP: impulse responses refactoring (1) --- wdsp/cfir.cpp | 15 ++++--- wdsp/cfir.hpp | 5 ++- wdsp/emphp.cpp | 42 +++++++++---------- wdsp/eq.cpp | 6 +-- wdsp/eqp.cpp | 109 ++++++++++++++++++++++++------------------------ wdsp/eqp.hpp | 12 +++++- wdsp/fcurve.cpp | 15 ++++--- wdsp/fcurve.hpp | 4 +- wdsp/fir.cpp | 10 ++--- wdsp/fir.hpp | 4 +- wdsp/fmd.cpp | 65 ++++++++++++++--------------- wdsp/fmsq.cpp | 14 +++---- wdsp/icfir.cpp | 15 ++++--- wdsp/icfir.hpp | 3 +- 14 files changed, 161 insertions(+), 158 deletions(-) diff --git a/wdsp/cfir.cpp b/wdsp/cfir.cpp index 456565df5..676d25b1b 100644 --- a/wdsp/cfir.cpp +++ b/wdsp/cfir.cpp @@ -34,11 +34,10 @@ namespace WDSP { void CFIR::calc() { - float* impulse; + std::vector impulse; scale = 1.0 / (float)(2 * size); - impulse = cfir_impulse (nc, DD, R, Pairs, runrate, cicrate, cutoff, xtype, xbw, 1, scale, wintype); - p = new FIRCORE(size, in, out, nc, mp, impulse); - delete[] impulse; + cfir_impulse (impulse, nc, DD, R, Pairs, runrate, cicrate, cutoff, xtype, xbw, 1, scale, wintype); + p = new FIRCORE(size, in, out, nc, mp, impulse.data()); } void CFIR::decalc() @@ -142,7 +141,8 @@ void CFIR::setOutRate(int rate) calc(); } -float* CFIR::cfir_impulse ( +void CFIR::cfir_impulse ( + std::vector& impulse, int _N, int _DD, int _R, @@ -175,7 +175,6 @@ float* CFIR::cfir_impulse ( double ri; double mag = 0; double fn; - float* impulse; std::vector A(_N); double ft = _cutoff / _cicrate; // normalized cutoff frequency int u_samps = (_N + 1) / 2; // number of unique samples, OK for odd or even N @@ -254,8 +253,8 @@ float* CFIR::cfir_impulse ( else for (i = u_samps, j = 1; i < _N; i++, j++) A[i] = A[u_samps - j]; - impulse = FIR::fir_fsamp (_N, A.data(), _rtype, 1.0, _wintype); - return impulse; + impulse.resize(2 * _N); + FIR::fir_fsamp (impulse, _N, A.data(), _rtype, 1.0, _wintype); } /******************************************************************************************************** diff --git a/wdsp/cfir.hpp b/wdsp/cfir.hpp index 155a24286..453f96250 100644 --- a/wdsp/cfir.hpp +++ b/wdsp/cfir.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_cfir_h #define wdsp_cfir_h +#include + #include "export.h" namespace WDSP { @@ -83,7 +85,8 @@ public: void setSamplerate(int rate); void setSize(int size); void setOutRate(int rate); - static float* cfir_impulse ( + static void cfir_impulse ( + std::vector& impulse, int N, int DD, int R, diff --git a/wdsp/emphp.cpp b/wdsp/emphp.cpp index 33c7c28d3..cb3084ef5 100644 --- a/wdsp/emphp.cpp +++ b/wdsp/emphp.cpp @@ -53,7 +53,6 @@ EMPHP::EMPHP( double _f_high ) { - float* impulse; run = _run; position = _position; size = _size; @@ -65,7 +64,9 @@ EMPHP::EMPHP( ctype = _ctype; f_low = _f_low; f_high = _f_high; - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc); + FCurve::fc_impulse ( + impulse, nc, f_low, f_high, @@ -76,8 +77,7 @@ EMPHP::EMPHP( 1.0 / (2.0 * size), 0, 0 ); - p = new FIRCORE(size, in, out, nc, mp, impulse); - delete[] (impulse); + p = new FIRCORE(size, in, out, nc, mp, impulse.data()); } EMPHP::~EMPHP() @@ -107,9 +107,10 @@ void EMPHP::setBuffers(float* _in, float* _out) void EMPHP::setSamplerate(int _rate) { - float* impulse; rate = _rate; - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc); + FCurve::fc_impulse ( + impulse, nc, f_low, f_high, @@ -120,16 +121,16 @@ void EMPHP::setSamplerate(int _rate) 1.0 / (2.0 * size), 0, 0 ); - p->setImpulse(impulse, 1); - delete[] (impulse); + p->setImpulse(impulse.data(), 1); } void EMPHP::setSize(int _size) { - float* impulse; size = _size; p->setSize(size); - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc); + FCurve::fc_impulse ( + impulse, nc, f_low, f_high, @@ -141,8 +142,7 @@ void EMPHP::setSize(int _size) 0, 0 ); - p->setImpulse(impulse, 1); - delete[] (impulse); + p->setImpulse(impulse.data(), 1); } /******************************************************************************************************** @@ -167,12 +167,12 @@ void EMPHP::setMP(int _mp) void EMPHP::setNC(int _nc) { - float* impulse; - if (nc != _nc) { nc = _nc; - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc); + FCurve::fc_impulse ( + impulse, nc, f_low, f_high, @@ -184,20 +184,19 @@ void EMPHP::setNC(int _nc) 0, 0 ); - p->setNc(nc, impulse); - delete[] (impulse); + p->setNc(nc, impulse.data()); } } void EMPHP::setFreqs(double low, double high) { - float* impulse; - if (f_low != low || f_high != high) { f_low = low; f_high = high; - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc); + FCurve::fc_impulse ( + impulse, nc, f_low, f_high, @@ -209,8 +208,7 @@ void EMPHP::setFreqs(double low, double high) 0, 0 ); - p->setImpulse(impulse, 1); - delete[] (impulse); + p->setImpulse(impulse.data(), 1); } } diff --git a/wdsp/eq.cpp b/wdsp/eq.cpp index 73ed34ee9..b2d61bc15 100644 --- a/wdsp/eq.cpp +++ b/wdsp/eq.cpp @@ -44,12 +44,12 @@ namespace WDSP { void EQ::eq_mults (std::vector& mults, int size, int nfreqs, float* F, float* G, float samplerate, float scale, int ctfmode, int wintype) { - float* impulse = EQP::eq_impulse (size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); - float* _mults = FIR::fftcv_mults(2 * size, impulse); + std::vector impulse; + EQP::eq_impulse (impulse, size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); + float* _mults = FIR::fftcv_mults(2 * size, impulse.data()); mults.resize(2 * size * 2); std::copy(_mults, _mults + 2*size*2, mults.begin()); delete[] _mults; - delete[] impulse; } void EQ::calc() diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index c211e5a2e..6237d707a 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -42,25 +42,34 @@ int EQP::fEQcompare (const void * a, const void * b) return 1; } -float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, double samplerate, double scale, int ctfmode, int wintype) +void EQP::eq_impulse ( + std::vector& impulse, + int N, + int _nfreqs, + const float* F, + const float* G, + double samplerate, + double scale, + int ctfmode, + int wintype +) { - std::vector fp(nfreqs + 2); - std::vector gp(nfreqs + 2); + std::vector fp(_nfreqs + 2); + std::vector gp(_nfreqs + 2); std::vector A(N / 2 + 1); - float* sary = new float[2 * nfreqs]; + float* sary = new float[2 * _nfreqs]; double gpreamp; double f; double frac; - float* impulse; int i; int j; int mid; fp[0] = 0.0; - fp[nfreqs + 1] = 1.0; + fp[_nfreqs + 1] = 1.0; gpreamp = G[0]; - for (i = 1; i <= nfreqs; i++) + for (i = 1; i <= _nfreqs; i++) { fp[i] = (float) (2.0 * F[i] / samplerate); @@ -73,22 +82,22 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl gp[i] = G[i]; } - for (i = 1, j = 0; i <= nfreqs; i++, j+=2) + for (i = 1, j = 0; i <= _nfreqs; i++, j+=2) { sary[j + 0] = fp[i]; sary[j + 1] = gp[i]; } - qsort (sary, nfreqs, 2 * sizeof (float), fEQcompare); + qsort (sary, _nfreqs, 2 * sizeof (float), fEQcompare); - for (i = 1, j = 0; i <= nfreqs; i++, j+=2) + for (i = 1, j = 0; i <= _nfreqs; i++, j+=2) { fp[i] = sary[j + 0]; gp[i] = sary[j + 1]; } gp[0] = gp[1]; - gp[nfreqs + 1] = gp[nfreqs]; + gp[_nfreqs + 1] = gp[_nfreqs]; mid = N / 2; j = 0; @@ -98,7 +107,7 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl { f = (double)i / (double)mid; - while ((f > fp[j + 1]) && (j < nfreqs)) + while ((f > fp[j + 1]) && (j < _nfreqs)) j++; frac = (f - fp[j]) / (fp[j + 1] - fp[j]); @@ -111,7 +120,7 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl { f = ((double)i + 0.5) / (double)mid; - while ((f > fp[j + 1]) && (j < nfreqs)) + while ((f > fp[j + 1]) && (j < _nfreqs)) j++; frac = (f - fp[j]) / (fp[j + 1] - fp[j]); @@ -132,7 +141,7 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl if (N & 1) { low = (int)(fp[1] * mid); - high = (int)(fp[nfreqs] * mid + 0.5); + high = (int)(fp[_nfreqs] * mid + 0.5); lowmag = A[low]; highmag = A[high]; flow4 = pow((double)low / (double)mid, 4.0); @@ -160,7 +169,7 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl else { low = (int)(fp[1] * mid - 0.5); - high = (int)(fp[nfreqs] * mid - 0.5); + high = (int)(fp[_nfreqs] * mid - 0.5); lowmag = A[low]; highmag = A[high]; flow4 = pow((double)low / (double)mid, 4.0); @@ -187,13 +196,14 @@ float* EQP::eq_impulse (int N, int nfreqs, const float* F, const float* G, doubl } } + impulse.resize(2 * N); + if (N & 1) - impulse = FIR::fir_fsamp_odd(N, A.data(), 1, 1.0, wintype); + FIR::fir_fsamp_odd(impulse, N, A.data(), 1, 1.0, wintype); else - impulse = FIR::fir_fsamp(N, A.data(), 1, 1.0, wintype); + FIR::fir_fsamp(impulse, N, A.data(), 1, 1.0, wintype); delete[] sary; - return impulse; } /******************************************************************************************************** @@ -218,7 +228,7 @@ EQP::EQP( ) { // NOTE: 'nc' must be >= 'size' - float* impulse; + std::vector impulse; run = _run; size = _size; nc = _nc; @@ -233,9 +243,8 @@ EQP::EQP( ctfmode = _ctfmode; wintype = _wintype; samplerate = (double) _samplerate; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore = new FIRCORE(size, in, out, nc, mp, impulse); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); } EQP::~EQP() @@ -265,21 +274,19 @@ void EQP::setBuffers(float* _in, float* _out) void EQP::setSamplerate(int rate) { - float* impulse; + std::vector impulse; samplerate = rate; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } void EQP::setSize(int _size) { - float* impulse; + std::vector impulse; size = _size; fircore->setSize(size); - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } /******************************************************************************************************** @@ -295,14 +302,13 @@ void EQP::setRun(int _run) void EQP::setNC(int _nc) { - float* impulse; + std::vector impulse; if (nc != _nc) { nc = _nc; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setNc(nc, impulse); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setNc(nc, impulse.data()); } } @@ -317,38 +323,35 @@ void EQP::setMP(int _mp) void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) { - float* impulse; + std::vector impulse; nfreqs = _nfreqs; F.resize(nfreqs + 1); G.resize(nfreqs + 1); std::copy(_F, _F + (_nfreqs + 1), F.begin()); std::copy(_G, _G + (_nfreqs + 1), G.begin()); - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } void EQP::setCtfmode(int _mode) { - float* impulse; + std::vector impulse; ctfmode = _mode; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } void EQP::setWintype(int _wintype) { - float* impulse; + std::vector impulse; wintype = _wintype; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } void EQP::setGrphEQ(const int *rxeq) { // three band equalizer (legacy compatibility) - float* impulse; + std::vector impulse; nfreqs = 4; F.resize(nfreqs + 1); G.resize(nfreqs + 1); @@ -362,14 +365,13 @@ void EQP::setGrphEQ(const int *rxeq) G[3] = (float)rxeq[2]; G[4] = (float)rxeq[3]; ctfmode = 0; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } void EQP::setGrphEQ10(const int *rxeq) { // ten band equalizer (legacy compatibility) - float* impulse; + std::vector impulse; nfreqs = 10; F.resize(nfreqs + 1); G.resize(nfreqs + 1); @@ -386,9 +388,8 @@ void EQP::setGrphEQ10(const int *rxeq) for (int i = 0; i <= nfreqs; i++) G[i] = (float)rxeq[i]; ctfmode = 0; - impulse = eq_impulse (nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse, 1); - delete[] impulse; + eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); + fircore->setImpulse(impulse.data(), 1); } } // namespace WDSP diff --git a/wdsp/eqp.hpp b/wdsp/eqp.hpp index df4167355..fce8193bc 100644 --- a/wdsp/eqp.hpp +++ b/wdsp/eqp.hpp @@ -92,7 +92,17 @@ public: void setGrphEQ(const int *rxeq); void setGrphEQ10(const int *rxeq); - static float* eq_impulse (int N, int nfreqs, const float* F, const float* G, double samplerate, double scale, int ctfmode, int wintype); + static void eq_impulse ( + std::vector& impulse, + int N, + int nfreqs, + const float* F, + const float* G, + double samplerate, + double scale, + int ctfmode, + int wintype + ); private: static int fEQcompare (const void * a, const void * b); diff --git a/wdsp/fcurve.cpp b/wdsp/fcurve.cpp index a6124ec2c..e0e9781c7 100644 --- a/wdsp/fcurve.cpp +++ b/wdsp/fcurve.cpp @@ -31,12 +31,11 @@ warren@wpratt.com namespace WDSP { -float* FCurve::fc_impulse (int nc, float f0, float f1, float g0, float, int curve, float samplerate, float scale, int ctfmode, int wintype) +void FCurve::fc_impulse (std::vector& impulse, int nc, float f0, float f1, float g0, float, int curve, float samplerate, float scale, int ctfmode, int wintype) { float* A = new float[nc / 2 + 1]; // (float *) malloc0 ((nc / 2 + 1) * sizeof (float)); int i; float fn, f; - float* impulse; int mid = nc / 2; float g0_lin = pow(10.0, g0 / 20.0); if (nc & 1) @@ -140,21 +139,21 @@ float* FCurve::fc_impulse (int nc, float f0, float f1, float g0, float, int curv } } } + if (nc & 1) - impulse = FIR::fir_fsamp_odd(nc, A, 1, 1.0, wintype); + FIR::fir_fsamp_odd(impulse, nc, A, 1, 1.0, wintype); else - impulse = FIR::fir_fsamp(nc, A, 1, 1.0, wintype); + FIR::fir_fsamp(impulse, nc, A, 1, 1.0, wintype); // print_impulse ("emph.txt", size + 1, impulse, 1, 0); delete[] (A); - return impulse; } // generate mask for Overlap-Save Filter float* FCurve::fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype) { - float* impulse = fc_impulse (size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype); - float* mults = FIR::fftcv_mults(2 * size, impulse); - delete[] (impulse); + std::vector impulse(2 * (size + 1)); + fc_impulse (impulse, size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype); + float* mults = FIR::fftcv_mults(2 * size, impulse.data()); return mults; } diff --git a/wdsp/fcurve.hpp b/wdsp/fcurve.hpp index d11d1ac68..4b137408a 100644 --- a/wdsp/fcurve.hpp +++ b/wdsp/fcurve.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_fcurve_h #define wdsp_fcurve_h +#include + #include "export.h" namespace WDSP { @@ -35,7 +37,7 @@ namespace WDSP { class WDSP_API FCurve { public: - static float* fc_impulse (int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); + static void fc_impulse (std::vector& impulse, int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); static float* fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); }; diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index 9fd5075a5..b2da83530 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -95,17 +95,16 @@ float* FIR::get_fsamp_window(int N, int wintype) return window; } -float* FIR::fir_fsamp_odd (int N, const float* A, int rtype, double scale, int wintype) +void FIR::fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype) { int mid = (N - 1) / 2; double mag; double phs; std::vector fcoef(N * 2); - auto *c_impulse = new float[N * 2]; fftwf_plan ptmp = fftwf_plan_dft_1d( N, (fftwf_complex *)fcoef.data(), - (fftwf_complex *)c_impulse, + (fftwf_complex *)c_impulse.data(), FFTW_BACKWARD, FFTW_PATIENT ); @@ -142,13 +141,11 @@ float* FIR::fir_fsamp_odd (int N, const float* A, int rtype, double scale, int w break; } delete[] window; - return c_impulse; } -float* FIR::fir_fsamp (int N, const float* A, int rtype, double scale, int wintype) +void FIR::fir_fsamp (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype) { double sum; - auto c_impulse = new float[N * 2]; if (N & 1) { @@ -202,7 +199,6 @@ float* FIR::fir_fsamp (int N, const float* A, int rtype, double scale, int winty break; } delete[] window; - return c_impulse; } float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale) diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index e852d0eb5..a57d19630 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -37,8 +37,8 @@ class WDSP_API FIR { public: static float* fftcv_mults (int NM, float* c_impulse); - static float* fir_fsamp_odd (int N, const float* A, int rtype, double scale, int wintype); - static float* fir_fsamp (int N, const float* A, int rtype, double scale, int wintype); + static void fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); + static void fir_fsamp (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); static void mp_imp (int N, std::vector& fir, std::vector& mpfir, int pfactor, int polarity); diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 14eafe841..8315497fe 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -143,11 +143,12 @@ FMD::FMD( lim_gain(0.0001), // 2.5 lim_pre_gain(0.01) // 0.4 { - float* impulse; calc(); // de-emphasis filter audio.resize(size * 2); - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc_de); + FCurve::fc_impulse ( + impulse, nc_de, (float) f_low, (float) f_high, @@ -158,12 +159,10 @@ FMD::FMD( 0, 0 ); - pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse); - delete[] impulse; + pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); // audio filter - impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulse); - delete[] impulse; + float *impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb); } FMD::~FMD() @@ -248,12 +247,13 @@ void FMD::setBuffers(float* _in, float* _out) void FMD::setSamplerate(int _rate) { - float* impulse; decalc(); rate = _rate; calc(); // de-emphasis filter - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc_de); + FCurve::fc_impulse ( + impulse, nc_de, (float) f_low, (float) f_high, @@ -265,25 +265,25 @@ void FMD::setSamplerate(int _rate) 0, 0 ); - pde->setImpulse(impulse, 1); - delete[] impulse; + pde->setImpulse(impulse.data(), 1); // audio filter - impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulse, 1); - delete[] impulse; + float* impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud->setImpulse(impulseb, 1); + delete[] impulseb; plim->setSamplerate((int) rate); } void FMD::setSize(int _size) { - float* impulse; decalc(); size = _size; calc(); audio.resize(size * 2); // de-emphasis filter delete (pde); - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc_de); + FCurve::fc_impulse ( + impulse, nc_de, (float) f_low, (float) f_high, @@ -295,13 +295,12 @@ void FMD::setSize(int _size) 0, 0 ); - pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse); - delete[] impulse; + pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); // audio filter delete (paud); - impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulse); - delete[] impulse; + float* impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb); + delete[] impulseb; plim->setSize(size); } @@ -331,12 +330,12 @@ void FMD::setCTCSSRun(int _run) void FMD::setNCde(int nc) { - float* impulse; - if (nc_de != nc) { nc_de = nc; - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc_de); + FCurve::fc_impulse ( + impulse, nc_de, (float) f_low, (float) f_high, @@ -348,8 +347,7 @@ void FMD::setNCde(int nc) 0, 0 ); - pde->setNc(nc_de, impulse); - delete[] impulse; + pde->setNc(nc_de, impulse.data()); } } @@ -405,14 +403,14 @@ void FMD::setLimGain(double gaindB) void FMD::setAFFilter(double low, double high) { - float* impulse; - if (f_low != low || f_high != high) { f_low = low; f_high = high; // de-emphasis filter - impulse = FCurve::fc_impulse ( + std::vector impulse(2 * nc_de); + FCurve::fc_impulse ( + impulse, nc_de, (float) f_low, (float) f_high, @@ -424,12 +422,11 @@ void FMD::setAFFilter(double low, double high) 0, 0 ); - pde->setImpulse(impulse, 1); - delete[] impulse; + pde->setImpulse(impulse.data(), 1); // audio filter - impulse = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulse, 1); - delete[] impulse; + float* impulseb = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud->setImpulse(impulseb, 1); + delete[] impulseb; } } diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 5f83a4675..859800aff 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -36,7 +36,7 @@ void FMSQ::calc() { double delta; double theta; - float* impulse; + std::vector impulse; int i; // noise filter noise.resize(2 * size * 2); @@ -48,9 +48,8 @@ void FMSQ::calc() G[1] = 0.0; G[2] = 3.0; G[3] = (float) (+20.0 * log10(20000.0 / *pllpole)); - impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - p = new FIRCORE(size, trigger, noise.data(), nc, mp, impulse); - delete[] impulse; + EQP::eq_impulse (impulse, nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); + p = new FIRCORE(size, trigger, noise.data(), nc, mp, impulse.data()); // noise averaging avm = exp(-1.0 / (rate * avtau)); onem_avm = 1.0 - avm; @@ -286,14 +285,13 @@ void FMSQ::setThreshold(double threshold) void FMSQ::setNC(int _nc) { - float* impulse; + std::vector impulse; if (nc != _nc) { nc = _nc; - impulse = EQP::eq_impulse (nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - p->setNc(nc, impulse); - delete[] impulse; + EQP::eq_impulse (impulse, nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); + p->setNc(nc, impulse.data()); } } diff --git a/wdsp/icfir.cpp b/wdsp/icfir.cpp index f22043a77..e03551417 100644 --- a/wdsp/icfir.cpp +++ b/wdsp/icfir.cpp @@ -34,11 +34,10 @@ namespace WDSP { void ICFIR::calc_icfir (ICFIR *a) { - float* impulse; + std::vector impulse; a->scale = 1.0f / (float)(2 * a->size); - impulse = icfir_impulse (a->nc, a->DD, a->R, a->Pairs, (float) a->runrate, (float) a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse); - delete[] (impulse); + icfir_impulse (impulse, a->nc, a->DD, a->R, a->Pairs, (float) a->runrate, (float) a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); + a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse.data()); } void ICFIR::decalc_icfir (ICFIR *a) @@ -145,7 +144,8 @@ void ICFIR::setOutRate_icfir (ICFIR *a, int rate) calc_icfir (a); } -float* ICFIR::icfir_impulse ( +void ICFIR::icfir_impulse ( + std::vector& impulse, int N, int DD, int R, @@ -178,7 +178,6 @@ float* ICFIR::icfir_impulse ( float ri; float mag; float fn; - float* impulse; auto* A = new float[N]; float ft = cutoff / cicrate; // normalized cutoff frequency int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N @@ -239,10 +238,10 @@ float* ICFIR::icfir_impulse ( else for (i = u_samps, j = 1; i < N; i++, j++) A[i] = A[u_samps - j]; - impulse = FIR::fir_fsamp (N, A, rtype, 1.0, wintype); + impulse.resize(2 * N); + FIR::fir_fsamp (impulse, N, A, rtype, 1.0, wintype); delete[] (A); delete[] xistion; - return impulse; } diff --git a/wdsp/icfir.hpp b/wdsp/icfir.hpp index 775e7cb5c..1dd4f28c9 100644 --- a/wdsp/icfir.hpp +++ b/wdsp/icfir.hpp @@ -79,7 +79,8 @@ public: static void setSamplerate_icfir (ICFIR *a, int rate); static void setSize_icfir (ICFIR *a, int size); static void setOutRate_icfir (ICFIR *a, int rate); - static float* icfir_impulse ( + static void icfir_impulse ( + std::vector& impulse, int N, int DD, int R, From 7319e4cb88d6060da3785070c58a74a7bf17e7ae Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 9 Aug 2024 06:33:00 +0200 Subject: [PATCH 38/46] WDSP: impulse responses refactoring (2) --- wdsp/TXA.cpp | 18 ++++++------------ wdsp/bps.cpp | 3 +-- wdsp/bps.hpp | 2 +- wdsp/emph.cpp | 4 ++-- wdsp/emph.hpp | 4 +++- wdsp/eq.cpp | 6 +++--- wdsp/fcurve.cpp | 5 ++--- wdsp/fcurve.hpp | 2 +- wdsp/fir.cpp | 7 +++---- wdsp/fir.hpp | 2 +- 10 files changed, 23 insertions(+), 30 deletions(-) diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index c1f160ef7..8897f043c 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -1040,9 +1040,8 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] (impulse); } @@ -1052,9 +1051,8 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] (impulse); } @@ -1064,9 +1062,8 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] (impulse); } } @@ -1080,9 +1077,8 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) if (a->wintype != _wintype) { a->wintype = _wintype; - delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] (impulse); } @@ -1091,9 +1087,8 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) if (a->wintype != _wintype) { a->wintype = _wintype; - delete[] (a->mults); impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] impulse; } @@ -1102,9 +1097,8 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) if (a->wintype != _wintype) { a->wintype = _wintype; - delete[] (a->mults); impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - a->mults = FIR::fftcv_mults (2 * a->size, impulse); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse); delete[] impulse; } } diff --git a/wdsp/bps.cpp b/wdsp/bps.cpp index af99c0e19..03d9ee0f7 100644 --- a/wdsp/bps.cpp +++ b/wdsp/bps.cpp @@ -46,7 +46,7 @@ void BPS::calc() infilt.resize(2 * size * 2); product.resize(2 * size * 2); impulse = FIR::fir_bandpass(size + 1, f_low, f_high, samplerate, wintype, 1, 1.0 / (float)(2 * size)); - mults = FIR::fftcv_mults(2 * size, impulse); + FIR::fftcv_mults(mults, 2 * size, impulse); CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) infilt.data(), (fftwf_complex *) product.data(), FFTW_FORWARD, FFTW_PATIENT); CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) product.data(), (fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT); delete[]impulse; @@ -56,7 +56,6 @@ void BPS::decalc() { fftwf_destroy_plan(CRev); fftwf_destroy_plan(CFor); - delete[] mults; } BPS::BPS( diff --git a/wdsp/bps.hpp b/wdsp/bps.hpp index 8133bef58..8a46e5fb3 100644 --- a/wdsp/bps.hpp +++ b/wdsp/bps.hpp @@ -56,7 +56,7 @@ public: double f_high; std::vector infilt; std::vector product; - float* mults; + std::vector mults; double samplerate; int wintype; double gain; diff --git a/wdsp/emph.cpp b/wdsp/emph.cpp index 552ac1539..e8f99db14 100644 --- a/wdsp/emph.cpp +++ b/wdsp/emph.cpp @@ -43,7 +43,8 @@ void EMPH::calc() { infilt = new float[2 * size * 2]; product = new float[2 * size * 2]; - mults = FCurve::fc_mults( + FCurve::fc_mults( + mults, size, f_low, f_high, @@ -63,7 +64,6 @@ void EMPH::decalc() { fftwf_destroy_plan(CRev); fftwf_destroy_plan(CFor); - delete[] mults; delete[] product; delete[] infilt; } diff --git a/wdsp/emph.hpp b/wdsp/emph.hpp index 21b9f8fc1..8aa535ca6 100644 --- a/wdsp/emph.hpp +++ b/wdsp/emph.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef _emph_h #define _emph_h +#include + #include "fftw3.h" #include "export.h" @@ -52,7 +54,7 @@ public: double f_high; float* infilt; float* product; - float* mults; + std::vector mults; double rate; fftwf_plan CFor; fftwf_plan CRev; diff --git a/wdsp/eq.cpp b/wdsp/eq.cpp index b2d61bc15..7d7132128 100644 --- a/wdsp/eq.cpp +++ b/wdsp/eq.cpp @@ -46,10 +46,10 @@ void EQ::eq_mults (std::vector& mults, int size, int nfreqs, float* F, fl { std::vector impulse; EQP::eq_impulse (impulse, size + 1, nfreqs, F, G, samplerate, scale, ctfmode, wintype); - float* _mults = FIR::fftcv_mults(2 * size, impulse.data()); + std::vector _mults; + FIR::fftcv_mults(_mults, 2 * size, impulse.data()); mults.resize(2 * size * 2); - std::copy(_mults, _mults + 2*size*2, mults.begin()); - delete[] _mults; + std::copy(_mults.begin(), _mults.end(), mults.begin()); } void EQ::calc() diff --git a/wdsp/fcurve.cpp b/wdsp/fcurve.cpp index e0e9781c7..9e0c74bb6 100644 --- a/wdsp/fcurve.cpp +++ b/wdsp/fcurve.cpp @@ -149,12 +149,11 @@ void FCurve::fc_impulse (std::vector& impulse, int nc, float f0, float f1 } // generate mask for Overlap-Save Filter -float* FCurve::fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype) +float* FCurve::fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype) { std::vector impulse(2 * (size + 1)); fc_impulse (impulse, size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype); - float* mults = FIR::fftcv_mults(2 * size, impulse.data()); - return mults; + FIR::fftcv_mults(mults, 2 * size, impulse.data()); } } // namespace WDSP diff --git a/wdsp/fcurve.hpp b/wdsp/fcurve.hpp index 4b137408a..4b33b1eb9 100644 --- a/wdsp/fcurve.hpp +++ b/wdsp/fcurve.hpp @@ -38,7 +38,7 @@ class WDSP_API FCurve { public: static void fc_impulse (std::vector& impulse, int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); - static float* fc_mults (int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); + static float* fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); }; } // namespace WDSP diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index b2da83530..aba6ddaae 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -35,14 +35,14 @@ warren@pratt.one namespace WDSP { -float* FIR::fftcv_mults (int NM, float* c_impulse) +void FIR::fftcv_mults (std::vector& mults, int NM, float* c_impulse) { - auto mults = new float[NM * 2]; + mults.resize(NM * 2); std::vector cfft_impulse(NM * 2); fftwf_plan ptmp = fftwf_plan_dft_1d( NM, (fftwf_complex *) cfft_impulse.data(), - (fftwf_complex *) mults, + (fftwf_complex *) mults.data(), FFTW_FORWARD, FFTW_PATIENT ); @@ -51,7 +51,6 @@ float* FIR::fftcv_mults (int NM, float* c_impulse) std::copy(c_impulse, c_impulse + (NM / 2 + 1) * 2, &(cfft_impulse[NM - 2])); fftwf_execute (ptmp); fftwf_destroy_plan (ptmp); - return mults; } float* FIR::get_fsamp_window(int N, int wintype) diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index a57d19630..2cf4a5f4f 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -36,7 +36,7 @@ namespace WDSP { class WDSP_API FIR { public: - static float* fftcv_mults (int NM, float* c_impulse); + static void fftcv_mults (std::vector& mults, int NM, float* c_impulse); static void fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); static void fir_fsamp (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); From 28cfad98ff48bb5cc7a09f3821f5853f07b6312f Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 02:06:19 +0200 Subject: [PATCH 39/46] WDSP: impulse responses refactoring (3) --- wdsp/fcurve.cpp | 2 +- wdsp/fcurve.hpp | 2 +- wdsp/fir.cpp | 28 ++++++++++++---------------- wdsp/fir.hpp | 6 +++--- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/wdsp/fcurve.cpp b/wdsp/fcurve.cpp index 9e0c74bb6..d70773caf 100644 --- a/wdsp/fcurve.cpp +++ b/wdsp/fcurve.cpp @@ -149,7 +149,7 @@ void FCurve::fc_impulse (std::vector& impulse, int nc, float f0, float f1 } // generate mask for Overlap-Save Filter -float* FCurve::fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype) +void FCurve::fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype) { std::vector impulse(2 * (size + 1)); fc_impulse (impulse, size + 1, f0, f1, g0, g1, curve, samplerate, scale, ctfmode, wintype); diff --git a/wdsp/fcurve.hpp b/wdsp/fcurve.hpp index 4b33b1eb9..1d05bb339 100644 --- a/wdsp/fcurve.hpp +++ b/wdsp/fcurve.hpp @@ -38,7 +38,7 @@ class WDSP_API FCurve { public: static void fc_impulse (std::vector& impulse, int nc, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); - static float* fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); + static void fc_mults (std::vector& mults, int size, float f0, float f1, float g0, float g1, int curve, float samplerate, float scale, int ctfmode, int wintype); }; } // namespace WDSP diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index aba6ddaae..956aac8ba 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -53,11 +53,11 @@ void FIR::fftcv_mults (std::vector& mults, int NM, float* c_impulse) fftwf_destroy_plan (ptmp); } -float* FIR::get_fsamp_window(int N, int wintype) +void FIR::get_fsamp_window(std::vector& window, int N, int wintype) { double arg0; double arg1; - auto window = new float[N]; + window.resize(N); switch (wintype) { case 0: @@ -91,7 +91,6 @@ float* FIR::get_fsamp_window(int N, int wintype) for (int i = 0; i < N; i++) window[i] = 1.0; } - return window; } void FIR::fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype) @@ -122,7 +121,8 @@ void FIR::fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, i } fftwf_execute (ptmp); fftwf_destroy_plan (ptmp); - float* window = get_fsamp_window(N, wintype); + std::vector window; + get_fsamp_window(window, N, wintype); switch (rtype) { case 0: @@ -139,7 +139,6 @@ void FIR::fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, i default: break; } - delete[] window; } void FIR::fir_fsamp (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype) @@ -180,7 +179,8 @@ void FIR::fir_fsamp (std::vector& c_impulse, int N, const float* A, int r c_impulse[2 * n + 1] = 0.0; } } - float* window = get_fsamp_window (N, wintype); + std::vector window; + get_fsamp_window (window, N, wintype); switch (rtype) { case 0: @@ -197,7 +197,6 @@ void FIR::fir_fsamp (std::vector& c_impulse, int N, const float* A, int r default: break; } - delete[] window; } float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale) @@ -277,7 +276,7 @@ float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, return c_impulse; } -float *FIR::fir_read (int N, const char *filename, int rtype, float scale) +void FIR::fir_read (std::vector& c_impulse, int N, const char *filename, int rtype, float scale) // N = number of real or complex coefficients (see rtype) // *filename = filename // rtype = 0: real coefficients @@ -289,12 +288,12 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale) FILE *file; float I; float Q; - auto c_impulse = new float[N * 2]; - std::fill(c_impulse, c_impulse + N*2, 0); + c_impulse.resize(N * 2); + std::fill(c_impulse.begin(), c_impulse.end(), 0); file = fopen (filename, "r"); if (!file) { - return c_impulse; + return; } for (int i = 0; i < N; i++) @@ -325,7 +324,6 @@ float *FIR::fir_read (int N, const char *filename, int rtype, float scale) } } fclose (file); - return c_impulse; } void FIR::analytic (int N, float* in, float* out) @@ -430,7 +428,7 @@ void FIR::mp_imp (int N, std::vector& fir, std::vector& mpfir, int // impulse response of a zero frequency filter comprising a cascade of two resonators, // each followed by a detrending filter -float* FIR::zff_impulse(int nc, float scale) +void FIR::zff_impulse(std::vector& c_dresdet, int nc, float scale) { // nc = number of coefficients (power of two) int n_resdet = nc / 2 - 1; // size of single zero-frequency resonator with detrender @@ -444,7 +442,7 @@ float* FIR::zff_impulse(int nc, float scale) // allocate the float and complex versions and make the values std::vector dresdet(n_dresdet); auto div = (float) ((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor - auto c_dresdet = new float[nc * 2]; + c_dresdet.resize(nc * 2); for (int n = 0; n < n_dresdet; n++) // convolve to make the cascade { for (int k = 0; k < n_resdet; k++) @@ -454,8 +452,6 @@ float* FIR::zff_impulse(int nc, float scale) c_dresdet[2 * n + 0] = dresdet[n] * scale; c_dresdet[2 * n + 1] = 0.0; } - - return c_dresdet; } } // namespace WDSP diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index 2cf4a5f4f..39e3b63e6 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -44,9 +44,9 @@ public: private: static void analytic (int N, float* in, float* out); - static float* get_fsamp_window(int N, int wintype); - static float *fir_read (int N, const char *filename, int rtype, float scale); - static float* zff_impulse(int nc, float scale); + static void get_fsamp_window(std::vector& window, int N, int wintype); + static void fir_read (std::vector& impulse, int N, const char *filename, int rtype, float scale); + static void zff_impulse(std::vector& impulse, int nc, float scale); }; #endif From ad3e7dfe194d19666c96a86981d7c7e4fc216b5a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 02:09:07 +0200 Subject: [PATCH 40/46] WDSP: fixed missing decalc in USLEW --- wdsp/slew.cpp | 2 -- wdsp/slew.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/wdsp/slew.cpp b/wdsp/slew.cpp index b66a2d8e9..95ad7fe8e 100644 --- a/wdsp/slew.cpp +++ b/wdsp/slew.cpp @@ -157,7 +157,6 @@ void USLEW::setBuffers(float* _in, float* _out) void USLEW::setSamplerate(int _rate) { - decalc(); rate = _rate; calc(); } @@ -177,7 +176,6 @@ void USLEW::setSize(int _size) void USLEW::setuSlewTime(double _time) { // NOTE: 'time' is in seconds - decalc(); tupslew = _time; calc(); } diff --git a/wdsp/slew.hpp b/wdsp/slew.hpp index 61f71b856..52dcc4bd6 100644 --- a/wdsp/slew.hpp +++ b/wdsp/slew.hpp @@ -85,7 +85,6 @@ public: private: void calc(); - void decalc(); }; } // namespace WDSP From e48dc227932d7b1c7b51d558359334e68aa74e39 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 06:40:35 +0200 Subject: [PATCH 41/46] WDSP: impulse responses refactoring (4) --- plugins/channelrx/wdsprx/wdsprxsettings.cpp | 13 +- wdsp/RXA.cpp | 12 +- wdsp/TXA.cpp | 55 ++-- wdsp/bandpass.cpp | 49 ++-- wdsp/bps.cpp | 16 +- wdsp/bpsnba.cpp | 3 +- wdsp/delay.cpp | 139 +++++----- wdsp/delay.hpp | 31 ++- wdsp/fir.cpp | 7 +- wdsp/fir.hpp | 4 +- wdsp/fircore.cpp | 6 +- wdsp/fircore.hpp | 7 +- wdsp/firmin.cpp | 122 ++++----- wdsp/firmin.hpp | 40 ++- wdsp/firopt.cpp | 38 ++- wdsp/firopt.hpp | 2 + wdsp/fmd.cpp | 29 +- wdsp/fmmod.cpp | 43 ++- wdsp/nbp.cpp | 41 ++- wdsp/nbp.hpp | 4 +- wdsp/resample.cpp | 6 +- wdsp/resamplef.cpp | 45 ++-- wdsp/rmatch.cpp | 175 ++++++------ wdsp/varsamp.cpp | 280 +++++++++----------- wdsp/varsamp.hpp | 36 +-- 25 files changed, 613 insertions(+), 590 deletions(-) diff --git a/plugins/channelrx/wdsprx/wdsprxsettings.cpp b/plugins/channelrx/wdsprx/wdsprxsettings.cpp index 335415faf..d36387d9b 100644 --- a/plugins/channelrx/wdsprx/wdsprxsettings.cpp +++ b/plugins/channelrx/wdsprx/wdsprxsettings.cpp @@ -279,6 +279,9 @@ QByteArray WDSPRxSettings::serialize() const s.writeDouble(163 + 100*i, m_profiles[i].m_ssqlTauMute); s.writeDouble(164 + 100*i, m_profiles[i].m_ssqlTauUnmute); s.writeDouble(165 + 100*i, m_profiles[i].m_amsqMaxTail); + // RIT + s.writeBool( 183 + 100*i, m_profiles[i].m_rit); + s.writeDouble(184 + 100*i, m_profiles[i].m_ritFrequency); // Equalizer s.writeBool( 190 + 100*i, m_profiles[i].m_equalizer); s.writeFloat(4100 + 100*i, m_profiles[i].m_eqF[0]); @@ -403,15 +406,15 @@ bool WDSPRxSettings::deserialize(const QByteArray& data) d.readU32( 74, &utmp, 0); if ((utmp > 1023) && (utmp < 65535)) { - m_reverseAPIPort = utmp; + m_reverseAPIPort = (uint16_t) utmp; } else { m_reverseAPIPort = 8888; } d.readU32( 75, &utmp, 0); - m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : (uint16_t) utmp; d.readU32( 76, &utmp, 0); - m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + m_reverseAPIChannelIndex = utmp > 99 ? 99 : (uint16_t) utmp; d.readS32( 77, &m_streamIndex, 0); if (m_rollupState) @@ -464,9 +467,9 @@ bool WDSPRxSettings::deserialize(const QByteArray& data) // Filter d.readS32 (100 + 100*i, &m_profiles[i].m_spanLog2, 3); d.readS32 (101 + 100*i, &tmp, 30); - m_profiles[i].m_highCutoff = tmp * 100.0; + m_profiles[i].m_highCutoff = (float) tmp * 100.0f; d.readS32 (102 + 100*i, &tmp, 3); - m_profiles[i].m_lowCutoff = tmp * 100.0; + m_profiles[i].m_lowCutoff = (float) tmp * 100.0f; d.readS32 (103 + 100*i, &m_profiles[i].m_fftWindow, 0); // AGC d.readBool( 110 + 100*i, &m_profiles[i].m_agc, true); diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index c030899db..740653271 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -1005,8 +1005,7 @@ void RXA::updateNBPFilters() if (a->fnfrun) { a->calc_impulse(); - a->fircore->setImpulse(a->impulse, 1); - delete[] (a->impulse); + a->fircore->setImpulse(a->impulse.data(), 1); } if (b->bpsnba->fnfrun) { @@ -1097,8 +1096,7 @@ void RXA::nbpSetNotchesRun(int _run) b->fnfrun = a->master_run; bpsnbaCheck(mode, _run); b->calc_impulse(); // recalc nbp impulse response - b->fircore->setImpulse(b->impulse, 0); // calculate new filter masks - delete[] (b->impulse); + b->fircore->setImpulse(b->impulse.data(), 0); // calculate new filter masks bpsnbaSet(); b->fircore->setUpdate(); // apply new filter masks } @@ -1115,8 +1113,7 @@ void RXA::nbpSetWindow(int _wintype) { a->wintype = _wintype; a->calc_impulse(); - a->fircore->setImpulse(a->impulse, 1); - delete[] (a->impulse); + a->fircore->setImpulse(a->impulse.data(), 1); } if (b->wintype != _wintype) @@ -1137,8 +1134,7 @@ void RXA::nbpSetAutoIncrease(int _autoincr) { a->autoincr = _autoincr; a->calc_impulse(); - a->fircore->setImpulse(a->impulse, 1); - delete[] (a->impulse); + a->fircore->setImpulse(a->impulse.data(), 1); } if (b->autoincr != _autoincr) diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index 8897f043c..fedb213d1 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -915,7 +915,9 @@ void TXA::setBandpassNC(int _nc) if (a->nc != _nc) { a->nc = _nc; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, a->nc, a->f_low, a->f_high, @@ -924,8 +926,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse); - delete[] impulse; + a->fircore->setNc(a->nc, impulse.data()); } a = bp1; @@ -933,7 +934,9 @@ void TXA::setBandpassNC(int _nc) if (a->nc != _nc) { a->nc = _nc; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, a->nc, a->f_low, a->f_high, @@ -942,8 +945,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse); - delete[] impulse; + a->fircore->setNc(a->nc, impulse.data()); } a = bp2; @@ -951,7 +953,9 @@ void TXA::setBandpassNC(int _nc) if (a->nc != _nc) { a->nc = _nc; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, a->nc, a->f_low, a->f_high, @@ -960,8 +964,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse); - delete[] impulse; + a->fircore->setNc(a->nc, impulse.data()); } } @@ -1032,7 +1035,7 @@ void TXA::SetBPSRun (TXA& txa, int _run) void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { - float* impulse; + std::vector impulse; BPS *a; a = txa.bps0; @@ -1040,9 +1043,8 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] (impulse); + FIR::fir_bandpass(impulse, a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } a = txa.bps1; @@ -1051,9 +1053,8 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] (impulse); + FIR::fir_bandpass(impulse, a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } a = txa.bps2; @@ -1062,24 +1063,22 @@ void TXA::SetBPSFreqs (TXA& txa, double _f_low, double _f_high) { a->f_low = _f_low; a->f_high = _f_high; - impulse = FIR::fir_bandpass(a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] (impulse); + FIR::fir_bandpass(impulse, a->size + 1, _f_low, _f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } } void TXA::SetBPSWindow (TXA& txa, int _wintype) { - float* impulse; + std::vector impulse; BPS *a; a = txa.bps0; if (a->wintype != _wintype) { a->wintype = _wintype; - impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] (impulse); + FIR::fir_bandpass(impulse, a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } a = txa.bps1; @@ -1087,9 +1086,8 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) if (a->wintype != _wintype) { a->wintype = _wintype; - impulse = FIR::fir_bandpass(a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] impulse; + FIR::fir_bandpass(impulse, a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } a = txa.bps2; @@ -1097,9 +1095,8 @@ void TXA::SetBPSWindow (TXA& txa, int _wintype) if (a->wintype != _wintype) { a->wintype = _wintype; - impulse = FIR::fir_bandpass (a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); - FIR::fftcv_mults (a->mults, 2 * a->size, impulse); - delete[] impulse; + FIR::fir_bandpass (impulse, a->size + 1, a->f_low, a->f_high, a->samplerate, a->wintype, 1, 1.0 / (float)(2 * a->size)); + FIR::fftcv_mults (a->mults, 2 * a->size, impulse.data()); } } diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index 3bdc00ece..b9989cedc 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -66,7 +66,9 @@ BANDPASS::BANDPASS( wintype(_wintype), gain(_gain) { - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -75,8 +77,7 @@ BANDPASS::BANDPASS( 1, gain / (double)(2 * size) ); - fircore = new FIRCORE(size, in, out, nc, mp, impulse); - delete[] impulse; + fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); } BANDPASS::~BANDPASS() @@ -107,7 +108,9 @@ void BANDPASS::setBuffers(float* _in, float* _out) void BANDPASS::setSamplerate(int _rate) { samplerate = _rate; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -116,8 +119,7 @@ void BANDPASS::setSamplerate(int _rate) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } void BANDPASS::setSize(int _size) @@ -126,7 +128,9 @@ void BANDPASS::setSize(int _size) size = _size; fircore->setSize(size); // recalc impulse because scale factor is a function of size - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -135,14 +139,15 @@ void BANDPASS::setSize(int _size) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } void BANDPASS::setGain(double _gain, int _update) { gain = _gain; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -151,8 +156,7 @@ void BANDPASS::setGain(double _gain, int _update) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse, _update); - delete[] impulse; + fircore->setImpulse(impulse.data(), _update); } void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) @@ -162,7 +166,9 @@ void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) f_low = _f_low; f_high = _f_high; gain = _gain; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -171,8 +177,7 @@ void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) 1, gain / (double)(2 * size) ); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } } @@ -186,7 +191,9 @@ void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) { if ((_f_low != f_low) || (_f_high != f_high)) { - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, _f_low, _f_high, @@ -196,8 +203,7 @@ void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) gain / (double)(2 * size) ); - fircore->setImpulse(impulse, 0); - delete[] impulse; + fircore->setImpulse(impulse.data(), 0); f_low = _f_low; f_high = _f_high; fircore->setUpdate(); @@ -210,7 +216,9 @@ void BANDPASS::SetBandpassNC(int _nc) if (_nc != nc) { nc = _nc; - float* impulse = FIR::fir_bandpass ( + std::vector impulse; + FIR::fir_bandpass ( + impulse, nc, f_low, f_high, @@ -219,8 +227,7 @@ void BANDPASS::SetBandpassNC(int _nc) 1, gain / (double)( 2 * size) ); - fircore->setNc(nc, impulse); - delete[] impulse; + fircore->setNc(nc, impulse.data()); } } diff --git a/wdsp/bps.cpp b/wdsp/bps.cpp index 03d9ee0f7..94703af48 100644 --- a/wdsp/bps.cpp +++ b/wdsp/bps.cpp @@ -42,14 +42,22 @@ namespace WDSP { void BPS::calc() { - float* impulse; infilt.resize(2 * size * 2); product.resize(2 * size * 2); - impulse = FIR::fir_bandpass(size + 1, f_low, f_high, samplerate, wintype, 1, 1.0 / (float)(2 * size)); - FIR::fftcv_mults(mults, 2 * size, impulse); + std::vector impulse; + FIR::fir_bandpass( + impulse, + size + 1, + f_low, + f_high, + samplerate, + wintype, + 1, + 1.0 / (float)(2 * size) + ); + FIR::fftcv_mults(mults, 2 * size, impulse.data()); CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) infilt.data(), (fftwf_complex *) product.data(), FFTW_FORWARD, FFTW_PATIENT); CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *) product.data(), (fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT); - delete[]impulse; } void BPS::decalc() diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index 6720871ed..5c28307e6 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -176,8 +176,7 @@ void BPSNBA::recalc_bpsnba_filter(int update) b->gain = gain; b->autoincr = autoincr; b->calc_impulse(); - b->fircore->setImpulse(b->impulse, update); - delete[] (b->impulse); + b->fircore->setImpulse(b->impulse.data(), update); } /******************************************************************************************************** diff --git a/wdsp/delay.cpp b/wdsp/delay.cpp index 03b68f20d..4e7b4ca33 100644 --- a/wdsp/delay.cpp +++ b/wdsp/delay.cpp @@ -31,81 +31,84 @@ warren@wpratt.com namespace WDSP { -DELAY* create_delay (int run, int size, float* in, float* out, int rate, float tdelta, float tdelay) +DELAY::DELAY( + int _run, + int _size, + float* _in, + float* _out, + int _rate, + float _tdelta, + float _tdelay +) { - DELAY *a = new DELAY; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->rate = rate; - a->tdelta = tdelta; - a->tdelay = tdelay; - a->L = (int)(0.5 + 1.0 / (a->tdelta * (float)a->rate)); - a->adelta = 1.0 / (a->rate * a->L); - a->ft = 0.45 / (float)a->L; - a->ncoef = (int)(60.0 / a->ft); - a->ncoef = (a->ncoef / a->L + 1) * a->L; - a->cpp = a->ncoef / a->L; - a->phnum = (int)(0.5 + a->tdelay / a->adelta); - a->snum = a->phnum / a->L; - a->phnum %= a->L; - a->idx_in = 0; - a->adelay = a->adelta * (a->snum * a->L + a->phnum); - a->h = FIR::fir_bandpass (a->ncoef,-a->ft, +a->ft, 1.0, 1, 0, (float)a->L); - a->rsize = a->cpp + (WSDEL - 1); - a->ring = new float[a->rsize * 2]; // (float *) malloc0 (a->rsize * sizeof (complex)); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + rate = _rate; + tdelta = _tdelta; + tdelay = _tdelay; + L = (int)(0.5 + 1.0 / (tdelta * (float)rate)); + adelta = 1.0f / (float) (rate * L); + ft = 0.45f / (float)L; + ncoef = (int)(60.0 / ft); + ncoef = (ncoef / L + 1) * L; + cpp = ncoef / L; + phnum = (int)(0.5 + tdelay / adelta); + snum = phnum / L; + phnum %= L; + idx_in = 0; + adelay = adelta * (float) (snum * L + phnum); + FIR::fir_bandpass (h, ncoef,-ft, +ft, 1.0, 1, 0, (float)L); + rsize = cpp + (WSDEL - 1); + ring.resize(rsize * 2); } -void DELAY::destroy_delay (DELAY *a) +void DELAY::flush() { - delete[] (a->ring); - delete[] (a->h); - delete (a); + std::fill(ring.begin(), ring.end(), 0); + idx_in = 0; } -void DELAY::flush_delay (DELAY *a) +void DELAY::execute() { - std::fill(a->ring, a->ring + a->cpp * 2, 0); - a->idx_in = 0; -} - -void DELAY::xdelay (DELAY *a) -{ - if (a->run) + if (run) { - int i, j, k, idx, n; - float Itmp, Qtmp; + int j; + int k; + int idx; + int n; + float Itmp; + float Qtmp; - for (i = 0; i < a->size; i++) + for (int i = 0; i < size; i++) { - a->ring[2 * a->idx_in + 0] = a->in[2 * i + 0]; - a->ring[2 * a->idx_in + 1] = a->in[2 * i + 1]; + ring[2 * idx_in + 0] = in[2 * i + 0]; + ring[2 * idx_in + 1] = in[2 * i + 1]; Itmp = 0.0; Qtmp = 0.0; - if ((n = a->idx_in + a->snum) >= a->rsize) - n -= a->rsize; + if ((n = idx_in + snum) >= rsize) + n -= rsize; - for (j = 0, k = a->L - 1 - a->phnum; j < a->cpp; j++, k+= a->L) + for (j = 0, k = L - 1 - phnum; j < cpp; j++, k+= L) { - if ((idx = n + j) >= a->rsize) - idx -= a->rsize; + if ((idx = n + j) >= rsize) + idx -= rsize; - Itmp += a->ring[2 * idx + 0] * a->h[k]; - Qtmp += a->ring[2 * idx + 1] * a->h[k]; + Itmp += ring[2 * idx + 0] * h[k]; + Qtmp += ring[2 * idx + 1] * h[k]; } - a->out[2 * i + 0] = Itmp; - a->out[2 * i + 1] = Qtmp; + out[2 * i + 0] = Itmp; + out[2 * i + 1] = Qtmp; - if (--a->idx_in < 0) - a->idx_in = a->rsize - 1; + if (--idx_in < 0) + idx_in = rsize - 1; } } - else if (a->out != a->in) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (out != in) + std::copy( in, in + size * 2, out); } /******************************************************************************************************** @@ -114,28 +117,28 @@ void DELAY::xdelay (DELAY *a) * * ********************************************************************************************************/ -void DELAY::SetDelayRun (DELAY *a, int run) +void DELAY::setRun(int _run) { - a->run = run; + run = _run; } -float DELAY::SetDelayValue (DELAY *a, float tdelay) +float DELAY::setValue(float _tdelay) { - float adelay; - a->tdelay = tdelay; - a->phnum = (int)(0.5 + a->tdelay / a->adelta); - a->snum = a->phnum / a->L; - a->phnum %= a->L; - a->adelay = a->adelta * (a->snum * a->L + a->phnum); - adelay = a->adelay; + float _adelay; + tdelay = _tdelay; + phnum = (int)(0.5 + tdelay / adelta); + snum = phnum / L; + phnum %= L; + _adelay = adelta * (float) (snum * L + phnum); + adelay = _adelay; return adelay; } -void DELAY::SetDelayBuffs (DELAY *a, int size, float* in, float* out) +void DELAY::setBuffs(int _size, float* _in, float* _out) { - a->size = size; - a->in = in; - a->out = out; + size = _size; + in = _in; + out = _out; } } // namespace WDSP diff --git a/wdsp/delay.hpp b/wdsp/delay.hpp index 01706c541..824035f22 100644 --- a/wdsp/delay.hpp +++ b/wdsp/delay.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_delay_h #define wdsp_delay_h +#include + #include "export.h" #define WSDEL 1025 // number of supported whole sample delays @@ -49,25 +51,36 @@ public: int ncoef; // number of coefficients int cpp; // coefficients per phase float ft; // normalized cutoff frequency - float* h; // coefficients + std::vector h; // coefficients int snum; // starting sample number (0 for sub-sample delay) int phnum; // phase number int idx_in; // index for input into ring int rsize; // ring size in complex samples - float* ring; // ring buffer + std::vector ring; // ring buffer float adelta; // actual delay increment float adelay; // actual delay - static DELAY* create_delay (int run, int size, float* in, float* out, int rate, float tdelta, float tdelay); - static void destroy_delay (DELAY *a); - static void flush_delay (DELAY *a); - static void xdelay (DELAY *a); + DELAY( + int run, + int size, + float* in, + float* out, + int rate, + float tdelta, + float tdelay + ); + DELAY(const DELAY&) = delete; + DELAY& operator=(DELAY& other) = delete; + ~DELAY() = default; + + void flush(); + void execute(); // Properties - static void SetDelayRun (DELAY *a, int run); - static float SetDelayValue (DELAY *a, float delay); // returns actual delay in seconds - static void SetDelayBuffs (DELAY *a, int size, float* in, float* out); + void setRun(int run); + float setValue(float delay); // returns actual delay in seconds + void setBuffs(int size, float* in, float* out); }; } // namespace WDSP diff --git a/wdsp/fir.cpp b/wdsp/fir.cpp index 956aac8ba..dd5fe3ce0 100644 --- a/wdsp/fir.cpp +++ b/wdsp/fir.cpp @@ -35,7 +35,7 @@ warren@pratt.one namespace WDSP { -void FIR::fftcv_mults (std::vector& mults, int NM, float* c_impulse) +void FIR::fftcv_mults (std::vector& mults, int NM, const float* c_impulse) { mults.resize(NM * 2); std::vector cfft_impulse(NM * 2); @@ -199,9 +199,9 @@ void FIR::fir_fsamp (std::vector& c_impulse, int N, const float* A, int r } } -float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale) +void FIR::fir_bandpass (std::vector& c_impulse, int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale) { - auto *c_impulse = new float[N * 2]; + c_impulse.resize(N * 2); double ft = (f_high - f_low) / (2.0 * samplerate); double ft_rad = TWOPI * ft; double w_osc = PI * (f_high + f_low) / samplerate; @@ -273,7 +273,6 @@ float* FIR::fir_bandpass (int N, double f_low, double f_high, double samplerate, break; } } - return c_impulse; } void FIR::fir_read (std::vector& c_impulse, int N, const char *filename, int rtype, float scale) diff --git a/wdsp/fir.hpp b/wdsp/fir.hpp index 39e3b63e6..99a073f71 100644 --- a/wdsp/fir.hpp +++ b/wdsp/fir.hpp @@ -36,10 +36,10 @@ namespace WDSP { class WDSP_API FIR { public: - static void fftcv_mults (std::vector& mults, int NM, float* c_impulse); + static void fftcv_mults (std::vector& mults, int NM, const float* impulse); static void fir_fsamp_odd (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); static void fir_fsamp (std::vector& c_impulse, int N, const float* A, int rtype, double scale, int wintype); - static float* fir_bandpass (int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); + static void fir_bandpass (std::vector& impulse, int N, double f_low, double f_high, double samplerate, int wintype, int rtype, double scale); static void mp_imp (int N, std::vector& fir, std::vector& mpfir, int pfactor, int polarity); private: diff --git a/wdsp/fircore.cpp b/wdsp/fircore.cpp index 5bcf996f5..6c4be6a6e 100644 --- a/wdsp/fircore.cpp +++ b/wdsp/fircore.cpp @@ -124,7 +124,7 @@ FIRCORE::FIRCORE( float* _out, int _nc, int _mp, - float* _impulse + const float* _impulse ) { size = _size; @@ -204,13 +204,13 @@ void FIRCORE::setSize(int _size) calc(1); } -void FIRCORE::setImpulse(float* _impulse, int _update) +void FIRCORE::setImpulse(const float* _impulse, int _update) { std::copy(_impulse, _impulse + nc * 2, impulse.begin()); calc(_update); } -void FIRCORE::setNc(int _nc, float* _impulse) +void FIRCORE::setNc(int _nc, const float* _impulse) { // because of FFT planning, this will probably cause a glitch in audio if done during dataflow deplan(); diff --git a/wdsp/fircore.hpp b/wdsp/fircore.hpp index 66e2d4832..3fd309a4c 100644 --- a/wdsp/fircore.hpp +++ b/wdsp/fircore.hpp @@ -72,8 +72,7 @@ public: float* out, int nc, int mp, - float* - impulse + const float* impulse ); FIRCORE(const FIRCORE&) = delete; FIRCORE& operator=(const FIRCORE& other) = delete; @@ -83,8 +82,8 @@ public: void execute(); void setBuffers(float* in, float* out); void setSize(int size); - void setImpulse(float* impulse, int update); - void setNc(int nc, float* impulse); + void setImpulse(const float* impulse, int update); + void setNc(int nc, const float* impulse); void setMp(int mp); void setUpdate(); diff --git a/wdsp/firmin.cpp b/wdsp/firmin.cpp index d3e289bc4..ced48f683 100644 --- a/wdsp/firmin.cpp +++ b/wdsp/firmin.cpp @@ -37,94 +37,96 @@ namespace WDSP { * * ********************************************************************************************************/ -void FIRMIN::calc_firmin (FIRMIN *a) +void FIRMIN::calc() { - a->h = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain); - a->rsize = a->nc; - a->mask = a->rsize - 1; - a->ring = new float[a->rsize * 2]; // (float *) malloc0 (a->rsize * sizeof (complex)); - a->idx = 0; + FIR::fir_bandpass (h, nc, f_low, f_high, samplerate, wintype, 1, gain); + rsize = nc; + mask = rsize - 1; + ring.resize(rsize * 2); + idx = 0; } -FIRMIN* FIRMIN::create_firmin (int run, int position, int size, float* in, float* out, - int nc, float f_low, float f_high, int samplerate, int wintype, float gain) +FIRMIN::FIRMIN( + int _run, + int _position, + int _size, + float* _in, + float* _out, + int _nc, + float _f_low, + float _f_high, + int _samplerate, + int _wintype, + float _gain +) { - FIRMIN *a = new FIRMIN; - a->run = run; - a->position = position; - a->size = size; - a->in = in; - a->out = out; - a->nc = nc; - a->f_low = f_low; - a->f_high = f_high; - a->samplerate = samplerate; - a->wintype = wintype; - a->gain = gain; - calc_firmin (a); - return a; + run = _run; + position = _position; + size = _size; + in = _in; + out = _out; + nc = _nc; + f_low = _f_low; + f_high = _f_high; + samplerate = (float) _samplerate; + wintype = _wintype; + gain = _gain; + calc(); } -void FIRMIN::destroy_firmin (FIRMIN *a) +void FIRMIN::flush() { - delete[] (a->ring); - delete[] (a->h); - delete (a); + std::fill(ring.begin(), ring.end(), 0); + idx = 0; } -void FIRMIN::flush_firmin (FIRMIN *a) +void FIRMIN::execute(int _pos) { - std::fill(a->ring, a->ring + a->rsize * 2, 0); - a->idx = 0; -} - -void FIRMIN::xfirmin (FIRMIN *a, int pos) -{ - if (a->run && a->position == pos) + if (run && position == _pos) { - int i, j, k; - for (i = 0; i < a->size; i++) + int k; + for (int i = 0; i < size; i++) { - a->ring[2 * a->idx + 0] = a->in[2 * i + 0]; - a->ring[2 * a->idx + 1] = a->in[2 * i + 1]; - a->out[2 * i + 0] = 0.0; - a->out[2 * i + 1] = 0.0; - k = a->idx; - for (j = 0; j < a->nc; j++) + ring[2 * idx + 0] = in[2 * i + 0]; + ring[2 * idx + 1] = in[2 * i + 1]; + out[2 * i + 0] = 0.0; + out[2 * i + 1] = 0.0; + k = idx; + for (int j = 0; j < nc; j++) { - a->out[2 * i + 0] += a->h[2 * j + 0] * a->ring[2 * k + 0] - a->h[2 * j + 1] * a->ring[2 * k + 1]; - a->out[2 * i + 1] += a->h[2 * j + 0] * a->ring[2 * k + 1] + a->h[2 * j + 1] * a->ring[2 * k + 0]; - k = (k + a->mask) & a->mask; + out[2 * i + 0] += h[2 * j + 0] * ring[2 * k + 0] - h[2 * j + 1] * ring[2 * k + 1]; + out[2 * i + 1] += h[2 * j + 0] * ring[2 * k + 1] + h[2 * j + 1] * ring[2 * k + 0]; + k = (k + mask) & mask; } - a->idx = (a->idx + 1) & a->mask; + idx = (idx + 1) & mask; } } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void FIRMIN::setBuffers_firmin (FIRMIN *a, float* in, float* out) +void FIRMIN::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void FIRMIN::setSamplerate_firmin (FIRMIN *a, int rate) +void FIRMIN::setSamplerate(int _rate) { - a->samplerate = (float)rate; - calc_firmin (a); + samplerate = (float) _rate; + calc(); } -void FIRMIN::setSize_firmin (FIRMIN *a, int size) +void FIRMIN::setSize(int _size) { - a->size = size; + size = _size; } -void FIRMIN::setFreqs_firmin (FIRMIN *a, float f_low, float f_high) +void FIRMIN::setFreqs(float _f_low, float _f_high) { - a->f_low = f_low; - a->f_high = f_high; - calc_firmin (a); + f_low = _f_low; + f_high = _f_high; + calc(); } } // namespace WDSP diff --git a/wdsp/firmin.hpp b/wdsp/firmin.hpp index 0e0e1b21b..1ef55153f 100644 --- a/wdsp/firmin.hpp +++ b/wdsp/firmin.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_firmin_h #define wdsp_firmin_h +#include + #include "fftw3.h" #include "export.h" @@ -50,8 +52,8 @@ public: int nc; // number of filter coefficients, power of two float f_low; // low cutoff frequency float f_high; // high cutoff frequency - float* ring; // internal complex ring buffer - float* h; // complex filter coefficients + std::vector ring; // internal complex ring buffer + std::vector h; // complex filter coefficients int rsize; // ring size, number of complex samples, power of two int mask; // mask to update indexes int idx; // ring input/output index @@ -59,18 +61,32 @@ public: int wintype; // filter window type float gain; // filter gain - static FIRMIN* create_firmin (int run, int position, int size, float* in, float* out, - int nc, float f_low, float f_high, int samplerate, int wintype, float gain); - static void destroy_firmin (FIRMIN *a); - static void flush_firmin (FIRMIN *a); - static void xfirmin (FIRMIN *a, int pos); - static void setBuffers_firmin (FIRMIN *a, float* in, float* out); - static void setSamplerate_firmin (FIRMIN *a, int rate); - static void setSize_firmin (FIRMIN *a, int size); - static void setFreqs_firmin (FIRMIN *a, float f_low, float f_high); + FIRMIN( + int run, + int position, + int size, + float* in, + float* out, + int nc, + float f_low, + float f_high, + int samplerate, + int wintype, + float gain + ); + FIRMIN(const FIRMIN&) = delete; + FIRMIN& operator=(const FIRMIN& other) = delete; + ~FIRMIN() = default; + + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setFreqs(float f_low, float f_high); private: - static void calc_firmin (FIRMIN *a); + void calc(); }; } // namespace WDSP diff --git a/wdsp/firopt.cpp b/wdsp/firopt.cpp index e466bf109..3d43d0037 100644 --- a/wdsp/firopt.cpp +++ b/wdsp/firopt.cpp @@ -44,16 +44,16 @@ void FIROPT::plan_firopt (FIROPT *a) a->nfor = a->nc / a->size; a->buffidx = 0; a->idxmask = a->nfor - 1; - a->fftin = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->fftout = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *)); - a->fmask = new float*[a->nfor]; // (float **) malloc0 (a->nfor * sizeof (float *)); - a->maskgen = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->pcfor = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan)); - a->maskplan = new fftwf_plan[a->nfor]; // (fftwf_plan *) malloc0 (a->nfor * sizeof (fftwf_plan)); + a->fftin = new float[2 * a->size * 2]; + a->fftout = new float*[a->nfor]; + a->fmask = new float*[a->nfor]; + a->maskgen = new float[2 * a->size * 2]; + a->pcfor = new fftwf_plan[a->nfor]; + a->maskplan = new fftwf_plan[a->nfor]; for (i = 0; i < a->nfor; i++) { - a->fftout[i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); - a->fmask[i] = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); + a->fftout[i] = new float[2 * a->size * 2]; + a->fmask[i] = new float[2 * a->size * 2]; a->pcfor[i] = fftwf_plan_dft_1d( 2 * a->size, (fftwf_complex *)a->fftin, @@ -69,7 +69,7 @@ void FIROPT::plan_firopt (FIROPT *a) FFTW_PATIENT ); } - a->accum = new float[2 * a->size * 2]; // (float *) malloc0 (2 * a->size * sizeof (complex)); + a->accum = new float[2 * a->size * 2]; a->crev = fftwf_plan_dft_1d( 2 * a->size, (fftwf_complex *)a->accum, @@ -83,17 +83,16 @@ void FIROPT::calc_firopt (FIROPT *a) { // call for change in frequency, rate, wintype, gain // must also call after a call to plan_firopt() - int i; - float* impulse = FIR::fir_bandpass (a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain); + std::vector impulse; + FIR::fir_bandpass (impulse, a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain); a->buffidx = 0; - for (i = 0; i < a->nfor; i++) + for (int i = 0; i < a->nfor; i++) { // I right-justified the impulse response => take output from left side of output buff, discard right side // Be careful about flipping an asymmetrical impulse response. std::copy(&(impulse[2 * a->size * i]), &(impulse[2 * a->size * i]) + a->size * 2, &(a->maskgen[2 * a->size])); fftwf_execute (a->maskplan[i]); } - delete[] (impulse); } FIROPT* FIROPT::create_firopt (int run, int position, int size, float* in, float* out, @@ -108,7 +107,7 @@ FIROPT* FIROPT::create_firopt (int run, int position, int size, float* in, float a->nc = nc; a->f_low = f_low; a->f_high = f_high; - a->samplerate = samplerate; + a->samplerate = (float) samplerate; a->wintype = wintype; a->gain = gain; plan_firopt (a); @@ -144,9 +143,8 @@ void FIROPT::destroy_firopt (FIROPT *a) void FIROPT::flush_firopt (FIROPT *a) { - int i; std::fill(a->fftin, a->fftin + 2 * a->size * 2, 0); - for (i = 0; i < a->nfor; i++) + for (int i = 0; i < a->nfor; i++) std::fill(a->fftout[i], a->fftout[i] + 2 * a->size * 2, 0); a->buffidx = 0; } @@ -155,14 +153,14 @@ void FIROPT::xfiropt (FIROPT *a, int pos) { if (a->run && (a->position == pos)) { - int i, j, k; + int k; std::copy(a->in, a->in + a->size * 2, &(a->fftin[2 * a->size])); fftwf_execute (a->pcfor[a->buffidx]); k = a->buffidx; std::fill(a->accum, a->accum + 2 * a->size * 2, 0); - for (j = 0; j < a->nfor; j++) + for (int j = 0; j < a->nfor; j++) { - for (i = 0; i < 2 * a->size; i++) + for (int i = 0; i < 2 * a->size; i++) { a->accum[2 * i + 0] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 0] - a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 1]; a->accum[2 * i + 1] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 1] + a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 0]; @@ -188,7 +186,7 @@ void FIROPT::setBuffers_firopt (FIROPT *a, float* in, float* out) void FIROPT::setSamplerate_firopt (FIROPT *a, int rate) { - a->samplerate = rate; + a->samplerate = (float) rate; calc_firopt (a); } diff --git a/wdsp/firopt.hpp b/wdsp/firopt.hpp index 05ea82cfe..343de76c9 100644 --- a/wdsp/firopt.hpp +++ b/wdsp/firopt.hpp @@ -34,6 +34,8 @@ warren@wpratt.com #ifndef wdsp_firopt_h #define wdsp_firopt_h +#include + #include "fftw3.h" #include "export.h" diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 8315497fe..144bbb8bc 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -161,8 +161,9 @@ FMD::FMD( ); pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); // audio filter - float *impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb); + std::vector impulseb; + FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb.data()); } FMD::~FMD() @@ -267,9 +268,9 @@ void FMD::setSamplerate(int _rate) ); pde->setImpulse(impulse.data(), 1); // audio filter - float* impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulseb, 1); - delete[] impulseb; + std::vector impulseb; + FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud->setImpulse(impulseb.data(), 1); plim->setSamplerate((int) rate); } @@ -298,9 +299,9 @@ void FMD::setSize(int _size) pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); // audio filter delete (paud); - float* impulseb = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb); - delete[] impulseb; + std::vector impulseb; + FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb.data()); plim->setSize(size); } @@ -367,9 +368,9 @@ void FMD::setNCaud(int nc) if (nc_aud != nc) { nc_aud = nc; - impulse = FIR::fir_bandpass(nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setNc(nc_aud, impulse); - delete[] impulse; + std::vector impulse; + FIR::fir_bandpass(impulse, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud->setNc(nc_aud, impulse.data()); } } @@ -424,9 +425,9 @@ void FMD::setAFFilter(double low, double high) ); pde->setImpulse(impulse.data(), 1); // audio filter - float* impulseb = FIR::fir_bandpass (nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulseb, 1); - delete[] impulseb; + std::vector impulseb; + FIR::fir_bandpass (impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); + paud->setImpulse(impulseb.data(), 1); } } diff --git a/wdsp/fmmod.cpp b/wdsp/fmmod.cpp index ee000b4db..fdbc206c1 100644 --- a/wdsp/fmmod.cpp +++ b/wdsp/fmmod.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "fircore.hpp" #include "fir.hpp" @@ -63,7 +65,7 @@ FMMOD::FMMOD( int _mp ) { - float* impulse; + std::vector impulse; run = _run; size = _size; in = _in; @@ -79,9 +81,8 @@ FMMOD::FMMOD( nc = _nc; mp = _mp; calc(); - impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p = new FIRCORE(size, out, out, nc, mp, impulse); - delete[] impulse; + FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p = new FIRCORE(size, out, out, nc, mp, impulse.data()); } FMMOD::~FMMOD() @@ -138,23 +139,21 @@ void FMMOD::setBuffers(float* _in, float* _out) void FMMOD::setSamplerate(int _rate) { - float* impulse; + std::vector impulse; samplerate = _rate; calc(); - impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse, 1); - delete[] impulse; + FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse.data(), 1); } void FMMOD::setSize(int _size) { - float* impulse; + std::vector impulse; size = _size; calc(); p->setSize(size); - impulse = FIR::fir_bandpass(nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse, 1); - delete[] impulse; + FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse.data(), 1); } /******************************************************************************************************** @@ -166,9 +165,9 @@ void FMMOD::setSize(int _size) void FMMOD::setDeviation(float _deviation) { double _bp_fc = f_high + _deviation; - float* impulse = FIR::fir_bandpass (nc, -_bp_fc, +_bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse, 0); - delete[] impulse; + std::vector impulse; + FIR::fir_bandpass (impulse, nc, -_bp_fc, +_bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse.data(), 0); deviation = _deviation; // mod sphase = 0.0; @@ -192,14 +191,13 @@ void FMMOD::setCTCSSRun (int _run) void FMMOD::setNC(int _nc) { - float* impulse; + std::vector impulse; if (nc != _nc) { nc = _nc; - impulse = FIR::fir_bandpass (nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setNc(nc, impulse); - delete[] impulse; + FIR::fir_bandpass (impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setNc(nc, impulse.data()); } } @@ -214,16 +212,15 @@ void FMMOD::setMP(int _mp) void FMMOD::setAFFreqs(float _low, float _high) { - float* impulse; + std::vector impulse; if (f_low != _low || f_high != _high) { f_low = _low; f_high = _high; bp_fc = deviation + f_high; - impulse = FIR::fir_bandpass (nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse, 1); - delete[] impulse; + FIR::fir_bandpass (impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); + p->setImpulse(impulse.data(), 1); } } diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index 29c2befc7..b3c27e405 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -158,21 +158,20 @@ void NOTCHDB::getNumNotches(int* _nnotches) const * * ********************************************************************************************************/ -float* NBP::fir_mbandpass (int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype) +void NBP::fir_mbandpass (std::vector& impulse, int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype) { - auto* impulse = new float[N * 2]; - std::fill(impulse, impulse + N*2, 0); + impulse.resize(N * 2); + std::fill(impulse.begin(), impulse.end(), 0); for (int k = 0; k < nbp; k++) { - float* imp = FIR::fir_bandpass (N, flow[k], fhigh[k], rate, wintype, 1, scale); + std::vector imp; + FIR::fir_bandpass (imp, N, flow[k], fhigh[k], rate, wintype, 1, scale); for (int i = 0; i < N; i++) { impulse[2 * i + 0] += imp[2 * i + 0]; impulse[2 * i + 1] += imp[2 * i + 1]; } - delete[] imp; } - return impulse; } double NBP::min_notch_width() const @@ -324,7 +323,8 @@ void NBP::calc_lightweight() bplow[i] -= offset; bphigh[i] -= offset; } - impulse = fir_mbandpass ( + fir_mbandpass ( + impulse, nc, numpb, bplow.data(), @@ -333,9 +333,8 @@ void NBP::calc_lightweight() gain / (float)(2 * size), wintype ); - fircore->setImpulse(impulse, 1); + fircore->setImpulse(impulse.data(), 1); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); - delete[] impulse; } hadnotch = havnotch; } @@ -375,7 +374,8 @@ void NBP::calc_impulse () bplow[i] -= offset; bphigh[i] -= offset; } - impulse = fir_mbandpass ( + fir_mbandpass ( + impulse, nc, numpb, bplow.data(), @@ -387,7 +387,8 @@ void NBP::calc_impulse () } else { - impulse = FIR::fir_bandpass( + FIR::fir_bandpass( + impulse, nc, flow, fhigh, @@ -437,14 +438,12 @@ NBP::NBP( bplow.resize(maxpb); bphigh.resize(maxpb); calc_impulse (); - fircore = new FIRCORE(size, in, out, nc, mp, impulse); - // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); - delete[]impulse; + fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); } NBP::~NBP() { - delete (fircore); + delete fircore; } void NBP::flush() @@ -471,8 +470,7 @@ void NBP::setSamplerate(int _rate) { rate = _rate; calc_impulse (); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } void NBP::setSize(int _size) @@ -481,15 +479,13 @@ void NBP::setSize(int _size) size = _size; fircore->setSize(size); calc_impulse (); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } void NBP::setNc() { calc_impulse(); - fircore->setNc(nc, impulse); - delete[] impulse; + fircore->setNc(nc, impulse.data()); } void NBP::setMp() @@ -517,8 +513,7 @@ void NBP::SetFreqs(double _flow, double _fhigh) flow = _flow; fhigh = _fhigh; calc_impulse(); - fircore->setImpulse(impulse, 1); - delete[] impulse; + fircore->setImpulse(impulse.data(), 1); } } diff --git a/wdsp/nbp.hpp b/wdsp/nbp.hpp index c56f71225..1b081e5b5 100644 --- a/wdsp/nbp.hpp +++ b/wdsp/nbp.hpp @@ -80,7 +80,7 @@ public: int autoincr; // auto-increment notch width double flow; // low bandpass cutoff freq double fhigh; // high bandpass cutoff freq - float* impulse; // filter impulse response + std::vector impulse; // filter impulse response int maxpb; // maximum number of passbands NOTCHDB* notchdb; // ptr to addr of notch-database data structure std::vector bplow; // array of passband lows @@ -129,7 +129,7 @@ public: void calc_lightweight(); private: - static float* fir_mbandpass (int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype); + static void fir_mbandpass (std::vector& impulse, int N, int nbp, const double* flow, const double* fhigh, double rate, double scale, int wintype); double min_notch_width () const; static int make_nbp ( int nn, diff --git a/wdsp/resample.cpp b/wdsp/resample.cpp index 19d16e3b3..826fe45a9 100644 --- a/wdsp/resample.cpp +++ b/wdsp/resample.cpp @@ -47,7 +47,7 @@ void RESAMPLE::calc() double full_rate; double fc_norm_high; double fc_norm_low; - float* impulse; + std::vector impulse; fc = fcin; ncoef = ncoefin; x = in_rate; @@ -88,7 +88,7 @@ void RESAMPLE::calc() ncoef = (ncoef / L + 1) * L; cpp = ncoef / L; h.resize(ncoef); - impulse = FIR::fir_bandpass(ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, gain * (double)L); + FIR::fir_bandpass(impulse, ncoef, fc_norm_low, fc_norm_high, 1.0, 1, 0, gain * (double)L); i = 0; for (int j = 0; j < L; j++) @@ -101,8 +101,6 @@ void RESAMPLE::calc() ring.resize(ringsize); idx_in = ringsize - 1; phnum = 0; - - delete[] impulse; } RESAMPLE::RESAMPLE ( diff --git a/wdsp/resamplef.cpp b/wdsp/resamplef.cpp index a79330f23..6f8e4fbba 100644 --- a/wdsp/resamplef.cpp +++ b/wdsp/resamplef.cpp @@ -25,6 +25,8 @@ warren@wpratt.com */ +#include + #include "comm.hpp" #include "fir.hpp" #include "resamplef.hpp" @@ -39,14 +41,16 @@ namespace WDSP { RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* out, int in_rate, int out_rate) { - RESAMPLEF *a = new RESAMPLEF; - int x, y, z; - int i, j, k; + auto *a = new RESAMPLEF; + int x; + int y; + int z; + int i; int min_rate; float full_rate; float fc; float fc_norm; - float* impulse; + std::vector impulse; a->run = run; a->size = size; a->in = in; @@ -72,36 +76,35 @@ RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* ou else min_rate = out_rate; - fc = 0.45 * (float)min_rate; + fc = 0.45f * (float)min_rate; full_rate = (float)(in_rate * a->L); fc_norm = fc / full_rate; a->ncoef = (int)(60.0 / fc_norm); a->ncoef = (a->ncoef / a->L + 1) * a->L; a->cpp = a->ncoef / a->L; - a->h = new float[a->ncoef]; // (float *) malloc0 (a->ncoef * sizeof (float)); - impulse = FIR::fir_bandpass (a->ncoef, -fc_norm, +fc_norm, 1.0, 1, 0, (float)a->L); + a->h = new float[a->ncoef]; + FIR::fir_bandpass (impulse, a->ncoef, -fc_norm, +fc_norm, 1.0, 1, 0, (float)a->L); i = 0; - for (j = 0; j < a->L; j ++) + for (int j = 0; j < a->L; j ++) { - for (k = 0; k < a->ncoef; k += a->L) + for (int k = 0; k < a->ncoef; k += a->L) a->h[i++] = impulse[j + k]; } a->ringsize = a->cpp; - a->ring = new float[a->ringsize]; //(float *) malloc0 (a->ringsize * sizeof (float)); + a->ring = new float[a->ringsize]; a->idx_in = a->ringsize - 1; a->phnum = 0; - delete[] (impulse); return a; } void RESAMPLEF::destroy_resampleF (RESAMPLEF *a) { - delete[] (a->ring); - delete[] (a->h); - delete (a); + delete[] a->ring; + delete[] a->h; + delete a; } void RESAMPLEF::flush_resampleF (RESAMPLEF *a) @@ -117,20 +120,20 @@ int RESAMPLEF::xresampleF (RESAMPLEF *a) if (a->run) { - int i, j, n; + int n; int idx_out; float I; - for (i = 0; i < a->size; i++) + for (int i = 0; i < a->size; i++) { - a->ring[a->idx_in] = (float)a->in[i]; + a->ring[a->idx_in] = a->in[i]; while (a->phnum < a->L) { I = 0.0; n = a->cpp * a->phnum; - for (j = 0; j < a->cpp; j++) + for (int j = 0; j < a->cpp; j++) { if ((idx_out = a->idx_in + j) >= a->ringsize) idx_out -= a->ringsize; @@ -138,7 +141,7 @@ int RESAMPLEF::xresampleF (RESAMPLEF *a) I += a->h[n + j] * a->ring[idx_out]; } - a->out[outsamps] = (float)I; + a->out[outsamps] = I; outsamps++; a->phnum += a->M; @@ -163,13 +166,13 @@ int RESAMPLEF::xresampleF (RESAMPLEF *a) void* RESAMPLEF::create_resampleFV (int in_rate, int out_rate) { - return (void *) create_resampleF (1, 0, 0, 0, in_rate, out_rate); + return (void *) create_resampleF (1, 0, nullptr, nullptr, in_rate, out_rate); } void RESAMPLEF::xresampleFV (float* input, float* output, int numsamps, int* outsamps, void* ptr) { - RESAMPLEF *a = (RESAMPLEF*) ptr; + auto *a = (RESAMPLEF*) ptr; a->in = input; a->out = output; a->size = numsamps; diff --git a/wdsp/rmatch.cpp b/wdsp/rmatch.cpp index 21fd0ffed..cca11e098 100644 --- a/wdsp/rmatch.cpp +++ b/wdsp/rmatch.cpp @@ -36,11 +36,11 @@ namespace WDSP { MAV* MAV::create_mav (int ringmin, int ringmax, float nom_value) { - MAV *a = new MAV; + auto *a = new MAV; a->ringmin = ringmin; a->ringmax = ringmax; a->nom_value = nom_value; - a->ring = new int[a->ringmax]; // (int *) malloc0 (a->ringmax * sizeof (int)); + a->ring = new int[a->ringmax]; a->mask = a->ringmax - 1; a->i = 0; a->load = 0; @@ -50,8 +50,8 @@ MAV* MAV::create_mav (int ringmin, int ringmax, float nom_value) void MAV::destroy_mav (MAV *a) { - delete[] (a->ring); - delete (a); + delete[] a->ring; + delete a; } void MAV::flush_mav (MAV *a) @@ -79,11 +79,11 @@ void MAV::xmav (MAV *a, int input, float* output) AAMAV* AAMAV::create_aamav (int ringmin, int ringmax, float nom_ratio) { - AAMAV *a = new AAMAV; + auto *a = new AAMAV; a->ringmin = ringmin; a->ringmax = ringmax; a->nom_ratio = nom_ratio; - a->ring = new int[a->ringmax]; // (int *) malloc0 (a->ringmax * sizeof (int)); + a->ring = new int[a->ringmax]; a->mask = a->ringmax - 1; a->i = 0; a->load = 0; @@ -94,8 +94,8 @@ AAMAV* AAMAV::create_aamav (int ringmin, int ringmax, float nom_ratio) void AAMAV::destroy_aamav (AAMAV *a) { - delete[] (a->ring); - delete[] (a); + delete[] a->ring; + delete[] a; } void AAMAV::flush_aamav (AAMAV *a) @@ -137,37 +137,38 @@ void AAMAV::xaamav (AAMAV *a, int input, float* output) void RMATCH::calc_rmatch (RMATCH *a) { int m; - float theta, dtheta; + float theta; + float dtheta; int max_ring_insize; a->nom_ratio = (float)a->nom_outrate / (float)a->nom_inrate; max_ring_insize = (int)(1.0 + (float)a->insize * (1.05 * a->nom_ratio)); if (a->ringsize < 2 * max_ring_insize) a->ringsize = 2 * max_ring_insize; if (a->ringsize < 2 * a->outsize) a->ringsize = 2 * a->outsize; - a->ring = new float[a->ringsize * 2]; // (float *) malloc0 (a->ringsize * sizeof (complex)); + a->ring = new float[a->ringsize * 2]; a->rsize = a->ringsize; a->n_ring = a->rsize / 2; a->iin = a->rsize / 2; a->iout = 0; - a->resout = new float[max_ring_insize * 2]; // (float *) malloc0 (max_ring_insize * sizeof (complex)); - a->v = VARSAMP::create_varsamp (1, a->insize, a->in, a->resout, a->nom_inrate, a->nom_outrate, + a->resout = new float[max_ring_insize * 2]; + a->v = new VARSAMP(1, a->insize, a->in, a->resout, a->nom_inrate, a->nom_outrate, a->fc_high, a->fc_low, a->R, a->gain, a->var, a->varmode); a->ffmav = AAMAV::create_aamav (a->ff_ringmin, a->ff_ringmax, a->nom_ratio); a->propmav = MAV::create_mav (a->prop_ringmin, a->prop_ringmax, 0.0); - a->pr_gain = a->prop_gain * 48000.0 / (float)a->nom_outrate; // adjust gain for rate + a->pr_gain = a->prop_gain * 48000.0f / (float)a->nom_outrate; // adjust gain for rate a->inv_nom_ratio = (float)a->nom_inrate / (float)a->nom_outrate; a->feed_forward = 1.0; a->av_deviation = 0.0; - a->ntslew = (int)(a->tslew * a->nom_outrate); + a->ntslew = (int)(a->tslew * (float) a->nom_outrate); if (a->ntslew + 1 > a->rsize / 2) a->ntslew = a->rsize / 2 - 1; - a->cslew = new float[a->ntslew + 1]; // (float *) malloc0 ((a->ntslew + 1) * sizeof (float)); - dtheta = PI / (float)a->ntslew; + a->cslew = new float[a->ntslew + 1]; + dtheta = (float) PI / (float) a->ntslew; theta = 0.0; for (m = 0; m <= a->ntslew; m++) { - a->cslew[m] = 0.5 * (1.0 - cos (theta)); + a->cslew[m] = 0.5f * (1.0f - cos (theta)); theta += dtheta; } - a->baux = new float[a->ringsize / 2 * 2]; // (float *) malloc0 (a->ringsize / 2 * sizeof (complex)); + a->baux = new float[a->ringsize / 2 * 2]; a->readsamps = 0; a->writesamps = 0; a->read_startup = (unsigned int)((float)a->nom_outrate * a->startup_delay); @@ -184,7 +185,7 @@ void RMATCH::decalc_rmatch (RMATCH *a) delete[] (a->cslew); MAV::destroy_mav (a->propmav); AAMAV::destroy_aamav (a->ffmav); - VARSAMP::destroy_varsamp (a->v); + delete a->v; delete[] (a->resout); delete[] (a->ring); } @@ -215,7 +216,7 @@ RMATCH* RMATCH::create_rmatch ( float tslew // slew/blend time (seconds) ) { - RMATCH *a = new RMATCH; + auto *a = new RMATCH; a->run = run; a->in = in; a->out = out; @@ -246,7 +247,7 @@ RMATCH* RMATCH::create_rmatch ( void RMATCH::destroy_rmatch (RMATCH *a) { decalc_rmatch (a); - delete (a); + delete a; } void RMATCH::reset_rmatch (RMATCH *a) @@ -264,30 +265,32 @@ void RMATCH::control (RMATCH *a, int change) float current_ratio; AAMAV::xaamav (a->ffmav, change, ¤t_ratio); current_ratio *= a->inv_nom_ratio; - a->feed_forward = a->ff_alpha * current_ratio + (1.0 - a->ff_alpha) * a->feed_forward; + a->feed_forward = a->ff_alpha * current_ratio + (1.0f - a->ff_alpha) * a->feed_forward; } { int deviation = a->n_ring - a->rsize / 2; MAV::xmav (a->propmav, deviation, &a->av_deviation); } a->var = a->feed_forward - a->pr_gain * a->av_deviation; - if (a->var > 1.04) a->var = 1.04; - if (a->var < 0.96) a->var = 0.96; + if (a->var > 1.04) a->var = 1.04f; + if (a->var < 0.96) a->var = 0.96f; } void RMATCH::blend (RMATCH *a) { - int i, j; + int i; + int j; for (i = 0, j = a->iout; i <= a->ntslew; i++, j = (j + 1) % a->rsize) { - a->ring[2 * j + 0] = a->cslew[i] * a->ring[2 * j + 0] + (1.0 - a->cslew[i]) * a->baux[2 * i + 0]; - a->ring[2 * j + 1] = a->cslew[i] * a->ring[2 * j + 1] + (1.0 - a->cslew[i]) * a->baux[2 * i + 1]; + a->ring[2 * j + 0] = a->cslew[i] * a->ring[2 * j + 0] + (1.0f - a->cslew[i]) * a->baux[2 * i + 0]; + a->ring[2 * j + 1] = a->cslew[i] * a->ring[2 * j + 1] + (1.0f - a->cslew[i]) * a->baux[2 * i + 1]; } } void RMATCH::upslew (RMATCH *a, int newsamps) { - int i, j; + int i; + int j; i = 0; j = a->iin; while (a->ucnt >= 0 && i < newsamps) @@ -302,10 +305,13 @@ void RMATCH::upslew (RMATCH *a, int newsamps) void RMATCH::xrmatchIN (void* b, float* in) { - RMATCH *a = (RMATCH*) b; + auto *a = (RMATCH*) b; if (a->run == 1) { - int newsamps, first, second, ovfl; + int newsamps; + int first; + int second; + int ovfl; float var; a->v->in = a->in = in; @@ -314,13 +320,12 @@ void RMATCH::xrmatchIN (void* b, float* in) else var = a->fvar; - newsamps = VARSAMP::xvarsamp (a->v, var); + newsamps = a->v->execute(var); a->n_ring += newsamps; if ((ovfl = a->n_ring - a->rsize) > 0) { a->overflows += 1; - // a->n_ring = a->rsize / 2; a->n_ring = a->rsize; // if ((a->ntslew + 1) > (a->rsize - a->iout)) @@ -336,7 +341,6 @@ void RMATCH::xrmatchIN (void* b, float* in) std::copy(a->ring + 2 * a->iout, a->ring + 2 * a->iout + first * 2, a->baux); std::copy(a->ring, a->ring + second * 2, a->baux + 2 * first); - // a->iout = (a->iout + ovfl + a->rsize / 2) % a->rsize; a->iout = (a->iout + ovfl) % a->rsize; // } @@ -376,8 +380,13 @@ void RMATCH::xrmatchIN (void* b, float* in) void RMATCH::dslew (RMATCH *a) { - int i, j, k, n; - int zeros, first, second; + int i; + int j; + int k; + int n; + int zeros; + int first; + int second; if (a->n_ring > a->ntslew + 1) { i = (a->iout + (a->n_ring - (a->ntslew + 1))) % a->rsize; @@ -414,7 +423,7 @@ void RMATCH::dslew (RMATCH *a) j--; n++; } - // zeros = a->outsize + a->rsize / 2 - n; + if ((zeros = a->outsize - n) > 0) // { // if (zeros > a->rsize - i) @@ -429,21 +438,20 @@ void RMATCH::dslew (RMATCH *a) } std::fill(a->ring + 2 * i, a->ring + 2 * i + first * 2, 0); std::fill(a->ring, a->ring + second * 2, 0); - n += zeros; // - } // - // a->n_ring = a->outsize + a->rsize / 2; - a->n_ring = n; // - // a->iin = (a->iout + a->outsize + a->rsize/2) % a->rsize; - a->iin = (a->iout + a->n_ring) % a->rsize; // + n += zeros; + } + a->n_ring = n; + a->iin = (a->iout + a->n_ring) % a->rsize; } void RMATCH::xrmatchOUT (void* b, float* out) { - RMATCH *a = (RMATCH*) b; + auto *a = (RMATCH*) b; if (a->run == 1) { - int first, second; + int first; + int second; a->out = out; if (a->n_ring < a->outsize) @@ -487,7 +495,7 @@ void RMATCH::xrmatchOUT (void* b, float* out) void RMATCH::getRMatchDiags (void* b, int* underflows, int* overflows, float* var, int* ringsize, int* nring) { - RMATCH *a = (RMATCH*) b; + auto *a = (RMATCH*) b; *underflows = a->underflows; *overflows = a->overflows; a->underflows &= 0xFFFFFFFF; @@ -500,15 +508,12 @@ void RMATCH::getRMatchDiags (void* b, int* underflows, int* overflows, float* va void RMATCH::resetRMatchDiags (void*) { - // RMATCH *a = (RMATCH*) b; - // InterlockedExchange (&a->underflows, 0); - // InterlockedExchange (&a->overflows, 0); } void RMATCH::forceRMatchVar (void* b, int force, float fvar) { - RMATCH *a = (RMATCH*) b; + auto *a = (RMATCH*) b; a->force = force; a->fvar = fvar; } @@ -518,8 +523,8 @@ void* RMATCH::create_rmatchV(int in_size, int out_size, int nom_inrate, int nom_ { return (void*)create_rmatch ( 1, // run - 0, // input buffer, stuffed in other calls - 0, // output buffer, stuffed in other calls + nullptr, // input buffer, stuffed in other calls + nullptr, // output buffer, stuffed in other calls in_size, // input buffer size (complex samples) out_size, // output buffer size (complex samples) nom_inrate, // nominal input sample-rate @@ -534,25 +539,25 @@ void* RMATCH::create_rmatchV(int in_size, int out_size, int nom_inrate, int nom_ var, // initial variable ratio 4096, // feed-forward moving average min size 262144, // feed-forward moving average max size - POWER OF TWO! - 0.01, // feed-forward exponential smoothing + 0.01f, // feed-forward exponential smoothing 4096, // proportional feedback min moving av ringsize 16384, // proportional feedback max moving av ringsize - POWER OF TWO! - 4.0e-06, // proportional feedback gain + 4.0e-06f, // proportional feedback gain 1, // linearly interpolate cvar by sample - 0.003 ); // slew time (seconds) + 0.003f ); // slew time (seconds) } void RMATCH::destroy_rmatchV (void* ptr) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; destroy_rmatch (a); } void RMATCH::setRMatchInsize (void* ptr, int insize) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); decalc_rmatch(a); @@ -564,7 +569,7 @@ void RMATCH::setRMatchInsize (void* ptr, int insize) void RMATCH::setRMatchOutsize (void* ptr, int outsize) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); decalc_rmatch(a); @@ -576,7 +581,7 @@ void RMATCH::setRMatchOutsize (void* ptr, int outsize) void RMATCH::setRMatchNomInrate (void* ptr, int nom_inrate) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); decalc_rmatch(a); @@ -588,7 +593,7 @@ void RMATCH::setRMatchNomInrate (void* ptr, int nom_inrate) void RMATCH::setRMatchNomOutrate (void* ptr, int nom_outrate) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); decalc_rmatch(a); @@ -600,7 +605,7 @@ void RMATCH::setRMatchNomOutrate (void* ptr, int nom_outrate) void RMATCH::setRMatchRingsize (void* ptr, int ringsize) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); decalc_rmatch(a); @@ -612,7 +617,7 @@ void RMATCH::setRMatchRingsize (void* ptr, int ringsize) void RMATCH::setRMatchFeedbackGain (void* b, float feedback_gain) { - RMATCH *a = (RMATCH*) b; + auto *a = (RMATCH*) b; a->prop_gain = feedback_gain; a->pr_gain = a->prop_gain * 48000.0 / (float)a->nom_outrate; } @@ -620,9 +625,8 @@ void RMATCH::setRMatchFeedbackGain (void* b, float feedback_gain) void RMATCH::setRMatchSlewTime (void* b, float slew_time) { - RMATCH *a = (RMATCH*) b; - a->run = 0; // InterlockedBitTestAndReset(&a->run, 0); // turn OFF new data coming into the rmatch - // Sleep(10); // wait for processing to cease + auto *a = (RMATCH*) b; + a->run = 0; decalc_rmatch(a); // deallocate all memory EXCEPT the data structure holding all current parameters a->tslew = slew_time; // change the value of 'slew_time' calc_rmatch(a); // recalculate/reallocate everything in the RMATCH @@ -632,21 +636,20 @@ void RMATCH::setRMatchSlewTime (void* b, float slew_time) void RMATCH::setRMatchSlewTime1(void* b, float slew_time) { - RMATCH *a = (RMATCH*) b; - float theta, dtheta; - int m; + auto *a = (RMATCH*) b; + float theta; + float dtheta; a->run = 0; - // Sleep(10); - delete[](a->cslew); + delete[] a->cslew; a->tslew = slew_time; - a->ntslew = (int)(a->tslew * a->nom_outrate); + a->ntslew = (int)(a->tslew * (float) a->nom_outrate); if (a->ntslew + 1 > a->rsize / 2) a->ntslew = a->rsize / 2 - 1; a->cslew = new float[a->ntslew + 1]; // (float*)malloc0((a->ntslew + 1) * sizeof(float)); - dtheta = PI / (float)a->ntslew; + dtheta = (float) PI / (float)a->ntslew; theta = 0.0; - for (m = 0; m <= a->ntslew; m++) + for (int m = 0; m <= a->ntslew; m++) { - a->cslew[m] = 0.5 * (1.0 - cos(theta)); + a->cslew[m] = 0.5f * (1.0f - cos(theta)); theta += dtheta; } a->run = 1; @@ -655,9 +658,8 @@ void RMATCH::setRMatchSlewTime1(void* b, float slew_time) void RMATCH::setRMatchPropRingMin(void* ptr, int prop_min) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; - // Sleep(10); decalc_rmatch(a); a->prop_ringmin = prop_min; calc_rmatch(a); @@ -667,9 +669,8 @@ void RMATCH::setRMatchPropRingMin(void* ptr, int prop_min) void RMATCH::setRMatchPropRingMax(void* ptr, int prop_max) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; - // Sleep(10); decalc_rmatch(a); a->prop_ringmax = prop_max; // must be a power of two calc_rmatch(a); @@ -679,9 +680,8 @@ void RMATCH::setRMatchPropRingMax(void* ptr, int prop_max) void RMATCH::setRMatchFFRingMin(void* ptr, int ff_ringmin) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; - // Sleep(10); decalc_rmatch(a); a->ff_ringmin = ff_ringmin; calc_rmatch(a); @@ -691,9 +691,8 @@ void RMATCH::setRMatchFFRingMin(void* ptr, int ff_ringmin) void RMATCH::setRMatchFFRingMax(void* ptr, int ff_ringmax) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; - // Sleep(10); decalc_rmatch(a); a->ff_ringmax = ff_ringmax; // must be a power of two calc_rmatch(a); @@ -703,7 +702,7 @@ void RMATCH::setRMatchFFRingMax(void* ptr, int ff_ringmax) void RMATCH::setRMatchFFAlpha(void* ptr, float ff_alpha) { - RMATCH *a = (RMATCH*) ptr; + auto *a = (RMATCH*) ptr; a->run = 0; std::this_thread::sleep_for(std::chrono::seconds(10)); a->ff_alpha = ff_alpha; @@ -713,7 +712,7 @@ void RMATCH::setRMatchFFAlpha(void* ptr, float ff_alpha) void RMATCH::getControlFlag(void* ptr, int* control_flag) { - RMATCH *a = (RMATCH*) ptr; + RMATCH const *a = (RMATCH*) ptr; *control_flag = a->control_flag; } @@ -724,8 +723,8 @@ void* RMATCH::create_rmatchLegacyV(int in_size, int out_size, int nom_inrate, in { return (void*) create_rmatch( 1, // run - 0, // input buffer, stuffed in other calls - 0, // output buffer, stuffed in other calls + nullptr, // input buffer, stuffed in other calls + nullptr, // output buffer, stuffed in other calls in_size, // input buffer size (complex samples) out_size, // output buffer size (complex samples) nom_inrate, // nominal input sample-rate @@ -740,12 +739,12 @@ void* RMATCH::create_rmatchLegacyV(int in_size, int out_size, int nom_inrate, in 1.0, // initial variable ratio 4096, // feed-forward moving average min size 262144, // feed-forward moving average max size - POWER OF TWO! - 0.01, // feed-forward exponential smoothing + 0.01f, // feed-forward exponential smoothing 4096, // proportional feedback min moving av ringsize 16384, // proportional feedback max moving av ringsize - POWER OF TWO! - 1.0e-06, // proportional feedback gain ***W4WMT - reduce loop gain a bit for PowerSDR to help Primary buffers > 512 + 1.0e-06f, // proportional feedback gain ***W4WMT - reduce loop gain a bit for PowerSDR to help Primary buffers > 512 0, // linearly interpolate cvar by sample ***W4WMT - set varmode = 0 for PowerSDR (doesn't work otherwise!?!) - 0.003); // slew time (seconds) + 0.003f); // slew time (seconds) } } // namespace WDSP diff --git a/wdsp/varsamp.cpp b/wdsp/varsamp.cpp index 5a9e3c7f3..ffbdfcc35 100644 --- a/wdsp/varsamp.cpp +++ b/wdsp/varsamp.cpp @@ -31,236 +31,220 @@ warren@wpratt.com namespace WDSP { -void VARSAMP::calc_varsamp (VARSAMP *a) +void VARSAMP::calc() { - float min_rate, max_rate, norm_rate; - float fc_norm_high, fc_norm_low; - a->nom_ratio = (float)a->out_rate / (float)a->in_rate; - a->cvar = a->var * a->nom_ratio; - a->inv_cvar = 1.0 / a->cvar; - a->old_inv_cvar = a->inv_cvar; - a->dicvar = 0.0; - a->delta = fabs (1.0 / a->cvar - 1.0); - a->fc = a->fcin; - if (a->out_rate >= a->in_rate) + float min_rate; + float max_rate; + float norm_rate; + float fc_norm_high; + float fc_norm_low; + nom_ratio = (float)out_rate / (float)in_rate; + cvar = var * nom_ratio; + inv_cvar = 1.0f / cvar; + old_inv_cvar = inv_cvar; + dicvar = 0.0; + delta = (float) fabs (1.0 / cvar - 1.0); + fc = fcin; + if (out_rate >= in_rate) { - min_rate = (float)a->in_rate; - max_rate = (float)a->out_rate; + min_rate = (float)in_rate; norm_rate = min_rate; } else { - min_rate = (float)a->out_rate; - max_rate = (float)a->in_rate; + min_rate = (float)out_rate; + max_rate = (float)in_rate; norm_rate = max_rate; } - if (a->fc == 0.0) a->fc = 0.95 * 0.45 * min_rate; - fc_norm_high = a->fc / norm_rate; - if (a->fc_low < 0.0) + if (fc == 0.0) fc = 0.95f * 0.45f * min_rate; + fc_norm_high = fc / norm_rate; + if (fc_low < 0.0) fc_norm_low = - fc_norm_high; else - fc_norm_low = a->fc_low / norm_rate; - a->rsize = (int)(140.0 * norm_rate / min_rate); - a->ncoef = a->rsize + 1; - a->ncoef += (a->R - 1) * (a->ncoef - 1); - a->h = FIR::fir_bandpass(a->ncoef, fc_norm_low, fc_norm_high, (float)a->R, 1, 0, (float)a->R * a->gain); - // print_impulse ("imp.txt", a->ncoef, a->h, 0, 0); - a->ring = new float[a->rsize * 2]; // (float *)malloc0(a->rsize * sizeof(complex)); - a->idx_in = a->rsize - 1; - a->h_offset = 0.0; - a->hs = new float[a->rsize]; // (float *)malloc0 (a->rsize * sizeof (float)); - a->isamps = 0.0; + fc_norm_low = fc_low / norm_rate; + rsize = (int)(140.0 * norm_rate / min_rate); + ncoef = rsize + 1; + ncoef += (R - 1) * (ncoef - 1); + FIR::fir_bandpass(h, ncoef, fc_norm_low, fc_norm_high, (float)R, 1, 0, (float)R * gain); + ring.resize(rsize * 2); + idx_in = rsize - 1; + h_offset = 0.0; + hs.resize(rsize); + isamps = 0.0; } -void VARSAMP::decalc_varsamp (VARSAMP *a) -{ - delete[] (a->hs); - delete[] (a->ring); - delete[] (a->h); -} - -VARSAMP* VARSAMP::create_varsamp ( - int run, - int size, - float* in, - float* out, - int in_rate, - int out_rate, - float fc, - float fc_low, - int R, - float gain, - float var, - int varmode +VARSAMP::VARSAMP( + int _run, + int _size, + float* _in, + float* _out, + int _in_rate, + int _out_rate, + float _fc, + float _fc_low, + int _R, + float _gain, + float _var, + int _varmode ) { - VARSAMP *a = new VARSAMP; - - a->run = run; - a->size = size; - a->in = in; - a->out = out; - a->in_rate = in_rate; - a->out_rate = out_rate; - a->fcin = fc; - a->fc_low = fc_low; - a->R = R; - a->gain = gain; - a->var = var; - a->varmode = varmode; - calc_varsamp (a); - return a; + run = _run; + size = _size; + in = _in; + out = _out; + in_rate = _in_rate; + out_rate = _out_rate; + fcin = _fc; + fc_low = _fc_low; + R = _R; + gain = _gain; + var = _var; + varmode = _varmode; + calc(); } -void VARSAMP::destroy_varsamp (VARSAMP *a) +void VARSAMP::flush() { - decalc_varsamp (a); - delete (a); + std::fill(ring.begin(), ring.end(), 0); + idx_in = rsize - 1; + h_offset = 0.0; + isamps = 0.0; } -void VARSAMP::flush_varsamp (VARSAMP *a) +void VARSAMP::hshift() { - std::fill(a->ring, a->ring + a->rsize * 2, 0); - a->idx_in = a->rsize - 1; - a->h_offset = 0.0; - a->isamps = 0.0; -} - -void VARSAMP::hshift (VARSAMP *a) -{ - int i, j, k; + int i; + int j; + int k; int hidx; - float frac, pos; - pos = (float)a->R * a->h_offset; + float frac; + float pos; + pos = (float)R * h_offset; hidx = (int)(pos); frac = pos - (float)hidx; - for (i = a->rsize - 1, j = hidx, k = hidx + 1; i >= 0; i--, j += a->R, k += a->R) - a->hs[i] = a->h[j] + frac * (a->h[k] - a->h[j]); + for (i = rsize - 1, j = hidx, k = hidx + 1; i >= 0; i--, j += R, k += R) + hs[i] = h[j] + frac * (h[k] - h[j]); } -int VARSAMP::xvarsamp (VARSAMP *a, float var) +int VARSAMP::execute(float _var) { int outsamps = 0; - uint64_t* picvar; + uint64_t const* picvar; uint64_t N; - a->var = var; - a->old_inv_cvar = a->inv_cvar; - a->cvar = a->var * a->nom_ratio; - a->inv_cvar = 1.0 / a->cvar; - if (a->varmode) + var = _var; + old_inv_cvar = inv_cvar; + cvar = var * nom_ratio; + inv_cvar = 1.0f / cvar; + if (varmode) { - a->dicvar = (a->inv_cvar - a->old_inv_cvar) / (float)a->size; - a->inv_cvar = a->old_inv_cvar; + dicvar = (inv_cvar - old_inv_cvar) / (float)size; + inv_cvar = old_inv_cvar; } - else a->dicvar = 0.0; - if (a->run) + else dicvar = 0.0; + if (run) { - int i, j; int idx_out; - float I, Q; - for (i = 0; i < a->size; i++) + float I; + float Q; + for (int i = 0; i < size; i++) { - a->ring[2 * a->idx_in + 0] = a->in[2 * i + 0]; - a->ring[2 * a->idx_in + 1] = a->in[2 * i + 1]; - a->inv_cvar += a->dicvar; - picvar = (uint64_t*)(&a->inv_cvar); + ring[2 * idx_in + 0] = in[2 * i + 0]; + ring[2 * idx_in + 1] = in[2 * i + 1]; + inv_cvar += dicvar; + picvar = (uint64_t*)(&inv_cvar); N = *picvar & 0xffffffffffff0000; - a->inv_cvar = static_cast(N); - a->delta = 1.0 - a->inv_cvar; - while (a->isamps < 1.0) + inv_cvar = static_cast(N); + delta = 1.0f - inv_cvar; + while (isamps < 1.0) { I = 0.0; Q = 0.0; - hshift (a); - a->h_offset += a->delta; - while (a->h_offset >= 1.0) a->h_offset -= 1.0; - while (a->h_offset < 0.0) a->h_offset += 1.0; - for (j = 0; j < a->rsize; j++) + hshift(); + h_offset += delta; + while (h_offset >= 1.0) h_offset -= 1.0f; + while (h_offset < 0.0) h_offset += 1.0f; + for (int j = 0; j < rsize; j++) { - if ((idx_out = a->idx_in + j) >= a->rsize) idx_out -= a->rsize; - I += a->hs[j] * a->ring[2 * idx_out + 0]; - Q += a->hs[j] * a->ring[2 * idx_out + 1]; + if ((idx_out = idx_in + j) >= rsize) idx_out -= rsize; + I += hs[j] * ring[2 * idx_out + 0]; + Q += hs[j] * ring[2 * idx_out + 1]; } - a->out[2 * outsamps + 0] = I; - a->out[2 * outsamps + 1] = Q; + out[2 * outsamps + 0] = I; + out[2 * outsamps + 1] = Q; outsamps++; - a->isamps += a->inv_cvar; + isamps += inv_cvar; } - a->isamps -= 1.0; - if (--a->idx_in < 0) a->idx_in = a->rsize - 1; + isamps -= 1.0f; + if (--idx_in < 0) idx_in = rsize - 1; } } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); return outsamps; } -void VARSAMP::setBuffers_varsamp (VARSAMP *a, float* in, float* out) +void VARSAMP::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; + in = _in; + out = _out; } -void VARSAMP::setSize_varsamp (VARSAMP *a, int size) +void VARSAMP::setSize(int _size) { - a->size = size; - flush_varsamp (a); + size = _size; + flush(); } -void VARSAMP::setInRate_varsamp (VARSAMP *a, int rate) +void VARSAMP::setInRate(int _rate) { - decalc_varsamp (a); - a->in_rate = rate; - calc_varsamp (a); + in_rate = _rate; + calc(); } -void VARSAMP::setOutRate_varsamp (VARSAMP *a, int rate) +void VARSAMP::setOutRate(int _rate) { - decalc_varsamp (a); - a->out_rate = rate; - calc_varsamp (a); + out_rate = _rate; + calc(); } -void VARSAMP::setFCLow_varsamp (VARSAMP *a, float fc_low) +void VARSAMP::setFCLow(float _fc_low) { - if (fc_low != a->fc_low) + if (_fc_low != fc_low) { - decalc_varsamp (a); - a->fc_low = fc_low; - calc_varsamp (a); + fc_low = _fc_low; + calc(); } } -void VARSAMP::setBandwidth_varsamp (VARSAMP *a, float fc_low, float fc_high) +void VARSAMP::setBandwidth(float _fc_low, float _fc_high) { - if (fc_low != a->fc_low || fc_high != a->fcin) + if (_fc_low != fc_low || _fc_high != fcin) { - decalc_varsamp (a); - a->fc_low = fc_low; - a->fcin = fc_high; - calc_varsamp (a); + fc_low = _fc_low; + fcin = _fc_high; + calc(); } } // exported calls -void* VARSAMP::create_varsampV (int in_rate, int out_rate, int R) +void* VARSAMP::create_varsampV (int _in_rate, int _out_rate, int R) { - return (void *)create_varsamp (1, 0, 0, 0, in_rate, out_rate, 0.0, -1.0, R, 1.0, 1.0, 1); + return (void *) new VARSAMP(1, 0, nullptr, nullptr, _in_rate, _out_rate, 0.0, -1.0, R, 1.0, 1.0, 1); } void VARSAMP::xvarsampV (float* input, float* output, int numsamps, float var, int* outsamps, void* ptr) { - VARSAMP *a = (VARSAMP*) ptr; + auto *a = (VARSAMP*) ptr; a->in = input; a->out = output; a->size = numsamps; - *outsamps = xvarsamp(a, var); + *outsamps = a->execute(var); } void VARSAMP::destroy_varsampV (void* ptr) { - destroy_varsamp ( (VARSAMP*) ptr ); + delete (VARSAMP*) ptr; } } // namespace WDSP diff --git a/wdsp/varsamp.hpp b/wdsp/varsamp.hpp index 97e21dd00..dacc7948f 100644 --- a/wdsp/varsamp.hpp +++ b/wdsp/varsamp.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_varsamp_h #define wdsp_varsamp_h +#include + #include "export.h" namespace WDSP { @@ -47,9 +49,9 @@ public: float gain; int idx_in; int ncoef; - float* h; + std::vector h; int rsize; - float* ring; + std::vector ring; float var; int varmode; float cvar; @@ -57,13 +59,13 @@ public: float old_inv_cvar; float dicvar; float delta; - float* hs; + std::vector hs; int R; float h_offset; float isamps; float nom_ratio; - static VARSAMP* create_varsamp ( + VARSAMP( int run, int size, float* in, @@ -77,24 +79,26 @@ public: float var, int varmode ); - static void destroy_varsamp (VARSAMP *a); - static void flush_varsamp (VARSAMP *a); - static int xvarsamp (VARSAMP *a, float var); - static void setBuffers_varsamp (VARSAMP *a, float* in, float* out); - static void setSize_varsamp (VARSAMP *a, int size); - static void setInRate_varsamp (VARSAMP *a, int rate); - static void setOutRate_varsamp (VARSAMP *a, int rate); - static void setFCLow_varsamp (VARSAMP *a, float fc_low); - static void setBandwidth_varsamp (VARSAMP *a, float fc_low, float fc_high); + VARSAMP(const VARSAMP&) = delete; + VARSAMP& operator=(VARSAMP& other) = delete; + ~VARSAMP() = default; + + void flush(); + int execute(float var); + void setBuffers(float* in, float* out); + void setSize(int size); + void setInRate(int rate); + void setOutRate(int rate); + void setFCLow(float fc_low); + void setBandwidth(float fc_low, float fc_high); // Exported calls static void* create_varsampV (int in_rate, int out_rate, int R); static void xvarsampV (float* input, float* output, int numsamps, float var, int* outsamps, void* ptr); static void destroy_varsampV (void* ptr); private: - static void calc_varsamp (VARSAMP *a); - static void decalc_varsamp (VARSAMP *a); - static void hshift (VARSAMP *a); + void calc(); + void hshift(); }; } // namespace WDSP From 12f4d0a0fde5ee55c40b977db087b6f0167d1e1a Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 09:59:42 +0200 Subject: [PATCH 42/46] WDSP: FIROPT rework --- wdsp/firopt.cpp | 211 ++++++++++++++++++++++++------------------------ wdsp/firopt.hpp | 54 ++++++++----- wdsp/fmd.cpp | 10 +-- 3 files changed, 143 insertions(+), 132 deletions(-) diff --git a/wdsp/firopt.cpp b/wdsp/firopt.cpp index 3d43d0037..1502a6412 100644 --- a/wdsp/firopt.cpp +++ b/wdsp/firopt.cpp @@ -37,172 +37,169 @@ namespace WDSP { * * ********************************************************************************************************/ -void FIROPT::plan_firopt (FIROPT *a) +void FIROPT::plan() { // must call for change in 'nc', 'size', 'out' - int i; - a->nfor = a->nc / a->size; - a->buffidx = 0; - a->idxmask = a->nfor - 1; - a->fftin = new float[2 * a->size * 2]; - a->fftout = new float*[a->nfor]; - a->fmask = new float*[a->nfor]; - a->maskgen = new float[2 * a->size * 2]; - a->pcfor = new fftwf_plan[a->nfor]; - a->maskplan = new fftwf_plan[a->nfor]; - for (i = 0; i < a->nfor; i++) + nfor = nc / size; + buffidx = 0; + idxmask = nfor - 1; + fftin.resize(2 * size * 2); + fftout.resize(nfor); + fmask.resize(nfor); + maskgen.resize(2 * size * 2); + pcfor.resize(nfor); + maskplan.resize(nfor); + for (int i = 0; i < nfor; i++) { - a->fftout[i] = new float[2 * a->size * 2]; - a->fmask[i] = new float[2 * a->size * 2]; - a->pcfor[i] = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->fftin, - (fftwf_complex *)a->fftout[i], + fftout[i].resize(2 * size * 2); + fmask[i].resize(2 * size * 2); + pcfor[i] = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)fftin.data(), + (fftwf_complex *)fftout[i].data(), FFTW_FORWARD, FFTW_PATIENT ); - a->maskplan[i] = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->maskgen, - (fftwf_complex *)a->fmask[i], + maskplan[i] = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)maskgen.data(), + (fftwf_complex *)fmask[i].data(), FFTW_FORWARD, FFTW_PATIENT ); } - a->accum = new float[2 * a->size * 2]; - a->crev = fftwf_plan_dft_1d( - 2 * a->size, - (fftwf_complex *)a->accum, - (fftwf_complex *)a->out, + accum.resize(2 * size * 2); + crev = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)accum.data(), + (fftwf_complex *)out, FFTW_BACKWARD, FFTW_PATIENT ); } -void FIROPT::calc_firopt (FIROPT *a) +void FIROPT::calc() { // call for change in frequency, rate, wintype, gain // must also call after a call to plan_firopt() std::vector impulse; - FIR::fir_bandpass (impulse, a->nc, a->f_low, a->f_high, a->samplerate, a->wintype, 1, a->gain); - a->buffidx = 0; - for (int i = 0; i < a->nfor; i++) + FIR::fir_bandpass (impulse, nc, f_low, f_high, samplerate, wintype, 1, gain); + buffidx = 0; + for (int i = 0; i < nfor; i++) { // I right-justified the impulse response => take output from left side of output buff, discard right side // Be careful about flipping an asymmetrical impulse response. - std::copy(&(impulse[2 * a->size * i]), &(impulse[2 * a->size * i]) + a->size * 2, &(a->maskgen[2 * a->size])); - fftwf_execute (a->maskplan[i]); + std::copy(&(impulse[2 * size * i]), &(impulse[2 * size * i]) + size * 2, &(maskgen[2 * size])); + fftwf_execute (maskplan[i]); } } -FIROPT* FIROPT::create_firopt (int run, int position, int size, float* in, float* out, - int nc, float f_low, float f_high, int samplerate, int wintype, float gain) +FIROPT::FIROPT( + int _run, + int _position, + int _size, + float* _in, + float* _out, + int _nc, + float _f_low, + float _f_high, + int _samplerate, + int _wintype, + float _gain +) { - FIROPT *a = new FIROPT; - a->run = run; - a->position = position; - a->size = size; - a->in = in; - a->out = out; - a->nc = nc; - a->f_low = f_low; - a->f_high = f_high; - a->samplerate = (float) samplerate; - a->wintype = wintype; - a->gain = gain; - plan_firopt (a); - calc_firopt (a); - return a; + run = _run; + position = _position; + size = _size; + in = _in; + out = _out; + nc = _nc; + f_low = _f_low; + f_high = _f_high; + samplerate = (float) _samplerate; + wintype = _wintype; + gain = _gain; + plan(); + calc(); } -void FIROPT::deplan_firopt (FIROPT *a) +void FIROPT::deplan() { - int i; - fftwf_destroy_plan (a->crev); - delete[] (a->accum); - for (i = 0; i < a->nfor; i++) + fftwf_destroy_plan (crev); + for (int i = 0; i < nfor; i++) { - delete[] (a->fftout[i]); - delete[] (a->fmask[i]); - fftwf_destroy_plan (a->pcfor[i]); - fftwf_destroy_plan (a->maskplan[i]); + fftwf_destroy_plan (pcfor[i]); + fftwf_destroy_plan (maskplan[i]); } - delete[] (a->maskplan); - delete[] (a->pcfor); - delete[] (a->maskgen); - delete[] (a->fmask); - delete[] (a->fftout); - delete[] (a->fftin); } -void FIROPT::destroy_firopt (FIROPT *a) +FIROPT::~FIROPT() { - deplan_firopt (a); - delete (a); + deplan(); } -void FIROPT::flush_firopt (FIROPT *a) +void FIROPT::flush() { - std::fill(a->fftin, a->fftin + 2 * a->size * 2, 0); - for (int i = 0; i < a->nfor; i++) - std::fill(a->fftout[i], a->fftout[i] + 2 * a->size * 2, 0); - a->buffidx = 0; + std::fill(fftin.begin(), fftin.end(), 0); + for (int i = 0; i < nfor; i++) + std::fill(fftout[i].begin(), fftout[i].end(), 0); + buffidx = 0; } -void FIROPT::xfiropt (FIROPT *a, int pos) +void FIROPT::execute(int pos) { - if (a->run && (a->position == pos)) + if (run && (position == pos)) { int k; - std::copy(a->in, a->in + a->size * 2, &(a->fftin[2 * a->size])); - fftwf_execute (a->pcfor[a->buffidx]); - k = a->buffidx; - std::fill(a->accum, a->accum + 2 * a->size * 2, 0); - for (int j = 0; j < a->nfor; j++) + std::copy(in, in + size * 2, &(fftin[2 * size])); + fftwf_execute (pcfor[buffidx]); + k = buffidx; + std::fill(accum.begin(), accum.end(), 0); + for (int j = 0; j < nfor; j++) { - for (int i = 0; i < 2 * a->size; i++) + for (int i = 0; i < 2 * size; i++) { - a->accum[2 * i + 0] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 0] - a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 1]; - a->accum[2 * i + 1] += a->fftout[k][2 * i + 0] * a->fmask[j][2 * i + 1] + a->fftout[k][2 * i + 1] * a->fmask[j][2 * i + 0]; + accum[2 * i + 0] += fftout[k][2 * i + 0] * fmask[j][2 * i + 0] - fftout[k][2 * i + 1] * fmask[j][2 * i + 1]; + accum[2 * i + 1] += fftout[k][2 * i + 0] * fmask[j][2 * i + 1] + fftout[k][2 * i + 1] * fmask[j][2 * i + 0]; } - k = (k + a->idxmask) & a->idxmask; + k = (k + idxmask) & idxmask; } - a->buffidx = (a->buffidx + 1) & a->idxmask; - fftwf_execute (a->crev); - std::copy(&(a->fftin[2 * a->size]), &(a->fftin[2 * a->size]) + a->size * 2, a->fftin); + buffidx = (buffidx + 1) & idxmask; + fftwf_execute (crev); + std::copy(&(fftin[2 * size]), &(fftin[2 * size]) + size * 2, fftin.begin()); } - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + else if (in != out) + std::copy( in, in + size * 2, out); } -void FIROPT::setBuffers_firopt (FIROPT *a, float* in, float* out) +void FIROPT::setBuffers(float* _in, float* _out) { - a->in = in; - a->out = out; - deplan_firopt (a); - plan_firopt (a); - calc_firopt (a); + in = _in; + out = _out; + deplan(); + plan(); + calc(); } -void FIROPT::setSamplerate_firopt (FIROPT *a, int rate) +void FIROPT::setSamplerate(int _rate) { - a->samplerate = (float) rate; - calc_firopt (a); + samplerate = (float) _rate; + calc(); } -void FIROPT::setSize_firopt (FIROPT *a, int size) +void FIROPT::setSize(int _size) { - a->size = size; - deplan_firopt (a); - plan_firopt (a); - calc_firopt (a); + size = _size; + deplan(); + plan(); + calc(); } -void FIROPT::setFreqs_firopt (FIROPT *a, float f_low, float f_high) +void FIROPT::setFreqs(float _f_low, float _f_high) { - a->f_low = f_low; - a->f_high = f_high; - calc_firopt (a); + f_low = _f_low; + f_high = _f_high; + calc(); } diff --git a/wdsp/firopt.hpp b/wdsp/firopt.hpp index 343de76c9..b6ac6d330 100644 --- a/wdsp/firopt.hpp +++ b/wdsp/firopt.hpp @@ -43,6 +43,7 @@ namespace WDSP { class WDSP_API FIROPT { +public: int run; // run control int position; // position at which to execute int size; // input/output buffer size, power of two @@ -55,31 +56,46 @@ class WDSP_API FIROPT int wintype; // filter window type float gain; // filter gain int nfor; // number of buffers in delay line - float* fftin; // fft input buffer - float** fmask; // frequency domain masks - float** fftout; // fftout delay line - float* accum; // frequency domain accumulator + std::vector fftin; // fft input buffer + std::vector> fmask; // frequency domain masks + std::vector> fftout; // fftout delay line + std::vector accum; // frequency domain accumulator int buffidx; // fft out buffer index int idxmask; // mask for index computations - float* maskgen; // input for mask generation FFT - fftwf_plan* pcfor; // array of forward FFT plans + std::vector maskgen; // input for mask generation FFT + std::vector pcfor; // array of forward FFT plans fftwf_plan crev; // reverse fft plan - fftwf_plan* maskplan; // plans for frequency domain masks + std::vector maskplan; // plans for frequency domain masks - static FIROPT* create_firopt (int run, int position, int size, float* in, float* out, - int nc, float f_low, float f_high, int samplerate, int wintype, float gain); - static void xfiropt (FIROPT *a, int pos); - static void destroy_firopt (FIROPT *a); - static void flush_firopt (FIROPT *a); - static void setBuffers_firopt (FIROPT *a, float* in, float* out); - static void setSamplerate_firopt (FIROPT *a, int rate); - static void setSize_firopt (FIROPT *a, int size); - static void setFreqs_firopt (FIROPT *a, float f_low, float f_high); + FIROPT( + int run, + int position, + int size, + float* in, + float* out, + int nc, + float f_low, + float f_high, + int samplerate, + int wintype, + float gain + ); + FIROPT(const FIROPT&) = delete; + FIROPT& operator=(const FIROPT& other) = delete; + ~FIROPT(); + + void destroy(); + void flush(); + void execute(int pos); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setFreqs(float f_low, float f_high); private: - static void plan_firopt (FIROPT *a); - static void calc_firopt (FIROPT *a); - static void deplan_firopt (FIROPT *a); + void plan(); + void calc(); + void deplan(); }; } // namespace WDSP diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 144bbb8bc..3f8719274 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -168,8 +168,8 @@ FMD::FMD( FMD::~FMD() { - delete (paud); - delete (pde); + delete paud; + delete pde; decalc(); } @@ -281,7 +281,7 @@ void FMD::setSize(int _size) calc(); audio.resize(size * 2); // de-emphasis filter - delete (pde); + delete pde; std::vector impulse(2 * nc_de); FCurve::fc_impulse ( impulse, @@ -298,7 +298,7 @@ void FMD::setSize(int _size) ); pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); // audio filter - delete (paud); + delete paud; std::vector impulseb; FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb.data()); @@ -363,8 +363,6 @@ void FMD::setMPde(int mp) void FMD::setNCaud(int nc) { - float* impulse; - if (nc_aud != nc) { nc_aud = nc; From ef0255f2bbe0333ac26fe721a0aaead0afaacee1 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 12:21:04 +0200 Subject: [PATCH 43/46] WDSP: impulse responses refactoring (5) --- wdsp/RXA.cpp | 8 ++++---- wdsp/TXA.cpp | 6 +++--- wdsp/bandpass.cpp | 14 +++++++------- wdsp/bpsnba.cpp | 2 +- wdsp/cfir.cpp | 2 +- wdsp/emphp.cpp | 30 +++++++++++++++--------------- wdsp/eqp.cpp | 18 +++++++++--------- wdsp/fircore.cpp | 33 ++++++++++++++++++++------------- wdsp/fircore.hpp | 7 +++---- wdsp/fmd.cpp | 20 ++++++++++---------- wdsp/fmmod.cpp | 12 ++++++------ wdsp/fmsq.cpp | 4 ++-- wdsp/icfir.cpp | 2 +- wdsp/nbp.cpp | 12 ++++++------ 14 files changed, 88 insertions(+), 82 deletions(-) diff --git a/wdsp/RXA.cpp b/wdsp/RXA.cpp index 740653271..001e3ab26 100644 --- a/wdsp/RXA.cpp +++ b/wdsp/RXA.cpp @@ -1005,7 +1005,7 @@ void RXA::updateNBPFilters() if (a->fnfrun) { a->calc_impulse(); - a->fircore->setImpulse(a->impulse.data(), 1); + a->fircore->setImpulse(a->impulse, 1); } if (b->bpsnba->fnfrun) { @@ -1096,7 +1096,7 @@ void RXA::nbpSetNotchesRun(int _run) b->fnfrun = a->master_run; bpsnbaCheck(mode, _run); b->calc_impulse(); // recalc nbp impulse response - b->fircore->setImpulse(b->impulse.data(), 0); // calculate new filter masks + b->fircore->setImpulse(b->impulse, 0); // calculate new filter masks bpsnbaSet(); b->fircore->setUpdate(); // apply new filter masks } @@ -1113,7 +1113,7 @@ void RXA::nbpSetWindow(int _wintype) { a->wintype = _wintype; a->calc_impulse(); - a->fircore->setImpulse(a->impulse.data(), 1); + a->fircore->setImpulse(a->impulse, 1); } if (b->wintype != _wintype) @@ -1134,7 +1134,7 @@ void RXA::nbpSetAutoIncrease(int _autoincr) { a->autoincr = _autoincr; a->calc_impulse(); - a->fircore->setImpulse(a->impulse.data(), 1); + a->fircore->setImpulse(a->impulse, 1); } if (b->autoincr != _autoincr) diff --git a/wdsp/TXA.cpp b/wdsp/TXA.cpp index fedb213d1..7fe31d5fe 100644 --- a/wdsp/TXA.cpp +++ b/wdsp/TXA.cpp @@ -926,7 +926,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse.data()); + a->fircore->setNc(impulse); } a = bp1; @@ -945,7 +945,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse.data()); + a->fircore->setNc(impulse); } a = bp2; @@ -964,7 +964,7 @@ void TXA::setBandpassNC(int _nc) 1, a->gain / (double)(2 * a->size) ); - a->fircore->setNc(a->nc, impulse.data()); + a->fircore->setNc(impulse); } } diff --git a/wdsp/bandpass.cpp b/wdsp/bandpass.cpp index b9989cedc..e315fdf6b 100644 --- a/wdsp/bandpass.cpp +++ b/wdsp/bandpass.cpp @@ -77,7 +77,7 @@ BANDPASS::BANDPASS( 1, gain / (double)(2 * size) ); - fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); + fircore = new FIRCORE(size, in, out, mp, impulse); } BANDPASS::~BANDPASS() @@ -119,7 +119,7 @@ void BANDPASS::setSamplerate(int _rate) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void BANDPASS::setSize(int _size) @@ -139,7 +139,7 @@ void BANDPASS::setSize(int _size) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void BANDPASS::setGain(double _gain, int _update) @@ -156,7 +156,7 @@ void BANDPASS::setGain(double _gain, int _update) 1, gain / (double) (2 * size) ); - fircore->setImpulse(impulse.data(), _update); + fircore->setImpulse(impulse, _update); } void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) @@ -177,7 +177,7 @@ void BANDPASS::calcBandpassFilter(double _f_low, double _f_high, double _gain) 1, gain / (double)(2 * size) ); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } } @@ -203,7 +203,7 @@ void BANDPASS::setBandpassFreqs(double _f_low, double _f_high) gain / (double)(2 * size) ); - fircore->setImpulse(impulse.data(), 0); + fircore->setImpulse(impulse, 0); f_low = _f_low; f_high = _f_high; fircore->setUpdate(); @@ -227,7 +227,7 @@ void BANDPASS::SetBandpassNC(int _nc) 1, gain / (double)( 2 * size) ); - fircore->setNc(nc, impulse.data()); + fircore->setNc(impulse); } } diff --git a/wdsp/bpsnba.cpp b/wdsp/bpsnba.cpp index 5c28307e6..30b88f198 100644 --- a/wdsp/bpsnba.cpp +++ b/wdsp/bpsnba.cpp @@ -176,7 +176,7 @@ void BPSNBA::recalc_bpsnba_filter(int update) b->gain = gain; b->autoincr = autoincr; b->calc_impulse(); - b->fircore->setImpulse(b->impulse.data(), update); + b->fircore->setImpulse(b->impulse, update); } /******************************************************************************************************** diff --git a/wdsp/cfir.cpp b/wdsp/cfir.cpp index 676d25b1b..c67e5263f 100644 --- a/wdsp/cfir.cpp +++ b/wdsp/cfir.cpp @@ -37,7 +37,7 @@ void CFIR::calc() std::vector impulse; scale = 1.0 / (float)(2 * size); cfir_impulse (impulse, nc, DD, R, Pairs, runrate, cicrate, cutoff, xtype, xbw, 1, scale, wintype); - p = new FIRCORE(size, in, out, nc, mp, impulse.data()); + p = new FIRCORE(size, in, out, mp, impulse); } void CFIR::decalc() diff --git a/wdsp/emphp.cpp b/wdsp/emphp.cpp index cb3084ef5..a57e008f3 100644 --- a/wdsp/emphp.cpp +++ b/wdsp/emphp.cpp @@ -68,16 +68,16 @@ EMPHP::EMPHP( FCurve::fc_impulse ( impulse, nc, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); - p = new FIRCORE(size, in, out, nc, mp, impulse.data()); + p = new FIRCORE(size, in, out, mp, impulse); } EMPHP::~EMPHP() @@ -121,7 +121,7 @@ void EMPHP::setSamplerate(int _rate) 1.0 / (2.0 * size), 0, 0 ); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } void EMPHP::setSize(int _size) @@ -142,7 +142,7 @@ void EMPHP::setSize(int _size) 0, 0 ); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } /******************************************************************************************************** @@ -174,17 +174,17 @@ void EMPHP::setNC(int _nc) FCurve::fc_impulse ( impulse, nc, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); - p->setNc(nc, impulse.data()); + p->setNc(impulse); } } @@ -208,7 +208,7 @@ void EMPHP::setFreqs(double low, double high) 0, 0 ); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } } diff --git a/wdsp/eqp.cpp b/wdsp/eqp.cpp index 6237d707a..37138451e 100644 --- a/wdsp/eqp.cpp +++ b/wdsp/eqp.cpp @@ -244,7 +244,7 @@ EQP::EQP( wintype = _wintype; samplerate = (double) _samplerate; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); + fircore = new FIRCORE(size, in, out, mp, impulse); } EQP::~EQP() @@ -277,7 +277,7 @@ void EQP::setSamplerate(int rate) std::vector impulse; samplerate = rate; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void EQP::setSize(int _size) @@ -286,7 +286,7 @@ void EQP::setSize(int _size) size = _size; fircore->setSize(size); eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } /******************************************************************************************************** @@ -308,7 +308,7 @@ void EQP::setNC(int _nc) { nc = _nc; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setNc(nc, impulse.data()); + fircore->setNc(impulse); } } @@ -330,7 +330,7 @@ void EQP::setProfile(int _nfreqs, const float* _F, const float* _G) std::copy(_F, _F + (_nfreqs + 1), F.begin()); std::copy(_G, _G + (_nfreqs + 1), G.begin()); eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void EQP::setCtfmode(int _mode) @@ -338,7 +338,7 @@ void EQP::setCtfmode(int _mode) std::vector impulse; ctfmode = _mode; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void EQP::setWintype(int _wintype) @@ -346,7 +346,7 @@ void EQP::setWintype(int _wintype) std::vector impulse; wintype = _wintype; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void EQP::setGrphEQ(const int *rxeq) @@ -366,7 +366,7 @@ void EQP::setGrphEQ(const int *rxeq) G[4] = (float)rxeq[3]; ctfmode = 0; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void EQP::setGrphEQ10(const int *rxeq) @@ -389,7 +389,7 @@ void EQP::setGrphEQ10(const int *rxeq) G[i] = (float)rxeq[i]; ctfmode = 0; eq_impulse (impulse, nc, nfreqs, F.data(), G.data(), samplerate, 1.0 / (2.0 * size), ctfmode, wintype); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } } // namespace WDSP diff --git a/wdsp/fircore.cpp b/wdsp/fircore.cpp index 6c4be6a6e..8c673ddd4 100644 --- a/wdsp/fircore.cpp +++ b/wdsp/fircore.cpp @@ -99,7 +99,7 @@ void FIRCORE::calc(int _flip) if (mp) FIR::mp_imp (nc, impulse, imp, 16, 0); else - std::copy(impulse.begin(), impulse.begin() + nc * 2, imp.begin()); + std::copy(impulse.begin(), impulse.end(), imp.begin()); for (int i = 0; i < nfor; i++) { @@ -122,20 +122,19 @@ FIRCORE::FIRCORE( int _size, float* _in, float* _out, - int _nc, int _mp, - const float* _impulse + const std::vector& _impulse ) { size = _size; in = _in; out = _out; - nc = _nc; + nc = (int) (_impulse.size() / 2); mp = _mp; plan(); - impulse.resize(nc * 2); - imp.resize(nc * 2); - std::copy(_impulse, _impulse + nc * 2, impulse.begin()); + impulse.resize(_impulse.size()); + imp.resize(_impulse.size()); + std::copy(_impulse.begin(), _impulse.end(), impulse.begin()); calc(1); } @@ -204,21 +203,29 @@ void FIRCORE::setSize(int _size) calc(1); } -void FIRCORE::setImpulse(const float* _impulse, int _update) +void FIRCORE::setImpulse(const std::vector& _impulse, int _update) { - std::copy(_impulse, _impulse + nc * 2, impulse.begin()); - calc(_update); + auto imp_nc = (int) (_impulse.size() / 2); + + if (imp_nc == nc) // to be on the safe side but setNc would be called if impulse size changes + { + std::copy(_impulse.begin(), _impulse.end(), impulse.begin()); + calc(_update); + } + else{ + setNc(_impulse); + } } -void FIRCORE::setNc(int _nc, const float* _impulse) +void FIRCORE::setNc(const std::vector& _impulse) { // because of FFT planning, this will probably cause a glitch in audio if done during dataflow deplan(); - nc = _nc; + nc = (int) (_impulse.size() / 2); plan(); imp.resize(nc * 2); impulse.resize(nc * 2); - std::copy(_impulse, _impulse + nc * 2, impulse.begin()); + std::copy(_impulse.begin(), _impulse.end(), impulse.begin()); calc(1); } diff --git a/wdsp/fircore.hpp b/wdsp/fircore.hpp index 3fd309a4c..be6176636 100644 --- a/wdsp/fircore.hpp +++ b/wdsp/fircore.hpp @@ -70,9 +70,8 @@ public: int size, float* in, float* out, - int nc, int mp, - const float* impulse + const std::vector& impulse ); FIRCORE(const FIRCORE&) = delete; FIRCORE& operator=(const FIRCORE& other) = delete; @@ -82,8 +81,8 @@ public: void execute(); void setBuffers(float* in, float* out); void setSize(int size); - void setImpulse(const float* impulse, int update); - void setNc(int nc, const float* impulse); + void setImpulse(const std::vector& impulse, int update); + void setNc(const std::vector& impulse); void setMp(int mp); void setUpdate(); diff --git a/wdsp/fmd.cpp b/wdsp/fmd.cpp index 3f8719274..2868af737 100644 --- a/wdsp/fmd.cpp +++ b/wdsp/fmd.cpp @@ -159,11 +159,11 @@ FMD::FMD( 0, 0 ); - pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); + pde = new FIRCORE(size, audio.data(), out, mp_de, impulse); // audio filter std::vector impulseb; FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb.data()); + paud = new FIRCORE(size, out, out, mp_aud, impulseb); } FMD::~FMD() @@ -266,11 +266,11 @@ void FMD::setSamplerate(int _rate) 0, 0 ); - pde->setImpulse(impulse.data(), 1); + pde->setImpulse(impulse, 1); // audio filter std::vector impulseb; FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulseb.data(), 1); + paud->setImpulse(impulseb, 1); plim->setSamplerate((int) rate); } @@ -296,12 +296,12 @@ void FMD::setSize(int _size) 0, 0 ); - pde = new FIRCORE(size, audio.data(), out, nc_de, mp_de, impulse.data()); + pde = new FIRCORE(size, audio.data(), out, mp_de, impulse); // audio filter delete paud; std::vector impulseb; FIR::fir_bandpass(impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud = new FIRCORE(size, out, out, nc_aud, mp_aud, impulseb.data()); + paud = new FIRCORE(size, out, out, mp_aud, impulseb); plim->setSize(size); } @@ -348,7 +348,7 @@ void FMD::setNCde(int nc) 0, 0 ); - pde->setNc(nc_de, impulse.data()); + pde->setNc(impulse); } } @@ -368,7 +368,7 @@ void FMD::setNCaud(int nc) nc_aud = nc; std::vector impulse; FIR::fir_bandpass(impulse, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setNc(nc_aud, impulse.data()); + paud->setNc(impulse); } } @@ -421,11 +421,11 @@ void FMD::setAFFilter(double low, double high) 0, 0 ); - pde->setImpulse(impulse.data(), 1); + pde->setImpulse(impulse, 1); // audio filter std::vector impulseb; FIR::fir_bandpass (impulseb, nc_aud, 0.8 * f_low, 1.1 * f_high, rate, 0, 1, afgain / (2.0 * size)); - paud->setImpulse(impulseb.data(), 1); + paud->setImpulse(impulseb, 1); } } diff --git a/wdsp/fmmod.cpp b/wdsp/fmmod.cpp index fdbc206c1..334e95620 100644 --- a/wdsp/fmmod.cpp +++ b/wdsp/fmmod.cpp @@ -82,7 +82,7 @@ FMMOD::FMMOD( mp = _mp; calc(); FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p = new FIRCORE(size, out, out, nc, mp, impulse.data()); + p = new FIRCORE(size, out, out, mp, impulse); } FMMOD::~FMMOD() @@ -143,7 +143,7 @@ void FMMOD::setSamplerate(int _rate) samplerate = _rate; calc(); FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } void FMMOD::setSize(int _size) @@ -153,7 +153,7 @@ void FMMOD::setSize(int _size) calc(); p->setSize(size); FIR::fir_bandpass(impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } /******************************************************************************************************** @@ -167,7 +167,7 @@ void FMMOD::setDeviation(float _deviation) double _bp_fc = f_high + _deviation; std::vector impulse; FIR::fir_bandpass (impulse, nc, -_bp_fc, +_bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse.data(), 0); + p->setImpulse(impulse, 0); deviation = _deviation; // mod sphase = 0.0; @@ -197,7 +197,7 @@ void FMMOD::setNC(int _nc) { nc = _nc; FIR::fir_bandpass (impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setNc(nc, impulse.data()); + p->setNc(impulse); } } @@ -220,7 +220,7 @@ void FMMOD::setAFFreqs(float _low, float _high) f_high = _high; bp_fc = deviation + f_high; FIR::fir_bandpass (impulse, nc, -bp_fc, +bp_fc, samplerate, 0, 1, 1.0 / (2 * size)); - p->setImpulse(impulse.data(), 1); + p->setImpulse(impulse, 1); } } diff --git a/wdsp/fmsq.cpp b/wdsp/fmsq.cpp index 859800aff..a61c0700b 100644 --- a/wdsp/fmsq.cpp +++ b/wdsp/fmsq.cpp @@ -49,7 +49,7 @@ void FMSQ::calc() G[2] = 3.0; G[3] = (float) (+20.0 * log10(20000.0 / *pllpole)); EQP::eq_impulse (impulse, nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - p = new FIRCORE(size, trigger, noise.data(), nc, mp, impulse.data()); + p = new FIRCORE(size, trigger, noise.data(), mp, impulse); // noise averaging avm = exp(-1.0 / (rate * avtau)); onem_avm = 1.0 - avm; @@ -291,7 +291,7 @@ void FMSQ::setNC(int _nc) { nc = _nc; EQP::eq_impulse (impulse, nc, 3, F.data(), G.data(), rate, 1.0 / (2.0 * size), 0, 0); - p->setNc(nc, impulse.data()); + p->setNc(impulse); } } diff --git a/wdsp/icfir.cpp b/wdsp/icfir.cpp index e03551417..7b3f73f2c 100644 --- a/wdsp/icfir.cpp +++ b/wdsp/icfir.cpp @@ -37,7 +37,7 @@ void ICFIR::calc_icfir (ICFIR *a) std::vector impulse; a->scale = 1.0f / (float)(2 * a->size); icfir_impulse (impulse, a->nc, a->DD, a->R, a->Pairs, (float) a->runrate, (float) a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = new FIRCORE(a->size, a->in, a->out, a->nc, a->mp, impulse.data()); + a->p = new FIRCORE(a->size, a->in, a->out, a->mp, impulse); } void ICFIR::decalc_icfir (ICFIR *a) diff --git a/wdsp/nbp.cpp b/wdsp/nbp.cpp index b3c27e405..73a11f88e 100644 --- a/wdsp/nbp.cpp +++ b/wdsp/nbp.cpp @@ -333,7 +333,7 @@ void NBP::calc_lightweight() gain / (float)(2 * size), wintype ); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); // print_impulse ("nbp.txt", size + 1, impulse, 1, 0); } hadnotch = havnotch; @@ -438,7 +438,7 @@ NBP::NBP( bplow.resize(maxpb); bphigh.resize(maxpb); calc_impulse (); - fircore = new FIRCORE(size, in, out, nc, mp, impulse.data()); + fircore = new FIRCORE(size, in, out, mp, impulse); } NBP::~NBP() @@ -470,7 +470,7 @@ void NBP::setSamplerate(int _rate) { rate = _rate; calc_impulse (); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void NBP::setSize(int _size) @@ -479,13 +479,13 @@ void NBP::setSize(int _size) size = _size; fircore->setSize(size); calc_impulse (); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } void NBP::setNc() { calc_impulse(); - fircore->setNc(nc, impulse.data()); + fircore->setNc(impulse); } void NBP::setMp() @@ -513,7 +513,7 @@ void NBP::SetFreqs(double _flow, double _fhigh) flow = _flow; fhigh = _fhigh; calc_impulse(); - fircore->setImpulse(impulse.data(), 1); + fircore->setImpulse(impulse, 1); } } From eaa544570214f8a691f2df80712cdb783a961cf0 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 10 Aug 2024 23:46:47 +0200 Subject: [PATCH 44/46] WDSP: more Sonar fixes --- wdsp/emph.cpp | 36 ++++--- wdsp/emph.hpp | 4 +- wdsp/emphp.cpp | 32 +++---- wdsp/gen.cpp | 13 +-- wdsp/gen.hpp | 9 +- wdsp/icfir.cpp | 227 ++++++++++++++++++++++----------------------- wdsp/icfir.hpp | 25 ++--- wdsp/resamplef.cpp | 53 ++++++----- wdsp/snba.cpp | 16 ++-- wdsp/snba.hpp | 6 +- 10 files changed, 214 insertions(+), 207 deletions(-) diff --git a/wdsp/emph.cpp b/wdsp/emph.cpp index e8f99db14..a15718120 100644 --- a/wdsp/emph.cpp +++ b/wdsp/emph.cpp @@ -41,31 +41,41 @@ namespace WDSP { void EMPH::calc() { - infilt = new float[2 * size * 2]; - product = new float[2 * size * 2]; + infilt.resize(2 * size * 2); + product.resize(2 * size * 2); FCurve::fc_mults( mults, size, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); - CFor = fftwf_plan_dft_1d(2 * size, (fftwf_complex *)infilt, (fftwf_complex *)product, FFTW_FORWARD, FFTW_PATIENT); - CRev = fftwf_plan_dft_1d(2 * size, (fftwf_complex *)product, (fftwf_complex *)out, FFTW_BACKWARD, FFTW_PATIENT); + CFor = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)infilt.data(), + (fftwf_complex *)product.data(), + FFTW_FORWARD, + FFTW_PATIENT) + ; + CRev = fftwf_plan_dft_1d( + 2 * size, + (fftwf_complex *)product.data(), + (fftwf_complex *)out, + FFTW_BACKWARD, + FFTW_PATIENT + ); } void EMPH::decalc() { fftwf_destroy_plan(CRev); fftwf_destroy_plan(CFor); - delete[] product; - delete[] infilt; } EMPH::EMPH( @@ -99,7 +109,7 @@ EMPH::~EMPH() void EMPH::flush() { - std::fill(infilt, infilt + 2 * size * 2, 0); + std::fill(infilt.begin(), infilt.end(), 0); } void EMPH::execute(int _position) @@ -118,7 +128,7 @@ void EMPH::execute(int _position) product[2 * i + 1] = (float) (I * mults[2 * i + 1] + Q * mults[2 * i + 0]); } fftwf_execute (CRev); - std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt); + std::copy(&(infilt[2 * size]), &(infilt[2 * size]) + size * 2, infilt.begin()); } else if (in != out) std::copy( in, in + size * 2, out); diff --git a/wdsp/emph.hpp b/wdsp/emph.hpp index 8aa535ca6..996c8c5a3 100644 --- a/wdsp/emph.hpp +++ b/wdsp/emph.hpp @@ -52,8 +52,8 @@ public: int ctype; double f_low; double f_high; - float* infilt; - float* product; + std::vector infilt; + std::vector product; std::vector mults; double rate; fftwf_plan CFor; diff --git a/wdsp/emphp.cpp b/wdsp/emphp.cpp index a57e008f3..6c7affd13 100644 --- a/wdsp/emphp.cpp +++ b/wdsp/emphp.cpp @@ -82,7 +82,7 @@ EMPHP::EMPHP( EMPHP::~EMPHP() { - delete (p); + delete p; } void EMPHP::flush() @@ -112,13 +112,13 @@ void EMPHP::setSamplerate(int _rate) FCurve::fc_impulse ( impulse, nc, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); p->setImpulse(impulse, 1); @@ -132,13 +132,13 @@ void EMPHP::setSize(int _size) FCurve::fc_impulse ( impulse, nc, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); @@ -198,13 +198,13 @@ void EMPHP::setFreqs(double low, double high) FCurve::fc_impulse ( impulse, nc, - f_low, - f_high, - -20.0 * log10(f_high / f_low), + (float) f_low, + (float) f_high, + (float) (-20.0 * log10(f_high / f_low)), 0.0, ctype, - rate, - 1.0 / (2.0 * size), + (float) rate, + (float) (1.0 / (2.0 * size)), 0, 0 ); diff --git a/wdsp/gen.cpp b/wdsp/gen.cpp index d7029889a..a5379fd19 100644 --- a/wdsp/gen.cpp +++ b/wdsp/gen.cpp @@ -94,7 +94,7 @@ void GEN::calc_pulse () pulse.pcount = pulse.pnoff; pulse.state = PState::OFF; - pulse.ctrans = new double[pulse.pntrans + 1]; + pulse.ctrans.resize(pulse.pntrans + 1); delta = PI / (float)pulse.pntrans; theta = 0.0; for (int i = 0; i <= pulse.pntrans; i++) @@ -114,11 +114,6 @@ void GEN::calc() calc_pulse(); } -void GEN::decalc() -{ - delete[] (pulse.ctrans); -} - GEN::GEN( int _run, int _size, @@ -165,11 +160,6 @@ GEN::GEN( calc(); } -GEN::~GEN() -{ - decalc(); -} - void GEN::flush() { pulse.state = PState::OFF; @@ -365,7 +355,6 @@ void GEN::setBuffers(float* _in, float* _out) void GEN::setSamplerate(int _rate) { - decalc(); rate = _rate; calc(); } diff --git a/wdsp/gen.hpp b/wdsp/gen.hpp index 8c2ee9225..f1d09db0a 100644 --- a/wdsp/gen.hpp +++ b/wdsp/gen.hpp @@ -28,6 +28,8 @@ warren@wpratt.com #ifndef wdsp_gen_h #define wdsp_gen_h +#include + #include "export.h" namespace WDSP { @@ -120,7 +122,7 @@ public: double pf; double pdutycycle; double ptranstime; - double* ctrans; + std::vector ctrans; int pcount; int pnon; int pntrans; @@ -143,7 +145,9 @@ public: int rate, int mode ); - ~GEN(); + GEN(const GEN&) = delete; + GEN& operator=(const GEN& other) = delete; + ~GEN() = default; void flush(); void execute(); @@ -197,7 +201,6 @@ private: void calc_triangle(); void calc_pulse(); void calc(); - void decalc(); }; } // namespace WDSP diff --git a/wdsp/icfir.cpp b/wdsp/icfir.cpp index 7b3f73f2c..ef2cf7c94 100644 --- a/wdsp/icfir.cpp +++ b/wdsp/icfir.cpp @@ -25,6 +25,8 @@ warren@pratt.one */ +#include + #include "comm.hpp" #include "fircore.hpp" #include "fir.hpp" @@ -32,35 +34,35 @@ warren@pratt.one namespace WDSP { -void ICFIR::calc_icfir (ICFIR *a) +void ICFIR::calc() { std::vector impulse; - a->scale = 1.0f / (float)(2 * a->size); - icfir_impulse (impulse, a->nc, a->DD, a->R, a->Pairs, (float) a->runrate, (float) a->cicrate, a->cutoff, a->xtype, a->xbw, 1, a->scale, a->wintype); - a->p = new FIRCORE(a->size, a->in, a->out, a->mp, impulse); + scale = 1.0f / (float)(2 * size); + icfir_impulse (impulse, nc, DD, R, Pairs, (float) runrate, (float) cicrate, cutoff, xtype, xbw, 1, scale, wintype); + p = new FIRCORE(size, in, out, mp, impulse); } -void ICFIR::decalc_icfir (ICFIR *a) +void ICFIR::decalc() { - delete (a->p); + delete p; } -ICFIR* ICFIR::create_icfir ( - int run, - int size, - int nc, - int mp, - float* in, - float* out, - int runrate, - int cicrate, - int DD, - int R, - int Pairs, - float cutoff, - int xtype, - float xbw, - int wintype +ICFIR::ICFIR( + int _run, + int _size, + int _nc, + int _mp, + float* _in, + float* _out, + int _runrate, + int _cicrate, + int _DD, + int _R, + int _Pairs, + float _cutoff, + int _xtype, + float _xbw, + int _wintype ) // run: 0 - no action; 1 - operate // size: number of complex samples in an input buffer to the CFIR filter @@ -76,88 +78,85 @@ ICFIR* ICFIR::create_icfir ( // xtype: 0 - fourth power transition; 1 - raised cosine transition // xbw: width of raised cosine transition { - ICFIR *a = new ICFIR; - a->run = run; - a->size = size; - a->nc = nc; - a->mp = mp; - a->in = in; - a->out = out; - a->runrate = runrate; - a->cicrate = cicrate; - a->DD = DD; - a->R = R; - a->Pairs = Pairs; - a->cutoff = cutoff; - a->xtype = xtype; - a->xbw = xbw; - a->wintype = wintype; - calc_icfir (a); - return a; + run = _run; + size = _size; + nc = _nc; + mp = _mp; + in = _in; + out = _out; + runrate = _runrate; + cicrate = _cicrate; + DD = _DD; + R = _R; + Pairs = _Pairs; + cutoff = _cutoff; + xtype = _xtype; + xbw = _xbw; + wintype = _wintype; + calc(); } -void ICFIR::destroy_icfir (ICFIR *a) +ICFIR::~ICFIR() { - decalc_icfir (a); - delete[] (a); + decalc(); } -void ICFIR::flush_icfir (ICFIR *a) +void ICFIR::flush() { - a->p->flush(); + p->flush(); } -void ICFIR::xicfir (ICFIR *a) +void ICFIR::execute() { - if (a->run) - a->p->execute(); - else if (a->in != a->out) - std::copy( a->in, a->in + a->size * 2, a->out); + if (run) + p->execute(); + else if (in != out) + std::copy( in, in + size * 2, out); } -void ICFIR::setBuffers_icfir (ICFIR *a, float* in, float* out) +void ICFIR::setBuffers(float* _in, float* _out) { - decalc_icfir (a); - a->in = in; - a->out = out; - calc_icfir (a); + decalc(); + in = _in; + out = _out; + calc(); } -void ICFIR::setSamplerate_icfir (ICFIR *a, int rate) +void ICFIR::setSamplerate(int _rate) { - decalc_icfir (a); - a->runrate = rate; - calc_icfir (a); + decalc(); + runrate = _rate; + calc(); } -void ICFIR::setSize_icfir (ICFIR *a, int size) +void ICFIR::setSize(int _size) { - decalc_icfir (a); - a->size = size; - calc_icfir (a); + decalc(); + size = _size; + calc(); } -void ICFIR::setOutRate_icfir (ICFIR *a, int rate) +void ICFIR::setOutRate(int _rate) { - decalc_icfir (a); - a->cicrate = rate; - calc_icfir (a); + decalc(); + cicrate = _rate; + calc(); } void ICFIR::icfir_impulse ( - std::vector& impulse, - int N, - int DD, - int R, - int Pairs, - float runrate, - float cicrate, - float cutoff, - int xtype, - float xbw, - int rtype, - float scale, - int wintype + std::vector& _impulse, + int _N, + int _DD, + int _R, + int _Pairs, + float _runrate, + float _cicrate, + float _cutoff, + int _xtype, + float _xbw, + int _rtype, + float _scale, + int _wintype ) { // N: number of impulse response samples @@ -173,75 +172,73 @@ void ICFIR::icfir_impulse ( // scale: scale factor to be applied to the output int i; int j; - float tmp; - float local_scale; - float ri; - float mag; - float fn; - auto* A = new float[N]; - float ft = cutoff / cicrate; // normalized cutoff frequency - int u_samps = (N + 1) / 2; // number of unique samples, OK for odd or even N - int c_samps = (int)(cutoff / runrate * N) + (N + 1) / 2 - N / 2; // number of unique samples within bandpass, OK for odd or even N - auto x_samps = (int)(xbw / runrate * N); // number of unique samples in transition region, OK for odd or even N - float offset = 0.5f - 0.5f * (float)((N + 1) / 2 - N / 2); // sample offset from center, OK for odd or even N - auto* xistion = new float[x_samps + 1]; - float delta = PI / (float)x_samps; - float L = cicrate / runrate; - float phs = 0.0; + double tmp; + double local_scale; + double ri; + double mag = 0; + double fn; + std::vector A(_N); + double ft = _cutoff / _cicrate; // normalized cutoff frequency + int u_samps = (_N + 1) / 2; // number of unique samples, OK for odd or even N + int c_samps = (int)(_cutoff / _runrate * _N) + (_N + 1) / 2 - _N / 2; // number of unique samples within bandpass, OK for odd or even N + auto x_samps = (int)(_xbw / _runrate * _N); // number of unique samples in transition region, OK for odd or even N + double offset = 0.5f - 0.5f * (float)((_N + 1) / 2 - _N / 2); // sample offset from center, OK for odd or even N + std::vector xistion(x_samps + 1); + double delta = PI / (float)x_samps; + double L = _cicrate / _runrate; + double phs = 0.0; for (i = 0; i <= x_samps; i++) { xistion[i] = 0.5 * (cos (phs) + 1.0); phs += delta; } - if ((tmp = DD * R * sin (PI * ft / R) / sin (PI * DD * ft)) < 0.0) //normalize by peak gain + if ((tmp = _DD * _R * sin (PI * ft / _R) / sin (PI * _DD * ft)) < 0.0) //normalize by peak gain tmp = -tmp; - local_scale = scale / pow (tmp, Pairs); - if (xtype == 0) + local_scale = _scale / pow (tmp, _Pairs); + if (_xtype == 0) { for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0) { - fn = ri / (L * (float)N); + fn = ri / (L * (float)_N); if (fn <= ft) { if (fn == 0.0) tmp = 1.0; - else if ((tmp = sin (PI * DD * fn) / (DD * R * sin (PI * fn / R))) < 0.0) + else if ((tmp = sin (PI * _DD * fn) / (_DD * _R * sin (PI * fn / _R))) < 0.0) tmp = -tmp; - mag = pow (tmp, Pairs) * local_scale; + mag = pow (tmp, _Pairs) * local_scale; } else mag *= (ft * ft * ft * ft) / (fn * fn * fn * fn); - A[i] = mag; + A[i] = (float) mag; } } - else if (xtype == 1) + else if (_xtype == 1) { for (i = 0, ri = offset; i < u_samps; i++, ri += 1.0) { - fn = ri / (L *(float)N); + fn = ri / (L *(float)_N); if (i < c_samps) { if (fn == 0.0) tmp = 1.0; - else if ((tmp = sin (PI * DD * fn) / (DD * R * sin (PI * fn / R))) < 0.0) + else if ((tmp = sin (PI * _DD * fn) / (_DD * _R * sin (PI * fn / _R))) < 0.0) tmp = -tmp; - mag = pow (tmp, Pairs) * local_scale; - A[i] = mag; + mag = pow (tmp, _Pairs) * local_scale; + A[i] = (float) mag; } else if ( i >= c_samps && i <= c_samps + x_samps) - A[i] = mag * xistion[i - c_samps]; + A[i] = (float) (mag * xistion[i - c_samps]); else A[i] = 0.0; } } - if (N & 1) - for (i = u_samps, j = 2; i < N; i++, j++) + if (_N & 1) + for (i = u_samps, j = 2; i < _N; i++, j++) A[i] = A[u_samps - j]; else - for (i = u_samps, j = 1; i < N; i++, j++) + for (i = u_samps, j = 1; i < _N; i++, j++) A[i] = A[u_samps - j]; - impulse.resize(2 * N); - FIR::fir_fsamp (impulse, N, A, rtype, 1.0, wintype); - delete[] (A); - delete[] xistion; + _impulse.resize(2 * _N); + FIR::fir_fsamp (_impulse, _N, A.data(), _rtype, 1.0, _wintype); } diff --git a/wdsp/icfir.hpp b/wdsp/icfir.hpp index 1dd4f28c9..9497eeb6e 100644 --- a/wdsp/icfir.hpp +++ b/wdsp/icfir.hpp @@ -55,7 +55,7 @@ public: int wintype; FIRCORE *p; - static ICFIR* create_icfir ( + ICFIR( int run, int size, int nc, @@ -72,14 +72,17 @@ public: float xbw, int wintype ); - static void destroy_icfir (ICFIR *a); - static void flush_icfir (ICFIR *a); - static void xicfir (ICFIR *a); - static void setBuffers_icfir (ICFIR *a, float* in, float* out); - static void setSamplerate_icfir (ICFIR *a, int rate); - static void setSize_icfir (ICFIR *a, int size); - static void setOutRate_icfir (ICFIR *a, int rate); - static void icfir_impulse ( + ICFIR(const ICFIR&) = delete; + ICFIR& operator=(const ICFIR& other) = delete; + ~ICFIR(); + + void flush(); + void execute(); + void setBuffers(float* in, float* out); + void setSamplerate(int rate); + void setSize(int size); + void setOutRate(int rate); + static void icfir_impulse( std::vector& impulse, int N, int DD, @@ -96,8 +99,8 @@ public: ); private: - static void calc_icfir (ICFIR *a); - static void decalc_icfir (ICFIR *a); + void calc(); + void decalc(); }; } // namespace WDSP diff --git a/wdsp/resamplef.cpp b/wdsp/resamplef.cpp index 6f8e4fbba..de3dc9e9f 100644 --- a/wdsp/resamplef.cpp +++ b/wdsp/resamplef.cpp @@ -39,7 +39,14 @@ namespace WDSP { * * ************************************************************************************************/ -RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* out, int in_rate, int out_rate) +RESAMPLEF* RESAMPLEF::create_resampleF ( + int _run, + int _size, + float* _in, + float* _out, + int _in_rate, + int _out_rate +) { auto *a = new RESAMPLEF; int x; @@ -51,12 +58,12 @@ RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* ou float fc; float fc_norm; std::vector impulse; - a->run = run; - a->size = size; - a->in = in; - a->out = out; - x = in_rate; - y = out_rate; + a->run = _run; + a->size = _size; + a->in = _in; + a->out = _out; + x = _in_rate; + y = _out_rate; while (y != 0) { @@ -65,19 +72,19 @@ RESAMPLEF* RESAMPLEF::create_resampleF ( int run, int size, float* in, float* ou x = z; } - a->L = out_rate / x; - a->M = in_rate / x; + a->L = _out_rate / x; + a->M = _in_rate / x; a->L = a->L <= 0 ? 1 : a->L; a->M = a->M <= 0 ? 1 : a->M; - if (in_rate < out_rate) - min_rate = in_rate; + if (_in_rate < _out_rate) + min_rate = _in_rate; else - min_rate = out_rate; + min_rate = _out_rate; fc = 0.45f * (float)min_rate; - full_rate = (float)(in_rate * a->L); + full_rate = (float)(_in_rate * a->L); fc_norm = fc / full_rate; a->ncoef = (int)(60.0 / fc_norm); a->ncoef = (a->ncoef / a->L + 1) * a->L; @@ -164,25 +171,25 @@ int RESAMPLEF::xresampleF (RESAMPLEF *a) // Exported calls -void* RESAMPLEF::create_resampleFV (int in_rate, int out_rate) +void* RESAMPLEF::create_resampleFV (int _in_rate, int _out_rate) { - return (void *) create_resampleF (1, 0, nullptr, nullptr, in_rate, out_rate); + return (void *) create_resampleF (1, 0, nullptr, nullptr, _in_rate, _out_rate); } -void RESAMPLEF::xresampleFV (float* input, float* output, int numsamps, int* outsamps, void* ptr) +void RESAMPLEF::xresampleFV (float* _input, float* _output, int _numsamps, int* _outsamps, void* _ptr) { - auto *a = (RESAMPLEF*) ptr; - a->in = input; - a->out = output; - a->size = numsamps; - *outsamps = xresampleF(a); + auto *a = (RESAMPLEF*) _ptr; + a->in = _input; + a->out = _output; + a->size = _numsamps; + *_outsamps = xresampleF(a); } -void RESAMPLEF::destroy_resampleFV (void* ptr) +void RESAMPLEF::destroy_resampleFV (void* _ptr) { - destroy_resampleF ( (RESAMPLEF*) ptr ); + destroy_resampleF ( (RESAMPLEF*) _ptr ); } } // namespace WDSP diff --git a/wdsp/snba.cpp b/wdsp/snba.cpp index c212aa3ca..dcd103031 100644 --- a/wdsp/snba.cpp +++ b/wdsp/snba.cpp @@ -114,8 +114,8 @@ void SNBA::calc() else isize = bsize * (internalrate / inrate); - inbuff = new float[isize * 2]; - outbuff = new float[isize * 2]; + inbuff.resize(isize * 2); + outbuff.resize(isize * 2); if (inrate != internalrate) resamprun = 1; @@ -126,7 +126,7 @@ void SNBA::calc() resamprun, bsize, in, - inbuff, + inbuff.data(), inrate, internalrate, 0.0, @@ -137,7 +137,7 @@ void SNBA::calc() outresamp = new RESAMPLE( resamprun, isize, - outbuff, + outbuff.data(), out, internalrate, inrate, @@ -217,8 +217,6 @@ SNBA::SNBA( isize(0), inresamp(nullptr), outresamp(nullptr), - inbuff(nullptr), - outbuff(nullptr), out_low_cut(_out_low_cut), out_high_cut(_out_high_cut), exec(_xsize, _asize, _npasses), @@ -237,8 +235,6 @@ void SNBA::decalc() { delete outresamp; delete inresamp; - delete[] outbuff; - delete[] inbuff; } SNBA::~SNBA() @@ -259,8 +255,8 @@ void SNBA::flush() std::fill(inaccum.begin(), inaccum.end(), 0); std::fill(outaccum.begin(), outaccum.end(), 0); std::fill(xaux, xaux + xsize, 0); - std::fill(inbuff, inbuff + isize * 2, 0); - std::fill(outbuff, outbuff + isize * 2, 0); + std::fill(inbuff.begin(), inbuff.end(), 0); + std::fill(outbuff.begin(), outbuff.end(), 0); inresamp->flush(); outresamp->flush(); diff --git a/wdsp/snba.hpp b/wdsp/snba.hpp index ab12d871c..1ab8718b1 100644 --- a/wdsp/snba.hpp +++ b/wdsp/snba.hpp @@ -64,8 +64,8 @@ public: int isize; RESAMPLE *inresamp; RESAMPLE *outresamp; - float* inbuff; - float* outbuff; + std::vector inbuff; + std::vector outbuff; double out_low_cut; double out_high_cut; static const int MAXIMP = 256; @@ -155,6 +155,8 @@ public: double out_low_cut, double out_high_cut ); + SNBA(const SNBA&) = delete; + SNBA& operator=(const SNBA& other) = delete; ~SNBA(); void flush(); From 11f91b577b37e8cfa6a4c369cee947891a510ee6 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 12 Aug 2024 10:33:53 +0200 Subject: [PATCH 45/46] Added new color maps. Part of #2191 --- sdrbase/util/colormap.cpp | 789 ++++++++++++++++++++++++++++++++++++++ sdrbase/util/colormap.h | 3 + 2 files changed, 792 insertions(+) diff --git a/sdrbase/util/colormap.cpp b/sdrbase/util/colormap.cpp index 9075691aa..e2678c648 100644 --- a/sdrbase/util/colormap.cpp +++ b/sdrbase/util/colormap.cpp @@ -69,6 +69,9 @@ QHash ColorMap::m_colorMaps{ {"Shrimp", &m_shrimp[0]}, {"Cubehelix", &m_cubehelix[0]}, {"Cubegamma", &m_cubegamma[0]}, + {"Cubehlx2", &m_cubehlx2[0]}, + {"Icy", &m_icy[0]}, + {"Mint", &m_mint[0]}, }; const float ColorMap::m_angel[m_size] = @@ -6313,3 +6316,789 @@ const float ColorMap::m_cubegamma[m_size] = 1.000, 0.998, 0.999, 1.000, 1.000, 1.000, }; + +// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html +// Using: Start color: 1.5, Number of rotations: -1.0, Rotation direction: negative, Hue: 1.0, Gamma: 0.7 +const float ColorMap::m_cubehlx2[m_size] = +{ + 0.000, 0.000, 0.000, + 0.023, 0.023, 0.001, + 0.037, 0.038, 0.002, + 0.051, 0.049, 0.003, + 0.063, 0.060, 0.004, + 0.075, 0.069, 0.005, + 0.086, 0.078, 0.007, + 0.098, 0.086, 0.009, + 0.109, 0.093, 0.010, + 0.120, 0.100, 0.013, + 0.131, 0.107, 0.015, + 0.141, 0.113, 0.017, + 0.152, 0.119, 0.020, + 0.163, 0.124, 0.022, + 0.173, 0.129, 0.025, + 0.184, 0.134, 0.028, + 0.195, 0.139, 0.032, + 0.205, 0.144, 0.035, + 0.216, 0.148, 0.039, + 0.226, 0.152, 0.043, + 0.237, 0.156, 0.047, + 0.247, 0.160, 0.051, + 0.257, 0.164, 0.055, + 0.267, 0.167, 0.060, + 0.278, 0.171, 0.065, + 0.288, 0.174, 0.070, + 0.298, 0.177, 0.075, + 0.308, 0.181, 0.080, + 0.318, 0.184, 0.085, + 0.328, 0.186, 0.091, + 0.337, 0.189, 0.097, + 0.347, 0.192, 0.103, + 0.357, 0.195, 0.109, + 0.366, 0.197, 0.116, + 0.375, 0.200, 0.122, + 0.385, 0.202, 0.129, + 0.394, 0.205, 0.136, + 0.403, 0.207, 0.143, + 0.412, 0.210, 0.150, + 0.421, 0.212, 0.158, + 0.429, 0.214, 0.165, + 0.438, 0.217, 0.173, + 0.446, 0.219, 0.181, + 0.454, 0.221, 0.189, + 0.462, 0.224, 0.197, + 0.470, 0.226, 0.205, + 0.478, 0.228, 0.214, + 0.486, 0.230, 0.222, + 0.493, 0.233, 0.231, + 0.501, 0.235, 0.239, + 0.508, 0.237, 0.248, + 0.515, 0.240, 0.257, + 0.522, 0.242, 0.266, + 0.528, 0.244, 0.276, + 0.535, 0.247, 0.285, + 0.541, 0.249, 0.294, + 0.547, 0.252, 0.304, + 0.553, 0.254, 0.313, + 0.559, 0.257, 0.323, + 0.565, 0.259, 0.332, + 0.570, 0.262, 0.342, + 0.575, 0.265, 0.352, + 0.580, 0.267, 0.362, + 0.585, 0.270, 0.372, + 0.590, 0.273, 0.381, + 0.594, 0.276, 0.391, + 0.599, 0.279, 0.401, + 0.603, 0.282, 0.411, + 0.607, 0.285, 0.421, + 0.610, 0.288, 0.431, + 0.614, 0.291, 0.441, + 0.617, 0.295, 0.451, + 0.621, 0.298, 0.461, + 0.624, 0.301, 0.471, + 0.626, 0.305, 0.481, + 0.629, 0.308, 0.491, + 0.632, 0.312, 0.500, + 0.634, 0.316, 0.510, + 0.636, 0.319, 0.520, + 0.638, 0.323, 0.530, + 0.640, 0.327, 0.539, + 0.641, 0.331, 0.549, + 0.643, 0.335, 0.558, + 0.644, 0.339, 0.568, + 0.645, 0.343, 0.577, + 0.646, 0.348, 0.586, + 0.647, 0.352, 0.595, + 0.648, 0.356, 0.604, + 0.648, 0.361, 0.613, + 0.649, 0.365, 0.622, + 0.649, 0.370, 0.631, + 0.649, 0.375, 0.639, + 0.649, 0.380, 0.648, + 0.649, 0.384, 0.656, + 0.648, 0.389, 0.665, + 0.648, 0.394, 0.673, + 0.648, 0.399, 0.681, + 0.647, 0.404, 0.688, + 0.646, 0.409, 0.696, + 0.645, 0.415, 0.704, + 0.644, 0.420, 0.711, + 0.643, 0.425, 0.718, + 0.642, 0.431, 0.726, + 0.641, 0.436, 0.732, + 0.640, 0.442, 0.739, + 0.638, 0.447, 0.746, + 0.637, 0.453, 0.752, + 0.635, 0.458, 0.759, + 0.634, 0.464, 0.765, + 0.632, 0.470, 0.771, + 0.630, 0.476, 0.776, + 0.629, 0.481, 0.782, + 0.627, 0.487, 0.787, + 0.625, 0.493, 0.793, + 0.623, 0.499, 0.798, + 0.621, 0.505, 0.803, + 0.620, 0.511, 0.807, + 0.618, 0.517, 0.812, + 0.616, 0.523, 0.816, + 0.614, 0.529, 0.821, + 0.612, 0.535, 0.825, + 0.610, 0.541, 0.828, + 0.608, 0.547, 0.832, + 0.606, 0.553, 0.836, + 0.604, 0.560, 0.839, + 0.602, 0.566, 0.842, + 0.601, 0.572, 0.845, + 0.599, 0.578, 0.848, + 0.597, 0.584, 0.850, + 0.595, 0.590, 0.853, + 0.594, 0.596, 0.855, + 0.592, 0.602, 0.857, + 0.591, 0.609, 0.859, + 0.589, 0.615, 0.861, + 0.588, 0.621, 0.862, + 0.586, 0.627, 0.864, + 0.585, 0.633, 0.865, + 0.584, 0.639, 0.866, + 0.582, 0.645, 0.867, + 0.581, 0.651, 0.868, + 0.580, 0.657, 0.869, + 0.579, 0.663, 0.870, + 0.579, 0.669, 0.870, + 0.578, 0.674, 0.870, + 0.577, 0.680, 0.871, + 0.577, 0.686, 0.871, + 0.576, 0.692, 0.871, + 0.576, 0.697, 0.870, + 0.576, 0.703, 0.870, + 0.576, 0.709, 0.870, + 0.576, 0.714, 0.869, + 0.576, 0.720, 0.869, + 0.576, 0.725, 0.868, + 0.577, 0.731, 0.867, + 0.577, 0.736, 0.866, + 0.578, 0.741, 0.865, + 0.578, 0.746, 0.864, + 0.579, 0.751, 0.863, + 0.580, 0.757, 0.862, + 0.581, 0.762, 0.861, + 0.583, 0.767, 0.860, + 0.584, 0.771, 0.858, + 0.586, 0.776, 0.857, + 0.587, 0.781, 0.855, + 0.589, 0.786, 0.854, + 0.591, 0.790, 0.852, + 0.593, 0.795, 0.851, + 0.595, 0.799, 0.849, + 0.597, 0.804, 0.848, + 0.600, 0.808, 0.846, + 0.602, 0.812, 0.845, + 0.605, 0.817, 0.843, + 0.608, 0.821, 0.841, + 0.611, 0.825, 0.840, + 0.614, 0.829, 0.838, + 0.617, 0.832, 0.837, + 0.620, 0.836, 0.835, + 0.624, 0.840, 0.834, + 0.627, 0.844, 0.832, + 0.631, 0.847, 0.831, + 0.635, 0.851, 0.829, + 0.639, 0.854, 0.828, + 0.643, 0.858, 0.827, + 0.647, 0.861, 0.825, + 0.651, 0.864, 0.824, + 0.655, 0.867, 0.823, + 0.660, 0.870, 0.822, + 0.664, 0.873, 0.821, + 0.669, 0.876, 0.820, + 0.674, 0.879, 0.819, + 0.679, 0.882, 0.818, + 0.683, 0.885, 0.818, + 0.688, 0.887, 0.817, + 0.693, 0.890, 0.817, + 0.699, 0.892, 0.816, + 0.704, 0.895, 0.816, + 0.709, 0.897, 0.816, + 0.714, 0.899, 0.816, + 0.720, 0.902, 0.815, + 0.725, 0.904, 0.816, + 0.731, 0.906, 0.816, + 0.736, 0.908, 0.816, + 0.742, 0.910, 0.816, + 0.748, 0.912, 0.817, + 0.753, 0.914, 0.818, + 0.759, 0.916, 0.818, + 0.765, 0.918, 0.819, + 0.771, 0.920, 0.820, + 0.776, 0.922, 0.821, + 0.782, 0.923, 0.823, + 0.788, 0.925, 0.824, + 0.794, 0.927, 0.826, + 0.800, 0.928, 0.827, + 0.806, 0.930, 0.829, + 0.811, 0.932, 0.831, + 0.817, 0.933, 0.833, + 0.823, 0.935, 0.835, + 0.829, 0.936, 0.837, + 0.835, 0.938, 0.840, + 0.840, 0.939, 0.842, + 0.846, 0.941, 0.845, + 0.852, 0.942, 0.848, + 0.857, 0.944, 0.851, + 0.863, 0.945, 0.854, + 0.869, 0.946, 0.857, + 0.874, 0.948, 0.860, + 0.880, 0.949, 0.863, + 0.885, 0.951, 0.867, + 0.890, 0.952, 0.871, + 0.896, 0.954, 0.874, + 0.901, 0.955, 0.878, + 0.906, 0.956, 0.882, + 0.911, 0.958, 0.886, + 0.916, 0.959, 0.890, + 0.921, 0.961, 0.894, + 0.926, 0.962, 0.899, + 0.930, 0.964, 0.903, + 0.935, 0.966, 0.908, + 0.940, 0.967, 0.912, + 0.944, 0.969, 0.917, + 0.948, 0.970, 0.922, + 0.953, 0.972, 0.927, + 0.957, 0.974, 0.932, + 0.961, 0.976, 0.937, + 0.965, 0.977, 0.942, + 0.968, 0.979, 0.947, + 0.972, 0.981, 0.952, + 0.976, 0.983, 0.957, + 0.979, 0.985, 0.962, + 0.983, 0.987, 0.968, + 0.986, 0.989, 0.973, + 0.989, 0.991, 0.978, + 0.992, 0.993, 0.984, + 0.995, 0.995, 0.989, + 0.997, 0.998, 0.995, + 1.000, 1.000, 1.000, +}; + +// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html +// Using: Start color: 3.0, Number of rotations: -0.5, Rotation direction: negative, Hue: 1.0, Gamma: 0.7 +const float ColorMap::m_icy[m_size] = +{ + 0.000, 0.000, 0.000, + 0.019, 0.018, 0.041, + 0.030, 0.029, 0.066, + 0.040, 0.039, 0.087, + 0.048, 0.048, 0.105, + 0.056, 0.057, 0.123, + 0.063, 0.065, 0.139, + 0.070, 0.073, 0.154, + 0.076, 0.080, 0.168, + 0.081, 0.088, 0.182, + 0.087, 0.095, 0.195, + 0.092, 0.103, 0.207, + 0.096, 0.110, 0.219, + 0.101, 0.117, 0.231, + 0.105, 0.124, 0.242, + 0.110, 0.130, 0.253, + 0.113, 0.137, 0.263, + 0.117, 0.144, 0.273, + 0.121, 0.151, 0.283, + 0.124, 0.157, 0.293, + 0.128, 0.164, 0.302, + 0.131, 0.171, 0.311, + 0.134, 0.177, 0.320, + 0.137, 0.184, 0.329, + 0.140, 0.190, 0.337, + 0.143, 0.196, 0.345, + 0.146, 0.203, 0.353, + 0.148, 0.209, 0.361, + 0.151, 0.216, 0.369, + 0.153, 0.222, 0.376, + 0.156, 0.228, 0.383, + 0.158, 0.235, 0.390, + 0.160, 0.241, 0.397, + 0.162, 0.247, 0.404, + 0.165, 0.253, 0.410, + 0.167, 0.260, 0.417, + 0.169, 0.266, 0.423, + 0.171, 0.272, 0.429, + 0.173, 0.278, 0.435, + 0.175, 0.284, 0.441, + 0.177, 0.290, 0.446, + 0.179, 0.297, 0.452, + 0.180, 0.303, 0.457, + 0.182, 0.309, 0.462, + 0.184, 0.315, 0.467, + 0.186, 0.321, 0.472, + 0.188, 0.327, 0.477, + 0.189, 0.333, 0.481, + 0.191, 0.339, 0.486, + 0.193, 0.345, 0.490, + 0.194, 0.351, 0.495, + 0.196, 0.357, 0.499, + 0.198, 0.362, 0.503, + 0.200, 0.368, 0.507, + 0.201, 0.374, 0.511, + 0.203, 0.380, 0.515, + 0.205, 0.386, 0.518, + 0.206, 0.392, 0.522, + 0.208, 0.397, 0.525, + 0.210, 0.403, 0.529, + 0.212, 0.409, 0.532, + 0.213, 0.415, 0.535, + 0.215, 0.420, 0.538, + 0.217, 0.426, 0.541, + 0.219, 0.431, 0.544, + 0.220, 0.437, 0.547, + 0.222, 0.443, 0.549, + 0.224, 0.448, 0.552, + 0.226, 0.454, 0.554, + 0.228, 0.459, 0.557, + 0.230, 0.465, 0.559, + 0.232, 0.470, 0.561, + 0.234, 0.475, 0.564, + 0.236, 0.481, 0.566, + 0.238, 0.486, 0.568, + 0.240, 0.491, 0.570, + 0.242, 0.497, 0.572, + 0.244, 0.502, 0.574, + 0.246, 0.507, 0.575, + 0.248, 0.512, 0.577, + 0.251, 0.518, 0.579, + 0.253, 0.523, 0.580, + 0.255, 0.528, 0.582, + 0.257, 0.533, 0.583, + 0.260, 0.538, 0.585, + 0.262, 0.543, 0.586, + 0.265, 0.548, 0.587, + 0.267, 0.553, 0.589, + 0.270, 0.558, 0.590, + 0.272, 0.563, 0.591, + 0.275, 0.567, 0.592, + 0.278, 0.572, 0.593, + 0.280, 0.577, 0.594, + 0.283, 0.582, 0.595, + 0.286, 0.586, 0.596, + 0.289, 0.591, 0.597, + 0.291, 0.596, 0.598, + 0.294, 0.600, 0.599, + 0.297, 0.605, 0.600, + 0.300, 0.609, 0.600, + 0.303, 0.614, 0.601, + 0.306, 0.618, 0.602, + 0.309, 0.623, 0.603, + 0.313, 0.627, 0.603, + 0.316, 0.631, 0.604, + 0.319, 0.636, 0.604, + 0.322, 0.640, 0.605, + 0.326, 0.644, 0.606, + 0.329, 0.648, 0.606, + 0.333, 0.653, 0.607, + 0.336, 0.657, 0.607, + 0.340, 0.661, 0.608, + 0.343, 0.665, 0.608, + 0.347, 0.669, 0.609, + 0.351, 0.673, 0.609, + 0.354, 0.677, 0.610, + 0.358, 0.681, 0.610, + 0.362, 0.685, 0.611, + 0.366, 0.688, 0.611, + 0.370, 0.692, 0.612, + 0.374, 0.696, 0.612, + 0.378, 0.700, 0.612, + 0.382, 0.703, 0.613, + 0.386, 0.707, 0.613, + 0.390, 0.711, 0.614, + 0.394, 0.714, 0.614, + 0.398, 0.718, 0.615, + 0.402, 0.721, 0.615, + 0.407, 0.725, 0.616, + 0.411, 0.728, 0.616, + 0.415, 0.731, 0.617, + 0.420, 0.735, 0.617, + 0.424, 0.738, 0.618, + 0.429, 0.741, 0.619, + 0.433, 0.744, 0.619, + 0.438, 0.748, 0.620, + 0.443, 0.751, 0.620, + 0.447, 0.754, 0.621, + 0.452, 0.757, 0.622, + 0.457, 0.760, 0.622, + 0.461, 0.763, 0.623, + 0.466, 0.766, 0.624, + 0.471, 0.769, 0.625, + 0.476, 0.772, 0.625, + 0.481, 0.775, 0.626, + 0.486, 0.778, 0.627, + 0.491, 0.781, 0.628, + 0.496, 0.783, 0.629, + 0.501, 0.786, 0.630, + 0.506, 0.789, 0.631, + 0.511, 0.792, 0.632, + 0.516, 0.794, 0.633, + 0.521, 0.797, 0.634, + 0.526, 0.799, 0.635, + 0.531, 0.802, 0.636, + 0.536, 0.805, 0.638, + 0.542, 0.807, 0.639, + 0.547, 0.810, 0.640, + 0.552, 0.812, 0.642, + 0.557, 0.815, 0.643, + 0.563, 0.817, 0.644, + 0.568, 0.819, 0.646, + 0.573, 0.822, 0.647, + 0.578, 0.824, 0.649, + 0.584, 0.826, 0.651, + 0.589, 0.829, 0.652, + 0.595, 0.831, 0.654, + 0.600, 0.833, 0.656, + 0.605, 0.835, 0.657, + 0.611, 0.837, 0.659, + 0.616, 0.840, 0.661, + 0.622, 0.842, 0.663, + 0.627, 0.844, 0.665, + 0.632, 0.846, 0.667, + 0.638, 0.848, 0.669, + 0.643, 0.850, 0.671, + 0.649, 0.852, 0.674, + 0.654, 0.854, 0.676, + 0.660, 0.856, 0.678, + 0.665, 0.858, 0.680, + 0.671, 0.860, 0.683, + 0.676, 0.862, 0.685, + 0.681, 0.864, 0.688, + 0.687, 0.866, 0.690, + 0.692, 0.868, 0.693, + 0.698, 0.869, 0.696, + 0.703, 0.871, 0.698, + 0.709, 0.873, 0.701, + 0.714, 0.875, 0.704, + 0.719, 0.877, 0.707, + 0.725, 0.879, 0.710, + 0.730, 0.880, 0.713, + 0.735, 0.882, 0.716, + 0.741, 0.884, 0.719, + 0.746, 0.886, 0.722, + 0.751, 0.887, 0.725, + 0.757, 0.889, 0.729, + 0.762, 0.891, 0.732, + 0.767, 0.893, 0.735, + 0.772, 0.894, 0.739, + 0.778, 0.896, 0.742, + 0.783, 0.898, 0.746, + 0.788, 0.900, 0.749, + 0.793, 0.901, 0.753, + 0.798, 0.903, 0.757, + 0.803, 0.905, 0.760, + 0.808, 0.906, 0.764, + 0.813, 0.908, 0.768, + 0.818, 0.910, 0.772, + 0.823, 0.911, 0.776, + 0.828, 0.913, 0.780, + 0.833, 0.915, 0.784, + 0.838, 0.917, 0.788, + 0.842, 0.918, 0.792, + 0.847, 0.920, 0.796, + 0.852, 0.922, 0.801, + 0.857, 0.923, 0.805, + 0.861, 0.925, 0.809, + 0.866, 0.927, 0.814, + 0.870, 0.929, 0.818, + 0.875, 0.930, 0.822, + 0.879, 0.932, 0.827, + 0.884, 0.934, 0.832, + 0.888, 0.936, 0.836, + 0.892, 0.937, 0.841, + 0.897, 0.939, 0.845, + 0.901, 0.941, 0.850, + 0.905, 0.943, 0.855, + 0.909, 0.945, 0.860, + 0.913, 0.946, 0.865, + 0.917, 0.948, 0.869, + 0.921, 0.950, 0.874, + 0.925, 0.952, 0.879, + 0.929, 0.954, 0.884, + 0.933, 0.956, 0.889, + 0.937, 0.958, 0.894, + 0.941, 0.960, 0.899, + 0.944, 0.962, 0.904, + 0.948, 0.964, 0.910, + 0.951, 0.965, 0.915, + 0.955, 0.967, 0.920, + 0.958, 0.970, 0.925, + 0.962, 0.972, 0.930, + 0.965, 0.974, 0.936, + 0.968, 0.976, 0.941, + 0.971, 0.978, 0.946, + 0.975, 0.980, 0.951, + 0.978, 0.982, 0.957, + 0.981, 0.984, 0.962, + 0.984, 0.986, 0.967, + 0.986, 0.989, 0.973, + 0.989, 0.991, 0.978, + 0.992, 0.993, 0.984, + 0.995, 0.995, 0.989, + 0.997, 0.998, 0.995, + 1.000, 1.000, 1.000, +}; + +// Generated with https://people.phy.cam.ac.uk/dag9/CUBEHELIX/cubetry.html +// Using: Start color: 2.75, Number of rotations: -0.5, Rotation direction: negative, Hue: 1.0, Gamma: 0.7 +const float ColorMap::m_mint[m_size] = +{ + 0.000, 0.000, 0.000, + 0.010, 0.023, 0.038, + 0.016, 0.037, 0.061, + 0.022, 0.050, 0.080, + 0.026, 0.061, 0.097, + 0.031, 0.071, 0.113, + 0.035, 0.081, 0.127, + 0.038, 0.091, 0.141, + 0.042, 0.100, 0.153, + 0.045, 0.109, 0.165, + 0.048, 0.118, 0.177, + 0.051, 0.127, 0.188, + 0.054, 0.135, 0.198, + 0.057, 0.143, 0.208, + 0.060, 0.151, 0.217, + 0.062, 0.159, 0.227, + 0.065, 0.167, 0.235, + 0.067, 0.175, 0.244, + 0.069, 0.183, 0.252, + 0.072, 0.190, 0.260, + 0.074, 0.198, 0.267, + 0.076, 0.205, 0.275, + 0.078, 0.213, 0.282, + 0.081, 0.220, 0.289, + 0.083, 0.227, 0.295, + 0.085, 0.234, 0.302, + 0.087, 0.241, 0.308, + 0.089, 0.248, 0.314, + 0.091, 0.255, 0.320, + 0.093, 0.262, 0.325, + 0.095, 0.269, 0.331, + 0.097, 0.276, 0.336, + 0.099, 0.282, 0.341, + 0.101, 0.289, 0.346, + 0.103, 0.296, 0.351, + 0.105, 0.302, 0.356, + 0.107, 0.309, 0.360, + 0.109, 0.315, 0.364, + 0.111, 0.322, 0.369, + 0.113, 0.328, 0.373, + 0.115, 0.335, 0.377, + 0.117, 0.341, 0.380, + 0.119, 0.347, 0.384, + 0.121, 0.354, 0.388, + 0.124, 0.360, 0.391, + 0.126, 0.366, 0.394, + 0.128, 0.372, 0.398, + 0.130, 0.378, 0.401, + 0.132, 0.384, 0.404, + 0.135, 0.390, 0.407, + 0.137, 0.396, 0.409, + 0.139, 0.402, 0.412, + 0.141, 0.408, 0.415, + 0.144, 0.413, 0.417, + 0.146, 0.419, 0.420, + 0.149, 0.425, 0.422, + 0.151, 0.431, 0.424, + 0.154, 0.436, 0.426, + 0.156, 0.442, 0.428, + 0.159, 0.447, 0.430, + 0.162, 0.453, 0.432, + 0.164, 0.458, 0.434, + 0.167, 0.464, 0.436, + 0.170, 0.469, 0.438, + 0.172, 0.474, 0.439, + 0.175, 0.480, 0.441, + 0.178, 0.485, 0.443, + 0.181, 0.490, 0.444, + 0.184, 0.495, 0.446, + 0.187, 0.500, 0.447, + 0.190, 0.505, 0.448, + 0.193, 0.510, 0.450, + 0.196, 0.515, 0.451, + 0.200, 0.520, 0.452, + 0.203, 0.525, 0.453, + 0.206, 0.530, 0.454, + 0.210, 0.535, 0.455, + 0.213, 0.540, 0.456, + 0.216, 0.544, 0.457, + 0.220, 0.549, 0.458, + 0.223, 0.554, 0.459, + 0.227, 0.558, 0.460, + 0.231, 0.563, 0.461, + 0.234, 0.567, 0.462, + 0.238, 0.572, 0.463, + 0.242, 0.576, 0.463, + 0.246, 0.581, 0.464, + 0.249, 0.585, 0.465, + 0.253, 0.589, 0.466, + 0.257, 0.593, 0.466, + 0.261, 0.598, 0.467, + 0.265, 0.602, 0.468, + 0.270, 0.606, 0.469, + 0.274, 0.610, 0.469, + 0.278, 0.614, 0.470, + 0.282, 0.618, 0.471, + 0.286, 0.622, 0.471, + 0.291, 0.626, 0.472, + 0.295, 0.630, 0.473, + 0.300, 0.633, 0.473, + 0.304, 0.637, 0.474, + 0.308, 0.641, 0.475, + 0.313, 0.645, 0.475, + 0.318, 0.648, 0.476, + 0.322, 0.652, 0.477, + 0.327, 0.655, 0.478, + 0.332, 0.659, 0.478, + 0.336, 0.663, 0.479, + 0.341, 0.666, 0.480, + 0.346, 0.669, 0.480, + 0.351, 0.673, 0.481, + 0.356, 0.676, 0.482, + 0.361, 0.679, 0.483, + 0.366, 0.683, 0.484, + 0.371, 0.686, 0.485, + 0.376, 0.689, 0.485, + 0.381, 0.692, 0.486, + 0.386, 0.695, 0.487, + 0.391, 0.698, 0.488, + 0.396, 0.701, 0.489, + 0.402, 0.704, 0.490, + 0.407, 0.707, 0.491, + 0.412, 0.710, 0.492, + 0.417, 0.713, 0.493, + 0.423, 0.716, 0.495, + 0.428, 0.719, 0.496, + 0.434, 0.722, 0.497, + 0.439, 0.724, 0.498, + 0.444, 0.727, 0.499, + 0.450, 0.730, 0.501, + 0.455, 0.732, 0.502, + 0.461, 0.735, 0.504, + 0.466, 0.738, 0.505, + 0.472, 0.740, 0.506, + 0.478, 0.743, 0.508, + 0.483, 0.745, 0.509, + 0.489, 0.748, 0.511, + 0.494, 0.750, 0.513, + 0.500, 0.753, 0.514, + 0.506, 0.755, 0.516, + 0.511, 0.757, 0.518, + 0.517, 0.760, 0.520, + 0.523, 0.762, 0.522, + 0.528, 0.764, 0.523, + 0.534, 0.767, 0.525, + 0.540, 0.769, 0.527, + 0.545, 0.771, 0.529, + 0.551, 0.773, 0.532, + 0.557, 0.775, 0.534, + 0.563, 0.778, 0.536, + 0.568, 0.780, 0.538, + 0.574, 0.782, 0.540, + 0.580, 0.784, 0.543, + 0.586, 0.786, 0.545, + 0.591, 0.788, 0.548, + 0.597, 0.790, 0.550, + 0.603, 0.792, 0.553, + 0.608, 0.794, 0.555, + 0.614, 0.796, 0.558, + 0.620, 0.798, 0.561, + 0.626, 0.800, 0.563, + 0.631, 0.802, 0.566, + 0.637, 0.804, 0.569, + 0.643, 0.806, 0.572, + 0.648, 0.808, 0.575, + 0.654, 0.809, 0.578, + 0.660, 0.811, 0.581, + 0.665, 0.813, 0.584, + 0.671, 0.815, 0.587, + 0.676, 0.817, 0.591, + 0.682, 0.819, 0.594, + 0.687, 0.820, 0.597, + 0.693, 0.822, 0.601, + 0.699, 0.824, 0.604, + 0.704, 0.826, 0.608, + 0.709, 0.828, 0.611, + 0.715, 0.829, 0.615, + 0.720, 0.831, 0.618, + 0.726, 0.833, 0.622, + 0.731, 0.835, 0.626, + 0.736, 0.836, 0.630, + 0.742, 0.838, 0.633, + 0.747, 0.840, 0.637, + 0.752, 0.842, 0.641, + 0.757, 0.843, 0.645, + 0.762, 0.845, 0.649, + 0.768, 0.847, 0.653, + 0.773, 0.849, 0.657, + 0.778, 0.850, 0.662, + 0.783, 0.852, 0.666, + 0.788, 0.854, 0.670, + 0.793, 0.856, 0.674, + 0.798, 0.857, 0.679, + 0.802, 0.859, 0.683, + 0.807, 0.861, 0.688, + 0.812, 0.863, 0.692, + 0.817, 0.865, 0.697, + 0.821, 0.866, 0.701, + 0.826, 0.868, 0.706, + 0.831, 0.870, 0.710, + 0.835, 0.872, 0.715, + 0.840, 0.874, 0.720, + 0.844, 0.876, 0.724, + 0.849, 0.877, 0.729, + 0.853, 0.879, 0.734, + 0.857, 0.881, 0.739, + 0.862, 0.883, 0.744, + 0.866, 0.885, 0.749, + 0.870, 0.887, 0.754, + 0.874, 0.889, 0.759, + 0.878, 0.891, 0.764, + 0.882, 0.893, 0.769, + 0.886, 0.895, 0.774, + 0.890, 0.897, 0.779, + 0.894, 0.899, 0.784, + 0.898, 0.901, 0.789, + 0.901, 0.903, 0.794, + 0.905, 0.905, 0.799, + 0.909, 0.907, 0.804, + 0.912, 0.909, 0.810, + 0.916, 0.911, 0.815, + 0.919, 0.913, 0.820, + 0.922, 0.915, 0.825, + 0.926, 0.917, 0.831, + 0.929, 0.920, 0.836, + 0.932, 0.922, 0.841, + 0.935, 0.924, 0.847, + 0.938, 0.926, 0.852, + 0.942, 0.929, 0.857, + 0.944, 0.931, 0.863, + 0.947, 0.933, 0.868, + 0.950, 0.936, 0.873, + 0.953, 0.938, 0.879, + 0.956, 0.940, 0.884, + 0.958, 0.943, 0.889, + 0.961, 0.945, 0.895, + 0.964, 0.948, 0.900, + 0.966, 0.950, 0.905, + 0.968, 0.953, 0.911, + 0.971, 0.955, 0.916, + 0.973, 0.958, 0.921, + 0.975, 0.961, 0.927, + 0.977, 0.963, 0.932, + 0.980, 0.966, 0.937, + 0.982, 0.969, 0.943, + 0.984, 0.971, 0.948, + 0.985, 0.974, 0.953, + 0.987, 0.977, 0.959, + 0.989, 0.980, 0.964, + 0.991, 0.982, 0.969, + 0.993, 0.985, 0.974, + 0.994, 0.988, 0.979, + 0.996, 0.991, 0.985, + 0.997, 0.994, 0.990, + 0.999, 0.997, 0.995, + 1.000, 1.000, 1.000, +}; diff --git a/sdrbase/util/colormap.h b/sdrbase/util/colormap.h index bc3dfa9f7..110b1a04f 100644 --- a/sdrbase/util/colormap.h +++ b/sdrbase/util/colormap.h @@ -66,6 +66,9 @@ private: static const float m_shrimp[m_size]; static const float m_cubehelix[m_size]; static const float m_cubegamma[m_size]; + static const float m_cubehlx2[m_size]; + static const float m_icy[m_size]; + static const float m_mint[m_size]; }; #endif From 3253e0088cf6ddb43b835958dcc68654878e16cd Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 13 Aug 2024 13:10:25 +0200 Subject: [PATCH 46/46] WDSP Rx plugin: added documentation --- doc/img/WDSPRx_AGC.png | Bin 0 -> 94678 bytes doc/img/WDSPRx_AGC_dialog.png | Bin 0 -> 11380 bytes doc/img/WDSPRx_AGC_dialog.xcf | Bin 0 -> 29102 bytes doc/img/WDSPRx_AM_dialog.png | Bin 0 -> 4430 bytes doc/img/WDSPRx_AudioPan_dialog.png | Bin 0 -> 5177 bytes doc/img/WDSPRx_CW_dialog.png | Bin 0 -> 13009 bytes doc/img/WDSPRx_CW_dialog.xcf | Bin 0 -> 34116 bytes doc/img/WDSPRx_CWpeak.png | Bin 0 -> 73690 bytes doc/img/WDSPRx_EQ.png | Bin 0 -> 164320 bytes doc/img/WDSPRx_EQ_dialog.png | Bin 0 -> 34219 bytes doc/img/WDSPRx_EQ_dialog.xcf | Bin 0 -> 98640 bytes doc/img/WDSPRx_FM_dialog.png | Bin 0 -> 19136 bytes doc/img/WDSPRx_FM_dialog.xcf | Bin 0 -> 55221 bytes doc/img/WDSPRx_NB.png | Bin 0 -> 230597 bytes doc/img/WDSPRx_NB2.png | Bin 0 -> 189336 bytes doc/img/WDSPRx_NB_dialog.png | Bin 0 -> 21632 bytes doc/img/WDSPRx_NB_dialog.xcf | Bin 0 -> 66057 bytes doc/img/WDSPRx_NR.png | Bin 0 -> 178647 bytes doc/img/WDSPRx_NR2.png | Bin 0 -> 131796 bytes doc/img/WDSPRx_NR_dialog.png | Bin 0 -> 15189 bytes doc/img/WDSPRx_NR_dialog.xcf | Bin 0 -> 43700 bytes doc/img/WDSPRx_RXA.png | Bin 0 -> 106268 bytes doc/img/WDSPRx_SQ_dialog.png | Bin 0 -> 18808 bytes doc/img/WDSPRx_SQ_dialog.xcf | Bin 0 -> 58640 bytes doc/img/WDSPRx_plugin.png | Bin 0 -> 80799 bytes doc/img/WDSPRx_plugin.xcf | Bin 0 -> 223663 bytes doc/img/WDSPRx_plugin_A.png | Bin 0 -> 34913 bytes doc/img/WDSPRx_plugin_A.xcf | Bin 0 -> 166768 bytes doc/img/WDSPRx_struct.png | Bin 0 -> 47880 bytes plugins/channelrx/wdsprx/readme.md | 442 +++++++++++++++++++++++++++++ 30 files changed, 442 insertions(+) create mode 100644 doc/img/WDSPRx_AGC.png create mode 100644 doc/img/WDSPRx_AGC_dialog.png create mode 100644 doc/img/WDSPRx_AGC_dialog.xcf create mode 100644 doc/img/WDSPRx_AM_dialog.png create mode 100644 doc/img/WDSPRx_AudioPan_dialog.png create mode 100644 doc/img/WDSPRx_CW_dialog.png create mode 100644 doc/img/WDSPRx_CW_dialog.xcf create mode 100644 doc/img/WDSPRx_CWpeak.png create mode 100644 doc/img/WDSPRx_EQ.png create mode 100644 doc/img/WDSPRx_EQ_dialog.png create mode 100644 doc/img/WDSPRx_EQ_dialog.xcf create mode 100644 doc/img/WDSPRx_FM_dialog.png create mode 100644 doc/img/WDSPRx_FM_dialog.xcf create mode 100644 doc/img/WDSPRx_NB.png create mode 100644 doc/img/WDSPRx_NB2.png create mode 100644 doc/img/WDSPRx_NB_dialog.png create mode 100644 doc/img/WDSPRx_NB_dialog.xcf create mode 100644 doc/img/WDSPRx_NR.png create mode 100644 doc/img/WDSPRx_NR2.png create mode 100644 doc/img/WDSPRx_NR_dialog.png create mode 100644 doc/img/WDSPRx_NR_dialog.xcf create mode 100644 doc/img/WDSPRx_RXA.png create mode 100644 doc/img/WDSPRx_SQ_dialog.png create mode 100644 doc/img/WDSPRx_SQ_dialog.xcf create mode 100644 doc/img/WDSPRx_plugin.png create mode 100644 doc/img/WDSPRx_plugin.xcf create mode 100644 doc/img/WDSPRx_plugin_A.png create mode 100644 doc/img/WDSPRx_plugin_A.xcf create mode 100644 doc/img/WDSPRx_struct.png create mode 100644 plugins/channelrx/wdsprx/readme.md diff --git a/doc/img/WDSPRx_AGC.png b/doc/img/WDSPRx_AGC.png new file mode 100644 index 0000000000000000000000000000000000000000..0b654a7c6a3f5e48903d19e64858438c2ad24f8c GIT binary patch literal 94678 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°G*nZ`2Dzp($4fAXD%#@vDhodOt% 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 AL;wH) literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_AGC_dialog.png b/doc/img/WDSPRx_AGC_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..5675f9151d80f85a2fdf81bf06a24ae090c7d489 GIT binary patch literal 11380 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_AGC_dialog.xcf b/doc/img/WDSPRx_AGC_dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..0c8ff7a26e7b07932b7844a2db1891b0e3996391 GIT binary patch literal 29102 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 Z%KZaBi|(I>Z5|~Z>pUoQMvvsB{}0oyeoz1a literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_AM_dialog.png b/doc/img/WDSPRx_AM_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..b66f7f94a34827fcf056a858f3dbaf636c005268 GIT binary patch literal 4430 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�L6Dueyn^0 zWV^W7etV|Hya}nRuP>iVwQzJ6>gr{5i7ISbABo(~mUUGnAK5ic zcOYjK)$bq6PLw5A_v8^e*r8DGV;z)A*?g4&%F!?6?)aDtkdfUX*x70kO46);wQZ;K zL8tAtq1Z&WWfbU5#}k51q7Uy2fI6DBoOyHi3;_=nMYS`OLhgLdMV(Vbj#IPIlD%#oI+WdT3Rp6y6LC zt^Fijzvbds#jS#0$6OdDI*`yBzEbEtv1m}+jx_d!qvYI2pFeK_lmGu_w||c@{u2p~ zSTNPnvO=Nu4h}7|$cFRbI(vbq@vP#24zC=N{5F5q1RWw9#3MG^$;TUl+R0FO3gnZ= z3?MV|Za>}EMui(T=@J&L%c!Whh8}Em9xIV?`u-|C{hcM$LR4Pf5AaD* zkfM>-xw)sG&Pyqu?v1&yv$B109rhV3)j#|5ndvkXgWWitPmmfN8=I&CeM*kx*l3q&wf=S<>)y%IJ^yR`!~kln={?EzR9)nEz4{98)v{#i7rRAI=cbX;W`IJ zcvx_zgDHS7Bw~1kur!c)%~S(i^XM8a6R!m(v}1}D9-$l|nJU!3bBMlI^5ciGM%<*^ z0WsKxomxKdbRWA!EK0g0pLffnu(j1}D_)?>pP{v?u+Vn+jXL3J=D0oiLy3o^PVeH>r@`!;?atF`Q?jN+wAw+iccG0K(dVl5vfzcPtF7H;7JwQ zk5P_aSD5#Q0a^`6_xkEKnHg@tq_Q{sElY+31ZsTT$jGQZgyfsyU{dkO+xE!H zXoxw0>P@obqJgKUG=Fhbl^ztX0oEM@BAqG}ox?u#^=r1G&${`uj)!Ju*{@!`N=vJA znRvgyfxQch>#tjd62QM30i>j+rMbGefcbsfEUU3z+}Ik^;NX+Z$-2kE_#Us7@5~yK zk2_DMT&s1_a!EE-9ZO@Qqihmpz>g{lB|}MDP1NeEolddJ6!*2+6~!7F8W>|EZ8+Ly^$eh&uXFcKjE{TIBo8dP zxVQkOmO=GX?I!t@G~Hy{8BEMm38JT`9~~XF3S4`;k{5(L*qkiY%bjZRDxGu-J(zTo zU>gA32H+WgRG2R867!yuiLrd4M``-1FAkiqK1(FJfqZmESg5)-_C2{vu^XH*a{-+I zs3B3w5`&IjAt51sNqudN(_3OX?l6p^xrYk#_~KlpmASdNzFf3jgBj2*()T?5{Vuj9w=o(Um~XrnE$4(yu5txrgK!p-}uyr(Ed#$!9Qsw z0Fk;E*ztMpPhbaO&o^jkX(_OSoENlBJeYpAq|Ff%u>P*}SID7dKq&(Q10IiGjJ=G?ctO+xBePSqo3q)_w`pj2K}I=4Oml4&U-^ zQ{hD^yL;W@<}n9`fSXedu9LM6cD>26=)wOn73%;;xw@9O7KS4;_Ku8!xu`I+)?rvHD}0|j z1*k(cUY{rcJHzyQDA0H~*+BbmxpQ2I$;egww$HJyp}wBu0!{Q5aLd)< zHShHFbb#m>V>P$TS`A!XUEy#zNeZtO1Ee;5(#_HI;8XPZa5XjHYD$;2H3tn=iDFoB zX=n5oWiR%Bz5G8(>c4W?QyhEB^J^DDYVcJ)fD2DxytNIs$k|~vU@j1Zfa~3^(y)&B E7k8T`tN;K2 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_CW_dialog.png b/doc/img/WDSPRx_CW_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..58abe5f788bb7198ec969483520f2224f621bb67 GIT binary patch literal 13009 zcmb7rby$>NxAq7E3Id{1QVJH`or-{@f^0$1|Ia|HzAiUT~TuVKQG zmG88b@Zz1RsOT$6QPF!gcGgCw7KRAKlL*fU!52*;lMh{@FDr#5 ziqyZ6+`ReT_{*EoUu(Z!$Ny+_P3CEDFOl+)Bj;Z6`xUOKBH%TrX{R=u?vQ+^hE57CxFNP)Z;<{6sC8a@uSwB<|D0)RWKO zbyz&d@zQ6S-6o^_oD5g@By!J9BZT6L=U79tqsHq4pAHJ$>9&uuBT;@F{(1y1!fDp+ z=;!4k-MK>==2ThuBGFY+QwXfU29x(CLmrb9o9i1>>;)HI2rC@Bj9WLXv|(8=^`s@9 z!!!IGi&lHX({-B{YW4^O6AkKd#U(@72@Ya8NXm*~EnmaE{(ySQhd&7pkvfQ}I*3|Z zS{hn8AVlp9-#8c=+;cW{Fu5loDf>#r7mpZ$xQCE@{#4OrbYsF*Q_=A9a%YIehXI{B zs6xT&L9l6@29sJe`St@Eul@BUajB>J7(E@_M ze*Ef(*MVSbI=i?hE-qqw|N3P2DeHvGVjTODrv0L)&{7<`{S6-q0)pcfdwJrZpeH?I z&!77;QBz|(5D>_xg!~%^g3z@7=PCc@fji6p|6Be!<=;E-RfPDT8~d+Q{?`Kp7*#

k)^9BJ(tRuiPTZd-k0}hqDEZ?bykq3 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_CW_dialog.xcf b/doc/img/WDSPRx_CW_dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..c009c0ae1c421fb4d79a9be9b4639c1aa0152aef GIT binary patch literal 34116 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%0D7(|!=8xOH^I0I`EwIJy*5o-Fjq@wmHQ(;okQi-u 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

WOf4g|`O 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 IM6N<$f+4pMB>(^b literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_EQ.png b/doc/img/WDSPRx_EQ.png new file mode 100644 index 0000000000000000000000000000000000000000..655036b14ab7944e02e2fc5524c53d3e77d2f9ee GIT binary patch literal 164320 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_FM_dialog.xcf b/doc/img/WDSPRx_FM_dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..2a3af102d64705d31cc67ddf3ae18d16ab5c4ac2 GIT binary patch literal 55221 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 g|bg34IuWyV+VS4IP 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­Pfm&rZ$Z=rO>Za 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

    77^iTLAc~Q^&%kp1CcgLkU;5miefE7X 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_NB_dialog.png b/doc/img/WDSPRx_NB_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb44786d9c10de51dfe9e886a13f7eeaec97f93 GIT binary patch literal 21632 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ⅇ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 WE~mQxv<&kI)GJh-Y*$-7ga034j4mbs literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_NR.png b/doc/img/WDSPRx_NR.png new file mode 100644 index 0000000000000000000000000000000000000000..63ba7d1b7d09e8c004a7ada14328ce3fa0ddffd2 GIT binary patch literal 178647 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
    c+{< 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

    d2D9331GuP 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_NR_dialog.xcf b/doc/img/WDSPRx_NR_dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..82361779b02f8464dc8bd8cba77e4c3e7b25204a GIT binary patch literal 43700 zcmeHw2|!cF)_-!dfLrmkww2cJ6}yU7qco`PXPW`nRxKo~3IYMbzPO;`R%l)8b1!bK zRkRfpZL9UEbt$f(LO|q#O576{RNQ4TkbD1U?wy1L@vB&Beedtp$?u#q=ge~E&YhV% z6A~F6Khh~VEW&AA_Z~e20)em;!rMULYYBvZKoAPy<0T08eL;i+TkO(u6pT)0o7#=y2zjogyN`65|p_J9Xlq>*&Z(wNrQQUk_)94s9g5hZFY? zM<ZL9AUbSO^8l%icScN(u~0ti4hTFG)YdJz7@@rw#vB7R+li3 zXQZ$3*k%Hc|IqU2c%8?8y7BmKGLM~i@aS@z$DR*(RPZ2JGnEhoGJm$>Uj-UB1@@iD z$rZ;?_yaB}1l(FIgpQb2q{1h(Cwl?qqd>JnYEV$AmkX%I2KDa@BS4tViD&B7F+QSfSdJAu#=Bm#DoG+hc3 zYs$1LeN`(?W<#wOrGqxnm83JPvLqa1T)ukE@-?d{NJE(3N?=-(&QYzoNNWkiuX^x( z*2>lC>8p8DyL5IrPq7)|bY?Y|ymbNFL4jXoL1GCKfgKBlVC#`$p|HT4(Hw6NGA!q@ z6~tp6ip8OsH^;k?#^;At;_WafA(l6}m)tSRB z_|jE7WGNh8@j1ti!#ud}efA(e*UD_^|}16I@JiEy1k>a|o6ayhN~)$2Jmz zFB5!+pew=tJPHIUZJ%PwCJseUf37-QCTr+uy_cTrqho_XoAN2w2{(|X0 z(r@nghWW#6Fy(0m>b@YwD!{V~PrDY8XV_T4PYL}IAfDgi%RJ>00xfcqW6`rTtm5L~ z;lmCB9{!p-O~*@uCz__qpOI;L516L+?rwX@VKE4k@aCSTJCN}jTvmTykU59LPn?wh zFyAx4Ipmn-wlmEZ0ueXgHKV;i%<*;t2|*z&ttgI`0-9+&9~Kfho*z<#77JR!vqlJk z$CgCi5F1V)U)6%d5?Tn@LP`tMSuK={Lns%Ab^>FuwxCc;7-2!3fG|?QgM4kFnUe@= z4r}uT^{h}!2~0#)ENG!+3!s=b-j4Bo@qE^e@Vp0IUXWN6Fk?5 z4T#!#@$zra63U#&-Nz>e}!N*kB)W(UnTe+!CnLh67(Y&NpL*DnFN;-%p|y- zM;LjVdvyP!^dvCjunvEu-`w%md1KnH8r~rEc%IBzs93cp`Fq8d@ z0#4r^9+QwG_Tk@9&%J^aK`NUfA;{I8qqRh4Rk}y&{we$S*%t-xH|t!!;pz#ek3)*^ z8}RX7!TVa^{-PG;?-ZOq4qT30%R;VCx1LSylMVY^Q|y5ubZy^wKZ_w*kAX1Ju%b)lSyJdBOr}7ekiOnw%Y1w?d%78Ly**sXs z099R}EhySkn6E9sJiIZ_*MbzUL~6B1Ar8}a0@h;7(@eoBqLiy8N>EVF5Rzc|Bw*2` zH6A;hUtne-*L-XSr76_n6p!N4{P9u)76{a58_!pr`u0t{+<@JcKJcdgZWl-knCjkH zLteM>i$H(;@RaJ$R<(<>b6OHD!yPaKewh=y=wiF6LYJiJvZb ztJLaxqhCGKaMAGw+0A{|o7Z_agkW4FHXw33%*&ll5-cZJ!(&I1cgOaG??kXS!9fK5 z2}Ti2COC`WGJ@*}{zmXH!IK2b3D)rVRuI9J1kdvL4)MV|q@M4D6L}KBX#~><{z&lO z1P>A{A$XDCeIDNx6KqHDZGv)w{dj}{@y`zEO|@@00cfiIpVj}f`3rvfOJ3kNwooB2 z_3cw(@(#IbVcP)0U$savr_1M)f^)^b{+sHH>kJDPm}POu`GAvYiA-Bx!bj@4F6>U~nEdq}cy}YbxlV1@13hwBThf&I1Q7SFV*I=cixaKie0Z`&saxxEOK4 zPx5DIF~aT6{F%iFUc@}n_J_+evKV=ar<6J^FpuMa<^=~C`#WuHBo0KK4w~hSol+96 z62a-9ESRy9gG%fgn9KrQy*ho>s_)GQieL^5B#yzymsswQQA;!cySEaA92NJeV;Dl2Ppd^C2Rq5$#T5=>lBE_!a^Q)&}=4?4e^?#jG zYJ3f4K7G^@c6ac!ync5_1e1L{;TN$`BvuLt1p*xr3#9?HO|Rknz2Qo(rfpPq`Q2j^ zTjR+-tU{K-LV#|Ca|GYbT!PxS8FGn@!94iRkDWuP!E89Dz;~hpXSjn(;5*Tex4YwH z>;-i#HdlPW7qjl=!&lztOIr8VDg8TqVe4G4o%$zVUUrj|W!LdJ*3%8k{m9LZXYN5_ zzD@B10pHV2-ABa_MXRgJ?s}%-x}(c&KF&o;kj&>@E;eEVqOORScl9I~LU1I(RDz^` zc19dI;h!G4bR$K1J_@$ zU0|OMTLTc9>!{JA(&6*r=FB+Pr?Aa}S2Sd+#U9Vl|CU@iFXP-()5*Fao%aDJYixaB zD{ChEF6l1`x-{!iDVW7Slpf9lhZOrXZY|M;1@13j!Qeb__#1O=>a*lodlvjBt~+|* zCpjKs@zDGXd^>`h{)RwVo^-k(0ox~9cl3Bh)*Vmr*p;h2ILmxBSa-O-ZH@pm#{ucu z$1JyXYK`p`41S<&E@%Tw=f*lCHPp}WcAp`>n#u1!Ma1Oy01UC}g zO;F3D;OVX%+CqK}=L~k+1T9!sE;RP;zBy%lJ}E+&v*KK}kShf9`})5M`FyxsasB_M z`a+$p_W?K`aI*S+04(E5*tqUfo&^hG?roPU=%R%=@n8NPALoHX3eJ`I;BTle&iT)T zhJEYLr1d1W&szTf5$gfrD0mj{CC}qFp6W69>*+(x-yeRKRtEUZCJs+J5y0P-wz?2r zDUi$J1ac|}eaJ_w2__gza6B;pu-E{N%6R!;{G|`Llu11LE#ff#A8Aq z9>)=R$~!#&^;{n359TqAOw^au@%+-MJT4>YE+Yp5R!rgL(2)gSsXmqoZGuGR(Cs~t ziDiuQ0BJwAKh=-w%k*=8mzQ-O+~yPJGYj{5n|{m?Gw1AS_nBlc^LZy;*VOsz1=M`; zJmFkw4l~WH)a3+qiMmJ`M0(Q&x(m_lLSLZHi_VG83ePY_L|EIU=*;QUqEnQPDl?tb zT2!?sMJI&E#iha$<_HloU52owUPOQnnnUKSP0q zhV!vfl2F|N-E=Pso%X#u-2K4!LB_Aq*C){W-`$5^ia>RHxyEVt|lO~_D6Ix93>hM+PrL7Z9jxclX#mKka57&s<_3w zMCeM;O@ts+UN0}du?gJ_K=G#$`sG`Vi7IDqPv!MnBF1N;2;Dlga7I2tLwt#iK2nS- zEf%UOs4{q?B`OK1Om%llxT8^3hPr2ePqZ_)y7gTjQ=bDmF#~ns; zLQ}5Z0B7-~`gHrHOPl*y*#s0`($D9e)dvZ>L4Qm8ST3{)61l?%?}1D#XS=rw7lpAd z?{XaDI-$)-HkprNT&J{2W+!7tB4@v}`$C#R=pV%EMeD?wY&x%4*Hd@tk_fsNrLVi7 zKhcv<%hdD41<`rx9CemD!_Y*?^rWfN)G0A^FQ$md>UtKPJV~7p9T%2TC5+ahSwPVu zzIz?9XyzR~N)?I=gt&XGa5m&t5xs5Z^xeMW3WNo@JGR{p6ooV3_IJubu?qF$YxPx(@ z-E2P}?qF$YjdZZIw5E43oKRCc7`AC@2TMz9S_i{*dF&2`m7Cnba4nkH!Emc^9ZdhR zLTD2t3WrbL1DQl&Hy^%&HlOYLE^uN6GjMC0&CI_C@*F$xg`La}3uBip-NR%P)-*6T zUz$f9p$;<#h=>_Gh3tFulI%9SkSb)DDJin%cqi`lfX-T$ji0U|6}y9Sql^i5(2L3hrR3suZfH ztnNFSUQ5YLD`~B(jQx(HjX^S5J+s4wXR5xVk8Y)tEKK1r`p}1*8pIUJ(&^dt%z&ds zTXAYe93z`e(JU~Wn(V_|8poI*hLY8d0Vah?m8FPN5!1V8_ijwj-q84;gvE7>uZs)& zH&BpXK9| zSXz1U>-IiOG}ed`d0(x)IojKMW`TFs9&c~oSvBF_=hpaqRUP=MHxorvs_K@|L`CFX zeJ#azxxcsfy+pG{q{!wRXn*9@)K`aND=zH_UBMP8<{H+@@pF(JQ-_b|VPh`pFd zoLYoPb%#|=D)*bARjse9tgJgVNu)AGUYgcQ1*#NQm8h5qtf~k$HS#6ZH!JD%;2TPTVhUis*p61RHUN7)ovQim&Ws-x^6#PT5okh!&WhqbN z`YT$`b=g3qmw9>?&3)aIPb6IA0nr{t-->*X%-<=GTAzwJVXVl=&VJQ{3CG!lxqQxq z0Rgv28r&lCy45<}MoMn_QAfMV+0~TJ7%rFBGkbdROx0@L@!h(K7N#^+N zQVP@0hSRPJb^~QFCMXp3%n^5V zj!}F|8CYOAHN~gL4LHUGF_fZiDll-1D4yRU&+09rN)c*hrl07oeYj6YZk(wr&i{4H zYci8-FIPudEsR2Wur^_%9AwwJkNmZuaQj3TnG8&1GQahQigGrDQZi%QCT-!CSUXuY zXuHY6Y5E(BkdmF2&zH&O(DZ?9n*NSA{jh-Ew(6IHX)@U?x_IOIV)_fIESIw#PSca? zr83z(X5XI>MN#e^T)_Af6ga><_uzGGN5<^8Tu;YcbeXW z6Iv;r!48nf?&5;FZ>EcVi16BgKrn1jOxJ;wtR|H%cIjJ8FQsHmuNWzBDf_IL{#nLL zPxgwDp<=qBd)(-qYggydTVRZ)=^g1?3in85hj6Yk*$SEtv`A&LMPS=~ly$50UbZ9e z=-wsnAdml9Te$a!M5*j5Z#%e{{>35%7cSt2c>7d-JP~T;rhn^p_2E7px#6U)tawNA zYjV>^eLixO*TP_PjD`Ur!9jk_b=+@s>F=q%{P%L9KYJfZzHijo^gZ=*|hjIxtg zgZ2P+mqV%(IpYs=b-JRhbNb3-?R4yLSn{tH>uQUW<(NM=T&?2_Mp+ri2h7@d zP^a6D6Iv^t%MO;v@8W_E+O8`bPK4J6hk_v#bAweeKN?EOYi8)mdOF*Pr6S2-_`xEi6sP;GQYcm!40NHvuz@%I^o$|*mpy0F6pCdA{q7xl z!#t@Xm$QvA80OSV6^hjcg}2_Y)g5IT3~Dah5e7rBLeXEPQ0WZ|6^bQ>vx6jxqhMTE zq}Oj4U?ujrU{H)R7$z$ekm@wfxZhEOp)7mJ=Zdj*238}1`ic#;r=}_}e@+u?;0z`y z6hQF?3eQE`3Jiw5IH8}#OW;Z@aQ3KFaR}$CP;4|9;w(~y zVhz}$FRfc;(7GeKb43{{kWa}zeYR*zid1oxw++x6_E@CgLil50Fktg{0*np^WOX4l z9r8LiJapihcCxw|G~El9CNK&-B;!Vni-+^SjRQNGY$u&5lSR?AFMMvyJDUB4Y^#{Q zlFknxHseX+uEH(wM9!gebMhOQPN(UDZHL#%mhPwN&6{TO#?~Xsv4V|-^g&m#?0lAs zOD3^i%V+F<3=eNTJn>M0OI?n~E}Mrn?JY~9X?PufEm0Dqgymbz}E~A*UobIBk}%Cu@C#J-ug(vkK;IN*9AHG z-zJlW%0HT&0t3jrgT*i!b7Q^jAj9RILQR*EJ0%V76nULSr_;R74IdqNrk%WQu1?nn zRwyt^JS5{rm5WD<(?^4yT)t2Dvs@mp(*?liug8w(Oq1^t>#k(-1B}ghleE8d2RyNJ zbOqXCV)_#?{THlCen0xN{QD#Dq~11{H?|&ePSRUSb$K6&<>xo`>h&|R-pE@IJgM8| zChrMj3Sa7SJhs`4Il5u;i7@(z<=4i_<)dt^VOSU|F;|!~wHa}87rBEb{}6{sfE zcrUK;c&KrsrZ!^_3|mo9=I`b5<(%mz-NBx&KY=Nfc_)&)ddcN8bh>_&++1es0SFIz zV4b7;d1D!8JyoaseGiZUa_q0&S||pL)Q7i?de~nuxD)FlI%_g#{m~L_@#-(QhDz8{ zd^~453@~Z=WiVQEa{=2yh_i+}MNaM%7+nr1>P8w2BVXr+mkvDBPEohSU>FDs6&OVx zl5wNX#gnJ9CWD0*Q7Ozz3w6q&^a!@j*sd1LDl>B5=aXALF&#ftMg-QBkn>#cmostbmkAquw;Jk;P) zm*cU|=3#FLS4=Y)LdA+}DGJ3TTkEj~LyE*)Vb0WML@HD$;thsTUT%rJcO);WgXw&^+R zxdub-VIU(E;IGp;rN1fgNM5{m(!>6$w=O&L?-`tR|Mb#RS+ht(ojO-I9|oY6C3+am zxw(SvAjRbkcS`-}Ba+MP=w1*~MAPb@G$Og&PS*=tHmCqlQ%O)PSF}pnjL7mnOHpl)bw33T;t>r?U2!cK6x0Z{5r*uLHm~R8v zMlR8{0NP&8>cpV;0_e2lcp>nt6;J72wCIX-ZLQL=hkP8Y^7*53*~G)X6?pV-4{Cik z*Z}yW&`KdTv{ncWA_(@N-&!F8o-zm_V7?7t8->Ks0%&^$YY>Cp3!u}I?R7` zns5lECO?5@Z}#Qd3zn-X1ac{l(CITcNA96`=@W)!z_upIEIzr_9@f&AREPm~-fxS~*mNOqz;*6r4P$^Od68S=*7{17EP1ch?JT#1?m0 z6n}XDE>rFO`*kN0z`##GUW6_y+5QOS-MqOIxkJ9^6R^=Cgce|J%Z|55@RnJ8Qdl}SB> zPR&8cU}FIxxUYJ+MH&O)Pn(ZN|Sa}EE1 z^+(Ve%GRi6$Uw9|9t|1DM=-2E9PNN6n{xKTv0yajjN$x5uv+rqaw?<{Qmj8S8F7s} z%$`e#*FQDUyD}KfEkmbAqL6K%k50$Kr`wsYQAnjH*R+|u{w>J3U+UeVGvOdezq+A$ zTZU8Ux>?_^$_=4oQ@B>$&Ffd=3Y>^TOLtJ{=Di@#pRS*uU`apnDurqe%;Q=#+fp7e zdN6}eokHcwRsGDV`;ydg`&(cbcGATC<&2fZVvW9>4Fy zUV9VzK4>?!@8jM4ruBVn(d535jhfo`u}#zZKDKCD-?vyaq3<{E{qTss4>f7L??XhB z`o2Z~G5bE~H>vM~ev|q>U!KS9`)2LO>-)U+WA=S>uf~TP8XRlDeV;q%&`aNJ2PvTM zb7?((--lxWP3Zfe-PFF14;nPB?_-N5_kC>C)V_~xn%4KRMbrAe#i9v)zj^P6NA!KD zN#lJVBAV3qE&7ky_d&l&eIN9j)c5)FJZ|4NYd>D!=d~ZR?~`6_ITpc&x(sDP@j(f- z%MeBl1mior`(V>>UpbH;`gRX7`S$J&WN$f~Xp#3$5sk#BTiSPf3)S_8vn{X2zXd0x zLL=JyzlE4?v5o}%Fc;(Kivf>BI8%P{EAP?zUrH{f9?Hbgk(3uRrQFWz?%8j==3VhhV5WHOz39Ii zM@3Mo+PP&a)iCPT?d2+8ZvpQLalD37mQTE{RI1eK-_w;}m+DTl%D5X! zWyThzIo=d-f09UfYkT?#V}dfQLA1#8>dK0#{(;LYzly!*tK7i)$5wcHuHE9vS%-@~ zt}nh|ynA@8hsW+~y3~@hu@xR3Yk&2?6%BLz+zLV+hhk3cI0RfK_Z{ATFkr6_ecOk; zjosV6TZ(Y)6ZjTSpdXx_=3sX112)>*Z(dc{3*N-n3>!^(-#_!O(HHj6-alTxw!=@% zMZx(W9OZTY+{}cUg8>RA@b1x{q)FUqq)5dE;z&5) za|_RTPfbw9-Bv2|?q0iAabBckmy$?^jT9@5akrGp9qD#T_5?5XWY`F?XI1PiPtU1m zhk2$graYPG+c*x+@Gz`K?7_s|f&)COFI_GSr97CdT{y1ZgU|2S=XdP$I~Kac^Jnby zXRPwCdB&!Q_o5H*xz!fPg+H@uk3QfItlA?vKCTLY532&;qpAS-JSx19VmHb0u~R9! zUR!Z)hxdwou+=^*|4N#-&(`wm`}{hR$hz77Rhs2@g1oQp@tSbOYt}`7|MLr9vOR1H zX|Qgpi{q9bQK?Git3qp3DnFI#x6F372TidWQtHPq+ox0>o23jkDg$<%DZ91qW$RO> z4=ZiS_Ezp(_e%W{Q%=S6!nmdTJUx%i^b9h39$4cUTfV6yK42sCLFn6Q#Sp7a0_4sM@OHFKdz&^Z_TV)78bOq zYO=TH?$+3#Z>mx|VDIxP(+-4Rq`a9k=_2piC_nGbMIvuzdJ<8%4{02n=2bMo%cm|< z>cyN|($2PNBi^zhEwV|ky6$knyoC(5GiZ_1;>C-ij=}R|EO;rPRmhAc~&gQ^Bi0KZIJJ@ z%Nsn0-}*fo-o}pWfZGWC1cefLR0TX3;W4qKy86UCI1e0ksIs~w`G0UT@d{i6*^sP@ z(DC^V|NMr3e#1Y1!ask)^WXINFMPsF`!=%EC`LDn&bTA=X*C+j8jbh!h69RCtosY- z`Xja*Zx&sh&l{A(7lgA;ppoV6P)*~zja5anZw%!UFQ-uLXY5d{d$+M&MiHtiO5FI9 zMRzd!8P>hm$o?WO^T;7LLU5U(2!pbYLYZN!Zbw^G`OwCq1S_NYT2jG#Io@FOGP>2s zZekTEx}TYfQ-v>Hk1T(skqtzwgGODhveJ3@)u9M6MI)iU`^`yVk0z#wJx~i5_5(R; z(VlgYBZ*_}NAEZ~xprcibQbW5x6F4o*EiAMgGP#yfEd-n((`_9DM8)+zo)ZePsV|3&=H zwle`g%*Ax}#Q^(a6?k9lcDBaHCusN0yO+~_d`c^PDW9r&>~tcL_+VAoF{{{8{?&-Y z+>3tRzwHn39(8kucWK2Jly~Lf+tbVvu{Wm+J87D_B+A?9WBAs)bP?r!Cpy5pbk8nt z?-AGOX`F{Fx?4K?mY>%*zZFm81a@9HP1s{&mX?TB z7ndGasb(F%e`B4Gs$@#uEY;CT#Z%1^u?nlgb{q56WwDaUJOp0^S53UQR#`G-PHu3y z>eyEzWgQlXl|<1LT%ANIjj9W2e&47l<(>3g(Dl2s@)zZ?uQ&;lo=cQPo@X=4r}%{K zJF8M&-stZcd3uFs$z;#Hw?=y&`_e2Cd*Xy)Kk2OJ3h?W$5gr~9xwr3K`q9&)WRl01 z7vU}5mu89B1M7x${@e;eI}gR2y7LfdJlLUg9t;RObj~3!;T<~XZtzQ`DCK!eKW}Nc zOP=@X|DHbGoL?1i!W$^9;OOZa{1!?}Af32ZY?$hZw^29(Z=`Sp-b&#J{3@R#@C$nz zqQM?XOZ1BED|f7Hv-5!kR=XTrV)$)2N8q>O9Dz42Yze%7!AbBA1}2=iJq(WGb}={# z?_=N^HmBPfNvqkuYhZ4YwE8AVvouMXxk=JGk|s&(NSY+ABWaQ}ZU+nMUQayKB*L@? zTU1!RVWXtkHcA?|&BUck8YQhGX%s7hG)h`W(kN+`MoDurH%c1Rk!zG@a~d>AV`(+p z*A>i-qOWfhy`@p~=0?$*8%1w!6ur4o^yWsXCmwDT{lkr-Z`de$+eXow8%5udG>YEb zC{_e%6ur4o^p-}^TN*`gZj@$o8Z^p7X*J&;ZETJc{ZB6q{70Xf@9*iEPpJQAApUDc z&G)Cypw7*H+V`(Z6Y8$VXT$#ZJlzWj;hUr>%4tne*30oz02c6gr~d!i^MBzpt{_cQ zPivaG9=~NzaTv?xWJRZ0(fSneV#I7L>BPQ-OdE5?b@~FZWNdP$I9TAYlbvI2AO>@j zoTD*J{AW~Ubk>#H+j|qE3Rg#A(FBJ`VH_JpMKbYm(#Xo~Hx?$Q&?h4c(<5Sgh|@^JnOS$G;iiI>)3AJO+pq_aQCvb?xFpOJS2d3cGe+)> z)vg?iW0M?0p%`&tVQ~qu($LC{cNWD?C@Kuqejmois%v86Le=Wf*jPJt)!Hky6gkAGA!eAEu3y3Fala8IJk5$*iCd7q?LPaHNQ(WydN^OiLm@&F-urVYSoEjnxW|p3l2AlG~8;vs@)i%f&42=*QEDd6&-H`@C zL<$$-5L6QknZf^HNsuYBYBm*Q49$+n|869X9_cUwiV_yN6xWCVxEyX= z>oIPL&NySY#D4}>j`-nH?af``q%3X;4gta#!731&g>mMC$=UcVvQtkC$ z0wB^CM@BpQV2E<|#t`Z3Wd#w=D)63rU`Qt=xYK<=K=^x_c1Y;n$&I)hPePhy`tQUt zCF}88!s!dsxZDlg@*>WZJhlW=9b=ivtSu2^mP9j?_^2pmB2Ppz6FA~#>Zx+Wl89v8 z)$5xgB7Q2b*casx!6fr>7t=K<)uVU+s)^Soep8+pS&-$RVaD@uH4z$(Zs|{_W&j@A ztkEnuZHZ%VP~mqAe~8$4@y^fTMfWNyZWw>adWcnivAmQQrXq( zKVy$p>72dmk{f6EFT7Zi)RtEyb1rB`toYsm8jH_s+=)vUW(Q6=d;Qv`z`%7^%8TP20+~cEu44d`z!Srn z_y)uY~*%lQ|D&F!PhKSDUMu_;kW>dJio+oZK5o?Ui|1$fCZD->^(SB7wU@6HMOotTT9#A7YgwLv zzqs0ffIqCtNt~3XkU)3rqBk^k;Q<5tat?H3+&QN{NsBz%CQJ{%#_$`25D54RgwUO% z1Ooiqu|R;9I$?!(t;xCyq@B$Fa}S4{-3MeexgRAV02QPUeRhX3FYUwaBk^ z{jigd%bZPc3Bfgu*nntlOJ2Sf|MVYRYdaGpKe4s;Gr|ui7(;L(!Px}AC74BUC&4^| zI)c{-8hOlUN$@p-oe6$K@H2wL3C0kdNN_g6ZwY1*+(|HxppM`*f<_)QTM~SYU}u6K z5&VqcaDp)eC-V5vFaNL&ehH}gdD~y>Y5w%#r%|lx@NoLi^SAyrG&aTd*NXmnp1Lnc zsShmNhu0vGAIuSq1^QERZ{mCSZJxF~DjmEuahkBC=)?r4q3SW*&-0K+hvku(0wx_H zh!wmo{tX?hgP*ccS&jw}ylF*uu%ex;=obi$ZQ;fekVo0OK(>ZpeYFF|oQ;)yU7XcZ z4krWGx^xZ%SqliRAh?c4{AsB%(CcsW^7Z8R2-nvTp8PCO7S9T@h%2&imEp=FWz51w z$2=}0Mlt|p;c8>vi335_+XOoie4k)9hvU(G~4?#q*AHhKchY<86=uOa{U?9N| zg5d)`;XNN F{vZA+y_Em} literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_RXA.png b/doc/img/WDSPRx_RXA.png new file mode 100644 index 0000000000000000000000000000000000000000..1e97323cfa46ffdae940c3c805678dc4a1ffbe72 GIT binary patch literal 106268 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�Nm>B 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 QfCuqJO73Cy1KrpE3+E1Q8~^|S literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_SQ_dialog.xcf b/doc/img/WDSPRx_SQ_dialog.xcf new file mode 100644 index 0000000000000000000000000000000000000000..a1dd53f0570fc4aff93111b1a4d1473678c4c749 GIT binary patch literal 58640 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 F{U5z5H(LMz literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_plugin.png b/doc/img/WDSPRx_plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..b7b5ebf32b8c166b4722b46b2ffc655eb7b9800d GIT binary patch literal 80799 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
  1. A%y>m}ty? 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 ix6GVk$a@kUg}BAa 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

    Introduction

    + +This plugin implements a modified "RXA unit" of the [WDSP library](https://github.com/TAPR/OpenHPSDR-wdsp) by Warren Pratt, NR0V. This library has been extensively modified to be productively included in SDRangel and can be found in the "wdsp" subdirectory of this repository. However the original DSP algorithms have been preserved and I want to address my thanks to the original author for making it available to the community. Only the code structure was modified to meet Qt and C++ standards. The original version written in C could not benefit of modern C++ features. Also the multithreading support was entirely removed as this is already handled at the upper level in SDRangel. This greatly simplifies the code. Although the details of implementation have been changed the explanation of DSP algorithms found in the [documentation](https://github.com/TAPR/OpenHPSDR-wdsp/blob/master/wdsp%201.24/WDSP%20Guide%2C%20Rev%201.24.pdf) is still valid. + +The WDSP library is at the heart of OpenHPSDR and Pi-HPSDR projects and both implement excellent communication receivers. Experience with Pi-HPSDR was the main motivation to create this plugin. + +As explained in the documentation the WDSP engine is organized in channels comprising a single unit itself composed of several blocks. In the implementation here only units and blocks were retained and more specifically this plugin makes use of the "RXA" unit. The channel is somehow represented by the channel plugin itself. + +![WDSP structure](../../../doc/img/WDSPRx_struct.png) + +This "RXA" unit is originally composed of these blocks: + +![WDSP RXA](../../../doc/img/WDSPRx_RXA.png) + + - Frequency Shifter: used to implement the RIT functionality + - Input Resampler: is not used. The channel interpolator/decimator is used to give a fixed rate of 48 kS/s which is the internal DSP rate of the RXA unit + - Signal Generator: was removed in this implementation + - Input Meter: not used + - Notched Bandpass: this is the main input filter and is permanently active but the notch feature is not used + - S Meter: permanently active this is used for channel power displays + - Post filter display send: permanently active this is the tap providing the spectrum view (B) + - AM Squelch capture: used in AM squelch functionality + - AM/SAM Demodulator: used for AM/SAM modes + - FM Demodulator: used for FM mode + - FM Squelch apply: used in FM squelch functionality + - Spectral Noise blanker: not used + - Equalizer: used in equalizer functionality + - AGC: used in AGC functionality + - Auto Notch Filter: used in ANF functionality + - LMS Noise Reduction: used in noise reduction ¨NR" mode + - Spectral Noise Reduction: used in noise reduction ¨NR2" mode + - Bandpass Filter: participates in the global channel filtering + - AGC Meter: not used + - Scope/Phase display send: not used + - AM Carrier Block: not used + - CW peaking filter: used in CW functionality + - Dolly Filter: not used + - Syllabic Squelch: not displayed in the diagram above. Implements the voice squelch mode mostly useful and efficient in SSB. + - Patch Panel: used for volume and pan controls + - AM Squelch Apply: participates in AM squeclh functionality + - Output resampler: used if the audio sample rate is not 48 kS/s + +In addition the noise blanker apply to the complete I/Q stream before it enters the RXA chain described above thus on the full 48 kHz stream. There are two available: + + - Preemptive Wideband Noise Blanker described at p.122 of the documentation implements the "NB" noise blanker mode + - Interpolating Wideband Noise Blanker described at p.129 of the documentation implements the "NB2" noise blanker mode + +Using this RXA unit this plugin provides something similar to the VFO concept found in other SDR software and hardware radios. It implements the classical amateur radio and SWL audio modes: SSB, AM, FM. CW is not considered a mode apart here. To work CW signals one selects the SSB mode (then USB or LSB) and one may use the CW peaking filter to isolate even more the pitch of interest. The trick to move the displayed frequency on the expected pitch is explained in the RIT section (A.13). + +While the advantage over AM and NFM demodulator plugins is debatable this plugin is much more advanced than the SSB demodulator plugin for working SSB and CW signals and generally should be preferred over the SSB demodulator plugin. It retains the SSB demodulator plugin GUI presentation but with better and extended capabilities. + +

    Interface

    + +The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + +Similarly to the SSB demodulator plugin the center of the GUI is divided into the settings (A) and spectrum (B) areas (sections). + +![WDSP Rx plugin](../../../doc/img/WDSPRx_plugin.png) + +

    B: Spectrum

    + +The spectrum view and controls is similar to other spectrum views. Controls on the bottom of the panel are identical to the ones of the main spectrum display. Details on the spectrum view and controls can be found [here](../../../sdrgui/gui/spectrum.md). + +This is the spectrum seen after the bandpass filter. The span can be controlled via the span setting (A.11). In LSB mode the frequencies displayed are negative to take into account the spectrum reversal. + +

    A: Settings

    + +![WDSP Rx plugin settings](../../../doc/img/WDSPRx_plugin_A.png) + +

    A.1: Frequency shift from center frequency of reception

    + +Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mouse wheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

    A.2: Channel power

    + +Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band. + +

    A.3: Monaural/binaural toggle

    + + - Monaural: the scalar signal is routed to both left and right audio channels. Note that you should set this mode for the CW decoder feature to work. + - Binaural: the complex signal is fed with the real part on the left audio channel and the imaginary part to the right audio channel. + +Right clicking on this button opens a dialog to control audio pan: + +![WDSP Rx AM plugin settings](../../../doc/img/WDSPRx_AudioPan_dialog.png) + +The "0" button resets the balance to the center. Use the slider to pan the audio more left or more right. + +

    A.4: Invert left and right channels

    + +Inverts left and right audio channels. Useful in binaural mode only. + +

    A.5: Demodulation mode

    + +Sets the demodulation mode between: + + - SSB: for DSB, USB, LSB, CW + - AM: for AM in classical detection mode + - SAM: for AM in synchronous mode + - FM: for narrowband FM + +Right clicking on this button opens a dialog to control settings pertaining to the current mode: + +**SSB**: no dialog + +**AM/SAM**: + +![WDSP Rx AM dialog](../../../doc/img/WDSPRx_AM_dialog.png) + +There is only one control. This "fade level" when checked substitutes the received carrier with a constant magnitude carrier overcoming possible carrier fades. + +**FM**: + +![WDSP Rx FM settings](../../../doc/img/WDSPRx_FM_dialog.png) + +

    1: Expected FM deviation

    + +Expected FM deviation in kHz. The value appears on the right of the control. + +

    2: Audio filter low frequency cutoff

    + +Audio filter low frequency cutoff in kHz. The value appears on the right of the control. + +

    3: Audio filter high frequency cutoff

    + +Audio filter high frequency cutoff in kHz. The value appears on the right of the control. + +

    4: AF limiter

    + +This is an AGC on the audio signal. + +

    5: AF limiter gain

    + +This is the top gain of the audio AGC. + +

    6: CTCSS notch filter

    + +This filter can be used to notch out the CTCSS tones. Of course this is useful only if the low frequency cutoff of the audio filter (2) is lower than the tone frequency. + +

    7: CTCSS notch frequency

    + +Select the CTCSS frequency to be notched out. + +

    A.6: Sideband flip

    + +Flip LSB/USB. Mirror filter bandwidth around zero frequency and change from LSB to USB or vice versa. Works in SSB mode only. This is similar to the same control of the SSB demodulator. + +

    A.7: SSB/DSB demodulation

    + +Toggles between SSB (icon with one sideband signal) and DSB (icon with double sidebands signal). In SSB mode the shape of the icon represents LSB or USB operation. This is similar to the same control of the SSB demodulator. + +

    A.8: Profile selection

    + +Selects the current profile. A profile keeps track of most of the settings. This allows to rapidly toggle between demodulation modes or between different preferences for the same modulation mode. Up to 10 profiles (0 to 9) can be stored. The current profile number is displayed at the right of the dial button. + +

    A.9: S points / dB

    + +Toggles between S points and dB units display for the level meter (A.10) + +

    A.10: Level meter

    + + - top bar (green): average value + - bottom bar (blue green): instantaneous peak value + - tip vertical bar (bright green): peak hold value + +

    A.11: Spectrum display frequency span

    + +The DSP sample rate of 48 kS/s is further decimated by powers of two for the spectrum display and bandpass filter limits. This effectively sets the total available bandwidth depending on the decimation: + + - **1** (no decimation): 24 kHz (SSB) or 48 kHz (DSB) + - **2**: 12 kHz (SSB) or 24 kHz (DSB) + - **4**: 6 kHz (SSB) or 12 kHz (DSB) + - **8**: 3 kHz (SSB) or 6 kHz (DSB) + - **16**: 1.5 kHz (SSB) or 3 kHz (DSB) + +

    A.12: Bandpass FIR filter window

    + +Controls the window applied to the impulse response of the bandpass filter. According to WDSP documentation you may leave it to "B-H4" which is the 4 term Blackman-Harris window. "B-H7" is the 7 term Blackman-Harris window and is generally an overkill. You have the choice still. + +

    A.13: Toggles RIT

    + +Toggles the RIT feature. Internally it uses the "shift" block of the "RXA" unit and hence applies an extra shift over the main frequency shift (A.1) effectively implementing a RIT feature. + +You may want to take advantage of the RIT when listening to CW signals. By setting the RIT frequency value (A.14) to the opposite of the desired pitch from the center frequency the displayed frequency and channel marker on the spectrum will fall right on the signal when the proper pitch is obtained. This can greatly facilitate tuning the CW signal at the right pitch which is important if you are using the CW peaking filter (A.23) + +For example if the desired pitch is 600 Hz and you are set in USB mode then you would set the RIT frequency to -600 Hz. Conversely if you are set in LSB mode the RIT frequency would be +600 Hz. + +When the RIT is engaged the bandwidth display on the spectrum channel marker moves accordingly while the center frequency stays fixed. + +

    A.14: RIT frequency

    + +Sets the RIT frequency shift in Hz. The value is displayed at the right of the button. + +

    A.15: Bandpass filter near frequency cutoff

    + +Controls the filter cutoff frequency closest to zero with a positive value in USB mode and a negative value in LSB mode. The value is limited by the far frequency cutoff so that it is always lower to the far frequency cutoff in absolute value. + +This is effective only for SSB. The value is automatically set to 0 in DSB, AM or FM and control is disabled. + +This is similar to the same control in the SSB demodulator. + +

    A.16: Bandpass filter far frequency cutoff

    + +Controls the filter cutoff frequency farthest to zero with a positive value in USB mode and a negative value in LSB mode. Moving the slider in the positive range effectively sets the USB mode while moving it in the negative range sets the LSB mode. + +One may also use the sideband flip (A.6) to quickly change between USB and LSB keeping the same filter limits. + +In DSB, AM and FM it just controls the filter half bandwidth. + +This is similar to the same control in the SSB demodulator. + +

    A.17: Volume control

    + +Controls the audio volume in dB. The value is displayed at the right of the button. + +

    A.18: AGC

    + +Toggles AGC. You would usually leave it always on. You will find details on the AGC algorithm starting p.41 of the WDSP documentation. By right clicking on the button the AGC controls dialog is opened: + +![WDSP AGC Dialog](../../../doc/img/WDSPRx_AGC_dialog.png) + +

    1: AGC mode

    + + - **Long**: Hang time = 2000 ms. Decay_time_constant = 2000 ms + - **Slow**: Hang time = 1000 ms. Decay_time_constant = 500 ms + - **Medium**: Hang is turned OFF. Decay_time_constant = 250 ms + - **Fast**: Hang is turned OFF. Decay_time_constant = 50 ms + + +

    2: AGC slope

    + +Adjust the slope in dB. See the diagram next paragraph for details. + +

    3: Hang threshold

    + +

    A.19: AGC top value

    + +This is the top value in dB of the gain applied to the signal. You may want to adjust it depending on the conditions to avoid audio signal saturation. + +In the following diagram extracted from WDSP documentation the top value is the red line, It is set at 0 dB on the diagram. The actual value is controlled by this button: + +![WDSP AGC](../../../doc/img/WDSPRx_AGC.png) + +The diagram also shows the effect of the "slope" setting found in the AGC control dialog (A.18) + +

    A.20: Noise Reduction

    + +Toggles the noise reduction feature. Details in the WDSP documentation start at p.51 There are 2 possible noise reduction schemes. + +Principle of LMS Noise reduction extracted from WDSP documentation: + +![WDSP NR](../../../doc/img/WDSPRx_NR.png) + +Principe of spectral noise reduction extracted from WDSP documentation: + +![WDSP NR2](../../../doc/img/WDSPRx_NR2.png) + +Right clicking on this button opens a dialog to control noise reduction settings: + +![WDSP AGC](../../../doc/img/WDSPRx_NR_dialog.png) + +

    1: Noise reduction scheme

    + +Choice is between "NR" for LMS Noise Reduction and "NR2" for Spectral Noise Reduction. + +NR usually works better for CW possibly FM while NR2 works better for SSB and AM/SAM. + +

    2: NR2 gain per frequency bin calculation

    + +Applies to spectral noise reduction (NR2). Choice between: + + - **Linear**: Gaussian speech distribution, linear amplitude scale + - **Log**: Gaussian speech distribution, log amplitude scale + - **Gamma**: (default) Gamma speech distribution + +According to WDSP documentation *"All three choices will produce +somewhat similar results with the default Gamma speech distribution being somewhat preferred."* + +

    3: NR2 Noise Power Estimation method

    + +Applies to spectral noise reduction (NR2). Choice between: + + - **OSMS**: (default) Optimal Smoothing Minimum Statistics + - **MMSE**: Minimum Mean‐Square Error + +According to WDSP documentation *"Both choices will produce somewhat similar results. For general operation, the default is preferred. In situation involving sudden changes in signal/noise amplitude, e.g., with "static crashes" caused by lightning, the MMSE method may be preferred due to its ability to more rapidly adjust to the changes."* + +

    4: Noise reduction position

    + +Applies to both NR and NR2. Locates the noise reduction block either before or after the AGC is applied. + +

    5: NR2 artifacts reduction

    + +Applies to spectral noise reduction (NR2). Turns off/on an artifact‐elimination post‐filter. It is recommended to leave it always on. + +

    A.21: Noise Blanking

    + +Toggles the noise blanking feature. Details in the WDSP documentation start at p.122. Choice is between Preemptive Wideband Noise Blanker (NB) and Interpolating Wideband Noise Blanker (NB2) + +Principe of the Preemptive Wideband Noise Blanker extracted from WDSP documentation: + +![WDSP NB](../../../doc/img/WDSPRx_NB.png) + +Principle of the Interpolating Wideband Noise Blanker extracted from WDSP documentation: + +![WDSP NB2](../../../doc/img/WDSPRx_NB2.png) + +Right clicking on this button opens a dialog to control noise blanking settings: + +![WDSP NB dialog](../../../doc/img/WDSPRx_NB_dialog.png) + +

    1: Noise blanker scheme

    + +Choice between "NB" or Preemptive Wideband Noise Blanker and "NB2" or Interpolating Wideband Noise Blanker. + +

    2: NB2 mode

    + +Determines what estimate values are used for the sequence of corrupt samples to be replaced. Choice between: + + - **Zero**: zero mode: estimate as zero + - **Sample&Hold**: take the value of non‐corrupt signal at the beginning of the impulse and hold that throughout the corrupt sequence + - **Mean Hold**: average the non‐corrupt values at the beginning and end of the corrupt sequence and use that as the estimate during the corrupt sequence + - **Hold Sample**: take the value of non‐corrupt signal at the end of the impulse and hold that throughout the corrupt sequence + - **Interpolate**: linearly interpolate across the corrupt sequence + +

    3: Slew time

    + +This is the duration of the raised‐cosine transitions between normal signal levels and the zero estimate during an impulse (tau) + +

    4: Lag time

    + +This is the hang time after the impulse + +

    5: Averaging time

    + +This is the time‐constant for averaging power across the wide bandwidth (back tau) + +

    6: Lead time

    + +This is the advance time before the impulse + +

    7: Threshold

    + +A sample is judged to be part of an impulse if its magnitude is greater than threshold*average_power. If threshold is too low, normal signals may be wrongly identified as impulse noise. If it is too high, successful detection will not occur. + +

    A.22: Automatic Notch Filter

    + +Toggles the Automatic Notch Filter. This is useful to eliminate a single tone signal. Of course this is not to be used for CW since it would automatically cancel the CW signal. + +

    A.23: CW peaking filter

    + +Toggles the CW peaking filter. The CW Peaking Filter is an audio IIR filter intended to peak a single frequency, the frequency of a +desired CW signal. + +This is the diagram extracted from WDSP documentation showing the response of the filter for different bandwidths: + +![WDSP CW peaking](../../../doc/img/WDSPRx_CWpeak.png) + +Right clicking on this button opens a dialog controlling the details of this filter: + +![WDSP CW dialog](../../../doc/img/WDSPRx_CW_dialog.png) + +

    1: Peak frequency

    + +Adjust the center frequency (Hz) of the filter. This should correspond to the desired pitch. To center the CW signal on this pitch frequency see RIT (A.13) for a practical method. + +

    2: Bandwidth

    + +Bandwidth (at -3 dB) of the filter in Hz. + +

    4: Gain

    + +Gain of the filter in linear terms (not dB) + +

    A.24: Squelch

    + +Toggles the squelch feature. Right clicking on this button opens a dialog to control squelch details: + +![WDSP SQ dialog](../../../doc/img/WDSPRx_SQ_dialog.png) + +

    1: Squelch type

    + +These checkboxes let you choose the type of squelch that is applied. It enables the options corresponding to the selected type of squelch (Voice: options 2 and 3, AM option 4). + + - **Voice**: This type of squelch is based on identifying voice artifacts to open the squelch. It best applies to SSB. + - **AM**: This is the classical magnitude based squelch. It best applies to AM and CW + - **FM**: This squelch is based on measuring the noise level after the discriminator and opens the squelch if it falls below a certain level. It only applies to FM (of course). + +

    2: Mute time constant

    + +For voice squelch this is the time to progressively mute the signal when the squelch closes. + +

    3: Un-mute time constant

    + +For voice squelch this is the time to progressively un-mute the signal when the squelch opens. + +

    4: Maximum tail time

    + +For AM squelch this is the maximum grace period after the squelch closes to effectively mute the audio. + +

    A.25: Squelch threshold

    + +Controls the squelch threshold. The value is a percentage and its effect depends on the type of squelch used (selected by A.23 dialog). Generally the greater the value the higher the threshold. Given θ is the factor value from 0.0 (0%) to 1.0 (100%): + + - Voice squelch: see `ssql.cpp` WDSP code: $0,75\times\theta$ + - AM squelch: magnitude threshold in dB: $160\times\theta - 160$ + - FM squelch: squelch opens if noise magnitude falls below this value: $0.9\times10^{-2\theta}$ + +

    A.26: Equalizer

    + +Toggles a 10 frequency points with continuous response equalizer. This diagram extracted from the WDSP documentation explains the difference with a classical band equalizer: + +![WDSP EQ](../../../doc/img/WDSPRx_EQ.png) + +Right clicking on this opens a dialog to control equalizer details: + +![WDSP EQ dialog](../../../doc/img/WDSPRx_EQ_dialog.png) + +

    1: Preamplifier gain

    + +This is in fact the global gain in dB. The value appears at the right of the slider. + +

    2: Frequencies

    + +Specify the frequency (Hz) of each of the 10 points. Defaults are at the center of the possible ranges: + + - **f1**: from 0 to 48 Hz + - **f2**: from 49 to 94 Hz + - **f3**: from 95 to 187 Hz + - **f4**: from 188 to 375 Hz + - **f5**: from 376 to 750 Hz + - **f6**: from 751 to 1500 Hz + - **f7**: from 1501 to 3000 Hz + - **f8**: from 3001 to 6000 Hz + - **f9**: from 6001 to 12000 Hz + - **f10**: from 12001 to 20000 Hz + +

    3: Gains

    + +Specify the gain (dB) at each of the 10 frequency points. The value appears at the right of the slider.

    KqB?__?sy7qAcm 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 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_plugin_A.png b/doc/img/WDSPRx_plugin_A.png new file mode 100644 index 0000000000000000000000000000000000000000..9c9ba16dec023fe1e03d740d336bb4bd11c03834 GIT binary patch literal 34913 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 GIRAgg0*oO5 literal 0 HcmV?d00001 diff --git a/doc/img/WDSPRx_plugin_A.xcf b/doc/img/WDSPRx_plugin_A.xcf new file mode 100644 index 0000000000000000000000000000000000000000..b33a0da3057ceea17e864c97e4a7969c36368a37 GIT binary patch literal 166768 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%0DE2|##>88r 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�BQrueBf}wzg8tn z&W`G$obzy}e9ofb9rYJC?M+v+8ryG`h2MdAx+O3w(CQdK97xDIy1K+eVjpEi;7x$4 zp7cg5QAMFEZ!*{5Zt`B1S%Z27Fi6qcy9PK3CRQ!x1*Ol1mW~I)yc5B>Y0sVI=W85z zB_|%oE`gf@r3Usms2rhF7`3Z2nllxR2#gr2RD7z3l%vhWB2)9%YNHPh*mV4yDMt@) z#Xx=r=##eI^KHQL+V$(_zLyZeQ)vP?lz6(J5N&IF5NoEOpg@Cw&6LKKB<75ptjPQx zb45Z_^ax^@J)pR1S6grn!9|N%A|fOGfWCsL4L%OE_<?-{Z93$zezZ331zZ1_bwLo+kJTVuT5Sp7s+iQ%(12h`fFkcm=THI)3k{6HN(3(y)O+liTeg601zwl}kFug7A zqTiqvLMSzyv=OwdwN6A>S*N$@xU{|DNi-As*`nAne$lm_Es2PV8a zib~FPGq#9k*`)Uxxi2uc*yZkJW@NzQqRV$`1ObvCtZrc2xWZb?jxmkA3uSy20CO<0 zCILV3B3EN+d6^KY24)niAx?z49r!$c|2FM^Lym!6i={*fj)tD-`1tJ64ZteG_A_5T zete;=EoMI>$v>&MBzXM=&h_6G{$AKDl)~%o+vF1e)JpFt4FdFmOaM#c5`^|+aBX7- zRl04@K(wmc>y>~g);~*#nUIcQgAqu?)ZjikI@*^kGiz6|C+P>l>fKclYr)UJN(Z4( z4aN~h^nr~SPt`$r*3|`adKukb@ZuC5fhX$yF4GXXeUP_%!m5_+LoY*kJF2&{GnLEw z{Nw;~KR~Ou;)5P#_WG;>tKGs8PLo@Q!23Z*TNt$SHB~>PEqy|dFd!El`&uEW{Ujxs z(n6Wq;ie6J@N&WQff^c0Q3x8bZuKQr9)bvL53CeOL}0N-Ys(-%Lnk(nL&>1PJTKSc zv9CXOee~#O^o_?F1-dYK1_Hz;CF4*Wg8Ot{h)q2u{mfT};{y&Jo`OCHLLDB}8|-c9 zUY6~weqW5}7MW=Uz=44Q z?oky)JaC*jGSOYVdJ6ff7GMCp$D<8aM~z$(Qqm-TBP6_QAohebq#10H?^}UU0|_UV z&p;X`V-{SRfYJb3I#y3dbKv0M$knDb&o?N2&^%o}U=QRiz>!moHDiy4Z^VP26;zkz zi${R2OifMQ-O<$36QeY%6G-V2uABYVWl+Gw(=cs7<^~P)>%gb@c!ET1h~z?y%HUo5 zEiO;j2?becd3lv?L3j6)vB;VU5r2mpYQEx0_?QEB6{k?TT|zMhwNR(ymjO$N6abo_ zwt(Kp85w4m7w6_B9zf9bD?Ex_0?-Xkxys7Q^mGmY=6lD7a3f%Gte<6NVPRoqogElp z4>N|l1$(heu79Q3qep~CQV%$bvPin~gRXl0db6p(tsL_; z&C?kwPrMuaH{YZ|)H;WX-uLqS-wyYxlQq06C9LD%Pz0uKYw(_-8Np=_X?Q|TPF(qq zK(B&b6GG(X>MA}dsVlS=jN=v*9A0x-s7(Ng38oIR4!Gq9Q0bVb`>b;T_7on%V79>#`#uL1i~r*CuYUh!QQH z``IKcJGdp{L{F#@@aEaxu_Ozb%iO#ufc{1zwnDs#(-c_=c%jSj1-rZ=HR8y5=Pi&( znwoU&j-qs+a3Sdzui-;lud33D#Uay=HRIX`dm>kVJ6%q_R_w^e!I6@p0iGCKH<|vAWJ&+I9+a+!A88`Bw(D;A;ak<8w#8jpWAb4^d5!CY zzh9^YT)a}-G~|cQtq$v8-VSd?1{uVf6^;^}N=J2H(Pn0R_n>fev2d^)P9`9 zZr|VUJlUX*k?HqrVxwL8n+@5@b=^GLjq0G~5h*$&L+6cVBu=^Bavd|m0kUl5 z_~*C_uPE`cF$4PT*~}BrKSBATwSG+abpLATI43!Lm^ur-j{p00<8lJ@hpQr>+`_l9 sWDSP receiver plugin + +

  2. Hs9*kK6wU3uZTO3n4ow>9-p&YlFv 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