1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-23 00:18:37 -05:00

Merge pull request #1650 from srcejon/fix_1641

Rotator Controller Updates
This commit is contained in:
Edouard Griffiths 2023-04-03 19:26:43 +02:00 committed by GitHub
commit e376dd0454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 3655 additions and 957 deletions

View File

@ -62,7 +62,3 @@ if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -203,12 +203,11 @@ void APRS::applySettings(const APRSSettings& settings, const QList<QString>& set
stop();
}
if (m_worker)
{
APRSWorker::MsgConfigureAPRSWorker *msg = APRSWorker::MsgConfigureAPRSWorker::create(
settings, settingsKeys, force
);
if (m_worker) {
m_worker->getInputMessageQueue()->push(msg);
}

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>
<item row="4" column="0">
<widget class="QLabel" name="azimuthMinLabel">
<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">
<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

@ -116,7 +116,10 @@ On the display tab, you can set:
* The default frequency in MHz that is used for calculating Doppler and free space path loss in the Satellite Data table.
* The units used to display azimuth and elevation to the target satellite. This can be in degrees, minutes and seconds or decimal degrees.
* The number of points used for ground tracks on the map. More points result in smoother tracks, but require more processing.
* Whether times are displayrf in the local time zone or UTC.
* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will
only be displayed if the source in the Rotator Controller is set to this Satellite Tracker and Track is enabled.
* The format used for displaying dates. E.g. yyyy/MM/dd
* Whether times are displayed in the local time zone or UTC.
* Whether to draw the satellites on the map.
<h3>9: Latitude</h3>

View File

@ -28,8 +28,10 @@
#include "device/deviceapi.h"
#include "device/deviceset.h"
#include "channel/channelwebapiutils.h"
#include "feature/featureset.h"
#include "feature/featureuiset.h"
#include "feature/featureutils.h"
#include "feature/featurewebapiutils.h"
#include "gui/basicfeaturesettingsdialog.h"
#include "gui/dialogpositioner.h"
@ -295,6 +297,8 @@ SatelliteTrackerGUI::SatelliteTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *fea
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
connect(&m_redrawTimer, &QTimer::timeout, this, &SatelliteTrackerGUI::plotChart);
// Intialise charts
m_emptyChart.layout()->setContentsMargins(0, 0, 0, 0);
m_emptyChart.setMargins(QMargins(1, 1, 1, 1));
@ -534,6 +538,7 @@ void SatelliteTrackerGUI::on_displaySettings_clicked()
m_settingsKeys.append("defaultFrequency");
m_settingsKeys.append("azElUnits");
m_settingsKeys.append("groundTrackPoints");
m_settingsKeys.append("drawRotators");
m_settingsKeys.append("dateFormat");
m_settingsKeys.append("utc");
m_settingsKeys.append("tles");
@ -792,6 +797,26 @@ static double interpolate(double x0, double y0, double x1, double y1, double x)
return (y0*(x1-x) + y1*(x-x0)) / (x1-x0);
}
// Reduce az/el range from 450,180 to 360,90
void SatelliteTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const
{
if (elevation > 90.0)
{
elevation = 180.0 - elevation;
if (azimuth < 180.0) {
azimuth += 180.0;
} else {
azimuth -= 180.0;
}
}
if (azimuth > 360.0) {
azimuth -= 360.0f;
}
if (azimuth == 0) {
azimuth = 360.0;
}
}
// Plot pass in polar coords
void SatelliteTrackerGUI::plotPolarChart()
{
@ -916,6 +941,86 @@ void SatelliteTrackerGUI::plotPolarChart()
series[i]->attachAxis(radialAxis);
}
int redrawTime = 0;
if (m_settings.m_drawRotators != SatelliteTrackerSettings::NO_ROTATORS)
{
// Plot rotator position
QString ourSourceName = QString("F0:%1 %2").arg(m_satelliteTracker->getIndexInFeatureSet()).arg(m_satelliteTracker->getIdentifier()); // Only one feature set in practice?
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
for (int featureSetIndex = 0; featureSetIndex < featureSets.size(); featureSetIndex++)
{
FeatureSet *featureSet = featureSets[featureSetIndex];
for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++)
{
Feature *feature = featureSet->getFeatureAt(featureIndex);
if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller"))
{
QString source;
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller
int track = 0;
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track);
if ((m_settings.m_drawRotators == SatelliteTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track))
{
int onTarget = 0;
ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget);
if (!onTarget)
{
// Target azimuth red dotted line
double targetAzimuth, targetElevation;
bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth);
bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation);
if (targetAzimuthOk && targetElevationOk)
{
limitAzElRange(targetAzimuth, targetElevation);
QScatterSeries *rotatorSeries = new QScatterSeries();
QColor color(255, 0, 0, 150);
QPen pen(color);
rotatorSeries->setPen(pen);
rotatorSeries->setColor(color.darker());
rotatorSeries->setMarkerSize(20);
rotatorSeries->append(targetAzimuth, 90-targetElevation);
m_polarChart->addSeries(rotatorSeries);
rotatorSeries->attachAxis(angularAxis);
rotatorSeries->attachAxis(radialAxis);
redrawTime = 333;
}
}
// Current azimuth line. Yellow while off target, green on target.
double currentAzimuth, currentElevation;
bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth);
bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation);
if (currentAzimuthOk && currentElevationOk)
{
limitAzElRange(currentAzimuth, currentElevation);
QScatterSeries *rotatorSeries = new QScatterSeries();
QColor color;
if (onTarget) {
color = QColor(0, 255, 0, 150);
} else {
color = QColor(255, 255, 0, 150);
}
rotatorSeries->setPen(QPen(color));
rotatorSeries->setColor(color.darker());
rotatorSeries->setMarkerSize(20);
rotatorSeries->append(currentAzimuth, 90-currentElevation);
m_polarChart->addSeries(rotatorSeries);
rotatorSeries->attachAxis(angularAxis);
rotatorSeries->attachAxis(radialAxis);
redrawTime = 333;
}
}
}
}
}
}
// Create series with single point, so we can plot time of AOS
QLineSeries *aosSeries = new QLineSeries();
aosSeries->append(polarSeries->at(0));
@ -967,7 +1072,8 @@ void SatelliteTrackerGUI::plotPolarChart()
if ((currentTime >= pass.m_aos) && (currentTime <= pass.m_los))
{
// Create series with single point, so we can plot current time
QLineSeries *nowSeries = new QLineSeries();
QScatterSeries *nowSeries = new QScatterSeries();
nowSeries->setMarkerSize(3);
// Find closest point to current time
int idx = std::round(polarSeries->count() * (currentTime.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch())
/ (pass.m_los.toMSecsSinceEpoch() - pass.m_aos.toMSecsSinceEpoch()));
@ -978,8 +1084,16 @@ void SatelliteTrackerGUI::plotPolarChart()
m_polarChart->addSeries(nowSeries);
nowSeries->attachAxis(angularAxis);
nowSeries->attachAxis(radialAxis);
// Redraw in 5 seconds (call plotChart, incase user selects a different chart)
QTimer::singleShot(5000, this, &SatelliteTrackerGUI::plotChart);
if (!redrawTime) {
redrawTime = 5000;
}
}
if (redrawTime > 0)
{
// Redraw to show updated satellite position or rotator position
m_redrawTimer.setSingleShot(true);
m_redrawTimer.start(redrawTime);
}
delete polarSeries;
@ -1567,3 +1681,4 @@ void SatelliteTrackerGUI::makeUIConnections()
QObject::connect(ui->satTable->horizontalHeader(), &QHeaderView::sortIndicatorChanged, this, &SatelliteTrackerGUI::on_satTableHeader_sortIndicatorChanged);
QObject::connect(ui->deviceFeatureSelect, qOverload<int>(&QComboBox::currentIndexChanged), this, &SatelliteTrackerGUI::on_deviceFeatureSelect_currentIndexChanged);
}

