Merge branch 'f4exb:master' into mac_package

pull/1261/head
srcejon 2022-05-29 11:21:53 +01:00 zatwierdzone przez GitHub
commit fe7f98c70e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
67 zmienionych plików z 6468 dodań i 18 usunięć

Wyświetl plik

@ -1,3 +1,15 @@
sdrangel (7.3.0-1) unstable; urgency=medium
* New Direction Of Arrival with 2 sources MI plugin. Implements #444
* Upgraded codec2 to v1.0.3. Fixes #1256
* FreeDV mod: improved output power
* FreeDV plugins: fixed spanLog2 GUI connection
* Fixed Interferometer and BeamSteeringCW absolute center frequency display
* Fixed Interferometer and BeamSteeringCW MIMO channel plugins
* Fixed Mac build. PR #1257
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Thu, 26 May 2022 03:12:00 +0200
sdrangel (7.2.1-1) unstable; urgency=medium
* AMBE feature: fixed many errors preventing build on Windows and MacOS. Issue #1254

Wyświetl plik

@ -15,8 +15,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# configure version
set(sdrangel_VERSION_MAJOR "7")
set(sdrangel_VERSION_MINOR "2")
set(sdrangel_VERSION_PATCH "1")
set(sdrangel_VERSION_MINOR "3")
set(sdrangel_VERSION_PATCH "0")
set(sdrangel_VERSION_SUFFIX "")
# SDRAngel cmake options

Wyświetl plik

@ -9,7 +9,7 @@ mkdir -p drowe67 && cd drowe67
git clone https://github.com/drowe67/codec2.git
cd codec2
git reset --hard "v0.9.2"
git reset --hard "v1.0.3"
mkdir -p build && cd build
cmake ..
sudo make install

12
debian/changelog vendored
Wyświetl plik

@ -1,3 +1,15 @@
sdrangel (7.3.0-1) unstable; urgency=medium
* New Direction Of Arrival with 2 sources MI plugin. Implements #444
* Upgraded codec2 to v1.0.3. Fixes #1256
* FreeDV mod: improved output power
* FreeDV plugins: fixed spanLog2 GUI connection
* Fixed Interferometer and BeamSteeringCW absolute center frequency display
* Fixed Interferometer and BeamSteeringCW MIMO channel plugins
* Fixed Mac build. PR #1257
-- Edouard Griffiths, F4EXB <f4exb06@gmail.com> Sun, 29 May 2022 10:05:21 +0200
sdrangel (7.2.1-1) unstable; urgency=medium
* AMBE feature: fixed many errors preventing build on Windows and MacOS. Issue #1254

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 130 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 32 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 33 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 338 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 29 KiB

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -8,7 +8,7 @@
# but make the configuration dirty
# which version/tag/checkout to use
set(CODEC2_TAG "v0.9.2")
set(CODEC2_TAG "v1.0.3")
set(CM256CC_TAG c0e92b92aca3d1d36c990b642b937c64d363c559)
set(MBELIB_TAG fe83b32c6a60cdd7bce8cecf3c7a0b9ec87a7667)
set(SERIALDV_TAG "v1.1.4")

Wyświetl plik

@ -1,6 +1,7 @@
project(channelmimo)
add_subdirectory(beamsteeringcwmod)
add_subdirectory(doa2)
if (NOT SERVER_MODE)
add_subdirectory(interferometer)

Wyświetl plik

@ -29,7 +29,7 @@
const PluginDescriptor BeamSteeringCWModPlugin::m_pluginDescriptor = {
BeamSteeringCWMod::m_channelId,
QStringLiteral("BeamSteeringCWMod"),
QStringLiteral("7.0.0"),
QStringLiteral("7.3.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,

Wyświetl plik

@ -0,0 +1,69 @@
project(doa2)
set(doa2_SOURCES
doa2.cpp
doa2corr.cpp
doa2settings.cpp
doa2baseband.cpp
doa2streamsink.cpp
doa2plugin.cpp
doa2webapiadapter.cpp
)
set(doa2_HEADERS
doa2.h
doa2corr.h
doa2settings.h
doa2baseband.h
doa2streamsink.h
doa2plugin.h
doa2webapiadapter.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
${Boost_INCLUDE_DIR}
)
if (NOT SERVER_MODE)
set(doa2_SOURCES
${doa2_SOURCES}
doa2gui.cpp
doa2compass.cpp
doa2gui.ui
)
set(doa2_HEADERS
${doa2_HEADERS}
doa2gui.h
doa2compass.cpp
)
set(TARGET_NAME doa2)
set(TARGET_LIB "Qt5::Widgets")
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME doa2srv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${doa2_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt5::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
swagger
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

Wyświetl plik

@ -0,0 +1,684 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QThread>
#include <QDebug>
#include <QBuffer>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "SWGChannelSettings.h"
#include "SWGWorkspaceInfo.h"
#include "SWGChannelReport.h"
#include "device/deviceapi.h"
#include "dsp/hbfilterchainconverter.h"
#include "dsp/dspcommands.h"
#include "feature/feature.h"
#include "util/db.h"
#include "maincore.h"
#include "doa2baseband.h"
#include "doa2.h"
MESSAGE_CLASS_DEFINITION(DOA2::MsgConfigureDOA2, Message)
MESSAGE_CLASS_DEFINITION(DOA2::MsgBasebandNotification, Message)
const char* const DOA2::m_channelIdURI = "sdrangel.channel.doa2";
const char* const DOA2::m_channelId = "DOA2";
const int DOA2::m_fftSize = 4096;
DOA2::DOA2(DeviceAPI *deviceAPI) :
ChannelAPI(m_channelIdURI, ChannelAPI::StreamMIMO),
m_deviceAPI(deviceAPI),
m_guiMessageQueue(nullptr),
m_frequencyOffset(0),
m_deviceSampleRate(48000),
m_deviceCenterFrequency(435000000)
{
setObjectName(m_channelId);
m_thread = new QThread(this);
m_basebandSink = new DOA2Baseband(m_fftSize);
m_basebandSink->setScopeSink(&m_scopeSink);
m_basebandSink->moveToThread(m_thread);
m_deviceAPI->addMIMOChannel(this);
m_deviceAPI->addMIMOChannelAPI(this);
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&DOA2::networkManagerFinished
);
}
DOA2::~DOA2()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&DOA2::networkManagerFinished
);
delete m_networkManager;
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeMIMOChannel(this);
delete m_basebandSink;
delete m_thread;
}
void DOA2::setDeviceAPI(DeviceAPI *deviceAPI)
{
if (deviceAPI != m_deviceAPI)
{
m_deviceAPI->removeChannelSinkAPI(this);
m_deviceAPI->removeMIMOChannel(this);
m_deviceAPI = deviceAPI;
m_deviceAPI->addMIMOChannel(this);
m_deviceAPI->addChannelSinkAPI(this);
}
}
void DOA2::startSinks()
{
if (m_deviceSampleRate != 0) {
m_basebandSink->setBasebandSampleRate(m_deviceSampleRate);
}
m_basebandSink->reset();
m_thread->start();
DOA2Baseband::MsgConfigureChannelizer *msg = DOA2Baseband::MsgConfigureChannelizer::create(
m_settings.m_log2Decim, m_settings.m_filterChainHash);
m_basebandSink->getInputMessageQueue()->push(msg);
}
void DOA2::stopSinks()
{
m_thread->exit();
m_thread->wait();
}
void DOA2::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, unsigned int sinkIndex)
{
m_basebandSink->feed(begin, end, sinkIndex);
}
void DOA2::pull(SampleVector::iterator& begin, unsigned int nbSamples, unsigned int sourceIndex)
{
(void) begin;
(void) nbSamples;
(void) sourceIndex;
}
void DOA2::applySettings(const DOA2Settings& settings, bool force)
{
qDebug() << "DOA2::applySettings: "
<< "m_correlationType: " << settings.m_correlationType
<< "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_fftAveragingIndex: "<< settings.m_fftAveragingIndex
<< "m_useReverseAPI: " << settings.m_useReverseAPI
<< "m_reverseAPIAddress: " << settings.m_reverseAPIAddress
<< "m_reverseAPIPort: " << settings.m_reverseAPIPort
<< "m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex
<< "m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex
<< "m_title: " << settings.m_title;
QList<QString> reverseAPIKeys;
if ((m_settings.m_correlationType != settings.m_correlationType) || force) {
reverseAPIKeys.append("correlationType");
}
if ((m_settings.m_filterChainHash != settings.m_filterChainHash) || force) {
reverseAPIKeys.append("filterChainHash");
}
if ((m_settings.m_log2Decim != settings.m_log2Decim) || force) {
reverseAPIKeys.append("log2Decim");
}
if ((m_settings.m_phase != settings.m_phase) || force) {
reverseAPIKeys.append("phase");
}
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_fftAveragingIndex != settings.m_fftAveragingIndex) || force)
{
reverseAPIKeys.append("m_fftAveragingIndex");
m_basebandSink->setFFTAveraging(DOA2Settings::getAveragingValue(settings.m_fftAveragingIndex));
}
if ((m_settings.m_log2Decim != settings.m_log2Decim)
|| (m_settings.m_filterChainHash != settings.m_filterChainHash) || force)
{
DOA2Baseband::MsgConfigureChannelizer *msg = DOA2Baseband::MsgConfigureChannelizer::create(
settings.m_log2Decim, settings.m_filterChainHash);
m_basebandSink->getInputMessageQueue()->push(msg);
}
if ((m_settings.m_correlationType != settings.m_correlationType) || force)
{
DOA2Baseband::MsgConfigureCorrelation *msg = DOA2Baseband::MsgConfigureCorrelation::create(
settings.m_correlationType);
m_basebandSink->getInputMessageQueue()->push(msg);
}
if ((m_settings.m_phase != settings.m_phase) || force) {
m_basebandSink->setPhase(settings.m_phase);
}
QList<ObjectPipe*> pipes;
MainCore::instance()->getMessagePipes().getMessagePipes(this, "settings", pipes);
if (pipes.size() > 0) {
sendChannelSettings(pipes, reverseAPIKeys, settings, force);
}
m_settings = settings;
}
void DOA2::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
bool DOA2::handleMessage(const Message& cmd)
{
if (MsgConfigureDOA2::match(cmd))
{
MsgConfigureDOA2& cfg = (MsgConfigureDOA2&) cmd;
qDebug() << "DOA2::handleMessage: MsgConfigureDOA2";
applySettings(cfg.getSettings(), cfg.getForce());
return true;
}
else if (DSPMIMOSignalNotification::match(cmd))
{
DSPMIMOSignalNotification& notif = (DSPMIMOSignalNotification&) cmd;
qDebug() << "DOA2::handleMessage: DSPMIMOSignalNotification:"
<< " inputSampleRate: " << notif.getSampleRate()
<< " centerFrequency: " << notif.getCenterFrequency()
<< " sourceElseSink: " << notif.getSourceOrSink()
<< " streamIndex: " << notif.getIndex();
if (notif.getSourceOrSink()) // deals with source messages only
{
m_deviceSampleRate = notif.getSampleRate();
m_deviceCenterFrequency = notif.getCenterFrequency();
calculateFrequencyOffset(); // This is when device sample rate changes
// Notify baseband sink of input sample rate change
DOA2Baseband::MsgSignalNotification *sig = DOA2Baseband::MsgSignalNotification::create(
m_deviceSampleRate, notif.getCenterFrequency(), notif.getIndex()
);
qDebug() << "DOA2::handleMessage: DSPMIMOSignalNotification: push to sink";
m_basebandSink->getInputMessageQueue()->push(sig);
if (getMessageQueueToGUI())
{
qDebug() << "DOA2::handleMessage: DSPMIMOSignalNotification: push to GUI";
MsgBasebandNotification *msg = MsgBasebandNotification::create(
notif.getSampleRate(), notif.getCenterFrequency());
getMessageQueueToGUI()->push(msg);
}
}
return true;
}
else
{
return false;
}
}
QByteArray DOA2::serialize() const
{
return m_settings.serialize();
}
bool DOA2::deserialize(const QByteArray& data)
{
(void) data;
if (m_settings.deserialize(data))
{
MsgConfigureDOA2 *msg = MsgConfigureDOA2::create(m_settings, true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureDOA2 *msg = MsgConfigureDOA2::create(m_settings, true);
m_inputMessageQueue.push(msg);
return false;
}
}
void DOA2::validateFilterChainHash(DOA2Settings& settings)
{
unsigned int s = 1;
for (unsigned int i = 0; i < settings.m_log2Decim; i++) {
s *= 3;
}
settings.m_filterChainHash = settings.m_filterChainHash >= s ? s-1 : settings.m_filterChainHash;
}
void DOA2::calculateFrequencyOffset()
{
double shiftFactor = HBFilterChainConverter::getShiftFactor(m_settings.m_log2Decim, m_settings.m_filterChainHash);
m_frequencyOffset = m_deviceSampleRate * shiftFactor;
}
void DOA2::applyChannelSettings(uint32_t log2Decim, uint32_t filterChainHash)
{
DOA2Baseband::MsgConfigureChannelizer *msg = DOA2Baseband::MsgConfigureChannelizer::create(log2Decim, filterChainHash);
m_basebandSink->getInputMessageQueue()->push(msg);
}
float DOA2::getPhi() const
{
return m_basebandSink ? m_basebandSink->getPhi() : 0.0f;
}
float DOA2::getPositiveDOA() const
{
return std::acos(getPhi()/M_PI)*(180/M_PI);
}
int DOA2::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDoa2Settings(new SWGSDRangel::SWGDOA2Settings());
response.getDoa2Settings()->init();
webapiFormatChannelSettings(response, m_settings);
return 200;
}
int DOA2::webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& response,
QString& errorMessage)
{
(void) errorMessage;
response.setIndex(m_settings.m_workspaceIndex);
return 200;
}
int DOA2::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
DOA2Settings settings = m_settings;
webapiUpdateChannelSettings(settings, channelSettingsKeys, response);
MsgConfigureDOA2 *msg = MsgConfigureDOA2::create(settings, force);
m_inputMessageQueue.push(msg);
if (getMessageQueueToGUI()) // forward to GUI if any
{
MsgConfigureDOA2 *msgToGUI = MsgConfigureDOA2::create(settings, force);
getMessageQueueToGUI()->push(msgToGUI);
}
webapiFormatChannelSettings(response, settings);
return 200;
}
int DOA2::webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDoa2Report(new SWGSDRangel::SWGDOA2Report());
response.getDoa2Report()->init();
webapiFormatChannelReport(response);
return 200;
}
void DOA2::webapiUpdateChannelSettings(
DOA2Settings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getDoa2Settings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getDoa2Settings()->getTitle();
}
if (channelSettingsKeys.contains("log2Decim")) {
settings.m_log2Decim = response.getDoa2Settings()->getLog2Decim();
}
if (channelSettingsKeys.contains("filterChainHash"))
{
settings.m_filterChainHash = response.getDoa2Settings()->getFilterChainHash();
validateFilterChainHash(settings);
}
if (channelSettingsKeys.contains("phase")) {
settings.m_phase = response.getDoa2Settings()->getPhase();
}
if (channelSettingsKeys.contains("antennaAz")) {
settings.m_antennaAz = response.getDoa2Settings()->getAntennaAz();
}
if (channelSettingsKeys.contains("basebandDistance")) {
settings.m_basebandDistance = response.getDoa2Settings()->getBasebandDistance();
}
if (channelSettingsKeys.contains("squelchdB")) {
settings.m_squelchdB = response.getDoa2Settings()->getSquelchdB();
}
if (channelSettingsKeys.contains("fftAveragingValue")) {
settings.m_fftAveragingIndex = DOA2Settings::getAveragingIndex(response.getDoa2Settings()->getFftAveragingValue());
}
if (channelSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getDoa2Settings()->getUseReverseApi() != 0;
}
if (channelSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getDoa2Settings()->getReverseApiAddress();
}
if (channelSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getDoa2Settings()->getReverseApiPort();
}
if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) {
settings.m_reverseAPIDeviceIndex = response.getDoa2Settings()->getReverseApiDeviceIndex();
}
if (channelSettingsKeys.contains("reverseAPIChannelIndex")) {
settings.m_reverseAPIChannelIndex = response.getDoa2Settings()->getReverseApiChannelIndex();
}
if (settings.m_scopeGUI && channelSettingsKeys.contains("scopeConfig")) {
settings.m_scopeGUI->updateFrom(channelSettingsKeys, response.getDoa2Settings()->getScopeConfig());
}
if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) {
settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getDoa2Settings()->getChannelMarker());
}
if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(channelSettingsKeys, response.getDoa2Settings()->getRollupState());
}
}
void DOA2::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const DOA2Settings& settings)
{
response.getDoa2Settings()->setRgbColor(settings.m_rgbColor);
if (response.getDoa2Settings()->getTitle()) {
*response.getDoa2Settings()->getTitle() = settings.m_title;
} else {
response.getDoa2Settings()->setTitle(new QString(settings.m_title));
}
response.getDoa2Settings()->setLog2Decim(settings.m_log2Decim);
response.getDoa2Settings()->setFilterChainHash(settings.m_filterChainHash);
response.getDoa2Settings()->setPhase(settings.m_phase);
response.getDoa2Settings()->setAntennaAz(settings.m_antennaAz);
response.getDoa2Settings()->setBasebandDistance(settings.m_basebandDistance);
response.getDoa2Settings()->setSquelchdB(settings.m_squelchdB);
response.getDoa2Settings()->setFftAveragingValue(DOA2Settings::getAveragingValue(settings.m_fftAveragingIndex));
response.getDoa2Settings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getDoa2Settings()->getReverseApiAddress()) {
*response.getDoa2Settings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getDoa2Settings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getDoa2Settings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getDoa2Settings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex);
response.getDoa2Settings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex);
if (settings.m_scopeGUI)
{
if (response.getDoa2Settings()->getScopeConfig())
{
settings.m_scopeGUI->formatTo(response.getDoa2Settings()->getScopeConfig());
}
else
{
SWGSDRangel::SWGGLScope *swgGLScope = new SWGSDRangel::SWGGLScope();
settings.m_scopeGUI->formatTo(swgGLScope);
response.getDoa2Settings()->setScopeConfig(swgGLScope);
}
}
if (settings.m_channelMarker)
{
if (response.getDoa2Settings()->getChannelMarker())
{
settings.m_channelMarker->formatTo(response.getDoa2Settings()->getChannelMarker());
}
else
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
response.getDoa2Settings()->setChannelMarker(swgChannelMarker);
}
}
if (settings.m_rollupState)
{
if (response.getDoa2Settings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getDoa2Settings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getDoa2Settings()->setRollupState(swgRollupState);
}
}
}
void DOA2::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response)
{
float phi = normalizeAngle(getPhi() * (180/M_PI), 180.0f);
response.getDoa2Report()->setPhi(phi);
float hwl = 1.5e8 / (m_deviceCenterFrequency + m_frequencyOffset);
float cosTheta = (getPhi() * hwl * 1000.0) / (M_PI * m_settings.m_basebandDistance);
// float blindAngle = ((cosTheta < -1.0) || (cosTheta > 1.0) ? 0 : std::acos(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);
qDebug("DOA2::webapiFormatChannelReport: phi: %f cosT: %f DOAngle: %f", getPhi(), cosTheta, doaAngle);
float posAngle = normalizeAngle(m_settings.m_antennaAz - doaAngle, 360.0f); // DOA angles are trigonometric but displayed angles are clockwise
float negAngle = normalizeAngle(m_settings.m_antennaAz + doaAngle, 360.0f);
response.getDoa2Report()->setPosAz(posAngle);
response.getDoa2Report()->setNegAz(negAngle);
response.getDoa2Report()->setFftSize(m_fftSize);
int channelSampleRate = m_deviceSampleRate / (1<<m_settings.m_log2Decim);
response.getDoa2Report()->setChannelSampleRate(channelSampleRate);
}
void DOA2::webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DOA2Settings& settings, bool force)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIDeviceIndex)
.arg(settings.m_reverseAPIChannelIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgChannelSettings->asJson().toUtf8());
buffer->seek(0);
// Always use PATCH to avoid passing reverse API settings
QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer);
reply->setParent(buffer);
delete swgChannelSettings;
}
void DOA2::sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const DOA2Settings& settings,
bool force)
{
for (const auto& pipe : pipes)
{
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
if (messageQueue)
{
SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings();
webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force);
MainCore::MsgChannelSettings *msg = MainCore::MsgChannelSettings::create(
this,
channelSettingsKeys,
swgChannelSettings,
force
);
messageQueue->push(msg);
}
}
}
void DOA2::webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DOA2Settings& settings,
bool force
)
{
swgChannelSettings->setDirection(2); // MIMO sink
swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet());
swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex());
swgChannelSettings->setChannelType(new QString("DOA2"));
swgChannelSettings->setDoa2Settings(new SWGSDRangel::SWGDOA2Settings());
SWGSDRangel::SWGDOA2Settings *swgDOA2Settings = swgChannelSettings->getDoa2Settings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (channelSettingsKeys.contains("rgbColor") || force) {
swgDOA2Settings->setRgbColor(settings.m_rgbColor);
}
if (channelSettingsKeys.contains("title") || force) {
swgDOA2Settings->setTitle(new QString(settings.m_title));
}
if (channelSettingsKeys.contains("log2Decim") || force) {
swgDOA2Settings->setLog2Decim(settings.m_log2Decim);
}
if (channelSettingsKeys.contains("filterChainHash") || force) {
swgDOA2Settings->setFilterChainHash(settings.m_filterChainHash);
}
if (channelSettingsKeys.contains("phase") || force) {
swgDOA2Settings->setPhase(settings.m_phase);
}
if (channelSettingsKeys.contains("antennaAz") || force) {
swgDOA2Settings->setAntennaAz(settings.m_antennaAz);
}
if (channelSettingsKeys.contains("basebandDistance") || force) {
swgDOA2Settings->setBasebandDistance(settings.m_basebandDistance);
}
if (channelSettingsKeys.contains("squelchdB") || force) {
swgDOA2Settings->setSquelchdB(settings.m_squelchdB);
}
if (channelSettingsKeys.contains("fftAveragingValue") || force) {
swgDOA2Settings->setFftAveragingValue(DOA2Settings::getAveragingValue(settings.m_fftAveragingIndex));
}
if (settings.m_scopeGUI)
{
if (channelSettingsKeys.contains("scopeConfig") || force) {
settings.m_scopeGUI->formatTo(swgDOA2Settings->getScopeConfig());
}
}
if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force))
{
SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker();
settings.m_channelMarker->formatTo(swgChannelMarker);
swgDOA2Settings->setChannelMarker(swgChannelMarker);
}
if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force))
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
swgDOA2Settings->setRollupState(swgRollupState);
}
}
void DOA2::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "DOA2::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("DOA2::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}
float DOA2::normalizeAngle(float angle, float max)
{
if (angle < 0) { return max + angle; }
if (angle > max) { return angle - max; }
return angle;
}

