1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-12-22 17:45:48 -05:00

ChanelAnalyzerNG: added PLL option

This commit is contained in:
f4exb 2018-05-12 06:01:54 +02:00
parent a3bd35ff27
commit 3ae7cda9be
12 changed files with 167 additions and 66 deletions

View File

@ -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"));

View File

@ -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));

View File

@ -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
View File

@ -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

View File

@ -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();
}

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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);

View File

@ -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">

View File

@ -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,

View File

@ -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.

View File

@ -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