1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-23 01:55:48 -05:00

AM demod: synchronous AM: implemented sidebands selection

This commit is contained in:
f4exb 2018-05-13 22:30:50 +02:00
parent e9f64a05f2
commit 21840c5dd3
19 changed files with 372 additions and 20 deletions

4
debian/changelog vendored
View File

@ -1,8 +1,10 @@
sdrangel (3.14.7-1) unstable; urgency=medium sdrangel (3.14.7-1) unstable; urgency=medium
* ChanelAnalyzerNG: added PLL option * ChanelAnalyzerNG: added PLL option
* RTL-SDR: fixed inf/sup decimators
* AM demod: syncrhronous AM detection option
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 13 May 2018 20:14:18 +0200 -- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 20 May 2018 20:14:18 +0200
sdrangel (3.14.6-1) unstable; urgency=medium sdrangel (3.14.6-1) unstable; urgency=medium

View File

@ -35,7 +35,6 @@ const QString ChannelAnalyzerNG::m_channelId = "ChannelAnalyzerNG";
ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) : ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) :
ChannelSinkAPI(m_channelIdURI), ChannelSinkAPI(m_channelIdURI),
m_deviceAPI(deviceAPI), m_deviceAPI(deviceAPI),
m_pll(0,0.05,0.01),
m_sampleSink(0), m_sampleSink(0),
m_settingsMutex(QMutex::Recursive) m_settingsMutex(QMutex::Recursive)
{ {
@ -50,6 +49,7 @@ ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) :
m_interpolatorDistanceRemain = 0.0f; m_interpolatorDistanceRemain = 0.0f;
SSBFilter = new fftfilt(m_config.m_LowCutoff / m_config.m_inputSampleRate, m_config.m_Bandwidth / m_config.m_inputSampleRate, ssbFftLen); SSBFilter = new fftfilt(m_config.m_LowCutoff / m_config.m_inputSampleRate, m_config.m_Bandwidth / m_config.m_inputSampleRate, ssbFftLen);
DSBFilter = new fftfilt(m_config.m_Bandwidth / m_config.m_inputSampleRate, 2*ssbFftLen); DSBFilter = new fftfilt(m_config.m_Bandwidth / m_config.m_inputSampleRate, 2*ssbFftLen);
m_pll.computeCoefficients(0.05f, 0.707f, 1000.0f); // bandwidth, damping factor, loop gain
apply(true); apply(true);
@ -249,6 +249,13 @@ void ChannelAnalyzerNG::apply(bool force)
m_settingsMutex.unlock(); m_settingsMutex.unlock();
} }
if (m_running.m_pll != m_config.m_pll || force)
{
if (m_config.m_pll) {
m_pll.reset();
}
}
m_running.m_frequency = m_config.m_frequency; m_running.m_frequency = m_config.m_frequency;
m_running.m_channelSampleRate = m_config.m_channelSampleRate; m_running.m_channelSampleRate = m_config.m_channelSampleRate;
m_running.m_inputSampleRate = m_config.m_inputSampleRate; m_running.m_inputSampleRate = m_config.m_inputSampleRate;

View File

