diff --git a/plugins/channelmimo/doa2/doa2.cpp b/plugins/channelmimo/doa2/doa2.cpp index 3b471436a..afb07781b 100644 --- a/plugins/channelmimo/doa2/doa2.cpp +++ b/plugins/channelmimo/doa2/doa2.cpp @@ -28,6 +28,7 @@ #include "dsp/hbfilterchainconverter.h" #include "dsp/dspcommands.h" #include "feature/feature.h" +#include "util/db.h" #include "maincore.h" #include "doa2baseband.h" @@ -132,6 +133,9 @@ void DOA2::applySettings(const DOA2Settings& settings, bool force) << "m_filterChainHash: " << settings.m_filterChainHash << "m_log2Decim: " << settings.m_log2Decim << "m_phase: " << settings.m_phase + << "m_antennaAz:" << settings.m_antennaAz + << "m_basebandDistance: " << settings.m_basebandDistance + << "m_squelchdB: " << settings.m_squelchdB << "m_useReverseAPI: " << settings.m_useReverseAPI << "m_reverseAPIAddress: " << settings.m_reverseAPIAddress << "m_reverseAPIPort: " << settings.m_reverseAPIPort @@ -156,6 +160,18 @@ void DOA2::applySettings(const DOA2Settings& settings, bool force) if ((m_settings.m_title != settings.m_title) || force) { reverseAPIKeys.append("title"); } + if ((m_settings.m_antennaAz != settings.m_antennaAz) || force) { + reverseAPIKeys.append("antennaAz"); + } + if ((m_settings.m_basebandDistance != settings.m_basebandDistance) || force) { + reverseAPIKeys.append("basebandDistance"); + } + + if ((m_settings.m_squelchdB != settings.m_squelchdB) || force) + { + reverseAPIKeys.append("squelchdB"); + m_basebandSink->setMagThreshold(CalcDb::powerFromdB(settings.m_squelchdB)); + } if ((m_settings.m_log2Decim != settings.m_log2Decim) || (m_settings.m_filterChainHash != settings.m_filterChainHash) || force) diff --git a/plugins/channelmimo/doa2/doa2baseband.cpp b/plugins/channelmimo/doa2/doa2baseband.cpp index c1aaa1163..baef35435 100644 --- a/plugins/channelmimo/doa2/doa2baseband.cpp +++ b/plugins/channelmimo/doa2/doa2baseband.cpp @@ -39,6 +39,7 @@ DOA2Baseband::DOA2Baseband(int fftSize) : m_magSum(0.0f), m_wphSum(0.0f), m_phi(0.0f), + m_magThreshold(0.0f), m_scopeSink(nullptr), m_mutex(QMutex::Recursive) { @@ -263,13 +264,20 @@ void DOA2Baseband::processDOA(const std::vector::iterator& begin, int n for (std::vector::iterator it = begin; it != end; ++it) { float ph = std::arg(*it); - float mag = std::norm(*it); - m_magSum += mag; - m_wphSum += mag*ph; + double mag = std::norm(*it); + + if (mag > m_magThreshold) + { + m_magSum += mag; + m_wphSum += mag*ph; + } if (++m_samplesCount == m_fftSize) { - m_phi = m_wphSum / m_magSum; + if (m_wphSum != 0) { + m_phi = m_wphSum / m_magSum; + } + m_magSum = 0; m_wphSum = 0; m_samplesCount = 0; diff --git a/plugins/channelmimo/doa2/doa2baseband.h b/plugins/channelmimo/doa2/doa2baseband.h index 3a7473b7d..6d713a771 100644 --- a/plugins/channelmimo/doa2/doa2baseband.h +++ b/plugins/channelmimo/doa2/doa2baseband.h @@ -111,6 +111,7 @@ public: void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, unsigned int streamIndex); void setBasebandSampleRate(unsigned int sampleRate); float getPhi() const { return m_phi; } + void setMagThreshold(float threshold) { m_magThreshold = threshold * SDR_RX_SCALED * SDR_RX_SCALED; } private: void processFifo(const std::vector& data, unsigned int ibegin, unsigned int iend); @@ -125,6 +126,7 @@ private: float m_magSum; float m_wphSum; float m_phi; + double m_magThreshold; SampleMIFifo m_sampleMIFifo; std::vector m_vbegin; int m_sizes[2]; diff --git a/plugins/channelmimo/doa2/doa2compass.cpp b/plugins/channelmimo/doa2/doa2compass.cpp index 72930bc44..c32cf9426 100644 --- a/plugins/channelmimo/doa2/doa2compass.cpp +++ b/plugins/channelmimo/doa2/doa2compass.cpp @@ -41,6 +41,7 @@ DOA2Compass::DOA2Compass(QWidget *parent) m_azPos = 0.0; m_azNeg = 0.0; m_azAnt = 0.0; + m_blindAngle = 0.0; } DOA2Compass::~DOA2Compass() @@ -86,6 +87,20 @@ void DOA2Compass::paintEvent(QPaintEvent *) painter.drawEllipse(-m_size/2, -m_size/2, m_size, m_size); } + // draw blind angle + if (m_blindAngle != 0) + { + painter.setBrush(QColor(48, 48, 48)); + painter.setPen(Qt::NoPen); + painter.rotate(m_azAnt - 90); + painter.drawPie(-m_size/2, -m_size/2, m_size, m_size, -m_blindAngle*16, m_blindAngle*32); + painter.rotate(180); + painter.drawPie(-m_size/2, -m_size/2, m_size, m_size, -m_blindAngle*16, m_blindAngle*32); + painter.rotate(-m_azAnt - 90); + painter.setBrush(Qt::NoBrush); + painter.setPen(whitePen); + painter.drawEllipse(-m_size/2, -m_size/2, m_size, m_size); + } // draw compass lines { @@ -222,7 +237,7 @@ void DOA2Compass::paintEvent(QPaintEvent *) painter.rotate(m_azNeg); painter.setPen(Qt::NoPen); - painter.setBrush(QBrush(QColor(0x80, 0x80, 0xFF, 0xA0))); + painter.setBrush(QBrush(QColor(0x80, 0xFF, 0x80, 0xA0))); QPointF points[3] = { QPointF(fx1, fy1), QPointF(fx2, fy2), @@ -230,7 +245,7 @@ void DOA2Compass::paintEvent(QPaintEvent *) }; painter.drawPolygon(points, 3); - painter.setPen(QColor(0x80, 0x80, 0xFF, 0xE0)); + painter.setPen(QColor(0x80, 0xFF, 0x80, 0xE0)); painter.drawLine(0, 0, 0, fyl); painter.rotate(-m_azNeg); diff --git a/plugins/channelmimo/doa2/doa2compass.h b/plugins/channelmimo/doa2/doa2compass.h index 68279adee..0b7571a88 100644 --- a/plugins/channelmimo/doa2/doa2compass.h +++ b/plugins/channelmimo/doa2/doa2compass.h @@ -91,6 +91,19 @@ public: emit canvasReplot(); } + /// + /// \brief Set half blind angle (in degree) + /// \param val - half blind angle (in degree) + /// + void setBlindAngle(double val) + { + m_blindAngle = val; + if( m_blindAngle < 0 ) m_blindAngle = 360 + m_blindAngle; + if( m_blindAngle > 360 ) m_blindAngle = m_blindAngle - 360; + + emit canvasReplot(); + } + /// /// \brief Draw legend in the center of the compass /// \param drawLegend - true to draw legend else false @@ -137,6 +150,7 @@ protected: double m_azPos; ///< forward (+) azimuth (in degree) double m_azNeg; ///< reverse (-) azimuth (in degree) double m_azAnt; ///< antennas azimuth from 1 (connected to stream 0) to 2 (connected to stream 1) + double m_blindAngle; //!< half the angle around antenna direction where DOA cannot be processed (when baseline distance exceeds half wavelength) }; #endif // INCLUDE_DOA2COMPASS_H diff --git a/plugins/channelmimo/doa2/doa2gui.cpp b/plugins/channelmimo/doa2/doa2gui.cpp index 137f5db5d..3a64ba1d4 100644 --- a/plugins/channelmimo/doa2/doa2gui.cpp +++ b/plugins/channelmimo/doa2/doa2gui.cpp @@ -154,10 +154,8 @@ DOA2GUI::DOA2GUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, MIMOChannel *ch connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); - // Test - ui->compass->setAzNeg(85); - ui->compass->setAzPos(315); - ui->compass->setAzAnt(20); + ui->halfWLLabel->setText(QString("%1/2").arg(QChar(0xBB, 0x03))); + ui->azUnits->setText(QString("%1").arg(QChar(0260))); } DOA2GUI::~DOA2GUI() @@ -204,6 +202,9 @@ void DOA2GUI::displaySettings() ui->phaseCorrectionText->setText(tr("%1").arg(m_settings.m_phase)); ui->compass->setAzAnt(m_settings.m_antennaAz); ui->antAz->setValue(m_settings.m_antennaAz); + ui->baselineDistance->setValue(m_settings.m_basebandDistance); + ui->squelch->setValue(m_settings.m_squelchdB); + ui->squelchText->setText(tr("%1").arg(m_settings.m_squelchdB, 3)); getRollupContents()->restoreState(m_rollupState); updateAbsoluteCenterFrequency(); blockApplySettings(false); @@ -330,6 +331,20 @@ void DOA2GUI::on_antAz_valueChanged(int value) applySettings(); } +void DOA2GUI::on_baselineDistance_valueChanged(int value) +{ + m_settings.m_basebandDistance = value < 1 ? 1 : value; + updateDOA(); + applySettings(); +} + +void DOA2GUI::on_squelch_valueChanged(int value) +{ + m_settings.m_squelchdB = value; + ui->squelchText->setText(tr("%1").arg(m_settings.m_squelchdB, 3)); + applySettings(); +} + void DOA2GUI::applyDecimation() { uint32_t maxHash = 1; @@ -372,20 +387,28 @@ void DOA2GUI::makeUIConnections() QObject::connect(ui->phaseCorrection, &QSlider::valueChanged, this, &DOA2GUI::on_phaseCorrection_valueChanged); QObject::connect(ui->correlationType, QOverload::of(&QComboBox::currentIndexChanged), this, &DOA2GUI::on_correlationType_currentIndexChanged); QObject::connect(ui->antAz, QOverload::of(&QSpinBox::valueChanged), this, &DOA2GUI::on_antAz_valueChanged); + QObject::connect(ui->baselineDistance, QOverload::of(&QSpinBox::valueChanged), this, &DOA2GUI::on_baselineDistance_valueChanged); + QObject::connect(ui->squelch, &QDial::valueChanged, this, &DOA2GUI::on_squelch_valueChanged); } void DOA2GUI::updateAbsoluteCenterFrequency() { - setStatusFrequency(m_centerFrequency + m_shiftFrequencyFactor * m_sampleRate); + qint64 cf = m_centerFrequency + m_shiftFrequencyFactor * m_sampleRate; + setStatusFrequency(cf); + m_hwl = 1.5e+8 / cf; + ui->halfWLText->setText(tr("%1").arg(m_hwl*1000, 5, 'f', 0)); } void DOA2GUI::updateDOA() { - float doaAngle = m_doa2->getPositiveDOA(); + float cosTheta = (m_doa2->getPhi() * m_hwl * 1000.0) / (M_PI * m_settings.m_basebandDistance); + float blindAngle = ((cosTheta < -1.0) || (cosTheta > 1.0) ? 0 : std::acos(m_hwl * 1000.0 / m_settings.m_basebandDistance)) * (180/M_PI); + float doaAngle = std::acos(cosTheta < -1.0 ? -1.0 : cosTheta > 1.0 ? 1.0 : cosTheta) * (180/M_PI); float posAngle = ui->antAz->value() - doaAngle; // DOA angles are trigonometric but displayed angles are clockwise float negAngle = ui->antAz->value() + doaAngle; + ui->compass->setBlindAngle(blindAngle); ui->compass->setAzPos(posAngle); ui->compass->setAzNeg(negAngle); - ui->posText->setText(tr("%1").arg(ui->compass->getAzPos(), 0, 'f', 0)); - ui->negText->setText(tr("%1").arg(ui->compass->getAzNeg(), 0, 'f', 0)); + ui->posText->setText(tr("%1").arg(ui->compass->getAzPos(), 3, 'f', 0, QLatin1Char('0'))); + ui->negText->setText(tr("%1").arg(ui->compass->getAzNeg(), 3, 'f', 0, QLatin1Char('0'))); } diff --git a/plugins/channelmimo/doa2/doa2gui.h b/plugins/channelmimo/doa2/doa2gui.h index a8d314b14..fdc9e13bd 100644 --- a/plugins/channelmimo/doa2/doa2gui.h +++ b/plugins/channelmimo/doa2/doa2gui.h @@ -74,6 +74,7 @@ private: ScopeVis* m_scopeVis; MessageQueue m_inputMessageQueue; uint32_t m_tickCount; + double m_hwl; //!< Half wavelength at center frequency explicit DOA2GUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, MIMOChannel *rxChannel, QWidget* parent = nullptr); virtual ~DOA2GUI(); @@ -99,6 +100,8 @@ private slots: void on_phaseCorrection_valueChanged(int value); void on_correlationType_currentIndexChanged(int index); void on_antAz_valueChanged(int value); + void on_baselineDistance_valueChanged(int value); + void on_squelch_valueChanged(int value); void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDialogCalled(const QPoint& p); void tick(); diff --git a/plugins/channelmimo/doa2/doa2gui.ui b/plugins/channelmimo/doa2/doa2gui.ui index 5f085cbfc..e4d4a1322 100644 --- a/plugins/channelmimo/doa2/doa2gui.ui +++ b/plugins/channelmimo/doa2/doa2gui.ui @@ -7,7 +7,7 @@ 0 0 720 - 770 + 642 @@ -403,7 +403,7 @@ 0 432 718 - 85 + 202 @@ -428,133 +428,328 @@ 2 - - 2 - - - 12 - 2 - - - + + + - - - Pos + + + + 0 + 0 + - - - - - - 000 - - - - - - 10 - 0 + 359 + 200 - - + + + Liberation Sans + 9 + + + + + + + 6 + - - - Neg - - + + + + + Pos + + + + + + + Port side arrival angle (degrees) + + + 000 + + + + + + + + 10 + 0 + + + + + + + + + + + Neg + + + + + + + Starboard side arrival angle (degrees) + + + 000 + + + + + + + + 10 + 0 + + + + + + + + + + + Ant + + + + + + + + 60 + 0 + + + + Antennas line azimuth (degrees) + + + true + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 359 + + + + + + + d + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - 000 - - + + + + + L/2 + + + + + + + Half wavelength (mm) + + + 00000 + + + + + + + + 10 + 0 + + + + + + + + + + + D + + + + + + + Baseline distance (mm) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 1 + + + 99999 + + + 500 + + + + + + + mm + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - 10 - 0 - - - - - - + + + + + Sq + + + + + + + + 24 + 24 + + + + Squelch threshold (dB) + + + -140 + + + 0 + + + 1 + + + 1 + + + -50 + + + + + + + + 28 + 0 + + + + Squelch threshold (dB) + + + -100 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + dB + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - Ant - - - - - - - - 60 - 0 - - - - true - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 359 - - - - - + - Qt::Horizontal + Qt::Vertical - 40 - 20 + 20 + 40 - - - - - - - - 359 - 200 - - - - - Liberation Sans - 9 - - - - diff --git a/plugins/channelmimo/doa2/doa2settings.cpp b/plugins/channelmimo/doa2/doa2settings.cpp index ace849550..75e3e6efd 100644 --- a/plugins/channelmimo/doa2/doa2settings.cpp +++ b/plugins/channelmimo/doa2/doa2settings.cpp @@ -46,6 +46,8 @@ void DOA2Settings::resetToDefaults() m_workspaceIndex = 0; m_hidden = false; m_antennaAz = 0; + m_basebandDistance = 500; + m_squelchdB = -50; } QByteArray DOA2Settings::serialize() const @@ -67,6 +69,8 @@ QByteArray DOA2Settings::serialize() const s.writeBlob(14, m_geometryBytes); s.writeBool(15, m_hidden); s.writeS32(16, m_antennaAz); + s.writeU32(17, m_basebandDistance); + s.writeS32(18, m_squelchdB); if (m_scopeGUI) { s.writeBlob(21, m_scopeGUI->serialize()); @@ -125,6 +129,9 @@ bool DOA2Settings::deserialize(const QByteArray& data) d.readBool(15, &m_hidden, false); d.readS32(16, &tmp, 0); m_antennaAz = tmp < 0 ? 0 : tmp > 359 ? 359 : tmp; + d.readU32(17, &utmp, 500); + m_basebandDistance = utmp == 0 ? 1 : utmp; + d.readS32(18, &m_squelchdB, -50); if (m_scopeGUI) { diff --git a/plugins/channelmimo/doa2/doa2settings.h b/plugins/channelmimo/doa2/doa2settings.h index cf0496b4f..4e4f7bc49 100644 --- a/plugins/channelmimo/doa2/doa2settings.h +++ b/plugins/channelmimo/doa2/doa2settings.h @@ -39,6 +39,8 @@ struct DOA2Settings uint32_t m_filterChainHash; int m_phase; int m_antennaAz; + uint32_t m_basebandDistance; //!< in millimeters + int m_squelchdB; bool m_useReverseAPI; QString m_reverseAPIAddress; uint16_t m_reverseAPIPort;