Wyświetl plik

@ -0,0 +1,203 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2_H
#define INCLUDE_DOA2_H
#include <vector>
#include <QNetworkRequest>
#include "dsp/mimochannel.h"
#include "dsp/scopevis.h"
#include "channel/channelapi.h"
#include "util/messagequeue.h"
#include "util/message.h"
#include "doa2settings.h"
class QThread;
class DeviceAPI;
class DOA2Baseband;
class QNetworkReply;
class QNetworkAccessManager;
class ObjectPipe;
class DOA2: public MIMOChannel, public ChannelAPI
{
public:
class MsgConfigureDOA2 : public Message {
MESSAGE_CLASS_DECLARATION
public:
const DOA2Settings& getSettings() const { return m_settings; }
bool getForce() const { return m_force; }
static MsgConfigureDOA2* create(const DOA2Settings& settings, bool force)
{
return new MsgConfigureDOA2(settings, force);
}
private:
DOA2Settings m_settings;
bool m_force;
MsgConfigureDOA2(const DOA2Settings& settings, bool force) :
Message(),
m_settings(settings),
m_force(force)
{ }
};
class MsgBasebandNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
static MsgBasebandNotification* create(int sampleRate, qint64 centerFrequency) {
return new MsgBasebandNotification(sampleRate, centerFrequency);
}
int getSampleRate() const { return m_sampleRate; }
qint64 getCenterFrequency() const { return m_centerFrequency; }
private:
MsgBasebandNotification(int sampleRate, qint64 centerFrequency) :
Message(),
m_sampleRate(sampleRate),
m_centerFrequency(centerFrequency)
{ }
int m_sampleRate;
qint64 m_centerFrequency;
};
DOA2(DeviceAPI *deviceAPI);
virtual ~DOA2();
virtual void destroy() { delete this; }
virtual void setDeviceAPI(DeviceAPI *deviceAPI);
virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; }
virtual void startSinks(); //!< thread start()
virtual void stopSinks(); //!< thread exit() and wait()
virtual void startSources() {}
virtual void stopSources() {}
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, unsigned int sinkIndex);
virtual void pull(SampleVector::iterator& begin, unsigned int nbSamples, unsigned int sourceIndex);
virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); }
virtual QString getMIMOName() { return objectName(); }
virtual void getIdentifier(QString& id) { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) { title = "DOA 2 sources"; }
virtual qint64 getCenterFrequency() const { return m_frequencyOffset; }
virtual void setCenterFrequency(qint64) {}
uint32_t getDeviceSampleRate() const { return m_deviceSampleRate; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int getNbSinkStreams() const { return 2; }
virtual int getNbSourceStreams() const { return 0; }
virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const
{
(void) streamIndex;
(void) sinkElseSource;
return m_frequencyOffset;
}
virtual void setMessageQueueToGUI(MessageQueue *queue) { m_guiMessageQueue = queue; }
MessageQueue *getMessageQueueToGUI() { return m_guiMessageQueue; }
ScopeVis *getScopeVis() { return &m_scopeSink; }
void applyChannelSettings(uint32_t log2Decim, uint32_t filterChainHash);
float getPhi() const;
float getPositiveDOA() const;
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiReportGet(
SWGSDRangel::SWGChannelReport& response,
QString& errorMessage);
virtual int webapiWorkspaceGet(
SWGSDRangel::SWGWorkspaceInfo& query,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const DOA2Settings& settings);
static void webapiUpdateChannelSettings(
DOA2Settings& settings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
static const char* const m_channelIdURI;
static const char* const m_channelId;
static const int m_fftSize;
private:
DeviceAPI *m_deviceAPI;
QThread *m_thread;
ScopeVis m_scopeSink;
DOA2Baseband* m_basebandSink;
DOA2Settings m_settings;
MessageQueue *m_guiMessageQueue; //!< Input message queue to the GUI
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
int64_t m_frequencyOffset;
uint32_t m_deviceSampleRate;
qint64 m_deviceCenterFrequency;
int m_count0, m_count1;
virtual bool handleMessage(const Message& cmd); //!< Processing of a message. Returns true if message has actually been processed
void applySettings(const DOA2Settings& settings, bool force = false);
static void validateFilterChainHash(DOA2Settings& settings);
void calculateFrequencyOffset();
void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response);
void webapiReverseSendSettings(QList<QString>& channelSettingsKeys, const DOA2Settings& settings, bool force);
void sendChannelSettings(
const QList<ObjectPipe*>& pipes,
QList<QString>& channelSettingsKeys,
const DOA2Settings& settings,
bool force
);
void webapiFormatChannelSettings(
QList<QString>& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings *swgChannelSettings,
const DOA2Settings& settings,
bool force
);
static float normalizeAngle(float angle, float max);
private slots:
void handleInputMessages();
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_DOA2_H

Wyświetl plik

@ -0,0 +1,303 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QMutexLocker>
#include <QDebug>
#include "dsp/downchannelizer.h"
#include "dsp/basebandsamplesink.h"
#include "dsp/scopevis.h"
#include "dsp/dspcommands.h"
#include "doa2baseband.h"
#include "doa2settings.h"
MESSAGE_CLASS_DEFINITION(DOA2Baseband::MsgConfigureChannelizer, Message)
MESSAGE_CLASS_DEFINITION(DOA2Baseband::MsgSignalNotification, Message)
MESSAGE_CLASS_DEFINITION(DOA2Baseband::MsgConfigureCorrelation, Message)
DOA2Baseband::DOA2Baseband(int fftSize) :
m_correlator(fftSize),
m_correlationType(DOA2Settings::CorrelationFFT),
m_fftSize(fftSize),
m_samplesCount(0),
m_magSum(0.0f),
m_wphSum(0.0f),
m_phi(0.0f),
m_magThreshold(0.0f),
m_fftAvg(1),
m_fftAvgCount(0),
m_scopeSink(nullptr),
m_mutex(QMutex::Recursive)
{
m_sampleMIFifo.init(2, 96000 * 8);
m_vbegin.resize(2);
for (int i = 0; i < 2; i++)
{
m_sinks[i].setStreamIndex(i);
m_channelizers[i] = new DownChannelizer(&m_sinks[i]);
m_sizes[i] = 0;
}
QObject::connect(
&m_sampleMIFifo,
&SampleMIFifo::dataSyncReady,
this,
&DOA2Baseband::handleData,
Qt::QueuedConnection
);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_lastStream = 0;
}
DOA2Baseband::~DOA2Baseband()
{
for (int i = 0; i < 2; i++)
{
delete m_channelizers[i];
}
}
void DOA2Baseband::reset()
{
QMutexLocker mutexLocker(&m_mutex);
m_sampleMIFifo.reset();
for (int i = 0; i < 2; i++) {
m_sinks[i].reset();
}
}
void DOA2Baseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, unsigned int streamIndex)
{
if (streamIndex > 1) {
return;
}
if (streamIndex == m_lastStream) {
qWarning("DOA2Baseband::feed: twice same stream in a row: %u", streamIndex);
}
m_lastStream = streamIndex;
m_vbegin[streamIndex] = begin;
m_sizes[streamIndex] = end - begin;
if (streamIndex == 1)
{
if (m_sizes[0] != m_sizes[1])
{
qWarning("DOA2Baseband::feed: unequal sizes: [0]: %d [1]: %d", m_sizes[0], m_sizes[1]);
m_sampleMIFifo.writeSync(m_vbegin, std::min(m_sizes[0], m_sizes[1]));
}
else
{
m_sampleMIFifo.writeSync(m_vbegin, m_sizes[0]);
}
}
}
void DOA2Baseband::handleData()
{
QMutexLocker mutexLocker(&m_mutex);
const std::vector<SampleVector>& data = m_sampleMIFifo.getData();
unsigned int ipart1begin;
unsigned int ipart1end;
unsigned int ipart2begin;
unsigned int ipart2end;
while ((m_sampleMIFifo.fillSync() > 0) && (m_inputMessageQueue.size() == 0))
{
m_sampleMIFifo.readSync(ipart1begin, ipart1end, ipart2begin, ipart2end);
if (ipart1begin != ipart1end) { // first part of FIFO data
processFifo(data, ipart1begin, ipart1end);
}
if (ipart2begin != ipart2end) { // second part of FIFO data (used when block wraps around)
processFifo(data, ipart2begin, ipart2end);
}
}
}
void DOA2Baseband::processFifo(const std::vector<SampleVector>& data, unsigned int ibegin, unsigned int iend)
{
for (unsigned int stream = 0; stream < 2; stream++) {
m_channelizers[stream]->feed(data[stream].begin() + ibegin, data[stream].begin() + iend);
}
run();
}
void DOA2Baseband::run()
{
if (m_correlator.performCorr(m_sinks[0].getData(), m_sinks[0].getSize(), m_sinks[1].getData(), m_sinks[1].getSize()))
{
if (m_correlationType == DOA2Settings::CorrelationType::CorrelationFFT) {
processDOA(m_correlator.m_xcorr.begin(), m_correlator.m_processed);
}
if (m_scopeSink)
{
std::vector<SampleVector::const_iterator> vbegin;
vbegin.push_back(m_correlator.m_tcorr.begin());
m_scopeSink->feed(vbegin, m_correlator.m_processed);
}
}
for (int i = 0; i < 2; i++)
{
std::copy(
m_sinks[i].getData().begin() + m_correlator.m_processed,
m_sinks[i].getData().begin() + m_correlator.m_processed + m_correlator.m_remaining[i],
m_sinks[i].getData().begin()
);
m_sinks[i].setDataStart(m_correlator.m_remaining[i]);
}
}
void DOA2Baseband::handleInputMessages()
{
qDebug("DOA2Baseband::handleInputMessage");
Message* message;
while ((message = m_inputMessageQueue.pop()) != 0)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool DOA2Baseband::handleMessage(const Message& cmd)
{
if (MsgConfigureChannelizer::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd;
int log2Decim = cfg.getLog2Decim();
int filterChainHash = cfg.getFilterChainHash();
qDebug() << "DOA2Baseband::handleMessage: MsgConfigureChannelizer:"
<< " log2Decim: " << log2Decim
<< " filterChainHash: " << filterChainHash;
for (int i = 0; i < 2; i++)
{
m_channelizers[i]->setDecimation(log2Decim, filterChainHash);
m_sinks[i].reset();
}
return true;
}
else if (MsgSignalNotification::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgSignalNotification& cfg = (MsgSignalNotification&) cmd;
int inputSampleRate = cfg.getInputSampleRate();
qint64 centerFrequency = cfg.getCenterFrequency();
int streamIndex = cfg.getStreamIndex();
qDebug() << "DOA2Baseband::handleMessage: MsgSignalNotification:"
<< " inputSampleRate: " << inputSampleRate
<< " centerFrequency: " << centerFrequency
<< " streamIndex: " << streamIndex;
if (streamIndex < 2)
{
m_channelizers[streamIndex]->setBasebandSampleRate(inputSampleRate);
m_sinks[streamIndex].reset();
}
return true;
}
else if (MsgConfigureCorrelation::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
MsgConfigureCorrelation& cfg = (MsgConfigureCorrelation&) cmd;
m_correlationType = cfg.getCorrelationType();
qDebug() << "DOA2Baseband::handleMessage: MsgConfigureCorrelation:"
<< " correlationType: " << m_correlationType;
m_correlator.setCorrType(m_correlationType);
return true;
}
else
{
qDebug("DOA2Baseband::handleMessage: unhandled: %s", cmd.getIdentifier());
return false;
}
}
void DOA2Baseband ::setBasebandSampleRate(unsigned int sampleRate)
{
for (int istream = 0; istream < 2; istream++)
{
m_channelizers[istream]->setBasebandSampleRate(sampleRate);
m_sinks[istream].reset();
}
}
void DOA2Baseband::setFFTAveraging(int nbFFT)
{
qDebug("DOA2Baseband::setFFTAveraging: %d", nbFFT);
m_fftAvg = nbFFT < 1 ? 1 : nbFFT;
m_fftAvgCount = 0;
m_magSum = 0;
m_wphSum = 0;
m_samplesCount = 0;
}
void DOA2Baseband::processDOA(const std::vector<Complex>::iterator& begin, int nbSamples)
{
const std::vector<Complex>::iterator end = begin + nbSamples;
for (std::vector<Complex>::iterator it = begin; it != end; ++it)
{
float ph = std::arg(*it);
double mag = std::norm(*it);
if (mag > m_magThreshold)
{
m_magSum += mag;
m_wphSum += mag*ph;
}
if (++m_samplesCount == m_fftSize)
{
if (m_wphSum != 0)
{
if (++m_fftAvgCount == m_fftAvg)
{
m_phi = m_wphSum / m_magSum;
m_fftAvgCount = 0;
}
}
m_magSum = 0;
m_wphSum = 0;
m_samplesCount = 0;
}
}
}

Wyświetl plik

@ -0,0 +1,148 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2BASEBAND_H
#define INCLUDE_DOA2BASEBAND_H
#include <QObject>
#include <QMutex>
#include "dsp/samplemififo.h"
#include "util/messagequeue.h"
#include "doa2streamsink.h"
#include "doa2corr.h"
class DownChannelizer;
class BasebandSampleSink;
class ScopeVis;
class DOA2Baseband : public QObject
{
Q_OBJECT
public:
class MsgConfigureChannelizer : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getLog2Decim() const { return m_log2Decim; }
int getFilterChainHash() const { return m_filterChainHash; }
static MsgConfigureChannelizer* create(unsigned int log2Decim, unsigned int filterChainHash) {
return new MsgConfigureChannelizer(log2Decim, filterChainHash);
}
private:
unsigned int m_log2Decim;
unsigned int m_filterChainHash;
MsgConfigureChannelizer(unsigned int log2Decim, unsigned int filterChainHash) :
Message(),
m_log2Decim(log2Decim),
m_filterChainHash(filterChainHash)
{ }
};
class MsgConfigureCorrelation : public Message {
MESSAGE_CLASS_DECLARATION
public:
DOA2Settings::CorrelationType getCorrelationType() const { return m_correlationType; }
static MsgConfigureCorrelation *create(DOA2Settings::CorrelationType correlationType) {
return new MsgConfigureCorrelation(correlationType);
}
private:
DOA2Settings::CorrelationType m_correlationType;
MsgConfigureCorrelation(DOA2Settings::CorrelationType correlationType) :
Message(),
m_correlationType(correlationType)
{}
};
class MsgSignalNotification : public Message {
MESSAGE_CLASS_DECLARATION
public:
int getInputSampleRate() const { return m_inputSampleRate; }
qint64 getCenterFrequency() const { return m_centerFrequency; }
int getStreamIndex() const { return m_streamIndex; }
static MsgSignalNotification* create(int inputSampleRate, qint64 centerFrequency, int streamIndex) {
return new MsgSignalNotification(inputSampleRate, centerFrequency, streamIndex);
}
private:
int m_inputSampleRate;
qint64 m_centerFrequency;
int m_streamIndex;
MsgSignalNotification(int inputSampleRate, qint64 centerFrequency, int streamIndex) :
Message(),
m_inputSampleRate(inputSampleRate),
m_centerFrequency(centerFrequency),
m_streamIndex(streamIndex)
{ }
};
DOA2Baseband(int fftSize);
~DOA2Baseband();
void reset();
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication
void setScopeSink(ScopeVis *scopeSink) { m_scopeSink = scopeSink; }
void setPhase(int phase) { m_correlator.setPhase(phase); }
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; }
void setFFTAveraging(int nbFFT);
private:
void processFifo(const std::vector<SampleVector>& data, unsigned int ibegin, unsigned int iend);
void run();
bool handleMessage(const Message& cmd);
void processDOA(const std::vector<Complex>::iterator& begin, int nbSamples);
DOA2Correlator m_correlator;
DOA2Settings::CorrelationType m_correlationType;
int m_fftSize;
int m_samplesCount; //!< Number of samples processed by DOA
float m_magSum; //!< Squared magnitudes accumulator
float m_wphSum; //!< Phase difference accumulator (averaging weighted by squared magnitude)
float m_phi; //!< Resulting calculated phase difference
double m_magThreshold; //!< Squared magnitude scaled threshold
int m_fftAvg; //!< Average over a certain number of FFTs
int m_fftAvgCount; //!< FFT averaging counter
SampleMIFifo m_sampleMIFifo;
std::vector<SampleVector::const_iterator> m_vbegin;
int m_sizes[2];
DOA2StreamSink m_sinks[2];
DownChannelizer *m_channelizers[2];
ScopeVis *m_scopeSink;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
QMutex m_mutex;
unsigned int m_lastStream;
private slots:
void handleInputMessages();
void handleData(); //!< Handle data when samples have to be processed
};
#endif // INCLUDE_DOA2BASEBAND_H

Wyświetl plik