View File

@ -82,6 +82,7 @@ private:
QChart m_emptyChart;
QChart *m_lineChart;
QPolarChart *m_polarChart;
QTimer m_redrawTimer;
QDateTime m_nextTargetAOS;
QDateTime m_nextTargetLOS;
@ -138,6 +139,7 @@ private:
void updateFileInputList();
void updateMapList();
void makeUIConnections();
void limitAzElRange(double& azimuth, double& elevation) const;
private slots:
void onMenuDialogCalled(const QPoint &p);

View File

@ -79,6 +79,7 @@ void SatelliteTrackerSettings::resetToDefaults()
m_dateTimeSelect = NOW;
m_mapFeature = "";
m_fileInputDevice = "";
m_drawRotators = MATCHING_TARGET;
m_workspaceIndex = 0;
m_columnSort = -1;
m_columnSortOrder = Qt::AscendingOrder;
@ -142,6 +143,7 @@ QByteArray SatelliteTrackerSettings::serialize() const
s.writeBlob(46, m_geometryBytes);
s.writeS32(47, m_columnSort);
s.writeS32(48, (int)m_columnSortOrder);
s.writeS32(49, (int)m_drawRotators);
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
s.writeS32(100 + i, m_columnIndexes[i]);
@ -237,6 +239,7 @@ bool SatelliteTrackerSettings::deserialize(const QByteArray& data)
d.readBlob(46, &m_geometryBytes);
d.readS32(47, &m_columnSort, -1);
d.readS32(48, (int *)&m_columnSortOrder, (int)Qt::AscendingOrder);
d.readS32(49, (int*)&m_drawRotators, (int)MATCHING_TARGET);
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
d.readS32(100 + i, &m_columnIndexes[i], i);
@ -398,6 +401,15 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co
if (settingsKeys.contains("dopplerPeriod")) {
m_dopplerPeriod = settings.m_dopplerPeriod;
}
if (settingsKeys.contains("predictionPeriod")) {
m_predictionPeriod = settings.m_predictionPeriod;
}
if (settingsKeys.contains("passStartTime")) {
m_passStartTime = settings.m_passStartTime;
}
if (settingsKeys.contains("passFinishTime")) {
m_passFinishTime = settings.m_passFinishTime;
}
if (settingsKeys.contains("defaultFrequency")) {
m_defaultFrequency = settings.m_defaultFrequency;
}
@ -410,23 +422,59 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co
if (settingsKeys.contains("aosSpeech")) {
m_aosSpeech = settings.m_aosSpeech;
}
if (settingsKeys.contains("aosCommand")) {
m_aosCommand = settings.m_aosCommand;
}
if (settingsKeys.contains("losSpeech")) {
m_losSpeech = settings.m_losSpeech;
}
if (settingsKeys.contains("aosCommand")) {
m_aosCommand = settings.m_aosCommand;
}
if (settingsKeys.contains("losCommand")) {
m_losCommand = settings.m_losCommand;
}
if (settingsKeys.contains("predictionPeriod")) {
m_predictionPeriod = settings.m_predictionPeriod;
if (settingsKeys.contains("chartsDarkTheme")) {
m_chartsDarkTheme = settings.m_chartsDarkTheme;
}
if (settingsKeys.contains("passStartTime")) {
m_passStartTime = settings.m_passStartTime;
if (settingsKeys.contains("deviceSettings")) {
m_deviceSettings = settings.m_deviceSettings;
}
if (settingsKeys.contains("replayEnabled")) {
m_replayEnabled = settings.m_replayEnabled;
}
if (settingsKeys.contains("replayStartDateTime")) {
m_replayStartDateTime = settings.m_replayStartDateTime;
}
if (settingsKeys.contains("sendTimeToMap")) {
m_sendTimeToMap = settings.m_sendTimeToMap;
}
if (settingsKeys.contains("dateTimeSelect")) {
m_dateTimeSelect = settings.m_dateTimeSelect;
}
if (settingsKeys.contains("mapFeature")) {
m_mapFeature = settings.m_mapFeature;
}
if (settingsKeys.contains("fileInputDevice")) {
m_fileInputDevice = settings.m_fileInputDevice;
}
if (settingsKeys.contains("drawRotators")) {
m_drawRotators = settings.m_drawRotators;
}
if (settingsKeys.contains("columnSort")) {
m_columnSort = settings.m_columnSort;
}
if (settingsKeys.contains("columnSortOrder")) {
m_columnSortOrder = settings.m_columnSortOrder;
}
if (settingsKeys.contains("columnIndexes"))
{
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
m_columnIndexes[i] = settings.m_columnIndexes[i];
}
}
if (settingsKeys.contains("columnSizes"))
{
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
m_columnSizes[i] = settings.m_columnSizes[i];
}
if (settingsKeys.contains("passFinishTime")) {
m_passFinishTime = settings.m_passFinishTime;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
@ -449,44 +497,8 @@ void SatelliteTrackerSettings::applySettings(const QStringList& settingsKeys, co
if (settingsKeys.contains("reverseAPIFeatureIndex")) {
m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("chartsDarkTheme")) {
m_chartsDarkTheme = settings.m_chartsDarkTheme;
}
if (settingsKeys.contains("replayEnabled")) {
m_replayEnabled = settings.m_replayEnabled;
}
if (settingsKeys.contains("replayStartDateTime")) {
m_replayStartDateTime = settings.m_replayStartDateTime;
}
if (settingsKeys.contains("sendTimeToMap")) {
m_sendTimeToMap = settings.m_sendTimeToMap;
}
if (settingsKeys.contains("dateTimeSelect")) {
m_dateTimeSelect = settings.m_dateTimeSelect;
}
if (settingsKeys.contains("columnSort")) {
m_columnSort = settings.m_columnSort;
}
if (settingsKeys.contains("columnSortOrder")) {
m_columnSortOrder = settings.m_columnSortOrder;
}
if (settingsKeys.contains("columnIndexes"))
{
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
m_columnIndexes[i] = settings.m_columnIndexes[i];
}
}
if (settingsKeys.contains("columnSizes"))
{
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
m_columnSizes[i] = settings.m_columnSizes[i];
}
}
if (settingsKeys.contains("deviceSettings")) {
m_deviceSettings = settings.m_deviceSettings;
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
}
@ -503,7 +515,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
if (settingsKeys.contains("heightAboveSeaLevel") || force) {
ostr << " m_heightAboveSeaLevel: " << m_heightAboveSeaLevel;
}
if (settingsKeys.contains("target") || force) {
ostr << " m_target: " << m_target.toStdString();
}
if (settingsKeys.contains("satellites") || force)
{
ostr << "m_satellites:";
@ -512,7 +526,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
ostr << " " << satellite.toStdString();
}
}
if (settingsKeys.contains("tles") || force)
{
ostr << " m_tles:";
@ -521,7 +534,6 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
ostr << " " << tle.toStdString();
}
}
if (settingsKeys.contains("dateTime") || force) {
ostr << " m_dateTime: " << m_dateTime.toStdString();
}
@ -555,6 +567,15 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
if (settingsKeys.contains("dopplerPeriod") || force) {
ostr << " m_dopplerPeriod: " << m_dopplerPeriod;
}
if (settingsKeys.contains("predictionPeriod") || force) {
ostr << " m_predictionPeriod: " << m_predictionPeriod;
}
if (settingsKeys.contains("passStartTime") || force) {
ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString();
}
if (settingsKeys.contains("passFinishTime") || force) {
ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString();
}
if (settingsKeys.contains("defaultFrequency") || force) {
ostr << " m_defaultFrequency: " << m_defaultFrequency;
}
@ -576,14 +597,68 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
if (settingsKeys.contains("losCommand") || force) {
ostr << " m_losCommand: " << m_losCommand.toStdString();
}
if (settingsKeys.contains("predictionPeriod") || force) {
ostr << " m_predictionPeriod: " << m_predictionPeriod;
if (settingsKeys.contains("chartsDarkTheme") || force) {
ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme;
}
if (settingsKeys.contains("passStartTime") || force) {
ostr << " m_passStartTime: " << m_passStartTime.toString().toStdString();
if (settingsKeys.contains("deviceSettings"))
{
ostr << "m_deviceSettings: [";
for (auto deviceSettingList : m_deviceSettings)
{
ostr << "[";
for (auto deviceSettings : *deviceSettingList) {
deviceSettings->getDebugString(ostr);
}
ostr << "]";
}
ostr << "]";
}
if (settingsKeys.contains("replayEnabled") || force) {
ostr << " m_replayEnabled: " << m_replayEnabled;
}
if (settingsKeys.contains("replayStartDateTime") || force) {
ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString();
}
if (settingsKeys.contains("sendTimeToMap") || force) {
ostr << " m_sendTimeToMap: " << m_sendTimeToMap;
}
if (settingsKeys.contains("dateTimeSelect") || force) {
ostr << " m_dateTimeSelect: " << m_dateTimeSelect;
}
if (settingsKeys.contains("mapFeature") || force) {
ostr << " m_mapFeature: " << m_mapFeature.toStdString();
}
if (settingsKeys.contains("fileInputDevice") || force) {
ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString();
}
if (settingsKeys.contains("drawRotators") || force) {
ostr << " m_drawRotators: " << m_drawRotators;
}
if (settingsKeys.contains("columnSort") || force) {
ostr << " m_columnSort: " << m_columnSort;
}
if (settingsKeys.contains("columnSortOrder") || force) {
ostr << " m_columnSortOrder: " << m_columnSortOrder;
}
if (settingsKeys.contains("columnIndexes"))
{
ostr << "m_columnIndexes:";
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
ostr << " " << m_columnIndexes[i];
}
}
if (settingsKeys.contains("columnSizes"))
{
ostr << "m_columnSizes:";
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
ostr << " " << m_columnSizes[i];
}
if (settingsKeys.contains("passFinishTime") || force) {
ostr << " m_passFinishTime: " << m_passFinishTime.toString().toStdString();
}
if (settingsKeys.contains("title") || force) {
ostr << " m_title: " << m_title.toStdString();
@ -606,72 +681,9 @@ QString SatelliteTrackerSettings::getDebugString(const QStringList& settingsKeys
if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("chartsDarkTheme") || force) {
ostr << " m_chartsDarkTheme: " << m_chartsDarkTheme;
}
if (settingsKeys.contains("replayEnabled") || force) {
ostr << " m_replayEnabled: " << m_replayEnabled;
}
if (settingsKeys.contains("replayStartDateTime") || force) {
ostr << " m_replayStartDateTime: " << m_replayStartDateTime.toString().toStdString();
}
if (settingsKeys.contains("sendTimeToMap") || force) {
ostr << " m_sendTimeToMap: " << m_sendTimeToMap;
}
if (settingsKeys.contains("dateTimeSelect") || force) {
ostr << " m_dateTimeSelect: " << m_dateTimeSelect;
}
if (settingsKeys.contains("mapFeature") || force) {
ostr << " m_mapFeature: " << m_mapFeature.toStdString();
}
if (settingsKeys.contains("fileInputDevice") || force) {
ostr << " m_fileInputDevice: " << m_fileInputDevice.toStdString();
}
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}
if (settingsKeys.contains("columnSort") || force) {
ostr << " m_columnSort: " << m_columnSort;
}
if (settingsKeys.contains("columnSortOrder") || force) {
ostr << " m_columnSortOrder: " << m_columnSortOrder;
}
if (settingsKeys.contains("columnIndexes"))
{
ostr << "m_columnIndexes:";
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
ostr << " " << m_columnIndexes[i];
}
}
if (settingsKeys.contains("columnSizes"))
{
ostr << "m_columnSizes:";
for (int i = 0; i < SAT_COL_COLUMNS; i++) {
ostr << " " << m_columnSizes[i];
}
}
if (settingsKeys.contains("deviceSettings"))
{
ostr << "m_deviceSettings: [";
for (auto deviceSettingList : m_deviceSettings)
{
ostr << "[";
for (auto deviceSettings : *deviceSettingList) {
deviceSettings->getDebugString(ostr);
}
ostr << "]";
}
ostr << "]";
}
return QString(ostr.str().c_str());
}
@ -696,3 +708,4 @@ void SatelliteTrackerSettings::SatelliteDeviceSettings::getDebugString(std::ostr
<< " m_aosCommand: " << m_aosCommand.toStdString()
<< " m_losCommand: " << m_losCommand.toStdString();
}

