1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-06-02 06:04:39 -04:00

Rotator Controller Updates

Add support for X/Y coordinates.
Add coordinate precision setting.
Automatically scan for serial port changes.
Refactor so each protocol is implemented in a separate class.
Add start of DFM protocol.
This commit is contained in:
Jon Beniston
2023-04-03 16:47:13 +01:00
parent c50c866732
commit 4ac5e729ff
31 changed files with 2859 additions and 483 deletions
@@ -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();
}
}