ScopeNG: moved Projector class to sdrbase to avoid excessive dependency on liquid-dsp

This commit is contained in:
f4exb 2018-04-03 18:51:21 +02:00
parent b856bc2aac
commit 8ce1c76a40
7 changed files with 191 additions and 144 deletions

View File

@ -38,6 +38,7 @@ set(sdrbase_SOURCES
dsp/nco.cpp
dsp/ncof.cpp
dsp/phaselock.cpp
dsp/projector.cpp
dsp/samplesinkfifo.cpp
dsp/samplesourcefifo.cpp
dsp/samplesinkfifodoublebuffered.cpp
@ -139,6 +140,7 @@ set(sdrbase_HEADERS
dsp/ncof.h
dsp/phasediscri.h
dsp/phaselock.h
dsp/projector.h
dsp/recursivefilters.h
dsp/samplesinkfifo.h
dsp/samplesourcefifo.h

92
sdrbase/dsp/projector.cpp Normal file
View File

@ -0,0 +1,92 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 F4EXB //
// written by Edouard Griffiths //
// //
// 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 "projector.h"
#include "symsync.h" // dependency on liquid-dsp
Projector::Projector(ProjectionType projectionType) :
m_projectionType(projectionType),
m_prevArg(0.0f),
m_cache(0),
m_cacheMaster(true)
{}
Projector::~Projector()
{}
Real Projector::run(const Sample& s)
{
Real v;
if ((m_cache) && !m_cacheMaster) {
return m_cache[(int) m_projectionType];
}
else
{
switch (m_projectionType)
{
case ProjectionImag:
v = s.m_imag / SDR_RX_SCALEF;
break;
case ProjectionMagLin:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = std::sqrt(magsq);
}
break;
case ProjectionMagDB:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = log10f(magsq) * 10.0f;
}
break;
case ProjectionPhase:
v = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI;
break;
case ProjectionDPhase:
{
Real curArg = std::atan2((float) s.m_imag, (float) s.m_real);
Real dPhi = (curArg - m_prevArg) / M_PI;
m_prevArg = curArg;
if (dPhi < -1.0f) {
dPhi += 2.0f;
} else if (dPhi > 1.0f) {
dPhi -= 2.0f;
}
v = dPhi;
}
break;
case ProjectionReal:
default:
v = s.m_real / SDR_RX_SCALEF;
break;
}
if (m_cache) {
m_cache[(int) m_projectionType] = v;
}
return v;
}
}

53
sdrbase/dsp/projector.h Normal file
View File

@ -0,0 +1,53 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2017 F4EXB //
// written by Edouard Griffiths //
// //
// 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 "dsptypes.h"
class SymbolSynchronizer;
class Projector
{
public:
enum ProjectionType
{
ProjectionReal = 0, //!< Extract real part
ProjectionImag, //!< Extract imaginary part
ProjectionMagLin, //!< Calculate linear magnitude or modulus
ProjectionMagDB, //!< Calculate logarithmic (dB) of squared magnitude
ProjectionPhase, //!< Calculate phase
ProjectionDPhase, //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate
ProjectionClock, //!< Clock projection (symbol synchronization)
nbProjectionTypes //!< Gives the number of projections in the enum
};
Projector(ProjectionType projectionType);
~Projector();
ProjectionType getProjectionType() const { return m_projectionType; }
void settProjectionType(ProjectionType projectionType) { m_projectionType = projectionType; }
void setCache(Real *cache) { m_cache = cache; }
void setCacheMaster(bool cacheMaster) { m_cacheMaster = cacheMaster; }
Real run(const Sample& s);
private:
ProjectionType m_projectionType;
Real m_prevArg;
Real *m_cache;
bool m_cacheMaster;
SymbolSynchronizer *m_symSync;
};

View File

