1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 01:39:05 -05:00

ATV Demod: implemented BFU with PLL for single sideband modes

This commit is contained in:
f4exb 2017-03-19 10:28:54 +01:00
parent a8adc84e89
commit 303fd3ccc7
9 changed files with 375 additions and 73 deletions

View File

@ -126,6 +126,7 @@ set(sdrbase_SOURCES
sdrbase/dsp/basebandsamplesink.cpp
sdrbase/dsp/basebandsamplesource.cpp
sdrbase/dsp/nullsink.cpp
sdrbase/dsp/recursivefilters.cpp
sdrbase/dsp/spectrumscopecombovis.cpp
sdrbase/dsp/spectrumscopengcombovis.cpp
sdrbase/dsp/scopevis.cpp
@ -235,6 +236,7 @@ set(sdrbase_HEADERS
sdrbase/dsp/phasediscri.h
sdrbase/dsp/phaselock.h
sdrbase/dsp/pidcontroller.h
sdrbase/dsp/recursivefilters.h
sdrbase/dsp/samplesinkfifo.h
sdrbase/dsp/samplesourcefifo.h
sdrbase/dsp/samplesinkfifodoublebuffered.h

View File

@ -51,6 +51,8 @@ ATVDemod::ATVDemod() :
m_fltAmpDelta(1.0),
m_fltAmpLineAverage(0.0f),
m_intNumberSamplePerTop(0),
m_bfoPLL(200/1000000, 100/1000000, 0.01),
m_bfoFilter(200.0, 1000000.0, 0.9),
m_interpolatorDistanceRemain(0.0f),
m_interpolatorDistance(1.0f),
m_DSBFilter(0),
@ -113,14 +115,16 @@ void ATVDemod::configureRF(
float fltRFBandwidth,
float fltRFOppBandwidth,
bool blnFFTFiltering,
bool blnDecimatorEnable)
bool blnDecimatorEnable,
float fltBFOFrequency)
{
Message* msgCmd = MsgConfigureRFATVDemod::create(
enmModulation,
fltRFBandwidth,
fltRFOppBandwidth,
blnFFTFiltering,
blnDecimatorEnable);
blnDecimatorEnable,
fltBFOFrequency);
objMessageQueue->push(msgCmd);
}
@ -238,7 +242,7 @@ void ATVDemod::demod(Complex& c)
int n_out;
fftfilt::cmplx *filtered;
n_out = m_DSBFilter->runAsym(c, &filtered, m_objRFRunning.m_enmModulation != ATV_VAML); // all usb except explicitely lsb
n_out = m_DSBFilter->runAsym(c, &filtered, m_objRFRunning.m_enmModulation != ATV_LSB); // all usb except explicitely lsb
if (n_out > 0)
{
@ -308,7 +312,7 @@ void ATVDemod::demod(Complex& c)
m_fltBufferQ[0]=fltNormQ;
}
else if ((m_objRFRunning.m_enmModulation == ATV_AM) || (m_objRFRunning.m_enmModulation == ATV_VAMU) || (m_objRFRunning.m_enmModulation == ATV_VAML))
else if (m_objRFRunning.m_enmModulation == ATV_AM)
{
//Amplitude AM
fltVal = fltNorm;
@ -329,6 +333,39 @@ void ATVDemod::demod(Complex& c)
fltVal -= m_fltAmpMin;
fltVal /=m_fltAmpDelta;
}
else if ((m_objRFRunning.m_enmModulation == ATV_USB) || (m_objRFRunning.m_enmModulation == ATV_LSB))
{
Real bfoValues[2];
float fltFiltered = m_bfoFilter.run(fltI);
m_bfoPLL.process(fltFiltered, bfoValues);
// do the mix
float mixI = fltI * bfoValues[0] - fltQ * bfoValues[1];
float mixQ = fltI * bfoValues[1] + fltQ * bfoValues[0];
if (m_objRFRunning.m_enmModulation == ATV_USB) {
fltVal = (mixI + mixQ);
} else {
fltVal = (mixI - mixQ);
}
//********** Mini and Maxi Amplitude tracking **********
if(fltVal<m_fltEffMin)
{
m_fltEffMin=fltVal;
}
if(fltVal>m_fltEffMax)
{
m_fltEffMax=fltVal;
}
//Normalisation
fltVal -= m_fltAmpMin;
fltVal /=m_fltAmpDelta;
}
else
{
fltVal = 0.0f;
@ -560,14 +597,14 @@ bool ATVDemod::handleMessage(const Message& cmd)
m_objConfig = objCfg.m_objMsgConfig;
qDebug() << "ATVDemod::handleMessage: MsgConfigureATVDemod:"
<< " m_fltVoltLevelSynchroBlack" << m_objConfig.m_fltVoltLevelSynchroBlack
<< " m_fltVoltLevelSynchroTop" << m_objConfig.m_fltVoltLevelSynchroTop
<< " m_fltFramePerS" << m_objConfig.m_fltFramePerS
<< " m_fltLineDurationUs" << m_objConfig.m_fltLineDurationUs
<< " m_fltRatioOfRowsToDisplay" << m_objConfig.m_fltRatioOfRowsToDisplay
<< " m_fltTopDurationUs" << m_objConfig.m_fltTopDurationUs
<< " m_blnHSync" << m_objConfig.m_blnHSync
<< " m_blnVSync" << m_objConfig.m_blnVSync;
<< " m_fltVoltLevelSynchroBlack:" << m_objConfig.m_fltVoltLevelSynchroBlack
<< " m_fltVoltLevelSynchroTop:" << m_objConfig.m_fltVoltLevelSynchroTop
<< " m_fltFramePerS:" << m_objConfig.m_fltFramePerS
<< " m_fltLineDurationUs:" << m_objConfig.m_fltLineDurationUs
<< " m_fltRatioOfRowsToDisplay:" << m_objConfig.m_fltRatioOfRowsToDisplay
<< " m_fltTopDurationUs:" << m_objConfig.m_fltTopDurationUs
<< " m_blnHSync:" << m_objConfig.m_blnHSync
<< " m_blnVSync:" << m_objConfig.m_blnVSync;
applySettings();
@ -580,11 +617,12 @@ bool ATVDemod::handleMessage(const Message& cmd)
m_objRFConfig = objCfg.m_objMsgConfig;
qDebug() << "ATVDemod::handleMessage: MsgConfigureRFATVDemod:"
<< " m_enmModulation" << m_objRFConfig.m_enmModulation
<< " m_fltRFBandwidth" << m_objRFConfig.m_fltRFBandwidth
<< " m_fltRFOppBandwidth" << m_objRFConfig.m_fltRFOppBandwidth
<< " m_blnFFTFiltering" << m_objRFConfig.m_blnFFTFiltering
<< " m_blnDecimatorEnable" << m_objRFConfig.m_blndecimatorEnable;
<< " m_enmModulation:" << m_objRFConfig.m_enmModulation
<< " m_fltRFBandwidth:" << m_objRFConfig.m_fltRFBandwidth
<< " m_fltRFOppBandwidth:" << m_objRFConfig.m_fltRFOppBandwidth
<< " m_blnFFTFiltering:" << m_objRFConfig.m_blnFFTFiltering
<< " m_blnDecimatorEnable:" << m_objRFConfig.m_blndecimatorEnable
<< " m_fltBFOFrequency:" << m_objRFConfig.m_fltBFOFrequency;
applySettings();
@ -677,6 +715,14 @@ void ATVDemod::applySettings()
m_objSettingsMutex.unlock();
}
if ((m_objConfigPrivate.m_intTVSampleRate != m_objRunningPrivate.m_intTVSampleRate)
|| (m_objRFConfig.m_fltBFOFrequency != m_objRFRunning.m_fltBFOFrequency))
{
m_bfoPLL.configure(m_objRFConfig.m_fltBFOFrequency / m_objConfigPrivate.m_intTVSampleRate,
100.0 / m_objConfigPrivate.m_intTVSampleRate,
0.01);
m_bfoFilter.setFrequencies(m_objRFConfig.m_fltBFOFrequency, m_objConfigPrivate.m_intTVSampleRate);
}
m_objRunning = m_objConfig;
m_objRFRunning = m_objRFConfig;
@ -692,3 +738,15 @@ int ATVDemod::getEffectiveSampleRate()
{
return m_objRFRunning.m_blndecimatorEnable ? m_objRunningPrivate.m_intTVSampleRate : m_objRunning.m_intSampleRate;
}
bool ATVDemod::getBFOLocked()
{
if ((m_objRFRunning.m_enmModulation == ATV_USB) || (m_objRFRunning.m_enmModulation == ATV_LSB))
{
return m_bfoPLL.locked();
}
else
{
return false;
}
}