@ -0,0 +1,278 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QtCore>
#include <QtGui>
#include <QDebug>
#include "doa2compass.h"
DOA2Compass::DOA2Compass(QWidget *parent)
: QWidget(parent)
{
connect(this, SIGNAL(canvasReplot(void)), this, SLOT(canvasReplot_slot(void)));
m_sizeMin = 200;
m_sizeMax = 600;
m_offset = 2;
m_size = m_sizeMin - 2*m_offset;
m_drawLegend = false;
setMinimumSize(m_sizeMin, m_sizeMin);
setMaximumSize(m_sizeMax, m_sizeMax);
resize(m_sizeMin, m_sizeMin);
setFocusPolicy(Qt::NoFocus);
m_azPos = 0.0;
m_azNeg = 0.0;
m_azAnt = 0.0;
m_blindAngle = 0.0;
m_blindColor = QColor(48, 48, 48);
}
DOA2Compass::~DOA2Compass()
{
}
void DOA2Compass::canvasReplot_slot(void)
{
update();
}
void DOA2Compass::resizeEvent(QResizeEvent *event)
{
m_size = qMin(width(),height()) - 2*m_offset;
QWidget::resizeEvent(event);
}
void DOA2Compass::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QBrush bgGround(palette().button().color());
QPen whitePen(Qt::white);
QPen blackPen(Qt::black);
QPen redPen(Qt::red);
QPen bluePen(Qt::blue);
QPen greenPen(Qt::green);
whitePen.setWidth(1);
blackPen.setWidth(2);
redPen.setWidth(2);
bluePen.setWidth(2);
greenPen.setWidth(2);
painter.setRenderHint(QPainter::Antialiasing);
painter.translate(width() / 2, height() / 2);
// draw background
{
painter.setPen(whitePen);
painter.setBrush(bgGround);
painter.drawEllipse(-m_size/2, -m_size/2, m_size, m_size);
}
// draw blind angle
if (m_blindAngle != 0)
{
painter.setBrush(m_blindColor);
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
{
int nyawLines = 36;
float rotAng = 360.0 / nyawLines;
int yawLineLeng = m_size/25;
double fx1, fy1, fx2, fy2;
int fontSize = 8;
QString s;
blackPen.setWidth(1);
painter.setPen(whitePen);
painter.setFont(font());
for(int i=0; i<nyawLines; i++) {
if( i == 0 ) {
s = "N";
} else if ( i == 9 ) {
s = "E";
} else if ( i == 18 ) {
s = "S";
} else if ( i == 27 ) {
s = "W";
} else {
s = QString("%1").arg(i*rotAng);
}
fx1 = 0;
fy1 = -m_size/2 + m_offset;
fx2 = 0;
if (i % 3 == 0)
{
fy2 = fy1 + yawLineLeng;
painter.drawLine(QPointF(fx1, fy1), QPointF(fx2, fy2));
fy2 = fy1 + yawLineLeng+4;
painter.drawText(QRectF(-50, fy2, 100, fontSize+2), Qt::AlignCenter, s);
}
else
{
fy2 = fy1 + yawLineLeng/2;
painter.drawLine(QPointF(fx1, fy1), QPointF(fx2, fy2));
}
painter.rotate(rotAng);
}
}
// draw antennas arrow
{
int arrowWidth = m_size/20;
double fx1, fy1, fx2, fy2, fx3, fy3;
fx1 = 0;
fy1 = -m_size/2 + m_offset + m_size/25 + 15;
fx2 = -arrowWidth/2;
fy2 = 0;
fx3 = arrowWidth/2;
fy3 = 0;
painter.rotate(m_azAnt);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(QColor(192, 192, 192)));
QPointF pointsN[3] = {
QPointF(fx1, fy1),
QPointF(fx2, fy2),
QPointF(fx3, fy3)
};
painter.drawPolygon(pointsN, 3);
fx1 = 0;
fy1 = m_size/2 - m_offset - m_size/25 - 15;
fx2 = -arrowWidth/2;
fy2 = 0;
fx3 = arrowWidth/2;
fy3 = 0;
painter.setBrush(QBrush(palette().button().color().lighter(150)));
QPointF pointsS[3] = {
QPointF(fx1, fy1),
QPointF(fx2, fy2),
QPointF(fx3, fy3)
};
painter.drawPolygon(pointsS, 3);
painter.rotate(-m_azAnt);
}
// draw azPos marker
{
int azMarkerSize = m_size/15;
double fx1, fy1, fx2, fy2, fx3, fy3, fyl;
fx1 = 0;
fy1 = -m_size/2 + m_offset;
fx2 = fx1 - azMarkerSize/2;
fy2 = fy1 + azMarkerSize;
fx3 = fx1 + azMarkerSize/2;
fy3 = fy1 + azMarkerSize;
fyl = -m_size/2; // -m_size/2 + m_offset + m_size/25 + 15;
painter.rotate(m_azPos);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(QColor(0xFF, 0x60, 0x60, 0xA0)));
QPointF points[3] = {
QPointF(fx1, fy1),
QPointF(fx2, fy2),
QPointF(fx3, fy3)
};
painter.drawPolygon(points, 3);
painter.setPen(QColor(0xFF, 0x60, 0x60, 0xE0));
painter.drawLine(0, 0, 0, fyl);
painter.rotate(-m_azPos);
}
// draw azNeg marker
{
int yawMarkerSize = m_size/15;
double fx1, fy1, fx2, fy2, fx3, fy3, fyl;
fx1 = 0;
fy1 = -m_size/2 + m_offset;
fx2 = fx1 - yawMarkerSize/2;
fy2 = fy1 + yawMarkerSize;
fx3 = fx1 + yawMarkerSize/2;
fy3 = fy1 + yawMarkerSize;
fyl = -m_size/2;
painter.rotate(m_azNeg);
painter.setPen(Qt::NoPen);
painter.setBrush(QBrush(QColor(0x80, 0xFF, 0x80, 0xA0)));
QPointF points[3] = {
QPointF(fx1, fy1),
QPointF(fx2, fy2),
QPointF(fx3, fy3)
};
painter.drawPolygon(points, 3);
painter.setPen(QColor(0x80, 0xFF, 0x80, 0xE0));
painter.drawLine(0, 0, 0, fyl);
painter.rotate(-m_azNeg);
}
// draw legend
if (m_drawLegend)
{
int altFontSize = 8;
int fx, fy, w, h;
w = 10*altFontSize;
h = 3*(altFontSize + 8);
fx = -w/2;
fy = -h/2;
painter.setPen(whitePen);
painter.setBrush(QBrush(Qt::black));
QFont f = font();
f.setPointSize(altFontSize);
painter.setFont(f);
painter.drawRoundedRect(fx, fy, w, h, 6, 6);
painter.drawText(QRectF(fx, fy+2, w, h/3), Qt::AlignCenter, QString("POS: %1%2").arg((int) m_azPos, 3, 10, QLatin1Char('0')).arg(QChar(0260)));
painter.drawText(QRectF(fx, fy+h/3, w, h/3), Qt::AlignCenter, QString("ANT: %1%2").arg((int) m_azAnt, 3, 10, QLatin1Char('0')).arg(QChar(0260)));
painter.drawText(QRectF(fx, fy+2*(h/3), w, h/3), Qt::AlignCenter, QString("NEG: %1%2").arg((int) m_azNeg, 3, 10, QLatin1Char('0')).arg(QChar(0260)));
}
}

Wyświetl plik

@ -0,0 +1,167 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2COMPASS_H
#define INCLUDE_DOA2COMPASS_H
#include <QWidget>
class DOA2Compass : public QWidget
{
Q_OBJECT
public:
DOA2Compass(QWidget *parent = nullptr);
~DOA2Compass();
///
/// \brief Set all data (yaw, alt, height)
///
/// \param azPos - forward (positive angles side relative to antennas direction) azimuth (in degrees)
/// \param azNeg - reverse (negatve angles side relative to antennas direction) azimuth (in degrees)
/// \param azAnt - antennas azimuth from 1 (connected to stream 0) to 2 (connected to stream 1)
///
void setData(double azPos, double azNeg, double azAnt) {
m_azPos = azPos;
m_azNeg = azNeg;
m_azAnt = azAnt;
if( m_azPos < 0 ) m_azPos = 360 + m_azPos;
if( m_azPos > 360 ) m_azPos = m_azPos - 360;
if( m_azNeg < 0 ) m_azNeg = 360 + m_azNeg;
if( m_azNeg > 360 ) m_azNeg = m_azNeg - 360;
if( azAnt < 0 ) azAnt = 360 + azAnt;
if( azAnt > 360 ) azAnt = azAnt - 360;
emit canvasReplot();
}
///
/// \brief Set forward azimoth (in degree)
/// \param val - forward azimoth (in degree)
///
void setAzPos(double val)
{
m_azPos = val;
if( m_azPos < 0 ) m_azPos = 360 + m_azPos;
if( m_azPos > 360 ) m_azPos = m_azPos - 360;
emit canvasReplot();
}
///
/// \brief Set reverse azimoth (in degree)
/// \param val - reverse azimoth (in degree)
///
void setAzNeg(double val)
{
m_azNeg = val;
if( m_azNeg < 0 ) m_azNeg = 360 + m_azNeg;
if( m_azNeg > 360 ) m_azNeg = m_azNeg - 360;
emit canvasReplot();
}
///
/// \brief Set antennas azimoth (in degree)
/// \param val - antennas azimoth (in degree)
///
void setAzAnt(double val)
{
m_azAnt = val;
if( m_azAnt < 0 ) m_azAnt = 360 + m_azAnt;
if( m_azAnt > 360 ) m_azAnt = m_azAnt - 360;
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 Set blind scector color
/// \param val - blind sector color
///
void setBlindColor(const QColor& color)
{
m_blindColor = color;
emit canvasReplot();
}
///
/// \brief Draw legend in the center of the compass
/// \param drawLegend - true to draw legend else false
///
void drawLegend(bool drawLegend)
{
m_drawLegend = drawLegend;
emit canvasReplot();
}
///
/// \brief Get forward azimuth
/// \return forward azimuth (in degree)
///
double getAzPos() const {return m_azPos; }
///
/// \brief Get reverse azimuth
/// \return reverse azimuth (in degree)
///
double getAzNeg() const {return m_azNeg; }
///
/// \brief Get antennas azimuth
/// \return antennas azimuth (in degree)
///
double getAzAnt() const {return m_azAnt; }
signals:
void canvasReplot(void);
protected slots:
void canvasReplot_slot(void);
protected:
void paintEvent(QPaintEvent *event);
void resizeEvent(QResizeEvent *event);
protected:
int m_sizeMin, m_sizeMax; ///< widget min/max size (in pixel)
int m_size, m_offset; ///< widget size and offset size
bool m_drawLegend; ///< draw legend in the center
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)
QColor m_blindColor;
};
#endif // INCLUDE_DOA2COMPASS_H

Wyświetl plik

@ -0,0 +1,389 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <algorithm>
#include <functional>
#include "dsp/dspengine.h"
#include "dsp/fftfactory.h"
#include "dsp/fftengine.h"
#include "doa2corr.h"
std::complex<float> s2cNorm(const Sample& s)
{
float x = s.real() / SDR_RX_SCALEF;
float y = s.imag() / SDR_RX_SCALEF;
float m = sqrt(x*x + y*y);
return std::complex<float>{x/m, y/m};
}
Sample sFirst(const Sample& a, const Sample& b) {
(void) b;
return a;
}
Sample sSecond(const Sample& a, const Sample& b) {
(void) a;
return b;
}
Sample sSecondInv(const Sample& a, const Sample& b) {
(void) a;
return Sample{-b.real(), -b.imag()};
}
Sample invfft2s(const std::complex<float>& a) { //!< Complex float to Sample for 1 side time correlation
Sample s;
s.setReal(a.real()/2.0f);
s.setImag(a.imag()/2.0f);
return s;
}
DOA2Correlator::DOA2Correlator(int fftSize) :
m_corrType(DOA2Settings::CorrelationFFT),
m_fftSize(fftSize)
{
setPhase(0);
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
m_window.create(FFTWindow::Function::Hanning, fftSize);
for (int i = 0; i < 2; i++) {
m_fftSequences[i] = fftFactory->getEngine(fftSize, false, &m_fft[i]);
}
m_invFFTSequence = fftFactory->getEngine(fftSize, true, &m_invFFT);
m_dataj = new std::complex<float>[2*fftSize]; // receives actual FFT result hence twice the data FFT size
m_tcorr.resize(fftSize);
m_xcorr.resize(fftSize);
m_tcorrSize = fftSize;
m_xcorrSize = fftSize;
}
DOA2Correlator::~DOA2Correlator()
{
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
fftFactory->releaseEngine(m_fftSize, true, m_invFFTSequence);
delete[] m_dataj;
for (int i = 0; i < 2; i++) {
fftFactory->releaseEngine(m_fftSize, false, m_fftSequences[i]);
}
}
bool DOA2Correlator::performCorr(
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1
)
{
bool results = false;
if (m_phase == 0)
{
switch (m_corrType)
{
case DOA2Settings::Correlation0:
results = performOpCorr(data0, size0, data1, size1, sFirst);
break;
case DOA2Settings::Correlation1:
results = performOpCorr(data0, size0, data1, size1, sSecond);
break;
case DOA2Settings::CorrelationFFT:
results = performFFTProd(data0, size0, data1, size1);
break;
default:
break;
}
}
else if ((m_phase == -180) || (m_phase == 180))
{
if (m_corrType == DOA2Settings::CorrelationFFT)
{
if (size1 > m_data1p.size()) {
m_data1p.resize(size1);
}
std::transform(
data1.begin(),
data1.begin() + size1,
m_data1p.begin(),
[](const Sample& s) -> Sample {
return Sample{-s.real(), -s.imag()};
}
);
}
switch (m_corrType)
{
case DOA2Settings::Correlation0:
results = performOpCorr(data0, size0, data1, size1, sFirst);
break;
case DOA2Settings::Correlation1:
results = performOpCorr(data0, size0, data1, size1, sSecondInv);
break;
case DOA2Settings::CorrelationFFT:
results = performFFTProd(data0, size0, m_data1p, size1);
break;
default:
break;
}
}
else
{
if (size1 > m_data1p.size()) {
m_data1p.resize(size1);
}
std::transform(
data1.begin(),
data1.begin() + size1,
m_data1p.begin(),
[this](const Sample& s) -> Sample {
Sample t;
int64_t sx = s.real();
int64_t sy = s.imag();
int64_t x = sx*m_cos + sy*m_sin;
int64_t y = sy*m_cos - sx*m_sin;
t.setReal(x>>(SDR_RX_SAMP_SZ-1));
t.setImag(y>>(SDR_RX_SAMP_SZ-1));
return t;
}
);
switch (m_corrType)
{
case DOA2Settings::Correlation0:
results = performOpCorr(data0, size0, m_data1p, size1, sFirst);
break;
case DOA2Settings::Correlation1:
results = performOpCorr(data0, size0, m_data1p, size1, sSecond);
break;
case DOA2Settings::CorrelationFFT:
results = performFFTProd(data0, size0, m_data1p, size1);
break;
default:
break;
}
}
return results;
}
bool DOA2Correlator::performOpCorr(
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1,
Sample sampleOp(const Sample& a, const Sample& b)
)
{
unsigned int size = std::min(size0, size1);
adjustTCorrSize(size);
adjustXCorrSize(size);
std::transform(
data0.begin(),
data0.begin() + size,
data1.begin(),
m_tcorr.begin(),
sampleOp
);
m_processed = size;
m_remaining[0] = size0 - size;
m_remaining[1] = size1 - size;
return true;
}
bool DOA2Correlator::performFFTProd(
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1
)
{
unsigned int size = std::min(size0, size1);
int nfft = 0;
SampleVector::const_iterator begin0 = data0.begin();
SampleVector::const_iterator begin1 = data1.begin();
adjustTCorrSize(size);
adjustXCorrSize(size);
while (size >= m_fftSize)
{
// FFT[0]
std::transform(
begin0,
begin0 + m_fftSize,
m_fft[0]->in(),
s2cNorm
);
m_window.apply(m_fft[0]->in());
m_fft[0]->transform();
// FFT[1]
std::transform(
begin1,
begin1 + m_fftSize,
m_fft[1]->in(),
s2cNorm
);
m_window.apply(m_fft[1]->in());
m_fft[1]->transform();
// conjugate FFT[1]
std::transform(
m_fft[1]->out(),
m_fft[1]->out() + m_fftSize,
m_dataj,
[](const std::complex<float>& c) -> std::complex<float> {
return std::conj(c);
}
);
// product of FFT[1]* with FFT[0] and store in both results
std::transform(
m_fft[0]->out(),
m_fft[0]->out() + m_fftSize,
m_dataj,
m_invFFT->in(),
[this](std::complex<float>& a, const std::complex<float>& b) -> std::complex<float> {
return (a*b);
}
);
// copy to complex vector for DOA with re-orderong
std::copy(
m_invFFT->in(),
m_invFFT->in() + m_fftSize/2,
m_xcorr.begin() + nfft*m_fftSize + m_fftSize/2
);
std::copy(
m_invFFT->in() + m_fftSize/2,
m_invFFT->in() + m_fftSize,
m_xcorr.begin() + nfft*m_fftSize
);
// convert and scale to FFT size for scope time domain display
std::transform(
m_xcorr.begin() + nfft*m_fftSize,
m_xcorr.begin() + nfft*m_fftSize + m_fftSize,
m_tcorr.begin() + nfft*m_fftSize,
[](const std::complex<float>& a) -> Sample {
Sample s;
s.setReal(a.real()/2.0f);
s.setImag(a.imag()/2.0f);
return s;
}
);
// copy product to time domain - re-order, convert and scale to FFT size
// std::transform(
// m_invFFT->in(),
// m_invFFT->in() + m_fftSize/2,
// m_tcorr.begin() + nfft*m_fftSize + m_fftSize/2,
// [](const std::complex<float>& a) -> Sample {
// Sample s;
// s.setReal(a.real()/2.0f);
// s.setImag(a.imag()/2.0f);
// return s;
// }
// );
// std::transform(
// m_invFFT->in() + m_fftSize/2,
// m_invFFT->in() + m_fftSize,
// m_tcorr.begin() + nfft*m_fftSize,
// [](const std::complex<float>& a) -> Sample {
// Sample s;
// s.setReal(a.real()/2.0f);
// s.setImag(a.imag()/2.0f);
// return s;
// }
// );
size -= m_fftSize;
begin0 += m_fftSize;
begin1 += m_fftSize;
nfft++;
}
// update the samples counters
m_processed = nfft*m_fftSize;
m_remaining[0] = size0 - nfft*m_fftSize;
m_remaining[1] = size1 - nfft*m_fftSize;
return nfft > 0;
}
void DOA2Correlator::adjustTCorrSize(int size)
{
int nFFTSize = (size/m_fftSize)*m_fftSize;
if (nFFTSize > m_tcorrSize)
{
m_tcorr.resize(nFFTSize);
m_tcorrSize = nFFTSize;
}
}
void DOA2Correlator::adjustXCorrSize(int size)
{
int nFFTSize = (size/m_fftSize)*m_fftSize;
if (nFFTSize > m_xcorrSize)
{
m_xcorr.resize(nFFTSize);
m_xcorrSize = nFFTSize;
}
}
void DOA2Correlator::setPhase(int phase)
{
m_phase = phase;
if (phase == 0)
{
m_sin = 0;
m_cos = 1<<(SDR_RX_SAMP_SZ-1);
}
else if (phase == 90)
{
m_sin = 1<<(SDR_RX_SAMP_SZ-1);
m_cos = 0;
}
else if (phase == -90)
{
m_sin = -(1<<(SDR_RX_SAMP_SZ-1));
m_cos = 0;
}
else if ((phase == -180) || (phase == 180))
{
m_sin = 0;
m_cos = -(1<<(SDR_RX_SAMP_SZ-1));
}
else
{
m_phase = phase % 180;
double d_sin = sin(M_PI*(m_phase/180.0)) * (1<<(SDR_RX_SAMP_SZ-1));
double d_cos = cos(M_PI*(m_phase/180.0)) * (1<<(SDR_RX_SAMP_SZ-1));
m_sin = d_sin;
m_cos = d_cos;
}
}

Wyświetl plik

@ -0,0 +1,90 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2CORR_H
#define INCLUDE_DOA2CORR_H
#include <complex>
#include <QObject>
#include "dsp/dsptypes.h"
#include "dsp/fftwindow.h"
#include "util/message.h"
#include "doa2settings.h"
class FFTEngine;
class DOA2Correlator : public QObject {
Q_OBJECT
public:
DOA2Correlator(int fftSize);
~DOA2Correlator();
void setCorrType(DOA2Settings::CorrelationType corrType) { m_corrType = corrType; }
DOA2Settings::CorrelationType getCorrType() const { return m_corrType; }
bool performCorr( //!< Returns true if results were produced
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1
);
int getFullFFTSize() const { return 2*m_fftSize; }
void setPhase(int phase);
SampleVector m_tcorr; //!< correlation result (time or spectrum inverse FFT) - Sample vector expected
std::vector<Complex> m_xcorr; //!< correlation result of inverse FFT of FFT prod with conjugate (performFFTProd) for DOA processing
int m_processed; //!< number of samples processed at the end of correlation
int m_remaining[2]; //!< number of samples remaining per member at the end of correlation
signals:
void dataReady(int start, int stop);
private:
bool performOpCorr( //!< Returns true if results were produced
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1,
Sample sampleOp(const Sample& a, const Sample& b)
);
bool performFFTProd( //!< Returns true if results were produced
const SampleVector& data0,
unsigned int size0,
const SampleVector& data1,
unsigned int size1
);
void adjustTCorrSize(int size);
void adjustXCorrSize(int size);
DOA2Settings::CorrelationType m_corrType;
unsigned int m_fftSize; //!< FFT length
FFTEngine *m_fft[2]; //!< FFT engines
FFTEngine *m_invFFT; //!< Inverse FFT engine
unsigned int m_fftSequences[2]; //!< FFT engines sequences
unsigned int m_invFFTSequence; //!< Inverse FFT engine sequence
FFTWindow m_window; //!< FFT window
std::complex<float> *m_dataj; //!< conjuate of FFT transform
SampleVector m_data1p; //!< data1 with phase correction
int m_tcorrSize; //!< time correlations vector size
int m_xcorrSize; //!< DOA correlations vector size
int m_phase; //!< phase correction
int64_t m_sin; //!< scaled sine of phase correction
int64_t m_cos; //!< scaled cosine of phase correction
};
#endif // INCLUDE_DOA2CORR_H

Wyświetl plik

