diff --git a/doc/img/GS232Controller_plugin.png b/doc/img/GS232Controller_plugin.png index 9dcef6887..a7510cdd8 100644 Binary files a/doc/img/GS232Controller_plugin.png and b/doc/img/GS232Controller_plugin.png differ diff --git a/plugins/feature/gs232controller/gs232controller.cpp b/plugins/feature/gs232controller/gs232controller.cpp index 6e83a841e..d9cf785a1 100644 --- a/plugins/feature/gs232controller/gs232controller.cpp +++ b/plugins/feature/gs232controller/gs232controller.cpp @@ -216,6 +216,8 @@ void GS232Controller::applySettings(const GS232ControllerSettings& settings, boo << " m_azimuthMax: " << settings.m_azimuthMax << " m_elevationMin: " << settings.m_elevationMin << " m_elevationMax: " << settings.m_elevationMax + << " m_tolerance: " << settings.m_tolerance + << " m_protocol: " << settings.m_protocol << " m_serialPort: " << settings.m_serialPort << " m_baudRate: " << settings.m_baudRate << " m_track: " << settings.m_track @@ -274,6 +276,12 @@ void GS232Controller::applySettings(const GS232ControllerSettings& settings, boo if ((m_settings.m_elevationMin != settings.m_elevationMin) || force) { reverseAPIKeys.append("elevationMin"); } + if ((m_settings.m_tolerance != settings.m_tolerance) || force) { + reverseAPIKeys.append("tolerance"); + } + if ((m_settings.m_protocol != settings.m_protocol) || force) { + reverseAPIKeys.append("m_protocol"); + } if ((m_settings.m_title != settings.m_title) || force) { reverseAPIKeys.append("title"); } @@ -360,6 +368,8 @@ void GS232Controller::webapiFormatFeatureSettings( response.getGs232ControllerSettings()->setAzimuthMax(settings.m_azimuthMax); response.getGs232ControllerSettings()->setElevationMin(settings.m_elevationMin); response.getGs232ControllerSettings()->setElevationMax(settings.m_elevationMax); + response.getGs232ControllerSettings()->setTolerance(settings.m_tolerance); + response.getGs232ControllerSettings()->setProtocol(settings.m_protocol); if (response.getGs232ControllerSettings()->getTitle()) { *response.getGs232ControllerSettings()->getTitle() = settings.m_title; @@ -420,6 +430,12 @@ void GS232Controller::webapiUpdateFeatureSettings( if (featureSettingsKeys.contains("elevationMax")) { settings.m_elevationMax = response.getGs232ControllerSettings()->getElevationMax(); } + if (featureSettingsKeys.contains("tolerance")) { + settings.m_tolerance = response.getGs232ControllerSettings()->getTolerance(); + } + if (featureSettingsKeys.contains("protocol")) { + settings.m_protocol = (GS232ControllerSettings::Protocol)response.getGs232ControllerSettings()->getProtocol(); + } if (featureSettingsKeys.contains("title")) { settings.m_title = *response.getGs232ControllerSettings()->getTitle(); } @@ -484,6 +500,12 @@ void GS232Controller::webapiReverseSendSettings(QList& featureSettingsK if (featureSettingsKeys.contains("elevationMax") || force) { swgGS232ControllerSettings->setElevationMax(settings.m_elevationMax); } + if (featureSettingsKeys.contains("tolerance") || force) { + swgGS232ControllerSettings->setTolerance(settings.m_tolerance); + } + if (featureSettingsKeys.contains("protocol") || force) { + swgGS232ControllerSettings->setProtocol((int)settings.m_protocol); + } if (featureSettingsKeys.contains("title") || force) { swgGS232ControllerSettings->setTitle(new QString(settings.m_title)); } diff --git a/plugins/feature/gs232controller/gs232controllergui.cpp b/plugins/feature/gs232controller/gs232controllergui.cpp index f53445912..e0109d61c 100644 --- a/plugins/feature/gs232controller/gs232controllergui.cpp +++ b/plugins/feature/gs232controller/gs232controllergui.cpp @@ -94,8 +94,8 @@ bool GS232ControllerGUI::handleMessage(const Message& message) else if (GS232ControllerReport::MsgReportAzAl::match(message)) { GS232ControllerReport::MsgReportAzAl& azAl = (GS232ControllerReport::MsgReportAzAl&) message; - ui->azimuthCurrentText->setText(QString("%1").arg(round(azAl.getAzimuth()))); - ui->elevationCurrentText->setText(QString("%1").arg(round(azAl.getElevation()))); + ui->azimuthCurrentText->setText(QString("%1").arg(azAl.getAzimuth())); + ui->elevationCurrentText->setText(QString("%1").arg(azAl.getElevation())); return true; } else if (MainCore::MsgTargetAzimuthElevation::match(message)) @@ -153,6 +153,9 @@ GS232ControllerGUI::GS232ControllerGUI(PluginAPI* pluginAPI, FeatureUISet *featu connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); m_statusTimer.start(1000); + ui->azimuthCurrentText->setText("-"); + ui->elevationCurrentText->setText("-"); + updateSerialPortList(); displaySettings(); applySettings(true); @@ -175,6 +178,8 @@ void GS232ControllerGUI::displaySettings() blockApplySettings(true); ui->azimuth->setValue(m_settings.m_azimuth); ui->elevation->setValue(m_settings.m_elevation); + ui->protocol->setCurrentIndex((int)m_settings.m_protocol); + updateDecimals(m_settings.m_protocol); if (m_settings.m_serialPort.length() > 0) ui->serialPort->lineEdit()->setText(m_settings.m_serialPort); ui->baudRate->setCurrentText(QString("%1").arg(m_settings.m_baudRate)); @@ -281,6 +286,27 @@ void GS232ControllerGUI::on_startStop_toggled(bool checked) } } +void GS232ControllerGUI::updateDecimals(GS232ControllerSettings::Protocol protocol) +{ + if (protocol == GS232ControllerSettings::GS232) + { + ui->azimuth->setDecimals(0); + ui->elevation->setDecimals(0); + } + else + { + ui->azimuth->setDecimals(1); + ui->elevation->setDecimals(1); + } +} + +void GS232ControllerGUI::on_protocol_currentIndexChanged(int index) +{ + m_settings.m_protocol = (GS232ControllerSettings::Protocol)index; + updateDecimals(m_settings.m_protocol); + applySettings(); +} + void GS232ControllerGUI::on_serialPort_currentIndexChanged(int index) { (void) index; @@ -295,16 +321,16 @@ void GS232ControllerGUI::on_baudRate_currentIndexChanged(int index) applySettings(); } -void GS232ControllerGUI::on_azimuth_valueChanged(int value) +void GS232ControllerGUI::on_azimuth_valueChanged(double value) { - m_settings.m_azimuth = value; + m_settings.m_azimuth = (float)value; ui->targetName->setText(""); applySettings(); } -void GS232ControllerGUI::on_elevation_valueChanged(int value) +void GS232ControllerGUI::on_elevation_valueChanged(double value) { - m_settings.m_elevation = value; + m_settings.m_elevation = (float)value; ui->targetName->setText(""); applySettings(); } @@ -345,6 +371,12 @@ void GS232ControllerGUI::on_elevationMax_valueChanged(int value) applySettings(); } +void GS232ControllerGUI::on_tolerance_valueChanged(int value) +{ + m_settings.m_tolerance = value; + applySettings(); +} + void GS232ControllerGUI::on_track_stateChanged(int state) { m_settings.m_track = state == Qt::Checked; diff --git a/plugins/feature/gs232controller/gs232controllergui.h b/plugins/feature/gs232controller/gs232controllergui.h index 8d46300e2..9fbfa8dda 100644 --- a/plugins/feature/gs232controller/gs232controllergui.h +++ b/plugins/feature/gs232controller/gs232controllergui.h @@ -64,6 +64,7 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); void displaySettings(); + void updateDecimals(GS232ControllerSettings::Protocol protocol); void updatePipeList(); void updateSerialPortList(); bool handleMessage(const Message& message); @@ -76,11 +77,12 @@ private slots: void onWidgetRolled(QWidget* widget, bool rollDown); void handleInputMessages(); void on_startStop_toggled(bool checked); + void on_protocol_currentIndexChanged(int index); void on_serialPort_currentIndexChanged(int index); void on_baudRate_currentIndexChanged(int index); void on_track_stateChanged(int state); - void on_azimuth_valueChanged(int value); - void on_elevation_valueChanged(int value); + void on_azimuth_valueChanged(double value); + void on_elevation_valueChanged(double value); void on_targets_currentTextChanged(const QString& text); void on_azimuthOffset_valueChanged(int value); void on_elevationOffset_valueChanged(int value); @@ -88,8 +90,8 @@ private slots: void on_azimuthMax_valueChanged(int value); void on_elevationMin_valueChanged(int value); void on_elevationMax_valueChanged(int value); + void on_tolerance_valueChanged(int value); void updateStatus(); }; - #endif // INCLUDE_FEATURE_GS232CONTROLLERGUI_H_ diff --git a/plugins/feature/gs232controller/gs232controllergui.ui b/plugins/feature/gs232controller/gs232controllergui.ui index 4e0c067dd..1ce95168a 100644 --- a/plugins/feature/gs232controller/gs232controllergui.ui +++ b/plugins/feature/gs232controller/gs232controllergui.ui @@ -6,8 +6,8 @@ 0 0 - 350 - 223 + 360 + 231 @@ -18,13 +18,13 @@ - 320 + 360 100 - 350 + 360 16777215 @@ -42,8 +42,8 @@ 10 10 - 331 - 191 + 341 + 211 @@ -70,7 +70,7 @@ - start/stop acquisition + Start/stop controller @@ -103,18 +103,18 @@ - + Target azimuth in degrees - - 0 + + 1 - 450 + 450.000000000000000 - 359 + 360.000000000000000 @@ -122,7 +122,7 @@ - 23 + 32 0 @@ -130,7 +130,7 @@ Current azimuth in degrees - - + 360.0 @@ -149,15 +149,18 @@ - + Target elevation in degrees + + 1 + - 180 + 180.000000000000000 - 180 + 180.000000000000000 @@ -165,7 +168,7 @@ - 22 + 32 0 @@ -173,75 +176,7 @@ Current elevation in degrees - - - - - - - - - - - - - Check to enable automatic tracking of azimuth and elevation from the specified channel - - - Track - - - - - - - Source - - - - - - - - 150 - 0 - - - - Target to track - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Target - - - - - - - Name of the target being tracked as indicated by the source channel / feature - - - true + 180.0 @@ -249,49 +184,60 @@ - - + + - Baud rate + Tolerance + + + + + + + Azimuth offset + + + + + + + Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment + + + -360 + + + 360 + + + + + + + Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment + + + -180 + + + 180 + + + 1 - - - 180 + + + Name of serial port to use to connect to the GS-232 controller - - - - - - Elevation min - - - - - - - Azimuth min - - - - - - - Elevation max + + true - - - 180 - - - - Serial port baud rate for the GS-232 controller @@ -351,84 +297,158 @@ - - - - 450 - - - - - - - 450 - - - - - - - Name of serial port to use to connect to the GS-232 controller - - - true - - - - + Serial Port - - - - Azimuth max - - - - - - - Azimuth offset - - - - - - - Specify an offset angel in degrees that will be added to the target azimuth to correct for misalignment - - - -360 - - - 360 - - - - + Elevation offset - - - - Specify an offset angle in degrees that will be added to the target elevation to correct for misalignment - - - -180 + + + + Elevation max + + + + 180 - - 1 + + + + + + Command protocol + + + + GS-232 + + + + + SPID + + + + + + + + Azimuth min + + + + + + + 450 + + + + + + + Tolerance in degrees + + + + + + + Protocol + + + + + + + 180 + + + + + + + Elevation min + + + + + + + Baud rate + + + + + + + 450 + + + + + + + Azimuth max + + + + + + + Check to enable automatic tracking of azimuth and elevation from the specified channel + + + Track + + + + + + + Name of the target being tracked as indicated by the source channel / feature + + + true + + + + + + + Target + + + + + + + Source + + + + + + + + 150 + 0 + + + + Target to track @@ -454,9 +474,6 @@ startStop azimuth elevation - track - targets - targetName serialPort baudRate azimuthOffset diff --git a/plugins/feature/gs232controller/gs232controllerplugin.cpp b/plugins/feature/gs232controller/gs232controllerplugin.cpp index 3bd347824..b8974314c 100644 --- a/plugins/feature/gs232controller/gs232controllerplugin.cpp +++ b/plugins/feature/gs232controller/gs232controllerplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor GS232ControllerPlugin::m_pluginDescriptor = { GS232Controller::m_featureId, QStringLiteral("GS-232 Rotator Controller"), - QStringLiteral("6.13.0"), + QStringLiteral("6.13.1"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/feature/gs232controller/gs232controllersettings.cpp b/plugins/feature/gs232controller/gs232controllersettings.cpp index 298f296e0..12f01d643 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.cpp +++ b/plugins/feature/gs232controller/gs232controllersettings.cpp @@ -44,8 +44,8 @@ GS232ControllerSettings::GS232ControllerSettings() void GS232ControllerSettings::resetToDefaults() { - m_azimuth = 0; - m_elevation = 0; + m_azimuth = 0.0f; + m_elevation = 0.0f; m_serialPort = ""; m_baudRate = 9600; m_track = false; @@ -63,14 +63,16 @@ void GS232ControllerSettings::resetToDefaults() m_azimuthMax = 450; m_elevationMin = 0; m_elevationMax = 180; + m_tolerance = 0; + m_protocol = GS232; } QByteArray GS232ControllerSettings::serialize() const { SimpleSerializer s(1); - s.writeS32(1, m_azimuth); - s.writeS32(2, m_elevation); + s.writeFloat(1, m_azimuth); + s.writeFloat(2, m_elevation); s.writeString(3, m_serialPort); s.writeS32(4, m_baudRate); s.writeBool(5, m_track); @@ -88,6 +90,8 @@ QByteArray GS232ControllerSettings::serialize() const s.writeS32(18, m_azimuthMax); s.writeS32(19, m_elevationMin); s.writeS32(20, m_elevationMax); + s.writeS32(21, m_tolerance); + s.writeS32(22, (int)m_protocol); return s.final(); } @@ -108,8 +112,8 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) uint32_t utmp; QString strtmp; - d.readS32(1, &m_azimuth, 0); - d.readS32(2, &m_elevation, 0); + d.readFloat(1, &m_azimuth, 0); + d.readFloat(2, &m_elevation, 0); d.readString(3, &m_serialPort, ""); d.readS32(4, &m_baudRate, 9600); d.readBool(5, &m_track, false); @@ -136,6 +140,8 @@ bool GS232ControllerSettings::deserialize(const QByteArray& data) d.readS32(18, &m_azimuthMax, 450); d.readS32(19, &m_elevationMin, 0); d.readS32(20, &m_elevationMax, 180); + d.readS32(21, &m_tolerance, 0); + d.readS32(22, (int*)&m_protocol, GS232); return true; } diff --git a/plugins/feature/gs232controller/gs232controllersettings.h b/plugins/feature/gs232controller/gs232controllersettings.h index 83dd8b787..eebb31b2b 100644 --- a/plugins/feature/gs232controller/gs232controllersettings.h +++ b/plugins/feature/gs232controller/gs232controllersettings.h @@ -28,8 +28,8 @@ class Serializable; struct GS232ControllerSettings { - int m_azimuth; - int m_elevation; + float m_azimuth; + float m_elevation; QString m_serialPort; int m_baudRate; bool m_track; @@ -40,6 +40,8 @@ struct GS232ControllerSettings int m_azimuthMax; int m_elevationMin; int m_elevationMax; + int m_tolerance; + enum Protocol { GS232, SPID } m_protocol; QString m_title; quint32 m_rgbColor; bool m_useReverseAPI; diff --git a/plugins/feature/gs232controller/gs232controllerworker.cpp b/plugins/feature/gs232controller/gs232controllerworker.cpp index 6b36fd002..11c930594 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.cpp +++ b/plugins/feature/gs232controller/gs232controllerworker.cpp @@ -17,6 +17,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #include #include @@ -38,8 +39,11 @@ GS232ControllerWorker::GS232ControllerWorker() : m_msgQueueToGUI(nullptr), m_running(false), m_mutex(QMutex::Recursive), - m_lastAzimuth(-1), - m_lastElevation(-1) + m_lastAzimuth(-1.0f), + m_lastElevation(-1.0f), + m_spidSetOutstanding(false), + m_spidSetSent(false), + m_spidStatusSent(false) { connect(&m_pollTimer, SIGNAL(timeout()), this, SLOT(update())); m_pollTimer.start(1000); @@ -54,6 +58,11 @@ void GS232ControllerWorker::reset() { QMutexLocker mutexLocker(&m_mutex); m_inputMessageQueue.clear(); + m_lastAzimuth = -1.0f; + m_lastElevation = -1.0f; + m_spidSetOutstanding = false; + m_spidSetSent = false; + m_spidStatusSent = false; } bool GS232ControllerWorker::startWork() @@ -61,7 +70,9 @@ bool GS232ControllerWorker::startWork() QMutexLocker mutexLocker(&m_mutex); connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); connect(&m_serialPort, &QSerialPort::readyRead, this, &GS232ControllerWorker::readSerialData); - openSerialPort(m_settings); + if (!m_settings.m_serialPort.isEmpty()) { + openSerialPort(m_settings); + } m_running = true; return m_running; } @@ -116,6 +127,8 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting << " m_azimuthMax: " << settings.m_azimuthMax << " m_elevationMin: " << settings.m_elevationMin << " m_elevationMax: " << settings.m_elevationMax + << " m_tolerance: " << settings.m_tolerance + << " m_protocol: " << settings.m_protocol << " m_serialPort: " << settings.m_serialPort << " m_baudRate: " << settings.m_baudRate << " force: " << force; @@ -131,21 +144,25 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting // Apply offset then clamp - int azimuth = settings.m_azimuth; + float azimuth = settings.m_azimuth; azimuth += settings.m_azimuthOffset; - azimuth = std::max(azimuth, settings.m_azimuthMin); - azimuth = std::min(azimuth, settings.m_azimuthMax); + azimuth = std::max(azimuth, (float)settings.m_azimuthMin); + azimuth = std::min(azimuth, (float)settings.m_azimuthMax); - int elevation = settings.m_elevation; + float elevation = settings.m_elevation; elevation += settings.m_elevationOffset; - elevation = std::max(elevation, settings.m_elevationMin); - elevation = std::min(elevation, settings.m_elevationMax); + elevation = std::max(elevation, (float)settings.m_elevationMin); + elevation = std::min(elevation, (float)settings.m_elevationMax); - if (((elevation != m_lastElevation) || force) && (settings.m_elevationMax != 0)) + // Don't set if within tolerance of last setting + float azDiff = std::abs(azimuth - m_lastAzimuth); + float elDiff = std::abs(elevation - m_lastElevation); + + if (((elDiff > settings.m_tolerance) || (m_lastElevation == -1) || force) && (settings.m_elevationMax != 0)) { setAzimuthElevation(azimuth, elevation); } - else if ((azimuth != m_lastAzimuth) || force) + else if ((azDiff > settings.m_tolerance) || (m_lastAzimuth == -1) || force) { setAzimuth(azimuth); } @@ -155,35 +172,81 @@ void GS232ControllerWorker::applySettings(const GS232ControllerSettings& setting void GS232ControllerWorker::openSerialPort(const GS232ControllerSettings& settings) { - if (m_serialPort.isOpen()) + if (m_serialPort.isOpen()) { m_serialPort.close(); + } m_serialPort.setPortName(settings.m_serialPort); m_serialPort.setBaudRate(settings.m_baudRate); if (!m_serialPort.open(QIODevice::ReadWrite)) { qCritical() << "GS232ControllerWorker::openSerialPort: Failed to open serial port " << settings.m_serialPort << ". Error: " << m_serialPort.error(); - if (m_msgQueueToFeature) + if (m_msgQueueToFeature) { m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Failed to open serial port %1: %2").arg(settings.m_serialPort).arg(m_serialPort.error()))); + } } m_lastAzimuth = -1; m_lastElevation = -1; } -void GS232ControllerWorker::setAzimuth(int azimuth) +void GS232ControllerWorker::setAzimuth(float azimuth) { - QString cmd = QString("M%1\r\n").arg(azimuth, 3, 10, QLatin1Char('0')); - QByteArray data = cmd.toLatin1(); - m_serialPort.write(data); - m_lastAzimuth = 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_serialPort.write(data); + } + else + { + setAzimuthElevation(azimuth, m_lastElevation); + } + m_lastAzimuth = azimuth; } -void GS232ControllerWorker::setAzimuthElevation(int azimuth, int elevation) +void GS232ControllerWorker::setAzimuthElevation(float azimuth, float elevation) { - QString cmd = QString("W%1 %2\r\n").arg(azimuth, 3, 10, QLatin1Char('0')).arg(elevation, 3, 10, QLatin1Char('0')); - QByteArray data = cmd.toLatin1(); - m_serialPort.write(data); - m_lastAzimuth = azimuth; - m_lastElevation = 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_serialPort.write(data); + } + else + { + qDebug() << "GS232ControllerWorker::setAzimuthElevation " << " AZ " << azimuth << " EL " << 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_serialPort.write(cmd); + + m_spidSetSent = true; + } + else + { + qDebug() << "GS232ControllerWorker::setAzimuthElevation: Not sent, waiting for status reply"; + m_spidSetOutstanding = true; + } + } + m_lastAzimuth = azimuth; + m_lastElevation = elevation; } void GS232ControllerWorker::readSerialData() @@ -191,32 +254,77 @@ void GS232ControllerWorker::readSerialData() char buf[1024]; qint64 len; - while (m_serialPort.canReadLine()) + if (m_settings.m_protocol == GS232ControllerSettings::GS232) { - len = m_serialPort.readLine(buf, sizeof(buf)); - if (len != -1) + while (m_serialPort.canReadLine()) { - 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()) + len = m_serialPort.readLine(buf, sizeof(buf)); + if (len != -1) { - QString az = match.captured(1); - QString el = match.captured(2); - //qDebug() << "GS232ControllerWorker::readSerialData read az " << az << " el " << el; - if (getMessageQueueToGUI()) - getMessageQueueToGUI()->push( GS232ControllerReport::MsgReportAzAl::create(az.toFloat(), el.toFloat())); + 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::readSerialData read Az " << az << " El " << el; + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push( GS232ControllerReport::MsgReportAzAl::create(az.toFloat(), el.toFloat())); + } + } + else if (response == "\r\n") + { + // Ignore + } + else + { + qDebug() << "GS232ControllerWorker::readSerialData - unexpected GS-232 response \"" << response << "\""; + if (m_msgQueueToFeature) { + m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected GS-232 response: %1").arg(response))); + } + } } - else if (response == "\r\n") + } + } + else + { + while (m_serialPort.bytesAvailable() >= 12) + { + len = m_serialPort.read(buf, 12); + if ((len == 12) && (buf[0] == 0x57)) { - // Ignore + 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::readSerialData read Az " << az << " El " << el; + if (getMessageQueueToGUI()) { + getMessageQueueToGUI()->push( GS232ControllerReport::MsgReportAzAl::create(az, el)); + } + if (m_spidStatusSent && m_spidSetSent) { + qDebug() << "GS232ControllerWorker::readSerialData - 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 { - qDebug() << "GS232ControllerWorker::readSerialData - unexpected response \"" << response << "\""; - if (m_msgQueueToFeature) - m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected GS-232 serial response: %1").arg(response))); + QByteArray bytes(buf, (int)len); + qDebug() << "GS232ControllerWorker::readSerialData - unexpected SPID rot2prog response \"" << bytes.toHex() << "\""; + if (m_msgQueueToFeature) { + m_msgQueueToFeature->push(GS232Controller::MsgReportWorker::create(QString("Unexpected SPID rot2prog response: %1").arg(bytes.toHex().data()))); + } } } } @@ -227,7 +335,27 @@ void GS232ControllerWorker::update() // Request current Az/El from GS-232 controller if (m_serialPort.isOpen()) { - QByteArray cmd("C2\r\n"); - m_serialPort.write(cmd); + if (m_settings.m_protocol == GS232ControllerSettings::GS232) + { + QByteArray cmd("C2\r\n"); + m_serialPort.write(cmd); + } + else + { + // 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_serialPort.write(cmd); + m_spidStatusSent = true; + } + } } } diff --git a/plugins/feature/gs232controller/gs232controllerworker.h b/plugins/feature/gs232controller/gs232controllerworker.h index 0bf22cace..3755cde20 100644 --- a/plugins/feature/gs232controller/gs232controllerworker.h +++ b/plugins/feature/gs232controller/gs232controllerworker.h @@ -76,15 +76,19 @@ private: QSerialPort m_serialPort; QTimer m_pollTimer; - int m_lastAzimuth; - int m_lastElevation; + float m_lastAzimuth; + float m_lastElevation; + + bool m_spidSetOutstanding; + bool m_spidSetSent; + bool m_spidStatusSent; bool handleMessage(const Message& cmd); void applySettings(const GS232ControllerSettings& settings, bool force = false); MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; } void openSerialPort(const GS232ControllerSettings& settings); - void setAzimuth(int azimuth); - void setAzimuthElevation(int azimuth, int elevation); + void setAzimuth(float azimuth); + void setAzimuthElevation(float azimuth, float elevation); private slots: void handleInputMessages(); diff --git a/plugins/feature/gs232controller/readme.md b/plugins/feature/gs232controller/readme.md index 854a336c9..4dfb24669 100644 --- a/plugins/feature/gs232controller/readme.md +++ b/plugins/feature/gs232controller/readme.md @@ -38,28 +38,40 @@ Specify the SDRangel Channel or Feature that that will control the target aziumt When tracking is enabled, this field will display a name for the target being tracked, as indicated by the selected Source plugin (5). For example, the ADS-B plugin will display the flight number of the target aircraft. The Star Tracker plugin will display Sun, Moon or Star. -

7: Serial Port

+

7: Protocol

+ +Selects which serial protocol to use. This can be GS-232 or SPID (rot2prog). + +

8: Tolerance

+ +Specifies a tolerance in degrees, below which, changes in target azimuth or elevation will not be sent to the rotator. +This can prevent some rotators that have a limited accuracy from making unbeneficial movements. + +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. + +

9: Serial Port

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

8: Baud rate

+

10: Baud rate

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

9: Azimuth Offset

+

11: Azimuth Offset

The azimuth offset specifies an angle in degrees that is added to the target azimuth before sending to the controller. This allows for a misalignment of the rotator to be corrected. -

10: Elevation Offset

+

12: Elevation Offset

The elevation offset specifies an angle in degrees that is added to the target elevation before sending to the controller. This allows for a misalignment of the rotator to be corrected. -

11 and 12: Azimuth Min and Max

+

13 and 14: Azimuth Min and Max

The azimuth min and max values specify the minimum and maximum azimuth values (after offset has been applied), that will be sent to the rotator. These values can be used to prevent the rotator from rotating an antenna in to an obstable. -

13 and 14: Elevation Min and Max

+

15 and 16: Elevation Min and Max

The elevation min and max values specify the minimum and maximum elevation values (after offset has been applied), that will be sent to the rotator. These values can be used to prevent the rotator from rotating an antenna in to an obstable. @@ -71,6 +83,13 @@ The controller uses the Waaa eee command when elevation needs to be set. When only azimuth needs to be set, the Maaa command is used. The C2 command is used to read current azimuth and elevation. A response of AZ=aaaEL=eee is expected. +

SPID rot2prog Protocol Implementation

+ +The controller uses the 0x2f set command with PH/PV=2 to set azimuth and elevation. +The 0x1f status command is used to read current azimuth and elevation. +A 12 byte response is expected for set and status commands. +All frames start with 0x57 and end with 0x20. +

API

Full details of the API can be found in the Swagger documentation. Here is a quick example of how to set the azimuth and elevation from the command line: diff --git a/plugins/feature/startracker/startrackergui.cpp b/plugins/feature/startracker/startrackergui.cpp index 4cb1e23db..0f0be1689 100644 --- a/plugins/feature/startracker/startrackergui.cpp +++ b/plugins/feature/startracker/startrackergui.cpp @@ -740,7 +740,7 @@ QList StarTrackerGUI::createDriftScan(bool galactic) AzAlt aa; aa.alt = m_settings.m_el; aa.az = m_settings.m_az; - double prevX, prevY; + double prevX; // Plot every 30min over a day for (int i = 0; i <= 24*2; i++) { @@ -767,7 +767,6 @@ QList StarTrackerGUI::createDriftScan(bool galactic) series->append(x, y); } prevX = x; - prevY = y; } return list; diff --git a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml index 347fa5239..e193bd01e 100644 --- a/swagger/sdrangel/api/swagger/include/GS232Controller.yaml +++ b/swagger/sdrangel/api/swagger/include/GS232Controller.yaml @@ -3,10 +3,12 @@ GS232ControllerSettings: properties: azimuth: description: Target azimuth in degrees (0-450) - type: integer + type: number + format: float elevation: description: Target elevation in degrees (0-180) - type: integer + type: number + format: float serialPort: description: The serial port the GS-232 controller is connected to type: string @@ -37,6 +39,12 @@ GS232ControllerSettings: elevationMax: description: Maximum elevation the controller will output type: integer + tolerance: + description: Tolerance in degrees + type: integer + protocol: + description: (0 GS-232, 1 SPID rot2prog) + type: integer title: type: string rgbColor: diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp index cab3b0ed5..d2b798065 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.cpp @@ -28,9 +28,9 @@ SWGGS232ControllerSettings::SWGGS232ControllerSettings(QString* json) { } SWGGS232ControllerSettings::SWGGS232ControllerSettings() { - azimuth = 0; + azimuth = 0.0f; m_azimuth_isSet = false; - elevation = 0; + elevation = 0.0f; m_elevation_isSet = false; serial_port = nullptr; m_serial_port_isSet = false; @@ -52,6 +52,10 @@ SWGGS232ControllerSettings::SWGGS232ControllerSettings() { m_elevation_min_isSet = false; elevation_max = 0; m_elevation_max_isSet = false; + tolerance = 0; + m_tolerance_isSet = false; + protocol = 0; + m_protocol_isSet = false; title = nullptr; m_title_isSet = false; rgb_color = 0; @@ -74,9 +78,9 @@ SWGGS232ControllerSettings::~SWGGS232ControllerSettings() { void SWGGS232ControllerSettings::init() { - azimuth = 0; + azimuth = 0.0f; m_azimuth_isSet = false; - elevation = 0; + elevation = 0.0f; m_elevation_isSet = false; serial_port = new QString(""); m_serial_port_isSet = false; @@ -98,6 +102,10 @@ SWGGS232ControllerSettings::init() { m_elevation_min_isSet = false; elevation_max = 0; m_elevation_max_isSet = false; + tolerance = 0; + m_tolerance_isSet = false; + protocol = 0; + m_protocol_isSet = false; title = new QString(""); m_title_isSet = false; rgb_color = 0; @@ -132,6 +140,8 @@ SWGGS232ControllerSettings::cleanup() { + + if(title != nullptr) { delete title; } @@ -156,9 +166,9 @@ SWGGS232ControllerSettings::fromJson(QString &json) { void SWGGS232ControllerSettings::fromJsonObject(QJsonObject &pJson) { - ::SWGSDRangel::setValue(&azimuth, pJson["azimuth"], "qint32", ""); + ::SWGSDRangel::setValue(&azimuth, pJson["azimuth"], "float", ""); - ::SWGSDRangel::setValue(&elevation, pJson["elevation"], "qint32", ""); + ::SWGSDRangel::setValue(&elevation, pJson["elevation"], "float", ""); ::SWGSDRangel::setValue(&serial_port, pJson["serialPort"], "QString", "QString"); @@ -180,6 +190,10 @@ SWGGS232ControllerSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&elevation_max, pJson["elevationMax"], "qint32", ""); + ::SWGSDRangel::setValue(&tolerance, pJson["tolerance"], "qint32", ""); + + ::SWGSDRangel::setValue(&protocol, pJson["protocol"], "qint32", ""); + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); @@ -246,6 +260,12 @@ SWGGS232ControllerSettings::asJsonObject() { if(m_elevation_max_isSet){ obj->insert("elevationMax", QJsonValue(elevation_max)); } + if(m_tolerance_isSet){ + obj->insert("tolerance", QJsonValue(tolerance)); + } + if(m_protocol_isSet){ + obj->insert("protocol", QJsonValue(protocol)); + } if(title != nullptr && *title != QString("")){ toJsonValue(QString("title"), title, obj, QString("QString")); } @@ -271,22 +291,22 @@ SWGGS232ControllerSettings::asJsonObject() { return obj; } -qint32 +float SWGGS232ControllerSettings::getAzimuth() { return azimuth; } void -SWGGS232ControllerSettings::setAzimuth(qint32 azimuth) { +SWGGS232ControllerSettings::setAzimuth(float azimuth) { this->azimuth = azimuth; this->m_azimuth_isSet = true; } -qint32 +float SWGGS232ControllerSettings::getElevation() { return elevation; } void -SWGGS232ControllerSettings::setElevation(qint32 elevation) { +SWGGS232ControllerSettings::setElevation(float elevation) { this->elevation = elevation; this->m_elevation_isSet = true; } @@ -391,6 +411,26 @@ SWGGS232ControllerSettings::setElevationMax(qint32 elevation_max) { this->m_elevation_max_isSet = true; } +qint32 +SWGGS232ControllerSettings::getTolerance() { + return tolerance; +} +void +SWGGS232ControllerSettings::setTolerance(qint32 tolerance) { + this->tolerance = tolerance; + this->m_tolerance_isSet = true; +} + +qint32 +SWGGS232ControllerSettings::getProtocol() { + return protocol; +} +void +SWGGS232ControllerSettings::setProtocol(qint32 protocol) { + this->protocol = protocol; + this->m_protocol_isSet = true; +} + QString* SWGGS232ControllerSettings::getTitle() { return title; @@ -502,6 +542,12 @@ SWGGS232ControllerSettings::isSet(){ if(m_elevation_max_isSet){ isObjectUpdated = true; break; } + if(m_tolerance_isSet){ + isObjectUpdated = true; break; + } + if(m_protocol_isSet){ + isObjectUpdated = true; break; + } if(title && *title != QString("")){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h index 689936b02..10d0adfdd 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGGS232ControllerSettings.h @@ -42,11 +42,11 @@ public: virtual void fromJsonObject(QJsonObject &json) override; virtual SWGGS232ControllerSettings* fromJson(QString &jsonString) override; - qint32 getAzimuth(); - void setAzimuth(qint32 azimuth); + float getAzimuth(); + void setAzimuth(float azimuth); - qint32 getElevation(); - void setElevation(qint32 elevation); + float getElevation(); + void setElevation(float elevation); QString* getSerialPort(); void setSerialPort(QString* serial_port); @@ -78,6 +78,12 @@ public: qint32 getElevationMax(); void setElevationMax(qint32 elevation_max); + qint32 getTolerance(); + void setTolerance(qint32 tolerance); + + qint32 getProtocol(); + void setProtocol(qint32 protocol); + QString* getTitle(); void setTitle(QString* title); @@ -103,10 +109,10 @@ public: virtual bool isSet() override; private: - qint32 azimuth; + float azimuth; bool m_azimuth_isSet; - qint32 elevation; + float elevation; bool m_elevation_isSet; QString* serial_port; @@ -139,6 +145,12 @@ private: qint32 elevation_max; bool m_elevation_max_isSet; + qint32 tolerance; + bool m_tolerance_isSet; + + qint32 protocol; + bool m_protocol_isSet; + QString* title; bool m_title_isSet;