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.
This commit is contained in:
Jon Beniston 2023-04-03 16:47:13 +01:00
parent c50c866732
commit 4ac5e729ff
31 changed files with 2859 additions and 483 deletions

View File

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

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QRegularExpression>
#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<QString>& 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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_CONTROLLERPROTOCOL_H_
#define INCLUDE_FEATURE_CONTROLLERPROTOCOL_H_
#include <QIODevice>
#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<QString>& 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_

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#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<QString>& 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);
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_DFMPROTOCOL_H_
#define INCLUDE_FEATURE_DFMPROTOCOL_H_
#include <QTimer>
#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<QString>& 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_

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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();
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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

View File

@ -0,0 +1,413 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DFMStatusDialog</class>
<widget class="QDialog" name="DFMStatusDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>474</width>
<height>488</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>DFM Status</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="statusGroup">
<property name="title">
<string>Status</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="trackOnLabel">
<property name="text">
<string>Track</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="trackOn">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="driverOnLabel">
<property name="text">
<string>Drives</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="driveOn">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="brakesOnLabel">
<property name="text">
<string>Brakes</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="brakesOn">
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="pumpsOnLabel">
<property name="text">
<string>Pumps</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="pumpsOn">
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="controller">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>OCU</string>
</property>
</item>
<item>
<property name="text">
<string>LCU</string>
</property>
</item>
<item>
<property name="text">
<string>MCU</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="controllerLabel">
<property name="text">
<string>Controller</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QGroupBox" name="antennaPositionGroup">
<property name="title">
<string>Antenna Position</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="2" column="1">
<widget class="QLineEdit" name="currentRA">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="currentRALabel">
<property name="text">
<string>RA</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="currentDecLabel">
<property name="text">
<string>Dec</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="currentDec">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="currentHALabel">
<property name="text">
<string>HA</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="currentHA">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QGroupBox" name="dateTimeGroup">
<property name="title">
<string>Date/Time</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="stLabel">
<property name="text">
<string>ST</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="utLabel">
<property name="text">
<string>UT</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="st">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="ut">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QGroupBox" name="xyStatus">
<property name="title">
<string>X/Y Status</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="1">
<widget class="QLineEdit" name="torqueX">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="currentX">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="torqueXYLabel">
<property name="text">
<string>Torque</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="rateX">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="torqueY">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="currentY">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="rateXYLabel">
<property name="text">
<string>Rate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="xColumnLabel">
<property name="text">
<string>X</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="currentXYLabel">
<property name="text">
<string>Antenna</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="yColumnLabel">
<property name="text">
<string>Y</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="rateY">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="siderealRateXYLabel">
<property name="text">
<string>Sidereal Rate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="siderealRateX">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="siderealRateY">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DFMStatusDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DFMStatusDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -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<QString>& 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<QSerialPortInfo> serialPorts = QSerialPortInfo::availablePorts();
QListIterator<QSerialPortInfo> i(serialPorts);
response.getGs232ControllerReport()->setSerialPorts(new QList<QString*>());
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<QSerialPortInfo> serialPortInfos = QSerialPortInfo::availablePorts();
QListIterator<QSerialPortInfo> 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;
}
}

View File

@ -22,6 +22,7 @@
#include <QThread>
#include <QNetworkRequest>
#include <QHash>
#include <QTimer>
#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<QObject*, GS232ControllerSettings::AvailableChannelOrFeature> 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_

View File

@ -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<GS232ControllerSettings::AvailableChannelOrFeature>& 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<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_port_valueChanged);
QObject::connect(ui->baudRate, qOverload<int>(&QComboBox::currentIndexChanged), this, &GS232ControllerGUI::on_baudRate_currentIndexChanged);
QObject::connect(ui->track, &QCheckBox::stateChanged, this, &GS232ControllerGUI::on_track_stateChanged);
QObject::connect(ui->azimuth, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuth_valueChanged);
QObject::connect(ui->elevation, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevation_valueChanged);
QObject::connect(ui->coord1, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_coord1_valueChanged);
QObject::connect(ui->coord2, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_coord2_valueChanged);
QObject::connect(ui->sources, &QComboBox::currentTextChanged, this, &GS232ControllerGUI::on_sources_currentTextChanged);
QObject::connect(ui->azimuthOffset, qOverload<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_azimuthOffset_valueChanged);
QObject::connect(ui->elevationOffset, qOverload<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationOffset_valueChanged);
@ -593,4 +803,12 @@ void GS232ControllerGUI::makeUIConnections()
QObject::connect(ui->elevationMin, qOverload<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMin_valueChanged);
QObject::connect(ui->elevationMax, qOverload<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_elevationMax_valueChanged);
QObject::connect(ui->tolerance, qOverload<double>(&QDoubleSpinBox::valueChanged), this, &GS232ControllerGUI::on_tolerance_valueChanged);
QObject::connect(ui->precision, qOverload<int>(&QSpinBox::valueChanged), this, &GS232ControllerGUI::on_precision_valueChanged);
QObject::connect(ui->coordinates, qOverload<int>(&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);
}