View File

@ -31,6 +31,8 @@
#include "dsp/movingaverage.h"
#include "dsp/fftfilt.h"
#include "dsp/agc.h"
#include "dsp/phaselock.h"
#include "dsp/recursivefilters.h"
#include "audio/audiofifo.h"
#include "util/message.h"
#include "atvscreen.h"
@ -46,8 +48,8 @@ public:
ATV_FM1, //!< Classical frequency modulation with discriminator #1
ATV_FM2, //!< Classical frequency modulation with discriminator #2
ATV_AM, //!< Classical amplitude modulation
ATV_VAMU, //!< AM with vestigial lower side band (main signal is in the upper side)
ATV_VAML //!< AM with vestigial upper side band (main signal is in the lower side)
ATV_USB, //!< AM with vestigial lower side band (main signal is in the upper side)
ATV_LSB //!< AM with vestigial upper side band (main signal is in the lower side)
};
struct ATVConfig
@ -84,6 +86,7 @@ public:
float m_fltRFOppBandwidth;
bool m_blnFFTFiltering;
bool m_blndecimatorEnable;
float m_fltBFOFrequency;
ATVRFConfig() :
m_intFrequencyOffset(0),
@ -91,7 +94,8 @@ public:
m_fltRFBandwidth(0),
m_fltRFOppBandwidth(0),
m_blnFFTFiltering(false),
m_blndecimatorEnable(false)
m_blndecimatorEnable(false),
m_fltBFOFrequency(0.0f)
{
}
};
@ -135,7 +139,8 @@ public:
float fltRFBandwidth,
float fltRFOppBandwidth,
bool blnFFTFiltering,
bool blndecimatorEnable);
bool blndecimatorEnable,
float fltBFOFrequency);
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po);
virtual void start();
@ -146,6 +151,7 @@ public:
int getSampleRate();
int getEffectiveSampleRate();
double getMagSq() const { return m_objMagSqAverage.average(); } //!< Beware this is scaled to 2^30
bool getBFOLocked();
private:
struct ATVConfigPrivate
@ -218,14 +224,16 @@ private:
float fltRFBandwidth,
float fltRFOppBandwidth,
bool blnFFTFiltering,
bool blndecimatorEnable)
bool blndecimatorEnable,
int intBFOFrequency)
{
return new MsgConfigureRFATVDemod(
enmModulation,
fltRFBandwidth,
fltRFOppBandwidth,
blnFFTFiltering,
blndecimatorEnable);
blndecimatorEnable,
intBFOFrequency);
}
ATVRFConfig m_objMsgConfig;
@ -236,7 +244,8 @@ private:
float fltRFBandwidth,
float fltRFOppBandwidth,
bool blnFFTFiltering,
bool blndecimatorEnable) :
bool blndecimatorEnable,
float fltBFOFrequency) :
Message()
{
m_objMsgConfig.m_enmModulation = enmModulation;
@ -244,6 +253,7 @@ private:
m_objMsgConfig.m_fltRFOppBandwidth = fltRFOppBandwidth;
m_objMsgConfig.m_blnFFTFiltering = blnFFTFiltering;
m_objMsgConfig.m_blndecimatorEnable = blndecimatorEnable;
m_objMsgConfig.m_fltBFOFrequency = fltBFOFrequency;
}
};
@ -286,6 +296,8 @@ private:
MovingAverage<double> m_objMagSqAverage;
NCO m_nco;
SimplePhaseLock m_bfoPLL;
SecondOrderRecursiveFilter m_bfoFilter;
// Interpolator group for decimation and/or double sideband RF filtering
Interpolator m_interpolator;