View File

@ -84,6 +84,7 @@ struct SatelliteTrackerSettings
enum DateTimeSelect {NOW, CUSTOM, FROM_MAP, FROM_FILE} m_dateTimeSelect;
QString m_mapFeature; //!< Which feature when FROM_MAP
QString m_fileInputDevice; //!< Which device when FROM_FILE
enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart
int m_columnSort; //!< Which column is used for sorting (-1 for none)
Qt::SortOrder m_columnSortOrder;

View File

@ -42,6 +42,7 @@ SatelliteTrackerSettingsDialog::SatelliteTrackerSettingsDialog(SatelliteTrackerS
ui->defaultFrequency->setValue(settings->m_defaultFrequency / 1000000.0);
ui->azElUnits->setCurrentIndex((int)settings->m_azElUnits);
ui->groundTrackPoints->setValue(settings->m_groundTrackPoints);
ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators);
ui->dateFormat->setText(settings->m_dateFormat);
ui->utc->setChecked(settings->m_utc);
ui->drawOnMap->setChecked(settings->m_drawOnMap);
@ -94,6 +95,7 @@ void SatelliteTrackerSettingsDialog::accept()
m_settings->m_defaultFrequency = (float)(ui->defaultFrequency->value() * 1000000.0);
m_settings->m_azElUnits = (SatelliteTrackerSettings::AzElUnits)ui->azElUnits->currentIndex();
m_settings->m_groundTrackPoints = ui->groundTrackPoints->value();
m_settings->m_drawRotators = (SatelliteTrackerSettings::Rotators)ui->drawRotators->currentIndex();
m_settings->m_dateFormat = ui->dateFormat->text();
m_settings->m_utc = ui->utc->isChecked();
m_settings->m_drawOnMap = ui->drawOnMap->isChecked();