View File

@ -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<GS232ControllerSettings::AvailableChannelOrFeature>& 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_

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>360</width>
<height>281</height>
<height>378</height>
</rect>
</property>
<property name="sizePolicy">
@ -43,7 +43,7 @@
<x>10</x>
<y>10</y>
<width>341</width>
<height>81</height>
<height>101</height>
</rect>
</property>
<property name="windowTitle">
@ -96,19 +96,28 @@
</spacer>
</item>
<item>
<widget class="QLabel" name="azimuthtLabel">
<widget class="QLabel" name="coord1Label">
<property name="minimumSize">
<size>
<width>54</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Azimuth</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="azimuth">
<widget class="QDoubleSpinBox" name="coord1">
<property name="toolTip">
<string>Target azimuth in degrees</string>
</property>
<property name="decimals">
<number>1</number>
<number>0</number>
</property>
<property name="maximum">
<double>450.000000000000000</double>
@ -119,10 +128,10 @@
</widget>
</item>
<item>
<widget class="QLabel" name="azimuthCurrentText">
<widget class="QLabel" name="coord1CurrentText">
<property name="minimumSize">
<size>
<width>32</width>
<width>40</width>
<height>0</height>
</size>
</property>
@ -130,7 +139,7 @@
<string>Current azimuth in degrees</string>
</property>
<property name="text">
<string>360.0</string>
<string>360.00</string>
</property>
</widget>
</item>
@ -142,19 +151,28 @@
</widget>
</item>
<item>
<widget class="QLabel" name="elevationLabel">
<widget class="QLabel" name="coord2Label">
<property name="minimumSize">
<size>
<width>54</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Elevation</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="elevation">
<widget class="QDoubleSpinBox" name="coord2">
<property name="toolTip">
<string>Target elevation in degrees</string>
</property>
<property name="decimals">
<number>1</number>
<number>0</number>
</property>
<property name="maximum">
<double>180.000000000000000</double>
@ -165,10 +183,10 @@
</widget>
</item>
<item>
<widget class="QLabel" name="elevationCurrentText">
<widget class="QLabel" name="coord2CurrentText">
<property name="minimumSize">
<size>
<width>32</width>
<width>40</width>
<height>0</height>
</size>
</property>
@ -176,7 +194,7 @@
<string>Current elevation in degrees</string>
</property>
<property name="text">
<string>180.0</string>
<string>180.00</string>
</property>
</widget>
</item>
@ -219,15 +237,76 @@
</item>
</layout>
</item>
<item>
<widget class="Line" name="dfmLine">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="dfmLayout">
<item>
<widget class="QToolButton" name="dfmTrack">
<property name="text">
<string>Track</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dfmLubePumps">
<property name="text">
<string>Pumps</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dfmBrakes">
<property name="text">
<string>Brakes</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dfmDrives">
<property name="text">
<string>Drives</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="dfmShowStatus">
<property name="toolTip">
<string>Show the DFM status dialog</string>
</property>
<property name="text">
<string>Status</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="settingsContainer" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>110</y>
<y>140</y>
<width>341</width>
<height>161</height>
<height>191</height>
</rect>
</property>
<property name="windowTitle">
@ -258,31 +337,26 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="azimuthMinLabel">
<property name="text">
<string>Azimuth min</string>
<item row="6" column="3">
<widget class="QSpinBox" name="precision">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Precision (number of decimal places) of azimuth, elevation and tolerance values</string>
</property>
<property name="statusTip">
<string/>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="azimuthMin">
<property name="maximum">
<number>450</number>
<number>5</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="protocolLabel">
<property name="text">
<string>Protocol</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="elevationMaxLabel">
<property name="text">
<string>Elevation max</string>
<item row="2" column="1">
<widget class="QLineEdit" name="host">
<property name="toolTip">
<string>Hostname / IP address of computer to connect to</string>
</property>
</widget>
</item>
@ -293,6 +367,43 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="toleranceLabel">
<property name="text">
<string>Tolerance</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="serialPort">
<property name="toolTip">
<string>Name of serial port to use to connect to the rotator</string>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="azimuthOffset">
<property name="toolTip">
<string>Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment</string>
</property>
<property name="minimum">
<number>-360</number>
</property>
<property name="maximum">
<number>360</number>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="azimuthMaxLabel">
<property name="text">
<string>Azimuth max</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="connectionLabel">
<property name="text">
@ -300,34 +411,20 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="elevationMin">
<property name="maximum">
<number>180</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="portLabel">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="tolerance">
<item row="2" column="3">
<widget class="QSpinBox" name="port">
<property name="toolTip">
<string>Tolerance in degrees</string>
<string>TCP port number to connect to</string>
</property>
<property name="decimals">
<number>1</number>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="serialPortLabel">
<item row="1" column="2">
<widget class="QLabel" name="baudRateLabel">
<property name="text">
<string>Serial Port</string>
<string>Baud rate</string>
</property>
</widget>
</item>
@ -348,17 +445,48 @@
</item>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="toleranceLabel">
<item row="1" column="0">
<widget class="QLabel" name="serialPortLabel">
<property name="text">
<string>Tolerance</string>
<string>Serial Port</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="elevationOffsetLabel">
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="tolerance">
<property name="toolTip">
<string>Tolerance in degrees</string>
</property>
<property name="decimals">
<number>0</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="azimuthMin">
<property name="maximum">
<number>450</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="hostLabel">
<property name="text">
<string>Elevation offset</string>
<string>Host</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="portLabel">
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="elevationMaxLabel">
<property name="text">
<string>Elevation max</string>
</property>
</widget>
</item>
@ -369,70 +497,29 @@
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QSpinBox" name="elevationOffset">
<property name="toolTip">
<string>Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment</string>
</property>
<property name="minimum">
<number>-180</number>
</property>
<item row="5" column="3">
<widget class="QSpinBox" name="elevationMax">
<property name="maximum">
<number>180</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="host">
<property name="toolTip">
<string>Hostname / IP address of computer to connect to</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="serialPort">
<property name="toolTip">
<string>Name of serial port to use to connect to the rotator</string>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="protocol">
<property name="toolTip">
<string>Command protocol</string>
</property>
<item>
<property name="text">
<string>GS-232</string>
</property>
</item>
<item>
<property name="text">
<string>SPID</string>
</property>
</item>
<item>
<property name="text">
<string>rotctld</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="hostLabel">
<item row="4" column="0">
<widget class="QLabel" name="azimuthMinLabel">
<property name="text">
<string>Host</string>
<string>Azimuth min</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QSpinBox" name="elevationMax">
<item row="3" column="2">
<widget class="QLabel" name="elevationOffsetLabel">
<property name="text">
<string>Elevation offset</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="elevationMin">
<property name="maximum">
<number>180</number>
</property>
@ -498,40 +585,92 @@
</item>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="azimuthMaxLabel">
<item row="6" column="2">
<widget class="QLabel" name="precisionLabel">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Azimuth max</string>
<string>Precision</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="azimuthOffset">
<item row="0" column="1">
<widget class="QComboBox" name="protocol">
<property name="toolTip">
<string>Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment</string>
<string>Command protocol</string>
</property>
<item>
<property name="text">
<string>GS-232</string>
</property>
</item>
<item>
<property name="text">
<string>SPID</string>
</property>
</item>
<item>
<property name="text">
<string>rotctld</string>
</property>
</item>
<item>
<property name="text">
<string>DFM</string>
</property>
</item>
</widget>
</item>
<item row="3" column="3">
<widget class="QSpinBox" name="elevationOffset">
<property name="toolTip">
<string>Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment</string>
</property>
<property name="minimum">
<number>-360</number>
<number>-180</number>
</property>
<property name="maximum">
<number>360</number>
<number>180</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QSpinBox" name="port">
<property name="toolTip">
<string>TCP port number to connect to</string>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="baudRateLabel">
<item row="0" column="0">
<widget class="QLabel" name="protocolLabel">
<property name="text">
<string>Baud rate</string>
<string>Protocol</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="coordinates">
<property name="toolTip">
<string>Coordinate system</string>
</property>
<item>
<property name="text">
<string>Az/El</string>
</property>
</item>
<item>
<property name="text">
<string>X/Y 85'</string>
</property>
</item>
<item>
<property name="text">
<string>X/Y 30'</string>
</property>
</item>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Coordinates</string>
</property>
</widget>
</item>
@ -541,24 +680,38 @@
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>startStop</tabstop>
<tabstop>azimuth</tabstop>
<tabstop>elevation</tabstop>
<tabstop>coord1</tabstop>
<tabstop>coord2</tabstop>
<tabstop>track</tabstop>
<tabstop>sources</tabstop>
<tabstop>targetName</tabstop>
<tabstop>protocol</tabstop>
<tabstop>connection</tabstop>
<tabstop>serialPort</tabstop>
<tabstop>baudRate</tabstop>
<tabstop>host</tabstop>
<tabstop>port</tabstop>
<tabstop>azimuthOffset</tabstop>
<tabstop>elevationOffset</tabstop>
<tabstop>azimuthMin</tabstop>
<tabstop>azimuthMax</tabstop>
<tabstop>elevationMin</tabstop>
<tabstop>elevationMax</tabstop>
<tabstop>tolerance</tabstop>
</tabstops>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>

View File

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

View File

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

View File

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

View File

@ -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<QString>& settingsKeys, bool force = false);
@ -102,3 +98,4 @@ private slots:
};
#endif // INCLUDE_FEATURE_GS232CONTROLLERWORKER_H_

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include <QDebug>
#include <QRegularExpression>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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_

View File

@ -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.
<h3>20: Precision</h3>
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.
<h3>21: Coordinates</h3>
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).
<h2>Protocol Implementations</h2>
<h3>GS-232 Protocol Implementation Notes</h3>

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QRegularExpression>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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_

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include <QDebug>
#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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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_

View File

@ -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<DeviceSet*> 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<DeviceSet*> 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;
}

View File

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

View File

@ -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. */
}

View File

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

View File

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

View File

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

View File

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