From 931a63dc8b72d0fd065c333d6bebdf944ebc9bbc Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Wed, 13 Jan 2021 19:44:53 +0000 Subject: [PATCH] GS-232 Controller updates. Update GS-232 controller to use message pipes for the target. Add field displaying the name of the target. Add azimuth and elevation offsets. Report worker thread errors to the GUI. --- doc/img/GS232Controller_plugin.png | Bin 16466 -> 16788 bytes doc/img/GS232Controller_plugin.xcf | Bin 56937 -> 74255 bytes .../gs232controller/gs232controller.cpp | 144 ++++++++---- .../feature/gs232controller/gs232controller.h | 27 ++- .../gs232controller/gs232controllergui.cpp | 215 ++++++------------ .../gs232controller/gs232controllergui.h | 11 +- .../gs232controller/gs232controllergui.ui | 187 ++++++++------- .../gs232controller/gs232controllerreport.h | 12 +- .../gs232controllersettings.cpp | 25 +- .../gs232controller/gs232controllersettings.h | 28 +-- .../gs232controller/gs232controllerworker.cpp | 113 ++++----- .../gs232controller/gs232controllerworker.h | 9 +- plugins/feature/gs232controller/readme.md | 49 ++-- 13 files changed, 417 insertions(+), 403 deletions(-) diff --git a/doc/img/GS232Controller_plugin.png b/doc/img/GS232Controller_plugin.png index a67064ab4a3f619af26cd0374ae1826e8c4fdae1..9dcef6887ea036de9d829106f5d24722bf1173c6 100644 GIT binary patch literal 16788 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 literal 16466 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 literal 56937 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 Vj`C5w8T1s-XzJ}7c>3j`{|D4ajlKW? diff --git a/plugins/feature/gs232controller/gs232controller.cpp b/plugins/feature/gs232controller/gs232controller.cpp index 6efc567f2..57e182e81 100644 --- a/plugins/feature/gs232controller/gs232controller.cpp +++ b/plugins/feature/gs232controller/gs232controller.cpp @@ -20,33 +20,42 @@ #include #include #include +#include #include "SWGFeatureSettings.h" #include "SWGFeatureReport.h" #include "SWGFeatureActions.h" -#include "SWGSimplePTTReport.h" #include "SWGDeviceState.h" +#include "SWGTargetAzimuthElevation.h" #include "dsp/dspengine.h" +#include "device/deviceset.h" +#include "channel/channelapi.h" +#include "feature/featureset.h" +#include "maincore.h" -#include "gs232controllerworker.h" #include "gs232controller.h" +#include "gs232controllerworker.h" +#include "gs232controllerreport.h" MESSAGE_CLASS_DEFINITION(GS232Controller::MsgConfigureGS232Controller, Message) MESSAGE_CLASS_DEFINITION(GS232Controller::MsgStartStop, Message) +MESSAGE_CLASS_DEFINITION(GS232Controller::MsgReportWorker, Message) const char* const GS232Controller::m_featureIdURI = "sdrangel.feature.gs232controller"; const char* const GS232Controller::m_featureId = "GS232Controller"; GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface) : - Feature(m_featureIdURI, webAPIAdapterInterface), - m_ptt(false) + Feature(m_featureIdURI, webAPIAdapterInterface) { qDebug("GS232Controller::GS232Controller: webAPIAdapterInterface: %p", webAPIAdapterInterface); setObjectName(m_featureId); - m_worker = new GS232ControllerWorker(webAPIAdapterInterface); + m_worker = new GS232ControllerWorker(); m_state = StIdle; m_errorMessage = "GS232Controller error"; + m_selectedPipe = nullptr; + connect(&m_updatePipesTimer, SIGNAL(timeout()), this, SLOT(updatePipes())); + m_updatePipesTimer.start(1000); } GS232Controller::~GS232Controller() @@ -105,21 +114,46 @@ bool GS232Controller::handleMessage(const Message& cmd) return true; } - else if (GS232ControllerSettings::MsgChannelIndexChange::match(cmd)) + else if (MsgReportWorker::match(cmd)) { - GS232ControllerSettings::MsgChannelIndexChange& cfg = (GS232ControllerSettings::MsgChannelIndexChange&) cmd; - int newChannelIndex = cfg.getIndex(); - qDebug() << "GS232Controller::handleMessage: MsgChannelIndexChange: " << newChannelIndex; - GS232ControllerSettings settings = m_settings; - settings.m_channelIndex = newChannelIndex; - applySettings(settings, false); - - if (getMessageQueueToGUI()) + MsgReportWorker& report = (MsgReportWorker&) cmd; + if (report.getMessage() == "Connected") + m_state = StRunning; + else if (report.getMessage() == "Disconnected") + m_state = StIdle; + else { - GS232ControllerSettings::MsgChannelIndexChange *msg = new GS232ControllerSettings::MsgChannelIndexChange(cfg); - getMessageQueueToGUI()->push(msg); + m_state = StError; + m_errorMessage = report.getMessage(); + } + return true; + } + else if (MainCore::MsgTargetAzimuthElevation::match(cmd)) + { + // New target from another plugin + if ((m_state == StRunning) && m_settings.m_track) + { + MainCore::MsgTargetAzimuthElevation& msg = (MainCore::MsgTargetAzimuthElevation&) cmd; + // Is it from the selected pipe? + if (msg.getPipeSource() == m_selectedPipe) + { + if (getMessageQueueToGUI()) + { + // Forward to GUI - which will then send us updated settings + getMessageQueueToGUI()->push(new MainCore::MsgTargetAzimuthElevation(msg)); + } + else + { + // No GUI, so save target - applySettings will propagate to worker + SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = msg.getSWGTargetAzimuthElevation(); + m_settings.m_azimuth = swgTarget->getAzimuth(); + m_settings.m_elevation = swgTarget->getElevation(); + applySettings(m_settings); + } + } + else + qDebug() << "GS232Controller::handleMessage: No match " << msg.getPipeSource() << " " << m_selectedPipe; } - return true; } else @@ -128,6 +162,23 @@ bool GS232Controller::handleMessage(const Message& cmd) } } +void GS232Controller::updatePipes() +{ + QList availablePipes = updateAvailablePipeSources("target", GS232ControllerSettings::m_pipeTypes, GS232ControllerSettings::m_pipeURIs, this); + + if (availablePipes != m_availablePipes) + { + m_availablePipes = availablePipes; + if (getMessageQueueToGUI()) + { + MsgReportPipes *msgToGUI = MsgReportPipes::create(); + QList& msgAvailablePipes = msgToGUI->getAvailablePipes(); + msgAvailablePipes.append(availablePipes); + getMessageQueueToGUI()->push(msgToGUI); + } + } +} + QByteArray GS232Controller::serialize() const { return m_settings.serialize(); @@ -158,8 +209,7 @@ void GS232Controller::applySettings(const GS232ControllerSettings& settings, boo << " m_serialPort: " << settings.m_serialPort << " m_baudRate: " << settings.m_baudRate << " m_track: " << settings.m_track - << " m_deviceIndex: " << settings.m_deviceIndex - << " m_channelIndex: " << settings.m_channelIndex + << " m_target: " << settings.m_target << " m_title: " << settings.m_title << " m_rgbColor: " << settings.m_rgbColor << " m_useReverseAPI: " << settings.m_useReverseAPI @@ -186,11 +236,24 @@ void GS232Controller::applySettings(const GS232ControllerSettings& settings, boo if ((m_settings.m_track != settings.m_track) || force) { reverseAPIKeys.append("track"); } - if ((m_settings.m_deviceIndex != settings.m_deviceIndex) || force) { - reverseAPIKeys.append("deviceIndex"); + if ((m_settings.m_target != settings.m_target) + || (!settings.m_target.isEmpty() && (m_selectedPipe == nullptr)) // Change in available pipes + || force) + { + if (!settings.m_target.isEmpty()) + { + m_selectedPipe = getPipeEndPoint(settings.m_target, m_availablePipes); + if (m_selectedPipe == nullptr) + qDebug() << "GS232Controller::applySettings: No plugin corresponding to target " << settings.m_target; + } + + reverseAPIKeys.append("target"); } - if ((m_settings.m_channelIndex != settings.m_channelIndex) || force) { - reverseAPIKeys.append("channelIndex"); + if ((m_settings.m_azimuthOffset != settings.m_azimuthOffset) || force) { + reverseAPIKeys.append("azimuthOffset"); + } + if ((m_settings.m_elevationOffset != settings.m_elevationOffset) || force) { + reverseAPIKeys.append("elevationOffset"); } if ((m_settings.m_title != settings.m_title) || force) { reverseAPIKeys.append("title"); @@ -252,7 +315,6 @@ int GS232Controller::webapiSettingsPutPatch( MsgConfigureGS232Controller *msg = MsgConfigureGS232Controller::create(settings, force); m_inputMessageQueue.push(msg); - qDebug("GS232Controller::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); if (m_guiMessageQueue) // forward to GUI if any { MsgConfigureGS232Controller *msgToGUI = MsgConfigureGS232Controller::create(settings, force); @@ -273,8 +335,8 @@ void GS232Controller::webapiFormatFeatureSettings( response.getGs232ControllerSettings()->setSerialPort(new QString(settings.m_serialPort)); response.getGs232ControllerSettings()->setBaudRate(settings.m_baudRate); response.getGs232ControllerSettings()->setTrack(settings.m_track); - response.getGs232ControllerSettings()->setDeviceIndex(settings.m_deviceIndex); - response.getGs232ControllerSettings()->setChannelIndex(settings.m_channelIndex); + response.getGs232ControllerSettings()->setAzimuthOffset(settings.m_azimuthOffset); + response.getGs232ControllerSettings()->setElevationOffset(settings.m_elevationOffset); if (response.getGs232ControllerSettings()->getTitle()) { *response.getGs232ControllerSettings()->getTitle() = settings.m_title; @@ -292,8 +354,6 @@ void GS232Controller::webapiFormatFeatureSettings( } response.getGs232ControllerSettings()->setReverseApiPort(settings.m_reverseAPIPort); - response.getGs232ControllerSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIFeatureSetIndex); - response.getGs232ControllerSettings()->setReverseApiChannelIndex(settings.m_reverseAPIFeatureIndex); } void GS232Controller::webapiUpdateFeatureSettings( @@ -316,11 +376,14 @@ void GS232Controller::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("track")) { settings.m_track = response.getGs232ControllerSettings()->getTrack() != 0; } - if (featureSettingsKeys.contains("deviceIndex")) { - settings.m_deviceIndex = response.getGs232ControllerSettings()->getDeviceIndex(); + if (featureSettingsKeys.contains("target")) { + settings.m_target = *response.getGs232ControllerSettings()->getTarget(); } - if (featureSettingsKeys.contains("channelIndex")) { - settings.m_channelIndex = response.getGs232ControllerSettings()->getChannelIndex(); + if (featureSettingsKeys.contains("azimuthOffset")) { + settings.m_azimuthOffset = response.getGs232ControllerSettings()->getAzimuthOffset(); + } + if (featureSettingsKeys.contains("elevationOffset")) { + settings.m_elevationOffset = response.getGs232ControllerSettings()->getElevationOffset(); } if (featureSettingsKeys.contains("title")) { settings.m_title = *response.getGs232ControllerSettings()->getTitle(); @@ -337,12 +400,6 @@ void GS232Controller::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("reverseAPIPort")) { settings.m_reverseAPIPort = response.getGs232ControllerSettings()->getReverseApiPort(); } - if (featureSettingsKeys.contains("reverseAPIDeviceIndex")) { - settings.m_reverseAPIFeatureSetIndex = response.getGs232ControllerSettings()->getReverseApiDeviceIndex(); - } - if (featureSettingsKeys.contains("reverseAPIChannelIndex")) { - settings.m_reverseAPIFeatureIndex = response.getGs232ControllerSettings()->getReverseApiChannelIndex(); - } } void GS232Controller::webapiReverseSendSettings(QList& featureSettingsKeys, const GS232ControllerSettings& settings, bool force) @@ -371,11 +428,14 @@ void GS232Controller::webapiReverseSendSettings(QList& featureSettingsK if (featureSettingsKeys.contains("track") || force) { swgGS232ControllerSettings->setTrack(settings.m_track); } - if (featureSettingsKeys.contains("deviceIndex") || force) { - swgGS232ControllerSettings->setDeviceIndex(settings.m_deviceIndex); + if (featureSettingsKeys.contains("target") || force) { + swgGS232ControllerSettings->setTarget(new QString(settings.m_target)); } - if (featureSettingsKeys.contains("channelIndex") || force) { - swgGS232ControllerSettings->setChannelIndex(settings.m_channelIndex); + if (featureSettingsKeys.contains("azimuthOffset") || force) { + swgGS232ControllerSettings->setAzimuthOffset(settings.m_azimuthOffset); + } + if (featureSettingsKeys.contains("elevationOffset") || force) { + swgGS232ControllerSettings->setElevationOffset(settings.m_elevationOffset); } if (featureSettingsKeys.contains("title") || force) { swgGS232ControllerSettings->setTitle(new QString(settings.m_title)); diff --git a/plugins/feature/gs232controller/gs232controller.h b/plugins/feature/gs232controller/gs232controller.h index 124b5942b..3d2c9b811 100644 --- a/plugins/feature/gs232controller/gs232controller.h +++ b/plugins/feature/gs232controller/gs232controller.h @@ -21,6 +21,7 @@ #include #include +#include #include "feature/feature.h" #include "util/message.h" @@ -81,6 +82,25 @@ public: { } }; + class MsgReportWorker : public Message { + MESSAGE_CLASS_DECLARATION + + public: + QString getMessage() { return m_message; } + + static MsgReportWorker* create(QString message) { + return new MsgReportWorker(message); + } + + private: + QString m_message; + + MsgReportWorker(QString message) : + Message(), + m_message(message) + {} + }; + GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface); virtual ~GS232Controller(); virtual void destroy() { delete this; } @@ -122,7 +142,10 @@ private: QThread m_thread; GS232ControllerWorker *m_worker; GS232ControllerSettings m_settings; - bool m_ptt; + //QHash m_availablePipes; + QList m_availablePipes; + PipeEndPoint *m_selectedPipe; + QTimer m_updatePipesTimer; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; @@ -133,7 +156,9 @@ private: void webapiReverseSendSettings(QList& featureSettingsKeys, const GS232ControllerSettings& settings, bool force); private slots: + void updatePipes(); void networkManagerFinished(QNetworkReply *reply); + //void handlePipeMessageQueue(MessageQueue* messageQueue); }; #endif // INCLUDE_FEATURE_GS232CONTROLLER_H_ diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index 6d2e3abad..9ed62c6f0 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -21,6 +21,8 @@ #include #include +#include "SWGTargetAzimuthElevation.h" + #include "feature/featureuiset.h" #include "gui/basicfeaturesettingsdialog.h" #include "mainwindow.h" @@ -51,7 +53,6 @@ void GS232ControllerGUI::resetToDefaults() QByteArray GS232ControllerGUI::serialize() const { - qDebug("GS232ControllerGUI::serialize: %d", m_settings.m_channelIndex); return m_settings.serialize(); } @@ -59,8 +60,6 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data) { if (m_settings.deserialize(data)) { - qDebug("GS232ControllerGUI::deserialize: %d", m_settings.m_channelIndex); - updateDeviceSetList(); displaySettings(); applySettings(true); return true; @@ -85,30 +84,28 @@ bool GS232ControllerGUI::handleMessage(const Message& message) return true; } - else if (GS232ControllerSettings::MsgChannelIndexChange::match(message)) + else if (PipeEndPoint::MsgReportPipes::match(message)) { - const GS232ControllerSettings::MsgChannelIndexChange& cfg = (GS232ControllerSettings::MsgChannelIndexChange&) message; - int newChannelIndex = cfg.getIndex(); - qDebug("GS232ControllerGUI::handleMessage: GS232ControllerSettings::MsgChannelIndexChange: %d", newChannelIndex); - ui->channel->blockSignals(true); - ui->channel->setCurrentIndex(newChannelIndex); - m_settings.m_channelIndex = newChannelIndex; - ui->channel->blockSignals(false); - + PipeEndPoint::MsgReportPipes& report = (PipeEndPoint::MsgReportPipes&) message; + m_availablePipes = report.getAvailablePipes(); + updatePipeList(); return true; - } else if (GS232ControllerReport::MsgReportAzAl::match(message)) + } + else if (GS232ControllerReport::MsgReportAzAl::match(message)) { GS232ControllerReport::MsgReportAzAl& azAl = (GS232ControllerReport::MsgReportAzAl&) message; - if (azAl.getType() == GS232ControllerReport::AzAlType::TARGET) - { - ui->azimuth->setValue(round(azAl.getAzimuth())); - ui->elevation->setValue(round(azAl.getElevation())); - } - else - { - ui->azimuthCurrentText->setText(QString("%1").arg(round(azAl.getAzimuth()))); - ui->elevationCurrentText->setText(QString("%1").arg(round(azAl.getElevation()))); - } + ui->azimuthCurrentText->setText(QString("%1").arg(round(azAl.getAzimuth()))); + ui->elevationCurrentText->setText(QString("%1").arg(round(azAl.getElevation()))); + return true; + } + else if (MainCore::MsgTargetAzimuthElevation::match(message)) + { + MainCore::MsgTargetAzimuthElevation& msg = (MainCore::MsgTargetAzimuthElevation&) message; + SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = msg.getSWGTargetAzimuthElevation(); + + ui->azimuth->setValue(round(swgTarget->getAzimuth())); + ui->elevation->setValue(round(swgTarget->getElevation())); + ui->targetName->setText(*swgTarget->getName()); return true; } @@ -157,7 +154,6 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_statusTimer.start(1000); updateSerialPortList(); - updateDeviceSetList(); displaySettings(); applySettings(true); } @@ -183,6 +179,9 @@ void GS232ControllerGUI::displaySettings() ui->serialPort->lineEdit()->setText(m_settings.m_serialPort); ui->baudRate->setCurrentText(QString("%1").arg(m_settings.m_baudRate)); ui->track->setChecked(m_settings.m_track); + ui->targets->setCurrentIndex(ui->targets->findText(m_settings.m_target)); + ui->azimuthOffset->setValue(m_settings.m_azimuthOffset); + ui->elevationOffset->setValue(m_settings.m_elevationOffset); blockApplySettings(false); } @@ -198,102 +197,34 @@ void GS232ControllerGUI::updateSerialPortList() } } -void GS232ControllerGUI::updateDeviceSetList() +void GS232ControllerGUI::updatePipeList() { - MainWindow *mainWindow = MainWindow::getInstance(); - std::vector& deviceUISets = mainWindow->getDeviceUISets(); - std::vector::const_iterator it = deviceUISets.begin(); + QString currentText = ui->targets->currentText(); + ui->targets->blockSignals(true); + ui->targets->clear(); + QList::const_iterator it = m_availablePipes.begin(); - ui->device->blockSignals(true); - - ui->device->clear(); - unsigned int deviceIndex = 0; - - for (; it != deviceUISets.end(); ++it, deviceIndex++) + for (int i = 0; it != m_availablePipes.end(); ++it, i++) { - DSPDeviceSourceEngine *deviceSourceEngine = (*it)->m_deviceSourceEngine; - - if (deviceSourceEngine) { - ui->device->addItem(QString("R%1").arg(deviceIndex), deviceIndex); - } + ui->targets->addItem(it->getName()); } - int newDeviceIndex; - - if (it != deviceUISets.begin()) + if (currentText.isEmpty()) { - if (m_settings.m_deviceIndex < 0) { - ui->device->setCurrentIndex(0); - } else { - ui->device->setCurrentIndex(m_settings.m_deviceIndex); - } - - newDeviceIndex = ui->device->currentData().toInt(); + if (m_availablePipes.size() > 0) + ui->targets->setCurrentIndex(0); } else + ui->targets->setCurrentIndex(ui->targets->findText(currentText)); + ui->targets->blockSignals(false); + + QString newText = ui->targets->currentText(); + if (currentText != newText) { - newDeviceIndex = -1; + m_settings.m_target = newText; + ui->targetName->setText(""); + applySettings(); } - - - if (newDeviceIndex != m_settings.m_deviceIndex) - { - qDebug("GS232ControllerGUI::updateDeviceSetLists: device index changed: %d", newDeviceIndex); - m_settings.m_deviceIndex = newDeviceIndex; - } - - updateChannelList(); - - ui->device->blockSignals(false); -} - -bool GS232ControllerGUI::updateChannelList() -{ - int newChannelIndex; - ui->channel->blockSignals(true); - ui->channel->clear(); - - if (m_settings.m_deviceIndex < 0) - { - newChannelIndex = -1; - } - else - { - MainWindow *mainWindow = MainWindow::getInstance(); - std::vector& deviceUISets = mainWindow->getDeviceUISets(); - DeviceUISet *deviceUISet = deviceUISets[m_settings.m_deviceIndex]; - int nbChannels = deviceUISet->getNumberOfChannels(); - - for (int ch = 0; ch < nbChannels; ch++) { - ui->channel->addItem(QString("%1").arg(ch), ch); - } - - if (nbChannels > 0) - { - if (m_settings.m_channelIndex < 0) { - ui->channel->setCurrentIndex(0); - } else { - ui->channel->setCurrentIndex(m_settings.m_channelIndex); - } - - newChannelIndex = ui->channel->currentIndex(); - } - else - { - newChannelIndex = -1; - } - } - - ui->channel->blockSignals(false); - - if (newChannelIndex != m_settings.m_channelIndex) - { - qDebug("GS232ControllerGUI::updateChannelList: channel index changed: %d", newChannelIndex); - m_settings.m_channelIndex = newChannelIndex; - return true; - } - - return false; } void GS232ControllerGUI::leaveEvent(QEvent*) @@ -346,32 +277,6 @@ void GS232ControllerGUI::on_startStop_toggled(bool checked) } } -void GS232ControllerGUI::on_devicesRefresh_clicked() -{ - updateDeviceSetList(); - displaySettings(); - applySettings(); -} - -void GS232ControllerGUI::on_device_currentIndexChanged(int index) -{ - if (index >= 0) - { - m_settings.m_deviceIndex = ui->device->currentData().toInt(); - updateChannelList(); - applySettings(); - } -} - -void GS232ControllerGUI::on_channel_currentIndexChanged(int index) -{ - if (index >= 0) - { - m_settings.m_channelIndex = index; - applySettings(); - } -} - void GS232ControllerGUI::on_serialPort_currentIndexChanged(int index) { (void) index; @@ -389,23 +294,43 @@ void GS232ControllerGUI::on_baudRate_currentIndexChanged(int index) void GS232ControllerGUI::on_azimuth_valueChanged(int value) { m_settings.m_azimuth = value; + ui->targetName->setText(""); applySettings(); } void GS232ControllerGUI::on_elevation_valueChanged(int value) { m_settings.m_elevation = value; + ui->targetName->setText(""); + applySettings(); +} + +void GS232ControllerGUI::on_azimuthOffset_valueChanged(int value) +{ + m_settings.m_azimuthOffset = value; + applySettings(); +} + +void GS232ControllerGUI::on_elevationOffset_valueChanged(int value) +{ + m_settings.m_elevationOffset = value; applySettings(); } void GS232ControllerGUI::on_track_stateChanged(int state) { m_settings.m_track = state == Qt::Checked; - ui->devicesRefresh->setEnabled(m_settings.m_track); - ui->deviceLabel->setEnabled(m_settings.m_track); - ui->device->setEnabled(m_settings.m_track); - ui->channelLabel->setEnabled(m_settings.m_track); - ui->channel->setEnabled(m_settings.m_track); + ui->targetsLabel->setEnabled(m_settings.m_track); + ui->targets->setEnabled(m_settings.m_track); + if (!m_settings.m_track) + ui->targetName->setText(""); + applySettings(); +} + +void GS232ControllerGUI::on_targets_currentTextChanged(const QString& text) +{ + m_settings.m_target = text; + ui->targetName->setText(""); applySettings(); } @@ -415,15 +340,23 @@ void GS232ControllerGUI::updateStatus() if (m_lastFeatureState != state) { + // We set checked state of start/stop button, in case it was changed via API + bool oldState; switch (state) { case Feature::StNotStarted: ui->startStop->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); break; case Feature::StIdle: + oldState = ui->startStop->blockSignals(true); + ui->startStop->setChecked(false); + ui->startStop->blockSignals(oldState); ui->startStop->setStyleSheet("QToolButton { background-color : blue; }"); break; case Feature::StRunning: + oldState = ui->startStop->blockSignals(true); + ui->startStop->setChecked(true); + ui->startStop->blockSignals(oldState); ui->startStop->setStyleSheet("QToolButton { background-color : green; }"); break; case Feature::StError: diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index a8a41f62c..b43356a3d 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -23,6 +23,7 @@ #include "feature/featuregui.h" #include "util/messagequeue.h" +#include "pipes/pipeendpoint.h" #include "gs232controllersettings.h" class PluginAPI; @@ -50,6 +51,7 @@ private: FeatureUISet* m_featureUISet; GS232ControllerSettings m_settings; bool m_doApplySettings; + QList m_availablePipes; GS232Controller* m_gs232Controller; MessageQueue m_inputMessageQueue; @@ -62,9 +64,8 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void updatePipeList(); void updateSerialPortList(); - void updateDeviceSetList(); - bool updateChannelList(); //!< true if channel index has changed bool handleMessage(const Message& message); void leaveEvent(QEvent*); @@ -75,14 +76,14 @@ private slots: void onWidgetRolled(QWidget* widget, bool rollDown); void handleInputMessages(); void on_startStop_toggled(bool checked); - void on_devicesRefresh_clicked(); - void on_device_currentIndexChanged(int index); - void on_channel_currentIndexChanged(int index); void on_serialPort_currentIndexChanged(int index); void on_baudRate_currentIndexChanged(int index); void on_track_stateChanged(int state); void on_azimuth_valueChanged(int value); void on_elevation_valueChanged(int value); + void on_targets_currentTextChanged(const QString& text); + void on_azimuthOffset_valueChanged(int value); + void on_elevationOffset_valueChanged(int value); void updateStatus(); }; diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index 5189f070b..71b594c3c 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -7,7 +7,7 @@ 0 0 320 - 187 + 175 @@ -24,7 +24,7 @@ - 320 + 350 16777215 @@ -43,7 +43,7 @@ 10 10 301 - 171 + 161 @@ -179,6 +179,74 @@ + + + + + + Check to enable automatic tracking of azimuth and elevation from the specified channel + + + Track + + + + + + + Source + + + + + + + + 150 + 0 + + + + Target to track + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Target + + + + + + + Name of the target being tracked as indicated by the source channel / feature + + + true + + + + + @@ -271,107 +339,50 @@ - + - - - Check to enable automatic tracking of azimuth and elevation from the specified channel - + - Track + Azimuth offset - - - - 24 - 16777215 - - + - Refresh indexes of available device sets + Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment - - + + -360 - - - :/recycle.png:/recycle.png + + 360 - + - Device + Elevation offset - - - - 55 - 0 - - + - Receiver deviceset index + Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment + + + -180 + + + 180 + + + 1 - - - - Channel - - - - - - - - 55 - 0 - - - - Channel index - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -390,6 +401,18 @@
gui/buttonswitch.h
+ + startStop + azimuth + elevation + track + targets + targetName + serialPort + baudRate + azimuthOffset + elevationOffset + diff --git a/plugins/feature/gs232controller/gs232controllerreport.h b/plugins/feature/gs232controller/gs232controllerreport.h index 8e175d8fc..52699961e 100644 --- a/plugins/feature/gs232controller/gs232controllerreport.h +++ b/plugins/feature/gs232controller/gs232controllerreport.h @@ -27,7 +27,6 @@ class GS232ControllerReport : public QObject { Q_OBJECT public: - enum AzAlType {TARGET, ACTUAL}; class MsgReportAzAl : public Message { MESSAGE_CLASS_DECLARATION @@ -35,23 +34,20 @@ public: public: float getAzimuth() const { return m_azimuth; } float getElevation() const { return m_elevation; } - AzAlType getType() const { return m_type; } - static MsgReportAzAl* create(float azimuth, float elevation, AzAlType type) + static MsgReportAzAl* create(float azimuth, float elevation) { - return new MsgReportAzAl(azimuth, elevation, type); + return new MsgReportAzAl(azimuth, elevation); } private: float m_azimuth; float m_elevation; - AzAlType m_type; - MsgReportAzAl(float azimuth, float elevation, AzAlType type) : + MsgReportAzAl(float azimuth, float elevation) : Message(), m_azimuth(azimuth), - m_elevation(elevation), - m_type(type) + m_elevation(elevation) { } }; diff --git a/plugins/feature/gs232controller/gs232controllersettings.cpp b/plugins/feature/gs232controller/gs232controllersettings.cpp index 66f02acdb..16cdec690 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.cpp +++ b/plugins/feature/gs232controller/gs232controllersettings.cpp @@ -23,7 +23,15 @@ #include "gs232controllersettings.h" -MESSAGE_CLASS_DEFINITION(GS232ControllerSettings::MsgChannelIndexChange, Message) +const QStringList GS232ControllerSettings::m_pipeTypes = { + QStringLiteral("ADSBDemod"), + QStringLiteral("StarTracker") +}; + +const QStringList GS232ControllerSettings::m_pipeURIs = { + QStringLiteral("sdrangel.channel.adsbdemod"), + QStringLiteral("sdrangel.feature.startracker") +}; GS232ControllerSettings::GS232ControllerSettings() { @@ -37,8 +45,7 @@ void GS232ControllerSettings::resetToDefaults() m_serialPort = ""; m_baudRate = 9600; m_track = false; - m_deviceIndex = -1; - m_channelIndex = -1; + m_target = ""; m_title = "GS-232 Rotator Controller"; m_rgbColor = QColor(225, 25, 99).rgb(); m_useReverseAPI = false; @@ -46,6 +53,8 @@ void GS232ControllerSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIFeatureSetIndex = 0; m_reverseAPIFeatureIndex = 0; + m_azimuthOffset = 0; + m_elevationOffset = 0; } QByteArray GS232ControllerSettings::serialize() const @@ -57,8 +66,7 @@ QByteArray GS232ControllerSettings::serialize() const s.writeString(3, m_serialPort); s.writeS32(4, m_baudRate); s.writeBool(5, m_track); - s.writeS32(6, m_deviceIndex); - s.writeS32(7, m_channelIndex); + s.writeString(6, m_target); s.writeString(8, m_title); s.writeU32(9, m_rgbColor); s.writeBool(10, m_useReverseAPI); @@ -66,6 +74,8 @@ QByteArray GS232ControllerSettings::serialize() const s.writeU32(12, m_reverseAPIPort); s.writeU32(13, m_reverseAPIFeatureSetIndex); s.writeU32(14, m_reverseAPIFeatureIndex); + s.writeS32(15, m_azimuthOffset); + s.writeS32(16, m_elevationOffset); return s.final(); } @@ -91,8 +101,7 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) d.readString(3, &m_serialPort, ""); d.readS32(4, &m_baudRate, 9600); d.readBool(5, &m_track, false); - d.readS32(6, &m_deviceIndex, -1); - d.readS32(7, &m_channelIndex, -1); + d.readString(6, &m_target, ""); d.readString(8, &m_title, "GS-232 Rotator Controller"); d.readU32(9, &m_rgbColor, QColor(225, 25, 99).rgb()); d.readBool(10, &m_useReverseAPI, false); @@ -109,6 +118,8 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp; d.readU32(14, &utmp, 0); m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp; + d.readS32(15, &m_azimuthOffset, 0); + d.readS32(16, &m_elevationOffset, 0); return true; } diff --git a/plugins/feature/gs232controller/gs232controllersettings.h b/plugins/feature/gs232controller/gs232controllersettings.h index 01cd25992..6eef7b78f 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.h +++ b/plugins/feature/gs232controller/gs232controllersettings.h @@ -25,35 +25,18 @@ #include "util/message.h" class Serializable; +class PipeEndPoint; struct GS232ControllerSettings { - class MsgChannelIndexChange : public Message { - MESSAGE_CLASS_DECLARATION - - public: - bool getIndex() const { return m_index; } - - static MsgChannelIndexChange* create(int index) { - return new MsgChannelIndexChange(index); - } - - protected: - int m_index; - - MsgChannelIndexChange(int index) : - Message(), - m_index(index) - { } - }; - int m_azimuth; int m_elevation; QString m_serialPort; int m_baudRate; - int m_deviceIndex; bool m_track; - int m_channelIndex; + QString m_target; // Plugin to get az/el from. E.g: "R0:0 ADSBDemod". Use a string, so can be set via WebAPI + int m_azimuthOffset; + int m_elevationOffset; QString m_title; quint32 m_rgbColor; bool m_useReverseAPI; @@ -66,6 +49,9 @@ struct GS232ControllerSettings void resetToDefaults(); QByteArray serialize() const; bool deserialize(const QByteArray& data); + + static const QStringList m_pipeTypes; + static const QStringList m_pipeURIs; }; #endif // INCLUDE_FEATURE_GS232CONTROLLERSETTINGS_H_ diff --git a/plugins/feature/gs232controller/gs232controllerworker.cpp b/plugins/feature/gs232controller/gs232controllerworker.cpp index 59af9333b..25ba64ac0 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.cpp +++ b/plugins/feature/gs232controller/gs232controllerworker.cpp @@ -24,25 +24,14 @@ #include #include -#include "SWGDeviceState.h" -#include "SWGSuccessResponse.h" -#include "SWGErrorResponse.h" -#include "SWGDeviceSettings.h" -#include "SWGChannelSettings.h" -#include "SWGDeviceSet.h" -#include "SWGChannelReport.h" - -#include "webapi/webapiadapterinterface.h" -#include "webapi/webapiutils.h" - +#include "gs232controller.h" #include "gs232controllerworker.h" #include "gs232controllerreport.h" MESSAGE_CLASS_DEFINITION(GS232ControllerWorker::MsgConfigureGS232ControllerWorker, Message) MESSAGE_CLASS_DEFINITION(GS232ControllerReport::MsgReportAzAl, Message) -GS232ControllerWorker::GS232ControllerWorker(WebAPIAdapterInterface *webAPIAdapterInterface) : - m_webAPIAdapterInterface(webAPIAdapterInterface), +GS232ControllerWorker::GS232ControllerWorker() : m_msgQueueToFeature(nullptr), m_msgQueueToGUI(nullptr), m_running(false), @@ -113,10 +102,10 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting qDebug() << "GS232ControllerWorker::applySettings:" << " m_azimuth: " << settings.m_azimuth << " m_elevation: " << settings.m_elevation + << " m_azimuthOffset: " << settings.m_azimuthOffset + << " m_elevationOffset: " << settings.m_elevationOffset << " m_serialPort: " << settings.m_serialPort << " m_baudRate: " << settings.m_baudRate - << " m_deviceIndex: " << settings.m_deviceIndex - << " m_channelIndex: " << settings.m_channelIndex << " force: " << force; if ((settings.m_serialPort != m_settings.m_serialPort) || force) @@ -126,34 +115,57 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting m_serialPort.setPortName(settings.m_serialPort); m_serialPort.setBaudRate(settings.m_baudRate); if (!m_serialPort.open(QIODevice::ReadWrite)) + { qCritical() << "GS232ControllerWorker::applySettings: Failed to open serial port " << settings.m_serialPort << ". Error: " << m_serialPort.error(); + if (m_msgQueueToFeature) + m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Failed to open serial port %1: %2").arg(settings.m_serialPort).arg(m_serialPort.error()))); + } } else if ((settings.m_baudRate != m_settings.m_baudRate) || force) { m_serialPort.setBaudRate(settings.m_baudRate); } - if ((settings.m_elevation != m_settings.m_elevation) || force) + if ((settings.m_elevation != m_settings.m_elevation) + || (settings.m_elevationOffset != m_settings.m_elevationOffset) + || force) { - setAzimuthElevation(settings.m_azimuth, settings.m_elevation); + setAzimuthElevation(settings.m_azimuth, settings.m_elevation, settings.m_azimuthOffset, settings.m_elevationOffset); } - else if ((settings.m_azimuth != m_settings.m_azimuth) || force) + else if ((settings.m_azimuth != m_settings.m_azimuth) + || (settings.m_azimuthOffset != m_settings.m_azimuthOffset) + || force) { - setAzimuth(settings.m_azimuth); + setAzimuth(settings.m_azimuth, settings.m_azimuthOffset); } m_settings = settings; } -void GS232ControllerWorker::setAzimuth(int azimuth) +void GS232ControllerWorker::setAzimuth(int azimuth, int azimuthOffset) { + azimuth += azimuthOffset; + if (azimuth < 0) + azimuth = 0; + else if (azimuth > 450) + azimuth = 450; QString cmd = QString("M%1\r\n").arg(azimuth, 3, 10, QLatin1Char('0')); QByteArray data = cmd.toLatin1(); m_serialPort.write(data); } -void GS232ControllerWorker::setAzimuthElevation(int azimuth, int elevation) +void GS232ControllerWorker::setAzimuthElevation(int azimuth, int elevation, int azimuthOffset, int elevationOffset) { + azimuth += azimuthOffset; + if (azimuth < 0) + azimuth = 0; + else if (azimuth > 450) + azimuth = 450; + elevation += elevationOffset; + if (elevation < 0) + elevation = 0; + else if (elevation > 180) + elevation = 180; QString cmd = QString("W%1 %2\r\n").arg(azimuth, 3, 10, QLatin1Char('0')).arg(elevation, 3, 10, QLatin1Char('0')); QByteArray data = cmd.toLatin1(); m_serialPort.write(data); @@ -170,7 +182,6 @@ void GS232ControllerWorker::readSerialData() if (len != -1) { QString response = QString::fromUtf8(buf, len); - QRegularExpression re("AZ=(\\d\\d\\d)EL=(\\d\\d\\d)"); QRegularExpressionMatch match = re.match(response); if (match.hasMatch()) @@ -179,15 +190,17 @@ void GS232ControllerWorker::readSerialData() QString el = match.captured(2); //qDebug() << "GS232ControllerWorker::readSerialData read az " << az << " el " << el; if (getMessageQueueToGUI()) - { - GS232ControllerReport::MsgReportAzAl *msg = GS232ControllerReport::MsgReportAzAl::create( - az.toFloat(), el.toFloat(), GS232ControllerReport::ACTUAL); - getMessageQueueToGUI()->push(msg); - } + getMessageQueueToGUI()->push( GS232ControllerReport::MsgReportAzAl::create(az.toFloat(), el.toFloat())); + } + else if (response == "\r\n") + { + // Ignore } else { - qDebug() << "GS232ControllerWorker::readSerialData unknown response " << response; + qDebug() << "GS232ControllerWorker::readSerialData - unexpected response " << response; + if (m_msgQueueToFeature) + m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected GS-232 serial reponse: %1").arg(response))); } } } @@ -201,48 +214,4 @@ void GS232ControllerWorker::update() QByteArray cmd("C2\r\n"); m_serialPort.write(cmd); } - - // Request target Az/EL from channel - if (m_settings.m_track) - { - SWGSDRangel::SWGChannelReport response; - SWGSDRangel::SWGErrorResponse errorResponse; - - int httpRC = m_webAPIAdapterInterface->devicesetChannelReportGet( - m_settings.m_deviceIndex, - m_settings.m_channelIndex, - response, - errorResponse - ); - - if (httpRC/100 != 2) - { - qWarning("GS232ControllerWorker::update: get channel report error %d: %s", - httpRC, qPrintable(*errorResponse.getMessage())); - } - else - { - QJsonObject *jsonObj = response.asJsonObject(); - double targetAzimuth; - double targetElevation; - bool gotElevation = false; - bool gotAzimuth = false; - - if (WebAPIUtils::getSubObjectDouble(*jsonObj, "targetAzimuth", targetAzimuth)) - gotAzimuth = true; - - if (WebAPIUtils::getSubObjectDouble(*jsonObj, "targetElevation", targetElevation)) - gotElevation = true; - - if (gotAzimuth && gotElevation) - { - if (getMessageQueueToGUI()) - { - GS232ControllerReport::MsgReportAzAl *msg = GS232ControllerReport::MsgReportAzAl::create( - targetAzimuth, targetElevation, GS232ControllerReport::TARGET); - getMessageQueueToGUI()->push(msg); - } - } - } - } } diff --git a/plugins/feature/gs232controller/gs232controllerworker.h b/plugins/feature/gs232controller/gs232controllerworker.h index 2041e17c8..3be417fe2 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.h +++ b/plugins/feature/gs232controller/gs232controllerworker.h @@ -28,8 +28,6 @@ #include "gs232controllersettings.h" -class WebAPIAdapterInterface; - class GS232ControllerWorker : public QObject { Q_OBJECT @@ -57,7 +55,7 @@ public: { } }; - GS232ControllerWorker(WebAPIAdapterInterface *webAPIAdapterInterface); + GS232ControllerWorker(); ~GS232ControllerWorker(); void reset(); bool startWork(); @@ -69,7 +67,6 @@ public: private: - WebAPIAdapterInterface *m_webAPIAdapterInterface; MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object MessageQueue *m_msgQueueToGUI; @@ -82,8 +79,8 @@ private: bool handleMessage(const Message& cmd); void applySettings(const GS232ControllerSettings& settings, bool force = false); MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; } - void setAzimuth(int azimuth); - void setAzimuthElevation(int azimuth, int elevation); + void setAzimuth(int azimuth, int azimuthOffset); + void setAzimuthElevation(int azimuth, int elevation, int azimuthOffset, int elevationOffset); private slots: void handleInputMessages(); diff --git a/plugins/feature/gs232controller/readme.md b/plugins/feature/gs232controller/readme.md index 3e55f92ea..51bff3925 100644 --- a/plugins/feature/gs232controller/readme.md +++ b/plugins/feature/gs232controller/readme.md @@ -4,11 +4,11 @@ The GS-232 Rotator Controller feature plugin allows SDRangel to send commands to GS-232 rotators. This allows SDRangel to point antennas mounted on a rotator to a specified azimuth and elevation. -Azimuth and elevation can be set manually by a user in the GUI, via the REST API, or via another plugin, such as the ADS-B Demodulator, which can track a selected aircraft. +Azimuth and elevation can be set manually by a user in the GUI, via the REST API, or via another plugin, such as the ADS-B Demodulator, which can track a selected aircraft, or the Star Tracker, for radio astronomy or EME communication.

Interface

-![File source channel plugin GUI](../../../doc/img/GS232Controller_plugin.png) +![GS232 Rotator Controller feature plugin GUI](../../../doc/img/GS232Controller_plugin.png)

1: Start/Stop plugin

@@ -16,37 +16,50 @@ This button starts or stops the plugin. When the plugin is stopped, azimuth and

2: Azimuth

-Specifies the target azimuth (angle in degrees, clockwise from North) to point the antenna towards. Valid values range from 0 to 450 degrees. The value to the right of the target azimuth, is the current azimuth read from the GS-232 rotator. +Specifies the target azimuth (angle in degrees, clockwise from North) to point the antenna towards. Valid values range from 0 to 450 degrees. +The value to the right of the target azimuth, is the current azimuth read from the GS-232 rotator.

3: Elevation

-Specifies the target elevation (angle in degrees) to point the antenna towards. Valid values range from 0 to 180 degrees, where 0 and 180 point towards the horizon and 90 degrees to zenith. The value to the right of the target elevation, is the current elevation read from the GS-232 rotator. +Specifies the target elevation (angle in degrees) to point the antenna towards. Valid values range from 0 to 180 degrees, where 0 and 180 point towards the horizon and 90 degrees to zenith. +The value to the right of the target elevation, is the current elevation read from the GS-232 rotator. -

4: Serial Port

+

4: Track

+ +When checked, the target azimuth and elevation will be controlled by the Channel or Feature Source (5). +For example, this allows an aircraft to be tracked, by setting the Source to the ADS-B Demodulator plugin, or the Moon to be tracked by settng Source to the Star Tracker plugin. + +

5: Source

+ +Specify the SDRangel Channel or Feature that that will control the target aziumth and elevation values, when Track (4) is checked. + +

6: Target

+ +When tracking is enabled, this field will display a name for the target being tracked, as indicated by the selected Source plugin (5). +For example, the ADS-B plugin will display the flight number of the target aircraft. The Star Tracker plugin will display Sun, Moon or Star. + +

7: Serial Port

Specifies the serial port (E.g. COM3 on Windows or /dev/ttyS0 on Linux) that will be used to send commands to the GS-232 rotator. -

5: Baud rate

+

8: Baud rate

Specifies the baud rate that will be used to send commands to the GS-232 rotator. Typically this is 9600. -

6: Track

+

9: Azimuth Offset

-When checked, the GS-232 Rotator Controller plugin will query the channel specified by the Device (8) and Channel (9) combo boxes for the target azimuth and elevation. For example, this allows an aircraft to be tracked, by setting the Device and Channel to correspond to the ADS-B Demodulator plugin. +The azimuth offset specifies an angle in degrees that is added to the target azimuth before sending to the controller. This allows for a misalignment of the rotator to be corrected. -

7: Refresh list of devices and channels

+

10: Elevation Offset

-Use this button to refresh the list of devices (8) and channels (9) +The elevation offset specifies an angle in degrees that is added to the target elevation before sending to the controller. This allows for a misalignment of the rotator to be corrected. -

8: Select device set

+

API

-Specify the SDRangel device set containing the channel plugin that will be asked for aziumth and elevation values. Defaults to R0. +Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the azimuth and elevation from the command line: -

9: Select channel

+ curl -X PATCH "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/settings" -d '{"featureType": "GS232Controller", "GS232ControllerSettings": { "azimuth": 180, "elevation": 45 }}' -The channel index specifies the SDRangel channel that will be asked for azimuth and elevation values. Defaults to 0. +To start sending commands to the rotator: -

Developer Information

- -For a channel plugin to be able to set the azimuth and elevation, its channel report should contain targetAzimuth and targetElevation. See the ADS-B plugin as an example. - \ No newline at end of file + curl -X POST "http://127.0.0.1:8091/sdrangel/featureset/0/feature/0/run"