@ -0,0 +1,487 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QLocale>
#include "device/deviceuiset.h"
#include "gui/basicchannelsettingsdialog.h"
#include "dsp/hbfilterchainconverter.h"
#include "dsp/scopevis.h"
#include "dsp/spectrumvis.h"
#include "maincore.h"
#include "doa2gui.h"
#include "doa2.h"
#include "ui_doa2gui.h"
DOA2GUI* DOA2GUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, MIMOChannel *channelMIMO)
{
DOA2GUI* gui = new DOA2GUI(pluginAPI, deviceUISet, channelMIMO);
return gui;
}
void DOA2GUI::destroy()
{
delete this;
}
void DOA2GUI::resetToDefaults()
{
m_settings.resetToDefaults();
displaySettings();
applySettings(true);
}
QByteArray DOA2GUI::serialize() const
{
return m_settings.serialize();
}
bool DOA2GUI::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
displaySettings();
applySettings(true);
return true;
}
else
{
resetToDefaults();
return false;
}
}
MessageQueue* DOA2GUI::getInputMessageQueue()
{
return &m_inputMessageQueue;
}
bool DOA2GUI::handleMessage(const Message& message)
{
if (DOA2::MsgBasebandNotification::match(message))
{
DOA2::MsgBasebandNotification& notif = (DOA2::MsgBasebandNotification&) message;
m_sampleRate = notif.getSampleRate();
m_centerFrequency = notif.getCenterFrequency();
displayRateAndShift();
updateAbsoluteCenterFrequency();
setFFTAveragingToolitp();
return true;
}
else if (DOA2::MsgConfigureDOA2::match(message))
{
const DOA2::MsgConfigureDOA2& notif = (const DOA2::MsgConfigureDOA2&) message;
m_settings = notif.getSettings();
ui->scopeGUI->updateSettings();
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
displaySettings();
return true;
}
else
{
return false;
}
}
DOA2GUI::DOA2GUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, MIMOChannel *channelMIMO, QWidget* parent) :
ChannelGUI(parent),
ui(new Ui::DOA2GUI),
m_pluginAPI(pluginAPI),
m_deviceUISet(deviceUISet),
m_sampleRate(48000),
m_centerFrequency(435000000),
m_tickCount(0)
{
setAttribute(Qt::WA_DeleteOnClose, true);
m_helpURL = "plugins/channelmimo/doa2/readme.md";
RollupContents *rollupContents = getRollupContents();
ui->setupUi(rollupContents);
setSizePolicy(rollupContents->sizePolicy());
rollupContents->arrangeRollups();
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
m_doa2 = (DOA2*) channelMIMO;
m_scopeVis = m_doa2->getScopeVis();
m_scopeVis->setGLScope(ui->glScope);
m_doa2->setMessageQueueToGUI(getInputMessageQueue());
m_sampleRate = m_doa2->getDeviceSampleRate();
ui->glScope->setTraceModulo(DOA2::m_fftSize);
ui->glScope->connectTimer(MainCore::instance()->getMasterTimer());
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
m_channelMarker.blockSignals(true);
m_channelMarker.addStreamIndex(1);
m_channelMarker.setColor(m_settings.m_rgbColor);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle("DOA 2 source");
m_channelMarker.blockSignals(false);
m_channelMarker.setVisible(true); // activate signal on the last setting only
m_settings.setChannelMarker(&m_channelMarker);
m_settings.setRollupState(&m_rollupState);
m_settings.setScopeGUI(ui->scopeGUI);
m_deviceUISet->addChannelMarker(&m_channelMarker);
ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope);
m_scopeVis->setTraceChunkSize(DOA2::m_fftSize); // Set scope trace length unit to FFT size
ui->scopeGUI->traceLengthChange();
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));
displaySettings();
makeUIConnections();
displayRateAndShift();
applySettings(true);
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
ui->halfWLLabel->setText(QString("%1/2").arg(QChar(0xBB, 0x03)));
ui->azUnits->setText(QString("%1").arg(QChar(0260)));
}
DOA2GUI::~DOA2GUI()
{
delete ui;
}
void DOA2GUI::blockApplySettings(bool block)
{
m_doApplySettings = !block;
}
void DOA2GUI::applySettings(bool force)
{
if (m_doApplySettings)
{
setTitleColor(m_channelMarker.getColor());
DOA2::MsgConfigureDOA2* message = DOA2::MsgConfigureDOA2::create(m_settings, force);
m_doa2->getInputMessageQueue()->push(message);
}
}
void DOA2GUI::displaySettings()
{
ui->correlationType->setCurrentIndex((int) m_settings.m_correlationType);
m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(0);
m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.setBandwidth(m_sampleRate);
m_channelMarker.setMovable(false); // do not let user move the center arbitrarily
m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
setTitleColor(m_settings.m_rgbColor);
setWindowTitle(m_channelMarker.getTitle());
setTitle(m_channelMarker.getTitle());
blockApplySettings(true);
ui->decimationFactor->setCurrentIndex(m_settings.m_log2Decim);
applyDecimation();
ui->phaseCorrection->setValue(m_settings.m_phase);
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));
ui->fftAveraging->setCurrentIndex(m_settings.m_fftAveragingIndex);
setFFTAveragingToolitp();
getRollupContents()->restoreState(m_rollupState);
updateAbsoluteCenterFrequency();
blockApplySettings(false);
}
void DOA2GUI::displayRateAndShift()
{
int shift = m_shiftFrequencyFactor * m_sampleRate;
double channelSampleRate = ((double) m_sampleRate) / (1<<m_settings.m_log2Decim);
QLocale loc;
ui->offsetFrequencyText->setText(tr("%1 Hz").arg(loc.toString(shift)));
ui->channelRateText->setText(tr("%1k").arg(QString::number(channelSampleRate / 1000.0, 'g', 5)));
m_channelMarker.setCenterFrequency(shift);
m_channelMarker.setBandwidth(channelSampleRate);
m_scopeVis->setLiveRate(channelSampleRate);
}
void DOA2GUI::setFFTAveragingToolitp()
{
float channelSampleRate = ((float) m_sampleRate) / (1<<m_settings.m_log2Decim);
float averagingTime = (DOA2::m_fftSize * DOA2Settings::getAveragingValue(m_settings.m_fftAveragingIndex)) /
channelSampleRate;
QString s;
setNumberStr(averagingTime, 2, s);
ui->fftAveraging->setToolTip(QString("Number of averaging FFTs (avg time: %1s)").arg(s));
}
void DOA2GUI::setNumberStr(float v, int decimalPlaces, QString& s)
{
if (v < 1e-6) {
s = tr("%1n").arg(v*1e9, 0, 'f', decimalPlaces);
} else if (v < 1e-3) {
s = tr("%1µ").arg(v*1e6, 0, 'f', decimalPlaces);
} else if (v < 1.0) {
s = tr("%1m").arg(v*1e3, 0, 'f', decimalPlaces);
} else if (v < 1e3) {
s = tr("%1").arg(v, 0, 'f', decimalPlaces);
} else if (v < 1e6) {
s = tr("%1k").arg(v*1e-3, 0, 'f', decimalPlaces);
} else if (v < 1e9) {
s = tr("%1M").arg(v*1e-6, 0, 'f', decimalPlaces);
} else {
s = tr("%1G").arg(v*1e-9, 0, 'f', decimalPlaces);
}
}
void DOA2GUI::leaveEvent(QEvent*)
{
m_channelMarker.setHighlighted(false);
}
void DOA2GUI::enterEvent(QEvent*)
{
m_channelMarker.setHighlighted(true);
}
void DOA2GUI::handleSourceMessages()
{
Message* message;
while ((message = getInputMessageQueue()->pop()) != 0)
{
if (handleMessage(*message))
{
delete message;
}
}
}
void DOA2GUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
(void) widget;
(void) rollDown;
RollupContents *rollupContents = getRollupContents();
if (rollupContents->hasExpandableWidgets()) {
setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding);
} else {
setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Fixed);
}
int h = rollupContents->height() + getAdditionalHeight();
resize(width(), h);
rollupContents->saveState(m_rollupState);
applySettings();
}
void DOA2GUI::onMenuDialogCalled(const QPoint &p)
{
if (m_contextMenuType == ContextMenuChannelSettings)
{
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
dialog.setDefaultTitle(m_displayedName);
dialog.move(p);
dialog.exec();
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
m_settings.m_title = m_channelMarker.getTitle();
m_settings.m_useReverseAPI = dialog.useReverseAPI();
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
setWindowTitle(m_settings.m_title);
setTitle(m_channelMarker.getTitle());
setTitleColor(m_settings.m_rgbColor);
applySettings();
}
resetContextMenuType();
}
void DOA2GUI::on_decimationFactor_currentIndexChanged(int index)
{
m_settings.m_log2Decim = index;
updateScopeFScale();
applyDecimation();
}
void DOA2GUI::on_position_valueChanged(int value)
{
m_settings.m_filterChainHash = value;
applyPosition();
}
void DOA2GUI::on_phaseCorrection_valueChanged(int value)
{
m_settings.m_phase = value;
ui->phaseCorrectionText->setText(tr("%1").arg(value));
applySettings();
}
void DOA2GUI::on_correlationType_currentIndexChanged(int index)
{
m_settings.m_correlationType = (DOA2Settings::CorrelationType) index;
updateScopeFScale();
applySettings();
}
void DOA2GUI::on_antAz_valueChanged(int value)
{
m_settings.m_antennaAz = value;
ui->compass->setAzAnt(value);
updateDOA();
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::on_fftAveraging_currentIndexChanged(int index)
{
qDebug("DOA2GUI::on_averaging_currentIndexChanged: %d", index);
m_settings.m_fftAveragingIndex = index;
applySettings();
setFFTAveragingToolitp();
}
void DOA2GUI::on_centerPosition_clicked()
{
uint32_t filterChainHash = 1;
uint32_t mul = 1;
for (uint32_t i = 1; i < m_settings.m_log2Decim; i++)
{
mul *= 3;
filterChainHash += mul;
}
m_settings.m_filterChainHash = filterChainHash;
ui->position->setValue(m_settings.m_filterChainHash);
applyPosition();
}
void DOA2GUI::applyDecimation()
{
uint32_t maxHash = 1;
for (uint32_t i = 0; i < m_settings.m_log2Decim; i++) {
maxHash *= 3;
}
ui->position->setMaximum(maxHash-1);
ui->position->setValue(m_settings.m_filterChainHash);
m_settings.m_filterChainHash = ui->position->value();
applyPosition();
}
void DOA2GUI::applyPosition()
{
ui->filterChainIndex->setText(tr("%1").arg(m_settings.m_filterChainHash));
QString s;
m_shiftFrequencyFactor = HBFilterChainConverter::convertToString(m_settings.m_log2Decim, m_settings.m_filterChainHash, s);
ui->filterChainText->setText(s);
displayRateAndShift();
updateAbsoluteCenterFrequency();
applySettings();
}
void DOA2GUI::tick()
{
if (++m_tickCount == 20) // once per second
{
updateDOA();
m_tickCount = 0;
}
}
void DOA2GUI::makeUIConnections()
{
QObject::connect(ui->decimationFactor, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DOA2GUI::on_decimationFactor_currentIndexChanged);
QObject::connect(ui->position, &QSlider::valueChanged, this, &DOA2GUI::on_position_valueChanged);
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);
QObject::connect(ui->fftAveraging, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DOA2GUI::on_fftAveraging_currentIndexChanged);
QObject::connect(ui->centerPosition, &QPushButton::clicked, this, &DOA2GUI::on_centerPosition_clicked);
}
void DOA2GUI::updateAbsoluteCenterFrequency()
{
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));
updateScopeFScale();
}
void DOA2GUI::updateScopeFScale()
{
if (m_settings.m_correlationType == DOA2Settings::CorrelationType::CorrelationFFT) {
ui->glScope->setXScaleFreq(true);
} else {
ui->glScope->setXScaleFreq(false);
}
ui->glScope->setXScaleCenterFrequency(m_centerFrequency);
ui->glScope->setXScaleFrequencySpan(m_sampleRate / (1<<m_settings.m_log2Decim));
}
void DOA2GUI::updateDOA()
{
float cosTheta = (m_doa2->getPhi() * m_hwl * 1000.0) / (M_PI * m_settings.m_basebandDistance);
float blindAngle = (m_settings.m_basebandDistance > m_hwl * 1000.0) ? std::acos((m_hwl * 1000.0) / m_settings.m_basebandDistance) * (180/M_PI) : 0;
ui->compass->setBlindAngle(blindAngle);
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->setAzPos(posAngle);
ui->compass->setAzNeg(negAngle);
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')));
}

Wyświetl plik

@ -0,0 +1,115 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2GUI_H
#define INCLUDE_DOA2GUI_H
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "util/movingaverage.h"
#include "util/messagequeue.h"
#include "settings/rollupstate.h"
#include "doa2settings.h"
class PluginAPI;
class DeviceUISet;
class MIMOChannel;
class DOA2;
class ScopeVis;
namespace Ui {
class DOA2GUI;
}
class DOA2GUI : public ChannelGUI {
Q_OBJECT
public:
static DOA2GUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, MIMOChannel *mimoChannel);
virtual void destroy();
virtual void resetToDefaults();
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual MessageQueue* getInputMessageQueue();
virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; };
virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; };
virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; };
virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; };
virtual QString getTitle() const { return m_settings.m_title; };
virtual QColor getTitleColor() const { return m_settings.m_rgbColor; };
virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; }
virtual bool getHidden() const { return m_settings.m_hidden; }
virtual ChannelMarker& getChannelMarker() { return m_channelMarker; }
virtual int getStreamIndex() const { return -1; }
virtual void setStreamIndex(int streamIndex) { (void) streamIndex; }
private:
Ui::DOA2GUI* ui;
PluginAPI* m_pluginAPI;
DeviceUISet* m_deviceUISet;
ChannelMarker m_channelMarker;
RollupState m_rollupState;
DOA2Settings m_settings;
int m_sampleRate;
qint64 m_centerFrequency;
double m_shiftFrequencyFactor; //!< Channel frequency shift factor
bool m_doApplySettings;
MovingAverageUtil<double, double, 40> m_channelPowerAvg;
DOA2 *m_doa2;
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();
void blockApplySettings(bool block);
void applySettings(bool force = false);
void applyDecimation();
void applyPosition();
void displaySettings();
void displayRateAndShift();
void setFFTAveragingToolitp();
static void setNumberStr(float v, int decimalPlaces, QString& s);
bool handleMessage(const Message& message);
void makeUIConnections();
void updateAbsoluteCenterFrequency();
void updateScopeFScale();
void updateDOA();
void leaveEvent(QEvent*);
void enterEvent(QEvent*);
private slots:
void handleSourceMessages();
void on_decimationFactor_currentIndexChanged(int index);
void on_position_valueChanged(int value);
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 on_fftAveraging_currentIndexChanged(int index);
void on_centerPosition_clicked();
void onWidgetRolled(QWidget* widget, bool rollDown);
void onMenuDialogCalled(const QPoint& p);
void tick();
};
#endif // INCLUDE_DOA2GUI_H

Wyświetl plik

@ -0,0 +1,960 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DOA2GUI</class>
<widget class="RollupContents" name="DOA2GUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>720</width>
<height>642</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>720</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>DOA 2 Sources</string>
</property>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>10</y>
<width>718</width>
<height>81</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>718</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QVBoxLayout" name="decimationLayer">
<property name="spacing">
<number>3</number>
</property>
<item>
<layout class="QHBoxLayout" name="decimationStageLayer">
<item>
<widget class="QLabel" name="decimationLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="decimationFactor">
<property name="maximumSize">
<size>
<width>55</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Decimation factor</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>4</string>
</property>
</item>
<item>
<property name="text">
<string>8</string>
</property>
</item>
<item>
<property name="text">
<string>16</string>
</property>
</item>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="channelRateText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Effective channel rate (kS/s)</string>
</property>
<property name="text">
<string>0000k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainText">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain stages left to right (L: low, C: center, H: high) </string>
</property>
<property name="text">
<string>LLLLLL</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Corr</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="correlationType">
<property name="toolTip">
<string>Scope correlation type</string>
</property>
<item>
<property name="text">
<string>A</string>
</property>
</item>
<item>
<property name="text">
<string>B</string>
</property>
</item>
<item>
<property name="text">
<string>FFT</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="phaseCorrectionLabel">
<property name="text">
<string>Ph</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="phaseCorrection">
<property name="toolTip">
<string>Phase correction on stream 1</string>
</property>
<property name="minimum">
<number>-180</number>
</property>
<property name="maximum">
<number>180</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="phaseCorrectionText">
<property name="minimumSize">
<size>
<width>28</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Phase correction on stream 1 in degrees</string>
</property>
<property name="text">
<string>-180</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="offsetFrequencyText">
<property name="minimumSize">
<size>
<width>85</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Offset frequency with thousands separator (Hz)</string>
</property>
<property name="text">
<string>-9,999,999 Hz</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="decimationShiftLayer">
<property name="rightMargin">
<number>10</number>
</property>
<item>
<widget class="QLabel" name="positionLabel">
<property name="text">
<string>Pos</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="position">
<property name="toolTip">
<string>Center frequency position</string>
</property>
<property name="maximum">
<number>2</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="filterChainIndex">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Filter chain hash code</string>
</property>
<property name="text">
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="centerPosition">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Center in passband</string>
</property>
<property name="text">
<string>C</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="scopeContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>98</y>
<width>718</width>
<height>334</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>718</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Scope</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutScope">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
</property>
<item>
<widget class="GLScope" name="glScope" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>300</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>8</pointsize>
</font>
</property>
</widget>
</item>
<item>
<widget class="GLScopeGUI" name="scopeGUI" native="true"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="doaContainer" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>432</y>
<width>718</width>
<height>202</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>718</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>DOA</string>
</property>
<layout class="QVBoxLayout" name="verticalLayoutSpectrum">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="DOA2Compass" name="compass" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<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>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<item>
<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 (positive) 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 (negative) 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>
<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="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Half wavelength (mm)</string>
</property>
<property name="text">
<string>00000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</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>
<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>
<widget class="QLabel" name="spcLabel1_4">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fftAveragingLabel">
<property name="text">
<string>FFT avg</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fftAveraging">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>45</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Number of averaging FFTs</string>
</property>
<item>
<property name="text">
<string>1</string>
</property>
</item>
<item>
<property name="text">
<string>2</string>
</property>
</item>
<item>
<property name="text">
<string>5</string>
</property>
</item>
<item>
<property name="text">
<string>10</string>
</property>
</item>
<item>
<property name="text">
<string>20</string>
</property>
</item>
<item>
<property name="text">
<string>50</string>
</property>
</item>
<item>
<property name="text">
<string>100</string>
</property>
</item>
<item>
<property name="text">
<string>200</string>
</property>
</item>
<item>
<property name="text">
<string>500</string>
</property>
</item>
<item>
<property name="text">
<string>1k</string>
</property>
</item>
<item>
<property name="text">
<string>2k</string>
</property>
</item>
<item>
<property name="text">
<string>5k</string>
</property>
</item>
<item>
<property name="text">
<string>10k</string>
</property>
</item>
<item>
<property name="text">
<string>20k</string>
</property>
</item>
<item>
<property name="text">
<string>50k</string>
</property>
</item>
<item>
<property name="text">
<string>1e5</string>
</property>
</item>
<item>
<property name="text">
<string>2e5</string>
</property>
</item>
<item>
<property name="text">
<string>5e5</string>
</property>
</item>
<item>
<property name="text">
<string>1M</string>
</property>
</item>
</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>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_5">
<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>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLScope</class>
<extends>QWidget</extends>
<header>gui/glscope.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>GLScopeGUI</class>
<extends>QWidget</extends>
<header>gui/glscopegui.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>DOA2Compass</class>
<extends>QWidget</extends>
<header>doa2compass.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
</resources>
<connections/>
</ui>

Wyświetl plik

