kopia lustrzana https://github.com/f4exb/sdrangel
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.pull/1650/head
rodzic
c50c866732
commit
4ac5e729ff
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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_
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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_
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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)
|
||||
˙ |