@ -25,7 +25,7 @@
#include "dsp/interpolator.h" #include "dsp/interpolator.h"
#include "dsp/ncof.h" #include "dsp/ncof.h"
#include "dsp/fftfilt.h" #include "dsp/fftfilt.h"
#include "dsp/phaselock.h" #include "dsp/phaselockcomplex.h"
#include "audio/audiofifo.h" #include "audio/audiofifo.h"
#include "util/message.h" #include "util/message.h"
@ -148,6 +148,9 @@ public:
int getChannelSampleRate() const { return m_running.m_channelSampleRate; } int getChannelSampleRate() const { return m_running.m_channelSampleRate; }
double getMagSq() const { return m_magsq; } double getMagSq() const { return m_magsq; }
bool isPllLocked() const { return m_running.m_pll && m_pll.locked(); } bool isPllLocked() const { return m_running.m_pll && m_pll.locked(); }
Real getPllFrequency() const { return m_pll.getFrequency(); }
Real getPllDeltaPhase() const { return m_pll.getDeltaPhi(); }
Real getPllPhase() const { return m_pll.getPhiHat(); }
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
virtual void start(); virtual void start();
@ -203,7 +206,7 @@ private:
bool m_useInterpolator; bool m_useInterpolator;
NCOF m_nco; NCOF m_nco;
SimplePhaseLock m_pll; PhaseLockComplex m_pll;
Interpolator m_interpolator; Interpolator m_interpolator;
Real m_interpolatorDistance; Real m_interpolatorDistance;
Real m_interpolatorDistanceRemain; Real m_interpolatorDistanceRemain;
@ -247,11 +250,13 @@ private:
if (m_running.m_pll) if (m_running.m_pll)
{ {
Real ncopll[2]; m_pll.feed(re, im);
m_pll.process(re, im, ncopll);
Real mixI = m_sum.real() * ncopll[0] - m_sum.imag() * ncopll[1]; // Use -fPLL to mix (exchange PLL real and image in the complex multiplication)
Real mixQ = m_sum.real() * ncopll[1] + m_sum.imag() * ncopll[0]; Real mixI = m_sum.real() * m_pll.getImag() - m_sum.imag() * m_pll.getReal();
Real mixQ = m_sum.real() * m_pll.getReal() + m_sum.imag() * m_pll.getImag();
// Real mixI = m_pll.getReal() * SDR_RX_SCALED;
// Real mixQ = m_pll.getImag() * SDR_RX_SCALED;
if (m_running.m_ssb & !m_usb) if (m_running.m_ssb & !m_usb)
{ // invert spectrum for LSB { // invert spectrum for LSB

View File

@ -243,6 +243,15 @@ void ChannelAnalyzerNGGUI::tick()
} else { } else {
ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
} }
if (ui->pll->isChecked())
{
int fHz = round(m_channelAnalyzer->getPllFrequency()*m_rate);
ui->pll->setToolTip(tr("PLL lock (f:%1 Hz e:%2 rad p:%3 rad)")
.arg(fHz)
.arg(m_channelAnalyzer->getPllDeltaPhase())
.arg(m_channelAnalyzer->getPllPhase()));
}
} }
void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value) void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
@ -257,8 +266,13 @@ void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
} }
} }
void ChannelAnalyzerNGGUI::on_pll_toggled(bool checked __attribute__((unused))) void ChannelAnalyzerNGGUI::on_pll_toggled(bool checked)
{ {
if (!checked && m_usePll) {
ui->pll->setToolTip("PLL lock");
}
m_usePll = checked;
applySettings(); applySettings();
} }
@ -399,7 +413,8 @@ ChannelAnalyzerNGGUI::ChannelAnalyzerNGGUI(PluginAPI* pluginAPI, DeviceUISet *de
m_channelMarker(this), m_channelMarker(this),
m_doApplySettings(true), m_doApplySettings(true),
m_rate(6000), m_rate(6000),
m_spanLog2(0) m_spanLog2(0),
m_usePll(false)
{ {
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true); setAttribute(Qt::WA_DeleteOnClose, true);

View File

@ -66,6 +66,7 @@ private:
bool m_doApplySettings; bool m_doApplySettings;
int m_rate; //!< sample rate after final in-channel decimation (spanlog2) int m_rate; //!< sample rate after final in-channel decimation (spanlog2)
int m_spanLog2; int m_spanLog2;
bool m_usePll;
MovingAverageUtil<Real, double, 40> m_channelPowerDbAvg; MovingAverageUtil<Real, double, 40> m_channelPowerDbAvg;
ChannelAnalyzerNG* m_channelAnalyzer; ChannelAnalyzerNG* m_channelAnalyzer;

View File

@ -7,6 +7,7 @@ set(am_SOURCES
amdemodgui.cpp amdemodgui.cpp
amdemodsettings.cpp amdemodsettings.cpp
amdemodplugin.cpp amdemodplugin.cpp
amdemodssbdialog.cpp
) )
set(am_HEADERS set(am_HEADERS
@ -14,10 +15,12 @@ set(am_HEADERS
amdemodgui.h amdemodgui.h
amdemodsettings.h amdemodsettings.h
amdemodplugin.h amdemodplugin.h
amdemodssbdialog.h
) )
set(am_FORMS set(am_FORMS
amdemodgui.ui amdemodgui.ui
amdemodssb.ui
) )
include_directories( include_directories(

View File

@ -67,6 +67,7 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue());
m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate();
DSBFilter = new fftfilt((2.0f * m_settings.m_rfBandwidth) / m_audioSampleRate, 2 * 1024); DSBFilter = new fftfilt((2.0f * m_settings.m_rfBandwidth) / m_audioSampleRate, 2 * 1024);
SSBFilter = new fftfilt(0.0f, m_settings.m_rfBandwidth / m_audioSampleRate, 1024);
applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true);
applySettings(m_settings, true); applySettings(m_settings, true);
@ -89,6 +90,7 @@ AMDemod::~AMDemod()
delete m_threadedChannelizer; delete m_threadedChannelizer;
delete m_channelizer; delete m_channelizer;
delete DSBFilter; delete DSBFilter;
delete SSBFilter;
} }
void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused))) void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused)))
@ -233,6 +235,7 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
m_inputMessageQueue.push(channelConfigMsg); m_inputMessageQueue.push(channelConfigMsg);
m_settingsMutex.lock(); m_settingsMutex.lock();
m_interpolator.create(16, m_inputSampleRate, m_settings.m_rfBandwidth / 2.2f); m_interpolator.create(16, m_inputSampleRate, m_settings.m_rfBandwidth / 2.2f);
m_interpolatorDistanceRemain = 0; m_interpolatorDistanceRemain = 0;
m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate;
@ -241,8 +244,14 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
m_squelchDelayLine.resize(sampleRate/5); m_squelchDelayLine.resize(sampleRate/5);
DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate); DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate);
m_pllFilt.create(101, sampleRate, 500.0); m_pllFilt.create(101, sampleRate, 500.0);
m_settingsMutex.unlock();
if (m_settings.m_pll) {
m_volumeAGC.resizeNew(sampleRate, 0.003);
} else {
m_volumeAGC.resizeNew(sampleRate/10, 0.003);
}
m_settingsMutex.unlock();
m_audioSampleRate = sampleRate; m_audioSampleRate = sampleRate;
} }
@ -282,6 +291,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
<< " m_bandpassEnable: " << settings.m_bandpassEnable << " m_bandpassEnable: " << settings.m_bandpassEnable
<< " m_audioDeviceName: " << settings.m_audioDeviceName << " m_audioDeviceName: " << settings.m_audioDeviceName
<< " m_pll: " << settings.m_pll << " m_pll: " << settings.m_pll
<< " m_syncAMOperation: " << (int) settings.m_syncAMOperation
<< " force: " << force; << " force: " << force;
if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) ||
@ -314,6 +324,23 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
} }
} }
if ((m_settings.m_pll != settings.m_pll) || force)
{
if (settings.m_pll)
{
m_volumeAGC.resizeNew(m_audioSampleRate/4, 0.003);
m_syncAMBuffIndex = 0;
}
else
{
m_volumeAGC.resizeNew(m_audioSampleRate/10, 0.003);
}
}
if ((m_settings.m_syncAMOperation != settings.m_syncAMOperation) || force) {
m_syncAMBuffIndex = 0;
}
m_settings = settings; m_settings = settings;
} }

