From 4ac5e729ffbd90c672967a03b0f0e90eb70ede0b Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:47:13 +0100 Subject: [PATCH 1/6] Rotator Controller Updates Add support for X/Y coordinates. Add coordinate precision setting. Automatically scan for serial port changes. Refactor so each protocol is implemented in a separate class. Add start of DFM protocol. --- .../feature/gs232controller/CMakeLists.txt | 13 + .../gs232controller/controllerprotocol.cpp | 157 ++++++ .../gs232controller/controllerprotocol.h | 57 +++ .../feature/gs232controller/dfmprotocol.cpp | 239 ++++++++++ plugins/feature/gs232controller/dfmprotocol.h | 120 +++++ .../gs232controller/dfmstatusdialog.cpp | 69 +++ .../feature/gs232controller/dfmstatusdialog.h | 40 ++ .../gs232controller/dfmstatusdialog.ui | 413 ++++++++++++++++ .../gs232controller/gs232controller.cpp | 71 ++- .../feature/gs232controller/gs232controller.h | 24 + .../gs232controller/gs232controllergui.cpp | 272 +++++++++-- .../gs232controller/gs232controllergui.h | 21 +- .../gs232controller/gs232controllergui.ui | 447 ++++++++++++------ .../gs232controllersettings.cpp | 161 ++++--- .../gs232controller/gs232controllersettings.h | 10 +- .../gs232controller/gs232controllerworker.cpp | 271 ++--------- .../gs232controller/gs232controllerworker.h | 9 +- .../feature/gs232controller/gs232protocol.cpp | 86 ++++ .../feature/gs232controller/gs232protocol.h | 34 ++ plugins/feature/gs232controller/readme.md | 15 + .../gs232controller/rotctrldprotocol.cpp | 118 +++++ .../gs232controller/rotctrldprotocol.h | 36 ++ .../feature/gs232controller/spidprotocol.cpp | 124 +++++ .../feature/gs232controller/spidprotocol.h | 37 ++ sdrbase/channel/channelwebapiutils.cpp | 233 ++++++++- sdrbase/channel/channelwebapiutils.h | 16 +- sdrbase/util/astronomy.cpp | 178 +++++++ sdrbase/util/astronomy.h | 5 + .../api/swagger/include/GS232Controller.yaml | 8 +- .../qt5/client/SWGGS232ControllerSettings.cpp | 46 ++ .../qt5/client/SWGGS232ControllerSettings.h | 12 + 31 files changed, 2859 insertions(+), 483 deletions(-) create mode 100644 plugins/feature/gs232controller/controllerprotocol.cpp create mode 100644 plugins/feature/gs232controller/controllerprotocol.h create mode 100644 plugins/feature/gs232controller/dfmprotocol.cpp create mode 100644 plugins/feature/gs232controller/dfmprotocol.h create mode 100644 plugins/feature/gs232controller/dfmstatusdialog.cpp create mode 100644 plugins/feature/gs232controller/dfmstatusdialog.h create mode 100644 plugins/feature/gs232controller/dfmstatusdialog.ui create mode 100644 plugins/feature/gs232controller/gs232protocol.cpp create mode 100644 plugins/feature/gs232controller/gs232protocol.h create mode 100644 plugins/feature/gs232controller/rotctrldprotocol.cpp create mode 100644 plugins/feature/gs232controller/rotctrldprotocol.h create mode 100644 plugins/feature/gs232controller/spidprotocol.cpp create mode 100644 plugins/feature/gs232controller/spidprotocol.h diff --git a/plugins/feature/gs232controller/CMakeLists.txt b/plugins/feature/gs232controller/CMakeLists.txt index ccc58673f..cfab468b1 100644 --- a/plugins/feature/gs232controller/CMakeLists.txt +++ b/plugins/feature/gs232controller/CMakeLists.txt @@ -6,6 +6,11 @@ set(gs232controller_SOURCES gs232controllerplugin.cpp gs232controllerworker.cpp gs232controllerwebapiadapter.cpp + controllerprotocol.cpp + gs232protocol.cpp + spidprotocol.cpp + rotctrldprotocol.cpp + dfmprotocol.cpp ) set(gs232controller_HEADERS @@ -15,6 +20,11 @@ set(gs232controller_HEADERS gs232controllerreport.h gs232controllerworker.h gs232controllerwebapiadapter.h + controllerprotocol.h + gs232protocol.h + spidprotocol.h + rotctrldprotocol.h + dfmprotocol.h ) include_directories( @@ -26,10 +36,13 @@ if(NOT SERVER_MODE) ${gs232controller_SOURCES} gs232controllergui.cpp gs232controllergui.ui + dfmstatusdialog.cpp + dfmstatusdialog.ui ) set(gs232controller_HEADERS ${gs232controller_HEADERS} gs232controllergui.h + dfmstatusdialog.h ) set(TARGET_NAME featuregs232controller) diff --git a/plugins/feature/gs232controller/controllerprotocol.cpp b/plugins/feature/gs232controller/controllerprotocol.cpp new file mode 100644 index 000000000..29baace01 --- /dev/null +++ b/plugins/feature/gs232controller/controllerprotocol.cpp @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "maincore.h" +#include "channel/channelwebapiutils.h" + +#include "gs232controllerreport.h" +#include "controllerprotocol.h" +#include "gs232protocol.h" +#include "spidprotocol.h" +#include "rotctrldprotocol.h" +#include "dfmprotocol.h" + +ControllerProtocol::ControllerProtocol() : + m_device(nullptr), + m_lastAzimuth(-1.0f), + m_lastElevation(-1.0f), + m_msgQueueToFeature(nullptr) +{ +} + +ControllerProtocol::~ControllerProtocol() +{ +} + +void ControllerProtocol::setAzimuth(float azimuth) +{ + setAzimuthElevation(azimuth, m_lastElevation); + m_lastAzimuth = azimuth; +} + +void ControllerProtocol::setAzimuthElevation(float azimuth, float elevation) +{ + m_lastAzimuth = azimuth; + m_lastElevation = elevation; +} + +void ControllerProtocol::applySettings(const GS232ControllerSettings& settings, const QList& settingsKeys, bool force) +{ + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +void ControllerProtocol::sendMessage(Message *message) +{ + m_msgQueueToFeature->push(message); +} + +void ControllerProtocol::reportAzEl(float az, float el) +{ + m_msgQueueToFeature->push(GS232ControllerReport::MsgReportAzAl::create(az, el)); +} + +void ControllerProtocol::reportError(const QString &message) +{ + m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(message)); +} + +void ControllerProtocol::getPosition(float& latitude, float& longitude) +{ + if (!m_settings.m_track) + { + // When not tracking, use My Position from preferences + // although this precludes having different antennas at different positions + latitude = MainCore::instance()->getSettings().getLatitude(); + longitude = MainCore::instance()->getSettings().getLongitude(); + } + else + { + // When tracking, get position from Star Tracker / Sat Tracker + QRegularExpression re("([FTR])(\\d+):(\\d+)"); + QRegularExpressionMatch match = re.match(m_settings.m_source); + if (match.hasMatch()) + { + QString kind = match.captured(1); + int setIndex = match.captured(2).toInt(); + int index = match.captured(3).toInt(); + if (kind == 'F') + { + double lat, lon; + bool latOk = ChannelWebAPIUtils::getFeatureSetting(setIndex, index, "latitude", lat); + bool lonOk = ChannelWebAPIUtils::getFeatureSetting(setIndex, index, "longitude", lon); + if (latOk && lonOk) + { + latitude = (float)lat; + longitude = (float)lon; + } + else + { + qDebug() << "ControllerProtocol::getPosition - Failed to get position from source: " << m_settings.m_source; + } + } + else + { + double lat, lon; + bool latOk = ChannelWebAPIUtils::getChannelSetting(setIndex, index, "latitude", lat); + bool lonOk = ChannelWebAPIUtils::getChannelSetting(setIndex, index, "longitude", lon); + if (latOk && lonOk) + { + latitude = (float)lat; + longitude = (float)lon; + } + else + { + qDebug() << "ControllerProtocol::getPosition - Failed to get position from source: " << m_settings.m_source; + } + } + } + else + { + qDebug() << "ControllerProtocol::getPosition - Couldn't parse source: " << m_settings.m_source; + } + } + //qDebug() << "ControllerProtocol::getPosition: " << latitude << longitude; +} + +ControllerProtocol *ControllerProtocol::create(GS232ControllerSettings::Protocol protocol) +{ + switch (protocol) + { + case GS232ControllerSettings::GS232: + return new GS232Protocol(); + break; + case GS232ControllerSettings::SPID: + return new SPIDProtocol(); + break; + case GS232ControllerSettings::ROTCTLD: + return new RotCtrlDProtocol(); + break; + case GS232ControllerSettings::DFM: + return new DFMProtocol(); + break; + default: + return nullptr; + } +} + + diff --git a/plugins/feature/gs232controller/controllerprotocol.h b/plugins/feature/gs232controller/controllerprotocol.h new file mode 100644 index 000000000..9bc82269b --- /dev/null +++ b/plugins/feature/gs232controller/controllerprotocol.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_CONTROLLERPROTOCOL_H_ +#define INCLUDE_FEATURE_CONTROLLERPROTOCOL_H_ + +#include + +#include "util/messagequeue.h" +#include "gs232controllersettings.h" +#include "gs232controller.h" + +class ControllerProtocol +{ +public: + ControllerProtocol(); + virtual ~ControllerProtocol(); + virtual void setAzimuth(float azimuth); + virtual void setAzimuthElevation(float azimuth, float elevation) = 0; + virtual void readData() = 0; + virtual void update() = 0; + void setDevice(QIODevice *device) { m_device = device; } + virtual void applySettings(const GS232ControllerSettings& settings, const QList& settingsKeys, bool force); + void setMessageQueue(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; } + void sendMessage(Message *message); + void reportAzEl(float az, float el); + void reportError(const QString &message); + void getPosition(float& latitude, float& longitude); + + static ControllerProtocol *create(GS232ControllerSettings::Protocol protocol); + +protected: + QIODevice *m_device; + GS232ControllerSettings m_settings; + float m_lastAzimuth; + float m_lastElevation; + +private: + MessageQueue *m_msgQueueToFeature; +}; + +#endif // INCLUDE_FEATURE_CONTROLLERPROTOCOL_H_ + diff --git a/plugins/feature/gs232controller/dfmprotocol.cpp b/plugins/feature/gs232controller/dfmprotocol.cpp new file mode 100644 index 000000000..63b65826b --- /dev/null +++ b/plugins/feature/gs232controller/dfmprotocol.cpp @@ -0,0 +1,239 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/astronomy.h" + +#include "dfmprotocol.h" + +MESSAGE_CLASS_DEFINITION(DFMProtocol::MsgReportDFMStatus, Message) + +DFMProtocol::DFMProtocol() : + m_packetCnt(0) +{ + // Call periodicTask() every 500ms + connect(&m_timer, &QTimer::timeout, this, &DFMProtocol::periodicTask); + m_timer.start(500); +} + +DFMProtocol::~DFMProtocol() +{ + m_timer.stop(); +} + +void DFMProtocol::setAzimuthElevation(float azimuth, float elevation) +{ + // This gets position from source plugin in track is enabled (E.g. Star Tracker / Satellite tracker) + // or My Position preference, if not tracking + float latitude, longitude; + getPosition(latitude, longitude); + + // Convert az/el to RA/Dec + AzAlt aa; + aa.az = azimuth; + aa.alt = elevation; + QDateTime dt = QDateTime::currentDateTime(); + RADec rd = Astronomy::azAltToRaDec(aa, latitude, longitude, dt); + // Save as target + m_targetRA = rd.ra; + m_targetDec = rd.dec; + + // Call parent method to save m_lastAzimuth and m_lastElevation + ControllerProtocol::setAzimuthElevation(azimuth, elevation); +} + +// Handle data received from LCU +// Packets are of the form #L,L,f,f,..,f; +void DFMProtocol::readData() +{ + char c; + while (m_device->getChar(&c)) + { + if (c == '#') + { + // Start packet + m_rxBuffer = QString(c); + } + else if (c == ';') + { + // End packet + m_rxBuffer.append(c); + + // Only process if we have valid packet + if (m_rxBuffer.startsWith('#')) + { + parseLCUResponse(m_rxBuffer); + m_rxBuffer = ""; + } + else + { + qDebug() << "DFMProtocol::readData - Ignoring partial packet: " << m_rxBuffer; + } + } + else + { + m_rxBuffer.append(c); + } + } +} + +void DFMProtocol::parseLCUResponse(const QString& packet) +{ + qDebug() << "DFMProtocol::parseLCUResponse - " << packet; + + // Check packet starts with expected header + if (!packet.startsWith("#L,L,")) + { + qDebug() << "DFMProtocol::readData - Ignoring non LCU packet: " << m_rxBuffer; + return; + } + + // Strip off header and footer + QString strippedPacket = packet.mid(5, packet.length() - 6); + + // Convert packet to list of strings + QStringList dataStrings = strippedPacket.split(","); + + // Extract values we are interested in + DFMStatus status; + + int statl = (int)dataStrings[1].toFloat(); + status.m_initialized = statl & 1; + status.m_brakesOn = (statl >> 1) & 1; + status.m_trackOn = (statl >> 2) & 1; + status.m_slewEnabled = (statl >> 3) & 1; + status.m_lubePumpsOn = (statl >> 4) & 1; + status.m_approachingSWLimit = (statl >> 5) & 1; + status.m_finalSWLimit = (statl >> 6) & 1; + status.m_slewing = (statl >> 7) & 1; + + int stath = (int)dataStrings[2].toFloat(); + status.m_setting = stath & 1; + status.m_haltMotorsIn = (stath >> 1) & 1; + status.m_excomSwitchOn = (stath >> 2) & 1; + status.m_servoPackAlarm = (stath >> 3) & 1; + status.m_targetOutOfRange = (stath >> 4) & 1; + status.m_cosdecOn = (stath >> 5) & 1; + status.m_rateCorrOn = (stath >> 6) & 1; + status.m_drivesOn = (stath >> 7) & 1; + + int statlh = (int)dataStrings[3].toFloat(); + status.m_pumpsReady = statlh & 1; + // Bit 1 unknown + status.m_minorPlus = (statlh >> 2) & 1; + status.m_minorMinus = (statlh >> 3) & 1; + status.m_majorPlus = (statlh >> 4) & 1; + status.m_majorMinus = (statlh >> 5) & 1; + status.m_nextObjectActive = (statlh >> 6) & 1; + status.m_auxTrackRate = (statlh >> 7) & 1; + + status.m_siderealTime = dataStrings[5].toFloat(); + status.m_universalTime = dataStrings[6].toFloat(); + + status.m_currentHA = dataStrings[7].toFloat(); + status.m_currentRA = dataStrings[8].toFloat(); + status.m_currentDec = dataStrings[9].toFloat(); + + status.m_currentX = dataStrings[20].toFloat(); + status.m_currentY = dataStrings[21].toFloat(); + + status.m_siderealRateX = dataStrings[30].toFloat(); + status.m_siderealRateY = dataStrings[31].toFloat(); + + status.m_torqueX = dataStrings[34].toFloat(); + status.m_torqueY = dataStrings[35].toFloat(); + + status.m_controller = (DFMStatus::Controller)dataStrings[38].toInt(); + + status.m_rateX = dataStrings[39].toFloat(); + status.m_rateY = dataStrings[40].toFloat(); + + // Display status in GUI + sendMessage(MsgReportDFMStatus::create(status)); + + // Convert current X/Y to Az/El + AzAlt aa = Astronomy::xy85ToAzAlt(status.m_currentX, status.m_currentY); + float az = aa.az; + float el = aa.alt; + reportAzEl(az, el); + + // If this is the second LCU packet, we send a commmand + m_packetCnt++; + if (m_packetCnt == 2) + { + m_packetCnt = 0; + sendCommand(); + } +} + +void DFMProtocol::sendCommand() +{ + // TODO: Use m_lastAzimuth/m_lastElevation or m_targetRA/m_targetDec to calculate position commands + + // Send a command to the LCU + int cmdId = 98; + int handPaddle = 0; + int frontPanel = (m_settings.m_dfmDrivesOn << 2) + | (m_settings.m_dfmTrackOn << 3) + | (m_settings.m_dfmLubePumpsOn << 4) + | (m_settings.m_dfmBrakesOn << 7); + + QString cmd = QString("#M,R,%1,%2.000000,%3.000000;").arg(cmdId).arg(handPaddle).arg(frontPanel); + m_device->write(cmd.toLatin1()); + + qDebug() << "DFMProtocol::sendCommand - " << cmd; +} + +// Request current Az/El +void DFMProtocol::update() +{ + // This is called periodically for protocols that need to send a command to get current az/el + // However, for this protocol, we might not need to do anything here, + // if we're continually calling reportAzEl() in response to packets received from the LCU. + //sendCommand(); +} + +void DFMProtocol::periodicTask() +{ + // Just as an example, this will be called every 500ms. Can be removed if not needed +} + +// This is called when new settings are available from GUI (or API). +void DFMProtocol::applySettings(const GS232ControllerSettings& settings, const QList& settingsKeys, bool force) +{ + if (settingsKeys.contains("dfmTrackOn") || force) + { + // Do something with settings.m_dfmTrackOn if needed + } + if (settingsKeys.contains("dfmLubePumpsOn") || force) + { + // Do something with settings.m_dfmLubePumpsOn if needed + } + if (settingsKeys.contains("dfmBrakesOn") || force) + { + // Do something with settings.m_dfmBreaksOn if needed + } + if (settingsKeys.contains("dfmDrivesOn") || force) + { + // Do something with settings.m_dfmDrivesOn if needed + } + + // Call parent method to set m_settings to settings + ControllerProtocol::applySettings(settings, settingsKeys, force); +} + diff --git a/plugins/feature/gs232controller/dfmprotocol.h b/plugins/feature/gs232controller/dfmprotocol.h new file mode 100644 index 000000000..ad0704db3 --- /dev/null +++ b/plugins/feature/gs232controller/dfmprotocol.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_DFMPROTOCOL_H_ +#define INCLUDE_FEATURE_DFMPROTOCOL_H_ + +#include + +#include "util/message.h" +#include "controllerprotocol.h" + +class DFMProtocol : public QObject, public ControllerProtocol +{ + Q_OBJECT +public: + + struct DFMStatus { + // STATL + bool m_initialized; + bool m_brakesOn; + bool m_trackOn; + bool m_slewEnabled; + bool m_lubePumpsOn; + bool m_approachingSWLimit; + bool m_finalSWLimit; + bool m_slewing; + // STATH + bool m_setting; + bool m_haltMotorsIn; + bool m_excomSwitchOn; + bool m_servoPackAlarm; + bool m_targetOutOfRange; + bool m_cosdecOn; + bool m_rateCorrOn; + bool m_drivesOn; + // STATLH + bool m_pumpsReady; + bool m_minorPlus; + bool m_minorMinus; + bool m_majorPlus; + bool m_majorMinus; + bool m_nextObjectActive; + bool m_auxTrackRate; + // Other status information + float m_currentHA; + float m_currentRA; + float m_currentDec; + float m_currentX; + float m_currentY; + enum Controller {NONE, OCU, LCU, MCU} m_controller; + float m_torqueX; + float m_torqueY; + float m_rateX; + float m_rateY; + float m_siderealRateX; + float m_siderealRateY; + float m_siderealTime; + float m_universalTime; + }; + + // Message from DFMProtocol to the GUI, with status information to display + class MsgReportDFMStatus : public Message { + MESSAGE_CLASS_DECLARATION + + public: + DFMStatus getDFMStatus() const { return m_dfmStatus; } + + static MsgReportDFMStatus* create(const DFMStatus& dfmStatus) + { + return new MsgReportDFMStatus(dfmStatus); + } + private: + DFMStatus m_dfmStatus; + + MsgReportDFMStatus(const DFMStatus& dfmStatus) : + Message(), + m_dfmStatus(dfmStatus) + { + } + }; + + DFMProtocol(); + ~DFMProtocol(); + void setAzimuthElevation(float azimuth, float elevation) override; + void readData() override; + void update() override; + void applySettings(const GS232ControllerSettings& settings, const QList& settingsKeys, bool force) override; + +private: + void parseLCUResponse(const QString& packet); + void sendCommand(); + + QTimer m_timer; + + QString m_rxBuffer; + int m_packetCnt; + + float m_targetRA; + float m_targetDec; + +private slots: + void periodicTask(); +}; + +#endif // INCLUDE_FEATURE_DFMPROTOCOL_H_ + diff --git a/plugins/feature/gs232controller/dfmstatusdialog.cpp b/plugins/feature/gs232controller/dfmstatusdialog.cpp new file mode 100644 index 000000000..7e7c07c8f --- /dev/null +++ b/plugins/feature/gs232controller/dfmstatusdialog.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/units.h" + +#include "dfmstatusdialog.h" + +DFMStatusDialog::DFMStatusDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::DFMStatusDialog) +{ + ui->setupUi(this); + // Make checkboxes read-only + ui->trackOn->setAttribute(Qt::WA_TransparentForMouseEvents); + ui->driveOn->setAttribute(Qt::WA_TransparentForMouseEvents); + ui->brakesOn->setAttribute(Qt::WA_TransparentForMouseEvents); + ui->pumpsOn->setAttribute(Qt::WA_TransparentForMouseEvents); + ui->controller->setAttribute(Qt::WA_TransparentForMouseEvents); +} + +void DFMStatusDialog::displayStatus(const DFMProtocol::DFMStatus& dfmStatus) +{ + ui->currentHA->setText(QString::number(dfmStatus.m_currentHA, 'f')); + ui->currentRA->setText(QString::number(dfmStatus.m_currentRA, 'f')); + ui->currentDec->setText(QString::number(dfmStatus.m_currentDec, 'f')); + + ui->st->setText(Units::decimalHoursToHoursMinutesAndSeconds(dfmStatus.m_siderealTime)); + ui->ut->setText(Units::decimalHoursToHoursMinutesAndSeconds(dfmStatus.m_universalTime)); + + ui->currentX->setText(QString::number(dfmStatus.m_currentX, 'f')); + ui->currentY->setText(QString::number(dfmStatus.m_currentY, 'f')); + ui->siderealRateX->setText(QString::number(dfmStatus.m_siderealRateX, 'f')); + ui->siderealRateY->setText(QString::number(dfmStatus.m_siderealRateY, 'f')); + ui->rateX->setText(QString::number(dfmStatus.m_rateX, 'f')); + ui->rateY->setText(QString::number(dfmStatus.m_rateY, 'f')); + ui->torqueX->setText(QString::number(dfmStatus.m_torqueX, 'f')); + ui->torqueY->setText(QString::number(dfmStatus.m_torqueY, 'f')); + + ui->trackOn->setChecked(dfmStatus.m_trackOn); + ui->driveOn->setChecked(dfmStatus.m_drivesOn); + ui->brakesOn->setChecked(dfmStatus.m_brakesOn); + ui->pumpsOn->setChecked(dfmStatus.m_pumpsReady); // ? + ui->controller->setCurrentIndex((int)dfmStatus.m_controller); +} + +DFMStatusDialog::~DFMStatusDialog() +{ + delete ui; +} + +void DFMStatusDialog::accept() +{ + QDialog::accept(); +} + diff --git a/plugins/feature/gs232controller/dfmstatusdialog.h b/plugins/feature/gs232controller/dfmstatusdialog.h new file mode 100644 index 000000000..93340f065 --- /dev/null +++ b/plugins/feature/gs232controller/dfmstatusdialog.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DFMSTATUSSDIALOG_H +#define INCLUDE_DFMSTATUSSDIALOG_H + +#include "ui_dfmstatusdialog.h" +#include "dfmprotocol.h" + +class DFMStatusDialog : public QDialog { + Q_OBJECT + +public: + explicit DFMStatusDialog(QWidget* parent = 0); + ~DFMStatusDialog(); + void displayStatus(const DFMProtocol::DFMStatus& dfmStatus); + +private slots: + void accept(); + +private: + Ui::DFMStatusDialog* ui; +}; + +#endif // INCLUDE_DFMSTATUSSDIALOG_H + diff --git a/plugins/feature/gs232controller/dfmstatusdialog.ui b/plugins/feature/gs232controller/dfmstatusdialog.ui new file mode 100644 index 000000000..89179dd62 --- /dev/null +++ b/plugins/feature/gs232controller/dfmstatusdialog.ui @@ -0,0 +1,413 @@ + + + DFMStatusDialog + + + + 0 + 0 + 474 + 488 + + + + + Liberation Sans + 9 + + + + DFM Status + + + + + + + + + Status + + + + + + Track + + + + + + + Qt::NoFocus + + + + + + true + + + + + + + Drives + + + + + + + Qt::NoFocus + + + + + + true + + + + + + + Brakes + + + + + + + Qt::NoFocus + + + + + + true + + + + + + + Pumps + + + + + + + true + + + Qt::NoFocus + + + + + + true + + + + + + + + None + + + + + OCU + + + + + LCU + + + + + MCU + + + + + + + + Controller + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Antenna Position + + + + + + true + + + + + + + RA + + + + + + + Dec + + + + + + + true + + + + + + + HA + + + + + + + true + + + + + + + + + + Date/Time + + + + + + ST + + + + + + + UT + + + + + + + true + + + + + + + true + + + + + + + + + + X/Y Status + + + + + + true + + + + + + + true + + + + + + + Torque + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + Rate + + + + + + + X + + + Qt::AlignCenter + + + + + + + Antenna + + + + + + + Y + + + Qt::AlignCenter + + + + + + + true + + + + + + + Sidereal Rate + + + + + + + true + + + + + + + true + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + DFMStatusDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DFMStatusDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/feature/gs232controller/gs232controller.cpp b/plugins/feature/gs232controller/gs232controller.cpp index 31387826b..6f58478be 100644 --- a/plugins/feature/gs232controller/gs232controller.cpp +++ b/plugins/feature/gs232controller/gs232controller.cpp @@ -39,12 +39,14 @@ #include "gs232controller.h" #include "gs232controllerworker.h" #include "gs232controllerreport.h" +#include "dfmprotocol.h" MESSAGE_CLASS_DEFINITION(GS232Controller::MsgConfigureGS232Controller, Message) MESSAGE_CLASS_DEFINITION(GS232Controller::MsgStartStop, Message) MESSAGE_CLASS_DEFINITION(GS232Controller::MsgReportWorker, Message) MESSAGE_CLASS_DEFINITION(GS232Controller::MsgReportAvailableChannelOrFeatures, Message) MESSAGE_CLASS_DEFINITION(GS232Controller::MsgScanAvailableChannelOrFeatures, Message) +MESSAGE_CLASS_DEFINITION(GS232Controller::MsgReportSerialPorts, Message) const char* const GS232Controller::m_featureIdURI = "sdrangel.feature.gs232controller"; const char* const GS232Controller::m_featureId = "GS232Controller"; @@ -52,7 +54,9 @@ const char* const GS232Controller::m_featureId = "GS232Controller"; GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface) : Feature(m_featureIdURI, webAPIAdapterInterface), m_thread(nullptr), - m_worker(nullptr) + m_worker(nullptr), + m_currentAzimuth(0.0f), + m_currentElevation(0.0f) { qDebug("GS232Controller::GS232Controller: webAPIAdapterInterface: %p", webAPIAdapterInterface); setObjectName(m_featureId); @@ -90,10 +94,14 @@ GS232Controller::GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface) this, &GS232Controller::handleChannelRemoved ); + connect(&m_timer, &QTimer::timeout, this, &GS232Controller::scanSerialPorts); + m_timer.start(5000); } GS232Controller::~GS232Controller() { + m_timer.stop(); + disconnect(&m_timer, &QTimer::timeout, this, &GS232Controller::scanSerialPorts); QObject::disconnect( MainCore::instance(), &MainCore::channelRemoved, @@ -244,6 +252,16 @@ bool GS232Controller::handleMessage(const Message& cmd) } return true; } + else if (DFMProtocol::MsgReportDFMStatus::match(cmd)) + { + // Forward to GUI + if (getMessageQueueToGUI()) + { + DFMProtocol::MsgReportDFMStatus& report = (DFMProtocol::MsgReportDFMStatus&) cmd; + getMessageQueueToGUI()->push(new DFMProtocol::MsgReportDFMStatus(report)); + } + return true; + } else { return false; @@ -256,8 +274,8 @@ bool GS232Controller::getOnTarget() const float targetAziumth, targetElevation; m_settings.calcTargetAzEl(targetAziumth, targetElevation); float readTolerance = m_settings.m_tolerance + 0.0f; - bool onTarget = (std::fabs(m_currentAzimuth - targetAziumth) < readTolerance) - && (std::fabs(m_currentElevation - targetElevation) < readTolerance); + bool onTarget = (std::fabs(m_currentAzimuth - targetAziumth) <= readTolerance) + && (std::fabs(m_currentElevation - targetElevation) <= readTolerance); return onTarget; } @@ -471,6 +489,8 @@ void GS232Controller::webapiFormatFeatureSettings( response.getGs232ControllerSettings()->setElevationMax(settings.m_elevationMax); response.getGs232ControllerSettings()->setTolerance(settings.m_tolerance); response.getGs232ControllerSettings()->setProtocol(settings.m_protocol); + response.getGs232ControllerSettings()->setPrecision(settings.m_precision); + response.getGs232ControllerSettings()->setCoordinates((int)settings.m_coordinates); if (response.getGs232ControllerSettings()->getTitle()) { *response.getGs232ControllerSettings()->getTitle() = settings.m_title; @@ -559,6 +579,12 @@ void GS232Controller::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("protocol")) { settings.m_protocol = (GS232ControllerSettings::Protocol)response.getGs232ControllerSettings()->getProtocol(); } + if (featureSettingsKeys.contains("precision")) { + settings.m_precision = response.getGs232ControllerSettings()->getPrecision(); + } + if (featureSettingsKeys.contains("coordinates")) { + settings.m_coordinates = (GS232ControllerSettings::Coordinates)response.getGs232ControllerSettings()->getCoordinates(); + } if (featureSettingsKeys.contains("title")) { settings.m_title = *response.getGs232ControllerSettings()->getTitle(); } @@ -644,6 +670,12 @@ void GS232Controller::webapiReverseSendSettings(const QList& featureSet if (featureSettingsKeys.contains("protocol") || force) { swgGS232ControllerSettings->setProtocol((int)settings.m_protocol); } + if (featureSettingsKeys.contains("precision") || force) { + swgGS232ControllerSettings->setPrecision(settings.m_precision); + } + if (featureSettingsKeys.contains("coordinates") || force) { + swgGS232ControllerSettings->setCoordinates(settings.m_coordinates); + } if (featureSettingsKeys.contains("title") || force) { swgGS232ControllerSettings->setTitle(new QString(settings.m_title)); } @@ -686,14 +718,9 @@ void GS232Controller::webapiFormatFeatureReport(SWGSDRangel::SWGFeatureReport& r response.getGs232ControllerReport()->getSources()->append(new QString(itemText)); } - QList serialPorts = QSerialPortInfo::availablePorts(); - QListIterator i(serialPorts); response.getGs232ControllerReport()->setSerialPorts(new QList()); - - while (i.hasNext()) - { - QSerialPortInfo info = i.next(); - response.getGs232ControllerReport()->getSerialPorts()->append(new QString(info.portName())); + for (const auto& serialPort : m_serialPorts) { + response.getGs232ControllerReport()->getSerialPorts()->append(new QString(serialPort)); } float azimuth, elevation; @@ -895,3 +922,27 @@ void GS232Controller::handlePipeMessageQueue(MessageQueue* messageQueue) } } } + +void GS232Controller::scanSerialPorts() +{ + // This can take 4ms on Windows, so we don't want to have it in webapiFormatFeatureReport + // as polling of target az/el by other plugins will be slowed down + QList serialPortInfos = QSerialPortInfo::availablePorts(); + QListIterator i(serialPortInfos); + QStringList serialPorts; + while (i.hasNext()) + { + QSerialPortInfo info = i.next(); + serialPorts.append(info.portName()); + } + if (m_serialPorts != serialPorts) + { + if (getMessageQueueToGUI()) + { + MsgReportSerialPorts *msg = MsgReportSerialPorts::create(serialPorts); + getMessageQueueToGUI()->push(msg); + } + m_serialPorts = serialPorts; + } +} + diff --git a/plugins/feature/gs232controller/gs232controller.h b/plugins/feature/gs232controller/gs232controller.h index e5662c61a..e8e507d58 100644 --- a/plugins/feature/gs232controller/gs232controller.h +++ b/plugins/feature/gs232controller/gs232controller.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "feature/feature.h" #include "util/message.h" @@ -138,6 +139,25 @@ public: { } }; + class MsgReportSerialPorts : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const QStringList& getSerialPorts() const { return m_serialPorts; } + + static MsgReportSerialPorts* create(QStringList serialPorts) { + return new MsgReportSerialPorts(serialPorts); + } + + private: + QStringList m_serialPorts; + + MsgReportSerialPorts(QStringList serialPorts) : + Message(), + m_serialPorts(serialPorts) + {} + }; + GS232Controller(WebAPIAdapterInterface *webAPIAdapterInterface); virtual ~GS232Controller(); virtual void destroy() { delete this; } @@ -194,6 +214,9 @@ private: QHash m_availableChannelOrFeatures; QObject *m_selectedPipe; + QTimer m_timer; + QStringList m_serialPorts; + QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; @@ -217,6 +240,7 @@ private slots: void handleChannelRemoved(int deviceSetIndex, ChannelAPI *feature); void handleMessagePipeToBeDeleted(int reason, QObject* object); void handlePipeMessageQueue(MessageQueue* messageQueue); + void scanSerialPorts(); }; #endif // INCLUDE_FEATURE_GS232CONTROLLER_H_ diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index 86ffe3268..3cb42e692 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -27,11 +27,13 @@ #include "gui/dialogpositioner.h" #include "mainwindow.h" #include "device/deviceuiset.h" +#include "util/astronomy.h" #include "ui_gs232controllergui.h" #include "gs232controller.h" #include "gs232controllergui.h" #include "gs232controllerreport.h" +#include "dfmprotocol.h" GS232ControllerGUI* GS232ControllerGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature) { @@ -72,6 +74,57 @@ bool GS232ControllerGUI::deserialize(const QByteArray& data) } } +void GS232ControllerGUI::azElToDisplay(float az, float el, float& coord1, float& coord2) const +{ + AzAlt aa; + double c1, c2; + if (m_settings.m_coordinates == GS232ControllerSettings::X_Y_85) + { + aa.az = az; + aa.alt = el; + Astronomy::azAltToXY85(aa, c1, c2); + coord1 = (float)c1; + coord2 = (float)c2; + } + else if (m_settings.m_coordinates == GS232ControllerSettings::X_Y_30) + { + aa.az = az; + aa.alt = el; + Astronomy::azAltToXY30(aa, c1, c2); + coord1 = (float)c1; + coord2 = (float)c2; + } + else + { + coord1 = az; + coord2 = el; + } +} + +void GS232ControllerGUI::displayToAzEl(float coord1, float coord2) +{ + if (m_settings.m_coordinates == GS232ControllerSettings::X_Y_85) + { + AzAlt aa = Astronomy::xy85ToAzAlt(coord1, coord2); + m_settings.m_azimuth = aa.az; + m_settings.m_elevation = aa.alt; + } + else if (m_settings.m_coordinates == GS232ControllerSettings::X_Y_30) + { + AzAlt aa = Astronomy::xy30ToAzAlt(coord1, coord2); + m_settings.m_azimuth = aa.az; + m_settings.m_elevation = aa.alt; + } + else + { + m_settings.m_azimuth = coord1; + m_settings.m_elevation = coord2; + } + m_settingsKeys.append("azimuth"); + m_settingsKeys.append("elevation"); + applySettings(); +} + bool GS232ControllerGUI::handleMessage(const Message& message) { if (GS232Controller::MsgConfigureGS232Controller::match(message)) @@ -101,20 +154,35 @@ bool GS232ControllerGUI::handleMessage(const Message& message) else if (GS232ControllerReport::MsgReportAzAl::match(message)) { GS232ControllerReport::MsgReportAzAl& azAl = (GS232ControllerReport::MsgReportAzAl&) message; - ui->azimuthCurrentText->setText(QString("%1").arg(azAl.getAzimuth())); - ui->elevationCurrentText->setText(QString("%1").arg(azAl.getElevation())); + float coord1, coord2; + azElToDisplay(azAl.getAzimuth(), azAl.getElevation(), coord1, coord2); + ui->coord1CurrentText->setText(QString::number(coord1, 'f', m_settings.m_precision)); + ui->coord2CurrentText->setText(QString::number(coord2, 'f', m_settings.m_precision)); return true; } else if (MainCore::MsgTargetAzimuthElevation::match(message)) { MainCore::MsgTargetAzimuthElevation& msg = (MainCore::MsgTargetAzimuthElevation&) message; SWGSDRangel::SWGTargetAzimuthElevation *swgTarget = msg.getSWGTargetAzimuthElevation(); - - ui->azimuth->setValue(swgTarget->getAzimuth()); - ui->elevation->setValue(swgTarget->getElevation()); + float coord1, coord2; + azElToDisplay(swgTarget->getAzimuth(), swgTarget->getElevation(), coord1, coord2); + ui->coord1->setValue(coord1); + ui->coord2->setValue(coord2); ui->targetName->setText(*swgTarget->getName()); return true; } + else if (GS232Controller::MsgReportSerialPorts::match(message)) + { + GS232Controller::MsgReportSerialPorts& msg = (GS232Controller::MsgReportSerialPorts&) message; + updateSerialPortList(msg.getSerialPorts()); + return true; + } + else if (DFMProtocol::MsgReportDFMStatus::match(message)) + { + DFMProtocol::MsgReportDFMStatus& report = (DFMProtocol::MsgReportDFMStatus&) message; + m_dfmStatusDialog.displayStatus(report.getDFMStatus()); + return true; + } return false; } @@ -147,7 +215,8 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu m_featureUISet(featureUISet), m_doApplySettings(true), m_lastFeatureState(0), - m_lastOnTarget(false) + m_lastOnTarget(false), + m_dfmStatusDialog() { m_feature = feature; setAttribute(Qt::WA_DeleteOnClose, true); @@ -166,8 +235,9 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(250); - ui->azimuthCurrentText->setText("-"); - ui->elevationCurrentText->setText("-"); + ui->coord1CurrentText->setText("-"); + ui->coord2CurrentText->setText("-"); + setProtocol(m_settings.m_protocol); // Hide DFM buttons updateSerialPortList(); if (ui->serialPort->currentIndex() >= 0) { @@ -182,10 +252,13 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu // Get pre-existing pipes m_gs232Controller->getInputMessageQueue()->push(GS232Controller::MsgScanAvailableChannelOrFeatures::create()); + + new DialogPositioner(&m_dfmStatusDialog, true); } GS232ControllerGUI::~GS232ControllerGUI() { + m_dfmStatusDialog.close(); delete ui; } @@ -206,11 +279,14 @@ void GS232ControllerGUI::displaySettings() setWindowTitle(m_settings.m_title); setTitle(m_settings.m_title); blockApplySettings(true); - ui->azimuth->setValue(m_settings.m_azimuth); - ui->elevation->setValue(m_settings.m_elevation); + ui->precision->setValue(m_settings.m_precision); // Must set before protocol and az/el ui->protocol->setCurrentIndex((int)m_settings.m_protocol); + ui->coordinates->setCurrentIndex((int)m_settings.m_coordinates); + float coord1, coord2; + azElToDisplay(m_settings.m_azimuth, m_settings.m_elevation, coord1, coord2); + ui->coord1->setValue(coord1); + ui->coord2->setValue(coord2); ui->connection->setCurrentIndex((int)m_settings.m_connection); - updateDecimals(m_settings.m_protocol); if (m_settings.m_serialPort.length() > 0) { ui->serialPort->lineEdit()->setText(m_settings.m_serialPort); } @@ -226,6 +302,10 @@ void GS232ControllerGUI::displaySettings() ui->elevationMin->setValue(m_settings.m_elevationMin); ui->elevationMax->setValue(m_settings.m_elevationMax); ui->tolerance->setValue(m_settings.m_tolerance); + ui->dfmTrack->setChecked(m_settings.m_dfmTrackOn); + ui->dfmLubePumps->setChecked(m_settings.m_dfmLubePumpsOn); + ui->dfmBrakes->setChecked(m_settings.m_dfmBrakesOn); + ui->dfmDrives->setChecked(m_settings.m_dfmDrivesOn); getRollupContents()->restoreState(m_rollupState); updateConnectionWidgets(); blockApplySettings(false); @@ -256,6 +336,19 @@ void GS232ControllerGUI::updateSerialPortList() } } +void GS232ControllerGUI::updateSerialPortList(const QStringList& serialPorts) +{ + ui->serialPort->blockSignals(true); + ui->serialPort->clear(); + for (const auto& serialPort : serialPorts) { + ui->serialPort->addItem(serialPort); + } + if (!m_settings.m_serialPort.isEmpty()) { + ui->serialPort->setCurrentText(m_settings.m_serialPort); + } + ui->serialPort->blockSignals(false); +} + void GS232ControllerGUI::updatePipeList(const QList& sources) { QString currentText = ui->sources->currentText(); @@ -359,24 +452,53 @@ void GS232ControllerGUI::on_startStop_toggled(bool checked) } } -void GS232ControllerGUI::updateDecimals(GS232ControllerSettings::Protocol protocol) +void GS232ControllerGUI::setProtocol(GS232ControllerSettings::Protocol protocol) { if (protocol == GS232ControllerSettings::GS232) { - ui->azimuth->setDecimals(0); - ui->elevation->setDecimals(0); + ui->precision->setValue(0); + ui->precision->setEnabled(false); + ui->precisionLabel->setEnabled(false); + } + else if (protocol == GS232ControllerSettings::SPID) +ÿ  + ui->precision->setValue(1); + ui->precision->setEnabled(false); + ui->precisionLabel->setEnabled(false); } else { - ui->azimuth->setDecimals(1); - ui->elevation->setDecimals(1); + ui->precision->setEnabled(true); + ui->precisionLabel->setEnabled(true); } + bool dfm = protocol == GS232ControllerSettings::DFM; + ui->dfmLine->setVisible(dfm); + ui->dfmTrack->setVisible(dfm); + ui->dfmLubePumps->setVisible(dfm); + ui->dfmBrakes->setVisible(dfm); + ui->dfmDrives->setVisible(dfm); + ui->dfmShowStatus->setVisible(dfm); + + // See RemoteControlGUI::createGUI() for additional weirdness in trying + // to resize a window after widgets are changed + getRollupContents()->arrangeRollups(); + layout()->activate(); // Recalculate sizeHint + setMinimumSize(sizeHint()); + setMaximumSize(sizeHint()); + resize(sizeHint()); +} + +void GS232ControllerGUI::setPrecision() +{ + ui->coord1->setDecimals(m_settings.m_precision); + ui->coord2->setDecimals(m_settings.m_precision); + ui->tolerance->setDecimals(m_settings.m_precision); } void GS232ControllerGUI::on_protocol_currentIndexChanged(int index) { m_settings.m_protocol = (GS232ControllerSettings::Protocol)index; - updateDecimals(m_settings.m_protocol); + setProtocol(m_settings.m_protocol); m_settingsKeys.append("protocol"); applySettings(); } @@ -419,20 +541,16 @@ void GS232ControllerGUI::on_port_valueChanged(int value) applySettings(); } -void GS232ControllerGUI::on_azimuth_valueChanged(double value) +void GS232ControllerGUI::on_coord1_valueChanged(double value) { - m_settings.m_azimuth = (float)value; + displayToAzEl(value, ui->coord2->value()); ui->targetName->setText(""); - m_settingsKeys.append("azimuth"); - applySettings(); } -void GS232ControllerGUI::on_elevation_valueChanged(double value) +void GS232ControllerGUI::on_coord2_valueChanged(double value) { - m_settings.m_elevation = (float)value; + displayToAzEl(ui->coord1->value(), value); ui->targetName->setText(""); - m_settingsKeys.append("elevation"); - applySettings(); } void GS232ControllerGUI::on_azimuthOffset_valueChanged(int value) @@ -484,6 +602,63 @@ void GS232ControllerGUI::on_tolerance_valueChanged(double value) applySettings(); } +void GS232ControllerGUI::on_precision_valueChanged(int value) +{ + m_settings.m_precision = value; + setPrecision(); + m_settingsKeys.append("precision"); + applySettings(); +} + +void GS232ControllerGUI::on_coordinates_currentIndexChanged(int index) +{ + m_settings.m_coordinates = (GS232ControllerSettings::Coordinates)index; + m_settingsKeys.append("coordinates"); + applySettings(); + + float coord1, coord2; + azElToDisplay(m_settings.m_azimuth, m_settings.m_elevation, coord1, coord2); + + ui->coord1->blockSignals(true); + if (m_settings.m_coordinates == GS232ControllerSettings::AZ_EL) + { + ui->coord1->setMinimum(0.0); + ui->coord1->setMaximum(450.0); + ui->coord1->setToolTip("Target azimuth in degrees"); + ui->coord1Label->setText("Azimuth"); + ui->coord1CurrentText->setToolTip("Current azimuth in degrees"); + } + else + { + ui->coord1->setMinimum(-90.0); + ui->coord1->setMaximum(90.0); + ui->coord1->setToolTip("Target X in degrees"); + ui->coord1Label->setText("X"); + ui->coord1CurrentText->setToolTip("Current X coordinate in degrees"); + } + ui->coord1->setValue(coord1); + ui->coord1->blockSignals(false); + ui->coord2->blockSignals(true); + if (m_settings.m_coordinates == GS232ControllerSettings::AZ_EL) + { + ui->coord2->setMinimum(0.0); + ui->coord2->setMaximum(180.0); + ui->coord2->setToolTip("Target elevation in degrees"); + ui->coord2Label->setText("Elevation"); + ui->coord2CurrentText->setToolTip("Current elevation in degrees"); + } + else + { + ui->coord2->setMinimum(-90.0); + ui->coord2->setMaximum(90.0); + ui->coord2->setToolTip("Target Y in degrees"); + ui->coord2Label->setText("Y"); + ui->coord2CurrentText->setToolTip("Current Y coordinate in degrees"); + } + ui->coord2->setValue(coord2); + ui->coord2->blockSignals(false); +} + void GS232ControllerGUI::on_track_stateChanged(int state) { m_settings.m_track = state == Qt::Checked; @@ -507,6 +682,41 @@ void GS232ControllerGUI::on_sources_currentTextChanged(const QString& text) applySettings(); } +void GS232ControllerGUI::on_dfmTrack_clicked(bool checked) +{ + m_settings.m_dfmTrackOn = checked; + m_settingsKeys.append("dfmTrackOn"); + applySettings(); +} + +void GS232ControllerGUI::on_dfmLubePumps_clicked(bool checked) +{ + m_settings.m_dfmLubePumpsOn = checked; + m_settingsKeys.append("dfmLubePumpsOn"); + applySettings(); +} + +void GS232ControllerGUI::on_dfmBrakes_clicked(bool checked) +{ + m_settings.m_dfmBrakesOn = checked; + m_settingsKeys.append("dfmBrakesOn"); + applySettings(); +} + +void GS232ControllerGUI::on_dfmDrives_clicked(bool checked) +{ + m_settings.m_dfmDrivesOn = checked; + m_settingsKeys.append("dfmDrivesOn"); + applySettings(); +} + +void GS232ControllerGUI::on_dfmShowStatus_clicked() +{ + m_dfmStatusDialog.show(); + m_dfmStatusDialog.raise(); + m_dfmStatusDialog.activateWindow(); +} + void GS232ControllerGUI::updateStatus() { int state = m_gs232Controller->getState(); @@ -583,8 +793,8 @@ void GS232ControllerGUI::makeUIConnections() QObject::connect(ui->port, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_port_valueChanged); QObject::connect(ui->baudRate, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_baudRate_currentIndexChanged); QObject::connect(ui->track, &QCheckBox::stateChanged, this, &GS232ControllerGUI::on_track_stateChanged); - QObject::connect(ui->azimuth, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuth_valueChanged); - QObject::connect(ui->elevation, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevation_valueChanged); + QObject::connect(ui->coord1, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_coord1_valueChanged); + QObject::connect(ui->coord2, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_coord2_valueChanged); QObject::connect(ui->sources, &QComboBox::currentTextChanged, this, &GS232ControllerGUI::on_sources_currentTextChanged); QObject::connect(ui->azimuthOffset, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuthOffset_valueChanged); QObject::connect(ui->elevationOffset, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationOffset_valueChanged); @@ -593,4 +803,12 @@ void GS232ControllerGUI::makeUIConnections() QObject::connect(ui->elevationMin, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMin_valueChanged); QObject::connect(ui->elevationMax, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMax_valueChanged); QObject::connect(ui->tolerance, qOverload(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_tolerance_valueChanged); + QObject::connect(ui->precision, qOverload(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_precision_valueChanged); + QObject::connect(ui->coordinates, qOverload(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_coordinates_currentIndexChanged); + QObject::connect(ui->dfmTrack, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmTrack_clicked); + QObject::connect(ui->dfmLubePumps, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmLubePumps_clicked); + QObject::connect(ui->dfmBrakes, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmBrakes_clicked); + QObject::connect(ui->dfmDrives, &QToolButton::toggled, this, &GS232ControllerGUI::on_dfmDrives_clicked); + QObject::connect(ui->dfmShowStatus, &QToolButton::clicked, this, &GS232ControllerGUI::on_dfmShowStatus_clicked); } + diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index 5d2426e52..2005d363b 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -26,6 +26,7 @@ #include "settings/rollupstate.h" #include "gs232controllersettings.h" +#include "dfmstatusdialog.h" class PluginAPI; class FeatureUISet; @@ -65,18 +66,24 @@ private: int m_lastFeatureState; bool m_lastOnTarget; + DFMStatusDialog m_dfmStatusDialog; + explicit GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr); virtual ~GS232ControllerGUI(); void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void setProtocol(GS232ControllerSettings::Protocol protocol); + void setPrecision(); void updateConnectionWidgets(); - void updateDecimals(GS232ControllerSettings::Protocol protocol); void updatePipeList(const QList& sources); void updateSerialPortList(); + void updateSerialPortList(const QStringList& serialPorts); bool handleMessage(const Message& message); void makeUIConnections(); + void azElToDisplay(float az, float el, float& coord1, float& coord2) const; + void displayToAzEl(float coord1, float coord2); private slots: void onMenuDialogCalled(const QPoint &p); @@ -90,8 +97,8 @@ private slots: void on_port_valueChanged(int value); void on_baudRate_currentIndexChanged(int index); void on_track_stateChanged(int state); - void on_azimuth_valueChanged(double value); - void on_elevation_valueChanged(double value); + void on_coord1_valueChanged(double value); + void on_coord2_valueChanged(double value); void on_sources_currentTextChanged(const QString& text); void on_azimuthOffset_valueChanged(int value); void on_elevationOffset_valueChanged(int value); @@ -100,7 +107,15 @@ private slots: void on_elevationMin_valueChanged(int value); void on_elevationMax_valueChanged(int value); void on_tolerance_valueChanged(double value); + void on_precision_valueChanged(int value); + void on_coordinates_currentIndexChanged(int index); + void on_dfmTrack_clicked(bool checked=false); + void on_dfmLubePumps_clicked(bool checked=false); + void on_dfmBrakes_clicked(bool checked=false); + void on_dfmDrives_clicked(bool checked=false); + void on_dfmShowStatus_clicked(); void updateStatus(); }; #endif // INCLUDE_FEATURE_GS232CONTROLLERGUI_H_ + diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index b697bc9eb..4a26a83c6 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -7,7 +7,7 @@ 0 0 360 - 281 + 378 @@ -43,7 +43,7 @@ 10 10 341 - 81 + 101 @@ -96,19 +96,28 @@ - + + + + 54 + 0 + + Azimuth + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + Target azimuth in degrees - 1 + 0 450.000000000000000 @@ -119,10 +128,10 @@ - + - 32 + 40 0 @@ -130,7 +139,7 @@ Current azimuth in degrees - 360.0 + 360.00 @@ -142,19 +151,28 @@ - + + + + 54 + 0 + + Elevation + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + - + Target elevation in degrees - 1 + 0 180.000000000000000 @@ -165,10 +183,10 @@ - + - 32 + 40 0 @@ -176,7 +194,7 @@ Current elevation in degrees - 180.0 + 180.00 @@ -219,15 +237,76 @@ + + + + Qt::Horizontal + + + + + + + + + Track + + + true + + + + + + + Pumps + + + true + + + + + + + Brakes + + + true + + + + + + + Drives + + + true + + + + + + + Show the DFM status dialog + + + Status + + + + + 10 - 110 + 140 341 - 161 + 191 @@ -258,31 +337,26 @@ - - - - Azimuth min + + + + false + + + Precision (number of decimal places) of azimuth, elevation and tolerance values + + + - - - - - 450 + 5 - - - - Protocol - - - - - - - Elevation max + + + + Hostname / IP address of computer to connect to @@ -293,6 +367,43 @@ + + + + Tolerance + + + + + + + Name of serial port to use to connect to the rotator + + + true + + + + + + + Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment + + + -360 + + + 360 + + + + + + + Azimuth max + + + @@ -300,34 +411,20 @@ - - - - 180 - - - - - - - Port - - - - - + + - Tolerance in degrees + TCP port number to connect to - - 1 + + 65535 - - + + - Serial Port + Baud rate @@ -348,17 +445,48 @@ - - + + - Tolerance + Serial Port - - + + + + Tolerance in degrees + + + 0 + + + + + + + 450 + + + + + - Elevation offset + Host + + + + + + + Port + + + + + + + Elevation max @@ -369,70 +497,29 @@ - - - - Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment - - - -180 - + + 180 - - 1 - - - - - Hostname / IP address of computer to connect to - - - - - - - Name of serial port to use to connect to the rotator - - - true - - - - - - - Command protocol - - - - GS-232 - - - - - SPID - - - - - rotctld - - - - - - + + - Host + Azimuth min - - + + + + Elevation offset + + + + + 180 @@ -498,40 +585,92 @@ - - + + + + false + - Azimuth max + Precision - - + + - Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment + Command protocol + + + + GS-232 + + + + + SPID + + + + + rotctld + + + + + DFM + + + + + + + + Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment - -360 + -180 - 360 + 180 + + + 1 - - - - TCP port number to connect to - - - 65535 - - - - - + + - Baud rate + Protocol + + + + + + + Coordinate system + + + + Az/El + + + + + X/Y 85' + + + + + X/Y 30' + + + + + + + + Coordinates @@ -541,24 +680,38 @@ + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
RollupContents QWidget
gui/rollupcontents.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
startStop - azimuth - elevation + coord1 + coord2 track sources + targetName + protocol + connection + serialPort + baudRate + host + port + azimuthOffset + elevationOffset + azimuthMin + azimuthMax + elevationMin + elevationMax + tolerance diff --git a/plugins/feature/gs232controller/gs232controllersettings.cpp b/plugins/feature/gs232controller/gs232controllersettings.cpp index e7aafabdb..8311a957b 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.cpp +++ b/plugins/feature/gs232controller/gs232controllersettings.cpp @@ -49,15 +49,10 @@ void GS232ControllerSettings::resetToDefaults() m_elevation = 0.0f; m_serialPort = ""; m_baudRate = 9600; + m_host = "127.0.0.1"; + m_port = 4533; m_track = false; m_source = ""; - m_title = "Rotator Controller"; - m_rgbColor = QColor(225, 25, 99).rgb(); - m_useReverseAPI = false; - m_reverseAPIAddress = "127.0.0.1"; - m_reverseAPIPort = 8888; - m_reverseAPIFeatureSetIndex = 0; - m_reverseAPIFeatureIndex = 0; m_azimuthOffset = 0; m_elevationOffset = 0; m_azimuthMin = 0; @@ -67,8 +62,19 @@ void GS232ControllerSettings::resetToDefaults() m_tolerance = 1.0f; m_protocol = GS232; m_connection = SERIAL; - m_host = "127.0.0.1"; - m_port = 4533; + m_precision = 0; + m_coordinates = AZ_EL; + m_dfmTrackOn = false; + m_dfmLubePumpsOn = false; + m_dfmBrakesOn = false; + m_dfmDrivesOn = false; + m_title = "Rotator Controller"; + m_rgbColor = QColor(225, 25, 99).rgb(); + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIFeatureSetIndex = 0; + m_reverseAPIFeatureIndex = 0; m_workspaceIndex = 0; } @@ -107,6 +113,12 @@ QByteArray GS232ControllerSettings::serialize() const s.writeS32(27, m_workspaceIndex); s.writeBlob(28, m_geometryBytes); + s.writeS32(29, m_precision); + s.writeS32(30, (int)m_coordinates); + s.writeBool(31, m_dfmTrackOn); + s.writeBool(32, m_dfmLubePumpsOn); + s.writeBool(33, m_dfmBrakesOn); + s.writeBool(34, m_dfmDrivesOn); return s.final(); } @@ -169,6 +181,12 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) d.readS32(27, &m_workspaceIndex, 0); d.readBlob(28, &m_geometryBytes); + d.readS32(29, &m_precision, 0); + d.readS32(30, (int *)&m_coordinates, (int)AZ_EL); + d.readBool(31, &m_dfmTrackOn); + d.readBool(32, &m_dfmLubePumpsOn); + d.readBool(33, &m_dfmBrakesOn); + d.readBool(34, &m_dfmDrivesOn); return true; } @@ -208,33 +226,18 @@ void GS232ControllerSettings::applySettings(const QStringList& settingsKeys, con if (settingsKeys.contains("baudRate")) { m_baudRate = settings.m_baudRate; } + if (settingsKeys.contains("host")) { + m_host = settings.m_host; + } + if (settingsKeys.contains("port")) { + m_port = settings.m_port; + } if (settingsKeys.contains("track")) { m_track = settings.m_track; } if (settingsKeys.contains("source")) { m_source = settings.m_source; } - if (settingsKeys.contains("title")) { - m_title = settings.m_title; - } - if (settingsKeys.contains("rgbColor")) { - m_rgbColor = settings.m_rgbColor; - } - if (settingsKeys.contains("useReverseAPI")) { - m_useReverseAPI = settings.m_useReverseAPI; - } - if (settingsKeys.contains("reverseAPIAddress")) { - m_reverseAPIAddress = settings.m_reverseAPIAddress; - } - if (settingsKeys.contains("reverseAPIPort")) { - m_reverseAPIPort = settings.m_reverseAPIPort; - } - if (settingsKeys.contains("reverseAPIFeatureSetIndex")) { - m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex; - } - if (settingsKeys.contains("reverseAPIFeatureIndex")) { - m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; - } if (settingsKeys.contains("azimuthOffset")) { m_azimuthOffset = settings.m_azimuthOffset; } @@ -262,11 +265,44 @@ void GS232ControllerSettings::applySettings(const QStringList& settingsKeys, con if (settingsKeys.contains("connection")) { m_connection = settings.m_connection; } - if (settingsKeys.contains("host")) { - m_host = settings.m_host; + if (settingsKeys.contains("precision")) { + m_precision = settings.m_precision; } - if (settingsKeys.contains("port")) { - m_port = settings.m_port; + if (settingsKeys.contains("coordinates")) { + m_coordinates = settings.m_coordinates; + } + if (settingsKeys.contains("dfmTrackOn")) { + m_dfmTrackOn = settings.m_dfmTrackOn; + } + if (settingsKeys.contains("dfmLubePumpsOn")) { + m_dfmLubePumpsOn = settings.m_dfmLubePumpsOn; + } + if (settingsKeys.contains("dfmBrakesOn")) { + m_dfmBrakesOn = settings.m_dfmBrakesOn; + } + if (settingsKeys.contains("dfmDrivesOn")) { + m_dfmDrivesOn = settings.m_dfmDrivesOn; + } + if (settingsKeys.contains("title")) { + m_title = settings.m_title; + } + if (settingsKeys.contains("rgbColor")) { + m_rgbColor = settings.m_rgbColor; + } + if (settingsKeys.contains("useReverseAPI")) { + m_useReverseAPI = settings.m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress")) { + m_reverseAPIAddress = settings.m_reverseAPIAddress; + } + if (settingsKeys.contains("reverseAPIPort")) { + m_reverseAPIPort = settings.m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIFeatureSetIndex")) { + m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex; + } + if (settingsKeys.contains("reverseAPIFeatureIndex")) { + m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; } if (settingsKeys.contains("workspaceIndex")) { m_workspaceIndex = settings.m_workspaceIndex; @@ -289,33 +325,18 @@ QString GS232ControllerSettings::getDebugString(const QStringList& settingsKeys, if (settingsKeys.contains("baudRate") || force) { ostr << " m_baudRate: " << m_baudRate; } + if (settingsKeys.contains("host") || force) { + ostr << " m_host: " << m_host.toStdString(); + } + if (settingsKeys.contains("port") || force) { + ostr << " m_port: " << m_port; + } if (settingsKeys.contains("track") || force) { ostr << " m_track: " << m_track; } if (settingsKeys.contains("source") || force) { ostr << " m_source: " << m_source.toStdString(); } - if (settingsKeys.contains("title") || force) { - ostr << " m_title: " << m_title.toStdString(); - } - if (settingsKeys.contains("rgbColor") || force) { - ostr << " m_rgbColor: " << m_rgbColor; - } - if (settingsKeys.contains("useReverseAPI") || force) { - ostr << " m_useReverseAPI: " << m_useReverseAPI; - } - if (settingsKeys.contains("azimuth") || force) { - ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString(); - } - if (settingsKeys.contains("reverseAPIPort") || force) { - ostr << " m_reverseAPIPort: " << m_reverseAPIPort; - } - if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) { - ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex; - } - if (settingsKeys.contains("reverseAPIFeatureIndex") || force) { - ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex; - } if (settingsKeys.contains("azimuthOffset") || force) { ostr << " m_azimuthOffset: " << m_azimuthOffset; } @@ -343,11 +364,32 @@ QString GS232ControllerSettings::getDebugString(const QStringList& settingsKeys, if (settingsKeys.contains("connection") || force) { ostr << " m_connection: " << m_connection; } - if (settingsKeys.contains("host") || force) { - ostr << " m_host: " << m_host.toStdString(); + if (settingsKeys.contains("precision") || force) { + ostr << " m_precision: " << m_precision; } - if (settingsKeys.contains("port") || force) { - ostr << " m_port: " << m_port; + if (settingsKeys.contains("coordinates") || force) { + ostr << " m_coordinates: " << m_precision; + } + if (settingsKeys.contains("title") || force) { + ostr << " m_title: " << m_title.toStdString(); + } + if (settingsKeys.contains("rgbColor") || force) { + ostr << " m_rgbColor: " << m_rgbColor; + } + if (settingsKeys.contains("useReverseAPI") || force) { + ostr << " m_useReverseAPI: " << m_useReverseAPI; + } + if (settingsKeys.contains("azimuth") || force) { + ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString(); + } + if (settingsKeys.contains("reverseAPIPort") || force) { + ostr << " m_reverseAPIPort: " << m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIFeatureSetIndex") || force) { + ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex; + } + if (settingsKeys.contains("reverseAPIFeatureIndex") || force) { + ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex; } if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; @@ -355,3 +397,4 @@ QString GS232ControllerSettings::getDebugString(const QStringList& settingsKeys, return QString(ostr.str().c_str()); } + diff --git a/plugins/feature/gs232controller/gs232controllersettings.h b/plugins/feature/gs232controller/gs232controllersettings.h index 7bc4ec97e..eff7863f1 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.h +++ b/plugins/feature/gs232controller/gs232controllersettings.h @@ -58,8 +58,16 @@ struct GS232ControllerSettings int m_elevationMin; int m_elevationMax; float m_tolerance; - enum Protocol { GS232, SPID, ROTCTLD } m_protocol; + enum Protocol { GS232, SPID, ROTCTLD, DFM } m_protocol; enum Connection { SERIAL, TCP } m_connection; + int m_precision; + enum Coordinates { AZ_EL, X_Y_85, X_Y_30 } m_coordinates; + + bool m_dfmTrackOn; + bool m_dfmLubePumpsOn; + bool m_dfmBrakesOn; + bool m_dfmDrivesOn; + Serializable *m_rollupState; QString m_title; quint32 m_rgbColor; diff --git a/plugins/feature/gs232controller/gs232controllerworker.cpp b/plugins/feature/gs232controller/gs232controllerworker.cpp index 1b18b705b..13b9f308d 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.cpp +++ b/plugins/feature/gs232controller/gs232controllerworker.cpp @@ -38,10 +38,11 @@ GS232ControllerWorker::GS232ControllerWorker() : m_pollTimer(this), m_lastAzimuth(-1.0f), m_lastElevation(-1.0f), - m_spidSetOutstanding(false), - m_spidSetSent(false), - m_spidStatusSent(false), - m_rotCtlDReadAz(false) + m_controllerProtocol(nullptr) +// m_spidSetOutstanding(false), +// m_spidSetSent(false), +// m_spidStatusSent(false), +// m_rotCtlDReadAz(false) { } @@ -50,6 +51,7 @@ GS232ControllerWorker::~GS232ControllerWorker() qDebug() << "GS232ControllerWorker::~GS232ControllerWorker"; stopWork(); m_inputMessageQueue.clear(); + delete m_controllerProtocol; } void GS232ControllerWorker::startWork() @@ -115,6 +117,17 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting { qDebug() << "GS232ControllerWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force; + if ((settingsKeys.contains("protocol") && (settings.m_protocol != m_settings.m_protocol)) || force) + { + delete m_controllerProtocol; + m_controllerProtocol = ControllerProtocol::create(settings.m_protocol); + if (m_controllerProtocol) + { + m_controllerProtocol->setMessageQueue(m_msgQueueToFeature); + m_controllerProtocol->setDevice(m_device); + } + } + if (settingsKeys.contains("connection") ) { if (m_device && m_device->isOpen()) @@ -126,26 +139,39 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting if (settings.m_connection == GS232ControllerSettings::TCP) { - if (settingsKeys.contains("host") || settingsKeys.contains("port") || force) { + if (settingsKeys.contains("host") || settingsKeys.contains("port") || force) + { m_device = openSocket(settings); + if (m_controllerProtocol) { + m_controllerProtocol->setDevice(m_device); + } } } else { - if (settingsKeys.contains("serialPort") || force) { + if (settingsKeys.contains("serialPort") || force) + { m_device = openSerialPort(settings); - } else if (settingsKeys.contains("baudRate") || force) { + if (m_controllerProtocol) { + m_controllerProtocol->setDevice(m_device); + } + } + else if (settingsKeys.contains("baudRate") || force) + { m_serialPort.setBaudRate(settings.m_baudRate); } } - // Must update m_settings before calling setAzimuthElevation, so m_settings.m_protocol is correct if (force) { m_settings = settings; } else { m_settings.applySettings(settingsKeys, settings); } + if (m_controllerProtocol) { + m_controllerProtocol->applySettings(settings, settingsKeys, force); + } + if (m_device != nullptr) { // Apply offset then clamp @@ -220,244 +246,35 @@ QIODevice *GS232ControllerWorker::openSocket(const GS232ControllerSettings& sett void GS232ControllerWorker::setAzimuth(float azimuth) { - if (m_settings.m_protocol == GS232ControllerSettings::GS232) - { - QString cmd = QString("M%1\r\n").arg((int)std::round(azimuth), 3, 10, QLatin1Char('0')); - QByteArray data = cmd.toLatin1(); - m_device->write(data); - } - else - { - setAzimuthElevation(azimuth, m_lastElevation); + if (m_device && m_device->isOpen() && m_controllerProtocol) { + m_controllerProtocol->setAzimuth(azimuth); } + m_lastAzimuth = azimuth; } void GS232ControllerWorker::setAzimuthElevation(float azimuth, float elevation) { - if (m_settings.m_protocol == GS232ControllerSettings::GS232) - { - QString cmd = QString("W%1 %2\r\n").arg((int)std::round(azimuth), 3, 10, QLatin1Char('0')).arg((int)std::round(elevation), 3, 10, QLatin1Char('0')); - QByteArray data = cmd.toLatin1(); - m_device->write(data); + if (m_device && m_device->isOpen() && m_controllerProtocol) { + m_controllerProtocol->setAzimuthElevation(azimuth, elevation); } - else if (m_settings.m_protocol == GS232ControllerSettings::SPID) - { - if (!m_spidSetSent && !m_spidStatusSent) - { - QByteArray cmd(13, (char)0); - cmd[0] = 0x57; // Start - int h = std::round((azimuth + 360.0f) * 2.0f); - cmd[1] = 0x30 | (h / 1000); - cmd[2] = 0x30 | ((h % 1000) / 100); - cmd[3] = 0x30 | ((h % 100) / 10); - cmd[4] = 0x30 | (h % 10); - cmd[5] = 2; // 2 degree per impulse - int v = std::round((elevation + 360.0f) * 2.0f); - cmd[6] = 0x30 | (v / 1000); - cmd[7] = 0x30 | ((v % 1000) / 100); - cmd[8] = 0x30 | ((v % 100) / 10); - cmd[9] = 0x30 | (v % 10); - cmd[10] = 2; // 2 degree per impulse - cmd[11] = 0x2f; // Set cmd - cmd[12] = 0x20; // End - - m_device->write(cmd); - - m_spidSetSent = true; - } - else - { - qDebug() << "GS232ControllerWorker::setAzimuthElevation: Not sent, waiting for status reply"; - m_spidSetOutstanding = true; - } - } - else - { - QString cmd = QString("P %1 %2\n").arg(azimuth).arg(elevation); - QByteArray data = cmd.toLatin1(); - m_device->write(data); - } m_lastAzimuth = azimuth; m_lastElevation = elevation; } void GS232ControllerWorker::readData() { - char buf[1024]; - qint64 len; - - if (m_settings.m_protocol == GS232ControllerSettings::GS232) - { - while (m_device->canReadLine()) - { - len = m_device->readLine(buf, sizeof(buf)); - if (len != -1) - { - QString response = QString::fromUtf8(buf, len); - // MD-02 can return AZ=-00 EL=-00 and other negative angles - QRegularExpression re("AZ=([-\\d]\\d\\d) *EL=([-\\d]\\d\\d)"); - QRegularExpressionMatch match = re.match(response); - if (match.hasMatch()) - { - QString az = match.captured(1); - QString el = match.captured(2); - //qDebug() << "GS232ControllerWorker::readData read Az " << az << " El " << el; - m_msgQueueToFeature->push(GS232ControllerReport::MsgReportAzAl::create(az.toFloat(), el.toFloat())); - } - else if (response == "\r\n") - { - // Ignore - } - else - { - qWarning() << "GS232ControllerWorker::readData - unexpected GS-232 response \"" << response << "\""; - m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected GS-232 response: %1").arg(response))); - } - } - } - } - else if (m_settings.m_protocol == GS232ControllerSettings::SPID) - { - while (m_device->bytesAvailable() >= 12) - { - len = m_device->read(buf, 12); - if ((len == 12) && (buf[0] == 0x57)) - { - double az; - double el; - az = buf[1] * 100.0 + buf[2] * 10.0 + buf[3] + buf[4] / 10.0 - 360.0; - el = buf[6] * 100.0 + buf[7] * 10.0 + buf[8] + buf[9] / 10.0 - 360.0; - //qDebug() << "GS232ControllerWorker::readData read Az " << az << " El " << el; - m_msgQueueToFeature->push(GS232ControllerReport::MsgReportAzAl::create(az, el)); - if (m_spidStatusSent && m_spidSetSent) { - qDebug() << "GS232ControllerWorker::readData - m_spidStatusSent and m_spidSetSent set simultaneously"; - } - if (m_spidStatusSent) { - m_spidStatusSent = false; - } - if (m_spidSetSent) { - m_spidSetSent = false; - } - if (m_spidSetOutstanding) - { - m_spidSetOutstanding = false; - setAzimuthElevation(m_lastAzimuth, m_lastElevation); - } - } - else - { - QByteArray bytes(buf, (int)len); - qWarning() << "GS232ControllerWorker::readData - unexpected SPID rot2prog response \"" << bytes.toHex() << "\""; - m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected SPID rot2prog response: %1").arg(bytes.toHex().data()))); - } - } - } - else - { - while (m_device->canReadLine()) - { - len = m_device->readLine(buf, sizeof(buf)); - if (len != -1) - { - QString response = QString::fromUtf8(buf, len).trimmed(); - QRegularExpression rprt("RPRT (-?\\d+)"); - QRegularExpressionMatch matchRprt = rprt.match(response); - QRegularExpression decimal("(-?\\d+.\\d+)"); - QRegularExpressionMatch matchDecimal = decimal.match(response); - if (matchRprt.hasMatch()) - { - // See rig_errcode_e in hamlib rig.h - const QStringList errors = { - "OK", - "Invalid parameter", - "Invalid configuration", - "No memory", - "Not implemented", - "Timeout", - "IO error", - "Internal error", - "Protocol error", - "Command rejected", - "Arg truncated", - "Not available", - "VFO not targetable", - "Bus error", - "Collision on bus", - "NULL rig handled or invalid pointer parameter", - "Invalid VFO", - "Argument out of domain of function" - }; - int rprt = matchRprt.captured(1).toInt(); - if (rprt != 0) - { - qWarning() << "GS232ControllerWorker::readData - rotctld error: " << errors[-rprt]; - // Seem to get a lot of EPROTO errors from rotctld due to extra 00 char in response to GS232 C2 command - // E.g: ./rotctld.exe -m 603 -r com7 -vvvvv - // read_string(): RX 16 characters - // 0000 00 41 5a 3d 31 37 35 20 20 45 4c 3d 30 33 38 0d .AZ=175 EL=038. - // So don't pass these to GUI for now - if (rprt != -8) { - m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("rotctld error: %1").arg(errors[-rprt]))); - } - } - m_rotCtlDReadAz = false; - } - else if (matchDecimal.hasMatch() && !m_rotCtlDReadAz) - { - m_rotCtlDAz = response; - m_rotCtlDReadAz = true; - } - else if (matchDecimal.hasMatch() && m_rotCtlDReadAz) - { - QString az = m_rotCtlDAz; - QString el = response; - m_rotCtlDReadAz = false; - //qDebug() << "GS232ControllerWorker::readData read Az " << az << " El " << el; - m_msgQueueToFeature->push(GS232ControllerReport::MsgReportAzAl::create(az.toFloat(), el.toFloat())); - } - else - { - qWarning() << "GS232ControllerWorker::readData - Unexpected rotctld response \"" << response << "\""; - m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected rotctld response: %1").arg(response))); - } - } - } + if (m_controllerProtocol) { + m_controllerProtocol->readData(); } } void GS232ControllerWorker::update() { // Request current Az/El from controller - if (m_device && m_device->isOpen()) - { - if (m_settings.m_protocol == GS232ControllerSettings::GS232) - { - QByteArray cmd("C2\r\n"); - m_device->write(cmd); - } - else if (m_settings.m_protocol == GS232ControllerSettings::SPID) - { - // Don't send a new status command, if waiting for a previous reply - if (!m_spidSetSent && !m_spidStatusSent) - { - // Status - QByteArray cmd; - cmd.append((char)0x57); // Start - for (int i = 0; i < 10; i++) { - cmd.append((char)0x0); - } - cmd.append((char)0x1f); // Status - cmd.append((char)0x20); // End - m_device->write(cmd); - m_spidStatusSent = true; - } - } - else - { - QByteArray cmd("p\n"); - m_device->write(cmd); - } + if (m_device && m_device->isOpen() && m_controllerProtocol) { + m_controllerProtocol->update(); } } + diff --git a/plugins/feature/gs232controller/gs232controllerworker.h b/plugins/feature/gs232controller/gs232controllerworker.h index 67041a3fc..263fd6ac0 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.h +++ b/plugins/feature/gs232controller/gs232controllerworker.h @@ -28,6 +28,7 @@ #include "util/messagequeue.h" #include "gs232controllersettings.h" +#include "controllerprotocol.h" class GS232ControllerWorker : public QObject { @@ -81,12 +82,7 @@ private: float m_lastAzimuth; float m_lastElevation; - bool m_spidSetOutstanding; - bool m_spidSetSent; - bool m_spidStatusSent; - - bool m_rotCtlDReadAz; //!< rotctrld returns 'p' responses over two lines - QString m_rotCtlDAz; + ControllerProtocol *m_controllerProtocol; bool handleMessage(const Message& cmd); void applySettings(const GS232ControllerSettings& settings, const QList& settingsKeys, bool force = false); @@ -102,3 +98,4 @@ private slots: }; #endif // INCLUDE_FEATURE_GS232CONTROLLERWORKER_H_ + diff --git a/plugins/feature/gs232controller/gs232protocol.cpp b/plugins/feature/gs232controller/gs232protocol.cpp new file mode 100644 index 000000000..f5f982f78 --- /dev/null +++ b/plugins/feature/gs232controller/gs232protocol.cpp @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include + +#include "gs232protocol.h" + +GS232Protocol::GS232Protocol() +{ +} + +void GS232Protocol::setAzimuth(float azimuth) +{ + QString cmd = QString("M%1\r\n").arg((int)std::round(azimuth), 3, 10, QLatin1Char('0')); + QByteArray data = cmd.toLatin1(); + m_device->write(data); + m_lastAzimuth = azimuth; +} + +void GS232Protocol::setAzimuthElevation(float azimuth, float elevation) +{ + QString cmd = QString("W%1 %2\r\n").arg((int)std::round(azimuth), 3, 10, QLatin1Char('0')).arg((int)std::round(elevation), 3, 10, QLatin1Char('0')); + QByteArray data = cmd.toLatin1(); + m_device->write(data); + ControllerProtocol::setAzimuthElevation(azimuth, elevation); +} + +// Handle data received from controller +void GS232Protocol::readData() +{ + char buf[1024]; + qint64 len; + + while (m_device->canReadLine()) + { + len = m_device->readLine(buf, sizeof(buf)); + if (len != -1) + { + QString response = QString::fromUtf8(buf, len); + // MD-02 can return AZ=-00 EL=-00 and other negative angles + QRegularExpression re("AZ=([-\\d]\\d\\d) *EL=([-\\d]\\d\\d)"); + QRegularExpressionMatch match = re.match(response); + if (match.hasMatch()) + { + QString az = match.captured(1); + QString el = match.captured(2); + //qDebug() << "SPIDProtocol::readData read Az " << az << " El " << el; + reportAzEl(az.toFloat(), el.toFloat()); + } + else if (response == "\r\n") + { + // Ignore + } + else + { + qWarning() << "SPIDProtocol::readData - unexpected GS-232 response \"" << response << "\""; + reportError(QString("Unexpected GS-232 response: %1").arg(response)); + } + } + } +} + +// Request current Az/El from controller +void GS232Protocol::update() +{ + QByteArray cmd("C2\r\n"); + m_device->write(cmd); +} + diff --git a/plugins/feature/gs232controller/gs232protocol.h b/plugins/feature/gs232controller/gs232protocol.h new file mode 100644 index 000000000..499c8db30 --- /dev/null +++ b/plugins/feature/gs232controller/gs232protocol.h @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_GS232PROTOCOL_H_ +#define INCLUDE_FEATURE_GS232PROTOCOL_H_ + +#include "controllerprotocol.h" + +class GS232Protocol : public ControllerProtocol +{ +public: + GS232Protocol(); + void setAzimuth(float azimuth) override; + void setAzimuthElevation(float azimuth, float elevation) override; + void readData() override; + void update() override; +}; + +#endif // INCLUDE_FEATURE_GS232PROTOCOL_H_ + diff --git a/plugins/feature/gs232controller/readme.md b/plugins/feature/gs232controller/readme.md index a023daea1..34cd7794f 100644 --- a/plugins/feature/gs232controller/readme.md +++ b/plugins/feature/gs232controller/readme.md @@ -92,6 +92,21 @@ This can prevent some rotators that have a limited accuracy from making unbenefi If this set to 0, every target azimuth and elevation received by the controller will be send to the rotator. If it is set to 2, then a change in azimuth of +-1 degree from the previous azimuth, would not be sent to the rotator. +

20: Precision

+ +Specifies the number of decimal places used for coordinates and the tolerance setting. +For GS-232 this is fixed to 0. For SPID it is fixed to 1. + +

21: Coordinates

+ +Specifies the coordinate system used by the GUI for entry and display of the position of the rotator. This can be: + +* Az/El - For azimuth and elevation in degrees. +* X/Y 85' - For X/Y coordinates in degrees. 0,0 is zenith. X is positive Southward. Y is positive Eastward. +* X/Y 30' - For X/Y coordinates in degrees. 0,0 is zenith. X is positivie Eastward. Y is positive Northward. + +Equations for translating between these coordinate systems can be found [here](https://ntrs.nasa.gov/citations/19670030005). +

Protocol Implementations

GS-232 Protocol Implementation Notes

diff --git a/plugins/feature/gs232controller/rotctrldprotocol.cpp b/plugins/feature/gs232controller/rotctrldprotocol.cpp new file mode 100644 index 000000000..058a65b0a --- /dev/null +++ b/plugins/feature/gs232controller/rotctrldprotocol.cpp @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "rotctrldprotocol.h" + +RotCtrlDProtocol::RotCtrlDProtocol() : + m_rotCtlDReadAz(false) +{ +} + +void RotCtrlDProtocol::setAzimuthElevation(float azimuth, float elevation) +{ + QString cmd = QString("P %1 %2\n").arg(azimuth).arg(elevation); + QByteArray data = cmd.toLatin1(); + m_device->write(data); + ControllerProtocol::setAzimuthElevation(azimuth, elevation); +} + +// Handle data received from rotctrld +void RotCtrlDProtocol::readData() +{ + char buf[1024]; + qint64 len; + + while (m_device->canReadLine()) + { + len = m_device->readLine(buf, sizeof(buf)); + if (len != -1) + { + QString response = QString::fromUtf8(buf, len).trimmed(); + QRegularExpression rprt("RPRT (-?\\d+)"); + QRegularExpressionMatch matchRprt = rprt.match(response); + QRegularExpression decimal("(-?\\d+.\\d+)"); + QRegularExpressionMatch matchDecimal = decimal.match(response); + if (matchRprt.hasMatch()) + { + // See rig_errcode_e in hamlib rig.h + const QStringList errors = { + "OK", + "Invalid parameter", + "Invalid configuration", + "No memory", + "Not implemented", + "Timeout", + "IO error", + "Internal error", + "Protocol error", + "Command rejected", + "Arg truncated", + "Not available", + "VFO not targetable", + "Bus error", + "Collision on bus", + "NULL rig handled or invalid pointer parameter", + "Invalid VFO", + "Argument out of domain of function" + }; + int rprt = matchRprt.captured(1).toInt(); + if (rprt != 0) + { + qWarning() << "GS232ControllerWorker::readData - rotctld error: " << errors[-rprt]; + // Seem to get a lot of EPROTO errors from rotctld due to extra 00 char in response to GS232 C2 command + // E.g: ./rotctld.exe -m 603 -r com7 -vvvvv + // read_string(): RX 16 characters + // 0000 00 41 5a 3d 31 37 35 20 20 45 4c 3d 30 33 38 0d .AZ=175 EL=038. + // So don't pass these to GUI for now + if (rprt != -8) { + reportError(QString("rotctld error: %1").arg(errors[-rprt])); + } + } + m_rotCtlDReadAz = false; + } + else if (matchDecimal.hasMatch() && !m_rotCtlDReadAz) + { + m_rotCtlDAz = response; + m_rotCtlDReadAz = true; + } + else if (matchDecimal.hasMatch() && m_rotCtlDReadAz) + { + QString az = m_rotCtlDAz; + QString el = response; + m_rotCtlDReadAz = false; + //qDebug() << "GS232ControllerWorker::readData read Az " << az << " El " << el; + reportAzEl(az.toFloat(), el.toFloat()); + } + else + { + qWarning() << "GS232ControllerWorker::readData - Unexpected rotctld response \"" << response << "\""; + reportError(QString("Unexpected rotctld response: %1").arg(response)); + } + } + } +} + +// Request current Az/El from rotctrld +void RotCtrlDProtocol::update() +{ + QByteArray cmd("p\n"); + m_device->write(cmd); +} + diff --git a/plugins/feature/gs232controller/rotctrldprotocol.h b/plugins/feature/gs232controller/rotctrldprotocol.h new file mode 100644 index 000000000..0f9eaffe8 --- /dev/null +++ b/plugins/feature/gs232controller/rotctrldprotocol.h @@ -0,0 +1,36 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_ROTCTRLDPROTOCOL_H_ +#define INCLUDE_FEATURE_ROTCTRLDPROTOCOL_H_ + +#include "controllerprotocol.h" + +class RotCtrlDProtocol : public ControllerProtocol +{ +public: + RotCtrlDProtocol(); + void setAzimuthElevation(float azimuth, float elevation) override; + void readData() override; + void update() override; +private: + bool m_rotCtlDReadAz; //!< rotctrld returns 'p' responses over two lines + QString m_rotCtlDAz; +}; + +#endif // INCLUDE_FEATURE_ROTCTRLDPROTOCOL_H_ + diff --git a/plugins/feature/gs232controller/spidprotocol.cpp b/plugins/feature/gs232controller/spidprotocol.cpp new file mode 100644 index 000000000..2b31eefb8 --- /dev/null +++ b/plugins/feature/gs232controller/spidprotocol.cpp @@ -0,0 +1,124 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include + +#include "spidprotocol.h" + +SPIDProtocol::SPIDProtocol() : + m_spidStatusSent(false), + m_spidSetSent(false), + m_spidSetOutstanding(false) +{ +} + +void SPIDProtocol::setAzimuthElevation(float azimuth, float elevation) +{ + if (!m_spidSetSent && !m_spidStatusSent) + { + QByteArray cmd(13, (char)0); + + cmd[0] = 0x57; // Start + int h = std::round((azimuth + 360.0f) * 2.0f); + cmd[1] = 0x30 | (h / 1000); + cmd[2] = 0x30 | ((h % 1000) / 100); + cmd[3] = 0x30 | ((h % 100) / 10); + cmd[4] = 0x30 | (h % 10); + cmd[5] = 2; // 2 degree per impulse + int v = std::round((elevation + 360.0f) * 2.0f); + cmd[6] = 0x30 | (v / 1000); + cmd[7] = 0x30 | ((v % 1000) / 100); + cmd[8] = 0x30 | ((v % 100) / 10); + cmd[9] = 0x30 | (v % 10); + cmd[10] = 2; // 2 degree per impulse + cmd[11] = 0x2f; // Set cmd + cmd[12] = 0x20; // End + + m_device->write(cmd); + + m_spidSetSent = true; + } + else + { + qDebug() << "SPIDProtocol::setAzimuthElevation: Not sent, waiting for status reply"; + m_spidSetOutstanding = true; + } + ControllerProtocol::setAzimuthElevation(azimuth, elevation); +} + +// Handle data received from controller +void SPIDProtocol::readData() +{ + char buf[1024]; + qint64 len; + + while (m_device->bytesAvailable() >= 12) + { + len = m_device->read(buf, 12); + if ((len == 12) && (buf[0] == 0x57)) + { + double az; + double el; + az = buf[1] * 100.0 + buf[2] * 10.0 + buf[3] + buf[4] / 10.0 - 360.0; + el = buf[6] * 100.0 + buf[7] * 10.0 + buf[8] + buf[9] / 10.0 - 360.0; + //qDebug() << "SPIDProtocol::readData read Az " << az << " El " << el; + reportAzEl(az, el); + if (m_spidStatusSent && m_spidSetSent) { + qDebug() << "SPIDProtocol::readData - m_spidStatusSent and m_spidSetSent set simultaneously"; + } + if (m_spidStatusSent) { + m_spidStatusSent = false; + } + if (m_spidSetSent) { + m_spidSetSent = false; + } + if (m_spidSetOutstanding) + { + m_spidSetOutstanding = false; + setAzimuthElevation(m_lastAzimuth, m_lastElevation); + } + } + else + { + QByteArray bytes(buf, (int)len); + qWarning() << "SPIDProtocol::readData - unexpected SPID rot2prog response \"" << bytes.toHex() << "\""; + reportError(QString("Unexpected SPID rot2prog response: %1").arg(bytes.toHex().data())); + } + } +} + +// Request current Az/El from controller +void SPIDProtocol::update() +{ + // Don't send a new status command, if waiting for a previous reply + if (!m_spidSetSent && !m_spidStatusSent) + { + // Status + QByteArray cmd; + cmd.append((char)0x57); // Start + for (int i = 0; i < 10; i++) { + cmd.append((char)0x0); + } + cmd.append((char)0x1f); // Status + cmd.append((char)0x20); // End + m_device->write(cmd); + m_spidStatusSent = true; + } +} + diff --git a/plugins/feature/gs232controller/spidprotocol.h b/plugins/feature/gs232controller/spidprotocol.h new file mode 100644 index 000000000..17b8a82df --- /dev/null +++ b/plugins/feature/gs232controller/spidprotocol.h @@ -0,0 +1,37 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2023 Jon Beniston, M7RCE // +// // +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_FEATURE_SPIDPROTOCOL_H_ +#define INCLUDE_FEATURE_SPIDPROTOCOL_H_ + +#include "controllerprotocol.h" + +class SPIDProtocol : public ControllerProtocol +{ +public: + SPIDProtocol(); + void setAzimuthElevation(float azimuth, float elevation) override; + void readData() override; + void update() override; +private: + bool m_spidStatusSent; + bool m_spidSetSent; + bool m_spidSetOutstanding; +}; + +#endif // INCLUDE_FEATURE_SPIDPROTOCOL_H_ + diff --git a/sdrbase/channel/channelwebapiutils.cpp b/sdrbase/channel/channelwebapiutils.cpp index bfdc3e257..685c0371f 100644 --- a/sdrbase/channel/channelwebapiutils.cpp +++ b/sdrbase/channel/channelwebapiutils.cpp @@ -34,6 +34,7 @@ #include "maincore.h" #include "device/deviceset.h" #include "device/deviceapi.h" +#include "channel/channelapi.h" #include "channel/channelutils.h" #include "dsp/devicesamplesource.h" #include "dsp/devicesamplesink.h" @@ -219,7 +220,83 @@ bool ChannelWebAPIUtils::getFeatureReport(unsigned int featureSetIndex, unsigned if (httpRC/100 != 2) { - qWarning("ChannelWebAPIUtils::getFeatureReport: get feature settings error %d: %s", + qWarning("ChannelWebAPIUtils::getFeatureReport: get feature report error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + + return true; +} + +bool ChannelWebAPIUtils::getChannelSettings(unsigned int deviceIndex, unsigned int channelIndex, SWGSDRangel::SWGChannelSettings &channelSettingsResponse, ChannelAPI *&channel) +{ + QString errorResponse; + int httpRC; + DeviceSet *deviceSet; + + // Get current channel settings + std::vector deviceSets = MainCore::instance()->getDeviceSets(); + if (deviceIndex < deviceSets.size()) + { + deviceSet = deviceSets[deviceIndex]; + if (channelIndex < (unsigned int) deviceSet->getNumberOfChannels()) + { + channel = deviceSet->getChannelAt(channelIndex); + httpRC = channel->webapiSettingsGet(channelSettingsResponse, errorResponse); + } + else + { + qDebug() << "ChannelWebAPIUtils::getChannelSettings: no channel " << deviceIndex << ":" << channelIndex; + return false; + } + } + else + { + qDebug() << "ChannelWebAPIUtils::getChannelSettings: no device " << deviceIndex; + return false; + } + + if (httpRC/100 != 2) + { + qWarning("ChannelWebAPIUtils::getChannelSettings: get channel settings error %d: %s", + httpRC, qPrintable(errorResponse)); + return false; + } + + return true; +} + +bool ChannelWebAPIUtils::getChannelReport(unsigned int deviceIndex, unsigned int channelIndex, SWGSDRangel::SWGChannelReport &channelReport) +{ + QString errorResponse; + int httpRC; + DeviceSet *deviceSet; + + // Get channel report + std::vector deviceSets = MainCore::instance()->getDeviceSets(); + if (deviceIndex < deviceSets.size()) + { + deviceSet = deviceSets[deviceIndex]; + if (channelIndex < (unsigned int) deviceSet->getNumberOfChannels()) + { + ChannelAPI *channel = deviceSet->getChannelAt(channelIndex); + httpRC = channel->webapiReportGet(channelReport, errorResponse); + } + else + { + qDebug() << "ChannelWebAPIUtils::getChannelReport: no channel " << deviceIndex << ":" << channelIndex; + return false; + } + } + else + { + qDebug() << "ChannelWebAPIUtils::getChannelReport: no device set " << deviceIndex; + return false; + } + + if (httpRC/100 != 2) + { + qWarning("ChannelWebAPIUtils::getChannelReport: get channel report error %d: %s", httpRC, qPrintable(errorResponse)); return false; } @@ -1193,6 +1270,22 @@ bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsign } } +bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value) +{ + SWGSDRangel::SWGFeatureSettings featureSettingsResponse; + Feature *feature; + + if (getFeatureSettings(featureSetIndex, featureIndex, featureSettingsResponse, feature)) + { + QJsonObject *jsonObj = featureSettingsResponse.asJsonObject(); + return WebAPIUtils::getSubObjectDouble(*jsonObj, setting, value); + } + else + { + return false; + } +} + bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, QString &value) { SWGSDRangel::SWGFeatureSettings featureSettingsResponse; @@ -1209,6 +1302,54 @@ bool ChannelWebAPIUtils::getFeatureSetting(unsigned int featureSetIndex, unsign } } +bool ChannelWebAPIUtils::getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, int &value) +{ + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + ChannelAPI *channel; + + if (getChannelSettings(deviceSetIndex, channelIndex, channelSettingsResponse, channel)) + { + QJsonObject *jsonObj = channelSettingsResponse.asJsonObject(); + return WebAPIUtils::getSubObjectInt(*jsonObj, setting, value); + } + else + { + return false; + } +} + +bool ChannelWebAPIUtils::getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, double &value) +{ + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + ChannelAPI *channel; + + if (getChannelSettings(deviceSetIndex, channelIndex, channelSettingsResponse, channel)) + { + QJsonObject *jsonObj = channelSettingsResponse.asJsonObject(); + return WebAPIUtils::getSubObjectDouble(*jsonObj, setting, value); + } + else + { + return false; + } +} + +bool ChannelWebAPIUtils::getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, QString &value) +{ + SWGSDRangel::SWGChannelSettings channelSettingsResponse; + ChannelAPI *channel; + + if (getChannelSettings(deviceSetIndex, channelIndex, channelSettingsResponse, channel)) + { + QJsonObject *jsonObj = channelSettingsResponse.asJsonObject(); + return WebAPIUtils::getSubObjectString(*jsonObj, setting, value); + } + else + { + return false; + } +} + bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value) { SWGSDRangel::SWGFeatureReport featureReport; @@ -1231,6 +1372,28 @@ bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, uns return false; } +bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, double &value) +{ + SWGSDRangel::SWGFeatureReport featureReport; + + if (getFeatureReport(featureSetIndex, featureIndex, featureReport)) + { + // Get value of requested key + QJsonObject *jsonObj = featureReport.asJsonObject(); + if (WebAPIUtils::getSubObjectDouble(*jsonObj, key, value)) + { + // Done + return true; + } + else + { + qWarning("ChannelWebAPIUtils::getFeatureReportValue: no key %s in feature report", qPrintable(key)); + return false; + } + } + return false; +} + bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value) { SWGSDRangel::SWGFeatureReport featureReport; @@ -1252,3 +1415,71 @@ bool ChannelWebAPIUtils::getFeatureReportValue(unsigned int featureSetIndex, uns } return false; } + + +bool ChannelWebAPIUtils::getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, int &value) +{ + SWGSDRangel::SWGChannelReport channelReport; + + if (getChannelReport(deviceIndex, channelIndex, channelReport)) + { + // Get value of requested key + QJsonObject *jsonObj = channelReport.asJsonObject(); + if (WebAPIUtils::getSubObjectInt(*jsonObj, key, value)) + { + // Done + return true; + } + else + { + qWarning("ChannelWebAPIUtils::getChannelReportValue: no key %s in channel report", qPrintable(key)); + return false; + } + } + return false; +} + +bool ChannelWebAPIUtils::getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, double &value) +{ + SWGSDRangel::SWGChannelReport channelReport; + + if (getChannelReport(deviceIndex, channelIndex, channelReport)) + { + // Get value of requested key + QJsonObject *jsonObj = channelReport.asJsonObject(); + if (WebAPIUtils::getSubObjectDouble(*jsonObj, key, value)) + { + // Done + return true; + } + else + { + qWarning("ChannelWebAPIUtils::getChannelReportValue: no key %s in channel report", qPrintable(key)); + return false; + } + } + return false; +} + +bool ChannelWebAPIUtils::getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, QString &value) +{ + SWGSDRangel::SWGChannelReport channelReport; + + if (getChannelReport(deviceIndex, channelIndex, channelReport)) + { + // Get value of requested key + QJsonObject *jsonObj = channelReport.asJsonObject(); + if (WebAPIUtils::getSubObjectString(*jsonObj, key, value)) + { + // Done + return true; + } + else + { + qWarning("ChannelWebAPIUtils::getChannelReportValue: no key %s in channel report", qPrintable(key)); + return false; + } + } + return false; +} + diff --git a/sdrbase/channel/channelwebapiutils.h b/sdrbase/channel/channelwebapiutils.h index ee95f21d4..1f2f8f9b4 100644 --- a/sdrbase/channel/channelwebapiutils.h +++ b/sdrbase/channel/channelwebapiutils.h @@ -24,11 +24,14 @@ #include "SWGDeviceReport.h" #include "SWGFeatureSettings.h" #include "SWGFeatureReport.h" +#include "SWGChannelSettings.h" +#include "SWGChannelReport.h" #include "export.h" class DeviceSet; class Feature; +class ChannelAPI; class SDRBASE_API ChannelWebAPIUtils { @@ -67,15 +70,26 @@ public: static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, const QString &value); static bool patchFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double value); static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, int &value); + static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, double &value); static bool getFeatureSetting(unsigned int featureSetIndex, unsigned int featureIndex, const QString &setting, QString &value); + static bool getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, int &value); + static bool getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, double &value); + static bool getChannelSetting(unsigned int deviceSetIndex, unsigned int channelIndex, const QString &setting, QString &value); static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, int &value); - static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value); + static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, double &value); + static bool getFeatureReportValue(unsigned int featureSetIndex, unsigned int featureIndex, const QString &key, QString &value); + static bool getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, int &value); + static bool getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, double &value); + static bool getChannelReportValue(unsigned int deviceIndex, unsigned int channelIndex, const QString &key, QString &value); protected: static bool getDeviceSettings(unsigned int deviceIndex, SWGSDRangel::SWGDeviceSettings &deviceSettingsResponse, DeviceSet *&deviceSet); static bool getDeviceReport(unsigned int deviceIndex, SWGSDRangel::SWGDeviceReport &deviceReport); static bool getFeatureSettings(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureSettings &featureSettingsResponse, Feature *&feature); static bool getFeatureReport(unsigned int featureSetIndex, unsigned int featureIndex, SWGSDRangel::SWGFeatureReport &featureReport); + static bool getChannelSettings(unsigned int deviceIndex, unsigned int channelIndex, SWGSDRangel::SWGChannelSettings &channelSettingsResponse, ChannelAPI *&channel); + static bool getChannelReport(unsigned int deviceIndex, unsigned int channelIndex, SWGSDRangel::SWGChannelReport &channelReport); static QString getDeviceHardwareId(unsigned int deviceIndex); }; #endif // SDRBASE_CHANNEL_CHANNELWEBAPIUTILS_H_ + diff --git a/sdrbase/util/astronomy.cpp b/sdrbase/util/astronomy.cpp index c3d26e051..0c1ac131b 100644 --- a/sdrbase/util/astronomy.cpp +++ b/sdrbase/util/astronomy.cpp @@ -286,6 +286,183 @@ RADec Astronomy::azAltToRaDec(AzAlt aa, double latitude, double longitude, QDate return rd; } +// https://ntrs.nasa.gov/api/citations/19670030005/downloads/19670030005.pdf +// X85 is positive Southward +// Y85 is positive Eastward +// X30 is positive Eastward +// Y30 is positive Northward +// atan2 range is (-pi,pi], we want az (0,360], so need to add pi +void Astronomy::azAltToXY85(AzAlt aa, double& x, double& y) +{ + if (aa.alt == 90.0) + { + x = 0.0; + y = 0.0; + //qDebug() << "azAltToXY85" << aa.az << aa.alt << x << y; + return; + } + double az = aa.az; + double el = aa.alt; + if (az >= 360.0) { + az -= 360.0; + } + if (el > 90.0) + { + el = 180.0 - el; + if (az >= 180.0) { + az = az - 180.0; + } else { + az = az + 180.0; + } + } + double azr = Units::degreesToRadians(az); + double elr = Units::degreesToRadians(el); + y = Units::radiansToDegrees(asin(cos(elr) * sin(azr))); + if (az == 0.0) + { + // cot(0) == Inf + if ((az == 90.0) || (az == 270.0)) { + x = 0.0; + } else if ((az > 90.0f) && (az < 270.0)) { + x = 90.0; + } else { + x = -90.0; + } + } + else + { + // cot(x)=1/tan(x)=cos(x)/sin(x) + x = Units::radiansToDegrees(atan(-(cos(elr)/sin(elr)) * cos(azr))); + } + + //qDebug() << "azAltToXY85" << aa.az << aa.alt << x << y; +} + +void Astronomy::azAltToXY30(AzAlt aa, double& x, double& y) +{ + if (aa.alt == 90.0) + { + x = 0.0; + y = 0.0; + //qDebug() << "azAltToXY30" << aa.az << aa.alt << x << y; + return; + } + double az = aa.az; + double el = aa.alt; + if (az >= 360.0) { + az -= 360.0; + } + if (el > 90.0) + { + el = 180.0 - el; + if (az >= 180.0) { + az = az - 180.0; + } else { + az = az + 180.0; + } + } + double azr = Units::degreesToRadians(az); + double elr = Units::degreesToRadians(el); + y = Units::radiansToDegrees(asin(cos(elr) * cos(azr))); + if (el == 0.0) + { + if ((az == 0.0) || (az == 180.0)) { + x = 0.0; + } else if ((az >= 0.0f) && (az <= 180.0)) { + x = 90.0; + } else { + x = -90.0; + } + } + else + { + x = Units::radiansToDegrees(atan((cos(elr)/sin(elr)) * sin(azr))); + } + //qDebug() << "azAltToXY30" << aa.az << aa.alt << x << y; +} + +AzAlt Astronomy::xy85ToAzAlt(double x, double y) +{ + AzAlt aa; + if ((x == 0.0) && (y == 0.0)) + { + aa.az = 0.0; + aa.alt = 90.0; + //qDebug() << "xy85ToAzAlt" << x << y << aa.az << aa.alt; + return aa; + } + double xr = Units::degreesToRadians(x); + double yr = Units::degreesToRadians(y); + double elr = asin(cos(yr) * cos(xr)); + double azr; + if (x == 0.0) + { + // 1/sin(x) == Inf + azr = y >= 0.0 ? M_PI/2.0 : 2.0*M_PI*3.0/4.0; + } + else if (y == 90.0) + { + // tan(90) == Inf + azr = M_PI/2.0; + } + else if (y == -90.0) + { + // tan(-90) == Inf + azr = 2.0*M_PI*3.0/4.0; + } + else + { + // atan2(y,x) = atan(y/x) + azr = atan2(-tan(yr), sin(xr)) + M_PI; + } + aa.az = Units::radiansToDegrees(azr); + aa.alt = Units::radiansToDegrees(elr); + //qDebug() << "xy85ToAzAlt" << x << y << aa.az << aa.alt; + return aa; +} + +AzAlt Astronomy::xy30ToAzAlt(double x, double y) +{ + AzAlt aa; + if ((x == 0.0) && (y == 0.0)) + { + aa.az = 0.0; + aa.alt = 90.0; + //qDebug() << "xy30ToAzAlt" << x << y << aa.az << aa.alt; + return aa; + } + double xr = Units::degreesToRadians(x); + double yr = Units::degreesToRadians(y); + double elr = asin(cos(yr) * cos(xr)); + double azr; + if (y == 0.0) + { + // cot(0) == Inf + azr = x >= 0.0 ? M_PI/2.0 : 2.0*M_PI*3.0/4.0; + } + else if (y == 90.0) + { + // tan(90) == Inf + azr = 0.0; + } + else if (y == -90.0) + { + // tan(-90) == Inf + azr = M_PI; + } + else + { + azr = atan2(sin(xr), tan(yr)); + if (azr < 0.0) { + azr += 2.0*M_PI; + } + } + aa.az = Units::radiansToDegrees(azr); + aa.alt = Units::radiansToDegrees(elr); + //qDebug() << "xy30ToAzAlt" << x << y << aa.az << aa.alt; + return aa; +} + // Needs to work for negative a double Astronomy::modulo(double a, double b) { @@ -3707,3 +3884,4 @@ static int eraEpv00(double date1, double date2, /* Finished. */ } + diff --git a/sdrbase/util/astronomy.h b/sdrbase/util/astronomy.h index 2fcf10d16..5d26cbfc0 100644 --- a/sdrbase/util/astronomy.h +++ b/sdrbase/util/astronomy.h @@ -49,6 +49,11 @@ public: static AzAlt raDecToAzAlt(RADec rd, double latitude, double longitude, QDateTime dt, bool j2000=true); static RADec azAltToRaDec(AzAlt aa, double latitude, double longitude, QDateTime dt); + static void azAltToXY85(AzAlt aa, double& x, double& y); + static void azAltToXY30(AzAlt aa, double& x, double& y); + static AzAlt xy85ToAzAlt(double x, double y); + static AzAlt xy30ToAzAlt(double x, double y); + static double localSiderealTime(QDateTime dateTime, double longitude); static void sunPosition(AzAlt& aa, RADec& rd, double latitude, double longitude, QDateTime dt); diff --git a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml index f3a201aa7..b555a959e 100644 --- a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml +++ b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml @@ -50,7 +50,13 @@ GS232ControllerSettings: type: number format: float protocol: - description: (0 GS-232, 1 SPID rot2prog) + description: (0 GS-232, 1 SPID rot2prog, 2 rotcltd, 3 DFM) + type: integer + precision: + description: Precision of azimuth and elevation values + type: integer + coordinates: + description: (0 Az/El, 1 X/Y 85, 2 X/Y 30) type: integer title: type: string diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp index b6f4c3056..954e40380 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp @@ -60,6 +60,10 @@ SWGGS232ControllerSettings::SWGGS232ControllerSettings() { m_tolerance_isSet = false; protocol = 0; m_protocol_isSet = false; + precision = 0; + m_precision_isSet = false; + coordinates = 0; + m_coordinates_isSet = false; title = nullptr; m_title_isSet = false; rgb_color = 0; @@ -116,6 +120,10 @@ SWGGS232ControllerSettings::init() { m_tolerance_isSet = false; protocol = 0; m_protocol_isSet = false; + precision = 0; + m_precision_isSet = false; + coordinates = 0; + m_coordinates_isSet = false; title = new QString(""); m_title_isSet = false; rgb_color = 0; @@ -158,6 +166,8 @@ SWGGS232ControllerSettings::cleanup() { + + if(title != nullptr) { delete title; } @@ -217,6 +227,10 @@ SWGGS232ControllerSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&protocol, pJson["protocol"], "qint32", ""); + ::SWGSDRangel::setValue(&precision, pJson["precision"], "qint32", ""); + + ::SWGSDRangel::setValue(&coordinates, pJson["coordinates"], "qint32", ""); + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); @@ -297,6 +311,12 @@ SWGGS232ControllerSettings::asJsonObject() { if(m_protocol_isSet){ obj->insert("protocol", QJsonValue(protocol)); } + if(m_precision_isSet){ + obj->insert("precision", QJsonValue(precision)); + } + if(m_coordinates_isSet){ + obj->insert("coordinates", QJsonValue(coordinates)); + } if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } @@ -485,6 +505,26 @@ SWGGS232ControllerSettings::setProtocol(qint32 protocol) { this->m_protocol_isSet = true; } +qint32 +SWGGS232ControllerSettings::getPrecision() { + return precision; +} +void +SWGGS232ControllerSettings::setPrecision(qint32 precision) { + this->precision = precision; + this->m_precision_isSet = true; +} + +qint32 +SWGGS232ControllerSettings::getCoordinates() { + return coordinates; +} +void +SWGGS232ControllerSettings::setCoordinates(qint32 coordinates) { + this->coordinates = coordinates; + this->m_coordinates_isSet = true; +} + QString* SWGGS232ControllerSettings::getTitle() { return title; @@ -618,6 +658,12 @@ SWGGS232ControllerSettings::isSet(){ if(m_protocol_isSet){ isObjectUpdated = true; break; } + if(m_precision_isSet){ + isObjectUpdated = true; break; + } + if(m_coordinates_isSet){ + isObjectUpdated = true; break; + } if(title && *title != QString("")){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h index 4f12f326e..eff95dede 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h @@ -91,6 +91,12 @@ public: qint32 getProtocol(); void setProtocol(qint32 protocol); + qint32 getPrecision(); + void setPrecision(qint32 precision); + + qint32 getCoordinates(); + void setCoordinates(qint32 coordinates); + QString* getTitle(); void setTitle(QString* title); @@ -167,6 +173,12 @@ private: qint32 protocol; bool m_protocol_isSet; + qint32 precision; + bool m_precision_isSet; + + qint32 coordinates; + bool m_coordinates_isSet; + QString* title; bool m_title_isSet; From f7ed6622381c57ce38a715e989682884c1b60a4d Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:53:51 +0100 Subject: [PATCH 2/6] Display rotator on Star Tracker and Satellite Tracker polar charts for #1641. --- plugins/feature/satellitetracker/readme.md | 5 +- .../satellitetracker/satellitetrackergui.cpp | 121 +++- .../satellitetracker/satellitetrackergui.h | 2 + .../satellitetrackersettings.cpp | 251 ++++---- .../satellitetrackersettings.h | 1 + .../satellitetrackersettingsdialog.cpp | 2 + .../satellitetrackersettingsdialog.ui | 195 ++++--- .../satellitetracker/satellitetrackersgp4.cpp | 2 +- plugins/feature/startracker/readme.md | 2 + .../feature/startracker/startrackergui.cpp | 114 +++- plugins/feature/startracker/startrackergui.h | 2 + .../startracker/startrackersettings.cpp | 9 + .../feature/startracker/startrackersettings.h | 1 + .../startracker/startrackersettingsdialog.cpp | 4 + .../startracker/startrackersettingsdialog.ui | 539 +++++++++--------- 15 files changed, 787 insertions(+), 463 deletions(-) diff --git a/plugins/feature/satellitetracker/readme.md b/plugins/feature/satellitetracker/readme.md index d079d1805..d0386545c 100644 --- a/plugins/feature/satellitetracker/readme.md +++ b/plugins/feature/satellitetracker/readme.md @@ -116,7 +116,10 @@ On the display tab, you can set: * The default frequency in MHz that is used for calculating Doppler and free space path loss in the Satellite Data table. * The units used to display azimuth and elevation to the target satellite. This can be in degrees, minutes and seconds or decimal degrees. * The number of points used for ground tracks on the map. More points result in smoother tracks, but require more processing. -* Whether times are displayrf in the local time zone or UTC. +* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will +only be displayed if the source in the Rotator Controller is set to this Satellite Tracker and Track is enabled. +* The format used for displaying dates. E.g. yyyy/MM/dd +* Whether times are displayed in the local time zone or UTC. * Whether to draw the satellites on the map.

9: Latitude

diff --git a/plugins/feature/satellitetracker/satellitetrackergui.cpp b/plugins/feature/satellitetracker/satellitetrackergui.cpp index 7c599294f..817498704 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.cpp +++ b/plugins/feature/satellitetracker/satellitetrackergui.cpp @@ -28,8 +28,10 @@ #include "device/deviceapi.h" #include "device/deviceset.h" +#include "channel/channelwebapiutils.h" #include "feature/featureset.h" #include "feature/featureuiset.h" +#include "feature/featureutils.h" #include "feature/featurewebapiutils.h" #include "gui/basicfeaturesettingsdialog.h" #include "gui/dialogpositioner.h" @@ -295,6 +297,8 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(1000); + connect(&m_redrawTimer, &QTimer::timeout, this, &SatelliteTrackerGUI::plotChart); + // Intialise charts m_emptyChart.layout()->setContentsMargins(0, 0, 0, 0); m_emptyChart.setMargins(QMargins(1, 1, 1, 1)); @@ -534,6 +538,7 @@ void SatelliteTrackerGUI::on_displaySettings_clicked() m_settingsKeys.append("defaultFrequency"); m_settingsKeys.append("azElUnits"); m_settingsKeys.append("groundTrackPoints"); + m_settingsKeys.append("drawRotators"); m_settingsKeys.append("dateFormat"); m_settingsKeys.append("utc"); m_settingsKeys.append("tles"); @@ -792,6 +797,26 @@ static double interpolate(double x0, double y0, double x1, double y1, double x) return (y0*(x1-x) + y1*(x-x0)) / (x1-x0); } +// Reduce az/el range from 450,180 to 360,90 +void SatelliteTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const +{ + if (elevation > 90.0) + { + elevation = 180.0 - elevation; + if (azimuth < 180.0) { + azimuth += 180.0; + } else { + azimuth -= 180.0; + } + } + if (azimuth > 360.0) { + azimuth -= 360.0f; + } + if (azimuth == 0) { + azimuth = 360.0; + } +} + // Plot pass in polar coords void SatelliteTrackerGUI::plotPolarChart() { @@ -916,6 +941,86 @@ void SatelliteTrackerGUI::plotPolarChart() series[i]->attachAxis(radialAxis); } + int redrawTime = 0; + + if (m_settings.m_drawRotators != SatelliteTrackerSettings::NO_ROTATORS) + { + // Plot rotator position + QString ourSourceName = QString("F0:%1 %2").arg(m_satelliteTracker->getIndexInFeatureSet()).arg(m_satelliteTracker->getIdentifier()); // Only one feature set in practice? + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + for (int featureSetIndex = 0; featureSetIndex < featureSets.size(); featureSetIndex++) + { + FeatureSet *featureSet = featureSets[featureSetIndex]; + for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++) + { + Feature *feature = featureSet->getFeatureAt(featureIndex); + if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller")) + { + QString source; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller + int track = 0; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track); + if ((m_settings.m_drawRotators == SatelliteTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track)) + { + int onTarget = 0; + ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget); + + if (!onTarget) + { + // Target azimuth red dotted line + double targetAzimuth, targetElevation; + bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth); + bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation); + if (targetAzimuthOk && targetElevationOk) + { + limitAzElRange(targetAzimuth, targetElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color(255, 0, 0, 150); + QPen pen(color); + rotatorSeries->setPen(pen); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(targetAzimuth, 90-targetElevation); + m_polarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + + // Current azimuth line. Yellow while off target, green on target. + double currentAzimuth, currentElevation; + bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth); + bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation); + if (currentAzimuthOk && currentElevationOk) + { + limitAzElRange(currentAzimuth, currentElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color; + if (onTarget) { + color = QColor(0, 255, 0, 150); + } else { + color = QColor(255, 255, 0, 150); + } + rotatorSeries->setPen(QPen(color)); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(currentAzimuth, 90-currentElevation); + m_polarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + } + } + } + } + // Create series with single point, so we can plot time of AOS QLineSeries *aosSeries = new QLineSeries(); aosSeries->append(polarSeries->at(0)); @@ -967,7 +1072,8 @@ void SatelliteTrackerGUI::plotPolarChart() if ((currentTime >= pass.m_aos) && (currentTime <= pass.m_los)) { // Create series with single point, so we can plot current time - QLineSeries *nowSeries = new QLineSeries(); + QScatterSeries *nowSeries = new QScatterSeries(); + nowSeries->setMarkerSize(3); // Find closest point to current time int idx = std::round(polarSeries->count() * (currentTime.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch()) / (pass.m_los.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch())); @@ -978,8 +1084,16 @@ void SatelliteTrackerGUI::plotPolarChart() m_polarChart->addSeries(nowSeries); nowSeries->attachAxis(angularAxis); nowSeries->attachAxis(radialAxis); - // Redraw in 5 seconds (call plotChart, incase user selects a different chart) - QTimer::singleShot(5000, this, &SatelliteTrackerGUI::plotChart); + if (!redrawTime) { + redrawTime = 5000; + } + } + + if (redrawTime > 0) + { + // Redraw to show updated satellite position or rotator position + m_redrawTimer.setSingleShot(true); + m_redrawTimer.start(redrawTime); } delete polarSeries; @@ -1567,3 +1681,4 @@ void SatelliteTrackerGUI::makeUIConnections() QObject::connect(ui->satTable->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &SatelliteTrackerGUI::on_satTableHeader_sortIndicatorChanged); QObject::connect(ui->deviceFeatureSelect, qOverload(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged); } + diff --git a/plugins/feature/satellitetracker/satellitetrackergui.h b/plugins/feature/satellitetracker/satellitetrackergui.h index b12382582..910226c1a 100644 --- a/plugins/feature/satellitetracker/satellitetrackergui.h +++ b/plugins/feature/satellitetracker/satellitetrackergui.h @@ -82,6 +82,7 @@ private: QChart m_emptyChart; QChart *m_lineChart; QPolarChart *m_polarChart; + QTimer m_redrawTimer; QDateTime m_nextTargetAOS; QDateTime m_nextTargetLOS; @@ -138,6 +139,7 @@ private: void updateFileInputList(); void updateMapList(); void makeUIConnections(); + void limitAzElRange(double& azimuth, double& elevation) const; private slots: void onMenuDialogCalled(const QPoint &p); diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.cpp b/plugins/feature/satellitetracker/satellitetrackersettings.cpp index ebf1d6267..648de91e7 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettings.cpp @@ -79,6 +79,7 @@ void SatelliteTrackerSettings::resetToDefaults() m_dateTimeSelect = NOW; m_mapFeature = ""; m_fileInputDevice = ""; + m_drawRotators = MATCHING_TARGET; m_workspaceIndex = 0; m_columnSort = -1; m_columnSortOrder = Qt::AscendingOrder; @@ -142,6 +143,7 @@ QByteArray SatelliteTrackerSettings::serialize() const s.writeBlob(46, m_geometryBytes); s.writeS32(47, m_columnSort); s.writeS32(48, (int)m_columnSortOrder); + s.writeS32(49, (int)m_drawRotators); for (int i = 0; i < SAT_COL_COLUMNS; i++) { s.writeS32(100 + i, m_columnIndexes[i]); @@ -237,6 +239,7 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data) d.readBlob(46, &m_geometryBytes); d.readS32(47, &m_columnSort, -1); d.readS32(48, (int *)&m_columnSortOrder, (int)Qt::AscendingOrder); + d.readS32(49, (int*)&m_drawRotators, (int)MATCHING_TARGET); for (int i = 0; i < SAT_COL_COLUMNS; i++) { d.readS32(100 + i, &m_columnIndexes[i], i); @@ -398,6 +401,15 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("dopplerPeriod")) { m_dopplerPeriod = settings.m_dopplerPeriod; } + if (settingsKeys.contains("predictionPeriod")) { + m_predictionPeriod = settings.m_predictionPeriod; + } + if (settingsKeys.contains("passStartTime")) { + m_passStartTime = settings.m_passStartTime; + } + if (settingsKeys.contains("passFinishTime")) { + m_passFinishTime = settings.m_passFinishTime; + } if (settingsKeys.contains("defaultFrequency")) { m_defaultFrequency = settings.m_defaultFrequency; } @@ -410,23 +422,59 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("aosSpeech")) { m_aosSpeech = settings.m_aosSpeech; } - if (settingsKeys.contains("aosCommand")) { - m_aosCommand = settings.m_aosCommand; - } if (settingsKeys.contains("losSpeech")) { m_losSpeech = settings.m_losSpeech; } + if (settingsKeys.contains("aosCommand")) { + m_aosCommand = settings.m_aosCommand; + } if (settingsKeys.contains("losCommand")) { m_losCommand = settings.m_losCommand; } - if (settingsKeys.contains("predictionPeriod")) { - m_predictionPeriod = settings.m_predictionPeriod; + if (settingsKeys.contains("chartsDarkTheme")) { + m_chartsDarkTheme = settings.m_chartsDarkTheme; } - if (settingsKeys.contains("passStartTime")) { - m_passStartTime = settings.m_passStartTime; + if (settingsKeys.contains("deviceSettings")) { + m_deviceSettings = settings.m_deviceSettings; } - if (settingsKeys.contains("passFinishTime")) { - m_passFinishTime = settings.m_passFinishTime; + if (settingsKeys.contains("replayEnabled")) { + m_replayEnabled = settings.m_replayEnabled; + } + if (settingsKeys.contains("replayStartDateTime")) { + m_replayStartDateTime = settings.m_replayStartDateTime; + } + if (settingsKeys.contains("sendTimeToMap")) { + m_sendTimeToMap = settings.m_sendTimeToMap; + } + if (settingsKeys.contains("dateTimeSelect")) { + m_dateTimeSelect = settings.m_dateTimeSelect; + } + if (settingsKeys.contains("mapFeature")) { + m_mapFeature = settings.m_mapFeature; + } + if (settingsKeys.contains("fileInputDevice")) { + m_fileInputDevice = settings.m_fileInputDevice; + } + if (settingsKeys.contains("drawRotators")) { + m_drawRotators = settings.m_drawRotators; + } + if (settingsKeys.contains("columnSort")) { + m_columnSort = settings.m_columnSort; + } + if (settingsKeys.contains("columnSortOrder")) { + m_columnSortOrder = settings.m_columnSortOrder; + } + if (settingsKeys.contains("columnIndexes")) + { + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + m_columnIndexes[i] = settings.m_columnIndexes[i]; + } + } + if (settingsKeys.contains("columnSizes")) + { + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + m_columnSizes[i] = settings.m_columnSizes[i]; + } } if (settingsKeys.contains("title")) { m_title = settings.m_title; @@ -449,44 +497,8 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co if (settingsKeys.contains("reverseAPIFeatureIndex")) { m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex; } - if (settingsKeys.contains("chartsDarkTheme")) { - m_chartsDarkTheme = settings.m_chartsDarkTheme; - } - if (settingsKeys.contains("replayEnabled")) { - m_replayEnabled = settings.m_replayEnabled; - } - if (settingsKeys.contains("replayStartDateTime")) { - m_replayStartDateTime = settings.m_replayStartDateTime; - } - if (settingsKeys.contains("sendTimeToMap")) { - m_sendTimeToMap = settings.m_sendTimeToMap; - } - if (settingsKeys.contains("dateTimeSelect")) { - m_dateTimeSelect = settings.m_dateTimeSelect; - } - if (settingsKeys.contains("columnSort")) { - m_columnSort = settings.m_columnSort; - } - if (settingsKeys.contains("columnSortOrder")) { - m_columnSortOrder = settings.m_columnSortOrder; - } - - if (settingsKeys.contains("columnIndexes")) - { - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - m_columnIndexes[i] = settings.m_columnIndexes[i]; - } - } - - if (settingsKeys.contains("columnSizes")) - { - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - m_columnSizes[i] = settings.m_columnSizes[i]; - } - } - - if (settingsKeys.contains("deviceSettings")) { - m_deviceSettings = settings.m_deviceSettings; + if (settingsKeys.contains("workspaceIndex")) { + m_workspaceIndex = settings.m_workspaceIndex; } } @@ -503,7 +515,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("heightAboveSeaLevel") || force) { ostr << " m_heightAboveSeaLevel: " << m_heightAboveSeaLevel; } - + if (settingsKeys.contains("target") || force) { + ostr << " m_target: " << m_target.toStdString(); + } if (settingsKeys.contains("satellites") || force) { ostr << "m_satellites:"; @@ -512,7 +526,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys ostr << " " << satellite.toStdString(); } } - if (settingsKeys.contains("tles") || force) { ostr << " m_tles:"; @@ -521,7 +534,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys ostr << " " << tle.toStdString(); } } - if (settingsKeys.contains("dateTime") || force) { ostr << " m_dateTime: " << m_dateTime.toStdString(); } @@ -555,6 +567,15 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("dopplerPeriod") || force) { ostr << " m_dopplerPeriod: " << m_dopplerPeriod; } + if (settingsKeys.contains("predictionPeriod") || force) { + ostr << " m_predictionPeriod: " << m_predictionPeriod; + } + if (settingsKeys.contains("passStartTime") || force) { + ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString(); + } + if (settingsKeys.contains("passFinishTime") || force) { + ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString(); + } if (settingsKeys.contains("defaultFrequency") || force) { ostr << " m_defaultFrequency: " << m_defaultFrequency; } @@ -576,14 +597,68 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("losCommand") || force) { ostr << " m_losCommand: " << m_losCommand.toStdString(); } - if (settingsKeys.contains("predictionPeriod") || force) { - ostr << " m_predictionPeriod: " << m_predictionPeriod; + if (settingsKeys.contains("chartsDarkTheme") || force) { + ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme; } - if (settingsKeys.contains("passStartTime") || force) { - ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString(); + if (settingsKeys.contains("deviceSettings")) + { + ostr << "m_deviceSettings: ["; + + for (auto deviceSettingList : m_deviceSettings) + { + ostr << "["; + + for (auto deviceSettings : *deviceSettingList) { + deviceSettings->getDebugString(ostr); + } + + ostr << "]"; + } + + ostr << "]"; } - if (settingsKeys.contains("passFinishTime") || force) { - ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString(); + if (settingsKeys.contains("replayEnabled") || force) { + ostr << " m_replayEnabled: " << m_replayEnabled; + } + if (settingsKeys.contains("replayStartDateTime") || force) { + ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString(); + } + if (settingsKeys.contains("sendTimeToMap") || force) { + ostr << " m_sendTimeToMap: " << m_sendTimeToMap; + } + if (settingsKeys.contains("dateTimeSelect") || force) { + ostr << " m_dateTimeSelect: " << m_dateTimeSelect; + } + if (settingsKeys.contains("mapFeature") || force) { + ostr << " m_mapFeature: " << m_mapFeature.toStdString(); + } + if (settingsKeys.contains("fileInputDevice") || force) { + ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString(); + } + if (settingsKeys.contains("drawRotators") || force) { + ostr << " m_drawRotators: " << m_drawRotators; + } + if (settingsKeys.contains("columnSort") || force) { + ostr << " m_columnSort: " << m_columnSort; + } + if (settingsKeys.contains("columnSortOrder") || force) { + ostr << " m_columnSortOrder: " << m_columnSortOrder; + } + if (settingsKeys.contains("columnIndexes")) + { + ostr << "m_columnIndexes:"; + + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + ostr << " " << m_columnIndexes[i]; + } + } + if (settingsKeys.contains("columnSizes")) + { + ostr << "m_columnSizes:"; + + for (int i = 0; i < SAT_COL_COLUMNS; i++) { + ostr << " " << m_columnSizes[i]; + } } if (settingsKeys.contains("title") || force) { ostr << " m_title: " << m_title.toStdString(); @@ -606,72 +681,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys if (settingsKeys.contains("reverseAPIFeatureIndex") || force) { ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex; } - if (settingsKeys.contains("chartsDarkTheme") || force) { - ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme; - } - if (settingsKeys.contains("replayEnabled") || force) { - ostr << " m_replayEnabled: " << m_replayEnabled; - } - if (settingsKeys.contains("replayStartDateTime") || force) { - ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString(); - } - if (settingsKeys.contains("sendTimeToMap") || force) { - ostr << " m_sendTimeToMap: " << m_sendTimeToMap; - } - if (settingsKeys.contains("dateTimeSelect") || force) { - ostr << " m_dateTimeSelect: " << m_dateTimeSelect; - } - if (settingsKeys.contains("mapFeature") || force) { - ostr << " m_mapFeature: " << m_mapFeature.toStdString(); - } - if (settingsKeys.contains("fileInputDevice") || force) { - ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString(); - } if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; } - if (settingsKeys.contains("columnSort") || force) { - ostr << " m_columnSort: " << m_columnSort; - } - if (settingsKeys.contains("columnSortOrder") || force) { - ostr << " m_columnSortOrder: " << m_columnSortOrder; - } - - if (settingsKeys.contains("columnIndexes")) - { - ostr << "m_columnIndexes:"; - - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - ostr << " " << m_columnIndexes[i]; - } - } - - if (settingsKeys.contains("columnSizes")) - { - ostr << "m_columnSizes:"; - - for (int i = 0; i < SAT_COL_COLUMNS; i++) { - ostr << " " << m_columnSizes[i]; - } - } - - if (settingsKeys.contains("deviceSettings")) - { - ostr << "m_deviceSettings: ["; - - for (auto deviceSettingList : m_deviceSettings) - { - ostr << "["; - - for (auto deviceSettings : *deviceSettingList) { - deviceSettings->getDebugString(ostr); - } - - ostr << "]"; - } - - ostr << "]"; - } return QString(ostr.str().c_str()); } @@ -696,3 +708,4 @@ void SatelliteTrackerSettings::SatelliteDeviceSettings::getDebugString(std::ostr << " m_aosCommand: " << m_aosCommand.toStdString() << " m_losCommand: " << m_losCommand.toStdString(); } + diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 9bd10a54f..92a120b77 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -84,6 +84,7 @@ struct SatelliteTrackerSettings enum DateTimeSelect {NOW, CUSTOM, FROM_MAP, FROM_FILE} m_dateTimeSelect; QString m_mapFeature; //!< Which feature when FROM_MAP QString m_fileInputDevice; //!< Which device when FROM_FILE + enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart int m_columnSort; //!< Which column is used for sorting (-1 for none) Qt::SortOrder m_columnSortOrder; diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp index 7ae16814c..1d36d6733 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.cpp @@ -42,6 +42,7 @@ SatelliteTrackerSettingsDialog::SatelliteTrackerSettingsDialog(SatelliteTrackerS ui->defaultFrequency->setValue(settings->m_defaultFrequency / 1000000.0); ui->azElUnits->setCurrentIndex((int)settings->m_azElUnits); ui->groundTrackPoints->setValue(settings->m_groundTrackPoints); + ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators); ui->dateFormat->setText(settings->m_dateFormat); ui->utc->setChecked(settings->m_utc); ui->drawOnMap->setChecked(settings->m_drawOnMap); @@ -94,6 +95,7 @@ void SatelliteTrackerSettingsDialog::accept() m_settings->m_defaultFrequency = (float)(ui->defaultFrequency->value() * 1000000.0); m_settings->m_azElUnits = (SatelliteTrackerSettings::AzElUnits)ui->azElUnits->currentIndex(); m_settings->m_groundTrackPoints = ui->groundTrackPoints->value(); + m_settings->m_drawRotators = (SatelliteTrackerSettings::Rotators)ui->drawRotators->currentIndex(); m_settings->m_dateFormat = ui->dateFormat->text(); m_settings->m_utc = ui->utc->isChecked(); m_settings->m_drawOnMap = ui->drawOnMap->isChecked(); diff --git a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui index 27f18a45a..9bd312301 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui +++ b/plugins/feature/satellitetracker/satellitetrackersettingsdialog.ui @@ -339,75 +339,20 @@ Display - - - - Frequency used for Doppler and free space path loss calculations in the satellite table - - - 3 - - - 1.000000000000000 - - - 50000.000000000000000 - - - 100.000000000000000 - - - 100.000000000000000 - - - - - - - Format for dates displayed in the GUI - - - - + - When checked satellite positions are sent to the map + When checked times are dispayed using UTC rather than the local time zone - Draw satellites on map + Display times in UTC - - - - Qt::Vertical - - - - 20 - 40 - - - - - - + + - Update period (s) - - - - - - - Enter the time in seconds between each calculation of the target's position - - - 3600.000000000000000 - - - 1.000000000000000 + Ground track points @@ -418,6 +363,20 @@ + + + + Date format + + + + + + + Format for dates displayed in the GUI + + + @@ -448,6 +407,48 @@
+ + + + Frequency used for Doppler and free space path loss calculations in the satellite table + + + 3 + + + 1.000000000000000 + + + 50000.000000000000000 + + + 100.000000000000000 + + + 100.000000000000000 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Update period (s) + + + @@ -455,27 +456,16 @@ - - + + - When checked times are dispayed using UTC rather than the local time zone + Enter the time in seconds between each calculation of the target's position - - Display times in UTC + + 3600.000000000000000 - - - - - - Date format - - - - - - - Ground track points + + 1.000000000000000 @@ -492,6 +482,45 @@ + + + + When checked satellite positions are sent to the map + + + Draw satellites on map + + + + + + + Select which rotators are displayed on the polar chart + + + + All + + + + + None + + + + + Matching target + + + + + + + + Rotators in polar chart + + +
@@ -614,8 +643,8 @@ accept() - 248 - 254 + 257 + 533 157 @@ -630,8 +659,8 @@ reject() - 316 - 260 + 325 + 533 286 diff --git a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp index f5d6f3685..c8b1e2442 100644 --- a/plugins/feature/satellitetracker/satellitetrackersgp4.cpp +++ b/plugins/feature/satellitetracker/satellitetrackersgp4.cpp @@ -134,7 +134,7 @@ void getPassAzEl(QLineSeries* azimuth, QLineSeries* elevation, QLineSeries* pola DateTime aosTime = qDateTimeToDateTime(aos); DateTime losTime = qDateTimeToDateTime(los); DateTime currentTime(aosTime); - int steps = 20; + int steps = 150; // Needs to be high enough, so rotator intersect with satellite position double timeStep = (losTime - aosTime).TotalSeconds() / steps; if (timeStep <= 0.0) diff --git a/plugins/feature/startracker/readme.md b/plugins/feature/startracker/readme.md index 1558ee427..fe98fe461 100644 --- a/plugins/feature/startracker/readme.md +++ b/plugins/feature/startracker/readme.md @@ -49,6 +49,8 @@ Pressing this button displays a settings dialog, that allows you to set: * The units to display the solar flux in, either Solar Flux Units, Jansky or Wm^-2Hz-1. 1 sfu equals 10,000 Jansky or 10^-22 Wm^-2Hz-1. * The update period in seconds, which controls how frequently azimuth and elevation are re-calculated. * The IP port number the Stellarium server listens on. +* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will +only be displayed if the source in the Rotator Controller is set to this Star Tracker and Track is enabled. * Whether to start a Stellarium telescope server. * Whether to draw the Sun in the map. * Whether to draw the Moon on the map. diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 930b833e3..45a8ecff9 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -33,8 +33,11 @@ #include "SWGStarTrackerDisplaySettings.h" #include "SWGStarTrackerDisplayLoSSettings.h" +#include "feature/featureset.h" #include "feature/featureuiset.h" +#include "feature/featureutils.h" #include "feature/featurewebapiutils.h" +#include "channel/channelwebapiutils.h" #include "gui/basicfeaturesettingsdialog.h" #include "gui/dmsspinbox.h" #include "gui/graphicsviewzoom.h" @@ -285,6 +288,8 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(1000); + connect(&m_redrawTimer, &QTimer::timeout, this, &StarTrackerGUI::plotChart); + connect(ui->azimuth, SIGNAL(valueChanged(double)), this, SLOT(on_azimuth_valueChanged(double))); ui->azimuth->setRange(0, 360.0); ui->elevation->setRange(-90.0, 90.0); @@ -812,7 +817,7 @@ void StarTrackerGUI::on_displaySettings_clicked() ui->galacticLongitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits); displaySolarFlux(); - if (ui->chartSelect->currentIndex() == 1) { + if (ui->chartSelect->currentIndex() <= 1) { plotChart(); } } @@ -1651,6 +1656,26 @@ void StarTrackerGUI::plotElevationLineChart() delete oldChart; } +// Reduce az/el range from 450,180 to 360,90 +void StarTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const +{ + if (elevation > 90.0) + { + elevation = 180.0 - elevation; + if (azimuth < 180.0) { + azimuth += 180.0; + } else { + azimuth -= 180.0; + } + } + if (azimuth > 360.0) { + azimuth -= 360.0f; + } + if (azimuth == 0) { + azimuth = 360.0; + } +} + // Plot target elevation angle over the day void StarTrackerGUI::plotElevationPolarChart() { @@ -1841,6 +1866,92 @@ void StarTrackerGUI::plotElevationPolarChart() series[i]->attachAxis(radialAxis); } + if (m_settings.m_drawRotators != StarTrackerSettings::NO_ROTATORS) + { + int redrawTime = 0; + // Plot rotator position + QString ourSourceName = QString("F0:%1 %2").arg(m_starTracker->getIndexInFeatureSet()).arg(m_starTracker->getIdentifier()); // Only one feature set in practice? + std::vector& featureSets = MainCore::instance()->getFeatureeSets(); + for (int featureSetIndex = 0; featureSetIndex < featureSets.size(); featureSetIndex++) + { + FeatureSet *featureSet = featureSets[featureSetIndex]; + for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++) + { + Feature *feature = featureSet->getFeatureAt(featureIndex); + if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller")) + { + QString source; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller + int track = 0; + ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track); + if ((m_settings.m_drawRotators == StarTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track)) + { + int onTarget = 0; + ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget); + + if (!onTarget) + { + // Target azimuth red dotted line + double targetAzimuth, targetElevation; + bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth); + bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation); + if (targetAzimuthOk && targetElevationOk) + { + limitAzElRange(targetAzimuth, targetElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color(255, 0, 0, 150); + QPen pen(color); + rotatorSeries->setPen(pen); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(targetAzimuth, 90-targetElevation); + m_azElPolarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + + // Current azimuth line. Yellow while off target, green on target. + double currentAzimuth, currentElevation; + bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth); + bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation); + if (currentAzimuthOk && currentElevationOk) + { + limitAzElRange(currentAzimuth, currentElevation); + + QScatterSeries *rotatorSeries = new QScatterSeries(); + QColor color; + if (onTarget) { + color = QColor(0, 255, 0, 150); + } else { + color = QColor(255, 255, 0, 150); + } + rotatorSeries->setPen(QPen(color)); + rotatorSeries->setColor(color.darker()); + rotatorSeries->setMarkerSize(20); + rotatorSeries->append(currentAzimuth, 90-currentElevation); + m_azElPolarChart->addSeries(rotatorSeries); + rotatorSeries->attachAxis(angularAxis); + rotatorSeries->attachAxis(radialAxis); + + redrawTime = 333; + } + } + } + } + } + if (redrawTime > 0) + { + // Redraw to show updated rotator position + // Update period may be long or custom time might be fixed + m_redrawTimer.setSingleShot(true); + m_redrawTimer.start(redrawTime); + } + } + // Create series with single point, so we can plot time of rising if (riseTime.isValid()) { @@ -2242,3 +2353,4 @@ void StarTrackerGUI::makeUIConnections() QObject::connect(ui->drawSun, &QToolButton::clicked, this, &StarTrackerGUI::on_drawSun_clicked); QObject::connect(ui->drawMoon, &QToolButton::clicked, this, &StarTrackerGUI::on_drawMoon_clicked); } + diff --git a/plugins/feature/startracker/startrackergui.h b/plugins/feature/startracker/startrackergui.h index 925232047..03d31762a 100644 --- a/plugins/feature/startracker/startrackergui.h +++ b/plugins/feature/startracker/startrackergui.h @@ -90,6 +90,7 @@ private: QChart *m_azElLineChart; QPolarChart *m_azElPolarChart; + QTimer m_redrawTimer; QChart m_chart; QDateTimeAxis m_chartXAxis; @@ -161,6 +162,7 @@ private: void updateChartSubSelect(); void updateSolarFlux(bool all); void makeUIConnections(); + void limitAzElRange(double& azimuth, double& elevation) const; private slots: void onMenuDialogCalled(const QPoint &p); diff --git a/plugins/feature/startracker/startrackersettings.cpp b/plugins/feature/startracker/startrackersettings.cpp index 9522018a7..9023ccb21 100644 --- a/plugins/feature/startracker/startrackersettings.cpp +++ b/plugins/feature/startracker/startrackersettings.cpp @@ -83,6 +83,7 @@ void StarTrackerSettings::resetToDefaults() m_drawSunOnSkyTempChart = true; m_drawMoonOnSkyTempChart = true; m_workspaceIndex = 0; + m_drawRotators = MATCHING_TARGET; } QByteArray StarTrackerSettings::serialize() const @@ -139,6 +140,7 @@ QByteArray StarTrackerSettings::serialize() const s.writeS32(45, m_workspaceIndex); s.writeBlob(46, m_geometryBytes); + s.writeS32(47, (int)m_drawRotators); return s.final(); } @@ -227,6 +229,7 @@ bool StarTrackerSettings::deserialize(const QByteArray& data) d.readS32(45, &m_workspaceIndex, 0); d.readBlob(46, &m_geometryBytes); + d.readS32(47, (int *)&m_drawRotators, MATCHING_TARGET); return true; } @@ -374,6 +377,9 @@ void StarTrackerSettings::applySettings(const QStringList& settingsKeys, const S if (settingsKeys.contains("workspaceIndex")) { m_workspaceIndex = settings.m_workspaceIndex; } + if (settingsKeys.contains("drawRotators")) { + m_drawRotators = settings.m_drawRotators; + } } QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, bool force) const @@ -512,6 +518,9 @@ QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, boo if (settingsKeys.contains("workspaceIndex") || force) { ostr << " m_workspaceIndex: " << m_workspaceIndex; } + if (settingsKeys.contains("drawRotators") || force) { + ostr << " m_drawRotators: " << m_drawRotators; + } return QString(ostr.str().c_str()); } diff --git a/plugins/feature/startracker/startrackersettings.h b/plugins/feature/startracker/startrackersettings.h index f8f2c48a8..8210bda8a 100644 --- a/plugins/feature/startracker/startrackersettings.h +++ b/plugins/feature/startracker/startrackersettings.h @@ -74,6 +74,7 @@ struct StarTrackerSettings Serializable *m_rollupState; int m_workspaceIndex; QByteArray m_geometryBytes; + enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart StarTrackerSettings(); void resetToDefaults(); diff --git a/plugins/feature/startracker/startrackersettingsdialog.cpp b/plugins/feature/startracker/startrackersettingsdialog.cpp index a85a2f08d..9b5b51e6d 100644 --- a/plugins/feature/startracker/startrackersettingsdialog.cpp +++ b/plugins/feature/startracker/startrackersettingsdialog.cpp @@ -44,8 +44,10 @@ StarTrackerSettingsDialog::StarTrackerSettingsDialog( ui->temperatureLapseRate->setValue(settings->m_temperatureLapseRate); ui->solarFluxData->setCurrentIndex((int)settings->m_solarFluxData); ui->solarFluxUnits->setCurrentIndex((int)settings->m_solarFluxUnits); + ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators); ui->drawSunOnMap->setChecked(settings->m_drawSunOnMap); ui->drawMoonOnMap->setChecked(settings->m_drawMoonOnMap); + ui->drawStarOnMap->setChecked(settings->m_drawStarOnMap); } StarTrackerSettingsDialog::~StarTrackerSettingsDialog() @@ -70,6 +72,7 @@ void StarTrackerSettingsDialog::accept() m_settings->m_temperatureLapseRate = ui->temperatureLapseRate->value(); m_settings->m_solarFluxData = (StarTrackerSettings::SolarFluxData)ui->solarFluxData->currentIndex(); m_settings->m_solarFluxUnits = (StarTrackerSettings::SolarFluxUnits)ui->solarFluxUnits->currentIndex(); + m_settings->m_drawRotators = (StarTrackerSettings::Rotators)ui->drawRotators->currentIndex(); m_settings->m_drawSunOnMap = ui->drawSunOnMap->isChecked(); m_settings->m_drawMoonOnMap = ui->drawMoonOnMap->isChecked(); m_settings->m_drawStarOnMap = ui->drawStarOnMap->isChecked(); @@ -89,6 +92,7 @@ void StarTrackerSettingsDialog::accept() m_settingsKeys.append("temperatureLapseRate"); m_settingsKeys.append("solarFluxData"); m_settingsKeys.append("solarFluxUnits"); + m_settingsKeys.append("drawRotators"); m_settingsKeys.append("drawSunOnMap"); m_settingsKeys.append("drawMoonOnMap"); m_settingsKeys.append("drawStarOnMap"); diff --git a/plugins/feature/startracker/startrackersettingsdialog.ui b/plugins/feature/startracker/startrackersettingsdialog.ui index 38afe4f36..a2d668912 100644 --- a/plugins/feature/startracker/startrackersettingsdialog.ui +++ b/plugins/feature/startracker/startrackersettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 569 - 535 + 556 @@ -23,24 +23,7 @@ - - - - Epoch for custom right ascension and declination - - - - J2000 - - - - - JNOW - - - - - + Enable Stellarium server which allows RA and Dec to be sent to and from Stellarium @@ -50,74 +33,7 @@ - - - - Air pressure (mb) - - - - - - - Relative humidity in % - - - 100 - - - 80 - - - - - - - Temperature lapse rate (K/m) - - - Temperature lapse rate (K/km) - - - - - - - Draw Moon on map - - - - - - - Draw Sun on map - - - - - - - Air temperature (C) - - - - - - - Air pressure in millibars, for use in atmospheric refraction correction - - - 2000.000000000000000 - - - 1.000000000000000 - - - 1010.000000000000000 - - - - + Qt::Vertical @@ -130,52 +46,13 @@ - - + + - Draw target star on map + Draw Sun on map - - - - Weather update period (min) - - - - - - - Solar flux density data - - - - - - - Atmospheric refraction correction - - - 0 - - - - None - - - - - Saemundsson - - - - - Positional Astronomy Library - - - - @@ -206,153 +83,58 @@ - - - - Air temperature in degrees Celsius, for use in atmospheric refraction correction + + + + Air temperature (C) - - -100 + + + + + + Relative humidity in % 100 - 10 + 80 - - - - Solar flux density units - - - - - - - 3 - - - 100.000000000000000 - - - 6.490000000000000 - - - - - - - Refraction correction - - - - - + + - Units to use for the display of the Solar flux density + Atmospheric refraction correction + + + 0 - Solar flux units (sfu) + None - Jansky (Jy) + Saemundsson - Watts per square metre per hertz (W m^-2 Hz-1) + Positional Astronomy Library - - - - Height above sea level (m) - - - - - + + - API key from openweathermap.org to download real-time weather + Temperature lapse rate (K/m) - - - - - Stellarium server port - - - - - - - Epoch for RA & Dec - - - - - - - Azimuth and elevation units - - - - - - - OpenWeatherMap API Key - - - - - - - Stellarium telescope server IP port number - - - 1024 - - - 65535 - - - 10001 - - - - - - - Enter the time in seconds between each calculation of the target's position - - - 1.000000000000000 - - - - - - - Update period (s) - - - - - - - Enter the time in minutes between each weather update - - - 100000 + Temperature lapse rate (K/km) @@ -363,16 +145,68 @@ - - - - Height of observation/antenna location above sea level in metres + + + + Epoch for RA & Dec - - -1000 + + + + + + Weather update period (min) + + + + + + + Air pressure (mb) + + + + + + + Draw target star on map + + + + + + + OpenWeatherMap API Key + + + + + + + Solar flux density units + + + + + + + Air pressure in millibars, for use in atmospheric refraction correction - 20000 + 2000.000000000000000 + + + 1.000000000000000 + + + 1010.000000000000000 + + + + + + + Height above sea level (m) @@ -433,6 +267,201 @@ + + + + 3 + + + 100.000000000000000 + + + 6.490000000000000 + + + + + + + API key from openweathermap.org to download real-time weather + + + + + + + Air temperature in degrees Celsius, for use in atmospheric refraction correction + + + -100 + + + 100 + + + 10 + + + + + + + Draw Moon on map + + + + + + + Update period (s) + + + + + + + Epoch for custom right ascension and declination + + + + J2000 + + + + + JNOW + + + + + + + + Refraction correction + + + + + + + Solar flux density data + + + + + + + Units to use for the display of the Solar flux density + + + + Solar flux units (sfu) + + + + + Jansky (Jy) + + + + + Watts per square metre per hertz (W m^-2 Hz-1) + + + + + + + + Height of observation/antenna location above sea level in metres + + + -1000 + + + 20000 + + + + + + + Enter the time in seconds between each calculation of the target's position + + + 1.000000000000000 + + + + + + + Stellarium telescope server IP port number + + + 1024 + + + 65535 + + + 10001 + + + + + + + Stellarium server port + + + + + + + Enter the time in minutes between each weather update + + + 100000 + + + + + + + Azimuth and elevation units + + + + + + + Rotators in polar chart + + + + + + + Select which rotators are displayed on the polar chart + + + + All + + + + + None + + + + + Matching target + + + +
From e247fbb4fa9c97ed38ab5763d159d5a4f25ec0e8 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:56:00 +0100 Subject: [PATCH 3/6] Fix Mac compilation error --- sdrgui/gui/coursedeviationindicator.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdrgui/gui/coursedeviationindicator.cpp b/sdrgui/gui/coursedeviationindicator.cpp index 6b3742122..ba5396d67 100644 --- a/sdrgui/gui/coursedeviationindicator.cpp +++ b/sdrgui/gui/coursedeviationindicator.cpp @@ -15,6 +15,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +#include + #include #include "coursedeviationindicator.h" @@ -46,6 +48,8 @@ void CourseDeviationIndicator::setGlideSlopeDDM(float ddm) void CourseDeviationIndicator::paintEvent(QPaintEvent *event) { + (void) event; + QPainter painter(this); QRect r = rect(); @@ -168,7 +172,6 @@ void CourseDeviationIndicator::paintEvent(QPaintEvent *event) { QFontMetrics fm(painter.font()); QString text = "G/S"; - int tw = fm.horizontalAdvance(text); int th = fm.ascent() / 2; painter.setPen(QColor(0, 255, 0)); painter.drawText(midW + bgw + 2, midH + th, text); From df20f2d6a02d673ab90b71ad837f4b38da99cb48 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:56:46 +0100 Subject: [PATCH 4/6] Fix memory leak --- plugins/feature/aprs/aprs.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/feature/aprs/aprs.cpp b/plugins/feature/aprs/aprs.cpp index 4a46e85c5..917b63f1e 100644 --- a/plugins/feature/aprs/aprs.cpp +++ b/plugins/feature/aprs/aprs.cpp @@ -203,12 +203,11 @@ void APRS::applySettings(const APRSSettings& settings, const QList& set stop(); } - - APRSWorker::MsgConfigureAPRSWorker *msg = APRSWorker::MsgConfigureAPRSWorker::create( - settings, settingsKeys, force - ); - - if (m_worker) { + if (m_worker) + { + APRSWorker::MsgConfigureAPRSWorker *msg = APRSWorker::MsgConfigureAPRSWorker::create( + settings, settingsKeys, force + ); m_worker->getInputMessageQueue()->push(msg); } From e91bdd3cfd7501570cbfaad09b57e9a4ec717c48 Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 16:57:26 +0100 Subject: [PATCH 5/6] Remove duplicate install command --- plugins/channelrx/demodais/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/channelrx/demodais/CMakeLists.txt b/plugins/channelrx/demodais/CMakeLists.txt index 09978a0b9..215165724 100644 --- a/plugins/channelrx/demodais/CMakeLists.txt +++ b/plugins/channelrx/demodais/CMakeLists.txt @@ -62,7 +62,3 @@ if (WIN32) install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) endif() -# Install debug symbols -if (WIN32) - install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) -endif() From fabcd7a795f45259919b932ec213936971729eff Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Mon, 3 Apr 2023 17:14:01 +0100 Subject: [PATCH 6/6] Fix corrupted char --- plugins/feature/gs232controller/gs232controllergui.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index 3cb42e692..10450a9ea 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -461,7 +461,7 @@ void GS232ControllerGUI::setProtocol(GS232ControllerSettings::Protocol protocol) ui->precisionLabel->setEnabled(false); } else if (protocol == GS232ControllerSettings::SPID) -ÿ  + { ui->precision->setValue(1); ui->precision->setEnabled(false); ui->precisionLabel->setEnabled(false);