diff --git a/plugins/channelrx/demodatv/atvdemod.cpp b/plugins/channelrx/demodatv/atvdemod.cpp index 8fbf1a4c6..d34d5ff8e 100644 --- a/plugins/channelrx/demodatv/atvdemod.cpp +++ b/plugins/channelrx/demodatv/atvdemod.cpp @@ -63,6 +63,7 @@ ATVDemod::ATVDemod() : m_intSynchroPoints=0; m_intNumberOfLines=0; m_intNumberOfRowsToDisplay=0; + m_intTVSampleRate = 0; m_objMagSqAverage.resize(32, 1.0); @@ -190,7 +191,10 @@ void ATVDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto fltI = it->real(); fltQ = it->imag(); #endif - + if (m_objRFRunning.m_intFrequencyOffset != 0) + { + m_nco.nextIQMul(fltI, fltQ); + } //********** demodulation ********** @@ -494,9 +498,11 @@ bool ATVDemod::handleMessage(const Message& cmd) { DownChannelizer::MsgChannelizerNotification& objNotif = (DownChannelizer::MsgChannelizerNotification&) cmd; m_objConfig.m_intSampleRate = objNotif.getSampleRate(); + m_objRFConfig.m_intFrequencyOffset = objNotif.getFrequencyOffset(); qDebug() << "ATVDemod::handleMessage: MsgChannelizerNotification:" - << " m_intMsps: " << m_objConfig.m_intSampleRate; + << " m_intSampleRate: " << m_objConfig.m_intSampleRate + << " m_intFrequencyOffset: " << m_objRFConfig.m_intFrequencyOffset; applySettings(); @@ -553,6 +559,32 @@ void ATVDemod::applySettings() return; } + if((m_objRFConfig.m_intFrequencyOffset != m_objRFRunning.m_intFrequencyOffset) || + (m_objConfig.m_intSampleRate != m_objRunning.m_intSampleRate)) + { + m_nco.setFreq(-m_objRFConfig.m_intFrequencyOffset, m_objConfig.m_intSampleRate); + } + + if ((m_objConfig.m_intSampleRate != m_objRunning.m_intSampleRate) + || (m_objRFConfig.m_fltRFBandwidth != m_objRFRunning.m_fltRFBandwidth)) + { + m_intTVSampleRate = (m_objConfig.m_intSampleRate / 1000000) * 1000000; // make sure working sample rate is a multiple of rate units + + if (m_intTVSampleRate > 0) + { + m_interpolatorDistance = (Real) m_intTVSampleRate / (Real) m_objConfig.m_intSampleRate; + } + else + { + m_intTVSampleRate = m_objConfig.m_intSampleRate; + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = 1.0f; + } + + m_interpolatorDistanceRemain = 0; + m_interpolator.create(48, m_intTVSampleRate, m_objRFConfig.m_fltRFBandwidth / 2.2, 3.0); + } + if((m_objConfig.m_fltFramePerS != m_objRunning.m_fltFramePerS) || (m_objConfig.m_fltLineDurationUs != m_objRunning.m_fltLineDurationUs) || (m_objConfig.m_intSampleRate != m_objRunning.m_intSampleRate) diff --git a/plugins/channelrx/demodatv/atvdemod.h b/plugins/channelrx/demodatv/atvdemod.h index 19deb84a6..ebb1482e3 100644 --- a/plugins/channelrx/demodatv/atvdemod.h +++ b/plugins/channelrx/demodatv/atvdemod.h @@ -78,6 +78,7 @@ public: struct ATVRFConfig { + int64_t m_intFrequencyOffset; ATVModulation m_enmModulation; float m_fltRFBandwidth; float m_fltRFOppBandwidth; @@ -85,6 +86,7 @@ public: bool m_blndecimatorEnable; ATVRFConfig() : + m_intFrequencyOffset(0), m_enmModulation(ATV_FM1), m_fltRFBandwidth(0), m_fltRFOppBandwidth(0), @@ -252,6 +254,9 @@ private: MovingAverage m_objMagSqAverage; + int m_intTVSampleRate; + NCO m_nco; + // Interpolator group for decimation and/or double sideband RF filtering Interpolator m_interpolator; Real m_interpolatorDistance; diff --git a/plugins/channelrx/demodatv/atvdemodgui.cpp b/plugins/channelrx/demodatv/atvdemodgui.cpp index b8b640710..aa55b0be9 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.cpp +++ b/plugins/channelrx/demodatv/atvdemodgui.cpp @@ -296,7 +296,7 @@ ATVDemodGUI::ATVDemodGUI(PluginAPI* objPluginAPI, DeviceSourceAPI *objDeviceAPI, m_objChannelMarker.setCenterFrequency(0); m_objChannelMarker.setVisible(true); - //connect(&m_objChannelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); + connect(&m_objChannelMarker, SIGNAL(changed()), this, SLOT(viewChanged())); m_objDeviceAPI->registerChannelInstance(m_strChannelID, this); m_objDeviceAPI->addChannelMarker(&m_objChannelMarker); @@ -333,7 +333,6 @@ void ATVDemodGUI::applySettings() ui->deltaFrequencyMinus->setChecked(m_objChannelMarker.getCenterFrequency() < 0); m_objChannelizer->configure(m_objChannelizer->getInputMessageQueue(), - //m_objATVDemod->GetSampleRate(), m_objChannelizer->getInputSampleRate(), // always use maximum available bandwidth m_objChannelMarker.getCenterFrequency()); @@ -584,3 +583,23 @@ void ATVDemodGUI::on_decimator_toggled(bool checked) applyRFSettings(); } +void ATVDemodGUI::on_deltaFrequency_changed(quint64 value) +{ + if (ui->deltaFrequencyMinus->isChecked()) { + m_objChannelMarker.setCenterFrequency(-value); + } else { + m_objChannelMarker.setCenterFrequency(value); + } +} + +void ATVDemodGUI::on_deltaFrequencyMinus_toggled(bool minus) +{ + int deltaFrequency = m_objChannelMarker.getCenterFrequency(); + bool minusDelta = (deltaFrequency < 0); + + if (minus ^ minusDelta) // sign change + { + m_objChannelMarker.setCenterFrequency(-deltaFrequency); + } +} + diff --git a/plugins/channelrx/demodatv/atvdemodgui.h b/plugins/channelrx/demodatv/atvdemodgui.h index 966535415..a627845e3 100644 --- a/plugins/channelrx/demodatv/atvdemodgui.h +++ b/plugins/channelrx/demodatv/atvdemodgui.h @@ -76,6 +76,8 @@ private slots: void on_rfOppBW_valueChanged(int value); void on_rfFiltering_toggled(bool checked); void on_decimator_toggled(bool checked); + void on_deltaFrequency_changed(quint64 value); + void on_deltaFrequencyMinus_toggled(bool minus); private: Ui::ATVDemodGUI* ui; diff --git a/sdrbase/dsp/nco.cpp b/sdrbase/dsp/nco.cpp index f64648b89..fc0866245 100644 --- a/sdrbase/dsp/nco.cpp +++ b/sdrbase/dsp/nco.cpp @@ -65,6 +65,17 @@ Complex NCO::nextQI() return Complex(-m_table[(m_phase + TableSize / 4) % TableSize], m_table[m_phase]); } +void NCO::nextIQMul(Real& i, Real& q) +{ + nextPhase(); + Real x = i; + Real y = q; + const Real& u = m_table[m_phase]; + const Real& v = -m_table[(m_phase + TableSize / 4) % TableSize]; + i = x*u - y*v; + q = x*v + y*u; +} + float NCO::get() { return m_table[m_phase]; diff --git a/sdrbase/dsp/nco.h b/sdrbase/dsp/nco.h index 8ea258414..58739fcbc 100644 --- a/sdrbase/dsp/nco.h +++ b/sdrbase/dsp/nco.h @@ -52,6 +52,7 @@ public: Real next(); //!< Return next real sample Complex nextIQ(); //!< Return next complex sample Complex nextQI(); //!< Return next complex sample (reversed) + void nextIQMul(Real& i, Real& q); //!< multiply I,Q separately with next sample 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)