@ -62,7 +62,7 @@ ScopeVisNG::ScopeVisNG(GLScopeNG* glScope) :
setObjectName("ScopeVisNG");
m_traceDiscreteMemory.resize(m_traceChunkSize); // arbitrary
m_glScope->setTraces(&m_traces.m_tracesData, &m_traces.m_traces[0]);
for (int i = 0; i < (int) nbProjectionTypes; i++) {
for (int i = 0; i < (int) Projector::nbProjectionTypes; i++) {
m_projectorCache[i] = 0.0;
}
}
@ -449,18 +449,18 @@ int ScopeVisNG::processTraces(const SampleVector::const_iterator& cbegin, const
continue;
}
ProjectionType projectionType = itData->m_projectionType;
Projector::ProjectionType projectionType = itData->m_projectionType;
if (itCtl->m_traceCount[m_traces.currentBufferIndex()] < m_traceSize)
{
uint32_t& traceCount = itCtl->m_traceCount[m_traces.currentBufferIndex()]; // reference for code clarity
float v;
if (projectionType == ProjectionMagLin)
if (projectionType == Projector::ProjectionMagLin)
{
v = (itCtl->m_projector.run(*begin) - itData->m_ofs)*itData->m_amp - 1.0f;
}
else if (projectionType == ProjectionMagDB)
else if (projectionType == Projector::ProjectionMagDB)
{
// there is no processing advantage in direct calculation without projector
// uint32_t magsq = begin->m_real*begin->m_real + begin->m_imag*begin->m_imag;
@ -786,8 +786,8 @@ void ScopeVisNG::updateMaxTraceDelay()
{
int maxTraceDelay = 0;
bool allocateCache = false;
uint32_t projectorCounts[(int) nbProjectionTypes];
memset(projectorCounts, 0, ((int) nbProjectionTypes)*sizeof(uint32_t));
uint32_t projectorCounts[(int) Projector::nbProjectionTypes];
memset(projectorCounts, 0, ((int) Projector::nbProjectionTypes)*sizeof(uint32_t));
std::vector<TraceData>::iterator itData = m_traces.m_tracesData.begin();
std::vector<TraceControl>::iterator itCtrl = m_traces.m_tracesControl.begin();
@ -799,7 +799,7 @@ void ScopeVisNG::updateMaxTraceDelay()
}
if (itData->m_projectionType < 0) {
itData->m_projectionType = ProjectionReal;
itData->m_projectionType = Projector::ProjectionReal;
}
if (projectorCounts[(int) itData->m_projectionType] > 0)
@ -861,11 +861,11 @@ void ScopeVisNG::computeDisplayTriggerLevels()
float levelPowerdB = (100.0f * (level - 1.0f));
float v;
if (itData->m_projectionType == ProjectionMagLin)
if (itData->m_projectionType == Projector::ProjectionMagLin)
{
v = (levelPowerLin - itData->m_ofs)*itData->m_amp - 1.0f;
}
else if (itData->m_projectionType == ProjectionMagDB)
else if (itData->m_projectionType == Projector::ProjectionMagDB)
{
float ofsdB = itData->m_ofs * 100.0f;
v = ((levelPowerdB + 100.0f - ofsdB)*itData->m_amp)/50.0f - 1.0f;

View File

@ -29,6 +29,7 @@
#include <boost/circular_buffer.hpp>
#include "dsp/dsptypes.h"
#include "dsp/basebandsamplesink.h"
#include "dsp/projector.h"
#include "export.h"
#include "util/message.h"
#include "util/doublebuffer.h"
@ -41,20 +42,9 @@ class GLScopeNG;
class SDRGUI_API ScopeVisNG : public BasebandSampleSink {
public:
enum ProjectionType
{
ProjectionReal = 0, //!< Extract real part
ProjectionImag, //!< Extract imaginary part
ProjectionMagLin, //!< Calculate linear magnitude or modulus
ProjectionMagDB, //!< Calculate logarithmic (dB) of squared magnitude
ProjectionPhase, //!< Calculate phase
ProjectionDPhase, //!< Calculate phase derivative i.e. instantaneous frequency scaled to sample rate
nbProjectionTypes //!< Gives the number of projections in the enum
};
struct TraceData
{
ProjectionType m_projectionType; //!< Complex to real projection type
Projector::ProjectionType m_projectionType; //!< Complex to real projection type
uint32_t m_inputIndex; //!< Input or feed index this trace is associated with
float m_amp; //!< Amplification factor
uint32_t m_ampIndex; //!< Index in list of amplification factors
@ -74,7 +64,7 @@ public:
bool m_viewTrace; //!< Trace visibility
TraceData() :
m_projectionType(ProjectionReal),
m_projectionType(Projector::ProjectionReal),
m_inputIndex(0),
m_amp(1.0f),
m_ampIndex(0),
@ -106,7 +96,7 @@ public:
struct TriggerData
{
ProjectionType m_projectionType; //!< Complex to real projection type
Projector::ProjectionType m_projectionType; //!< Complex to real projection type
uint32_t m_inputIndex; //!< Input or feed index this trigger is associated with
Real m_triggerLevel; //!< Level in real units
int m_triggerLevelCoarse;
@ -124,7 +114,7 @@ public:
float m_triggerColorB; //!< Trigger line display color - blue shortcut
TriggerData() :
m_projectionType(ProjectionReal),
m_projectionType(Projector::ProjectionReal),
m_inputIndex(0),
m_triggerLevel(0.0f),
m_triggerLevelCoarse(0),
@ -512,96 +502,6 @@ private:
// ---------------------------------------------
/**
* Projection stuff
*/
class Projector
{
public:
Projector(ProjectionType projectionType) :
m_projectionType(projectionType),
m_prevArg(0.0f),
m_cache(0),
m_cacheMaster(true)
{}
~Projector()
{}
ProjectionType getProjectionType() const { return m_projectionType; }
void settProjectionType(ProjectionType projectionType) { m_projectionType = projectionType; }
void setCache(Real *cache) { m_cache = cache; }
void setCacheMaster(bool cacheMaster) { m_cacheMaster = cacheMaster; }
Real run(const Sample& s)
{
Real v;
if ((m_cache) && !m_cacheMaster) {
return m_cache[(int) m_projectionType];
}
else
{
switch (m_projectionType)
{
case ProjectionImag:
v = s.m_imag / SDR_RX_SCALEF;
break;
case ProjectionMagLin:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = std::sqrt(magsq);
}
break;
case ProjectionMagDB:
{
Real re = s.m_real / SDR_RX_SCALEF;
Real im = s.m_imag / SDR_RX_SCALEF;
Real magsq = re*re + im*im;
v = log10f(magsq) * 10.0f;
}
break;
case ProjectionPhase:
v = std::atan2((float) s.m_imag, (float) s.m_real) / M_PI;
break;
case ProjectionDPhase:
{
Real curArg = std::atan2((float) s.m_imag, (float) s.m_real);
Real dPhi = (curArg - m_prevArg) / M_PI;
m_prevArg = curArg;
if (dPhi < -1.0f) {
dPhi += 2.0f;
} else if (dPhi > 1.0f) {
dPhi -= 2.0f;
}
v = dPhi;
}
break;
case ProjectionReal:
default:
v = s.m_real / SDR_RX_SCALEF;
break;
}
if (m_cache) {
m_cache[(int) m_projectionType] = v;
}
return v;
}
}
private:
ProjectionType m_projectionType;
Real m_prevArg;
Real *m_cache;
bool m_cacheMaster;
};
/**
* Trigger stuff
*/
@ -623,7 +523,7 @@ private:
uint32_t m_triggerCounter; //!< Counter of trigger occurences
TriggerCondition(const TriggerData& triggerData) :
m_projector(ProjectionReal),
m_projector(Projector::ProjectionReal),
m_triggerData(triggerData),
m_prevCondition(false),
m_triggerDelayCount(0),
@ -785,7 +685,7 @@ private:
Real m_sumPow; //!< Cumulative power over the current trace for MagDB overlay display
int m_nbPow; //!< Number of power samples over the current trace for MagDB overlay display
TraceControl() : m_projector(ProjectionReal)
TraceControl() : m_projector(Projector::ProjectionReal)
{
reset();
}
@ -794,7 +694,7 @@ private:
{
}
void initProjector(ProjectionType projectionType)
void initProjector(Projector::ProjectionType projectionType)
{
m_projector.settProjectionType(projectionType);
}
@ -967,9 +867,9 @@ private:
bool condition, trigger;
if (triggerCondition.m_projector.getProjectionType() == ProjectionMagDB) {
if (triggerCondition.m_projector.getProjectionType() == Projector::ProjectionMagDB) {
condition = triggerCondition.m_projector.run(s) > m_levelPowerDB;
} else if (triggerCondition.m_projector.getProjectionType() == ProjectionMagLin) {
} else if (triggerCondition.m_projector.getProjectionType() == Projector::ProjectionMagLin) {
condition = triggerCondition.m_projector.run(s) > m_levelPowerLin;
} else {
condition = triggerCondition.m_projector.run(s) > m_level;
@ -1040,7 +940,7 @@ private:
int m_maxTraceDelay; //!< Maximum trace delay
TriggerComparator m_triggerComparator; //!< Compares sample level to trigger level
QMutex m_mutex;
Real m_projectorCache[(int) nbProjectionTypes];
Real m_projectorCache[(int) Projector::nbProjectionTypes];
bool m_triggerOneShot; //!< True when one shot mode is active
bool m_triggerWaitForReset; //!< In one shot mode suspended until reset by UI
uint32_t m_currentTraceMemoryIndex; //!< The current index of trace in memory (0: current)

View File

@ -1868,22 +1868,22 @@ void GLScopeNG::setYScale(ScaleEngine& scale, uint32_t highlightedTraceIndex)
switch (traceData.m_projectionType)
{
case ScopeVisNG::ProjectionMagDB: // dB scale
case Projector::ProjectionMagDB: // dB scale
scale.setRange(Unit::Decibel, pow_floor, pow_floor + pow_range);
break;
case ScopeVisNG::ProjectionMagLin:
case Projector::ProjectionMagLin:
if (amp_range < 2.0) {
scale.setRange(Unit::None, amp_ofs * 1000.0, amp_range * 1000.0 + amp_ofs * 1000.0);
} else {
scale.setRange(Unit::None, amp_ofs, amp_range + amp_ofs);
}
break;
case ScopeVisNG::ProjectionPhase: // Phase or frequency
case ScopeVisNG::ProjectionDPhase:
case Projector::ProjectionPhase: // Phase or frequency
case Projector::ProjectionDPhase:
scale.setRange(Unit::None, -1.0/traceData.m_amp + amp_ofs, 1.0/traceData.m_amp + amp_ofs);
break;
case ScopeVisNG::ProjectionReal: // Linear generic
case ScopeVisNG::ProjectionImag:
case Projector::ProjectionReal: // Linear generic
case Projector::ProjectionImag:
default:
if (amp_range < 2.0) {
scale.setRange(Unit::None, - amp_range * 500.0 + amp_ofs * 1000.0, amp_range * 500.0 + amp_ofs * 1000.0);

View File

@ -944,10 +944,10 @@ void GLScopeNGGUI::setTimeOfsDisplay()
void GLScopeNGGUI::setAmpScaleDisplay()
{
ScopeVisNG::ProjectionType projectionType = (ScopeVisNG::ProjectionType) ui->traceMode->currentIndex();
Projector::ProjectionType projectionType = (Projector::ProjectionType) ui->traceMode->currentIndex();
double ampValue = amps[ui->amp->value()];
if (projectionType == ScopeVisNG::ProjectionMagDB)
if (projectionType == Projector::ProjectionMagDB)
{
double displayValue = ampValue*500.0f;
@ -975,10 +975,10 @@ void GLScopeNGGUI::setAmpScaleDisplay()
void GLScopeNGGUI::setAmpOfsDisplay()
{
ScopeVisNG::ProjectionType projectionType = (ScopeVisNG::ProjectionType) ui->traceMode->currentIndex();
Projector::ProjectionType projectionType = (Projector::ProjectionType) ui->traceMode->currentIndex();
double o = (ui->ofsCoarse->value() * 10.0f) + (ui->ofsFine->value() / 20.0f);
if (projectionType == ScopeVisNG::ProjectionMagDB)
if (projectionType == Projector::ProjectionMagDB)
{
ui->ofsText->setText(tr("%1\ndB").arg(o/10.0f - 100.0f, 0, 'f', 1));
}
@ -986,7 +986,7 @@ void GLScopeNGGUI::setAmpOfsDisplay()
{
double a;
if (projectionType == ScopeVisNG::ProjectionMagLin)
if (projectionType == Projector::ProjectionMagLin)
{
a = o/2000.0f;
}
@ -1040,19 +1040,19 @@ void GLScopeNGGUI::setTrigIndexDisplay()
void GLScopeNGGUI::setTrigLevelDisplay()
{
double t = (ui->trigLevelCoarse->value() / 100.0f) + (ui->trigLevelFine->value() / 20000.0f);
ScopeVisNG::ProjectionType projectionType = (ScopeVisNG::ProjectionType) ui->trigMode->currentIndex();
Projector::ProjectionType projectionType = (Projector::ProjectionType) ui->trigMode->currentIndex();
ui->trigLevelCoarse->setToolTip(QString("Trigger level coarse: %1 %").arg(ui->trigLevelCoarse->value() / 100.0f));
ui->trigLevelFine->setToolTip(QString("Trigger level fine: %1 ppm").arg(ui->trigLevelFine->value() * 50));
if (projectionType == ScopeVisNG::ProjectionMagDB) {
if (projectionType == Projector::ProjectionMagDB) {
ui->trigLevelText->setText(tr("%1\ndB").arg(100.0 * (t - 1.0), 0, 'f', 1));
}
else
{
double a;
if (projectionType == ScopeVisNG::ProjectionMagLin) {
if (projectionType == Projector::ProjectionMagLin) {
a = 1.0 + t;
} else {
a = t;
@ -1146,12 +1146,12 @@ void GLScopeNGGUI::changeCurrentTrigger()
void GLScopeNGGUI::fillProjectionCombo(QComboBox* comboBox)
{
comboBox->addItem("Real", ScopeVisNG::ProjectionReal);
comboBox->addItem("Imag", ScopeVisNG::ProjectionImag);
comboBox->addItem("Mag", ScopeVisNG::ProjectionMagLin);
comboBox->addItem("MagdB", ScopeVisNG::ProjectionMagDB);
comboBox->addItem("Phi", ScopeVisNG::ProjectionPhase);
comboBox->addItem("dPhi", ScopeVisNG::ProjectionDPhase);
comboBox->addItem("Real", Projector::ProjectionReal);
comboBox->addItem("Imag", Projector::ProjectionImag);
comboBox->addItem("Mag", Projector::ProjectionMagLin);
comboBox->addItem("MagdB", Projector::ProjectionMagDB);
comboBox->addItem("Phi", Projector::ProjectionPhase);
comboBox->addItem("dPhi", Projector::ProjectionDPhase);
}
void GLScopeNGGUI::disableLiveMode(bool disable)
@ -1176,8 +1176,8 @@ void GLScopeNGGUI::disableLiveMode(bool disable)
void GLScopeNGGUI::fillTraceData(ScopeVisNG::TraceData& traceData)
{
traceData.m_projectionType = (ScopeVisNG::ProjectionType) ui->traceMode->currentIndex();
traceData.m_hasTextOverlay = (traceData.m_projectionType == ScopeVisNG::ProjectionMagDB);
traceData.m_projectionType = (Projector::ProjectionType) ui->traceMode->currentIndex();
traceData.m_hasTextOverlay = (traceData.m_projectionType == Projector::ProjectionMagDB);
traceData.m_textOverlay.clear();
traceData.m_inputIndex = 0;
traceData.m_amp = 0.2 / amps[ui->amp->value()];
@ -1186,7 +1186,7 @@ void GLScopeNGGUI::fillTraceData(ScopeVisNG::TraceData& traceData)
traceData.m_ofsCoarse = ui->ofsCoarse->value();
traceData.m_ofsFine = ui->ofsFine->value();
if (traceData.m_projectionType == ScopeVisNG::ProjectionMagLin) {
if (traceData.m_projectionType == Projector::ProjectionMagLin) {
traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 2000.0f;
} else {
traceData.m_ofs = ((10.0 * ui->ofsCoarse->value()) + (ui->ofsFine->value() / 20.0)) / 1000.0f;
@ -1201,7 +1201,7 @@ void GLScopeNGGUI::fillTraceData(ScopeVisNG::TraceData& traceData)
void GLScopeNGGUI::fillTriggerData(ScopeVisNG::TriggerData& triggerData)
{
triggerData.m_projectionType = (ScopeVisNG::ProjectionType) ui->trigMode->currentIndex();
triggerData.m_projectionType = (Projector::ProjectionType) ui->trigMode->currentIndex();
triggerData.m_inputIndex = 0;
triggerData.m_triggerLevel = (ui->trigLevelCoarse->value() / 100.0) + (ui->trigLevelFine->value() / 20000.0);
triggerData.m_triggerLevelCoarse = ui->trigLevelCoarse->value();