1 /*
2  *  guidinglog.cpp
3  *  PHD Guiding
4  *
5  *  Created by Bret McKee
6  *  Copyright (c) 2012-2013 Bret McKee
7  *  All rights reserved.
8  *
9  *  This source code is distributed under the following "BSD" license
10  *  Redistribution and use in source and binary forms, with or without
11  *  modification, are permitted provided that the following conditions are met:
12  *    Redistributions of source code must retain the above copyright notice,
13  *     this list of conditions and the following disclaimer.
14  *    Redistributions in binary form must reproduce the above copyright notice,
15  *     this list of conditions and the following disclaimer in the
16  *     documentation and/or other materials provided with the distribution.
17  *    Neither the name of Bret McKee, Dad Dog Development,
18  *     Craig Stark, Stark Labs nor the names of its
19  *     contributors may be used to endorse or promote products derived from
20  *     this software without specific prior written permission.
21  *
22  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
26  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  *  POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35 
36 #include "phd.h"
37 
38 #include <wx/wfstream.h>
39 #include <wx/txtstrm.h>
40 
41 #define GUIDELOG_VERSION _T("2.5")
42 
43 const int RetentionPeriod = 60;
44 
GuidingLog()45 GuidingLog::GuidingLog()
46     :
47     m_enabled(false),
48     m_keepFile(false),
49     m_isGuiding(false)
50 {
51 }
52 
~GuidingLog()53 GuidingLog::~GuidingLog()
54 {
55 }
56 
PierSideStr(PierSide p)57 static wxString PierSideStr(PierSide p)
58 {
59     switch (p)
60     {
61     case PIER_SIDE_EAST: return "East";
62     case PIER_SIDE_WEST: return "West";
63     default:             return "Unknown";
64     }
65 }
66 
ParityStr(int p)67 static wxString ParityStr(int p)
68 {
69     switch (p)
70     {
71     case GUIDE_PARITY_EVEN: return "Even";
72     case GUIDE_PARITY_ODD:  return "Odd";
73     default:                return "N/A";
74     }
75 }
76 
HourAngle(double ra,double lst)77 static double HourAngle(double ra, double lst)
78 {
79     return norm(lst - ra, -12.0, 12.0);
80 }
81 
RotatorPosStr()82 static wxString RotatorPosStr()
83 {
84     if (!pRotator)
85         return "N/A";
86     double pos = Rotator::RotatorPosition();
87     if (pos == Rotator::POSITION_UNKNOWN)
88         return "Unknown";
89     else
90         return wxString::Format("%.1f", norm(pos, 0.0, 360.0));
91 }
92 
93 // Angles in degrees, HA in hours, results in degrees
GetAltAz(double Latitude,double HA,double Dec,double & Altitude,double & Azimuth)94 static void GetAltAz(double Latitude, double HA, double Dec, double& Altitude, double& Azimuth)
95 {
96     const double HrsToRad = 0.2617993881;
97     // Get altitude first
98     // sin(Alt) = cos(HA)cos(Dec)cos(Lat) + sin(Dec)sin(Lat)
99     double decRadians = radians(Dec);
100     double latRadians = radians(Latitude);
101     double haRadians = HA * HrsToRad;
102     double altRadians;
103     double sinAlt = cos(haRadians) * cos(decRadians) * cos(latRadians) + sin(decRadians) * sin(latRadians);
104     altRadians = asin(sinAlt);
105     Altitude = degrees(asin(sinAlt));
106     // Special handling if we're very close to zenith (very small zenith angle)
107     double zenDist = acos(sinAlt);
108     double zSin = sin(zenDist);
109     if (fabs(zSin) < 1e-5)
110     {
111         Altitude = 90.0;
112         // Azimuth values are arbitrary in this situation
113         if (sin(haRadians) >= 0)
114             Azimuth = 270;
115         else
116             Azimuth = 90;
117         return;
118     }
119 
120     // Now get azimuth - alternative formula using zSin avoids div-by-zero conditions
121     double As = cos(decRadians) * sin(haRadians) / zSin;
122     double Ac = (sin(latRadians) * cos(decRadians) * cos(haRadians) - cos(latRadians) *
123         sin(decRadians)) / zSin;
124     // atan2 doesn't want both params = 0
125     if (Ac == 0.0 && As == 0.0)
126     {
127         if (Dec > 0)
128             Azimuth = 180.0;
129         else
130             Azimuth = 0.0;
131         return;
132     }
133     // arctan2 handles quadrants automatically but returns result in the range of -180 to +180 - we want 0 to 360
134     double altPrime = atan2(As, Ac);
135     Azimuth = degrees(altPrime) + 180.0;
136 }
137 
PointingInfo()138 static wxString PointingInfo()
139 {
140     double cur_ra, cur_dec, cur_st;
141     double latitude, longitude;
142     double alt;
143     double az;
144     double ha;
145     wxString rslt;
146     bool pointingError = false;
147     if (pPointingSource && !pPointingSource->GetCoordinates(&cur_ra, &cur_dec, &cur_st))
148     {
149         ha = HourAngle(cur_ra, cur_st);
150         rslt = wxString::Format("RA = %0.2f hr, Dec = %0.1f deg, Hour angle = %0.2f hr, Pier side = %s, Rotator pos = %s, ",
151             cur_ra, cur_dec, ha, PierSideStr(pPointingSource->SideOfPier()), RotatorPosStr());
152     }
153     else
154     {
155         rslt = wxString::Format("RA/Dec = Unknown, Hour angle = Unknown, Pier side = Unknown, Rotator pos = %s, ", RotatorPosStr());
156         pointingError = true;
157     }
158     if (pPointingSource && !pointingError && !pPointingSource->GetSiteLatLong(&latitude, &longitude))
159     {
160         GetAltAz(latitude, ha, cur_dec, alt, az);
161     }
162     if (!pointingError)
163         rslt += wxString::Format("Alt = %0.1f deg, Az = %0.1f deg", alt, az);
164     else
165         rslt += "Alt = Unknown, Az = Unknown";
166 
167     return rslt;
168 }
169 
GuidingHeader(wxFFile & file)170 static void GuidingHeader(wxFFile& file)
171 // output guiding header to log file
172 {
173     file.Write("Equipment Profile = " + pConfig->GetCurrentProfile() + "\n");
174 
175     file.Write(pFrame->GetSettingsSummary());
176     file.Write(pFrame->pGuider->GetSettingsSummary());
177 
178     if (pCamera)
179     {
180         file.Write(pCamera->GetSettingsSummary());
181         file.Write("Exposure = " + pFrame->ExposureDurationSummary() + "\n");
182     }
183 
184     if (pMount)
185         file.Write(pMount->GetSettingsSummary());
186 
187     if (pSecondaryMount)
188         file.Write(pSecondaryMount->GetSettingsSummary());
189 
190     file.Write(PointingInfo());
191     file.Write("\n");
192 
193     const Star& star = pFrame->pGuider->PrimaryStar();
194 
195     file.Write(wxString::Format("Lock position = %.3f, %.3f, Star position = %.3f, %.3f, HFD = %.2f px\n",
196                                 pFrame->pGuider->LockPosition().X,
197                                 pFrame->pGuider->LockPosition().Y,
198                                 pFrame->pGuider->CurrentPosition().X,
199                                 pFrame->pGuider->CurrentPosition().Y,
200                                 star.HFD));
201 
202     file.Write("Frame,Time,mount,dx,dy,RARawDistance,DECRawDistance,RAGuideDistance,DECGuideDistance,"
203                "RADuration,RADirection,DECDuration,DECDirection,XStep,YStep,StarMass,SNR,ErrorCode\n");
204 }
205 
WriteSummaryInfo(wxFFile & file,const GuideLogSummaryInfo & summary)206 static void WriteSummaryInfo(wxFFile& file, const GuideLogSummaryInfo& summary)
207 {
208     if (!summary.valid)
209         return;
210 
211     file.Write(wxString::Format("Log Summary: calcnt:%u gcnt:%u gdur:%.f gacnt:%u\n",
212                                 summary.cal_cnt, summary.guide_cnt, summary.guide_dur,
213                                 summary.ga_cnt));
214 }
215 
LoadSummaryInfo(wxFFile & file)216 void GuideLogSummaryInfo::LoadSummaryInfo(wxFFile& file)
217 {
218     Clear();
219 
220     wxFFileInputStream is(file);
221     if (is.IsOk())
222     {
223         struct RestorePos {
224             wxFFile& f;
225             wxFileOffset o;
226             RestorePos(wxFFile& f_) : f(f_) { o = f.Tell(); }
227             ~RestorePos() { try { f.Seek(o); } catch (...) {} }
228         } restore(file);
229         wxFileOffset ofs = wxMax(file.Length() - 128, 0LL);
230         is.SeekI(ofs);
231         wxTextInputStream tis(is, wxS(" "), wxMBConvUTF8());
232         while (!is.Eof())
233         {
234             wxString s = tis.ReadLine();
235             if (s.StartsWith(wxS("Log Summary: ")))
236             {
237                 size_t pos;
238                 wxStringCharType *e;
239                 if ((pos = s.find(wxS("calcnt:"))) != wxString::npos)
240                     cal_cnt = wxStrtoul(s.substr(pos + 7), &e, 10);
241                 if ((pos = s.find(wxS("gcnt:"))) != wxString::npos)
242                     guide_cnt = wxStrtoul(s.substr(pos + 5), &e, 10);
243                 if ((pos = s.find(wxS("gdur:"))) != wxString::npos)
244                     guide_dur = wxStrtod(s.substr(pos + 5), &e);
245                 if ((pos = s.find(wxS("gacnt:"))) != wxString::npos)
246                     ga_cnt = wxStrtoul(s.substr(pos + 6), &e, 10);
247                 valid = true;
248                 return;
249             }
250         }
251     }
252 }
253 
EnableLogging()254 void GuidingLog::EnableLogging()
255 {
256     if (m_enabled)
257         return;
258 
259     try
260     {
261         const wxDateTime& logFileTime = wxGetApp().GetLogFileTime();
262         if (!m_file.IsOpened())
263         {
264             m_fileName = GetLogDir() + PATHSEPSTR + logFileTime.Format(_T("PHD2_GuideLog_%Y-%m-%d_%H%M%S.txt"));
265 
266             if (!m_file.Open(m_fileName, "a+"))
267             {
268                 throw ERROR_INFO("unable to open file");
269             }
270             if (m_file.Length() > 0)
271             {
272                 m_keepFile = true;
273                 m_summary.LoadSummaryInfo(m_file);
274             }
275             else
276             {
277                 // starting a new log, don't keep it until something meaningful is logged
278                 m_keepFile = false;
279                 m_summary.valid = true;
280             }
281         }
282 
283         assert(m_file.IsOpened());
284 
285         m_file.Write(_T("PHD2 version ") FULLVER _T(" [") PHD_OSNAME _T("]") _T(", Log version ") GUIDELOG_VERSION _T(". Log enabled at ") +
286             logFileTime.Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
287 
288         m_enabled = true;
289 
290         // persist state
291         pConfig->Global.SetBoolean("/LoggingMode", m_enabled);
292 
293         // dump guiding header if logging enabled during guide
294         if (pFrame && pFrame->pGuider->IsGuiding())
295             GuidingHeader(m_file);
296 
297         Flush();
298     }
299     catch (const wxString& Msg)
300     {
301         POSSIBLY_UNUSED(Msg);
302     }
303 }
304 
EnableLogging(bool enable)305 void GuidingLog::EnableLogging(bool enable)
306 {
307     if (enable)
308         EnableLogging();
309     else
310         DisableLogging();
311 }
312 
DisableLogging()313 void GuidingLog::DisableLogging()
314 {
315     if (!m_enabled)
316         return;
317 
318     if (m_file.IsOpened())
319     {
320         wxDateTime now = wxDateTime::Now();
321 
322         m_file.Write("\n");
323         m_file.Write("Log disabled at " + now.Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
324         Flush();
325     }
326 
327     m_enabled = false;
328 
329     // persist state
330     pConfig->Global.SetBoolean("/LoggingMode", m_enabled);
331 }
332 
ChangeDirLog(const wxString & newdir)333 bool GuidingLog::ChangeDirLog(const wxString& newdir)
334 {
335     bool enabled = IsEnabled();
336     bool ok = true;
337 
338     CloseGuideLog();
339 
340     if (!SetLogDir(newdir))
341     {
342         wxMessageBox(wxString::Format("invalid folder name %s, log folder unchanged", newdir));
343         ok = false;
344     }
345 
346     if (enabled)             // if SetLogDir failed, no harm no foul, stay with original. Otherwise
347     {
348         EnableLogging();     // start fresh...
349     }
350 
351     return ok;
352 }
353 
RemoveOldFiles()354 void GuidingLog::RemoveOldFiles()
355 {
356     Logger::RemoveMatchingFiles("PHD2_GuideLog*.txt", RetentionPeriod);
357 }
358 
Flush()359 bool GuidingLog::Flush()
360 {
361     if (!m_enabled)
362         return false;
363 
364     bool error = false;
365 
366     try
367     {
368         assert(m_file.IsOpened());
369 
370         if (!m_file.Flush())
371         {
372             throw ERROR_INFO("unable to flush file");
373         }
374     }
375     catch (const wxString& Msg)
376     {
377         POSSIBLY_UNUSED(Msg);
378         error = true;
379     }
380 
381     return error;
382 }
383 
CloseGuideLog()384 void GuidingLog::CloseGuideLog()
385 {
386     if (m_file.IsOpened())
387     {
388         if (m_keepFile)
389         {
390             wxDateTime now = wxDateTime::Now();
391 
392             m_file.Write("\n");
393             WriteSummaryInfo(m_file, m_summary);
394             m_file.Write("Log closed at " + now.Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
395             Flush();
396         }
397 
398         m_file.Close();
399     }
400 
401     m_enabled = false;
402 
403     if (!m_keepFile)            // Delete the file if nothing useful was logged
404     {
405         wxRemove(m_fileName);
406     }
407 }
408 
StartCalibration(const Mount * pCalibrationMount)409 void GuidingLog::StartCalibration(const Mount *pCalibrationMount)
410 {
411     m_isGuiding = true;
412 
413     if (!m_enabled)
414         return;
415 
416     assert(m_file.IsOpened());
417     wxDateTime now = wxDateTime::Now();
418 
419     m_file.Write("\n");
420     m_file.Write("Calibration Begins at " + now.Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
421     m_file.Write("Equipment Profile = " + pConfig->GetCurrentProfile() + "\n");
422 
423     m_file.Write(pFrame->GetSettingsSummary());
424     m_file.Write(pFrame->pGuider->GetSettingsSummary());
425 
426     if (pCamera)
427     {
428         m_file.Write(pCamera->GetSettingsSummary());
429         m_file.Write("Exposure = " + pFrame->ExposureDurationSummary() + "\n");
430     }
431 
432     assert(pCalibrationMount && pCalibrationMount->IsConnected());
433 
434     m_file.Write("Mount = " + pCalibrationMount->Name());
435     wxString calSettings = pCalibrationMount->CalibrationSettingsSummary();
436     if (!calSettings.IsEmpty())
437         m_file.Write(", " + calSettings);
438     m_file.Write("\n");
439 
440     m_file.Write(PointingInfo());
441     m_file.Write("\n");
442 
443     const Star& star = pFrame->pGuider->PrimaryStar();
444 
445     m_file.Write(wxString::Format("Lock position = %.3f, %.3f, Star position = %.3f, %.3f, HFD = %.2f px\n",
446                 pFrame->pGuider->LockPosition().X,
447                 pFrame->pGuider->LockPosition().Y,
448                 pFrame->pGuider->CurrentPosition().X,
449                 pFrame->pGuider->CurrentPosition().Y,
450                 star.HFD));
451 
452     m_file.Write("Direction,Step,dx,dy,x,y,Dist\n");
453 
454     Flush();
455 
456     m_keepFile = true;
457 }
458 
CalibrationFailed(const Mount * pCalibrationMount,const wxString & msg)459 void GuidingLog::CalibrationFailed(const Mount *pCalibrationMount, const wxString& msg)
460 {
461     m_isGuiding = false;
462 
463     if (!m_enabled)
464         return;
465 
466     assert(m_file.IsOpened());
467 
468     m_file.Write(msg); m_file.Write("\n");
469     Flush();
470 }
471 
CalibrationStep(const CalibrationStepInfo & info)472 void GuidingLog::CalibrationStep(const CalibrationStepInfo& info)
473 {
474     if (!m_enabled)
475         return;
476 
477     assert(m_file.IsOpened());
478 
479     // Direction,Step,dx,dy,x,y,Dist
480     m_file.Write(wxString::Format("%s,%d,%.3f,%.3f,%.3f,%.3f,%.3f\n",
481         info.direction,
482         info.stepNumber,
483         info.dx, info.dy,
484         info.pos.X, info.pos.Y,
485         info.dist));
486 
487     Flush();
488 }
489 
CalibrationDirectComplete(const Mount * pCalibrationMount,const wxString & direction,double angle,double rate,int parity)490 void GuidingLog::CalibrationDirectComplete(const Mount *pCalibrationMount, const wxString& direction, double angle, double rate, int parity)
491 {
492     if (!m_enabled)
493         return;
494 
495     assert(m_file.IsOpened());
496 
497     m_file.Write(wxString::Format("%s calibration complete. Angle = %.1f deg, Rate = %.3f px/sec, Parity = %s\n",
498         direction, degrees(angle), rate * 1000.0, ParityStr(parity)));
499 
500     Flush();
501 }
502 
CalibrationComplete(const Mount * pCalibrationMount)503 void GuidingLog::CalibrationComplete(const Mount *pCalibrationMount)
504 {
505     m_isGuiding = false;
506 
507     if (!m_enabled)
508         return;
509 
510     ++m_summary.cal_cnt;
511 
512     assert(m_file.IsOpened());
513 
514     m_file.Write(wxString::Format("Calibration complete, mount = %s.\n", pCalibrationMount->Name()));
515 
516     Flush();
517 }
518 
GuidingStarted()519 void GuidingLog::GuidingStarted()
520 {
521     m_isGuiding = true;
522 
523     if (!m_enabled)
524         return;
525 
526     assert(m_file.IsOpened());
527 
528     m_file.Write("\n");
529     m_file.Write("Guiding Begins at " + pFrame->m_guidingStarted.Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
530 
531     // add common guiding header
532     GuidingHeader(m_file);
533 
534     Flush();
535 
536     m_keepFile = true;
537 }
538 
GuidingStopped()539 void GuidingLog::GuidingStopped()
540 {
541     m_isGuiding = false;
542 
543     if (!m_enabled)
544         return;
545 
546     assert(m_file.IsOpened());
547 
548     ++m_summary.guide_cnt;
549     m_summary.guide_dur += pFrame->TimeSinceGuidingStarted();
550 
551     m_file.Write("Guiding Ends at " + wxDateTime::Now().Format(_T("%Y-%m-%d %H:%M:%S")) + "\n");
552     Flush();
553 }
554 
GuideStep(const GuideStepInfo & step)555 void GuidingLog::GuideStep(const GuideStepInfo& step)
556 {
557     if (!m_enabled)
558         return;
559 
560     assert(m_file.IsOpened());
561 
562     m_file.Write(wxString::Format("%d,%.3f,\"%s\",%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,",
563         step.frameNumber, step.time,
564         step.mount->IsStepGuider() ? "AO" : "Mount",
565         step.cameraOffset.X, step.cameraOffset.Y,
566         step.mountOffset.X, step.mountOffset.Y,
567         step.guideDistanceRA, step.guideDistanceDec));
568 
569     if (step.mount->IsStepGuider())
570     {
571         int xSteps = step.directionRA == LEFT ? -step.durationRA : step.durationRA;
572         int ySteps = step.directionDec == DOWN ? -step.durationDec : step.durationDec;
573         m_file.Write(wxString::Format(",,,,%d,%d,", xSteps, ySteps));
574     }
575     else
576     {
577         m_file.Write(wxString::Format("%d,%s,%d,%s,,,",
578             step.durationRA, step.durationRA > 0 ? step.mount->DirectionChar((GUIDE_DIRECTION)step.directionRA) : "",
579             step.durationDec, step.durationDec > 0 ? step.mount->DirectionChar((GUIDE_DIRECTION)step.directionDec): ""));
580     }
581 
582     m_file.Write(wxString::Format("%.f,%.2f,%d\n",
583             step.starMass, step.starSNR, step.starError));
584 
585     Flush();
586 }
587 
FrameDropped(const FrameDroppedInfo & info)588 void GuidingLog::FrameDropped(const FrameDroppedInfo& info)
589 {
590     if (!m_enabled)
591         return;
592 
593     assert(m_file.IsOpened());
594 
595     m_file.Write(wxString::Format("%d,%.3f,\"DROP\",,,,,,,,,,,,,%.f,%.2f,%d,\"%s\"\n",
596         info.frameNumber, info.time, info.starMass, info.starSNR, info.starError, info.status));
597 
598     Flush();
599 }
600 
601 
CalibrationFrameDropped(const FrameDroppedInfo & info)602 void GuidingLog::CalibrationFrameDropped(const FrameDroppedInfo& info)
603 {
604     if (!m_enabled)
605         return;
606 
607     assert(m_file.IsOpened());
608 
609     m_file.Write(wxString::Format("INFO: STAR LOST during calibration, Mass= %.f, SNR= %.2f, Error= %d, Status=%s\n",
610         info.starMass, info.starSNR, info.starError, info.status));
611 
612     Flush();
613 }
614 
NotifyGuidingDithered(Guider * guider,double dx,double dy)615 void GuidingLog::NotifyGuidingDithered(Guider *guider, double dx, double dy)
616 {
617     if (!m_enabled || !m_isGuiding)
618         return;
619 
620     m_file.Write(wxString::Format("INFO: DITHER by %.3f, %.3f, new lock pos = %.3f, %.3f\n",
621         dx, dy, guider->LockPosition().X, guider->LockPosition().Y));
622 
623     Flush();
624 }
625 
NotifySettlingStateChange(const wxString & msg)626 void GuidingLog::NotifySettlingStateChange(const wxString& msg)
627 {
628     if (!m_enabled)
629         return;
630     m_file.Write(wxString::Format("INFO: SETTLING STATE CHANGE, %s\n", msg));
631     Flush();
632 }
633 
NotifyGACompleted()634 void GuidingLog::NotifyGACompleted()
635 {
636     if (!m_enabled)
637         return;
638 
639     ++m_summary.ga_cnt;
640 }
641 
NotifyGAResult(const wxString & msg)642 void GuidingLog::NotifyGAResult(const wxString& msg)
643 {
644     if (!m_enabled)
645         return;
646 
647     // Client needs to handle end-of-line formatting
648     m_file.Write(wxString::Format("INFO: GA Result - %s", msg));
649     Flush();
650 }
651 
NotifySetLockPosition(Guider * guider)652 void GuidingLog::NotifySetLockPosition(Guider *guider)
653 {
654     if (!m_enabled || !m_isGuiding)
655         return;
656 
657     m_file.Write(wxString::Format("INFO: SET LOCK POSITION, new lock pos = %.3f, %.3f\n",
658         guider->LockPosition().X, guider->LockPosition().Y));
659 
660     Flush();
661 
662     m_keepFile = true;
663 }
664 
NotifyLockShiftParams(const LockPosShiftParams & shiftParams,const PHD_Point & cameraRate)665 void GuidingLog::NotifyLockShiftParams(const LockPosShiftParams& shiftParams, const PHD_Point& cameraRate)
666 {
667     if (!m_enabled || !m_isGuiding)
668         return;
669 
670     wxString details;
671     if (shiftParams.shiftEnabled)
672     {
673         details = wxString::Format("%s rate (%.2f,%.2f) %s/hr (%.2f,%.2f) px/hr",
674                                     shiftParams.shiftIsMountCoords ? "RA,Dec" : "X,Y",
675                                     shiftParams.shiftRate.IsValid() ? shiftParams.shiftRate.X : 0.0,
676                                     shiftParams.shiftRate.IsValid() ? shiftParams.shiftRate.Y : 0.0,
677                                     shiftParams.shiftUnits == UNIT_ARCSEC ? "arc-sec" : "pixels",
678                                     cameraRate.IsValid() ? cameraRate.X * 3600.0 : 0.0,
679                                     cameraRate.IsValid() ? cameraRate.Y * 3600.0 : 0.0);
680     }
681 
682     m_file.Write(wxString::Format("INFO: LOCK SHIFT, enabled = %d %s\n", shiftParams.shiftEnabled, details));
683     Flush();
684 
685     m_keepFile = true;
686 }
687 
ServerCommand(Guider * guider,const wxString & cmd)688 void GuidingLog::ServerCommand(Guider *guider, const wxString& cmd)
689 {
690     if (!m_enabled || !m_isGuiding)
691         return;
692 
693     m_file.Write(wxString::Format("INFO: Server received %s\n", cmd));
694     Flush();
695 
696     m_keepFile = true;
697 }
698 
NotifyManualGuide(const Mount * mount,int direction,int duration)699 void GuidingLog::NotifyManualGuide(const Mount *mount, int direction, int duration)
700 {
701     if (!m_enabled || !m_isGuiding)
702         return;
703 
704     m_file.Write(wxString::Format("INFO: Manual guide (%s) %s %d %s\n",
705                                   mount->IsStepGuider() ? "AO" : "Mount",
706                                   mount->DirectionStr(static_cast<GUIDE_DIRECTION>(direction)), duration,
707                                   mount->IsStepGuider() ? (duration != 1 ? "steps" : "step") : "ms"));
708     Flush();
709 
710     m_keepFile = true;
711 }
712 
SetGuidingParam(const wxString & name,double val)713 void GuidingLog::SetGuidingParam(const wxString& name, double val)
714 {
715     SetGuidingParam(name, wxString::Format("%.2f", val));
716 }
717 
SetGuidingParam(const wxString & name,int val)718 void GuidingLog::SetGuidingParam(const wxString& name, int val)
719 {
720     SetGuidingParam(name, wxString::Format("%d", val));
721 }
722 
SetGuidingParam(const wxString & name,bool val)723 void GuidingLog::SetGuidingParam(const wxString& name, bool val)
724 {
725     SetGuidingParam(name, wxString(val ? "true" : "false"));
726 }
727 
SetGuidingParam(const wxString & name,const wxString & val)728 void GuidingLog::SetGuidingParam(const wxString& name, const wxString& val)
729 {
730     if (!m_enabled || !m_isGuiding)
731         return;
732 
733     m_file.Write(wxString::Format("INFO: Guiding parameter change, %s = %s\n", name, val));
734     Flush();
735 
736     m_keepFile = true;
737 }
738 
SetGuidingParam(const wxString & name,const wxString & val,bool AlwaysLog)739 void GuidingLog::SetGuidingParam(const wxString& name, const wxString& val, bool AlwaysLog)
740 {
741     m_file.Write(wxString::Format("INFO: Guiding parameter change, %s = %s\n", name, val));
742     Flush();
743 
744     m_keepFile = true;
745 }
746