File indexing completed on 2024-05-12 15:23:36

0001 /*
0002     SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "linguider.h"
0008 
0009 #include "Options.h"
0010 
0011 #include <KLocalizedString>
0012 #include <KMessageBox>
0013 
0014 #include <QNetworkReply>
0015 
0016 namespace Ekos
0017 {
0018 LinGuider::LinGuider()
0019 {
0020     tcpSocket = new QTcpSocket(this);
0021 
0022     rawBuffer.clear();
0023 
0024     connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readLinGuider()));
0025     connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this,
0026             SLOT(displayError(QAbstractSocket::SocketError)));
0027 
0028     connect(tcpSocket, SIGNAL(connected()), this, SLOT(onConnected()));
0029 
0030     deviationTimer.setInterval(1000);
0031     connect(&deviationTimer, &QTimer::timeout, this, [&]()
0032     {
0033         sendCommand(GET_RA_DEC_DRIFT);
0034     });
0035 }
0036 
0037 bool LinGuider::Connect()
0038 {
0039     if (connection == DISCONNECTED)
0040     {
0041         rawBuffer.clear();
0042         connection = CONNECTING;
0043         tcpSocket->connectToHost(Options::linGuiderHost(), Options::linGuiderPort());
0044     }
0045     // Already connected, let's connect equipment
0046     else
0047         emit newStatus(GUIDE_CONNECTED);
0048 
0049     return true;
0050 }
0051 
0052 bool LinGuider::Disconnect()
0053 {
0054     rawBuffer.clear();
0055     connection = DISCONNECTED;
0056     tcpSocket->disconnectFromHost();
0057 
0058     emit newStatus(GUIDE_DISCONNECTED);
0059 
0060     return true;
0061 }
0062 
0063 void LinGuider::displayError(QAbstractSocket::SocketError socketError)
0064 {
0065     switch (socketError)
0066     {
0067         case QAbstractSocket::RemoteHostClosedError:
0068             break;
0069         case QAbstractSocket::HostNotFoundError:
0070             emit newLog(i18n("The host was not found. Please check the host name and port settings in Guide options."));
0071             emit newStatus(GUIDE_DISCONNECTED);
0072             break;
0073         case QAbstractSocket::ConnectionRefusedError:
0074             emit newLog(i18n("The connection was refused by the peer. Make sure the LinGuider is running, and check "
0075                              "that the host name and port settings are correct."));
0076             emit newStatus(GUIDE_DISCONNECTED);
0077             break;
0078         default:
0079             emit newLog(i18n("The following error occurred: %1.", tcpSocket->errorString()));
0080     }
0081 
0082     connection = DISCONNECTED;
0083 }
0084 
0085 void LinGuider::readLinGuider()
0086 {
0087     while (tcpSocket->atEnd() == false)
0088     {
0089         rawBuffer += tcpSocket->readAll();
0090 
0091         while (1)
0092         {
0093             if (rawBuffer.count() < 8)
0094                 break;
0095 
0096             if (Options::guideLogging())
0097                 qDebug() << Q_FUNC_INFO << "Guide:" << rawBuffer;
0098 
0099             qint16 magicNumber = *(reinterpret_cast<qint16 *>(rawBuffer.data()));
0100             if (magicNumber != 0x02)
0101             {
0102                 emit newLog(i18n("Invalid response."));
0103                 rawBuffer = rawBuffer.mid(1);
0104                 continue;
0105             }
0106 
0107             qint16 command = *(reinterpret_cast<qint16 *>(rawBuffer.data() + 2));
0108             if (command < GET_VER || command > GET_RA_DEC_DRIFT)
0109             {
0110                 emit newLog(i18n("Invalid response."));
0111                 rawBuffer = rawBuffer.mid(1);
0112                 continue;
0113             }
0114 
0115             qint16 datalen = *(reinterpret_cast<qint16 *>(rawBuffer.data() + 4));
0116             if (rawBuffer.count() < datalen + 8)
0117                 break;
0118 
0119             QString reply = rawBuffer.mid(8, datalen);
0120             processResponse(static_cast<LinGuiderCommand>(command), reply);
0121             rawBuffer = rawBuffer.mid(8 + datalen);
0122         }
0123     }
0124 }
0125 
0126 void LinGuider::processResponse(LinGuiderCommand command, const QString &reply)
0127 {
0128     if (reply == "Error: Guiding not started.")
0129     {
0130         state = IDLE;
0131         emit newStatus(GUIDE_ABORTED);
0132         deviationTimer.stop();
0133         return;
0134     }
0135 
0136     switch (command)
0137     {
0138         case GET_VER:
0139             emit newLog(i18n("Connected to LinGuider %1", reply));
0140             if (reply < "v.4.1.0")
0141             {
0142                 emit newLog(
0143                     i18n("Only LinGuider v4.1.0 or higher is supported. Please upgrade LinGuider and try again."));
0144                 Disconnect();
0145             }
0146 
0147             sendCommand(GET_GUIDER_STATE);
0148             break;
0149 
0150         case GET_GUIDER_STATE:
0151             if (reply == "GUIDING")
0152             {
0153                 state = GUIDING;
0154                 emit newStatus(GUIDE_GUIDING);
0155                 deviationTimer.start();
0156             }
0157             else
0158             {
0159                 state = IDLE;
0160                 deviationTimer.stop();
0161             }
0162             break;
0163 
0164         case FIND_STAR:
0165         {
0166             emit newLog(i18n("Auto star selected %1", reply));
0167             QStringList pos = reply.split(' ');
0168             if (pos.count() == 2)
0169             {
0170                 starCenter = reply;
0171                 sendCommand(SET_GUIDER_RETICLE_POS, reply);
0172             }
0173             else
0174             {
0175                 emit newLog(i18n("Failed to process star position."));
0176                 emit newStatus(GUIDE_CALIBRATION_ERROR);
0177             }
0178         }
0179         break;
0180 
0181         case SET_GUIDER_RETICLE_POS:
0182             if (reply == "OK")
0183             {
0184                 sendCommand(SET_GUIDER_SQUARE_POS, starCenter);
0185             }
0186             else
0187             {
0188                 emit newLog(i18n("Failed to set guider reticle position."));
0189                 emit newStatus(GUIDE_CALIBRATION_ERROR);
0190             }
0191             break;
0192 
0193         case SET_GUIDER_SQUARE_POS:
0194             if (reply == "OK")
0195             {
0196                 emit newStatus(GUIDE_CALIBRATION_SUCCESS);
0197             }
0198             else
0199             {
0200                 emit newLog(i18n("Failed to set guider square position."));
0201                 emit newStatus(GUIDE_CALIBRATION_ERROR);
0202             }
0203             break;
0204 
0205         case GUIDER:
0206             if (reply == "OK")
0207             {
0208                 if (state == IDLE)
0209                 {
0210                     emit newStatus(GUIDE_GUIDING);
0211                     state = GUIDING;
0212 
0213                     deviationTimer.start();
0214                 }
0215                 else
0216                 {
0217                     emit newStatus(GUIDE_IDLE);
0218                     state = IDLE;
0219 
0220                     deviationTimer.stop();
0221                 }
0222             }
0223             else
0224             {
0225                 if (state == IDLE)
0226                     emit newLog(i18n("Failed to start guider."));
0227                 else
0228                     emit newLog(i18n("Failed to stop guider."));
0229             }
0230             break;
0231 
0232         case GET_RA_DEC_DRIFT:
0233         {
0234             if (state != GUIDING)
0235             {
0236                 state = GUIDING;
0237                 emit newStatus(GUIDE_GUIDING);
0238             }
0239 
0240             QStringList pos = reply.split(' ');
0241             if (pos.count() == 2)
0242             {
0243                 bool raOK = false, deOK = false;
0244 
0245                 double raDev = pos[0].toDouble(&raOK);
0246                 double deDev = pos[1].toDouble(&deOK);
0247 
0248                 if (raOK && deOK)
0249                     emit newAxisDelta(raDev, deDev);
0250             }
0251             else
0252             {
0253                 emit newLog(i18n("Failed to get RA/DEC Drift."));
0254             }
0255         }
0256         break;
0257 
0258         case SET_DITHERING_RANGE:
0259             if (reply == "OK")
0260             {
0261                 sendCommand(DITHER);
0262 
0263                 deviationTimer.stop();
0264             }
0265             else
0266             {
0267                 emit newLog(i18n("Failed to set dither range."));
0268             }
0269             break;
0270 
0271         case DITHER:
0272             if (reply == "Long time cmd finished")
0273                 emit newStatus(GUIDE_DITHERING_SUCCESS);
0274             else
0275                 emit newStatus(GUIDE_DITHERING_ERROR);
0276 
0277             state = GUIDING;
0278             deviationTimer.start();
0279             break;
0280 
0281         default:
0282             break;
0283     }
0284 }
0285 
0286 void LinGuider::onConnected()
0287 {
0288     connection = CONNECTED;
0289 
0290     emit newStatus(GUIDE_CONNECTED);
0291     // Get version
0292 
0293     sendCommand(GET_VER);
0294 }
0295 
0296 void LinGuider::sendCommand(LinGuiderCommand command, const QString &args)
0297 {
0298     // Command format: Magic Number (0x00 0x02), cmd (2 bytes), len_of_param (4 bytes), param (ascii)
0299 
0300     int size = 8 + args.size();
0301 
0302     QByteArray cmd(size, 0);
0303 
0304     // Magic number
0305     cmd[0] = 0x02;
0306     cmd[1] = 0x00;
0307 
0308     // Command
0309     cmd[2] = command;
0310     cmd[3] = 0x00;
0311 
0312     // Len
0313     qint32 len = args.size();
0314     memcpy(cmd.data() + 4, &len, 4);
0315 
0316     // Params
0317     if (args.isEmpty() == false)
0318         memcpy(cmd.data() + 8, args.toLatin1().data(), args.size());
0319 
0320     tcpSocket->write(cmd);
0321 }
0322 
0323 bool LinGuider::calibrate()
0324 {
0325     // Let's start calibration. It is already calibrated but in this step we auto-select and star and set the square
0326     emit newStatus(Ekos::GUIDE_CALIBRATING);
0327 
0328     sendCommand(FIND_STAR);
0329 
0330     return true;
0331 }
0332 
0333 bool LinGuider::guide()
0334 {
0335     sendCommand(GUIDER, "start");
0336     return true;
0337 }
0338 
0339 bool LinGuider::abort()
0340 {
0341     sendCommand(GUIDER, "stop");
0342     return true;
0343 }
0344 
0345 bool LinGuider::suspend()
0346 {
0347     return abort();
0348 }
0349 
0350 bool LinGuider::resume()
0351 {
0352     return guide();
0353 }
0354 
0355 bool LinGuider::dither(double pixels)
0356 {
0357     QString pixelsString = QString::number(pixels, 'f', 2);
0358     QString args         = QString("%1 %2").arg(pixelsString, pixelsString);
0359 
0360     sendCommand(SET_DITHERING_RANGE, args);
0361 
0362     return true;
0363 }
0364 }