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

0001 /*
0002     SPDX-FileCopyrightText: 2020 Hy Murveit <hy@murveit.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "guidelog.h"
0008 
0009 #include <math.h>
0010 #include <cstdint>
0011 
0012 #include <QDateTime>
0013 #include <QStandardPaths>
0014 #include <QTextStream>
0015 
0016 #include "auxiliary/kspaths.h"
0017 #include <version.h>
0018 
0019 // This class writes a guide log that is compatible with the phdlogview program.
0020 // See https://openphdguiding.org/phd2-log-viewer/ for details on that program.
0021 
0022 namespace
0023 {
0024 
0025 // These conversion aren't correct. I believe the KStars way of doing it, with RA_INC etc
0026 // is better, however, it is consistent and will work with phdlogview.
0027 QString directionString(GuideDirection direction)
0028 {
0029     switch(direction)
0030     {
0031         case DEC_INC_DIR:
0032             return "N";
0033         case DEC_DEC_DIR:
0034             return "S";
0035         case RA_DEC_DIR:
0036             return "E";
0037         case RA_INC_DIR:
0038             return "W";
0039         case NO_DIR:
0040             return "";
0041     }
0042     return "";
0043 }
0044 
0045 QString directionStringLong(GuideDirection direction)
0046 {
0047     switch(direction)
0048     {
0049         case DEC_INC_DIR:
0050             return "North";
0051         case DEC_DEC_DIR:
0052             return "South";
0053         case RA_DEC_DIR:
0054             return "East";
0055         case RA_INC_DIR:
0056             return "West";
0057         case NO_DIR:
0058             return "";
0059     }
0060     return "";
0061 }
0062 
0063 QString pierSideString(ISD::Mount::PierSide side)
0064 {
0065     switch(side)
0066     {
0067         case ISD::Mount::PierSide::PIER_WEST:
0068             return QString("West");
0069         case ISD::Mount::PierSide::PIER_EAST:
0070             return QString("East");
0071         case ISD::Mount::PierSide::PIER_UNKNOWN:
0072             return QString("Unknown");
0073     }
0074     return QString("");
0075 }
0076 
0077 double degreesToHours(double degrees)
0078 {
0079     return 24.0 * degrees / 360.0;
0080 }
0081 
0082 } // namespace
0083 
0084 GuideLog::GuideLog()
0085 {
0086 }
0087 
0088 GuideLog::~GuideLog()
0089 {
0090     endLog();
0091 }
0092 
0093 void GuideLog::appendToLog(const QString &lines)
0094 {
0095     if (!enabled)
0096         return;
0097     QTextStream out(&logFile);
0098     out << lines;
0099     out.flush();
0100 }
0101 
0102 // Creates the filename and opens the file.
0103 // Prints a line like the one below.
0104 //   KStars version 3.4.0. PHD2 log version 2.5. Log enabled at 2019-11-21 00:00:48
0105 void GuideLog::startLog()
0106 {
0107     QDir dir = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("guidelogs");
0108     dir.mkpath(".");
0109 
0110     logFileName = dir.filePath("guide_log-" + QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss") + ".txt");
0111     logFile.setFileName(logFileName);
0112     logFile.open(QIODevice::WriteOnly | QIODevice::Text);
0113 
0114     appendToLog(QString("KStars version %1. PHD2 log version 2.5. Log enabled at %2\n\n")
0115                 .arg(KSTARS_VERSION)
0116                 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")));
0117 
0118     initialized = true;
0119 }
0120 
0121 // Prints a line like the one below and closes the file.
0122 //   Log closed at 2019-11-21 08:46:38
0123 void GuideLog::endLog()
0124 {
0125     if (!enabled || !initialized)
0126         return;
0127 
0128     if (isGuiding && initialized)
0129         endGuiding();
0130 
0131     appendToLog(QString("Log closed at %1\n")
0132                 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")));
0133     logFile.close();
0134 }
0135 
0136 // Output at the start of Guiding.
0137 // Note that in the PHD2 generated versions of this log, there is a lot of guiding information here.
0138 // We just output two lines which phdlogview needs, for pixel scale and RA/DEC.
0139 void GuideLog::startGuiding(const GuideInfo &info)
0140 {
0141     if (!enabled)
0142         return;
0143     if (!initialized)
0144         startLog();
0145 
0146     // Currently phdlogview just reads the Pixel scale value on the 2nd line, and
0147     // just reads the Dec value on the 3rd line.
0148     // Note the log wants hrs for RA, the input to this method is in degrees.
0149     appendToLog(QString("Guiding Begins at %1\n"
0150                         "Pixel scale = %2 arc-sec/px, Binning = %3, Focal length = %4 mm\n"
0151                         "RA = %5 hr, Dec = %6 deg, Hour angle = N/A hr, Pier side = %7, "
0152                         "Rotator pos = N/A, Alt = %8 deg, Az = %9 deg\n"
0153                         "Mount = mount, xAngle = %10, xRate = %11, yAngle = %12, yRate = %13\n"
0154                         "Frame,Time,mount,dx,dy,RARawDistance,DECRawDistance,RAGuideDistance,DECGuideDistance,"
0155                         "RADuration,RADirection,DECDuration,DECDirection,XStep,YStep,StarMass,SNR,ErrorCode\n")
0156                 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
0157                 .arg(QString::number(info.pixelScale, 'f', 2))
0158                 .arg(info.binning)
0159                 .arg(info.focalLength)
0160                 .arg(QString::number(degreesToHours(info.ra), 'f', 2))
0161                 .arg(QString::number(info.dec, 'f', 1))
0162                 .arg(pierSideString(info.pierSide))
0163                 .arg(QString::number(info.altitude, 'f', 1))
0164                 .arg(QString::number(info.azimuth, 'f', 1))
0165                 .arg(QString::number(info.xangle, 'f', 1))
0166                 .arg(QString::number(info.xrate, 'f', 3))
0167                 .arg(QString::number(info.yangle, 'f', 1))
0168                 .arg(QString::number(info.yrate, 'f', 3)));
0169 
0170 
0171     guideIndex = 1;
0172     isGuiding = true;
0173     timer.start();
0174 }
0175 
0176 // Prints a line that looks something like this:
0177 //   55,467.914,"Mount",-1.347,-2.160,2.319,-1.451,1.404,-0.987,303,W,218,N,,,2173,26.91,0
0178 // See the log analysis section in https://openphdguiding.org/PHD2_User_Guide.pdf for definitions of the fields.
0179 void GuideLog::addGuideData(const GuideData &data)
0180 {
0181     QString mountString = data.type == GuideData::MOUNT ? "\"Mount\"" : "\"DROP\"";
0182     QString xStepString = "";
0183     QString yStepString = "";
0184     appendToLog(QString("%1,%2,%3,%4,%5,%6,%7,%8,%9,%10,%11,%12,%13,%14,%15,%16,%17,%18\n")
0185                 .arg(guideIndex)
0186                 .arg(QString::number(timer.elapsed() / 1000.0, 'f', 3))
0187                 .arg(mountString)
0188                 .arg(QString::number(data.dx, 'f', 3))
0189                 .arg(QString::number(data.dy, 'f', 3))
0190                 .arg(QString::number(data.raDistance, 'f', 3))
0191                 .arg(QString::number(data.decDistance, 'f', 3))
0192                 .arg(QString::number(data.raGuideDistance, 'f', 3))
0193                 .arg(QString::number(data.decGuideDistance, 'f', 3))
0194                 .arg(data.raDuration)
0195                 .arg(directionString(data.raDirection))
0196                 .arg(data.decDuration)
0197                 .arg(directionString(data.decDirection))
0198                 .arg(xStepString)
0199                 .arg(yStepString)
0200                 .arg(QString::number(data.mass, 'f', 0))
0201                 .arg(QString::number(data.snr, 'f', 2))
0202                 .arg(static_cast<int>(data.code)));
0203     ++guideIndex;
0204 }
0205 
0206 // Prints a line that looks like:
0207 //   Guiding Ends at 2019-11-21 01:57:45
0208 void GuideLog::endGuiding()
0209 {
0210     appendToLog(QString("Guiding Ends at %1\n\n")
0211                 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")));
0212     isGuiding = false;
0213 }
0214 
0215 // Note that in the PHD2 generated versions of this log, there is a lot of calibration information here.
0216 // We just output two lines which phdlogview needs, for pixel scale and RA/DEC.
0217 void GuideLog::startCalibration(const GuideInfo &info)
0218 {
0219     if (!enabled)
0220         return;
0221     if (!initialized)
0222         startLog();
0223     // Currently phdlogview just reads the Pixel scale value on the 2nd line, and
0224     // just reads the Dec value on the 3rd line.
0225     appendToLog(QString("Calibration Begins at %1\n"
0226                         "Pixel scale = %2 arc-sec/px, Binning = %3, Focal length = %4 mm\n"
0227                         "RA = %5 hr, Dec = %6 deg, Hour angle = N/A hr, Pier side = %7, "
0228                         "Rotator pos = N/A, Alt = %8 deg, Az = %9 deg\n"
0229                         "Direction,Step,dx,dy,x,y,Dist\n")
0230                 .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
0231                 .arg(QString::number(info.pixelScale, 'f', 2))
0232                 .arg(info.binning)
0233                 .arg(info.focalLength)
0234                 .arg(QString::number(degreesToHours(info.ra), 'f', 2))
0235                 .arg(QString::number(info.dec, 'f', 1))
0236                 .arg(pierSideString(info.pierSide))
0237                 .arg(QString::number(info.altitude, 'f', 1))
0238                 .arg(QString::number(info.azimuth, 'f', 1)));
0239 
0240     calibrationIndex = 1;
0241     timer.start();
0242     lastCalibrationDirection = NO_DIR;
0243 }
0244 
0245 // Prints a line that looks like:
0246 //   West,2,-15.207,-1.037,54.800,58.947,15.242
0247 void GuideLog::addCalibrationData(GuideDirection direction, double x, double y, double xOrigin, double yOrigin)
0248 {
0249     if (direction != lastCalibrationDirection)
0250         calibrationIndex = 1;
0251     lastCalibrationDirection = direction;
0252 
0253     appendToLog(QString("%1,%2,%3,%4,%5,%6,%7\n")
0254                 .arg(directionStringLong(direction))
0255                 .arg(calibrationIndex)
0256                 .arg(QString::number(x - xOrigin, 'f', 3))
0257                 .arg(QString::number(y - yOrigin, 'f', 3))
0258                 .arg(QString::number(x, 'f', 3))
0259                 .arg(QString::number(y, 'f', 3))
0260                 .arg(QString::number(hypot(x - xOrigin, y - yOrigin), 'f', 3)));
0261 
0262     // This is a little different than PHD2--they seem to count down in the reverse directions.
0263     calibrationIndex++;
0264 }
0265 
0266 // Prints a line that looks like:
0267 //   West calibration complete. Angle = 106.8 deg
0268 // Currently phdlogview ignores this line.
0269 void GuideLog::endCalibrationSection(GuideDirection direction, double degrees)
0270 {
0271     appendToLog(QString("%1 calibration complete. Angle = %2 deg\n")
0272                 .arg(directionStringLong(direction))
0273                 .arg(QString::number(degrees, 'f', 1)));
0274 }
0275 
0276 // Prints two lines that look like:
0277 //  Calibration guide speeds: RA = 191.5 a-s/s, Dec = 408.0 a-s/s
0278 //  Calibration complete
0279 // The failed version is not in the PHD2 log, will be ignored by the viewer.
0280 void GuideLog::endCalibration(double raSpeed, double decSpeed)
0281 {
0282     if (raSpeed == 0 && decSpeed == 0)
0283         appendToLog(QString("Calibration complete (Failed)\n\n"));
0284     else
0285         appendToLog(QString("Calibration guide speeds: RA = %1 a-s/s, Dec = %2 a-s/s\n"
0286                             "Calibration complete\n\n")
0287                     .arg(QString::number(raSpeed, 'f', 1))
0288                     .arg(QString::number(decSpeed, 'f', 1)));
0289 }
0290 
0291 void GuideLog::ditherInfo(double dx, double dy, double x, double y)
0292 {
0293     appendToLog(QString("INFO: DITHER by %1, %2, new lock pos = %3, %4\n")
0294                 .arg(QString::number(dx, 'f', 3))
0295                 .arg(QString::number(dy, 'f', 3))
0296                 .arg(QString::number(x, 'f', 3))
0297                 .arg(QString::number(y, 'f', 3)));
0298     // Below moved to ditherInfo from settleStartedInfo() to match phdlogview.
0299     appendToLog("INFO: SETTLING STATE CHANGE, Settling started\n");
0300 }
0301 
0302 void GuideLog::pauseInfo()
0303 {
0304     appendToLog("INFO: Server received PAUSE\n");
0305 }
0306 
0307 void GuideLog::resumeInfo()
0308 {
0309     appendToLog("INFO: Server received RESUME\n");
0310 }
0311 
0312 void GuideLog::settleStartedInfo()
0313 {
0314     // This was moved to ditherInfo() to match phdlogview
0315     // appendToLog("INFO: SETTLING STATE CHANGE, Settling started\n");
0316 }
0317 
0318 void GuideLog::settleCompletedInfo()
0319 {
0320     appendToLog("INFO: SETTLING STATE CHANGE, Settling complete\n");
0321 }