@ -0,0 +1,94 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 "doa2plugin.h"
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "doa2gui.h"
#endif
#include "doa2.h"
#include "doa2webapiadapter.h"
#include "doa2plugin.h"
const PluginDescriptor DOA2Plugin::m_pluginDescriptor = {
DOA2::m_channelId,
QStringLiteral("DOA 2 sources"),
QStringLiteral("7.3.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
DOA2Plugin::DOA2Plugin(QObject* parent) :
QObject(parent),
m_pluginAPI(0)
{
}
const PluginDescriptor& DOA2Plugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void DOA2Plugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
// register channel MIMO
m_pluginAPI->registerMIMOChannel(DOA2::m_channelIdURI, DOA2::m_channelId, this);
}
void DOA2Plugin::createMIMOChannel(DeviceAPI *deviceAPI, MIMOChannel **bs, ChannelAPI **cs) const
{
if (bs || cs)
{
DOA2 *instance = new DOA2(deviceAPI);
if (bs) {
*bs = instance;
}
if (cs) {
*cs = instance;
}
}
}
#ifdef SERVER_MODE
ChannelGUI* DOA2Plugin::createMIMOChannelGUI(
DeviceUISet *deviceUISet,
MIMOChannel *mimoChannel) const
{
(void) deviceUISet;
(void) mimoChannel;
return nullptr;
}
#else
ChannelGUI* DOA2Plugin::createMIMOChannelGUI(DeviceUISet *deviceUISet, MIMOChannel *mimoChannel) const
{
return DOA2GUI::create(m_pluginAPI, deviceUISet, mimoChannel);
}
#endif
ChannelWebAPIAdapter* DOA2Plugin::createChannelWebAPIAdapter() const
{
return new DOA2WebAPIAdapter();
}

Wyświetl plik

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef PLUGINS_CHANNELMIMO_DOA2_DOA2PLUGIN_H_
#define PLUGINS_CHANNELMIMO_DOA2_DOA2PLUGIN_H_
#include <QObject>
#include "plugin/plugininterface.h"
class DeviceUISet;
class MIMOChannel;
class DOA2Plugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.channelmimo.doa2")
public:
explicit DOA2Plugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual void createMIMOChannel(DeviceAPI *deviceAPI, MIMOChannel **bs, ChannelAPI **cs) const;
virtual ChannelGUI* createMIMOChannelGUI(DeviceUISet *deviceUISet, MIMOChannel *mimoChannel) const;
virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif /* PLUGINS_CHANNELMIMO_DOA2_DOA2PLUGIN_H_ */

Wyświetl plik

@ -0,0 +1,222 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QColor>
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "doa2settings.h"
DOA2Settings::DOA2Settings() :
m_channelMarker(nullptr),
m_scopeGUI(nullptr),
m_rollupState(nullptr)
{
resetToDefaults();
}
void DOA2Settings::resetToDefaults()
{
m_correlationType = CorrelationFFT;
m_rgbColor = QColor(250, 120, 120).rgb();
m_title = "DOA 2 sources";
m_log2Decim = 0;
m_filterChainHash = 0;
m_phase = 0;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
m_workspaceIndex = 0;
m_hidden = false;
m_antennaAz = 0;
m_basebandDistance = 500;
m_squelchdB = -50;
m_fftAveragingIndex = 0;
}
QByteArray DOA2Settings::serialize() const
{
SimpleSerializer s(1);
s.writeS32(2, (int) m_correlationType);
s.writeU32(3, m_rgbColor);
s.writeString(4, m_title);
s.writeU32(5, m_log2Decim);
s.writeU32(6, m_filterChainHash);
s.writeBool(7, m_useReverseAPI);
s.writeString(8, m_reverseAPIAddress);
s.writeU32(9, m_reverseAPIPort);
s.writeU32(10, m_reverseAPIDeviceIndex);
s.writeU32(11, m_reverseAPIChannelIndex);
s.writeS32(12, m_phase);
s.writeS32(13,m_workspaceIndex);
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);
s.writeS32(19, m_fftAveragingIndex);
if (m_scopeGUI) {
s.writeBlob(21, m_scopeGUI->serialize());
}
if (m_channelMarker) {
s.writeBlob(22, m_channelMarker->serialize());
}
if (m_rollupState) {
s.writeBlob(23, m_rollupState->serialize());
}
return s.final();
}
bool DOA2Settings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QByteArray bytetmp;
int tmp;
quint32 utmp;
d.readS32(2, &tmp, 0);
m_correlationType = (CorrelationType) tmp;
d.readU32(3, &m_rgbColor);
d.readString(4, &m_title, "DOA 2 sources");
d.readU32(5, &utmp, 0);
m_log2Decim = utmp > 6 ? 6 : utmp;
d.readU32(6, &m_filterChainHash, 0);
d.readBool(7, &m_useReverseAPI, false);
d.readString(8, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(9, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(10, &utmp, 0);
m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp;
d.readU32(11, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readS32(12, &tmp, 0);
m_phase = tmp < -180 ? -180 : tmp > 180 ? 180 : tmp;
d.readS32(13, &m_workspaceIndex);
d.readBlob(14, &m_geometryBytes);
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);
d.readS32(19, &tmp, 0);
m_fftAveragingIndex = tmp < 0 ?
0 :
tmp > 3*m_averagingMaxExponent + 3 ?
3*m_averagingMaxExponent + 3:
tmp;
if (m_scopeGUI)
{
d.readBlob(21, &bytetmp);
m_scopeGUI->deserialize(bytetmp);
}
if (m_channelMarker)
{
d.readBlob(22, &bytetmp);
m_channelMarker->deserialize(bytetmp);
}
if (m_rollupState)
{
d.readBlob(23, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
return true;
}
else
{
resetToDefaults();
return false;
}
}
int DOA2Settings::getAveragingValue(int averagingIndex)
{
if (averagingIndex <= 0) {
return 1;
}
int v = averagingIndex - 1;
int m = pow(10.0, v/3 > m_averagingMaxExponent ? m_averagingMaxExponent : v/3);
int x = 1;
if (v % 3 == 0) {
x = 2;
} else if (v % 3 == 1) {
x = 5;
} else if (v % 3 == 2) {
x = 10;
}
return x * m;
}
int DOA2Settings::getAveragingIndex(int averagingValue)
{
if (averagingValue <= 1) {
return 0;
}
int v = averagingValue;
int j = 0;
for (int i = 0; i <= m_averagingMaxExponent; i++)
{
if (v < 20)
{
if (v < 2) {
j = 0;
} else if (v < 5) {
j = 1;
} else if (v < 10) {
j = 2;
} else {
j = 3;
}
return 3*i + j;
}
v /= 10;
}
return 3*m_averagingMaxExponent + 3;
}

Wyświetl plik

@ -0,0 +1,70 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2SETTINGS_H
#define INCLUDE_DOA2SETTINGS_H
#include <QByteArray>
#include <QString>
class Serializable;
struct DOA2Settings
{
enum CorrelationType
{
Correlation0,
Correlation1,
CorrelationFFT,
};
CorrelationType m_correlationType;
quint32 m_rgbColor;
QString m_title;
uint32_t m_log2Decim;
uint32_t m_filterChainHash;
int m_phase;
int m_antennaAz;
uint32_t m_basebandDistance; //!< in millimeters
int m_squelchdB;
int m_fftAveragingIndex;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIDeviceIndex;
uint16_t m_reverseAPIChannelIndex;
int m_workspaceIndex;
QByteArray m_geometryBytes;
bool m_hidden;
Serializable *m_channelMarker;
Serializable *m_scopeGUI;
Serializable *m_rollupState;
DOA2Settings();
void resetToDefaults();
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; }
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
static int getAveragingValue(int averagingIndex);
static int getAveragingIndex(int averagingValue);
static const int m_averagingMaxExponent = 5; //!< Max 1M (10 * 10^5)
};
#endif // INCLUDE_DOA2SETTINGS_H

Wyświetl plik

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QMutexLocker>
#include <QDebug>
#include "doa2streamsink.h"
DOA2StreamSink::DOA2StreamSink() :
m_streamIndex(0),
m_dataSize(0),
m_bufferSize(0),
m_dataStart(0)
{}
DOA2StreamSink::~DOA2StreamSink()
{}
void DOA2StreamSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
m_dataSize = (end - begin) + m_dataStart;
if (m_dataSize > m_bufferSize)
{
m_data.resize(m_dataSize);
m_bufferSize = m_dataSize;
}
std::copy(begin, end, m_data.begin() + m_dataStart);
}
void DOA2StreamSink::reset()
{
m_dataStart = 0;
}

Wyświetl plik

@ -0,0 +1,51 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_DOA2STREAMSINK_H_
#define SDRBASE_DOA2STREAMSINK_H_
#include "dsp/channelsamplesink.h"
class DOA2StreamSink : public ChannelSampleSink
{
public:
DOA2StreamSink();
virtual ~DOA2StreamSink();
virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end);
void reset();
unsigned int getStreamIndex() const { return m_streamIndex; }
void setStreamIndex(unsigned int streamIndex) { m_streamIndex = streamIndex; }
SampleVector& getData() { return m_data; }
int getSize() const { return m_dataSize; }
void setDataStart(int dataStart) { m_dataStart = dataStart; }
private:
unsigned int m_streamIndex;
SampleVector m_data;
int m_dataSize;
int m_bufferSize;
int m_dataStart;
uint32_t m_log2Decim;
uint32_t m_filterChainHash;
};
#endif // SDRBASE_DOA2STREAMSINK_H_

Wyświetl plik

@ -0,0 +1,315 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB. //
// //
// 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 //
// (at your option) any later version. //
// //
// 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 <QString>
#include "SWGChannelSettings.h"
#include "doa2webapiadapter.h"
DOA2WebAPIAdapter::DOA2WebAPIAdapter()
{
m_settings.setScopeGUI(&m_glScopeSettings);
}
DOA2WebAPIAdapter::~DOA2WebAPIAdapter()
{}
int DOA2WebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setDoa2Settings(new SWGSDRangel::SWGDOA2Settings());
response.getDoa2Settings()->init();
webapiFormatChannelSettings(response, m_settings, m_glScopeSettings);
return 200;
}
void DOA2WebAPIAdapter::webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const DOA2Settings& settings,
const GLScopeSettings& scopeSettings)
{
response.getDoa2Settings()->setCorrelationType((int) settings.m_correlationType);
response.getDoa2Settings()->setRgbColor(settings.m_rgbColor);
response.getDoa2Settings()->setTitle(new QString(settings.m_title));
// scope
SWGSDRangel::SWGGLScope *swgScope = new SWGSDRangel::SWGGLScope();
swgScope->init();
response.getDoa2Settings()->setScopeConfig(swgScope);
swgScope->setDisplayMode(scopeSettings.m_displayMode);
swgScope->setGridIntensity(scopeSettings.m_gridIntensity);
swgScope->setTime(scopeSettings.m_time);
swgScope->setTimeOfs(scopeSettings.m_timeOfs);
swgScope->setTraceIntensity(scopeSettings.m_traceIntensity);
swgScope->setTraceLenMult(scopeSettings.m_traceLenMult);
swgScope->setTrigPre(scopeSettings.m_trigPre);
// array of traces
swgScope->setTracesData(new QList<SWGSDRangel::SWGTraceData *>);
std::vector<GLScopeSettings::TraceData>::const_iterator traceIt = scopeSettings.m_tracesData.begin();
for (; traceIt != scopeSettings.m_tracesData.end(); ++traceIt)
{
swgScope->getTracesData()->append(new SWGSDRangel::SWGTraceData);
swgScope->getTracesData()->back()->setAmp(traceIt->m_amp);
swgScope->getTracesData()->back()->setHasTextOverlay(traceIt->m_hasTextOverlay ? 1 : 0);
swgScope->getTracesData()->back()->setStreamIndex(traceIt->m_streamIndex);
swgScope->getTracesData()->back()->setOfs(traceIt->m_ofs);
swgScope->getTracesData()->back()->setProjectionType((int) traceIt->m_projectionType);
swgScope->getTracesData()->back()->setTextOverlay(new QString(traceIt->m_textOverlay));
swgScope->getTracesData()->back()->setTraceColor(qColorToInt(traceIt->m_traceColor));
swgScope->getTracesData()->back()->setTraceColorB(traceIt->m_traceColorB);
swgScope->getTracesData()->back()->setTraceColorG(traceIt->m_traceColorG);
swgScope->getTracesData()->back()->setTraceColorR(traceIt->m_traceColorR);
swgScope->getTracesData()->back()->setTraceDelay(traceIt->m_traceDelay);
swgScope->getTracesData()->back()->setTraceDelayCoarse(traceIt->m_traceDelayCoarse);
swgScope->getTracesData()->back()->setTraceDelayFine(traceIt->m_traceDelayFine);
swgScope->getTracesData()->back()->setTriggerDisplayLevel(traceIt->m_triggerDisplayLevel);
swgScope->getTracesData()->back()->setViewTrace(traceIt->m_viewTrace ? 1 : 0);
}
// array of triggers
swgScope->setTriggersData(new QList<SWGSDRangel::SWGTriggerData *>);
std::vector<GLScopeSettings::TriggerData>::const_iterator triggerIt = scopeSettings.m_triggersData.begin();
for (; triggerIt != scopeSettings.m_triggersData.end(); ++triggerIt)
{
swgScope->getTriggersData()->append(new SWGSDRangel::SWGTriggerData);
swgScope->getTriggersData()->back()->setInputIndex(triggerIt->m_inputIndex);
swgScope->getTriggersData()->back()->setProjectionType((int) triggerIt->m_projectionType);
swgScope->getTriggersData()->back()->setTriggerBothEdges(triggerIt->m_triggerBothEdges ? 1 : 0);
swgScope->getTriggersData()->back()->setTriggerColor(qColorToInt(triggerIt->m_triggerColor));
swgScope->getTriggersData()->back()->setTriggerColorB(triggerIt->m_triggerColorB);
swgScope->getTriggersData()->back()->setTriggerColorG(triggerIt->m_triggerColorG);
swgScope->getTriggersData()->back()->setTriggerColorR(triggerIt->m_triggerColorR);
swgScope->getTriggersData()->back()->setTriggerDelay(triggerIt->m_triggerDelay);
swgScope->getTriggersData()->back()->setTriggerDelayCoarse(triggerIt->m_triggerDelayCoarse);
swgScope->getTriggersData()->back()->setTriggerDelayFine(triggerIt->m_triggerDelayFine);
swgScope->getTriggersData()->back()->setTriggerDelayMult(triggerIt->m_triggerDelayMult);
swgScope->getTriggersData()->back()->setTriggerHoldoff(triggerIt->m_triggerHoldoff ? 1 : 0);
swgScope->getTriggersData()->back()->setTriggerLevel(triggerIt->m_triggerLevel);
swgScope->getTriggersData()->back()->setTriggerLevelCoarse(triggerIt->m_triggerLevelCoarse);
swgScope->getTriggersData()->back()->setTriggerLevelFine(triggerIt->m_triggerLevelFine);
swgScope->getTriggersData()->back()->setTriggerPositiveEdge(triggerIt->m_triggerPositiveEdge ? 1 : 0);
swgScope->getTriggersData()->back()->setTriggerRepeat(triggerIt->m_triggerRepeat);
}
}
int DOA2WebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
webapiUpdateChannelSettings(m_settings, m_glScopeSettings, channelSettingsKeys, response);
return 200;
}
void DOA2WebAPIAdapter::webapiUpdateChannelSettings(
DOA2Settings& settings,
GLScopeSettings& scopeSettings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response)
{
if (channelSettingsKeys.contains("correlationType")) {
settings.m_correlationType = (DOA2Settings::CorrelationType) response.getDoa2Settings()->getCorrelationType();
}
if (channelSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getDoa2Settings()->getRgbColor();
}
if (channelSettingsKeys.contains("title")) {
settings.m_title = *response.getDoa2Settings()->getTitle();
}
// scope
if (channelSettingsKeys.contains("scopeConfig"))
{
if (channelSettingsKeys.contains("scopeConfig.displayMode")) {
scopeSettings.m_displayMode = (GLScopeSettings::DisplayMode) response.getDoa2Settings()->getScopeConfig()->getDisplayMode();
}
if (channelSettingsKeys.contains("scopeConfig.gridIntensity")) {
scopeSettings.m_gridIntensity = response.getDoa2Settings()->getScopeConfig()->getGridIntensity();
}
if (channelSettingsKeys.contains("scopeConfig.time")) {
scopeSettings.m_time = response.getDoa2Settings()->getScopeConfig()->getTime();
}
if (channelSettingsKeys.contains("scopeConfig.timeOfs")) {
scopeSettings.m_timeOfs = response.getDoa2Settings()->getScopeConfig()->getTimeOfs();
}
if (channelSettingsKeys.contains("scopeConfig.traceIntensity")) {
scopeSettings.m_traceIntensity = response.getDoa2Settings()->getScopeConfig()->getTraceIntensity();
}
if (channelSettingsKeys.contains("scopeConfig.traceLenMult")) {
scopeSettings.m_traceLenMult = response.getDoa2Settings()->getScopeConfig()->getTraceLenMult();
}
if (channelSettingsKeys.contains("scopeConfig.trigPre")) {
scopeSettings.m_trigPre = response.getDoa2Settings()->getScopeConfig()->getTrigPre();
}
// traces
if (channelSettingsKeys.contains("scopeConfig.tracesData"))
{
QList<SWGSDRangel::SWGTraceData *> *tracesData = response.getDoa2Settings()->getScopeConfig()->getTracesData();
scopeSettings.m_tracesData.clear();
for (int i = 0; i < 10; i++) // no more than 10 traces anyway
{
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1]").arg(i)))
{
SWGSDRangel::SWGTraceData *traceData = tracesData->at(i);
scopeSettings.m_tracesData.push_back(GLScopeSettings::TraceData());
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].streamIndex").arg(i))) {
scopeSettings.m_tracesData.back().m_streamIndex = traceData->getStreamIndex();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].amp").arg(i))) {
scopeSettings.m_tracesData.back().m_amp = traceData->getAmp();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].hasTextOverlay").arg(i))) {
scopeSettings.m_tracesData.back().m_hasTextOverlay = traceData->getHasTextOverlay() != 0;
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].streamIndex").arg(i))) {
scopeSettings.m_tracesData.back().m_streamIndex = traceData->getStreamIndex();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].ofs").arg(i))) {
scopeSettings.m_tracesData.back().m_ofs = traceData->getOfs();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].projectionType").arg(i))) {
scopeSettings.m_tracesData.back().m_projectionType = (Projector::ProjectionType) traceData->getProjectionType();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceColor").arg(i))) {
scopeSettings.m_tracesData.back().m_traceColor = intToQColor(traceData->getTraceColor());
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceColorB").arg(i))) {
scopeSettings.m_tracesData.back().m_traceColorB = traceData->getTraceColorB();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceColorG").arg(i))) {
scopeSettings.m_tracesData.back().m_traceColorG = traceData->getTraceColorG();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceColorR").arg(i))) {
scopeSettings.m_tracesData.back().m_traceColorR = traceData->getTraceColorR();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceDelay").arg(i))) {
scopeSettings.m_tracesData.back().m_traceDelay = traceData->getTraceDelay();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceDelayCoarse").arg(i))) {
scopeSettings.m_tracesData.back().m_traceDelayCoarse = traceData->getTraceDelayCoarse();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].traceDelayFine").arg(i))) {
scopeSettings.m_tracesData.back().m_traceDelayFine = traceData->getTraceDelayFine();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].triggerDisplayLevel").arg(i))) {
scopeSettings.m_tracesData.back().m_triggerDisplayLevel = traceData->getTriggerDisplayLevel();
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].viewTrace").arg(i))) {
scopeSettings.m_tracesData.back().m_viewTrace = traceData->getViewTrace() != 0;
}
}
else
{
break;
}
}
}
// triggers
if (channelSettingsKeys.contains("scopeConfig.triggersData"))
{
QList<SWGSDRangel::SWGTriggerData *> *triggersData = response.getDoa2Settings()->getScopeConfig()->getTriggersData();
scopeSettings.m_triggersData.clear();
for (int i = 0; i < 10; i++) // no more than 10 triggers anyway
{
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1]").arg(i)))
{
SWGSDRangel::SWGTriggerData *triggerData = triggersData->at(i);
scopeSettings.m_triggersData.push_back(GLScopeSettings::TriggerData());
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].streamIndex").arg(i))) {
scopeSettings.m_triggersData.back().m_streamIndex = triggerData->getStreamIndex();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].inputIndex").arg(i))) {
scopeSettings.m_triggersData.back().m_inputIndex = triggerData->getInputIndex();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].projectionType").arg(i))) {
scopeSettings.m_triggersData.back().m_projectionType = (Projector::ProjectionType) triggerData->getProjectionType();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerBothEdges").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerBothEdges = triggerData->getTriggerBothEdges() != 0;
}
if (channelSettingsKeys.contains(QString("scopeConfig.tracesData[%1].triggerColor").arg(i))) {
scopeSettings.m_tracesData.back().m_traceColor = intToQColor(triggerData->getTriggerColor());
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerColorB").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerColorB = triggerData->getTriggerColorB();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerColorG").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerColorG = triggerData->getTriggerColorG();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerColorR").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerColorR = triggerData->getTriggerColorR();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerDelay").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerDelay = triggerData->getTriggerDelay();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerDelayCoarse").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerDelayCoarse = triggerData->getTriggerDelayCoarse();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerDelayFine").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerDelayFine = triggerData->getTriggerDelayFine();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerDelayMult").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerDelayMult = triggerData->getTriggerDelayMult();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerHoldoff").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerHoldoff = triggerData->getTriggerHoldoff();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerLevel").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerLevel = triggerData->getTriggerLevel();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerLevelCoarse").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerLevelCoarse = triggerData->getTriggerLevelCoarse();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerLevelFine").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerLevelFine = triggerData->getTriggerLevelFine();
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerPositiveEdge").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerPositiveEdge = triggerData->getTriggerPositiveEdge() != 0;
}
if (channelSettingsKeys.contains(QString("scopeConfig.triggersData[%1].triggerRepeat").arg(i))) {
scopeSettings.m_triggersData.back().m_triggerRepeat = triggerData->getTriggerRepeat() != 0;
}
}
}
}
}
}
int DOA2WebAPIAdapter::qColorToInt(const QColor& color)
{
return 256*256*color.blue() + 256*color.green() + color.red();
}
QColor DOA2WebAPIAdapter::intToQColor(int intColor)
{
int r = intColor % 256;
int bg = intColor / 256;
int g = bg % 256;
int b = bg / 256;
return QColor(r, g, b);
}

Wyświetl plik