View File

@ -339,75 +339,20 @@
<string>Display</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="defaultFrequency">
<property name="toolTip">
<string>Frequency used for Doppler and free space path loss calculations in the satellite table</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>50000.000000000000000</double>
</property>
<property name="singleStep">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="dateFormat">
<property name="toolTip">
<string>Format for dates displayed in the GUI</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QCheckBox" name="drawOnMap">
<widget class="QCheckBox" name="utc">
<property name="toolTip">
<string>When checked satellite positions are sent to the map</string>
<string>When checked times are dispayed using UTC rather than the local time zone</string>
</property>
<property name="text">
<string>Draw satellites on map</string>
<string>Display times in UTC</string>
</property>
</widget>
</item>
<item row="12" column="0">
<spacer name="verticalSpacer_2">
<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="0" column="0">
<widget class="QLabel" name="updatePeriodLabel">
<item row="4" column="0">
<widget class="QLabel" name="groundTrackPointsLabel">
<property name="text">
<string>Update period (s)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="updatePeriod">
<property name="toolTip">
<string>Enter the time in seconds between each calculation of the target's position</string>
</property>
<property name="maximum">
<double>3600.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
<string>Ground track points</string>
</property>
</widget>
</item>
@ -418,6 +363,20 @@
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="dateFormatLabel">
<property name="text">
<string>Date format</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="dateFormat">
<property name="toolTip">
<string>Format for dates displayed in the GUI</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="azElUnits">
<property name="toolTip">
@ -448,6 +407,48 @@
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="defaultFrequency">
<property name="toolTip">
<string>Frequency used for Doppler and free space path loss calculations in the satellite table</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>50000.000000000000000</double>
</property>
<property name="singleStep">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>100.000000000000000</double>
</property>
</widget>
</item>
<item row="13" column="0">
<spacer name="verticalSpacer_2">
<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="0" column="0">
<widget class="QLabel" name="updatePeriodLabel">
<property name="text">
<string>Update period (s)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="azElUnitsLabel">
<property name="text">
@ -455,27 +456,16 @@
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QCheckBox" name="utc">
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="updatePeriod">
<property name="toolTip">
<string>When checked times are dispayed using UTC rather than the local time zone</string>
<string>Enter the time in seconds between each calculation of the target's position</string>
</property>
<property name="text">
<string>Display times in UTC</string>
<property name="maximum">
<double>3600.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="dateFormatLabel">
<property name="text">
<string>Date format</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="groundTrackPointsLabel">
<property name="text">
<string>Ground track points</string>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
@ -492,6 +482,45 @@
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QCheckBox" name="drawOnMap">
<property name="toolTip">
<string>When checked satellite positions are sent to the map</string>
</property>
<property name="text">
<string>Draw satellites on map</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="drawRotators">
<property name="toolTip">
<string>Select which rotators are displayed on the polar chart</string>
</property>
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Matching target</string>
</property>
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="drawRotatorsLabel">
<property name="text">
<string>Rotators in polar chart</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="replayTab">
@ -614,8 +643,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
<x>257</x>
<y>533</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@ -630,8 +659,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
<x>325</x>
<y>533</y>
</hint>
<hint type="destinationlabel">
<x>286</x>