View File

@ -167,11 +167,12 @@ private:
int m_magsqCount; int m_magsqCount;
MovingAverageUtil<Real, double, 16> m_movingAverage; MovingAverageUtil<Real, double, 16> m_movingAverage;
SimpleAGC<4096> m_volumeAGC; SimpleAGC<4800> m_volumeAGC;
Bandpass<Real> m_bandpass; Bandpass<Real> m_bandpass;
Lowpass<std::complex<float> > m_pllFilt; Lowpass<std::complex<float> > m_pllFilt;
PhaseLockComplex m_pll; PhaseLockComplex m_pll;
fftfilt* DSBFilter; fftfilt* DSBFilter;
fftfilt* SSBFilter;
Real m_syncAMBuff[2*1024]; Real m_syncAMBuff[2*1024];
uint32_t m_syncAMBuffIndex; uint32_t m_syncAMBuffIndex;
@ -238,16 +239,29 @@ private:
fftfilt::cmplx *sideband; fftfilt::cmplx *sideband;
std::complex<float> cs(yr, yi); std::complex<float> cs(yr, yi);
int n_out = DSBFilter->runDSB(cs, &sideband, false); int n_out;
if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMDSB) {
n_out = DSBFilter->runDSB(cs, &sideband, false);
} else {
n_out = SSBFilter->runSSB(cs, &sideband, m_settings.m_syncAMOperation == AMDemodSettings::SyncAMUSB, false);
}
for (int i = 0; i < n_out; i++) for (int i = 0; i < n_out; i++)
{ {
m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag()); if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMDSB) {
m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag())/2.0f;
} else if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMUSB) {
m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag());
} else {
m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag());
}
m_syncAMBuffIndex = 0; m_syncAMBuffIndex = 0;
} }
m_syncAMBuffIndex = m_syncAMBuffIndex < 2*1024 ? m_syncAMBuffIndex : 0; m_syncAMBuffIndex = m_syncAMBuffIndex < 2*1024 ? m_syncAMBuffIndex : 0;
demod = m_syncAMBuff[m_syncAMBuffIndex++]*0.7*(SDR_RX_SCALEF/602.0f); demod = m_syncAMBuff[m_syncAMBuffIndex++]*(SDR_RX_SCALEF/602.0f);
m_volumeAGC.feed(demod); m_volumeAGC.feed(demod);
demod /= (10.0*m_volumeAGC.getValue()); demod /= (10.0*m_volumeAGC.getValue());
} }

