mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-21 23:55:13 -05:00
ChanelAnalyzerNG: added PLL option
This commit is contained in:
parent
a3bd35ff27
commit
3ae7cda9be
@ -35,7 +35,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
|
||||
*/
|
||||
QCoreApplication::setOrganizationName("f4exb");
|
||||
QCoreApplication::setApplicationName("SDRangel");
|
||||
QCoreApplication::setApplicationVersion("3.14.6");
|
||||
QCoreApplication::setApplicationVersion("3.14.7");
|
||||
|
||||
#if 1
|
||||
qApp->setStyle(QStyleFactory::create("fusion"));
|
||||
|
@ -57,7 +57,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
|
||||
|
||||
QCoreApplication::setOrganizationName("f4exb");
|
||||
QCoreApplication::setApplicationName("SDRangelBench");
|
||||
QCoreApplication::setApplicationVersion("3.14.6");
|
||||
QCoreApplication::setApplicationVersion("3.14.7");
|
||||
|
||||
int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
|
||||
std::vector<int> vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int));
|
||||
|
@ -56,7 +56,7 @@ static int runQtApplication(int argc, char* argv[], qtwebapp::LoggerWithFile *lo
|
||||
|
||||
QCoreApplication::setOrganizationName("f4exb");
|
||||
QCoreApplication::setApplicationName("SDRangelSrv");
|
||||
QCoreApplication::setApplicationVersion("3.14.6");
|
||||
QCoreApplication::setApplicationVersion("3.14.7");
|
||||
|
||||
int catchSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
|
||||
std::vector<int> vsig(catchSignals, catchSignals + sizeof(catchSignals) / sizeof(int));
|
||||
|
6
debian/changelog
vendored
6
debian/changelog
vendored
@ -1,3 +1,9 @@
|
||||
sdrangel (3.14.7-1) unstable; urgency=medium
|
||||
|
||||
* ChanelAnalyzerNG: added PLL option
|
||||
|
||||
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 13 May 2018 20:14:18 +0200
|
||||
|
||||
sdrangel (3.14.6-1) unstable; urgency=medium
|
||||
|
||||
* Fixed keyboard input for negative values on realtive integer value dials
|
||||
|
@ -35,6 +35,7 @@ const QString ChannelAnalyzerNG::m_channelId = "ChannelAnalyzerNG";
|
||||
ChannelAnalyzerNG::ChannelAnalyzerNG(DeviceSourceAPI *deviceAPI) :
|
||||
ChannelSinkAPI(m_channelIdURI),
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_pll(0,0.05,0.01),
|
||||
m_sampleSink(0),
|
||||
m_settingsMutex(QMutex::Recursive)
|
||||
{
|
||||
@ -73,9 +74,10 @@ void ChannelAnalyzerNG::configure(MessageQueue* messageQueue,
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb)
|
||||
bool ssb,
|
||||
bool pll)
|
||||
{
|
||||
Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb);
|
||||
Message* cmd = MsgConfigureChannelAnalyzer::create(channelSampleRate, Bandwidth, LowCutoff, spanLog2, ssb, pll);
|
||||
messageQueue->push(cmd);
|
||||
}
|
||||
|
||||
@ -165,13 +167,15 @@ bool ChannelAnalyzerNG::handleMessage(const Message& cmd)
|
||||
m_config.m_LowCutoff = cfg.getLoCutoff();
|
||||
m_config.m_spanLog2 = cfg.getSpanLog2();
|
||||
m_config.m_ssb = cfg.getSSB();
|
||||
m_config.m_pll = cfg.getPLL();
|
||||
|
||||
qDebug() << "ChannelAnalyzerNG::handleMessage: MsgConfigureChannelAnalyzer:"
|
||||
<< " m_channelSampleRate: " << m_config.m_channelSampleRate
|
||||
<< " m_Bandwidth: " << m_config.m_Bandwidth
|
||||
<< " m_LowCutoff: " << m_config.m_LowCutoff
|
||||
<< " m_spanLog2: " << m_config.m_spanLog2
|
||||
<< " m_ssb: " << m_config.m_ssb;
|
||||
<< " m_ssb: " << m_config.m_ssb
|
||||
<< " m_pll: " << m_config.m_pll;
|
||||
|
||||
apply();
|
||||
return true;
|
||||
@ -254,5 +258,6 @@ void ChannelAnalyzerNG::apply(bool force)
|
||||
//m_settingsMutex.lock();
|
||||
m_running.m_spanLog2 = m_config.m_spanLog2;
|
||||
m_running.m_ssb = m_config.m_ssb;
|
||||
m_running.m_pll = m_config.m_pll;
|
||||
//m_settingsMutex.unlock();
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "dsp/interpolator.h"
|
||||
#include "dsp/ncof.h"
|
||||
#include "dsp/fftfilt.h"
|
||||
#include "dsp/phaselock.h"
|
||||
#include "audio/audiofifo.h"
|
||||
#include "util/message.h"
|
||||
|
||||
@ -45,20 +46,23 @@ public:
|
||||
Real getLoCutoff() const { return m_LowCutoff; }
|
||||
int getSpanLog2() const { return m_spanLog2; }
|
||||
bool getSSB() const { return m_ssb; }
|
||||
bool getPLL() const { return m_pll; }
|
||||
|
||||
static MsgConfigureChannelAnalyzer* create(
|
||||
int channelSampleRate,
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb)
|
||||
bool ssb,
|
||||
bool pll)
|
||||
{
|
||||
return new MsgConfigureChannelAnalyzer(
|
||||
channelSampleRate,
|
||||
Bandwidth,
|
||||
LowCutoff,
|
||||
spanLog2,
|
||||
ssb);
|
||||
ssb,
|
||||
pll);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -67,19 +71,22 @@ public:
|
||||
Real m_LowCutoff;
|
||||
int m_spanLog2;
|
||||
bool m_ssb;
|
||||
bool m_pll;
|
||||
|
||||
MsgConfigureChannelAnalyzer(
|
||||
int channelSampleRate,
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb) :
|
||||
bool ssb,
|
||||
bool pll) :
|
||||
Message(),
|
||||
m_channelSampleRate(channelSampleRate),
|
||||
m_Bandwidth(Bandwidth),
|
||||
m_LowCutoff(LowCutoff),
|
||||
m_spanLog2(spanLog2),
|
||||
m_ssb(ssb)
|
||||
m_ssb(ssb),
|
||||
m_pll(pll)
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -133,12 +140,14 @@ public:
|
||||
Real Bandwidth,
|
||||
Real LowCutoff,
|
||||
int spanLog2,
|
||||
bool ssb);
|
||||
bool ssb,
|
||||
bool pll);
|
||||
|
||||
DownChannelizer *getChannelizer() { return m_channelizer; }
|
||||
int getInputSampleRate() const { return m_running.m_inputSampleRate; }
|
||||
int getChannelSampleRate() const { return m_running.m_channelSampleRate; }
|
||||
double getMagSq() const { return m_magsq; }
|
||||
bool isPllLocked() const { return m_running.m_pll && m_pll.locked(); }
|
||||
|
||||
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
|
||||
virtual void start();
|
||||
@ -166,6 +175,7 @@ private:
|
||||
Real m_LowCutoff;
|
||||
int m_spanLog2;
|
||||
bool m_ssb;
|
||||
bool m_pll;
|
||||
|
||||
Config() :
|
||||
m_frequency(0),
|
||||
@ -174,7 +184,8 @@ private:
|
||||
m_Bandwidth(5000),
|
||||
m_LowCutoff(300),
|
||||
m_spanLog2(3),
|
||||
m_ssb(false)
|
||||
m_ssb(false),
|
||||
m_pll(false)
|
||||
{}
|
||||
};
|
||||
|
||||
@ -192,6 +203,7 @@ private:
|
||||
bool m_useInterpolator;
|
||||
|
||||
NCOF m_nco;
|
||||
SimplePhaseLock m_pll;
|
||||
Interpolator m_interpolator;
|
||||
Real m_interpolatorDistance;
|
||||
Real m_interpolatorDistanceRemain;
|
||||
@ -233,13 +245,33 @@ private:
|
||||
Real im = m_sum.imag() / SDR_RX_SCALED;
|
||||
m_magsq = re*re + im*im;
|
||||
|
||||
if (m_running.m_ssb & !m_usb)
|
||||
{ // invert spectrum for LSB
|
||||
m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real()));
|
||||
if (m_running.m_pll)
|
||||
{
|
||||
Real ncopll[2];
|
||||
m_pll.process(re, im, ncopll);
|
||||
|
||||
Real mixI = m_sum.real() * ncopll[0] - m_sum.imag() * ncopll[1];
|
||||
Real mixQ = m_sum.real() * ncopll[1] + m_sum.imag() * ncopll[0];
|
||||
|
||||
if (m_running.m_ssb & !m_usb)
|
||||
{ // invert spectrum for LSB
|
||||
m_sampleBuffer.push_back(Sample(mixQ, mixI));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sampleBuffer.push_back(Sample(mixI, mixQ));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag()));
|
||||
if (m_running.m_ssb & !m_usb)
|
||||
{ // invert spectrum for LSB
|
||||
m_sampleBuffer.push_back(Sample(m_sum.imag(), m_sum.real()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sampleBuffer.push_back(Sample(m_sum.real(), m_sum.imag()));
|
||||
}
|
||||
}
|
||||
|
||||
m_sum = 0;
|
||||
|
@ -237,6 +237,12 @@ void ChannelAnalyzerNGGUI::tick()
|
||||
double powDb = CalcDb::dbPower(m_channelAnalyzer->getMagSq());
|
||||
m_channelPowerDbAvg(powDb);
|
||||
ui->channelPower->setText(tr("%1 dB").arg((Real) m_channelPowerDbAvg, 0, 'f', 1));
|
||||
|
||||
if (m_channelAnalyzer->isPllLocked()) {
|
||||
ui->pll->setStyleSheet("QToolButton { background-color : green; }");
|
||||
} else {
|
||||
ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }");
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
|
||||
@ -251,6 +257,11 @@ void ChannelAnalyzerNGGUI::on_channelSampleRate_changed(quint64 value)
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_pll_toggled(bool checked __attribute__((unused)))
|
||||
{
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void ChannelAnalyzerNGGUI::on_useRationalDownsampler_toggled(bool checked __attribute__((unused)))
|
||||
{
|
||||
setNewFinalRate(m_spanLog2);
|
||||
@ -578,7 +589,8 @@ void ChannelAnalyzerNGGUI::applySettings()
|
||||
ui->BW->value() * 100.0,
|
||||
ui->lowCut->value() * 100.0,
|
||||
m_spanLog2,
|
||||
ui->ssb->isChecked());
|
||||
ui->ssb->isChecked(),
|
||||
ui->pll->isChecked());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ private:
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
void on_channelSampleRate_changed(quint64 value);
|
||||
void on_pll_toggled(bool checked);
|
||||
void on_useRationalDownsampler_toggled(bool checked);
|
||||
void on_BW_valueChanged(int value);
|
||||
void on_lowCut_valueChanged(int value);
|
||||
|
@ -179,6 +179,24 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="ChannelSamplingLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="pll">
|
||||
<property name="toolTip">
|
||||
<string>PLL lock</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>
|
||||
<widget class="ButtonSwitch" name="useRationalDownsampler">
|
||||
<property name="toolTip">
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
const PluginDescriptor ChannelAnalyzerNGPlugin::m_pluginDescriptor = {
|
||||
QString("Channel Analyzer NG"),
|
||||
QString("3.14.5"),
|
||||
QString("3.14.7"),
|
||||
QString("(c) Edouard Griffiths, F4EXB"),
|
||||
QString("https://github.com/f4exb/sdrangel"),
|
||||
true,
|
||||
|
@ -262,61 +262,85 @@ void PhaseLock::process(const Real& sample_in, Real *samples_out)
|
||||
processPhase(samples_out);
|
||||
|
||||
// Multiply locked tone with input.
|
||||
Real x = sample_in;
|
||||
Real phasor_i = m_psin * x;
|
||||
Real phasor_q = m_pcos * x;
|
||||
Real phasor_i = m_psin * sample_in;
|
||||
Real phasor_q = m_pcos * sample_in;
|
||||
|
||||
// Run IQ phase error through low-pass filter.
|
||||
phasor_i = m_phasor_b0 * phasor_i
|
||||
- m_phasor_a1 * m_phasor_i1
|
||||
- m_phasor_a2 * m_phasor_i2;
|
||||
phasor_q = m_phasor_b0 * phasor_q
|
||||
- m_phasor_a1 * m_phasor_q1
|
||||
- m_phasor_a2 * m_phasor_q2;
|
||||
m_phasor_i2 = m_phasor_i1;
|
||||
m_phasor_i1 = phasor_i;
|
||||
m_phasor_q2 = m_phasor_q1;
|
||||
m_phasor_q1 = phasor_q;
|
||||
// Actual PLL
|
||||
process_phasor(phasor_i, phasor_q);
|
||||
}
|
||||
|
||||
// Convert I/Q ratio to estimate of phase error.
|
||||
Real phase_err;
|
||||
void PhaseLock::process(const Real& real_in, const Real& imag_in, Real *samples_out)
|
||||
{
|
||||
m_pps_events.clear();
|
||||
|
||||
// Generate locked pilot tone.
|
||||
m_psin = sin(m_phase);
|
||||
m_pcos = cos(m_phase);
|
||||
|
||||
// Generate output
|
||||
processPhase(samples_out);
|
||||
|
||||
// Multiply locked tone with input.
|
||||
Real phasor_i = m_psin * real_in - m_pcos * imag_in;
|
||||
Real phasor_q = m_pcos * real_in + m_psin * imag_in;
|
||||
|
||||
// Actual PLL
|
||||
process_phasor(phasor_i, phasor_q);
|
||||
}
|
||||
|
||||
void PhaseLock::process_phasor(Real& phasor_i, Real& phasor_q)
|
||||
{
|
||||
// Run IQ phase error through low-pass filter.
|
||||
phasor_i = m_phasor_b0 * phasor_i
|
||||
- m_phasor_a1 * m_phasor_i1
|
||||
- m_phasor_a2 * m_phasor_i2;
|
||||
phasor_q = m_phasor_b0 * phasor_q
|
||||
- m_phasor_a1 * m_phasor_q1
|
||||
- m_phasor_a2 * m_phasor_q2;
|
||||
m_phasor_i2 = m_phasor_i1;
|
||||
m_phasor_i1 = phasor_i;
|
||||
m_phasor_q2 = m_phasor_q1;
|
||||
m_phasor_q1 = phasor_q;
|
||||
|
||||
// Convert I/Q ratio to estimate of phase error.
|
||||
Real phase_err;
|
||||
if (phasor_i > std::abs(phasor_q)) {
|
||||
// We are within +/- 45 degrees from lock.
|
||||
// Use simple linear approximation of arctan.
|
||||
phase_err = phasor_q / phasor_i;
|
||||
} else if (phasor_q > 0) {
|
||||
// We are lagging more than 45 degrees behind the input.
|
||||
phase_err = 1;
|
||||
} else {
|
||||
// We are more than 45 degrees ahead of the input.
|
||||
phase_err = -1;
|
||||
}
|
||||
// We are within +/- 45 degrees from lock.
|
||||
// Use simple linear approximation of arctan.
|
||||
phase_err = phasor_q / phasor_i;
|
||||
} else if (phasor_q > 0) {
|
||||
// We are lagging more than 45 degrees behind the input.
|
||||
phase_err = 1;
|
||||
} else {
|
||||
// We are more than 45 degrees ahead of the input.
|
||||
phase_err = -1;
|
||||
}
|
||||
|
||||
// Detect pilot level (conservative).
|
||||
// m_pilot_level = std::min(m_pilot_level, phasor_i);
|
||||
m_pilot_level = phasor_i;
|
||||
// Detect pilot level (conservative).
|
||||
// m_pilot_level = std::min(m_pilot_level, phasor_i);
|
||||
m_pilot_level = phasor_i;
|
||||
|
||||
// Run phase error through loop filter and update frequency estimate.
|
||||
m_freq += m_loopfilter_b0 * phase_err
|
||||
+ m_loopfilter_b1 * m_loopfilter_x1;
|
||||
m_loopfilter_x1 = phase_err;
|
||||
// Run phase error through loop filter and update frequency estimate.
|
||||
m_freq += m_loopfilter_b0 * phase_err
|
||||
+ m_loopfilter_b1 * m_loopfilter_x1;
|
||||
m_loopfilter_x1 = phase_err;
|
||||
|
||||
// Limit frequency to allowable range.
|
||||
m_freq = std::max(m_minfreq, std::min(m_maxfreq, m_freq));
|
||||
// Limit frequency to allowable range.
|
||||
m_freq = std::max(m_minfreq, std::min(m_maxfreq, m_freq));
|
||||
|
||||
// Update locked phase.
|
||||
m_phase += m_freq;
|
||||
if (m_phase > 2.0 * M_PI)
|
||||
{
|
||||
m_phase -= 2.0 * M_PI;
|
||||
m_pilot_periods++;
|
||||
// Update locked phase.
|
||||
m_phase += m_freq;
|
||||
if (m_phase > 2.0 * M_PI)
|
||||
{
|
||||
m_phase -= 2.0 * M_PI;
|
||||
m_pilot_periods++;
|
||||
|
||||
// Generate pulse-per-second.
|
||||
if (m_pilot_periods == pilot_frequency)
|
||||
{
|
||||
m_pilot_periods = 0;
|
||||
}
|
||||
}
|
||||
// Generate pulse-per-second.
|
||||
if (m_pilot_periods == pilot_frequency)
|
||||
{
|
||||
m_pilot_periods = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update lock status.
|
||||
if (2 * m_pilot_level > m_minsignal)
|
||||
@ -328,7 +352,7 @@ void PhaseLock::process(const Real& sample_in, Real *samples_out)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lock_cnt = 0;
|
||||
m_lock_cnt = 0;
|
||||
}
|
||||
|
||||
// Drop PPS events when pilot not locked.
|
||||
|
@ -73,6 +73,7 @@ public:
|
||||
* This is the in flow version
|
||||
*/
|
||||
void process(const Real& sample_in, Real *samples_out);
|
||||
void process(const Real& real_in, const Real& imag_in, Real *samples_out);
|
||||
|
||||
/** Return true if the phase-locked loop is locked. */
|
||||
bool locked() const
|
||||
@ -111,6 +112,8 @@ private:
|
||||
quint64 m_pps_cnt;
|
||||
quint64 m_sample_cnt;
|
||||
std::vector<PpsEvent> m_pps_events;
|
||||
|
||||
void process_phasor(Real& phasor_i, Real& phasor_q);
|
||||
};
|
||||
|
||||
class SimplePhaseLock : public PhaseLock
|
||||
|
Loading…
Reference in New Issue
Block a user