View File

@ -105,6 +105,7 @@ QByteArray ATVDemodGUI::serialize() const
s.writeBool(11, ui->halfImage->isChecked());
s.writeS32(12, ui->rfBW->value());
s.writeS32(13, ui->rfOppBW->value());
s.writeS32(14, ui->bfo->value());
return s.final();
}
@ -160,6 +161,8 @@ bool ATVDemodGUI::deserialize(const QByteArray& arrData)
ui->rfBW->setValue(tmp);
d.readS32(13, &tmp, 10);
ui->rfOppBW->setValue(tmp);
d.readS32(14, &tmp, 10);
ui->bfo->setValue(tmp);
blockApplySettings(false);
m_objChannelMarker.blockSignals(false);
@ -336,7 +339,8 @@ void ATVDemodGUI::applyRFSettings()
ui->rfBW->value() * 100000.0f,
ui->rfOppBW->value() * 100000.0f,
ui->rfFiltering->isChecked(),
ui->decimatorEnable->isChecked());
ui->decimatorEnable->isChecked(),
ui->bfo->value() * 10.0f);
}
}
@ -349,9 +353,9 @@ void ATVDemodGUI::setChannelMarkerBandwidth()
m_objChannelMarker.setBandwidth(ui->rfBW->value()*100000);
m_objChannelMarker.setOppositeBandwidth(ui->rfOppBW->value()*100000);
if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_VAML) {
if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_LSB) {
m_objChannelMarker.setSidebands(ChannelMarker::vlsb);
} else if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_VAMU) {
} else if (ui->modulation->currentIndex() == (int) ATVDemod::ATV_USB) {
m_objChannelMarker.setSidebands(ChannelMarker::vusb);
} else {
m_objChannelMarker.setSidebands(ChannelMarker::vusb);
@ -413,6 +417,12 @@ void ATVDemodGUI::tick()
m_objMagSqAverage.feed(m_objATVDemod->getMagSq());
double magSqDB = CalcDb::dbPower(m_objMagSqAverage.average() / (1<<30));
ui->channePowerText->setText(tr("%1 dB").arg(magSqDB, 0, 'f', 1));
if (m_objATVDemod->getBFOLocked()) {
ui->bfoLockedLabel->setStyleSheet("QLabel { background-color : green; }");
} else {
ui->bfoLockedLabel->setStyleSheet("QLabel { background:rgb(79,79,79); }");
}
}
m_intTickCount = 0;
@ -525,3 +535,9 @@ void ATVDemodGUI::on_deltaFrequencyMinus_toggled(bool minus)
}
}
void ATVDemodGUI::on_bfo_valueChanged(int value)
{
ui->bfoText->setText(QString("%1").arg(value * 10.0, 0, 'f', 0));
applyRFSettings();
}

