diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 57967a1d1..e90989e35 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -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 diff --git a/sdrbase/dsp/projector.cpp b/sdrbase/dsp/projector.cpp new file mode 100644 index 000000000..6eeea26cd --- /dev/null +++ b/sdrbase/dsp/projector.cpp @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; + } +} + diff --git a/sdrbase/dsp/projector.h b/sdrbase/dsp/projector.h new file mode 100644 index 000000000..1370fca44 --- /dev/null +++ b/sdrbase/dsp/projector.h @@ -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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#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; +}; diff --git a/sdrgui/dsp/scopevisng.cpp b/sdrgui/dsp/scopevisng.cpp index f44485055..c8fdb6ed2 100644 --- a/sdrgui/dsp/scopevisng.cpp +++ b/sdrgui/dsp/scopevisng.cpp @@ -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::iterator itData = m_traces.m_tracesData.begin(); std::vector::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; diff --git a/sdrgui/dsp/scopevisng.h b/sdrgui/dsp/scopevisng.h index dc5ba22bd..5f022c2bc 100644 --- a/sdrgui/dsp/scopevisng.h +++ b/sdrgui/dsp/scopevisng.h @@ -29,6 +29,7 @@ #include #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) diff --git a/sdrgui/gui/glscopeng.cpp b/sdrgui/gui/glscopeng.cpp index 30f32bac0..3bda3ff10 100644 --- a/sdrgui/gui/glscopeng.cpp +++ b/sdrgui/gui/glscopeng.cpp @@ -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); diff --git a/sdrgui/gui/glscopenggui.cpp b/sdrgui/gui/glscopenggui.cpp index 894d88ced..12f043b35 100644 --- a/sdrgui/gui/glscopenggui.cpp +++ b/sdrgui/gui/glscopenggui.cpp @@ -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();