1 /*
2 * drift_tool.h
3 * PHD Guiding
4 *
5 * Created by Andy Galasso
6 * Copyright (c) 2013-2014 Andy Galasso
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 Craig Stark, Stark Labs nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35 #include "phd.h"
36 #include "drift_tool.h"
37
38 #include <wx/gbsizer.h>
39 #include <wx/valnum.h>
40
41 enum Phase
42 {
43 PHASE_ADJUST_AZ,
44 PHASE_ADJUST_ALT,
45 };
46
47 enum Mode
48 {
49 MODE_IDLE,
50 MODE_DRIFT,
51 MODE_ADJUST,
52 };
53
54 enum CtrlIds
55 {
56 ID_SLEW = 10001,
57 ID_SAVE,
58 ID_DRIFT,
59 ID_ADJUST,
60 ID_PHASE,
61 ID_TIMER,
62 };
63
64 struct DriftToolWin : public wxFrame
65 {
66 DriftToolWin();
67 ~DriftToolWin();
68
69 void UpdatePhaseState();
70 void UpdateModeState();
71
72 Phase m_phase;
73 Mode m_mode;
74 bool m_drifting;
75 bool m_need_end_dec_drift;
76 bool m_save_lock_pos_is_sticky;
77 bool m_save_use_subframes;
78 GraphLogClientWindow::GRAPH_MODE m_save_graph_mode;
79 int m_save_graph_length;
80 int m_save_graph_height;
81 bool m_location_prompt_done;
82
83 bool m_can_slew;
84 bool m_slewing;
85 PHD_Point m_siteLatLong;
86
87 wxStaticBitmap *m_bmp;
88 wxBitmap *m_azArrowBmp;
89 wxBitmap *m_altArrowBmp;
90 wxStaticText *m_instructions;
91 wxTextCtrl *m_raCurrent;
92 wxTextCtrl *m_decCurrent;
93 wxSpinCtrl *m_raSlew;
94 wxSpinCtrl *m_decSlew;
95 wxButton *m_slew;
96 wxButton *m_saveCoords;
97 wxStaticText *m_notesLabel;
98 wxTextCtrl *m_notes;
99 wxButton *m_drift;
100 wxButton *m_adjust;
101 wxButton *m_phaseBtn;
102 wxStatusBar *m_statusBar;
103 wxTimer *m_timer;
104
105 void EnableSlew(bool enable);
106
107 void OnSlew(wxCommandEvent& evt);
108 void OnSaveCoords(wxCommandEvent& evt);
109 void OnNotesText(wxCommandEvent& evt);
110 void OnDrift(wxCommandEvent& evt);
111 void OnAdjust(wxCommandEvent& evt);
112 void OnPhase(wxCommandEvent& evt);
113 void OnAppStateNotify(wxCommandEvent& evt);
114 void OnClose(wxCloseEvent& evt);
115 void OnTimer(wxTimerEvent& evt);
116
117 void UpdateScopeCoordinates(void);
118
119 void SetStatusText(const wxString &text, int number = 0) override;
120
121 DECLARE_EVENT_TABLE()
122 };
123
BEGIN_EVENT_TABLE(DriftToolWin,wxFrame)124 BEGIN_EVENT_TABLE(DriftToolWin, wxFrame)
125 EVT_BUTTON(ID_SLEW, DriftToolWin::OnSlew)
126 EVT_BUTTON(ID_SAVE, DriftToolWin::OnSaveCoords)
127 EVT_BUTTON(ID_DRIFT, DriftToolWin::OnDrift)
128 EVT_BUTTON(ID_ADJUST, DriftToolWin::OnAdjust)
129 EVT_BUTTON(ID_PHASE, DriftToolWin::OnPhase)
130 EVT_COMMAND(wxID_ANY, APPSTATE_NOTIFY_EVENT, DriftToolWin::OnAppStateNotify)
131 EVT_CLOSE(DriftToolWin::OnClose)
132 EVT_TIMER(ID_TIMER, DriftToolWin::OnTimer)
133 END_EVENT_TABLE()
134
135 DriftToolWin::DriftToolWin()
136 : wxFrame(pFrame, wxID_ANY, _("Drift Align"), wxDefaultPosition, wxDefaultSize,
137 wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxTAB_TRAVERSAL|wxFRAME_FLOAT_ON_PARENT|wxFRAME_NO_TASKBAR),
138 m_need_end_dec_drift(false),
139 m_slewing(false)
140 {
141 SetSizeHints(wxDefaultSize, wxDefaultSize);
142
143 // a vertical sizer holding everything
144 wxBoxSizer *topSizer = new wxBoxSizer(wxVERTICAL);
145
146 // a horizontal box sizer for the bitmap and the instructions
147 wxBoxSizer *instrSizer = new wxBoxSizer(wxHORIZONTAL);
148
149 # include "icons/AzArrow.xpm"
150 m_azArrowBmp = new wxBitmap(AzArrow);
151 # include "icons/AltArrow.xpm"
152 m_altArrowBmp = new wxBitmap(AltArrow);
153
154 m_bmp = new wxStaticBitmap(this, wxID_ANY, *m_azArrowBmp, wxDefaultPosition, wxSize(80, 100));
155 instrSizer->Add(m_bmp, 0, wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 5);
156
157 m_instructions = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(400,120), wxALIGN_LEFT|wxST_NO_AUTORESIZE);
158 #ifdef __WXOSX__
159 m_instructions->SetFont(*wxSMALL_FONT);
160 #endif
161 m_instructions->Wrap(-1);
162 instrSizer->Add(m_instructions, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
163
164 topSizer->Add(instrSizer);
165
166 // static box sizer holding the scope pointing controls
167 wxStaticBoxSizer *sbSizer = new wxStaticBoxSizer(new wxStaticBox(this, wxID_ANY, _("Scope Pointing")), wxVERTICAL);
168
169 // a grid box sizer for laying out scope pointing the controls
170 wxGridBagSizer *gbSizer = new wxGridBagSizer(0, 0);
171 gbSizer->SetFlexibleDirection(wxBOTH);
172 gbSizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
173
174 wxStaticText *txt;
175
176 txt = new wxStaticText(this, wxID_ANY, wxString::Format(_("Meridian Offset (%s)"), DEGREES_SYMBOL));
177 txt->Wrap(-1);
178 gbSizer->Add(txt, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL, 5);
179
180 txt = new wxStaticText(this, wxID_ANY, wxString::Format(_("Declination (%s)"), DEGREES_SYMBOL));
181 txt->Wrap(-1);
182 gbSizer->Add(txt, wxGBPosition(0, 2), wxGBSpan(1, 1), wxALL, 5);
183
184 txt = new wxStaticText(this, wxID_ANY, _("Current"));
185 txt->Wrap(-1);
186 gbSizer->Add(txt, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL, 5);
187
188 m_raCurrent = new wxTextCtrl(this, wxID_ANY, _T("--"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
189 gbSizer->Add(m_raCurrent, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5);
190
191 m_decCurrent = new wxTextCtrl(this, wxID_ANY, _T("--"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
192 gbSizer->Add(m_decCurrent, wxGBPosition(1, 2), wxGBSpan(1, 1), wxEXPAND | wxALL, 5);
193
194 txt = new wxStaticText(this, wxID_ANY, _("Slew To"));
195 txt->Wrap(-1);
196 gbSizer->Add(txt, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALL, 5);
197
198 m_raSlew = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, -90, 90);
199 gbSizer->Add(m_raSlew, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND | wxALL, 5);
200
201 m_decSlew = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, -90, 90);
202 gbSizer->Add(m_decSlew, wxGBPosition(2, 2), wxGBSpan(1, 1), wxEXPAND | wxALL, 5);
203
204 m_slew = new wxButton(this, ID_SLEW, _("Slew"));
205 m_slew->SetToolTip(_("Click to slew to given coordinates."));
206 gbSizer->Add(m_slew, wxGBPosition(2, 3), wxGBSpan(1, 1), wxALL, 5);
207
208 wxString label = _("Save");
209 wxSize sz(GetTextExtent(label));
210 sz.SetHeight(-1);
211 sz.IncBy(16, 0);
212 m_saveCoords = new wxButton(this, ID_SAVE, label, wxDefaultPosition, sz, 0);
213 m_saveCoords->SetToolTip(_("Click to save these coordinates as the default location for this axis adjustment."));
214 gbSizer->Add(m_saveCoords, wxGBPosition(2, 4), wxGBSpan(1, 1), wxTOP | wxBOTTOM | wxRIGHT, 5);
215
216 // add grid bag sizer to static sizer
217 sbSizer->Add(gbSizer, 1, wxALIGN_CENTER, 5);
218
219 // add static sizer to top-level sizer
220 topSizer->Add(sbSizer, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5);
221
222 // add some padding below the static sizer
223 topSizer->Add(0, 3, 0, wxEXPAND, 3);
224
225 m_notesLabel = new wxStaticText(this, wxID_ANY, _("Altitude adjustment notes"));
226 m_notesLabel->Wrap(-1);
227 topSizer->Add(m_notesLabel, 0, wxEXPAND|wxTOP|wxLEFT, 8);
228
229 m_notes = new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(-1, 54), wxTE_MULTILINE);
230 pFrame->RegisterTextCtrl(m_notes);
231 topSizer->Add(m_notes, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5);
232 m_notes->Bind(wxEVT_COMMAND_TEXT_UPDATED, &DriftToolWin::OnNotesText, this);
233
234 // horizontal sizer for the buttons
235 wxBoxSizer *hSizer = new wxBoxSizer(wxHORIZONTAL);
236
237 // proportional pad on left of Drift button
238 hSizer->Add(0, 0, 2, wxEXPAND, 5);
239
240 m_drift = new wxButton(this, ID_DRIFT, _("Drift"));
241 hSizer->Add(m_drift, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
242
243 // proportional pad on right of Drift button
244 hSizer->Add(0, 0, 1, wxEXPAND, 5);
245
246 m_adjust = new wxButton(this, ID_ADJUST, _("Adjust"));
247 hSizer->Add(m_adjust, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
248
249 // proportional pad on right of Align button
250 hSizer->Add(0, 0, 2, wxEXPAND, 5);
251
252 m_phaseBtn = new wxButton(this, ID_PHASE, wxT("???"));
253 hSizer->Add(m_phaseBtn, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
254
255 // add button sizer to top level sizer
256 topSizer->Add(hSizer, 1, wxEXPAND|wxALL, 5);
257
258 SetSizer(topSizer);
259
260 m_statusBar = CreateStatusBar(1, wxST_SIZEGRIP, wxID_ANY);
261
262 Layout();
263 topSizer->Fit(this);
264
265 int xpos = pConfig->Global.GetInt("/DriftTool/pos.x", -1);
266 int ypos = pConfig->Global.GetInt("/DriftTool/pos.y", -1);
267 MyFrame::PlaceWindowOnScreen(this, xpos, ypos);
268
269 // can mount slew?
270 m_can_slew = pPointingSource && pPointingSource->CanSlew();
271
272 // get site lat/long from scope
273 double lat, lon;
274 m_siteLatLong.Invalidate();
275 if (pPointingSource && !pPointingSource->GetSiteLatLong(&lat, &lon))
276 {
277 m_siteLatLong.SetXY(lat, lon);
278 }
279
280 m_timer = NULL;
281 if (m_can_slew || (pPointingSource && pPointingSource->CanReportPosition()))
282 {
283 enum { SCOPE_POS_POLL_MS = 1500 };
284 m_timer = new wxTimer(this, ID_TIMER);
285 m_timer->Start(SCOPE_POS_POLL_MS, false /* continuous */);
286 }
287
288 // make sure graph window is showing
289 if (!pFrame->pGraphLog->IsShown())
290 {
291 wxCommandEvent evt;
292 evt.SetInt(1); // "Checked"
293 pFrame->OnGraph(evt);
294 }
295
296 // graph must be showing RA/Dec
297 m_save_graph_mode = pFrame->pGraphLog->SetMode(GraphLogClientWindow::MODE_RADEC);
298
299 // resize graph scale
300 m_save_graph_length = pFrame->pGraphLog->GetLength();
301 pFrame->pGraphLog->SetLength(pConfig->Global.GetInt("/DriftTool/GraphLength", GraphLogWindow::DefaultMaxLength));
302 m_save_graph_height = pFrame->pGraphLog->GetHeight();
303 pFrame->pGraphLog->SetHeight(pConfig->Global.GetInt("/DriftTool/GraphHeight", GraphLogWindow::DefaultMaxHeight));
304 pFrame->pGraphLog->Refresh();
305
306 // we do not want sticky lock position enabled
307 m_save_lock_pos_is_sticky = pFrame->pGuider->LockPosIsSticky();
308 pFrame->pGuider->SetLockPosIsSticky(false);
309 pFrame->tools_menu->FindItem(EEGG_STICKY_LOCK)->Check(false);
310
311 m_save_use_subframes = pCamera->UseSubframes;
312
313 m_phase = PHASE_ADJUST_AZ;
314 m_mode = MODE_IDLE;
315 m_location_prompt_done = false;
316 UpdatePhaseState();
317 UpdateModeState();
318 }
319
~DriftToolWin()320 DriftToolWin::~DriftToolWin()
321 {
322 delete m_timer;
323 delete m_azArrowBmp;
324 delete m_altArrowBmp;
325 pFrame->pDriftTool = NULL;
326 }
327
EnableSlew(bool enable)328 void DriftToolWin::EnableSlew(bool enable)
329 {
330 m_raSlew->Enable(enable);
331 m_decSlew->Enable(enable);
332 m_slew->Enable(enable && !m_slewing);
333 m_saveCoords->Enable(enable);
334 }
335
LoadRADec(Phase phase,double * ra,double * dec)336 static void LoadRADec(Phase phase, double *ra, double *dec)
337 {
338 if (phase == PHASE_ADJUST_AZ)
339 {
340 *ra = pConfig->Global.GetDouble("/DriftTool/Az/SlewRAOfs", 0.0);
341 *dec = pConfig->Global.GetDouble("/DriftTool/Az/SlewDec", 0.0);
342 }
343 else
344 {
345 *ra = pConfig->Global.GetDouble("/DriftTool/Alt/SlewRAOfs", -65.0);
346 *dec = pConfig->Global.GetDouble("/DriftTool/Alt/SlewDec", 0.0);
347 }
348 }
349
SaveRADec(Phase phase,double ra,double dec)350 static void SaveRADec(Phase phase, double ra, double dec)
351 {
352 if (phase == PHASE_ADJUST_AZ)
353 {
354 pConfig->Global.SetDouble("/DriftTool/Az/SlewRAOfs", ra);
355 pConfig->Global.SetDouble("/DriftTool/Az/SlewDec", dec);
356 }
357 else
358 {
359 pConfig->Global.SetDouble("/DriftTool/Alt/SlewRAOfs", ra);
360 pConfig->Global.SetDouble("/DriftTool/Alt/SlewDec", dec);
361 }
362 }
363
UpdatePhaseState()364 void DriftToolWin::UpdatePhaseState()
365 {
366 double ra, dec;
367 LoadRADec(m_phase, &ra, &dec);
368 m_raSlew->SetValue((int) floor(ra));
369 m_decSlew->SetValue((int) floor(dec));
370
371 if (m_phase == PHASE_ADJUST_AZ)
372 {
373 SetTitle(_("Drift Align - Azimuth Adjustment"));
374 m_bmp->SetBitmap(*m_azArrowBmp);
375 m_instructions->SetLabel(
376 _("Slew to near the Meridian and the Equator.\n"
377 "Press Drift to measure drift, watch the Dec trend line.\n"
378 "Press Adjust and adjust your mount's azimuth.\n"
379 "Repeat Drift/Adjust until alignment is complete.\n"
380 "Then, click Altitude to begin Altitude adjustment."));
381 m_notesLabel->SetLabel(_("Azimuth adjustment notes"));
382 m_notes->SetValue(pConfig->Profile.GetString("/DriftTool/Az/Notes", wxEmptyString));
383 m_phaseBtn->SetLabel(_("> Altitude"));
384 }
385 else
386 {
387 SetTitle(_("Drift Align - Altitude Adjustment"));
388 m_bmp->SetBitmap(*m_altArrowBmp);
389 m_instructions->SetLabel(
390 _("Slew to a location near the Equator and the Eastern or Western horizon.\n"
391 "Press Drift to measure drift, watch the Dec trend line.\n"
392 "Press Adjust and adjust your mount's altitude.\n"
393 "Repeat Drift/Adjust until alignment is complete.\n"
394 "Click Azimuth to repeat Azimuth adjustment."));
395 m_notesLabel->SetLabel(_("Altitude adjustment notes"));
396 m_notes->SetValue(pConfig->Profile.GetString("/DriftTool/Alt/Notes", wxEmptyString));
397 m_phaseBtn->SetLabel(_("< Azimuth"));
398 }
399 }
400
UpdateModeState()401 void DriftToolWin::UpdateModeState()
402 {
403 wxString idleStatus;
404
405 repeat:
406
407 if (m_mode == MODE_DRIFT)
408 {
409 m_drift->Enable(false);
410 m_adjust->Enable(true);
411 EnableSlew(false);
412
413 // restore subframes setting
414 pCamera->UseSubframes = m_save_use_subframes;
415
416 // restore normal guider behavior for star lost events
417 pFrame->pGuider->SetIgnoreLostStarLooping(false);
418
419 if (!m_drifting)
420 {
421 if (!pCamera->Connected ||
422 !pMount || !pMount->IsConnected())
423 {
424 idleStatus = _("Please connect a camera and a mount");
425 m_mode = MODE_IDLE;
426 goto repeat;
427 }
428
429 if (!pMount->IsCalibrated())
430 {
431 idleStatus = _("Please calibrate before starting drift alignment");
432 m_mode = MODE_IDLE;
433 goto repeat;
434 }
435
436 if (!pFrame->CaptureActive)
437 {
438 // loop exposures
439 SetStatusText(_("Start Looping..."));
440 pFrame->StartLoopingInteractive(_T("DriftTool:drift"));
441 return;
442 }
443
444 GUIDER_STATE st = pFrame->pGuider->GetState();
445 switch (st)
446 {
447 case STATE_UNINITIALIZED:
448 case STATE_CALIBRATED:
449 case STATE_SELECTING:
450 if (!pFrame->pGuider->IsLocked())
451 {
452 SetStatusText(_("Auto-selecting a star"));
453 // call Guider's AutoSelect directly to skip MyFrame::AutoSelectStar guide state check
454 pFrame->pGuider->AutoSelect();
455 }
456 return;
457 case STATE_CALIBRATING_PRIMARY:
458 case STATE_CALIBRATING_SECONDARY:
459 if (!pMount->IsCalibrated())
460 SetStatusText(_("Waiting for calibration to complete..."));
461 return;
462 case STATE_SELECTED:
463 SetStatusText(_("Start guiding..."));
464 pFrame->GuideButtonClick(false, _T("DriftTool:drift"));
465 return;
466 case STATE_GUIDING: {
467 // turn of dec guiding
468 if (!m_need_end_dec_drift)
469 {
470 pMount->StartDecDrift();
471 m_need_end_dec_drift = true;
472 }
473 // clear graph data
474 SetStatusText(_("Drifting... click Adjust when done drifting"));
475 wxCommandEvent dummy;
476 pFrame->pGraphLog->OnButtonClear(dummy);
477 pFrame->pGraphLog->EnableTrendLines(true);
478 m_drifting = true;
479 return;
480 }
481 case STATE_STOP:
482 return;
483 }
484 }
485 }
486 else if (m_mode == MODE_ADJUST)
487 {
488 m_drift->Enable(true);
489 m_adjust->Enable(false);
490 m_drifting = false;
491 EnableSlew(m_can_slew);
492 SetStatusText(m_phase == PHASE_ADJUST_AZ ? _("Adjust azimuth, click Drift when done") :
493 _("Adjust altitude, click Drift when done"));
494
495 // use full frames for adjust phase
496 pCamera->UseSubframes = false;
497
498 if (pFrame->pGuider->IsGuiding())
499 {
500 // stop guiding but continue looping
501 pFrame->StartLoopingInteractive(_T("DriftTool:adjust"));
502
503 // Set the lock position to the where the star has drifted to. This will be the center of the polar align circle.
504 pFrame->pGuider->SetLockPosition(pFrame->pGuider->CurrentPosition());
505 // Make sure guider does not react to star lost (like by invalidating the lock position) while adjustment is in progress
506 pFrame->pGuider->SetIgnoreLostStarLooping(true);
507 pFrame->pGraphLog->Refresh(); // polar align circle is updated in graph window's OnPaint handler
508 }
509 }
510 else // MODE_IDLE
511 {
512 m_drift->Enable(true);
513 m_adjust->Enable(true);
514 m_drifting = false;
515 EnableSlew(m_can_slew);
516 SetStatusText(idleStatus);
517
518 // restore subframes setting
519 pCamera->UseSubframes = m_save_use_subframes;
520
521 // restore normal guider behavior for star lost events
522 pFrame->pGuider->SetIgnoreLostStarLooping(false);
523
524 if (pFrame->pGuider->IsGuiding())
525 {
526 // stop guiding but continue looping
527 pFrame->StartLoopingInteractive(_T("DriftTool:idle"));
528 }
529 }
530 }
531
OnSlew(wxCommandEvent & evt)532 void DriftToolWin::OnSlew(wxCommandEvent& evt)
533 {
534 double raSlew = (double) m_raSlew->GetValue();
535 double decSlew = (double) m_decSlew->GetValue();
536
537 double cur_ra, cur_dec, cur_st;
538 if (pPointingSource->GetCoordinates(&cur_ra, &cur_dec, &cur_st))
539 {
540 Debug.Write("Drift tool: slew failed to get scope coordinates\n");
541 return;
542 }
543
544 double slew_ra = norm_ra(cur_st + (raSlew * 24.0 / 360.0));
545
546 Debug.Write(wxString::Format("Drift tool: slew from ra %.2f, dec %.1f to ra %.2f, dec %.1f\n",
547 cur_ra, cur_dec, slew_ra, decSlew));
548
549 if (pPointingSource->CanSlewAsync())
550 {
551 struct SlewInBg : public RunInBg
552 {
553 double ra, dec;
554 SlewInBg(wxWindow *parent, double ra_, double dec_)
555 : RunInBg(parent, _("Slew"), _("Slewing...")), ra(ra_), dec(dec_)
556 {
557 SetPopupDelay(100);
558 }
559 bool Entry()
560 {
561 bool err = pPointingSource->SlewToCoordinatesAsync(ra, dec);
562 if (err)
563 {
564 SetErrorMsg(_("Slew failed!"));
565 return true;
566 }
567 while (pPointingSource->Slewing())
568 {
569 wxMilliSleep(500);
570 if (IsCanceled())
571 {
572 pPointingSource->AbortSlew();
573 SetErrorMsg(_("Slew was canceled"));
574 break;
575 }
576 }
577 return false;
578 }
579 };
580 SlewInBg bg(this, slew_ra, decSlew);
581 if (bg.Run())
582 {
583 SetStatusText(bg.GetErrorMsg());
584 }
585 }
586 else
587 {
588 wxBusyCursor busy;
589 m_slewing = true;
590 m_slew->Enable(false);
591 GetStatusBar()->PushStatusText(_("Slewing ..."));
592 if (pPointingSource->SlewToCoordinates(slew_ra, decSlew))
593 {
594 GetStatusBar()->PopStatusText();
595 m_slewing = false;
596 m_slew->Enable(true);
597 Debug.Write("Drift tool: slew failed\n");
598 }
599 }
600
601 SaveRADec(m_phase, raSlew, decSlew);
602 }
603
OnSaveCoords(wxCommandEvent & evt)604 void DriftToolWin::OnSaveCoords(wxCommandEvent& evt)
605 {
606 double raSlew = (double) m_raSlew->GetValue();
607 double decSlew = (double) m_decSlew->GetValue();
608
609 SaveRADec(m_phase, raSlew, decSlew);
610 SetStatusText(_("Coordinates saved."));
611 }
612
OnNotesText(wxCommandEvent & evt)613 void DriftToolWin::OnNotesText(wxCommandEvent& evt)
614 {
615 if (m_phase == PHASE_ADJUST_AZ)
616 {
617 pConfig->Profile.SetString("/DriftTool/Az/Notes", m_notes->GetValue());
618 }
619 else
620 {
621 pConfig->Profile.SetString("/DriftTool/Alt/Notes", m_notes->GetValue());
622 }
623 }
624
OnDrift(wxCommandEvent & evt)625 void DriftToolWin::OnDrift(wxCommandEvent& evt)
626 {
627 Debug.Write("Drift tool: Drift\n");
628
629 if (!m_location_prompt_done)
630 {
631 if (pPointingSource && pPointingSource->IsConnected())
632 {
633 bool error = pPointingSource->PreparePositionInteractive();
634 if (error)
635 return;
636 m_location_prompt_done = true;
637 }
638 }
639
640 m_mode = MODE_DRIFT;
641 UpdateModeState();
642 }
643
OnAdjust(wxCommandEvent & evt)644 void DriftToolWin::OnAdjust(wxCommandEvent& evt)
645 {
646 Debug.Write("Drift tool: Adjust\n");
647 m_mode = MODE_ADJUST;
648 UpdateModeState();
649 }
650
OnPhase(wxCommandEvent & evt)651 void DriftToolWin::OnPhase(wxCommandEvent& evt)
652 {
653 if (m_phase == PHASE_ADJUST_ALT)
654 m_phase = PHASE_ADJUST_AZ;
655 else
656 m_phase = PHASE_ADJUST_ALT;
657
658 Debug.Write(wxString::Format("Drift tool: Phase %s\n", m_phase == PHASE_ADJUST_ALT ? wxS("Alt") : wxS("Az")));
659
660 m_location_prompt_done = false;
661
662 UpdatePhaseState();
663
664 if (m_mode != MODE_IDLE)
665 {
666 m_mode = MODE_IDLE;
667 UpdateModeState();
668 }
669 }
670
OnAppStateNotify(wxCommandEvent & evt)671 void DriftToolWin::OnAppStateNotify(wxCommandEvent& evt)
672 {
673 UpdateModeState();
674 }
675
OnClose(wxCloseEvent & evt)676 void DriftToolWin::OnClose(wxCloseEvent& evt)
677 {
678 Debug.Write("Drift tool: Close DriftTool\n");
679
680 if (m_need_end_dec_drift)
681 {
682 pMount->EndDecDrift();
683 pFrame->pGraphLog->EnableTrendLines(false);
684 m_need_end_dec_drift = false;
685 }
686
687 // restore graph mode
688 pFrame->pGraphLog->SetMode(m_save_graph_mode);
689
690 // restore graph scale
691 pConfig->Global.SetInt("/DriftTool/GraphLength", pFrame->pGraphLog->GetLength());
692 pFrame->pGraphLog->SetLength(m_save_graph_length);
693 pConfig->Global.SetInt("/DriftTool/GraphHeight", pFrame->pGraphLog->GetHeight());
694 pFrame->pGraphLog->SetHeight(m_save_graph_height);
695 pFrame->pGraphLog->Refresh();
696
697 // turn sticky lock position back on if we disabled it
698 if (m_save_lock_pos_is_sticky)
699 {
700 pFrame->pGuider->SetLockPosIsSticky(true);
701 pFrame->tools_menu->FindItem(EEGG_STICKY_LOCK)->Check(true);
702 }
703
704 // restore subframes setting
705 pCamera->UseSubframes = m_save_use_subframes;
706
707 // restore normal guider behavior for star lost events
708 pFrame->pGuider->SetIgnoreLostStarLooping(false);
709
710 // save the window position
711 int x, y;
712 GetPosition(&x, &y);
713 pConfig->Global.SetInt("/DriftTool/pos.x", x);
714 pConfig->Global.SetInt("/DriftTool/pos.y", y);
715
716 // restore polar align circle correction factor
717 pFrame->pGuider->SetPolarAlignCircleCorrection(1.0);
718
719 Destroy();
720 }
721
UpdateScopeCoordinates(void)722 void DriftToolWin::UpdateScopeCoordinates(void)
723 {
724 if (!pMount)
725 return;
726
727 double ra_hrs, dec_deg, st_hrs;
728 if (pPointingSource->GetCoordinates(&ra_hrs, &dec_deg, &st_hrs))
729 return; // error
730 double ra_ofs_deg = (ra_hrs - st_hrs) * (360.0 / 24.0);
731 if (ra_ofs_deg > 180.0)
732 ra_ofs_deg -= 360.0;
733 if (ra_ofs_deg <= -180.0)
734 ra_ofs_deg += 360.0;
735
736 m_raCurrent->SetValue(wxString::Format("%+.f", ra_ofs_deg));
737 m_decCurrent->SetValue(wxString::Format("%+.f", dec_deg));
738
739 // update polar align circle radius
740 if (m_siteLatLong.IsValid())
741 {
742 double correction;
743
744 if (m_phase == PHASE_ADJUST_AZ)
745 {
746 // azimuth correction from "Star Offset Positioning for Polar Axis Alignment", Frank Barrett, 2/19/2010
747 double dec_r = radians(dec_deg);
748 if (fabs(dec_r) < Scope::DEC_COMP_LIMIT)
749 {
750 double alt_r = radians(90.0 - m_siteLatLong.X + dec_deg);
751 correction = cos(alt_r) / cos(dec_r);
752 }
753 else
754 {
755 correction = 1.0;
756 }
757 }
758 else
759 {
760 // altitude correction:
761 // convert scope coordinates (RA = a, Dec = d) to cartesian coords:
762 // x = cos a cos d
763 // y = sin a cos d
764 // z = sin d
765 // altitude adjustment is rotation about x-axis, so correction factor is radius of
766 // circle of projection of scope vector onto the plane of the meridian (y-z plane)
767 // r^2 = y^2 + z^2
768 // substitute y and z above and use a = 90 - h (h = hour angle)
769 //
770 // -ag
771 //
772 double ha_r = radians(ra_ofs_deg);
773 double cos_dec = cos(radians(dec_deg));
774 double cos_ha = cos(ha_r);
775 correction = sqrt(1. + cos_dec * cos_dec * (cos_ha * cos_ha - 1.));
776
777 // drift rate for the altitude measurement is assumed to be measured at the horizon,
778 // but rate decreases as we move away from the horizon - Measuring Polar Axis Alignment
779 // Error, Frank Barrett 2nd Edition 2 / 19 / 2010, Equation (2)
780 if (fabs(ra_ofs_deg) > 15.)
781 {
782 correction /= fabs(sin(ha_r));
783 }
784 else
785 {
786 correction = 1.0;
787 }
788 }
789
790 pFrame->pGuider->SetPolarAlignCircleCorrection(correction);
791 }
792 }
793
OnTimer(wxTimerEvent & evt)794 void DriftToolWin::OnTimer(wxTimerEvent& evt)
795 {
796 UpdateScopeCoordinates();
797
798 if (m_slewing)
799 {
800 if (!pPointingSource || !pPointingSource->Slewing())
801 {
802 m_slew->Enable(true);
803 m_slewing = false;
804 GetStatusBar()->PopStatusText(); // clear "slewing" message
805 }
806 }
807 }
808
SetStatusText(const wxString & text,int number)809 void DriftToolWin::SetStatusText(const wxString &text, int number)
810 {
811 Debug.Write(wxString::Format("Drift tool: status: %s\n", text));
812 wxFrame::SetStatusText(text, number);
813 }
814
CreateDriftToolWindow()815 wxWindow *DriftTool::CreateDriftToolWindow()
816 {
817 if (!pCamera)
818 {
819 wxMessageBox(_("Please connect a camera first."));
820 return 0;
821 }
822
823 // confirm that image scale is specified
824
825 if (pFrame->GetCameraPixelScale() == 1.0)
826 {
827 bool confirmed = ConfirmDialog::Confirm(_(
828 "The Drift Align tool is most effective when PHD2 knows your guide\n"
829 "scope focal length and camera pixel size.\n"
830 "\n"
831 "Enter your guide scope focal length on the Global tab in the Brain.\n"
832 "Enter your camera pixel size on the Camera tab in the Brain.\n"
833 "\n"
834 "Would you like to run the drift tool anyway?"),
835 "/drift_tool_without_pixscale");
836
837 if (!confirmed)
838 {
839 return 0;
840 }
841 }
842
843 return new DriftToolWin();
844 }
845