View File

@ -134,7 +134,7 @@ void getPassAzEl(QLineSeries* azimuth, QLineSeries* elevation, QLineSeries* pola
DateTime aosTime = qDateTimeToDateTime(aos);
DateTime losTime = qDateTimeToDateTime(los);
DateTime currentTime(aosTime);
int steps = 20;
int steps = 150; // Needs to be high enough, so rotator intersect with satellite position
double timeStep = (losTime - aosTime).TotalSeconds() / steps;
if (timeStep <= 0.0)

View File

@ -49,6 +49,8 @@ Pressing this button displays a settings dialog, that allows you to set:
* The units to display the solar flux in, either Solar Flux Units, Jansky or Wm^-2Hz-1. 1 sfu equals 10,000 Jansky or 10^-22 Wm^-2Hz-1.
* The update period in seconds, which controls how frequently azimuth and elevation are re-calculated.
* The IP port number the Stellarium server listens on.
* Which rotators are displayed on the polar chart. This can be All, None or Matching target. When Matching target is selected, the rotator will
only be displayed if the source in the Rotator Controller is set to this Star Tracker and Track is enabled.
* Whether to start a Stellarium telescope server.
* Whether to draw the Sun in the map.
* Whether to draw the Moon on the map.

View File

@ -33,8 +33,11 @@
#include "SWGStarTrackerDisplaySettings.h"
#include "SWGStarTrackerDisplayLoSSettings.h"
#include "feature/featureset.h"
#include "feature/featureuiset.h"
#include "feature/featureutils.h"
#include "feature/featurewebapiutils.h"
#include "channel/channelwebapiutils.h"
#include "gui/basicfeaturesettingsdialog.h"
#include "gui/dmsspinbox.h"
#include "gui/graphicsviewzoom.h"
@ -285,6 +288,8 @@ StarTrackerGUI::StarTrackerGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet,
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
m_statusTimer.start(1000);
connect(&m_redrawTimer, &QTimer::timeout, this, &StarTrackerGUI::plotChart);
connect(ui->azimuth, SIGNAL(valueChanged(double)), this, SLOT(on_azimuth_valueChanged(double)));
ui->azimuth->setRange(0, 360.0);
ui->elevation->setRange(-90.0, 90.0);
@ -812,7 +817,7 @@ void StarTrackerGUI::on_displaySettings_clicked()
ui->galacticLongitude->setUnits((DMSSpinBox::DisplayUnits)m_settings.m_azElUnits);
displaySolarFlux();
if (ui->chartSelect->currentIndex() == 1) {
if (ui->chartSelect->currentIndex() <= 1) {
plotChart();
}
}
@ -1651,6 +1656,26 @@ void StarTrackerGUI::plotElevationLineChart()
delete oldChart;
}
// Reduce az/el range from 450,180 to 360,90
void StarTrackerGUI::limitAzElRange(double& azimuth, double& elevation) const
{
if (elevation > 90.0)
{
elevation = 180.0 - elevation;
if (azimuth < 180.0) {
azimuth += 180.0;
} else {
azimuth -= 180.0;
}
}
if (azimuth > 360.0) {
azimuth -= 360.0f;
}
if (azimuth == 0) {
azimuth = 360.0;
}
}
// Plot target elevation angle over the day
void StarTrackerGUI::plotElevationPolarChart()
{
@ -1841,6 +1866,92 @@ void StarTrackerGUI::plotElevationPolarChart()
series[i]->attachAxis(radialAxis);
}
if (m_settings.m_drawRotators != StarTrackerSettings::NO_ROTATORS)
{
int redrawTime = 0;
// Plot rotator position
QString ourSourceName = QString("F0:%1 %2").arg(m_starTracker->getIndexInFeatureSet()).arg(m_starTracker->getIdentifier()); // Only one feature set in practice?
std::vector<FeatureSet*>& featureSets = MainCore::instance()->getFeatureeSets();
for (int featureSetIndex = 0; featureSetIndex < featureSets.size(); featureSetIndex++)
{
FeatureSet *featureSet = featureSets[featureSetIndex];
for (int featureIndex = 0; featureIndex < featureSet->getNumberOfFeatures(); featureIndex++)
{
Feature *feature = featureSet->getFeatureAt(featureIndex);
if (FeatureUtils::compareFeatureURIs(feature->getURI(), "sdrangel.feature.gs232controller"))
{
QString source;
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "source", source); // Will return false if source isn't set in Controller
int track = 0;
ChannelWebAPIUtils::getFeatureSetting(featureSetIndex, featureIndex, "track", track);
if ((m_settings.m_drawRotators == StarTrackerSettings::ALL_ROTATORS) || ((source == ourSourceName) && track))
{
int onTarget = 0;
ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "onTarget", onTarget);
if (!onTarget)
{
// Target azimuth red dotted line
double targetAzimuth, targetElevation;
bool targetAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetAzimuth", targetAzimuth);
bool targetElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "targetElevation", targetElevation);
if (targetAzimuthOk && targetElevationOk)
{
limitAzElRange(targetAzimuth, targetElevation);
QScatterSeries *rotatorSeries = new QScatterSeries();
QColor color(255, 0, 0, 150);
QPen pen(color);
rotatorSeries->setPen(pen);
rotatorSeries->setColor(color.darker());
rotatorSeries->setMarkerSize(20);
rotatorSeries->append(targetAzimuth, 90-targetElevation);
m_azElPolarChart->addSeries(rotatorSeries);
rotatorSeries->attachAxis(angularAxis);
rotatorSeries->attachAxis(radialAxis);
redrawTime = 333;
}
}
// Current azimuth line. Yellow while off target, green on target.
double currentAzimuth, currentElevation;
bool currentAzimuthOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentAzimuth", currentAzimuth);
bool currentElevationOk = ChannelWebAPIUtils::getFeatureReportValue(featureSetIndex, featureIndex, "currentElevation", currentElevation);
if (currentAzimuthOk && currentElevationOk)
{
limitAzElRange(currentAzimuth, currentElevation);
QScatterSeries *rotatorSeries = new QScatterSeries();
QColor color;
if (onTarget) {
color = QColor(0, 255, 0, 150);
} else {
color = QColor(255, 255, 0, 150);
}
rotatorSeries->setPen(QPen(color));
rotatorSeries->setColor(color.darker());
rotatorSeries->setMarkerSize(20);
rotatorSeries->append(currentAzimuth, 90-currentElevation);
m_azElPolarChart->addSeries(rotatorSeries);
rotatorSeries->attachAxis(angularAxis);
rotatorSeries->attachAxis(radialAxis);
redrawTime = 333;
}
}
}
}
}
if (redrawTime > 0)
{
// Redraw to show updated rotator position
// Update period may be long or custom time might be fixed
m_redrawTimer.setSingleShot(true);
m_redrawTimer.start(redrawTime);
}
}
// Create series with single point, so we can plot time of rising
if (riseTime.isValid())
{
@ -2242,3 +2353,4 @@ void StarTrackerGUI::makeUIConnections()
QObject::connect(ui->drawSun, &QToolButton::clicked, this, &StarTrackerGUI::on_drawSun_clicked);
QObject::connect(ui->drawMoon, &QToolButton::clicked, this, &StarTrackerGUI::on_drawMoon_clicked);
}