View File

@ -79,6 +79,7 @@ private slots:
void on_decimatorEnable_toggled(bool checked);
void on_deltaFrequency_changed(quint64 value);
void on_deltaFrequencyMinus_toggled(bool minus);
void on_bfo_valueChanged(int value);
private:
Ui::ATVDemodGUI* ui;

View File

@ -40,7 +40,7 @@
<x>10</x>
<y>10</y>
<width>501</width>
<height>71</height>
<height>81</height>
</rect>
</property>
<property name="windowTitle">
@ -52,23 +52,6 @@
</property>
<item>
<layout class="QHBoxLayout" name="rfSettings1Layout">
<item>
<widget class="ButtonSwitch" name="decimatorEnable">
<property name="toolTip">
<string>Toggle decimator on/off</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrbase/resources/res.qrc">
<normaloff>:/arrow_down.png</normaloff>:/arrow_down.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="deltaFrequencyLayout">
<item>
@ -124,6 +107,30 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="decimatorEnable">
<property name="toolTip">
<string>Toggle decimator on/off</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrbase/resources/res.qrc">
<normaloff>:/arrow_down.png</normaloff>:/arrow_down.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="channelSampleRateText">
<property name="minimumSize">
@ -144,35 +151,77 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="modulation">
<property name="toolTip">
<string>Modulation type</string>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bfoLockedLabel">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../../sdrbase/resources/res.qrc">:/carrier.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="bfo">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>BFO frequency adjust</string>
</property>
<property name="minimum">
<number>-500</number>
</property>
<property name="maximum">
<number>500</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bfoText">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>BFO frequency (Hz)</string>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<item>
<property name="text">
<string>FM 1</string>
</property>
</item>
<item>
<property name="text">
<string>FM 2</string>
</property>
</item>
<item>
<property name="text">
<string>AM</string>
</property>
</item>
<item>
<property name="text">
<string>VAMU</string>
</property>
</item>
<item>
<property name="text">
<string>VAML</string>
</property>
</item>
</widget>
</item>
<item>
@ -211,6 +260,38 @@
</item>
<item>
<layout class="QHBoxLayout" name="rfSettings2Layout">
<item>
<widget class="QComboBox" name="modulation">
<property name="toolTip">
<string>Modulation type</string>
</property>
<item>
<property name="text">
<string>FM 1</string>
</property>
</item>
<item>
<property name="text">
<string>FM 2</string>
</property>
</item>
<item>
<property name="text">
<string>AM</string>
</property>
</item>
<item>
<property name="text">
<string>USB</string>
</property>
</item>
<item>
<property name="text">
<string>LSB</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="rfFiltering">
<property name="toolTip">

