1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-26 14:56:33 -04:00

DOA2: added more controls and details

This commit is contained in:
f4exb 2022-05-28 05:48:49 +02:00
parent 0f7972856f
commit 1edf7a008d
10 changed files with 398 additions and 113 deletions

View File

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

View File

@ -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<Complex>::iterator& begin, int n
for (std::vector<Complex>::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;

View File

@ -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<SampleVector>& 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<SampleVector::const_iterator> m_vbegin;
int m_sizes[2];

View File

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

View File

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

View File

@ -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<int>::of(&QComboBox::currentIndexChanged), this, &DOA2GUI::on_correlationType_currentIndexChanged);
QObject::connect(ui->antAz, QOverload<int>::of(&QSpinBox::valueChanged), this, &DOA2GUI::on_antAz_valueChanged);
QObject::connect(ui->baselineDistance, QOverload<int>::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')));
}

View File

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

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>720</width>
<height>770</height>
<height>642</height>
</rect>
</property>
<property name="sizePolicy">
@ -403,7 +403,7 @@
<x>0</x>
<y>432</y>
<width>718</width>
<height>85</height>
<height>202</height>
</rect>
</property>
<property name="sizePolicy">
@ -428,133 +428,328 @@
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<layout class="QHBoxLayout" name="doaValuesLayout">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="posLabel">
<property name="text">
<string>Pos</string>
<widget class="DOA2Compass" name="compass" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="posText">
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spcLabel1">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
<width>359</width>
<height>200</height>
</size>
</property>
<property name="text">
<string/>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="negLabel">
<property name="text">
<string>Neg</string>
</property>
</widget>
<layout class="QHBoxLayout" name="doaValuesLayout">
<item>
<widget class="QLabel" name="posLabel">
<property name="text">
<string>Pos</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="posText">
<property name="toolTip">
<string>Port side arrival angle (degrees)</string>
</property>
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spcLabel1">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="negLabel">
<property name="text">
<string>Neg</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="negText">
<property name="toolTip">
<string>Starboard side arrival angle (degrees)</string>
</property>
<property name="text">
<string>000</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spcLabel1_2">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="antLabel">
<property name="text">
<string>Ant</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="antAz">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Antennas line azimuth (degrees)</string>
</property>
<property name="wrapping">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>359</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="azUnits">
<property name="text">
<string>d</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="negText">
<property name="text">
<string>000</string>
</property>
</widget>
<layout class="QHBoxLayout" name="baselineLayout">
<item>
<widget class="QLabel" name="halfWLLabel">
<property name="text">
<string>L/2</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="halfWLText">
<property name="toolTip">
<string>Half wavelength (mm)</string>
</property>
<property name="text">
<string>00000</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spcLabel1_3">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="baselineDistLabel">
<property name="text">
<string>D</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="baselineDistance">
<property name="toolTip">
<string>Baseline distance (mm)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="disanceUnits">
<property name="text">
<string>mm</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="spcLabel1_2">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
<layout class="QHBoxLayout" name="squelchLayout">
<item>
<widget class="QLabel" name="squelchLabel">
<property name="text">
<string>Sq</string>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="squelch">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Squelch threshold (dB)</string>
</property>
<property name="minimum">
<number>-140</number>
</property>
<property name="maximum">
<number>0</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>-50</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="squelchText">
<property name="minimumSize">
<size>
<width>28</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Squelch threshold (dB)</string>
</property>
<property name="text">
<string>-100</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="squelchUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="antLabel">
<property name="text">
<string>Ant</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="antAz">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="wrapping">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>359</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout"/>
</item>
<item row="0" column="0" rowspan="2">
<widget class="DOA2Compass" name="compass" native="true">
<property name="minimumSize">
<size>
<width>359</width>
<height>200</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

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

View File

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