From a2089724cf7ab1b6baeb18a2d4836b936af3b05c Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 13 Dec 2016 19:57:07 +0100 Subject: [PATCH] SSB Modulator: Interim state (4) --- plugins/channeltx/modssb/ssbmod.cpp | 115 ++++++++++++++++++++++++---- plugins/channeltx/modssb/ssbmod.h | 4 + sdrbase/dsp/nco.cpp | 17 ++++ sdrbase/dsp/nco.h | 3 + 4 files changed, 126 insertions(+), 13 deletions(-) diff --git a/plugins/channeltx/modssb/ssbmod.cpp b/plugins/channeltx/modssb/ssbmod.cpp index 465150837..6cdc32f07 100644 --- a/plugins/channeltx/modssb/ssbmod.cpp +++ b/plugins/channeltx/modssb/ssbmod.cpp @@ -39,6 +39,10 @@ const int SSBMod::m_ssbFftLen = 1024; SSBMod::SSBMod() : m_SSBFilter(0), m_DSBFilter(0), + m_SSBFilterBuffer(0), + m_DSBFilterBuffer(0), + m_SSBFilterBufferIndex(0), + m_DSBFilterBufferIndex(0), m_audioFifo(4, 48000), m_settingsMutex(QMutex::Recursive), m_fileSize(0), @@ -76,6 +80,10 @@ SSBMod::SSBMod() : m_SSBFilter = new fftfilt(m_config.m_lowCutoff / m_config.m_audioSampleRate, m_config.m_bandwidth / m_config.m_audioSampleRate, m_ssbFftLen); m_DSBFilter = new fftfilt((2.0f * m_config.m_bandwidth) / m_config.m_audioSampleRate, 2 * m_ssbFftLen); + m_SSBFilterBuffer = new Complex[m_ssbFftLen>>1]; // filter returns data exactly half of its size + m_DSBFilterBuffer = new Complex[m_ssbFftLen]; + memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1)); + memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen)); } SSBMod::~SSBMod() @@ -88,6 +96,14 @@ SSBMod::~SSBMod() delete m_DSBFilter; } + if (m_SSBFilterBuffer) { + delete m_SSBFilterBuffer; + } + + if (m_DSBFilterBuffer) { + delete m_DSBFilterBuffer; + } + DSPEngine::instance()->removeAudioSource(&m_audioFifo); } @@ -161,18 +177,35 @@ void SSBMod::modulateSample() pullAF(c); calculateLevel(c); - m_modSample.real(0.0f); // TOOO - m_modSample.imag(0.0f); + // TODO: feed spectrum + + m_modSample = m_carrierNco.nextIQ() * c; } void SSBMod::pullAF(Complex& sample) { int16_t audioSample[2]; + Complex ci; + fftfilt::cmplx *filtered; + int n_out; switch (m_afInput) { case SSBModInputTone: - sample = m_toneNco.nextIQ(); + if (m_running.m_dsb) + { + Real t = m_toneNco.next(); + sample.real(t); + sample.imag(t); + } + else + { + if (m_running.m_usb) { + sample = m_toneNco.nextIQ(); + } else { + sample = m_toneNco.nextQI(); + } + } break; case SSBModInputFile: // sox f4exb_call.wav --encoding float --endian little f4exb_call.raw @@ -190,32 +223,45 @@ void SSBMod::pullAF(Complex& sample) if (m_ifstream.eof()) { - sample.real(0.0f); - sample.imag(0.0f); + ci.real(0.0f); + ci.imag(0.0f); } else { Real real; m_ifstream.read(reinterpret_cast(&real), sizeof(Real)); - sample.real(real * m_running.m_volumeFactor); - sample.imag(0.0f); + ci.real(real * m_running.m_volumeFactor); + ci.imag(0.0f); } } else { - sample.real(0.0f); - sample.imag(0.0f); + ci.real(0.0f); + ci.imag(0.0f); } break; case SSBModInputAudio: m_audioFifo.read(reinterpret_cast(audioSample), 1, 10); - sample.real(((audioSample[0] + audioSample[1]) / 65536.0f) * m_running.m_volumeFactor); - sample.imag(0.0f); + ci.real(((audioSample[0] + audioSample[1]) / 65536.0f) * m_running.m_volumeFactor); + ci.imag(0.0f); break; case SSBModInputCWTone: if (m_cwKeyer.getSample()) { - sample = m_toneNco.nextIQ(); + if (m_running.m_dsb) + { + Real t = m_toneNco.next(); + sample.real(t); + sample.imag(t); + } + else + { + if (m_running.m_usb) { + sample = m_toneNco.nextIQ(); + } else { + sample = m_toneNco.nextQI(); + } + } } else { @@ -226,9 +272,38 @@ void SSBMod::pullAF(Complex& sample) break; case SSBModInputNone: default: - sample = 0.0f; break; } + + if ((m_afInput == SSBModInputFile) || (m_afInput == SSBModInputAudio)) // real audio + { + if (m_running.m_dsb) + { + n_out = m_DSBFilter->runDSB(ci, &filtered); + + if (n_out > 0) + { + memcpy((void *) m_DSBFilterBuffer, (const void *) filtered, n_out*sizeof(Complex)); + m_DSBFilterBufferIndex = 0; + } + + sample = m_DSBFilterBuffer[m_DSBFilterBufferIndex]; + m_DSBFilterBufferIndex++; + } + else + { + n_out = m_SSBFilter->runSSB(ci, &filtered, m_running.m_usb); + + if (n_out > 0) + { + memcpy((void *) m_SSBFilterBuffer, (const void *) filtered, n_out*sizeof(Complex)); + m_SSBFilterBufferIndex = 0; + } + + sample = m_SSBFilterBuffer[m_SSBFilterBufferIndex]; + m_SSBFilterBufferIndex++; + } + } } void SSBMod::calculateLevel(Complex& sample) @@ -425,6 +500,20 @@ void SSBMod::apply() m_cwKeyer.setSampleRate(m_config.m_audioSampleRate); } + if (m_config.m_dsb != m_running.m_dsb) + { + if (m_config.m_dsb) + { + memset(m_DSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen)); + m_DSBFilterBufferIndex = 0; + } + else + { + memset(m_SSBFilterBuffer, 0, sizeof(Complex)*(m_ssbFftLen>>1)); + m_SSBFilterBufferIndex = 0; + } + } + m_running.m_outputSampleRate = m_config.m_outputSampleRate; m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset; m_running.m_bandwidth = m_config.m_bandwidth; diff --git a/plugins/channeltx/modssb/ssbmod.h b/plugins/channeltx/modssb/ssbmod.h index f0c857099..d0fda3250 100644 --- a/plugins/channeltx/modssb/ssbmod.h +++ b/plugins/channeltx/modssb/ssbmod.h @@ -344,6 +344,10 @@ private: bool m_interpolatorConsumed; fftfilt* m_SSBFilter; fftfilt* m_DSBFilter; + Complex* m_SSBFilterBuffer; + Complex* m_DSBFilterBuffer; + int m_SSBFilterBufferIndex; + int m_DSBFilterBufferIndex; static const int m_ssbFftLen; Real m_magsq; diff --git a/sdrbase/dsp/nco.cpp b/sdrbase/dsp/nco.cpp index c06c31a53..f64648b89 100644 --- a/sdrbase/dsp/nco.cpp +++ b/sdrbase/dsp/nco.cpp @@ -59,6 +59,12 @@ Complex NCO::nextIQ() return Complex(m_table[m_phase], -m_table[(m_phase + TableSize / 4) % TableSize]); } +Complex NCO::nextQI() +{ + nextPhase(); + return Complex(-m_table[(m_phase + TableSize / 4) % TableSize], m_table[m_phase]); +} + float NCO::get() { return m_table[m_phase]; @@ -74,3 +80,14 @@ void NCO::getIQ(Complex& c) c.real(m_table[m_phase]); c.imag(-m_table[(m_phase + TableSize / 4) % TableSize]); } + +Complex NCO::getQI() +{ + return Complex(-m_table[(m_phase + TableSize / 4) % TableSize], m_table[m_phase]); +} + +void NCO::getQI(Complex& c) +{ + c.imag(m_table[m_phase]); + c.real(-m_table[(m_phase + TableSize / 4) % TableSize]); +} diff --git a/sdrbase/dsp/nco.h b/sdrbase/dsp/nco.h index 3d98a4724..8ea258414 100644 --- a/sdrbase/dsp/nco.h +++ b/sdrbase/dsp/nco.h @@ -51,9 +51,12 @@ public: Real next(); //!< Return next real sample Complex nextIQ(); //!< Return next complex sample + Complex nextQI(); //!< Return next complex sample (reversed) Real get(); //!< Return current real sample (no phase increment) Complex getIQ(); //!< Return current complex sample (no phase increment) void getIQ(Complex& c); //!< Sets to the current complex sample (no phase increment) + Complex getQI(); //!< Return current complex sample (no phase increment, reversed) + void getQI(Complex& c); //!< Sets to the current complex sample (no phase increment, reversed) }; #endif // INCLUDE_NCO_H