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 }