View File

@ -90,6 +90,7 @@ private:
QChart *m_azElLineChart;
QPolarChart *m_azElPolarChart;
QTimer m_redrawTimer;
QChart m_chart;
QDateTimeAxis m_chartXAxis;
@ -161,6 +162,7 @@ private:
void updateChartSubSelect();
void updateSolarFlux(bool all);
void makeUIConnections();
void limitAzElRange(double& azimuth, double& elevation) const;
private slots:
void onMenuDialogCalled(const QPoint &p);

View File

@ -83,6 +83,7 @@ void StarTrackerSettings::resetToDefaults()
m_drawSunOnSkyTempChart = true;
m_drawMoonOnSkyTempChart = true;
m_workspaceIndex = 0;
m_drawRotators = MATCHING_TARGET;
}
QByteArray StarTrackerSettings::serialize() const
@ -139,6 +140,7 @@ QByteArray StarTrackerSettings::serialize() const
s.writeS32(45, m_workspaceIndex);
s.writeBlob(46, m_geometryBytes);
s.writeS32(47, (int)m_drawRotators);
return s.final();
}
@ -227,6 +229,7 @@ bool StarTrackerSettings::deserialize(const QByteArray& data)
d.readS32(45, &m_workspaceIndex, 0);
d.readBlob(46, &m_geometryBytes);
d.readS32(47, (int *)&m_drawRotators, MATCHING_TARGET);
return true;
}
@ -374,6 +377,9 @@ void StarTrackerSettings::applySettings(const QStringList& settingsKeys, const S
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
if (settingsKeys.contains("drawRotators")) {
m_drawRotators = settings.m_drawRotators;
}
}
QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, bool force) const
@ -512,6 +518,9 @@ QString StarTrackerSettings::getDebugString(const QStringList& settingsKeys, boo
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}
if (settingsKeys.contains("drawRotators") || force) {
ostr << " m_drawRotators: " << m_drawRotators;
}
return QString(ostr.str().c_str());
}

View File

@ -74,6 +74,7 @@ struct StarTrackerSettings
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
enum Rotators {ALL_ROTATORS, NO_ROTATORS, MATCHING_TARGET} m_drawRotators; //!< Which rotators to draw on polar chart
StarTrackerSettings();
void resetToDefaults();