View File

@ -112,6 +112,24 @@ private:
std::vector<PpsEvent> m_pps_events;
};
class SimplePhaseLock : public PhaseLock
{
public:
SimplePhaseLock(Real freq, Real bandwidth, Real minsignal) :
PhaseLock(freq, bandwidth, minsignal)
{}
virtual ~SimplePhaseLock()
{}
protected:
virtual void processPhase(Real *samples_out) const
{
samples_out[0] = m_psin; // f Pilot
samples_out[1] = m_pcos; // f Pilot
}
};
class StereoPhaseLock : public PhaseLock
{
public:

View File

@ -0,0 +1,69 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// //
// 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 as version 3 of the License, or //
// //
// 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include "recursivefilters.h"
SecondOrderRecursiveFilter::SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r) :
m_r(r),
m_frequencyRatio(centerFrequency/samplingFrequency)
{
init();
}
SecondOrderRecursiveFilter::~SecondOrderRecursiveFilter()
{}
void SecondOrderRecursiveFilter::setFrequencies(float samplingFrequency, float centerFrequency)
{
m_frequencyRatio = centerFrequency / samplingFrequency;
init();
}
void SecondOrderRecursiveFilter::setR(float r)
{
m_r = r;
init();
}
short SecondOrderRecursiveFilter::run(short sample)
{
m_v[0] = ((1.0f - m_r) * (float) sample) + (2.0f * m_r * cos(2.0*M_PI*m_frequencyRatio) * m_v[1]) - (m_r * m_r * m_v[2]);
float y = m_v[0] - m_v[2];
m_v[2] = m_v[1];
m_v[1] = m_v[0];
return (short) y;
}
float SecondOrderRecursiveFilter::run(float sample)
{
m_v[0] = ((1.0f - m_r) * sample) + (2.0f * m_r * cos(2.0*M_PI*m_frequencyRatio) * m_v[1]) - (m_r * m_r * m_v[2]);
float y = m_v[0] - m_v[2];
m_v[2] = m_v[1];
m_v[1] = m_v[0];
return y;
}
void SecondOrderRecursiveFilter::init()
{
for (int i = 0; i < 3; i++)
{
m_v[i] = 0.0f;
}
}

View File

@ -0,0 +1,45 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 Edouard Griffiths, F4EXB //
// //
// 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 as version 3 of the License, or //
// //
// 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 V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DSP_RECURSIVEFILTERS_H_
#define SDRBASE_DSP_RECURSIVEFILTERS_H_
/**
* \Brief: This is a second order bandpass filter using recursive method. r is in range ]0..1[ the higher the steeper the filter.
* inspired by:http://www.ece.umd.edu/~tretter/commlab/c6713slides/FSKSlides.pdf
*/
class SecondOrderRecursiveFilter
{
public:
SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r);
~SecondOrderRecursiveFilter();
void setFrequencies(float samplingFrequency, float centerFrequency);
void setR(float r);
short run(short sample);
float run(float sample);
private:
void init();
float m_r;
float m_frequencyRatio;
float m_v[3];
};
#endif /* SDRBASE_DSP_RECURSIVEFILTERS_H_ */