@ -0,0 +1,66 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Edouard Griffiths, F4EXB. //
// //
// 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 //
// (at your option) any later version. //
// //
// 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/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_DOA2_WEBAPIADAPTER_H
#define INCLUDE_DOA2_WEBAPIADAPTER_H
#include "channel/channelwebapiadapter.h"
#include "dsp/glscopesettings.h"
#include "dsp/spectrumsettings.h"
#include "doa2settings.h"
/**
* Standalone API adapter only for the settings
*/
class DOA2WebAPIAdapter : public ChannelWebAPIAdapter {
public:
DOA2WebAPIAdapter();
virtual ~DOA2WebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response,
QString& errorMessage);
static void webapiFormatChannelSettings(
SWGSDRangel::SWGChannelSettings& response,
const DOA2Settings& settings,
const GLScopeSettings& scopeSettings);
static void webapiUpdateChannelSettings(
DOA2Settings& settings,
GLScopeSettings& scopeSettings,
const QStringList& channelSettingsKeys,
SWGSDRangel::SWGChannelSettings& response);
private:
DOA2Settings m_settings;
GLScopeSettings m_glScopeSettings;
static int qColorToInt(const QColor& color);
static QColor intToQColor(int intColor);
};
#endif // INCLUDE_DOA2_WEBAPIADAPTER_H

Wyświetl plik

@ -0,0 +1,221 @@
<h1>DOA with 2 sources plugin</h1>
<h2>Introduction</h2>
This MIMO reception only (MI) plugin can be used to determine the direction of arrival (DOA) of an incoming wave on a 2 antenna system connected to a coherent dual receiving device in MIMO (thus MI) mode like BladeRF3, LimeSDR USB, Pluto+. It is assume that antenna 1 is connected to stream 0 and antenna 2 is connected to stream 1. The direction of the antenna system goes from antenna 1 to antenna 2.
Example of setup:
![DOA2 full picture](../../../doc/img/DOA2_plugin_full.png)
This plugin has similarities with the [Interferometer plugin](../interferometer/readme.md) upon which it is built but is specialized in DOA processing and has displays and controls added in the "DOA" section. Also there is no spectrum display as it is of little interest for this purpose.
<h2>Interface</h2>
The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md)
![DOA2 plugin GUI](../../../doc/img/DOA2_plugin.png)
The interface is divided in 3 sections that will be detailed next:
- A: settings. These are the general plugin controls
- B: scope. This is a scope display analogous to other scope displays. I can show the A (stream 0) input, the B (stream 1) input or FFT (correlation for DOA). In the FFT case This is not a time domain but a frequency domain display transposed to time analogous to a frequency sweep. Therefore on the X and XY displays the time scale is replaced by a frequency scale.
- C: DOA specific section
<h2>A. Settings section</h2>
![DOA2 plugin settings GUI](../../../doc/img/DOA2_plugin_settings.png)
<h3>A.1. Decimation</h3>
Input streams from baseband are decimated by a power of two. Use this combo to select from 0 (no decimation) to 64 (2^6). The resulting channel sample rate is displayed next (A.2)
<h3>A.2. Channel sample rate</h3>
This is the channel sample rate in kilo or mega samples per second indicated by the `k` or `M` letter.
<h3>A.3. Half-band filter chain sequence display</h3>
This string represents the sequence of half-band filters used in the decimation from device baseband to channel stream and controlled by (A.7). Each character represents a filter type:
- **L**: lower half-band
- **H**: higher half-band
- **C**: centered
<h3>A.4. Correlation function</h3>
This combo selects which function is applied to A and B channel inputs to obtain resulting correlation signal.
- **A**: A channel only (no correlation thus no DOA processing)
- **B**: B channel only (no correlation thus no DOA processing)
- **FFT**: The FFT of A is multiplied by the conjugate of the FFT of B (no IFFT) thus the result is in the frequency domain. This is sometimes called "interspectrum". This is used to evaluate the direction of arrival (DOA) of a plane wave which is the purpose of this plugin. Note that DOA processing is active only in this mode.
<h4>Scope inputs</h4>
The scope display inputs vary depending on the correlation types and are summarized in the table below:
<table>
<tr>
<th>Correlation type</th>
<th>Scope</th>
</tr>
<tr>
<td>A</td>
<td>A(t)</td>
</tr>
<tr>
<td>B</td>
<td>B(t)</td>
</tr>
<tr>
<td>FFT</td>
<td>FFT(A)*FFT(B)* possibly shows several FFT lengths on the scope (B) depending on trace length. Leave the default trace length to get accurate frequency readings on the X scale</td>
</tr>
</table>
<h3>A.5. Phase difference correction</h3>
This is the phase correction in degrees applied to signal in channel B.
<h3>A.6 Center frequency shift</h3>
This is the shift of the channel center frequency from the device center frequency. Its value is driven by the baseband sample rate, the decimation factor (A.1) and the filter chain sequence (A.7).
<h3>A.7. Half-band filter chain sequence adjust</h3>
The slider moves the channel center frequency roughly from the lower to the higher frequency in the device baseband. The number on the right represents the filter sequence as the decimal value of a base 3 number. Each base 3 digit represents the filter type and its sequence from MSB to LSB in the filter chain:
- **0**: lower half-band
- **1**: centered
- **2**: higher half-band
<h3>A.8. Center filter in passband</h3>
Use this button to center the filter in the middle of the passband automatically. This sets all filters to center i.e CCC...
The resulting filter chain sequence is represented in (A.3)
<h2>B. Scope display</h2>
This is the scope (mainly time domain) display. This display and controls are identical to all scope displays in the software. Further details can be found in [Channel Analyzer documentation](../../channelrx/chanalyzer/readme.md)
See the principle of operation section below for optimum scope settings.
<h2>C. DOA settings</h2>
![DOA2 settings DOA](../../../doc/img/DOA2_plugin_DOA.png)
<h3>C.1 Compass readings</h3>
![DOA2 compass](../../../doc/img/DOA2_plugin_compass.png)
<h4>C.1.1 Port side incoming wave</h4>
The red arrow shows the direction of the incoming wave assuming it is ccoming from the port side of the antenna system (antenna 1 to 2). The value in degrees is displayed in (C.2).
This also corresponds to positive angles in the trigonometric sense with respect to the antenna baseline.
<h4>C.1.2 Starboard side incoming wave</h4>
The green arrow shows the direction of the incoming wave assuming it is ccoming from the starboard side of the antenna system (antenna 1 to 2). The value in degrees is displayed in (C.3).
This also corresponds to negative angles in the trigonometric sense with respect to the antenna baseline.
<h4>C.1.3 Compass needle</h4>
The compass needle shows the antennas direction with the white arrow towards antenna 2 and the grey arrow towards antenna 1. The direction (azimuth) is set with control (C.4). You must update this direction when the direction of the antenna system changes to get accurate readings.
<h4>C.1.4 Blind sector</h4>
The darker area on the compass background shows the sector where no readings can be made when the distance between the two antennas (baseline distance set in C.6) is larger than the half of the wavelength displayed in (C.5). Check the principle of operation section at the ned of the document for details.
<h3>C.2 Positive DOA angle (Port side)</h3>
Displays the posiive azimuth in degrees with respect to antenna direction of the incoming wave. This corresponds to the port side with respect to the antenna system from antenna 1 to antenna 2. This direction is displayed with a red arrow on the compass (1).
<h3>C.3 Negative DOA angle (Starboard side)</h3>
Displays the negative azimuth in degrees with respect to antenna direction of the incoming wave. This corresponds to the starboard side with respect to the antenna system from antenna 1 to antenna 2. This direction is displayed with a green arrow on the compass (1).
<h3>C.4 Antenna system azimuth</h3>
This is the direction in degrees of the antenna system from antenna 1 to antenna 2. You must update this value when the direction of the antenna system changes to get accurate readings.
<h3>C.5 Half wavelength distance in mm</h3>
This displays the half wavelength distance in millimeters at the center of frequency of reception.
<h3>C.6 Antenna baseline distance in mm</h3>
This is the distance in millimeters between antenna 1 and antenna 2. You must update this value when the distance between antennas change to get accurate readings.
<h3>C.7 Squelch threshold</h3>
This is the threshold of squared magnitude in dB above which DOA processing takes place. You can use the scope display in XY mode to find the best value.
![DOA2 XY display](../../../doc/img/DOA2_plugin_xy.png)
In this exaple setting a squelch value of -55~-50 dB will select the narrow peak corresponding to samples giving the most accurate DOA measurements.
This threshold can also be used with transient signals to activate DOA processing only when the signal is present effectively acting like a squelch in FM modes.
<h3>C.8 FFT Averaging</h3>
This is the number of FFT series used for DOA calculation thus the weighting average is computed over this many FFTs. The tooltip shows the averaging time to obtain one result.
<h2>Principle of operation</h2>
DOA estimation is based on the "FFT" correlation funtion and active only when selected with (A.2). FFT analysis helps in removing non essential contributions and is more efficient than simple product wutn conjugate (A.B*).
It assumes that channel A is connected to antenna 1 or antenna of reference (device stream 0) and channel B is connected antenna 2 the second antenna (device stream 1) in the following configuration:
![Interferometer antennas](../../../doc/img/interferometer_antennas.png)
<h3>Configuring the scope dsisplay</h3>
The scope shall be configured to have X and Y displays with Y1 set to a magnitude display projection and X to a phase related projection. See scope controls in B section for setup. Here are the different possibilities:
- **X**: Phi, DOAP, DOAN
- **Y1**: Mag, MagSq, MagDB (this one is usually the most convenient)
You will select the XY display on the scope and you can use the polar grid display to show phase or direction angles with respect to the antenna system directly. Note that for direction modes DOAP or DOAN a distance of half wavelength is assumed between antennas.
Angles are counted from -&pi; to &pi; and normalized to &pi; for display thus displayed from -1.0 to 1.0 on the scope.
<h3>DOA processing</h3>
The phase difference &phi; between the reference signal A (antenna 1) and second signal B (antenna 2) and seen on scope X display with `Phi` selected is used to estimate the direction of arrival (DOA) of the incoming wave. For a given phase difference the wave may come from the positive side (port side) of angles (0 to &pi;) or the negative side (starboard side) (-&pi; to 0). Angles of arrival are referenced to the axis perpendicular to the axis passing by the two antennas.
![Interferometer DOA](../../../doc/img/interferometer_doa.png)
Thus when antennas are separated by half the wavelength the relation between the angle of arrival &theta; and the phase difference &phi; can be expressed as:
&phi; = &pi; cos(&theta;) thus &theta; = acos(&phi; / &pi;)
This angle can be displayed on the scope when `DOAP` (positive angles) or `DOAN` (negative angles) is selected for X input.
![Interferometer DOA General](../../../doc/img/interferometer_doa_general.png)
In general the angle can be calculated from the baseline distance D (distance between antennas) with the following formula:
&phi; = &pi; d / (&lambda;/2)
&phi; = &pi; D cos(&theta;) / (&lambda;/2)
cos(&theta;) = (&phi; / &pi;) . ((&lambda;/2) / D)
If D is larger than half the wavelength (&lambda;/2) then a section in front of antenna 2 and at the back of antenna 1 cannot be reached since cos(&theta;) will lie in an interval smaller than [-1:1]. This section is shown on the compass with a darker background (C.1.4)
If D is smaller than half the wavelength some incorrect values can be read as some angles will ovelap. Unless you have a hint about the wave direction this mode of operation is not recommended,
There are two possible angles for the incoming wave leading to the same phase difference. One from the port side of the antenna system (positive) and the other from the starboard side (negative).
To disambiguate readings one may turn the antenna system then the correct side will show a constant absolute angle with respect to true North (or South).
For best results the antenna system should be clear of possible reflectors including your own body that can affect the incoming direct wave. Ideally you should also start the procedure with a distance between antennas (baseline distance) of half the wavelength of the signal of interest.
Then a possible procedure to determine DOA is the following:
1. Arrange antennas axis so that the phase difference &phi; is zero or DOA angle &theta; is roughly &pi;/2
2. Make an assumption for the wave to come from the positive (port) or negative (starboard) angles side and take it as the DOA angle
3. Rotate the antennas axis of about 45 degrees and if the DOA angle is roughly stable in absolute value then the assumption is correct and the wave is coming from the side corresponding to your assumption. You can then refine the antenna axis direction to obtain a &pi;/2 or -&pi;/2 angle depending from which side the wave is coming. The red arrow on the compass (C.1.1) shows the absolute direction of the wave coming from the port side and the green arrow (C.1.2) shows the absolute direction of the wave coming from the starboard side assuming that the direction of the antennas is set properly and follows the antennas system movement (C.4).
4. If when performing previous step (3) the DOA angle is significantly moving in absolute value then the wave is coming from the opposite side of the antenna system. Then take the other side angle reading as valid.
5. Once the &plusmn;&pi;/2 DOA angle (zero phase difference) is obtained at &lambda;/2 distance between antennas you can move your antennas further apart to refine the &plusmn;&pi;/2 DOA angle.

Wyświetl plik

@ -30,7 +30,7 @@
const PluginDescriptor InterferometerPlugin::m_pluginDescriptor = {
Interferometer::m_channelId,
QStringLiteral("Interferometer"),
QStringLiteral("7.0.0"),
QStringLiteral("7.3.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,

Wyświetl plik

@ -520,6 +520,7 @@ void FreeDVDemodGUI::makeUIConnections()
QObject::connect(ui->volumeIn, &QDial::valueChanged, this, &FreeDVDemodGUI::on_volumeIn_valueChanged);
QObject::connect(ui->agc, &ButtonSwitch::toggled, this, &FreeDVDemodGUI::on_agc_toggled);
QObject::connect(ui->audioMute, &QToolButton::toggled, this, &FreeDVDemodGUI::on_audioMute_toggled);
QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &FreeDVDemodGUI::on_spanLog2_valueChanged);
}
void FreeDVDemodGUI::updateAbsoluteCenterFrequency()

Wyświetl plik

@ -635,6 +635,7 @@ void FreeDVModGUI::makeUIConnections()
QObject::connect(ui->morseKeyer, &ButtonSwitch::toggled, this, &FreeDVModGUI::on_morseKeyer_toggled);
QObject::connect(ui->navTimeSlider, &QSlider::valueChanged, this, &FreeDVModGUI::on_navTimeSlider_valueChanged);
QObject::connect(ui->showFileDialog, &QPushButton::clicked, this, &FreeDVModGUI::on_showFileDialog_clicked);
QObject::connect(ui->spanLog2, &QSlider::valueChanged, this, &FreeDVModGUI::on_spanLog2_valueChanged);
}
void FreeDVModGUI::updateAbsoluteCenterFrequency()

Wyświetl plik

