mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
AM demod: basic synchronous AM detection option
This commit is contained in:
parent
1549ecaa0f
commit
e9f64a05f2
@ -66,6 +66,7 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
|
||||
|
||||
DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue());
|
||||
m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate();
|
||||
DSBFilter = new fftfilt((2.0f * m_settings.m_rfBandwidth) / m_audioSampleRate, 2 * 1024);
|
||||
|
||||
applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true);
|
||||
applySettings(m_settings, true);
|
||||
@ -74,6 +75,10 @@ AMDemod::AMDemod(DeviceSourceAPI *deviceAPI) :
|
||||
m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this);
|
||||
m_deviceAPI->addThreadedSink(m_threadedChannelizer);
|
||||
m_deviceAPI->addChannelAPI(this);
|
||||
|
||||
m_pllFilt.create(101, m_audioSampleRate, 500.0);
|
||||
m_pll.computeCoefficients(0.05, 0.707, 1000);
|
||||
m_syncAMBuffIndex = 0;
|
||||
}
|
||||
|
||||
AMDemod::~AMDemod()
|
||||
@ -83,6 +88,7 @@ AMDemod::~AMDemod()
|
||||
m_deviceAPI->removeThreadedSink(m_threadedChannelizer);
|
||||
delete m_threadedChannelizer;
|
||||
delete m_channelizer;
|
||||
delete DSBFilter;
|
||||
}
|
||||
|
||||
void AMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst __attribute__((unused)))
|
||||
@ -233,6 +239,8 @@ void AMDemod::applyAudioSampleRate(int sampleRate)
|
||||
m_bandpass.create(301, sampleRate, 300.0, m_settings.m_rfBandwidth / 2.0f);
|
||||
m_audioFifo.setSize(sampleRate);
|
||||
m_squelchDelayLine.resize(sampleRate/5);
|
||||
DSBFilter->create_dsb_filter((2.0f * m_settings.m_rfBandwidth) / (float) sampleRate);
|
||||
m_pllFilt.create(101, sampleRate, 500.0);
|
||||
m_settingsMutex.unlock();
|
||||
|
||||
m_audioSampleRate = sampleRate;
|
||||
@ -273,6 +281,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
|
||||
<< " m_audioMute: " << settings.m_audioMute
|
||||
<< " m_bandpassEnable: " << settings.m_bandpassEnable
|
||||
<< " m_audioDeviceName: " << settings.m_audioDeviceName
|
||||
<< " m_pll: " << settings.m_pll
|
||||
<< " force: " << force;
|
||||
|
||||
if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) ||
|
||||
@ -283,6 +292,7 @@ void AMDemod::applySettings(const AMDemodSettings& settings, bool force)
|
||||
m_interpolatorDistanceRemain = 0;
|
||||
m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate;
|
||||
m_bandpass.create(301, m_audioSampleRate, 300.0, settings.m_rfBandwidth / 2.0f);
|
||||
DSBFilter->create_dsb_filter((2.0f * settings.m_rfBandwidth) / (float) m_audioSampleRate);
|
||||
m_settingsMutex.unlock();
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,9 @@
|
||||
#include "util/movingaverage.h"
|
||||
#include "dsp/agc.h"
|
||||
#include "dsp/bandpass.h"
|
||||
#include "dsp/lowpass.h"
|
||||
#include "dsp/phaselockcomplex.h"
|
||||
#include "dsp/fftfilt.h"
|
||||
#include "audio/audiofifo.h"
|
||||
#include "util/message.h"
|
||||
#include "util/doublebufferfifo.h"
|
||||
@ -118,6 +121,7 @@ public:
|
||||
uint32_t getAudioSampleRate() const { return m_audioSampleRate; }
|
||||
double getMagSq() const { return m_magsq; }
|
||||
bool getSquelchOpen() const { return m_squelchOpen; }
|
||||
bool getPllLocked() const { return m_settings.m_pll && m_pll.locked(); }
|
||||
|
||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
|
||||
{
|
||||
@ -165,6 +169,11 @@ private:
|
||||
MovingAverageUtil<Real, double, 16> m_movingAverage;
|
||||
SimpleAGC<4096> m_volumeAGC;
|
||||
Bandpass<Real> m_bandpass;
|
||||
Lowpass<std::complex<float> > m_pllFilt;
|
||||
PhaseLockComplex m_pll;
|
||||
fftfilt* DSBFilter;
|
||||
Real m_syncAMBuff[2*1024];
|
||||
uint32_t m_syncAMBuffIndex;
|
||||
|
||||
AudioVector m_audioBuffer;
|
||||
uint32_t m_audioBufferFill;
|
||||
@ -217,9 +226,37 @@ private:
|
||||
|
||||
if (m_squelchOpen && !m_settings.m_audioMute)
|
||||
{
|
||||
Real demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20));
|
||||
m_volumeAGC.feed(demod);
|
||||
demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue();
|
||||
Real demod;
|
||||
|
||||
if (m_settings.m_pll)
|
||||
{
|
||||
std::complex<float> s(re, im);
|
||||
s = m_pllFilt.filter(s);
|
||||
m_pll.feed(s.real(), s.imag());
|
||||
float yr = re * m_pll.getImag() - im * m_pll.getReal();
|
||||
float yi = re * m_pll.getReal() + im * m_pll.getImag();
|
||||
|
||||
fftfilt::cmplx *sideband;
|
||||
std::complex<float> cs(yr, yi);
|
||||
int n_out = DSBFilter->runDSB(cs, &sideband, false);
|
||||
|
||||
for (int i = 0; i < n_out; i++)
|
||||
{
|
||||
m_syncAMBuff[i] = (sideband[i].real() + sideband[i].imag());
|
||||
m_syncAMBuffIndex = 0;
|
||||
}
|
||||
|
||||
m_syncAMBuffIndex = m_syncAMBuffIndex < 2*1024 ? m_syncAMBuffIndex : 0;
|
||||
demod = m_syncAMBuff[m_syncAMBuffIndex++]*0.7*(SDR_RX_SCALEF/602.0f);
|
||||
m_volumeAGC.feed(demod);
|
||||
demod /= (10.0*m_volumeAGC.getValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
demod = sqrt(m_squelchDelayLine.readBack(m_audioSampleRate/20));
|
||||
m_volumeAGC.feed(demod);
|
||||
demod = (demod - m_volumeAGC.getValue()) / m_volumeAGC.getValue();
|
||||
}
|
||||
|
||||
if (m_settings.m_bandpassEnable)
|
||||
{
|
||||
|
@ -139,6 +139,16 @@ void AMDemodGUI::on_deltaFrequency_changed(qint64 value)
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void AMDemodGUI::on_pll_toggled(bool checked)
|
||||
{
|
||||
if (!checked) {
|
||||
ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
|
||||
m_settings.m_pll = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void AMDemodGUI::on_bandpassEnable_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_bandpassEnable = checked;
|
||||
@ -302,6 +312,7 @@ void AMDemodGUI::displaySettings()
|
||||
|
||||
ui->audioMute->setChecked(m_settings.m_audioMute);
|
||||
ui->bandpassEnable->setChecked(m_settings.m_bandpassEnable);
|
||||
ui->pll->setChecked(m_settings.m_pll);
|
||||
|
||||
blockApplySettings(false);
|
||||
}
|
||||
@ -359,6 +370,15 @@ void AMDemodGUI::tick()
|
||||
}
|
||||
}
|
||||
|
||||
if (m_settings.m_pll)
|
||||
{
|
||||
if (m_amDemod->getPllLocked()) {
|
||||
ui->pll->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else {
|
||||
ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
}
|
||||
|
||||
m_tickCount++;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ private:
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_pll_toggled(bool checked);
|
||||
void on_bandpassEnable_toggled(bool checked);
|
||||
void on_rfBW_valueChanged(int value);
|
||||
void on_volume_valueChanged(int value);
|
||||
|
@ -129,6 +129,31 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="pll">
|
||||
<property name="toolTip">
|
||||
<string>PLL for synchronous AM</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/unlocked.png</normaloff>
|
||||
<normalon>:/locked.png</normalon>:/unlocked.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
const PluginDescriptor AMDemodPlugin::m_pluginDescriptor = {
|
||||
QString("AM Demodulator"),
|
||||
QString("3.14.5"),
|
||||
QString("3.14.7"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
@ -38,6 +38,7 @@ void AMDemodSettings::resetToDefaults()
|
||||
m_rgbColor = QColor(255, 255, 0).rgb();
|
||||
m_title = "AM Demodulator";
|
||||
m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName;
|
||||
m_pll = false;
|
||||
}
|
||||
|
||||
QByteArray AMDemodSettings::serialize() const
|
||||
@ -56,6 +57,7 @@ QByteArray AMDemodSettings::serialize() const
|
||||
s.writeBool(8, m_bandpassEnable);
|
||||
s.writeString(9, m_title);
|
||||
s.writeString(11, m_audioDeviceName);
|
||||
s.writeBool(12, m_pll);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@ -93,6 +95,7 @@ bool AMDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readBool(8, &m_bandpassEnable, false);
|
||||
d.readString(9, &m_title, "AM Demodulator");
|
||||
d.readString(11, &m_audioDeviceName, AudioDeviceManager::m_defaultDeviceName);
|
||||
d.readBool(12, &m_pll, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ struct AMDemodSettings
|
||||
QString m_title;
|
||||
Serializable *m_channelMarker;
|
||||
QString m_audioDeviceName;
|
||||
bool m_pll;
|
||||
|
||||
AMDemodSettings();
|
||||
void resetToDefaults();
|
||||
|
@ -298,7 +298,7 @@ int fftfilt::runSSB(const cmplx & in, cmplx **out, bool usb, bool getDC)
|
||||
}
|
||||
|
||||
// Version for double sideband. You have to double the FFT size used for SSB.
|
||||
int fftfilt::runDSB(const cmplx & in, cmplx **out)
|
||||
int fftfilt::runDSB(const cmplx & in, cmplx **out, bool getDC)
|
||||
{
|
||||
data[inptr++] = in;
|
||||
if (inptr < flen2)
|
||||
@ -312,6 +312,9 @@ int fftfilt::runDSB(const cmplx & in, cmplx **out)
|
||||
data[flen2 + i] *= filter[flen2 + i];
|
||||
}
|
||||
|
||||
// get or reject DC component
|
||||
data[0] = getDC ? data[0] : 0;
|
||||
|
||||
// in-place FFT: freqdata overwritten with filtered timedata
|
||||
fft->InverseComplexFFT(data);
|
||||
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
int noFilt(const cmplx& in, cmplx **out);
|
||||
int runFilt(const cmplx& in, cmplx **out);
|
||||
int runSSB(const cmplx& in, cmplx **out, bool usb, bool getDC = true);
|
||||
int runDSB(const cmplx& in, cmplx **out);
|
||||
int runDSB(const cmplx& in, cmplx **out, bool getDC = true);
|
||||
int runAsym(const cmplx & in, cmplx **out, bool usb); //!< Asymmetrical fitering can be used for vestigial sideband
|
||||
|
||||
protected:
|
||||
|
Loading…
Reference in New Issue
Block a user