View File

@ -44,8 +44,10 @@ StarTrackerSettingsDialog::StarTrackerSettingsDialog(
ui->temperatureLapseRate->setValue(settings->m_temperatureLapseRate);
ui->solarFluxData->setCurrentIndex((int)settings->m_solarFluxData);
ui->solarFluxUnits->setCurrentIndex((int)settings->m_solarFluxUnits);
ui->drawRotators->setCurrentIndex((int)settings->m_drawRotators);
ui->drawSunOnMap->setChecked(settings->m_drawSunOnMap);
ui->drawMoonOnMap->setChecked(settings->m_drawMoonOnMap);
ui->drawStarOnMap->setChecked(settings->m_drawStarOnMap);
}
StarTrackerSettingsDialog::~StarTrackerSettingsDialog()
@ -70,6 +72,7 @@ void StarTrackerSettingsDialog::accept()
m_settings->m_temperatureLapseRate = ui->temperatureLapseRate->value();
m_settings->m_solarFluxData = (StarTrackerSettings::SolarFluxData)ui->solarFluxData->currentIndex();
m_settings->m_solarFluxUnits = (StarTrackerSettings::SolarFluxUnits)ui->solarFluxUnits->currentIndex();
m_settings->m_drawRotators = (StarTrackerSettings::Rotators)ui->drawRotators->currentIndex();
m_settings->m_drawSunOnMap = ui->drawSunOnMap->isChecked();
m_settings->m_drawMoonOnMap = ui->drawMoonOnMap->isChecked();
m_settings->m_drawStarOnMap = ui->drawStarOnMap->isChecked();
@ -89,6 +92,7 @@ void StarTrackerSettingsDialog::accept()
m_settingsKeys.append("temperatureLapseRate");
m_settingsKeys.append("solarFluxData");
m_settingsKeys.append("solarFluxUnits");
m_settingsKeys.append("drawRotators");
m_settingsKeys.append("drawSunOnMap");
m_settingsKeys.append("drawMoonOnMap");
m_settingsKeys.append("drawStarOnMap");

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>569</width>
<height>535</height>
<height>556</height>
</rect>
</property>
<property name="font">
@ -23,24 +23,7 @@
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QComboBox" name="epoch">
<property name="toolTip">
<string>Epoch for custom right ascension and declination</string>
</property>
<item>
<property name="text">
<string>J2000</string>
</property>
</item>
<item>
<property name="text">
<string>JNOW</string>
</property>
</item>
</widget>
</item>
<item row="15" column="0">
<item row="16" column="0">
<widget class="QCheckBox" name="enableServer">
<property name="toolTip">
<string>Enable Stellarium server which allows RA and Dec to be sent to and from Stellarium</string>
@ -50,74 +33,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="pressureLabel">
<property name="text">
<string>Air pressure (mb)</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="humidity">
<property name="toolTip">
<string>Relative humidity in %</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>80</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="temperatureLapseRateLabel">
<property name="toolTip">
<string>Temperature lapse rate (K/m)</string>
</property>
<property name="text">
<string>Temperature lapse rate (K/km)</string>
</property>
</widget>
</item>
<item row="17" column="0">
<widget class="QCheckBox" name="drawMoonOnMap">
<property name="text">
<string>Draw Moon on map</string>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QCheckBox" name="drawSunOnMap">
<property name="text">
<string>Draw Sun on map</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="temperatureLabel">
<property name="text">
<string>Air temperature (C)</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="pressure">
<property name="toolTip">
<string>Air pressure in millibars, for use in atmospheric refraction correction</string>
</property>
<property name="maximum">
<double>2000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>1010.000000000000000</double>
</property>
</widget>
</item>
<item row="20" column="1">
<item row="21" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -130,52 +46,13 @@
</property>
</spacer>
</item>
<item row="18" column="0">
<widget class="QCheckBox" name="drawStarOnMap">
<item row="17" column="0">
<widget class="QCheckBox" name="drawSunOnMap">
<property name="text">
<string>Draw target star on map</string>
<string>Draw Sun on map</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="weatherUpdatePeriodLabel">
<property name="text">
<string>Weather update period (min)</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="solarFluxDataLabel">
<property name="text">
<string>Solar flux density data</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="refraction">
<property name="toolTip">
<string>Atmospheric refraction correction</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Saemundsson</string>
</property>
</item>
<item>
<property name="text">
<string>Positional Astronomy Library</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="azElUnits">
<property name="toolTip">
@ -206,153 +83,58 @@
</item>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="temperature">
<property name="toolTip">
<string>Air temperature in degrees Celsius, for use in atmospheric refraction correction</string>
<item row="6" column="0">
<widget class="QLabel" name="temperatureLabel">
<property name="text">
<string>Air temperature (C)</string>
</property>
<property name="minimum">
<number>-100</number>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="humidity">
<property name="toolTip">
<string>Relative humidity in %</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>10</number>
<number>80</number>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="solarFluxUnitsLabel">
<property name="text">
<string>Solar flux density units</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QDoubleSpinBox" name="temperatureLapseRate">
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>6.490000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="refractionLabel">
<property name="text">
<string>Refraction correction</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QComboBox" name="solarFluxUnits">
<item row="2" column="1">
<widget class="QComboBox" name="refraction">
<property name="toolTip">
<string>Units to use for the display of the Solar flux density</string>
<string>Atmospheric refraction correction</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Solar flux units (sfu)</string>
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Jansky (Jy)</string>
<string>Saemundsson</string>
</property>
</item>
<item>
<property name="text">
<string>Watts per square metre per hertz (W m^-2 Hz-1)</string>
<string>Positional Astronomy Library</string>
</property>
</item>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="heightLabel">
<property name="text">
<string>Height above sea level (m)</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="owmAPIKey">
<item row="9" column="0">
<widget class="QLabel" name="temperatureLapseRateLabel">
<property name="toolTip">
<string>API key from openweathermap.org to download real-time weather</string>
<string>Temperature lapse rate (K/m)</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="serverPortLabel">
<property name="text">
<string>Stellarium server port</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="epochLabel">
<property name="text">
<string>Epoch for RA &amp; Dec</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="azElUnitsLabel">
<property name="text">
<string>Azimuth and elevation units</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="owmAPIKeyLabel">
<property name="text">
<string>OpenWeatherMap API Key</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="serverPort">
<property name="toolTip">
<string>Stellarium telescope server IP port number</string>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>10001</number>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="updatePeriod">
<property name="toolTip">
<string>Enter the time in seconds between each calculation of the target's position</string>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="updatePeriodLabel">
<property name="text">
<string>Update period (s)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="weatherUpdatePeriod">
<property name="toolTip">
<string>Enter the time in minutes between each weather update</string>
</property>
<property name="maximum">
<number>100000</number>
<string>Temperature lapse rate (K/km)</string>
</property>
</widget>
</item>
@ -363,16 +145,68 @@
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="height">
<property name="toolTip">
<string>Height of observation/antenna location above sea level in metres</string>
<item row="0" column="0">
<widget class="QLabel" name="epochLabel">
<property name="text">
<string>Epoch for RA &amp; Dec</string>
</property>
<property name="minimum">
<number>-1000</number>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="weatherUpdatePeriodLabel">
<property name="text">
<string>Weather update period (min)</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="pressureLabel">
<property name="text">
<string>Air pressure (mb)</string>
</property>
</widget>
</item>
<item row="19" column="0">
<widget class="QCheckBox" name="drawStarOnMap">
<property name="text">
<string>Draw target star on map</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="owmAPIKeyLabel">
<property name="text">
<string>OpenWeatherMap API Key</string>
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QLabel" name="solarFluxUnitsLabel">
<property name="text">
<string>Solar flux density units</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="pressure">
<property name="toolTip">
<string>Air pressure in millibars, for use in atmospheric refraction correction</string>
</property>
<property name="maximum">
<number>20000</number>
<double>2000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>1010.000000000000000</double>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="heightLabel">
<property name="text">
<string>Height above sea level (m)</string>
</property>
</widget>
</item>
@ -433,6 +267,201 @@
</item>
</widget>
</item>
<item row="9" column="1">
<widget class="QDoubleSpinBox" name="temperatureLapseRate">
<property name="decimals">
<number>3</number>
</property>
<property name="maximum">
<double>100.000000000000000</double>
</property>
<property name="value">
<double>6.490000000000000</double>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="owmAPIKey">
<property name="toolTip">
<string>API key from openweathermap.org to download real-time weather</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="temperature">
<property name="toolTip">
<string>Air temperature in degrees Celsius, for use in atmospheric refraction correction</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="18" column="0">
<widget class="QCheckBox" name="drawMoonOnMap">
<property name="text">
<string>Draw Moon on map</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="updatePeriodLabel">
<property name="text">
<string>Update period (s)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="epoch">
<property name="toolTip">
<string>Epoch for custom right ascension and declination</string>
</property>
<item>
<property name="text">
<string>J2000</string>
</property>
</item>
<item>
<property name="text">
<string>JNOW</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="refractionLabel">
<property name="text">
<string>Refraction correction</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="solarFluxDataLabel">
<property name="text">
<string>Solar flux density data</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QComboBox" name="solarFluxUnits">
<property name="toolTip">
<string>Units to use for the display of the Solar flux density</string>
</property>
<item>
<property name="text">
<string>Solar flux units (sfu)</string>
</property>
</item>
<item>
<property name="text">
<string>Jansky (Jy)</string>
</property>
</item>
<item>
<property name="text">
<string>Watts per square metre per hertz (W m^-2 Hz-1)</string>
</property>
</item>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="height">
<property name="toolTip">
<string>Height of observation/antenna location above sea level in metres</string>
</property>
<property name="minimum">
<number>-1000</number>
</property>
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QDoubleSpinBox" name="updatePeriod">
<property name="toolTip">
<string>Enter the time in seconds between each calculation of the target's position</string>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QSpinBox" name="serverPort">
<property name="toolTip">
<string>Stellarium telescope server IP port number</string>
</property>
<property name="minimum">
<number>1024</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>10001</number>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="serverPortLabel">
<property name="text">
<string>Stellarium server port</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="weatherUpdatePeriod">
<property name="toolTip">
<string>Enter the time in minutes between each weather update</string>
</property>
<property name="maximum">
<number>100000</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="azElUnitsLabel">
<property name="text">
<string>Azimuth and elevation units</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="drawRotatorsLabel">
<property name="text">
<string>Rotators in polar chart</string>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QComboBox" name="drawRotators">
<property name="toolTip">
<string>Select which rotators are displayed on the polar chart</string>
</property>
<item>
<property name="text">
<string>All</string>
</property>
</item>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Matching target</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>

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

@ -15,6 +15,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <cmath>
#include <QPainter>
#include "coursedeviationindicator.h"
@ -46,6 +48,8 @@ void CourseDeviationIndicator::setGlideSlopeDDM(float ddm)
void CourseDeviationIndicator::paintEvent(QPaintEvent *event)
{
(void) event;
QPainter painter(this);
QRect r = rect();
@ -168,7 +172,6 @@ void CourseDeviationIndicator::paintEvent(QPaintEvent *event)
{
QFontMetrics fm(painter.font());
QString text = "G/S";
int tw = fm.horizontalAdvance(text);
int th = fm.ascent() / 2;
painter.setPen(QColor(0, 255, 0));
painter.drawText(midW + bgw + 2, midH + th, text);

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;