@ -28,7 +28,7 @@
const PluginDescriptor FreeDVModPlugin::m_pluginDescriptor = {
FreeDVMod::m_channelId,
QStringLiteral("FreeDV Modulator"),
QStringLiteral("7.0.0"),
QStringLiteral("7.3.0"),
QStringLiteral("(c) Edouard Griffiths, F4EXB"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,

Wyświetl plik

@ -455,7 +455,7 @@ void FreeDVModSource::applyFreeDVMode(FreeDVModSettings::FreeDVMode mode)
{
case FreeDVModSettings::FreeDVMode700C:
fdv_mode = FREEDV_MODE_700C;
m_scaleFactor = SDR_TX_SCALEF / 3.2f;
m_scaleFactor = SDR_TX_SCALEF / 6.4f;
break;
case FreeDVModSettings::FreeDVMode700D:
fdv_mode = FREEDV_MODE_700D;
@ -463,16 +463,16 @@ void FreeDVModSource::applyFreeDVMode(FreeDVModSettings::FreeDVMode mode)
break;
case FreeDVModSettings::FreeDVMode800XA:
fdv_mode = FREEDV_MODE_800XA;
m_scaleFactor = SDR_TX_SCALEF / 8.2f;
m_scaleFactor = SDR_TX_SCALEF / 10.3f;
break;
case FreeDVModSettings::FreeDVMode1600:
fdv_mode = FREEDV_MODE_1600;
m_scaleFactor = SDR_TX_SCALEF / 3.2f;
m_scaleFactor = SDR_TX_SCALEF / 4.0f;
break;
case FreeDVModSettings::FreeDVMode2400A:
default:
fdv_mode = FREEDV_MODE_2400A;
m_scaleFactor = SDR_TX_SCALEF / 8.2f;
m_scaleFactor = SDR_TX_SCALEF / 10.3f;
break;
}

Wyświetl plik

@ -36,6 +36,7 @@
<file>webapi/doc/swagger/include/DATVDemod.yaml</file>
<file>webapi/doc/swagger/include/DATVMod.yaml</file>
<file>webapi/doc/swagger/include/DemodAnalyzer.yaml</file>
<file>webapi/doc/swagger/include/DOA2.yaml</file>
<file>webapi/doc/swagger/include/DSDDemod.yaml</file>
<file>webapi/doc/swagger/include/DeviceActions.yaml</file>
<file>webapi/doc/swagger/include/DeviceSettings.yaml</file>

Wyświetl plik

@ -3397,6 +3397,9 @@ margin-bottom: 20px;
"DATVModReport" : {
"$ref" : "#/definitions/DATVModReport"
},
"DOA2Report" : {
"$ref" : "#/definitions/DOA2Report"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -3543,6 +3546,9 @@ margin-bottom: 20px;
"DABDemodSettings" : {
"$ref" : "#/definitions/DABDemodSettings"
},
"DOA2Settings" : {
"$ref" : "#/definitions/DOA2Settings"
},
"DSDDemodSettings" : {
"$ref" : "#/definitions/DSDDemodSettings"
},
@ -4522,6 +4528,97 @@ margin-bottom: 20px;
}
},
"description" : "DATVMod"
};
defs.DOA2Report = {
"properties" : {
"phi" : {
"type" : "integer",
"description" : "Raw phase difference in degrees from 0 to 180"
},
"posAz" : {
"type" : "integer",
"description" : "Calculated port side (positive) arrival angle in degrees from 0 to 180"
},
"negAz" : {
"type" : "integer",
"description" : "Calculated starboard side (negative) arrival angle in degrees from 0 to 180"
},
"fftSize" : {
"type" : "integer",
"description" : "Size of FFT used in correlation"
},
"channelSampleRate" : {
"type" : "integer",
"description" : "Channel sample rate (then used in FFT) in S/s"
}
},
"description" : "DOA2"
};
defs.DOA2Settings = {
"properties" : {
"correlationType" : {
"type" : "integer",
"description" : "see DOA2Settings::CorrelationType"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"log2Decim" : {
"type" : "integer"
},
"filterChainHash" : {
"type" : "integer"
},
"phase" : {
"type" : "integer",
"description" : "Phase difference correction in degrees from -180 to +180"
},
"antennaAz" : {
"type" : "integer",
"description" : "Antennas azimuth from antenna 1 to antenna 2 in degrees from 0 to 359"
},
"basebandDistance" : {
"type" : "integer",
"description" : "Antennas baseline distance in millimeters from 1 to 99999"
},
"squelchdB" : {
"type" : "integer",
"description" : "Porcessing squared magnitude threshold (squelch) in dB from -140 t0 0"
},
"fftAveragingValue" : {
"type" : "integer",
"description" : "Number of FFTs to average over. Use 1, 2, 5 or 10 times 10^0 to 10^5 (1 to 1M)"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
},
"scopeConfig" : {
"$ref" : "#/definitions/GLScope"
},
"channelMarker" : {
"$ref" : "#/definitions/ChannelMarker"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "DOA2"
};
defs.DSDDemodReport = {
"properties" : {
@ -56060,7 +56157,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2022-05-25T12:47:57.273+02:00
Generated 2022-05-28T12:29:36.569+02:00
</div>
</div>
</div>

Wyświetl plik

@ -33,6 +33,8 @@ ChannelReport:
$ref: "/doc/swagger/include/DATVDemod.yaml#/DATVDemodReport"
DATVModReport:
$ref: "/doc/swagger/include/DATVMod.yaml#/DATVModReport"
DOA2Report:
$ref: "/doc/swagger/include/DOA2.yaml#/DOA2Report"
DSDDemodReport:
$ref: "/doc/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

Wyświetl plik

@ -49,6 +49,8 @@ ChannelSettings:
$ref: "/doc/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DABDemodSettings:
$ref: "/doc/swagger/include/DABDemod.yaml#/DABDemodSettings"
DOA2Settings:
$ref: "/doc/swagger/include/DOA2.yaml#/DOA2Settings"
DSDDemodSettings:
$ref: "/doc/swagger/include/DSDDemod.yaml#/DSDDemodSettings"
FileSinkSettings:

Wyświetl plik

@ -0,0 +1,65 @@
DOA2Settings:
description: DOA2
properties:
correlationType:
description: see DOA2Settings::CorrelationType
type: integer
rgbColor:
type: integer
title:
type: string
log2Decim:
type: integer
filterChainHash:
type: integer
phase:
type: integer
description: Phase difference correction in degrees from -180 to +180
antennaAz:
type: integer
description: Antennas azimuth from antenna 1 to antenna 2 in degrees from 0 to 359
basebandDistance:
type: integer
description: Antennas baseline distance in millimeters from 1 to 99999
squelchdB:
type: integer
description: Porcessing squared magnitude threshold (squelch) in dB from -140 t0 0
fftAveragingValue:
type: integer
description: Number of FFTs to average over. Use 1, 2, 5 or 10 times 10^0 to 10^5 (1 to 1M)
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
scopeConfig:
$ref: "/doc/swagger/include/GLScope.yaml#/GLScope"
channelMarker:
$ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "/doc/swagger/include/RollupState.yaml#/RollupState"
DOA2Report:
description: DOA2
properties:
phi:
type: integer
description: Raw phase difference in degrees from 0 to 180
posAz:
type: integer
description: Calculated port side (positive) arrival angle in degrees from 0 to 180
negAz:
type: integer
description: Calculated starboard side (negative) arrival angle in degrees from 0 to 180
fftSize:
type: integer
description: Size of FFT used in correlation
channelSampleRate:
type: integer
description: Channel sample rate (then used in FFT) in S/s

Wyświetl plik

@ -4471,6 +4471,11 @@ bool WebAPIRequestMapper::getChannelSettings(
channelSettings->setDabDemodSettings(new SWGSDRangel::SWGDABDemodSettings());
channelSettings->getDabDemodSettings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DOA2Settings")
{
channelSettings->setDoa2Settings(new SWGSDRangel::SWGDOA2Settings());
channelSettings->getDoa2Settings()->fromJsonObject(settingsJsonObject);
}
else if (channelSettingsKey == "DSDDemodSettings")
{
channelSettings->setDsdDemodSettings(new SWGSDRangel::SWGDSDDemodSettings());

Wyświetl plik

@ -40,6 +40,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelURIToSettingsKey = {
{"sdrangel.channel.demodatv", "ATVDemodSettings"},
{"sdrangel.channel.demoddatv", "DATVDemodSettings"},
{"sdrangel.channel.dabdemod", "DABDemodSettings"},
{"sdrangel.channel.doa2", "DOA2Settings"},
{"sdrangel.channel.dsddemod", "DSDDemodSettings"},
{"sdrangel.channel.filesink", "FileSinkSettings"},
{"sdrangel.channeltx.filesource", "FileSourceSettings"},
@ -144,6 +145,7 @@ const QMap<QString, QString> WebAPIUtils::m_channelTypeToSettingsKey = {
{"DATVDemod", "DATVDemodSettings"},
{"DATVMod", "DATVModSettings"},
{"DABDemod", "DABDemodSettings"},
{"DOA2", "DOA2Settings"},
{"DSDDemod", "DSDDemodSettings"},
{"FileSink", "FileSinkSettings"},
{"FileSource", "FileSourceSettings"},

Wyświetl plik

@ -75,6 +75,9 @@ GLScope::GLScope(QWidget *parent) :
m_x1Scale.setOrientation(Qt::Horizontal);
m_x2Scale.setFont(font());
m_x2Scale.setOrientation(Qt::Horizontal);
m_xScaleFreq = false;
m_xScaleCenterFrequency = 0;
m_xScaleFrequencySpan = 48000;
m_channelOverlayFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
m_channelOverlayFont.setBold(true);
@ -1077,12 +1080,23 @@ void GLScope::applyConfig()
// scales
m_x1Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
if (m_displayMode == DisplayPol) {
setYScale(m_x2Scale, 0); // polar scale (X)
if (m_xScaleFreq) {
m_x1Scale.setRange(Unit::Frequency, m_xScaleCenterFrequency - (m_xScaleFrequencySpan/2), m_xScaleCenterFrequency + (m_xScaleFrequencySpan/2));
} else {
m_x2Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
m_x1Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
}
if (m_displayMode == DisplayPol)
{
setYScale(m_x2Scale, 0); // polar scale (X)
}
else
{
if (m_xScaleFreq) {
m_x2Scale.setRange(Unit::Time, t_start, t_start + t_len); // time scale
} else {
m_x2Scale.setRange(Unit::Frequency, m_xScaleCenterFrequency - (m_xScaleFrequencySpan/2), m_xScaleCenterFrequency + (m_xScaleFrequencySpan/2));
}
}
if (m_traces->size() > 0) {

Wyświetl plik

@ -86,6 +86,11 @@ public:
virtual const QAtomicInt& getProcessingTraceIndex() const { return m_processingTraceIndex; }
void setTraceModulo(int modulo) { m_traceModulo = modulo; }
void setXScaleFreq(bool set) { m_xScaleFreq = set; m_configChanged = true; }
bool isXScaleFreq() const { return m_xScaleFreq; }
void setXScaleCenterFrequency(qint64 cf) { m_xScaleCenterFrequency = cf; m_configChanged = true; }
void setXScaleFrequencySpan(int span) { m_xScaleFrequencySpan = span; m_configChanged = true; }
signals:
void sampleRateChanged(int);
void traceSizeChanged(uint32_t);
@ -187,6 +192,9 @@ private:
ScaleEngine m_x2Scale; //!< Display #2 X scale. Time scale
ScaleEngine m_y1Scale; //!< Display #1 Y scale. Always connected to trace #0 (X trace)
ScaleEngine m_y2Scale; //!< Display #2 Y scale. Connected to highlighted Y trace (#1..n)
bool m_xScaleFreq; //!< Force frequency display on time line for correlation modes
qint64 m_xScaleCenterFrequency; //!< Frequency time line mode center frequency
int m_xScaleFrequencySpan; //!< Frequency time line mode frequency span
QFont m_channelOverlayFont;
QFont m_textOverlayFont;

Wyświetl plik

@ -33,6 +33,8 @@ ChannelReport:
$ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodReport"
DATVModReport:
$ref: "http://swgserver:8081/api/swagger/include/DATVMod.yaml#/DATVModReport"
DOA2Report:
$ref: "http://swgserver:8081/api/swagger/include/DOA2.yaml#/DOA2Report"
DSDDemodReport:
$ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodReport"
IEEE_802_15_4_ModReport:

Wyświetl plik

@ -49,6 +49,8 @@ ChannelSettings:
$ref: "http://swgserver:8081/api/swagger/include/DATVDemod.yaml#/DATVDemodSettings"
DABDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DABDemod.yaml#/DABDemodSettings"
DOA2Settings:
$ref: "http://swgserver:8081/api/swagger/include/DOA2.yaml#/DOA2Settings"
DSDDemodSettings:
$ref: "http://swgserver:8081/api/swagger/include/DSDDemod.yaml#/DSDDemodSettings"
FileSinkSettings:

Wyświetl plik

@ -0,0 +1,65 @@
DOA2Settings:
description: DOA2
properties:
correlationType:
description: see DOA2Settings::CorrelationType
type: integer
rgbColor:
type: integer
title:
type: string
log2Decim:
type: integer
filterChainHash:
type: integer
phase:
type: integer
description: Phase difference correction in degrees from -180 to +180
antennaAz:
type: integer
description: Antennas azimuth from antenna 1 to antenna 2 in degrees from 0 to 359
basebandDistance:
type: integer
description: Antennas baseline distance in millimeters from 1 to 99999
squelchdB:
type: integer
description: Porcessing squared magnitude threshold (squelch) in dB from -140 t0 0
fftAveragingValue:
type: integer
description: Number of FFTs to average over. Use 1, 2, 5 or 10 times 10^0 to 10^5 (1 to 1M)
useReverseAPI:
description: Synchronize with reverse API (1 for yes, 0 for no)
type: integer
reverseAPIAddress:
type: string
reverseAPIPort:
type: integer
reverseAPIDeviceIndex:
type: integer
reverseAPIChannelIndex:
type: integer
scopeConfig:
$ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope"
channelMarker:
$ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker"
rollupState:
$ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState"
DOA2Report:
description: DOA2
properties:
phi:
type: integer
description: Raw phase difference in degrees from 0 to 180
posAz:
type: integer
description: Calculated port side (positive) arrival angle in degrees from 0 to 180
negAz:
type: integer
description: Calculated starboard side (negative) arrival angle in degrees from 0 to 180
fftSize:
type: integer
description: Size of FFT used in correlation
channelSampleRate:
type: integer
description: Channel sample rate (then used in FFT) in S/s

Wyświetl plik

@ -3397,6 +3397,9 @@ margin-bottom: 20px;
"DATVModReport" : {
"$ref" : "#/definitions/DATVModReport"
},
"DOA2Report" : {
"$ref" : "#/definitions/DOA2Report"
},
"DSDDemodReport" : {
"$ref" : "#/definitions/DSDDemodReport"
},
@ -3543,6 +3546,9 @@ margin-bottom: 20px;
"DABDemodSettings" : {
"$ref" : "#/definitions/DABDemodSettings"
},
"DOA2Settings" : {
"$ref" : "#/definitions/DOA2Settings"
},
"DSDDemodSettings" : {
"$ref" : "#/definitions/DSDDemodSettings"
},
@ -4522,6 +4528,97 @@ margin-bottom: 20px;
}
},
"description" : "DATVMod"
};
defs.DOA2Report = {
"properties" : {
"phi" : {
"type" : "integer",
"description" : "Raw phase difference in degrees from 0 to 180"
},
"posAz" : {
"type" : "integer",
"description" : "Calculated port side (positive) arrival angle in degrees from 0 to 180"
},
"negAz" : {
"type" : "integer",
"description" : "Calculated starboard side (negative) arrival angle in degrees from 0 to 180"
},
"fftSize" : {
"type" : "integer",
"description" : "Size of FFT used in correlation"
},
"channelSampleRate" : {
"type" : "integer",
"description" : "Channel sample rate (then used in FFT) in S/s"
}
},
"description" : "DOA2"
};
defs.DOA2Settings = {
"properties" : {
"correlationType" : {
"type" : "integer",
"description" : "see DOA2Settings::CorrelationType"
},
"rgbColor" : {
"type" : "integer"
},
"title" : {
"type" : "string"
},
"log2Decim" : {
"type" : "integer"
},
"filterChainHash" : {
"type" : "integer"
},
"phase" : {
"type" : "integer",
"description" : "Phase difference correction in degrees from -180 to +180"
},
"antennaAz" : {
"type" : "integer",
"description" : "Antennas azimuth from antenna 1 to antenna 2 in degrees from 0 to 359"
},
"basebandDistance" : {
"type" : "integer",
"description" : "Antennas baseline distance in millimeters from 1 to 99999"
},
"squelchdB" : {
"type" : "integer",
"description" : "Porcessing squared magnitude threshold (squelch) in dB from -140 t0 0"
},
"fftAveragingValue" : {
"type" : "integer",
"description" : "Number of FFTs to average over. Use 1, 2, 5 or 10 times 10^0 to 10^5 (1 to 1M)"
},
"useReverseAPI" : {
"type" : "integer",
"description" : "Synchronize with reverse API (1 for yes, 0 for no)"
},
"reverseAPIAddress" : {
"type" : "string"
},
"reverseAPIPort" : {
"type" : "integer"
},
"reverseAPIDeviceIndex" : {
"type" : "integer"
},
"reverseAPIChannelIndex" : {
"type" : "integer"
},
"scopeConfig" : {
"$ref" : "#/definitions/GLScope"
},
"channelMarker" : {
"$ref" : "#/definitions/ChannelMarker"
},
"rollupState" : {
"$ref" : "#/definitions/RollupState"
}
},
"description" : "DOA2"
};
defs.DSDDemodReport = {
"properties" : {
@ -56060,7 +56157,7 @@ except ApiException as e:
</div>
<div id="generator">
<div class="content">
Generated 2022-05-25T12:47:57.273+02:00
Generated 2022-05-28T12:29:36.569+02:00
</div>
</div>
</div>

Wyświetl plik

@ -54,6 +54,8 @@ SWGChannelReport::SWGChannelReport() {
m_datv_demod_report_isSet = false;
datv_mod_report = nullptr;
m_datv_mod_report_isSet = false;
doa2_report = nullptr;
m_doa2_report_isSet = false;
dsd_demod_report = nullptr;
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = nullptr;
@ -138,6 +140,8 @@ SWGChannelReport::init() {
m_datv_demod_report_isSet = false;
datv_mod_report = new SWGDATVModReport();
m_datv_mod_report_isSet = false;
doa2_report = new SWGDOA2Report();
m_doa2_report_isSet = false;
dsd_demod_report = new SWGDSDDemodReport();
m_dsd_demod_report_isSet = false;
ieee_802_15_4_mod_report = new SWGIEEE_802_15_4_ModReport();
@ -229,6 +233,9 @@ SWGChannelReport::cleanup() {
if(datv_mod_report != nullptr) {
delete datv_mod_report;
}
if(doa2_report != nullptr) {
delete doa2_report;
}
if(dsd_demod_report != nullptr) {
delete dsd_demod_report;
}
@ -343,6 +350,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&datv_mod_report, pJson["DATVModReport"], "SWGDATVModReport", "SWGDATVModReport");
::SWGSDRangel::setValue(&doa2_report, pJson["DOA2Report"], "SWGDOA2Report", "SWGDOA2Report");
::SWGSDRangel::setValue(&dsd_demod_report, pJson["DSDDemodReport"], "SWGDSDDemodReport", "SWGDSDDemodReport");
::SWGSDRangel::setValue(&ieee_802_15_4_mod_report, pJson["IEEE_802_15_4_ModReport"], "SWGIEEE_802_15_4_ModReport", "SWGIEEE_802_15_4_ModReport");
@ -448,6 +457,9 @@ SWGChannelReport::asJsonObject() {
if((datv_mod_report != nullptr) && (datv_mod_report->isSet())){
toJsonValue(QString("DATVModReport"), datv_mod_report, obj, QString("SWGDATVModReport"));
}
if((doa2_report != nullptr) && (doa2_report->isSet())){
toJsonValue(QString("DOA2Report"), doa2_report, obj, QString("SWGDOA2Report"));
}
if((dsd_demod_report != nullptr) && (dsd_demod_report->isSet())){
toJsonValue(QString("DSDDemodReport"), dsd_demod_report, obj, QString("SWGDSDDemodReport"));
}
@ -657,6 +669,16 @@ SWGChannelReport::setDatvModReport(SWGDATVModReport* datv_mod_report) {
this->m_datv_mod_report_isSet = true;
}
SWGDOA2Report*
SWGChannelReport::getDoa2Report() {
return doa2_report;
}
void
SWGChannelReport::setDoa2Report(SWGDOA2Report* doa2_report) {
this->doa2_report = doa2_report;
this->m_doa2_report_isSet = true;
}
SWGDSDDemodReport*
SWGChannelReport::getDsdDemodReport() {
return dsd_demod_report;
@ -951,6 +973,9 @@ SWGChannelReport::isSet(){
if(datv_mod_report && datv_mod_report->isSet()){
isObjectUpdated = true; break;
}
if(doa2_report && doa2_report->isSet()){
isObjectUpdated = true; break;
}
if(dsd_demod_report && dsd_demod_report->isSet()){
isObjectUpdated = true; break;
}

Wyświetl plik

@ -33,6 +33,7 @@
#include "SWGChirpChatModReport.h"
#include "SWGDATVDemodReport.h"
#include "SWGDATVModReport.h"
#include "SWGDOA2Report.h"
#include "SWGDSDDemodReport.h"
#include "SWGFileSinkReport.h"
#include "SWGFileSourceReport.h"
@ -117,6 +118,9 @@ public:
SWGDATVModReport* getDatvModReport();
void setDatvModReport(SWGDATVModReport* datv_mod_report);
SWGDOA2Report* getDoa2Report();
void setDoa2Report(SWGDOA2Report* doa2_report);
SWGDSDDemodReport* getDsdDemodReport();
void setDsdDemodReport(SWGDSDDemodReport* dsd_demod_report);
@ -235,6 +239,9 @@ private:
SWGDATVModReport* datv_mod_report;
bool m_datv_mod_report_isSet;
SWGDOA2Report* doa2_report;
bool m_doa2_report_isSet;
SWGDSDDemodReport* dsd_demod_report;
bool m_dsd_demod_report_isSet;

Wyświetl plik

@ -68,6 +68,8 @@ SWGChannelSettings::SWGChannelSettings() {
m_datv_demod_settings_isSet = false;
dab_demod_settings = nullptr;
m_dab_demod_settings_isSet = false;
doa2_settings = nullptr;
m_doa2_settings_isSet = false;
dsd_demod_settings = nullptr;
m_dsd_demod_settings_isSet = false;
file_sink_settings = nullptr;
@ -174,6 +176,8 @@ SWGChannelSettings::init() {
m_datv_demod_settings_isSet = false;
dab_demod_settings = new SWGDABDemodSettings();
m_dab_demod_settings_isSet = false;
doa2_settings = new SWGDOA2Settings();
m_doa2_settings_isSet = false;
dsd_demod_settings = new SWGDSDDemodSettings();
m_dsd_demod_settings_isSet = false;
file_sink_settings = new SWGFileSinkSettings();
@ -290,6 +294,9 @@ SWGChannelSettings::cleanup() {
if(dab_demod_settings != nullptr) {
delete dab_demod_settings;
}
if(doa2_settings != nullptr) {
delete doa2_settings;
}
if(dsd_demod_settings != nullptr) {
delete dsd_demod_settings;
}
@ -430,6 +437,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&dab_demod_settings, pJson["DABDemodSettings"], "SWGDABDemodSettings", "SWGDABDemodSettings");
::SWGSDRangel::setValue(&doa2_settings, pJson["DOA2Settings"], "SWGDOA2Settings", "SWGDOA2Settings");
::SWGSDRangel::setValue(&dsd_demod_settings, pJson["DSDDemodSettings"], "SWGDSDDemodSettings", "SWGDSDDemodSettings");
::SWGSDRangel::setValue(&file_sink_settings, pJson["FileSinkSettings"], "SWGFileSinkSettings", "SWGFileSinkSettings");
@ -564,6 +573,9 @@ SWGChannelSettings::asJsonObject() {
if((dab_demod_settings != nullptr) && (dab_demod_settings->isSet())){
toJsonValue(QString("DABDemodSettings"), dab_demod_settings, obj, QString("SWGDABDemodSettings"));
}
if((doa2_settings != nullptr) && (doa2_settings->isSet())){
toJsonValue(QString("DOA2Settings"), doa2_settings, obj, QString("SWGDOA2Settings"));
}
if((dsd_demod_settings != nullptr) && (dsd_demod_settings->isSet())){
toJsonValue(QString("DSDDemodSettings"), dsd_demod_settings, obj, QString("SWGDSDDemodSettings"));
}
@ -855,6 +867,16 @@ SWGChannelSettings::setDabDemodSettings(SWGDABDemodSettings* dab_demod_settings)
this->m_dab_demod_settings_isSet = true;
}
SWGDOA2Settings*
SWGChannelSettings::getDoa2Settings() {
return doa2_settings;
}
void
SWGChannelSettings::setDoa2Settings(SWGDOA2Settings* doa2_settings) {
this->doa2_settings = doa2_settings;
this->m_doa2_settings_isSet = true;
}
SWGDSDDemodSettings*
SWGChannelSettings::getDsdDemodSettings() {
return dsd_demod_settings;
@ -1210,6 +1232,9 @@ SWGChannelSettings::isSet(){
if(dab_demod_settings && dab_demod_settings->isSet()){
isObjectUpdated = true; break;
}
if(doa2_settings && doa2_settings->isSet()){
isObjectUpdated = true; break;
}
if(dsd_demod_settings && dsd_demod_settings->isSet()){
isObjectUpdated = true; break;
}

Wyświetl plik

@ -38,6 +38,7 @@
#include "SWGDABDemodSettings.h"
#include "SWGDATVDemodSettings.h"
#include "SWGDATVModSettings.h"
#include "SWGDOA2Settings.h"
#include "SWGDSDDemodSettings.h"
#include "SWGFileSinkSettings.h"
#include "SWGFileSourceSettings.h"
@ -147,6 +148,9 @@ public:
SWGDABDemodSettings* getDabDemodSettings();
void setDabDemodSettings(SWGDABDemodSettings* dab_demod_settings);
SWGDOA2Settings* getDoa2Settings();
void setDoa2Settings(SWGDOA2Settings* doa2_settings);
SWGDSDDemodSettings* getDsdDemodSettings();
void setDsdDemodSettings(SWGDSDDemodSettings* dsd_demod_settings);
@ -298,6 +302,9 @@ private:
SWGDABDemodSettings* dab_demod_settings;
bool m_dab_demod_settings_isSet;
SWGDOA2Settings* doa2_settings;
bool m_doa2_settings_isSet;
SWGDSDDemodSettings* dsd_demod_settings;
bool m_dsd_demod_settings_isSet;

Wyświetl plik

@ -0,0 +1,200 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGDOA2Report.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDOA2Report::SWGDOA2Report(QString* json) {
init();
this->fromJson(*json);
}
SWGDOA2Report::SWGDOA2Report() {
phi = 0;
m_phi_isSet = false;
pos_az = 0;
m_pos_az_isSet = false;
neg_az = 0;
m_neg_az_isSet = false;
fft_size = 0;
m_fft_size_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
SWGDOA2Report::~SWGDOA2Report() {
this->cleanup();
}
void
SWGDOA2Report::init() {
phi = 0;
m_phi_isSet = false;
pos_az = 0;
m_pos_az_isSet = false;
neg_az = 0;
m_neg_az_isSet = false;
fft_size = 0;
m_fft_size_isSet = false;
channel_sample_rate = 0;
m_channel_sample_rate_isSet = false;
}
void
SWGDOA2Report::cleanup() {
}
SWGDOA2Report*
SWGDOA2Report::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDOA2Report::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&phi, pJson["phi"], "qint32", "");
::SWGSDRangel::setValue(&pos_az, pJson["posAz"], "qint32", "");
::SWGSDRangel::setValue(&neg_az, pJson["negAz"], "qint32", "");
::SWGSDRangel::setValue(&fft_size, pJson["fftSize"], "qint32", "");
::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", "");
}
QString
SWGDOA2Report::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDOA2Report::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_phi_isSet){
obj->insert("phi", QJsonValue(phi));
}
if(m_pos_az_isSet){
obj->insert("posAz", QJsonValue(pos_az));
}
if(m_neg_az_isSet){
obj->insert("negAz", QJsonValue(neg_az));
}
if(m_fft_size_isSet){
obj->insert("fftSize", QJsonValue(fft_size));
}
if(m_channel_sample_rate_isSet){
obj->insert("channelSampleRate", QJsonValue(channel_sample_rate));
}
return obj;
}
qint32
SWGDOA2Report::getPhi() {
return phi;
}
void
SWGDOA2Report::setPhi(qint32 phi) {
this->phi = phi;
this->m_phi_isSet = true;
}
qint32
SWGDOA2Report::getPosAz() {
return pos_az;
}
void
SWGDOA2Report::setPosAz(qint32 pos_az) {
this->pos_az = pos_az;
this->m_pos_az_isSet = true;
}
qint32
SWGDOA2Report::getNegAz() {
return neg_az;
}
void
SWGDOA2Report::setNegAz(qint32 neg_az) {
this->neg_az = neg_az;
this->m_neg_az_isSet = true;
}
qint32
SWGDOA2Report::getFftSize() {
return fft_size;
}
void
SWGDOA2Report::setFftSize(qint32 fft_size) {
this->fft_size = fft_size;
this->m_fft_size_isSet = true;
}
qint32
SWGDOA2Report::getChannelSampleRate() {
return channel_sample_rate;
}
void
SWGDOA2Report::setChannelSampleRate(qint32 channel_sample_rate) {
this->channel_sample_rate = channel_sample_rate;
this->m_channel_sample_rate_isSet = true;
}
bool
SWGDOA2Report::isSet(){
bool isObjectUpdated = false;
do{
if(m_phi_isSet){
isObjectUpdated = true; break;
}
if(m_pos_az_isSet){
isObjectUpdated = true; break;
}
if(m_neg_az_isSet){
isObjectUpdated = true; break;
}
if(m_fft_size_isSet){
isObjectUpdated = true; break;
}
if(m_channel_sample_rate_isSet){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

Wyświetl plik

@ -0,0 +1,82 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGDOA2Report.h
*
* DOA2
*/
#ifndef SWGDOA2Report_H_
#define SWGDOA2Report_H_
#include <QJsonObject>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDOA2Report: public SWGObject {
public:
SWGDOA2Report();
SWGDOA2Report(QString* json);
virtual ~SWGDOA2Report();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDOA2Report* fromJson(QString &jsonString) override;
qint32 getPhi();
void setPhi(qint32 phi);
qint32 getPosAz();
void setPosAz(qint32 pos_az);
qint32 getNegAz();
void setNegAz(qint32 neg_az);
qint32 getFftSize();
void setFftSize(qint32 fft_size);
qint32 getChannelSampleRate();
void setChannelSampleRate(qint32 channel_sample_rate);
virtual bool isSet() override;
private:
qint32 phi;
bool m_phi_isSet;
qint32 pos_az;
bool m_pos_az_isSet;
qint32 neg_az;
bool m_neg_az_isSet;
qint32 fft_size;
bool m_fft_size_isSet;
qint32 channel_sample_rate;
bool m_channel_sample_rate_isSet;
};
}
#endif /* SWGDOA2Report_H_ */

Wyświetl plik

@ -0,0 +1,509 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
#include "SWGDOA2Settings.h"
#include "SWGHelpers.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QObject>
#include <QDebug>
namespace SWGSDRangel {
SWGDOA2Settings::SWGDOA2Settings(QString* json) {
init();
this->fromJson(*json);
}
SWGDOA2Settings::SWGDOA2Settings() {
correlation_type = 0;
m_correlation_type_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = nullptr;
m_title_isSet = false;
log2_decim = 0;
m_log2_decim_isSet = false;
filter_chain_hash = 0;
m_filter_chain_hash_isSet = false;
phase = 0;
m_phase_isSet = false;
antenna_az = 0;
m_antenna_az_isSet = false;
baseband_distance = 0;
m_baseband_distance_isSet = false;
squelchd_b = 0;
m_squelchd_b_isSet = false;
fft_averaging_value = 0;
m_fft_averaging_value_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = nullptr;
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
scope_config = nullptr;
m_scope_config_isSet = false;
channel_marker = nullptr;
m_channel_marker_isSet = false;
rollup_state = nullptr;
m_rollup_state_isSet = false;
}
SWGDOA2Settings::~SWGDOA2Settings() {
this->cleanup();
}
void
SWGDOA2Settings::init() {
correlation_type = 0;
m_correlation_type_isSet = false;
rgb_color = 0;
m_rgb_color_isSet = false;
title = new QString("");
m_title_isSet = false;
log2_decim = 0;
m_log2_decim_isSet = false;
filter_chain_hash = 0;
m_filter_chain_hash_isSet = false;
phase = 0;
m_phase_isSet = false;
antenna_az = 0;
m_antenna_az_isSet = false;
baseband_distance = 0;
m_baseband_distance_isSet = false;
squelchd_b = 0;
m_squelchd_b_isSet = false;
fft_averaging_value = 0;
m_fft_averaging_value_isSet = false;
use_reverse_api = 0;
m_use_reverse_api_isSet = false;
reverse_api_address = new QString("");
m_reverse_api_address_isSet = false;
reverse_api_port = 0;
m_reverse_api_port_isSet = false;
reverse_api_device_index = 0;
m_reverse_api_device_index_isSet = false;
reverse_api_channel_index = 0;
m_reverse_api_channel_index_isSet = false;
scope_config = new SWGGLScope();
m_scope_config_isSet = false;
channel_marker = new SWGChannelMarker();
m_channel_marker_isSet = false;
rollup_state = new SWGRollupState();
m_rollup_state_isSet = false;
}
void
SWGDOA2Settings::cleanup() {
if(title != nullptr) {
delete title;
}
if(reverse_api_address != nullptr) {
delete reverse_api_address;
}
if(scope_config != nullptr) {
delete scope_config;
}
if(channel_marker != nullptr) {
delete channel_marker;
}
if(rollup_state != nullptr) {
delete rollup_state;
}
}
SWGDOA2Settings*
SWGDOA2Settings::fromJson(QString &json) {
QByteArray array (json.toStdString().c_str());
QJsonDocument doc = QJsonDocument::fromJson(array);
QJsonObject jsonObject = doc.object();
this->fromJsonObject(jsonObject);
return this;
}
void
SWGDOA2Settings::fromJsonObject(QJsonObject &pJson) {
::SWGSDRangel::setValue(&correlation_type, pJson["correlationType"], "qint32", "");
::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", "");
::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString");
::SWGSDRangel::setValue(&log2_decim, pJson["log2Decim"], "qint32", "");
::SWGSDRangel::setValue(&filter_chain_hash, pJson["filterChainHash"], "qint32", "");
::SWGSDRangel::setValue(&phase, pJson["phase"], "qint32", "");
::SWGSDRangel::setValue(&antenna_az, pJson["antennaAz"], "qint32", "");
::SWGSDRangel::setValue(&baseband_distance, pJson["basebandDistance"], "qint32", "");
::SWGSDRangel::setValue(&squelchd_b, pJson["squelchdB"], "qint32", "");
::SWGSDRangel::setValue(&fft_averaging_value, pJson["fftAveragingValue"], "qint32", "");
::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString");
::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", "");
::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", "");
::SWGSDRangel::setValue(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope");
::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker");
::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState");
}
QString
SWGDOA2Settings::asJson ()
{
QJsonObject* obj = this->asJsonObject();
QJsonDocument doc(*obj);
QByteArray bytes = doc.toJson();
delete obj;
return QString(bytes);
}
QJsonObject*
SWGDOA2Settings::asJsonObject() {
QJsonObject* obj = new QJsonObject();
if(m_correlation_type_isSet){
obj->insert("correlationType", QJsonValue(correlation_type));
}
if(m_rgb_color_isSet){
obj->insert("rgbColor", QJsonValue(rgb_color));
}
if(title != nullptr && *title != QString("")){
toJsonValue(QString("title"), title, obj, QString("QString"));
}
if(m_log2_decim_isSet){
obj->insert("log2Decim", QJsonValue(log2_decim));
}
if(m_filter_chain_hash_isSet){
obj->insert("filterChainHash", QJsonValue(filter_chain_hash));
}
if(m_phase_isSet){
obj->insert("phase", QJsonValue(phase));
}
if(m_antenna_az_isSet){
obj->insert("antennaAz", QJsonValue(antenna_az));
}
if(m_baseband_distance_isSet){
obj->insert("basebandDistance", QJsonValue(baseband_distance));
}
if(m_squelchd_b_isSet){
obj->insert("squelchdB", QJsonValue(squelchd_b));
}
if(m_fft_averaging_value_isSet){
obj->insert("fftAveragingValue", QJsonValue(fft_averaging_value));
}
if(m_use_reverse_api_isSet){
obj->insert("useReverseAPI", QJsonValue(use_reverse_api));
}
if(reverse_api_address != nullptr && *reverse_api_address != QString("")){
toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString"));
}
if(m_reverse_api_port_isSet){
obj->insert("reverseAPIPort", QJsonValue(reverse_api_port));
}
if(m_reverse_api_device_index_isSet){
obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index));
}
if(m_reverse_api_channel_index_isSet){
obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index));
}
if((scope_config != nullptr) && (scope_config->isSet())){
toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope"));
}
if((channel_marker != nullptr) && (channel_marker->isSet())){
toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker"));
}
if((rollup_state != nullptr) && (rollup_state->isSet())){
toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState"));
}
return obj;
}
qint32
SWGDOA2Settings::getCorrelationType() {
return correlation_type;
}
void
SWGDOA2Settings::setCorrelationType(qint32 correlation_type) {
this->correlation_type = correlation_type;
this->m_correlation_type_isSet = true;
}
qint32
SWGDOA2Settings::getRgbColor() {
return rgb_color;
}
void
SWGDOA2Settings::setRgbColor(qint32 rgb_color) {
this->rgb_color = rgb_color;
this->m_rgb_color_isSet = true;
}
QString*
SWGDOA2Settings::getTitle() {
return title;
}
void
SWGDOA2Settings::setTitle(QString* title) {
this->title = title;
this->m_title_isSet = true;
}
qint32
SWGDOA2Settings::getLog2Decim() {
return log2_decim;
}
void
SWGDOA2Settings::setLog2Decim(qint32 log2_decim) {
this->log2_decim = log2_decim;
this->m_log2_decim_isSet = true;
}
qint32
SWGDOA2Settings::getFilterChainHash() {
return filter_chain_hash;
}
void
SWGDOA2Settings::setFilterChainHash(qint32 filter_chain_hash) {
this->filter_chain_hash = filter_chain_hash;
this->m_filter_chain_hash_isSet = true;
}
qint32
SWGDOA2Settings::getPhase() {
return phase;
}
void
SWGDOA2Settings::setPhase(qint32 phase) {
this->phase = phase;
this->m_phase_isSet = true;
}
qint32
SWGDOA2Settings::getAntennaAz() {
return antenna_az;
}
void
SWGDOA2Settings::setAntennaAz(qint32 antenna_az) {
this->antenna_az = antenna_az;
this->m_antenna_az_isSet = true;
}
qint32
SWGDOA2Settings::getBasebandDistance() {
return baseband_distance;
}
void
SWGDOA2Settings::setBasebandDistance(qint32 baseband_distance) {
this->baseband_distance = baseband_distance;
this->m_baseband_distance_isSet = true;
}
qint32
SWGDOA2Settings::getSquelchdB() {
return squelchd_b;
}
void
SWGDOA2Settings::setSquelchdB(qint32 squelchd_b) {
this->squelchd_b = squelchd_b;
this->m_squelchd_b_isSet = true;
}
qint32
SWGDOA2Settings::getFftAveragingValue() {
return fft_averaging_value;
}
void
SWGDOA2Settings::setFftAveragingValue(qint32 fft_averaging_value) {
this->fft_averaging_value = fft_averaging_value;
this->m_fft_averaging_value_isSet = true;
}
qint32
SWGDOA2Settings::getUseReverseApi() {
return use_reverse_api;
}
void
SWGDOA2Settings::setUseReverseApi(qint32 use_reverse_api) {
this->use_reverse_api = use_reverse_api;
this->m_use_reverse_api_isSet = true;
}
QString*
SWGDOA2Settings::getReverseApiAddress() {
return reverse_api_address;
}
void
SWGDOA2Settings::setReverseApiAddress(QString* reverse_api_address) {
this->reverse_api_address = reverse_api_address;
this->m_reverse_api_address_isSet = true;
}
qint32
SWGDOA2Settings::getReverseApiPort() {
return reverse_api_port;
}
void
SWGDOA2Settings::setReverseApiPort(qint32 reverse_api_port) {
this->reverse_api_port = reverse_api_port;
this->m_reverse_api_port_isSet = true;
}
qint32
SWGDOA2Settings::getReverseApiDeviceIndex() {
return reverse_api_device_index;
}
void
SWGDOA2Settings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) {
this->reverse_api_device_index = reverse_api_device_index;
this->m_reverse_api_device_index_isSet = true;
}
qint32
SWGDOA2Settings::getReverseApiChannelIndex() {
return reverse_api_channel_index;
}
void
SWGDOA2Settings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) {
this->reverse_api_channel_index = reverse_api_channel_index;
this->m_reverse_api_channel_index_isSet = true;
}
SWGGLScope*
SWGDOA2Settings::getScopeConfig() {
return scope_config;
}
void
SWGDOA2Settings::setScopeConfig(SWGGLScope* scope_config) {
this->scope_config = scope_config;
this->m_scope_config_isSet = true;
}
SWGChannelMarker*
SWGDOA2Settings::getChannelMarker() {
return channel_marker;
}
void
SWGDOA2Settings::setChannelMarker(SWGChannelMarker* channel_marker) {
this->channel_marker = channel_marker;
this->m_channel_marker_isSet = true;
}
SWGRollupState*
SWGDOA2Settings::getRollupState() {
return rollup_state;
}
void
SWGDOA2Settings::setRollupState(SWGRollupState* rollup_state) {
this->rollup_state = rollup_state;
this->m_rollup_state_isSet = true;
}
bool
SWGDOA2Settings::isSet(){
bool isObjectUpdated = false;
do{
if(m_correlation_type_isSet){
isObjectUpdated = true; break;
}
if(m_rgb_color_isSet){
isObjectUpdated = true; break;
}
if(title && *title != QString("")){
isObjectUpdated = true; break;
}
if(m_log2_decim_isSet){
isObjectUpdated = true; break;
}
if(m_filter_chain_hash_isSet){
isObjectUpdated = true; break;
}
if(m_phase_isSet){
isObjectUpdated = true; break;
}
if(m_antenna_az_isSet){
isObjectUpdated = true; break;
}
if(m_baseband_distance_isSet){
isObjectUpdated = true; break;
}
if(m_squelchd_b_isSet){
isObjectUpdated = true; break;
}
if(m_fft_averaging_value_isSet){
isObjectUpdated = true; break;
}
if(m_use_reverse_api_isSet){
isObjectUpdated = true; break;
}
if(reverse_api_address && *reverse_api_address != QString("")){
isObjectUpdated = true; break;
}
if(m_reverse_api_port_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_device_index_isSet){
isObjectUpdated = true; break;
}
if(m_reverse_api_channel_index_isSet){
isObjectUpdated = true; break;
}
if(scope_config && scope_config->isSet()){
isObjectUpdated = true; break;
}
if(channel_marker && channel_marker->isSet()){
isObjectUpdated = true; break;
}
if(rollup_state && rollup_state->isSet()){
isObjectUpdated = true; break;
}
}while(false);
return isObjectUpdated;
}
}

Wyświetl plik

@ -0,0 +1,164 @@
/**
* SDRangel
* This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time ---
*
* OpenAPI spec version: 7.0.0
* Contact: f4exb06@gmail.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/*
* SWGDOA2Settings.h
*
* DOA2
*/
#ifndef SWGDOA2Settings_H_
#define SWGDOA2Settings_H_
#include <QJsonObject>
#include "SWGChannelMarker.h"
#include "SWGGLScope.h"
#include "SWGRollupState.h"
#include <QString>
#include "SWGObject.h"
#include "export.h"
namespace SWGSDRangel {
class SWG_API SWGDOA2Settings: public SWGObject {
public:
SWGDOA2Settings();
SWGDOA2Settings(QString* json);
virtual ~SWGDOA2Settings();
void init();
void cleanup();
virtual QString asJson () override;
virtual QJsonObject* asJsonObject() override;
virtual void fromJsonObject(QJsonObject &json) override;
virtual SWGDOA2Settings* fromJson(QString &jsonString) override;
qint32 getCorrelationType();
void setCorrelationType(qint32 correlation_type);
qint32 getRgbColor();
void setRgbColor(qint32 rgb_color);
QString* getTitle();
void setTitle(QString* title);
qint32 getLog2Decim();
void setLog2Decim(qint32 log2_decim);
qint32 getFilterChainHash();
void setFilterChainHash(qint32 filter_chain_hash);
qint32 getPhase();
void setPhase(qint32 phase);
qint32 getAntennaAz();
void setAntennaAz(qint32 antenna_az);
qint32 getBasebandDistance();
void setBasebandDistance(qint32 baseband_distance);
qint32 getSquelchdB();
void setSquelchdB(qint32 squelchd_b);
qint32 getFftAveragingValue();
void setFftAveragingValue(qint32 fft_averaging_value);
qint32 getUseReverseApi();
void setUseReverseApi(qint32 use_reverse_api);
QString* getReverseApiAddress();
void setReverseApiAddress(QString* reverse_api_address);
qint32 getReverseApiPort();
void setReverseApiPort(qint32 reverse_api_port);
qint32 getReverseApiDeviceIndex();
void setReverseApiDeviceIndex(qint32 reverse_api_device_index);
qint32 getReverseApiChannelIndex();
void setReverseApiChannelIndex(qint32 reverse_api_channel_index);
SWGGLScope* getScopeConfig();
void setScopeConfig(SWGGLScope* scope_config);
SWGChannelMarker* getChannelMarker();
void setChannelMarker(SWGChannelMarker* channel_marker);
SWGRollupState* getRollupState();
void setRollupState(SWGRollupState* rollup_state);
virtual bool isSet() override;
private:
qint32 correlation_type;
bool m_correlation_type_isSet;
qint32 rgb_color;
bool m_rgb_color_isSet;
QString* title;
bool m_title_isSet;
qint32 log2_decim;
bool m_log2_decim_isSet;
qint32 filter_chain_hash;
bool m_filter_chain_hash_isSet;
qint32 phase;
bool m_phase_isSet;
qint32 antenna_az;
bool m_antenna_az_isSet;
qint32 baseband_distance;
bool m_baseband_distance_isSet;
qint32 squelchd_b;
bool m_squelchd_b_isSet;
qint32 fft_averaging_value;
bool m_fft_averaging_value_isSet;
qint32 use_reverse_api;
bool m_use_reverse_api_isSet;
QString* reverse_api_address;
bool m_reverse_api_address_isSet;
qint32 reverse_api_port;
bool m_reverse_api_port_isSet;
qint32 reverse_api_device_index;
bool m_reverse_api_device_index_isSet;
qint32 reverse_api_channel_index;
bool m_reverse_api_channel_index_isSet;
SWGGLScope* scope_config;
bool m_scope_config_isSet;
SWGChannelMarker* channel_marker;
bool m_channel_marker_isSet;
SWGRollupState* rollup_state;
bool m_rollup_state_isSet;
};
}
#endif /* SWGDOA2Settings_H_ */

Wyświetl plik

@ -95,6 +95,8 @@
#include "SWGDATVDemodSettings.h"
#include "SWGDATVModReport.h"
#include "SWGDATVModSettings.h"
#include "SWGDOA2Report.h"
#include "SWGDOA2Settings.h"
#include "SWGDSDDemodReport.h"
#include "SWGDSDDemodSettings.h"
#include "SWGDVSerialDevice.h"
@ -738,6 +740,16 @@ namespace SWGSDRangel {
obj->init();
return obj;
}
if(QString("SWGDOA2Report").compare(type) == 0) {
SWGDOA2Report *obj = new SWGDOA2Report();
obj->init();
return obj;
}
if(QString("SWGDOA2Settings").compare(type) == 0) {
SWGDOA2Settings *obj = new SWGDOA2Settings();
obj->init();
return obj;
}
if(QString("SWGDSDDemodReport").compare(type) == 0) {
SWGDSDDemodReport *obj = new SWGDSDDemodReport();
obj->init();