View File

@ -18,6 +18,7 @@
#include <QMainWindow> #include <QMainWindow>
#include "amdemodgui.h" #include "amdemodgui.h"
#include "amdemodssbdialog.h"
#include "device/devicesourceapi.h" #include "device/devicesourceapi.h"
#include "device/deviceuiset.h" #include "device/deviceuiset.h"
@ -149,6 +150,12 @@ void AMDemodGUI::on_pll_toggled(bool checked)
applySettings(); applySettings();
} }
void AMDemodGUI::on_ssb_toggled(bool checked)
{
m_settings.m_syncAMOperation = checked ? m_samUSB ? AMDemodSettings::SyncAMUSB : AMDemodSettings::SyncAMLSB : AMDemodSettings::SyncAMDSB;
applySettings();
}
void AMDemodGUI::on_bandpassEnable_toggled(bool checked) void AMDemodGUI::on_bandpassEnable_toggled(bool checked)
{ {
m_settings.m_bandpassEnable = checked; m_settings.m_bandpassEnable = checked;
@ -215,6 +222,7 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
m_channelMarker(this), m_channelMarker(this),
m_doApplySettings(true), m_doApplySettings(true),
m_squelchOpen(false), m_squelchOpen(false),
m_samUSB(true),
m_tickCount(0) m_tickCount(0)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -230,6 +238,9 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute);
connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect())); connect(audioMuteRightClickEnabler, SIGNAL(rightClick()), this, SLOT(audioSelect()));
CRightClickEnabler *samSidebandRightClickEnabler = new CRightClickEnabler(ui->ssb);
connect(samSidebandRightClickEnabler, SIGNAL(rightClick()), this, SLOT(samSSBSelect()));
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
@ -254,6 +265,11 @@ AMDemodGUI::AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_iconDSBUSB.addPixmap(QPixmap("://dsb.png"), QIcon::Normal, QIcon::Off);
m_iconDSBUSB.addPixmap(QPixmap("://usb.png"), QIcon::Normal, QIcon::On);
m_iconDSBLSB.addPixmap(QPixmap("://dsb.png"), QIcon::Normal, QIcon::Off);
m_iconDSBLSB.addPixmap(QPixmap("://lsb.png"), QIcon::Normal, QIcon::On);
displaySettings(); displaySettings();
applySettings(true); applySettings(true);
} }
@ -314,6 +330,20 @@ void AMDemodGUI::displaySettings()
ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable); ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable);
ui->pll->setChecked(m_settings.m_pll); ui->pll->setChecked(m_settings.m_pll);
if (m_settings.m_pll) {
if (m_settings.m_syncAMOperation == AMDemodSettings::SyncAMLSB) {
m_samUSB = false;
ui->ssb->setIcon(m_iconDSBLSB);
} else {
m_samUSB = true;
ui->ssb->setIcon(m_iconDSBUSB);
}
}
else
{
ui->ssb->setIcon(m_iconDSBUSB);
}
blockApplySettings(false); blockApplySettings(false);
} }
@ -340,6 +370,26 @@ void AMDemodGUI::audioSelect()
} }
} }
void AMDemodGUI::samSSBSelect()
{
AMDemodSSBDialog ssbSelect(m_samUSB);
ssbSelect.exec();
ui->ssb->setIcon(ssbSelect.isUsb() ? m_iconDSBUSB : m_iconDSBLSB);
if (ssbSelect.isUsb() != m_samUSB)
{
qDebug("AMDemodGUI::samSSBSelect: %s", ssbSelect.isUsb() ? "usb" : "lsb");
m_samUSB = ssbSelect.isUsb();
if (m_settings.m_syncAMOperation != AMDemodSettings::SyncAMDSB)
{
m_settings.m_syncAMOperation = m_samUSB ? AMDemodSettings::SyncAMUSB : AMDemodSettings::SyncAMLSB;
applySettings();
}
}
}
void AMDemodGUI::tick() void AMDemodGUI::tick()
{ {
double magsqAvg, magsqPeak; double magsqAvg, magsqPeak;

View File

@ -1,7 +1,9 @@
#ifndef INCLUDE_AMDEMODGUI_H #ifndef INCLUDE_AMDEMODGUI_H
#define INCLUDE_AMDEMODGUI_H #define INCLUDE_AMDEMODGUI_H
#include <plugin/plugininstancegui.h> #include <QIcon>
#include "plugin/plugininstancegui.h"
#include "gui/rollupwidget.h" #include "gui/rollupwidget.h"
#include "dsp/channelmarker.h" #include "dsp/channelmarker.h"
#include "dsp/movingaverage.h" #include "dsp/movingaverage.h"
@ -50,9 +52,13 @@ private:
AMDemod* m_amDemod; AMDemod* m_amDemod;
bool m_squelchOpen; bool m_squelchOpen;
bool m_samUSB;
uint32_t m_tickCount; uint32_t m_tickCount;
MessageQueue m_inputMessageQueue; MessageQueue m_inputMessageQueue;
QIcon m_iconDSBUSB;
QIcon m_iconDSBLSB;
explicit AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); explicit AMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~AMDemodGUI(); virtual ~AMDemodGUI();
@ -66,6 +72,7 @@ private:
private slots: private slots:
void on_deltaFrequency_changed(qint64 value); void on_deltaFrequency_changed(qint64 value);
void on_pll_toggled(bool checked); void on_pll_toggled(bool checked);
void on_ssb_toggled(bool checked);
void on_bandpassEnable_toggled(bool checked); void on_bandpassEnable_toggled(bool checked);
void on_rfBW_valueChanged(int value); void on_rfBW_valueChanged(int value);
void on_volume_valueChanged(int value); void on_volume_valueChanged(int value);
@ -75,6 +82,7 @@ private slots:
void onMenuDialogCalled(const QPoint& p); void onMenuDialogCalled(const QPoint& p);
void handleInputMessages(); void handleInputMessages();
void audioSelect(); void audioSelect();
void samSSBSelect();
void tick(); void tick();
}; };

