1 /*
2  *  guide_algorithm_ra.cpp
3  *  PHD Guiding
4  *
5  *  Created by Bret McKee
6  *  Copyright (c) 2012 Bret McKee
7  *  All rights reserved.
8  *
9  *  Based upon work by Craig Stark.
10  *  Copyright (c) 2006-2010 Craig Stark.
11  *  All rights reserved.
12  *
13  *  This source code is distributed under the following "BSD" license
14  *  Redistribution and use in source and binary forms, with or without
15  *  modification, are permitted provided that the following conditions are met:
16  *    Redistributions of source code must retain the above copyright notice,
17  *     this list of conditions and the following disclaimer.
18  *    Redistributions in binary form must reproduce the above copyright notice,
19  *     this list of conditions and the following disclaimer in the
20  *     documentation and/or other materials provided with the distribution.
21  *    Neither the name of Bret McKee, Dad Dog Development,
22  *     Craig Stark, Stark Labs nor the names of its
23  *     contributors may be used to endorse or promote products derived from
24  *     this software without specific prior written permission.
25  *
26  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
30  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  *  POSSIBILITY OF SUCH DAMAGE.
37  *
38  */
39 
40 #include "phd.h"
41 
42 static const double DefaultMinMove    = 0.2;
43 static const double DefaultHysteresis = 0.1;
44 static const double DefaultAggression = 0.7;
45 static const double MaxAggression = 2.0;
46 static const double MaxHysteresis = 0.99;
47 
GuideAlgorithmHysteresis(Mount * pMount,GuideAxis axis)48 GuideAlgorithmHysteresis::GuideAlgorithmHysteresis(Mount *pMount, GuideAxis axis)
49     : GuideAlgorithm(pMount, axis)
50 {
51     wxString configPath = GetConfigPath();
52 
53     double minMove    = pConfig->Profile.GetDouble(configPath + "/minMove", DefaultMinMove);
54     SetMinMove(minMove);
55 
56     double hysteresis = pConfig->Profile.GetDouble(configPath + "/hysteresis", DefaultHysteresis);
57     SetHysteresis(hysteresis);
58 
59     double aggression = pConfig->Profile.GetDouble(configPath + "/aggression", DefaultAggression);
60     SetAggression(aggression);
61 
62     reset();
63 }
64 
~GuideAlgorithmHysteresis(void)65 GuideAlgorithmHysteresis::~GuideAlgorithmHysteresis(void)
66 {
67 }
68 
Algorithm() const69 GUIDE_ALGORITHM GuideAlgorithmHysteresis::Algorithm() const
70 {
71     return GUIDE_ALGORITHM_HYSTERESIS;
72 }
reset(void)73 void GuideAlgorithmHysteresis::reset(void)
74 {
75     m_lastMove = 0;
76 }
77 
result(double input)78 double GuideAlgorithmHysteresis::result(double input)
79 {
80     double dReturn = (1.0 - m_hysteresis) * input + m_hysteresis * m_lastMove;
81 
82     dReturn *= m_aggression;
83 
84     if (fabs(input) < m_minMove)
85     {
86         dReturn = 0.0;
87     }
88 
89     m_lastMove = dReturn;
90 
91     Debug.Write(wxString::Format("GuideAlgorithmHysteresis::Result() returns %.2f from input %.2f\n", dReturn, input));
92 
93     return dReturn;
94 }
95 
SetMinMove(double minMove)96 bool GuideAlgorithmHysteresis::SetMinMove(double minMove)
97 {
98     bool bError = false;
99 
100     try
101     {
102         if (minMove < 0.0)
103         {
104             throw ERROR_INFO("invalid minMove");
105         }
106 
107         m_minMove = minMove;
108 
109     }
110     catch (const wxString& Msg)
111     {
112         POSSIBLY_UNUSED(Msg);
113         bError = true;
114         m_minMove = DefaultMinMove;
115     }
116 
117     pConfig->Profile.SetDouble(GetConfigPath() + "/minMove", m_minMove);
118 
119     return bError;
120 }
121 
SetHysteresis(double hysteresis)122 bool GuideAlgorithmHysteresis::SetHysteresis(double hysteresis)
123 {
124     bool bError = false;
125 
126     try
127     {
128         if (hysteresis < 0.0 || hysteresis > MaxHysteresis)
129         {
130             throw ERROR_INFO("invalid hysteresis");
131         }
132 
133         m_hysteresis = hysteresis;
134 
135     }
136     catch (const wxString& Msg)
137     {
138         POSSIBLY_UNUSED(Msg);
139         bError = true;
140         m_hysteresis = wxMin(wxMax(hysteresis, 0.0), MaxHysteresis);
141     }
142 
143     pConfig->Profile.SetDouble(GetConfigPath() + "/hysteresis", m_hysteresis);
144 
145     return bError;
146 }
147 
SetAggression(double aggression)148 bool GuideAlgorithmHysteresis::SetAggression(double aggression)
149 {
150     bool bError = false;
151 
152     try
153     {
154         if (aggression < 0.0 || aggression > MaxAggression)
155         {
156             throw ERROR_INFO("invalid aggression");
157         }
158 
159         m_aggression = aggression;
160     }
161     catch (const wxString& Msg)
162     {
163         POSSIBLY_UNUSED(Msg);
164         bError = true;
165         m_aggression = DefaultAggression;
166     }
167 
168     m_lastMove = 0.0;
169     pConfig->Profile.SetDouble(GetConfigPath() + "/aggression", m_aggression);
170 
171     return bError;
172 }
173 
GetSettingsSummary() const174 wxString GuideAlgorithmHysteresis::GetSettingsSummary() const
175 {
176     // return a loggable summary of current mount settings
177     return wxString::Format("Hysteresis = %.3f, Aggression = %.3f, Minimum move = %.3f\n",
178             GetHysteresis(),
179             GetAggression(),
180             GetMinMove()
181         );
182 }
183 
GetParamNames(wxArrayString & names) const184 void GuideAlgorithmHysteresis::GetParamNames(wxArrayString& names) const
185 {
186     names.push_back("minMove");
187     names.push_back("hysteresis");
188     names.push_back("aggression");
189 }
190 
GetParam(const wxString & name,double * val) const191 bool GuideAlgorithmHysteresis::GetParam(const wxString& name, double *val) const
192 {
193     bool ok = true;
194 
195     if (name == "minMove")
196         *val = GetMinMove();
197     else if (name == "hysteresis")
198         *val = GetHysteresis();
199     else if (name == "aggression")
200         *val = GetAggression();
201     else
202         ok = false;
203 
204     return ok;
205 }
206 
SetParam(const wxString & name,double val)207 bool GuideAlgorithmHysteresis::SetParam(const wxString& name, double val)
208 {
209     bool err;
210 
211     if (name == "minMove")
212         err = SetMinMove(val);
213     else if (name == "hysteresis")
214         err = SetHysteresis(val);
215     else if (name == "aggression")
216         err = SetAggression(val);
217     else
218         err = true;
219 
220     return !err;
221 }
222 
GetConfigDialogPane(wxWindow * pParent)223 ConfigDialogPane *GuideAlgorithmHysteresis::GetConfigDialogPane(wxWindow *pParent)
224 {
225     return new GuideAlgorithmHysteresisConfigDialogPane(pParent, this);
226 }
227 
228 GuideAlgorithmHysteresis::
229 GuideAlgorithmHysteresisConfigDialogPane::
GuideAlgorithmHysteresisConfigDialogPane(wxWindow * pParent,GuideAlgorithmHysteresis * pGuideAlgorithm)230 GuideAlgorithmHysteresisConfigDialogPane(wxWindow *pParent, GuideAlgorithmHysteresis *pGuideAlgorithm)
231     : ConfigDialogPane(_("Hysteresis Guide Algorithm"), pParent)
232 {
233     int width;
234 
235     m_pGuideAlgorithm = pGuideAlgorithm;
236 
237     width = StringWidth(_T("000"));
238     m_pHysteresis = pFrame->MakeSpinCtrl(pParent, wxID_ANY,_T(""), wxDefaultPosition,
239         wxSize(width, -1), wxSP_ARROW_KEYS, 0.0, MaxHysteresis * 100.0, 0.0, _T("Hysteresis"));
240 
241     DoAdd(_("Hysteresis"), m_pHysteresis,
242            wxString::Format(_("How much history of previous guide pulses should be applied\nDefault = %.f%%, increase to smooth out guiding commands"), DefaultHysteresis * 100.0));
243 
244     width = StringWidth(_T("000"));
245     m_pAggression = pFrame->MakeSpinCtrl(pParent, wxID_ANY, _T(" "), wxDefaultPosition,
246         wxSize(width, -1), wxSP_ARROW_KEYS, 0.0, MaxAggression * 100.0, 0.0, _T("Aggressiveness"));
247 
248     DoAdd(_("Aggressiveness"), m_pAggression,
249           wxString::Format(_("What percentage of the computed correction should be applied? Default = %.f%%, adjust if responding too much or too slowly"), DefaultAggression * 100.0));
250 
251     width = StringWidth(_T("00.00"));
252     m_pMinMove = pFrame->MakeSpinCtrlDouble(pParent, wxID_ANY, _T(" "), wxDefaultPosition,
253         wxSize(width, -1), wxSP_ARROW_KEYS, 0.0, 20.0, 0.0, 0.01, _T("MinMove"));
254     m_pMinMove->SetDigits(2);
255 
256     DoAdd(_("Minimum Move (pixels)"), m_pMinMove,
257           wxString::Format(_("How many (fractional) pixels must the star move to trigger a guide pulse? \n"
258           "If camera is binned, this is a fraction of the binned pixel size. Default = %.2f"), DefaultMinMove));
259 }
260 
261 GuideAlgorithmHysteresis::
262 GuideAlgorithmHysteresisConfigDialogPane::
~GuideAlgorithmHysteresisConfigDialogPane(void)263 ~GuideAlgorithmHysteresisConfigDialogPane(void)
264 {
265 }
266 
267 void GuideAlgorithmHysteresis::
268 GuideAlgorithmHysteresisConfigDialogPane::
LoadValues(void)269 LoadValues(void)
270 {
271     m_pHysteresis->SetValue(100.0 * m_pGuideAlgorithm->GetHysteresis());
272     m_pAggression->SetValue(100.0 * m_pGuideAlgorithm->GetAggression());
273     m_pMinMove->SetValue(m_pGuideAlgorithm->GetMinMove());
274 }
275 
276 void GuideAlgorithmHysteresis::
277 GuideAlgorithmHysteresisConfigDialogPane::
UnloadValues(void)278 UnloadValues(void)
279 {
280     m_pGuideAlgorithm->SetHysteresis(m_pHysteresis->GetValue() / 100.0);
281     m_pGuideAlgorithm->SetAggression(m_pAggression->GetValue() / 100.0);
282     m_pGuideAlgorithm->SetMinMove(m_pMinMove->GetValue());
283 }
284 
285 void GuideAlgorithmHysteresis::
OnImageScaleChange()286 GuideAlgorithmHysteresisConfigDialogPane::OnImageScaleChange()
287 {
288     GuideAlgorithm::AdjustMinMoveSpinCtrl(m_pMinMove);
289 }
290 
291 void GuideAlgorithmHysteresis::
EnableDecControls(bool enable)292 GuideAlgorithmHysteresisConfigDialogPane::EnableDecControls(bool enable)
293 {
294     m_pAggression->Enable(enable);
295     m_pMinMove->Enable(enable);
296     m_pHysteresis->Enable(enable);
297 }
298 
GetGraphControlPane(wxWindow * pParent,const wxString & label)299 GraphControlPane *GuideAlgorithmHysteresis::GetGraphControlPane(wxWindow *pParent, const wxString& label)
300 {
301     return new GuideAlgorithmHysteresisGraphControlPane(pParent, this, label);
302 }
303 
304 GuideAlgorithmHysteresis::
305 GuideAlgorithmHysteresisGraphControlPane::
GuideAlgorithmHysteresisGraphControlPane(wxWindow * pParent,GuideAlgorithmHysteresis * pGuideAlgorithm,const wxString & label)306 GuideAlgorithmHysteresisGraphControlPane(wxWindow *pParent, GuideAlgorithmHysteresis *pGuideAlgorithm, const wxString& label)
307     : GraphControlPane(pParent, label)
308 {
309     int width;
310 
311     m_pGuideAlgorithm = pGuideAlgorithm;
312 
313     // Aggression
314     width = StringWidth(_T("000"));
315     m_pAggression = pFrame->MakeSpinCtrl(this, wxID_ANY, _T(""), wxDefaultPosition,
316         wxSize(width, -1), wxSP_ARROW_KEYS | wxALIGN_RIGHT, 0.0, MaxAggression * 100.0, 0.0, _T("Aggressiveness"));
317     m_pAggression->SetToolTip(wxString::Format(_("What percentage of the computed correction should be applied?? Default = %.f%%, adjust if responding too much or too slowly"), DefaultAggression * 100.0));
318     m_pAggression->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &GuideAlgorithmHysteresis::GuideAlgorithmHysteresisGraphControlPane::OnAggressionSpinCtrl, this);
319     DoAdd(m_pAggression, _("Agr"));
320 
321     // Hysteresis
322     width = StringWidth(_T("000"));
323     m_pHysteresis = pFrame->MakeSpinCtrl(this, wxID_ANY, _T(""), wxDefaultPosition,
324         wxSize(width, -1), wxSP_ARROW_KEYS | wxALIGN_RIGHT, 0.0, MaxHysteresis * 100.0, 0.0, _T("Hysteresis"));
325     m_pHysteresis->SetToolTip(wxString::Format(_("How much history of previous guide pulses should be applied\nDefault = %.f%%, increase to smooth out guiding commands"), DefaultHysteresis * 100.0));
326     m_pHysteresis->Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &GuideAlgorithmHysteresis::GuideAlgorithmHysteresisGraphControlPane::OnHysteresisSpinCtrl, this);
327     DoAdd(m_pHysteresis,_("Hys"));
328 
329     // Min move
330     width = StringWidth(_T("00.00"));
331     m_pMinMove = pFrame->MakeSpinCtrlDouble(this, wxID_ANY, _T(""), wxPoint(-1, -1),
332         wxSize(width, -1), wxSP_ARROW_KEYS, 0.0, 20.0, 0.0, 0.01, _T("MinMove"));
333     m_pMinMove->SetDigits(2);
334     m_pMinMove->SetToolTip(wxString::Format(_("How many (fractional) pixels must the star move to trigger a guide pulse? \n"
335         "If camera is binned, this is a fraction of the binned pixel size. Default = %.2f"), DefaultMinMove));
336     m_pMinMove->Bind(wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED, &GuideAlgorithmHysteresis::GuideAlgorithmHysteresisGraphControlPane::OnMinMoveSpinCtrlDouble, this);
337     DoAdd(m_pMinMove,_("MnMo"));
338 
339     m_pHysteresis->SetValue(100.0 * m_pGuideAlgorithm->GetHysteresis());
340     m_pAggression->SetValue(100.0 * m_pGuideAlgorithm->GetAggression());
341     m_pMinMove->SetValue(m_pGuideAlgorithm->GetMinMove());
342 
343     if (TheScope() && pGuideAlgorithm->GetAxis() == "DEC")
344     {
345         DEC_GUIDE_MODE currDecGuideMode = TheScope()->GetDecGuideMode();
346         m_pAggression->Enable(currDecGuideMode != DEC_NONE);
347         m_pMinMove->Enable(currDecGuideMode != DEC_NONE);
348         m_pHysteresis->Enable(currDecGuideMode != DEC_NONE);
349     }
350 }
351 
352 GuideAlgorithmHysteresis::
353 GuideAlgorithmHysteresisGraphControlPane::
~GuideAlgorithmHysteresisGraphControlPane(void)354 ~GuideAlgorithmHysteresisGraphControlPane(void)
355 {
356 }
357 
358 void GuideAlgorithmHysteresis::
EnableDecControls(bool enable)359 GuideAlgorithmHysteresisGraphControlPane::EnableDecControls(bool enable)
360 {
361     m_pAggression->Enable(enable);
362     m_pHysteresis->Enable(enable);
363     m_pMinMove->Enable(enable);
364 }
365 
366 void GuideAlgorithmHysteresis::
367     GuideAlgorithmHysteresisGraphControlPane::
OnAggressionSpinCtrl(wxSpinEvent & WXUNUSED (evt))368     OnAggressionSpinCtrl(wxSpinEvent& WXUNUSED(evt))
369 {
370     m_pGuideAlgorithm->SetAggression(m_pAggression->GetValue() / 100.0);
371     pFrame->NotifyGuidingParam(m_pGuideAlgorithm->GetAxis() + " Hysteresis aggression", m_pAggression->GetValue());
372 }
373 
374 void GuideAlgorithmHysteresis::
375     GuideAlgorithmHysteresisGraphControlPane::
OnHysteresisSpinCtrl(wxSpinEvent & WXUNUSED (evt))376     OnHysteresisSpinCtrl(wxSpinEvent& WXUNUSED(evt))
377 {
378     m_pGuideAlgorithm->SetHysteresis(this->m_pHysteresis->GetValue() / 100.0);
379     pFrame->NotifyGuidingParam(m_pGuideAlgorithm->GetAxis() + " Hysteresis hysteresis", m_pHysteresis->GetValue());
380 }
381 
382 void GuideAlgorithmHysteresis::
383     GuideAlgorithmHysteresisGraphControlPane::
OnMinMoveSpinCtrlDouble(wxSpinDoubleEvent & WXUNUSED (evt))384     OnMinMoveSpinCtrlDouble(wxSpinDoubleEvent& WXUNUSED(evt))
385 {
386     m_pGuideAlgorithm->SetMinMove(m_pMinMove->GetValue());
387     pFrame->NotifyGuidingParam(m_pGuideAlgorithm->GetAxis() + " Hysteresis minimum move", m_pMinMove->GetValue());
388 }
389