View File

@ -154,6 +154,24 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="ssb">
<property name="toolTip">
<string>Synchronous AM SSB/DSB toggle right click to select side band</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/dsb.png</normaloff>
<normalon>:/usb.png</normalon>:/dsb.png</iconset>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">

View File

@ -39,6 +39,7 @@ void AMDemodSettings::resetToDefaults()
m_title = "AM Demodulator"; m_title = "AM Demodulator";
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
m_pll = false; m_pll = false;
m_syncAMOperation = SyncAMDSB;
} }
QByteArray AMDemodSettings::serialize() const QByteArray AMDemodSettings::serialize() const
@ -58,6 +59,7 @@ QByteArray AMDemodSettings::serialize() const
s.writeString(9, m_title); s.writeString(9, m_title);
s.writeString(11, m_audioDeviceName); s.writeString(11, m_audioDeviceName);
s.writeBool(12, m_pll); s.writeBool(12, m_pll);
s.writeS32(13, (int) m_syncAMOperation);
return s.final(); return s.final();
} }
@ -96,6 +98,8 @@ bool AMDemodSettings::deserialize(const QByteArray& data)
d.readString(9, &m_title, "AM Demodulator"); d.readString(9, &m_title, "AM Demodulator");
d.readString(11, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName); d.readString(11, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
d.readBool(12, &m_pll, false); d.readBool(12, &m_pll, false);
d.readS32(13, &tmp, 0);
m_syncAMOperation = tmp < 0 ? SyncAMDSB : tmp > 2 ? SyncAMDSB : (SyncAMOperation) tmp;
return true; return true;
} }

View File

@ -23,6 +23,13 @@ class Serializable;
struct AMDemodSettings struct AMDemodSettings
{ {
enum SyncAMOperation
{
SyncAMDSB,
SyncAMUSB,
SyncAMLSB
};
qint32 m_inputFrequencyOffset; qint32 m_inputFrequencyOffset;
Real m_rfBandwidth; Real m_rfBandwidth;
Real m_squelch; Real m_squelch;
@ -34,6 +41,7 @@ struct AMDemodSettings
Serializable *m_channelMarker; Serializable *m_channelMarker;
QString m_audioDeviceName; QString m_audioDeviceName;
bool m_pll; bool m_pll;
SyncAMOperation m_syncAMOperation;
AMDemodSettings(); AMDemodSettings();
void resetToDefaults(); void resetToDefaults();

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AMDemodSSBDialog</class>
<widget class="QDialog" name="AMDemodSSBDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>199</width>
<height>115</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>SAM sideband selection</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="1" column="0">
<widget class="QRadioButton" name="lsb">
<property name="text">
<string>LSB</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="usb">
<property name="text">
<string>USB</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AMDemodSSBDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>203</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AMDemodSSBDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>314</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>203</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,41 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 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 "amdemodssbdialog.h"
#include "ui_amdemodssb.h"
AMDemodSSBDialog::AMDemodSSBDialog(bool usb, QWidget* parent) :
QDialog(parent),
ui(new Ui::AMDemodSSBDialog),
m_usb(usb)
{
ui->setupUi(this);
ui->usb->setChecked(usb);
ui->lsb->setChecked(!usb);
}
AMDemodSSBDialog::~AMDemodSSBDialog()
{
delete ui;
}
void AMDemodSSBDialog::accept()
{
m_usb = ui->usb->isChecked();
QDialog::accept();
}

View File

@ -0,0 +1,43 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2018 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 PLUGINS_CHANNELRX_DEMODAM_AMDEMODSSBDIALOG_H_
#define PLUGINS_CHANNELRX_DEMODAM_AMDEMODSSBDIALOG_H_
#include <QDialog>
namespace Ui {
class AMDemodSSBDialog;
}
class AMDemodSSBDialog : public QDialog
{
Q_OBJECT
public:
explicit AMDemodSSBDialog(bool usb, QWidget* parent = 0);
~AMDemodSSBDialog();
bool isUsb() const { return m_usb; }
private:
Ui::AMDemodSSBDialog* ui;
bool m_usb;
private slots:
void accept();
};
#endif /* PLUGINS_CHANNELRX_DEMODAM_AMDEMODSSBDIALOG_H_ */

View File

@ -89,6 +89,13 @@ public:
m_moving_average.resize(AvgSize, initial); m_moving_average.resize(AvgSize, initial);
} }
void resizeNew(uint32_t newSize, Real initial, Real cutoff=0, Real clip=0)
{
m_cutoff = cutoff;
m_clip = clip;
m_moving_average.resize(newSize, initial);
}
void fill(double value) void fill(double value)
{ {
m_moving_average.fill(value); m_moving_average.fill(value);

View File

@ -18,11 +18,12 @@
#include "recursivefilters.h" #include "recursivefilters.h"
#undef M_PI #undef M_PI
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
SecondOrderRecursiveFilter::SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r) : SecondOrderRecursiveFilter::SecondOrderRecursiveFilter(float samplingFrequency, float centerFrequency, float r) :
m_r(r), m_r(r),
m_frequencyRatio(centerFrequency/samplingFrequency) m_frequencyRatio(centerFrequency/samplingFrequency),
m_f(cos(2.0*M_PI*(m_frequencyRatio)))
{ {
init(); init();
} }
@ -33,6 +34,7 @@ SecondOrderRecursiveFilter::~SecondOrderRecursiveFilter()
void SecondOrderRecursiveFilter::setFrequencies(float samplingFrequency, float centerFrequency) void SecondOrderRecursiveFilter::setFrequencies(float samplingFrequency, float centerFrequency)
{ {
m_frequencyRatio = centerFrequency / samplingFrequency; m_frequencyRatio = centerFrequency / samplingFrequency;
m_f = cos(2.0*M_PI*m_frequencyRatio);
init(); init();
} }
@ -44,7 +46,7 @@ void SecondOrderRecursiveFilter::setR(float r)
short SecondOrderRecursiveFilter::run(short sample) 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]); m_v[0] = ((1.0f - m_r) * (float) sample) + (2.0f * m_r * m_f * m_v[1]) - (m_r * m_r * m_v[2]);
float y = m_v[0] - m_v[2]; float y = m_v[0] - m_v[2];
m_v[2] = m_v[1]; m_v[2] = m_v[1];
m_v[1] = m_v[0]; m_v[1] = m_v[0];
@ -54,7 +56,7 @@ short SecondOrderRecursiveFilter::run(short sample)
float SecondOrderRecursiveFilter::run(float sample) 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]); m_v[0] = ((1.0f - m_r) * sample) + (2.0f * m_r * m_f * m_v[1]) - (m_r * m_r * m_v[2]);
float y = m_v[0] - m_v[2]; float y = m_v[0] - m_v[2];
m_v[2] = m_v[1]; m_v[2] = m_v[1];
m_v[1] = m_v[0]; m_v[1] = m_v[0];

View File

@ -39,6 +39,7 @@ private:
float m_r; float m_r;
float m_frequencyRatio; float m_frequencyRatio;
float m_f;
float m_v[3]; float m_v[3];
}; };