1 /*
2  *  stepguider_sxao_indi.cpp
3  *  PHD Guiding
4  *
5  *  Created by Hans Lambermont
6  *  Copyright (c) 2016 Hans Lambermont
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 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 
37 #ifdef STEPGUIDER_SXAO_INDI
38 
39 #include "stepguider_sxao_indi.h"
40 #include "phdindiclient.h"
41 #include "config_indi.h"
42 
43 #include <libindi/basedevice.h>
44 #include <libindi/indiproperty.h>
45 
46 class StepGuiderSxAoINDI : public StepGuider, public PhdIndiClient
47 {
48 private:
49     // INDI parts
50     static const int MaxDeviceInitWaitMilliSeconds = 2000;
51     static const int MaxDevicePropertiesWaitMilliSeconds = 5000;
52     long     INDIport;
53     wxString INDIhost;
54     wxString INDIaoDeviceName;
55     bool     modal;
56     bool     ready;
57     void     ClearStatus();
58     void     CheckState();
59 
60     INumberVectorProperty *pulseGuideNS_prop;
61     INumber               *pulseN_prop;
62     INumber               *pulseS_prop;
63     INumberVectorProperty *pulseGuideWE_prop;
64     INumber               *pulseW_prop;
65     INumber               *pulseE_prop;
66     INumberVectorProperty *aoNS_prop;
67     INumber               *aoN_prop;
68     INumber               *aoS_prop;
69     INumberVectorProperty *aoWE_prop;
70     INumber               *aoW_prop;
71     INumber               *aoE_prop;
72     ISwitchVectorProperty *aoCenterUnjam_prop;
73     ISwitch               *aoCenter_prop;
74     ISwitch               *aoUnjam_prop;
75     INDI::BaseDevice      *ao_device;
76     ITextVectorProperty   *ao_port;
77     ITextVectorProperty   *ao_driverInfo;
78     IText                 *ao_driverName;
79     IText                 *ao_driverExec;
80     IText                 *ao_driverVersion;
81     IText                 *ao_driverInterface;
82     ITextVectorProperty   *ao_info;
83     IText                 *ao_firmware;
84     ILightVectorProperty  *ao_limit;
85     ILight                *ao_limit_north;
86     ILight                *ao_limit_south;
87     ILight                *ao_limit_east;
88     ILight                *ao_limit_west;
89 
90     // StepGuider parts
91     static const int DefaultMaxSteps = 45;
92     wxString m_Name;
93     int m_maxSteps;
94     int SxAoVersion;
95 
96     bool Center() override;
97     STEP_RESULT Step(GUIDE_DIRECTION direction, int steps) override;
98     int MaxPosition(GUIDE_DIRECTION direction) const override;
99     bool SetMaxPosition(int steps) override;
100     bool IsAtLimit(GUIDE_DIRECTION direction, bool *isAtLimit) override;
101 
102     bool FirmwareVersion(int *version);
103 
104     bool    ST4HasGuideOutput() override;
105     bool    ST4HostConnected() override;
106     bool    ST4HasNonGuiMove() override;
107     bool    ST4PulseGuideScope(int direction, int duration) override;
108 
109 protected:
110     // INDI parts
111     void newDevice(INDI::BaseDevice *dp) override;
112 #ifndef INDI_PRE_1_0_0
113     void removeDevice(INDI::BaseDevice *dp) override;
114 #endif
115     void newProperty(INDI::Property *property) override;
removeProperty(INDI::Property * property)116     void removeProperty(INDI::Property *property) override {};
newBLOB(IBLOB * bp)117     void newBLOB(IBLOB *bp) override {};
newSwitch(ISwitchVectorProperty * svp)118     void newSwitch(ISwitchVectorProperty *svp) override {};
119     void newNumber(INumberVectorProperty *nvp) override;
120     void newMessage(INDI::BaseDevice *dp, int messageID) override;
newText(ITextVectorProperty * tvp)121     void newText(ITextVectorProperty *tvp) override {};
newLight(ILightVectorProperty * lvp)122     void newLight(ILightVectorProperty *lvp) override {};
123     void serverConnected() override;
124     void IndiServerDisconnected(int exit_code) override;
125 
126 public:
127     StepGuiderSxAoINDI();
128     ~StepGuiderSxAoINDI();
129 
130     // StepGuider parts
131     bool Connect() override;
132     bool Disconnect() override;
133     bool HasNonGuiMove() override;
134     bool HasSetupDialog() const override;
135     void SetupDialog() override;
136 
137 private:
138     void ShowPropertyDialog() override;
139 };
140 
StepGuiderSxAoINDI()141 StepGuiderSxAoINDI::StepGuiderSxAoINDI()
142 {
143     ClearStatus();
144 
145     // load the values from the current profile
146     INDIhost   = pConfig->Profile.GetString("/indi/INDIhost", _T("localhost"));
147     INDIport   = pConfig->Profile.GetLong("/indi/INDIport", 7624);
148     INDIaoDeviceName = pConfig->Profile.GetString("/indi/INDIao", _T("INDI SXV-AO-LF"));
149 
150     m_Name = INDIaoDeviceName;
151     m_maxSteps = pConfig->Profile.GetInt("/stepguider/sxao/MaxSteps", DefaultMaxSteps);
152 }
153 
~StepGuiderSxAoINDI()154 StepGuiderSxAoINDI::~StepGuiderSxAoINDI()
155 {
156     DisconnectIndiServer();
157 }
158 
ClearStatus()159 void StepGuiderSxAoINDI::ClearStatus()
160 {
161     // reset properties
162     pulseGuideNS_prop = nullptr;
163     pulseN_prop = nullptr;
164     pulseS_prop = nullptr;
165     pulseGuideWE_prop = nullptr;
166     pulseW_prop = nullptr;
167     pulseE_prop = nullptr;
168     aoNS_prop = nullptr;
169     aoN_prop = nullptr;
170     aoS_prop = nullptr;
171     aoWE_prop = nullptr;
172     aoW_prop = nullptr;
173     aoE_prop = nullptr;
174     aoCenterUnjam_prop = nullptr;
175     aoCenter_prop = nullptr;
176     aoUnjam_prop = nullptr;
177     ao_device = nullptr;
178     ao_port = nullptr;
179     ao_driverInfo = nullptr;
180     ao_driverName = nullptr;
181     ao_driverExec = nullptr;
182     ao_driverVersion = nullptr;
183     ao_driverInterface = nullptr;
184     ao_info = nullptr;
185     ao_firmware = nullptr;
186     ao_limit = nullptr;
187     ao_limit_north = nullptr;
188     ao_limit_south = nullptr;
189     ao_limit_east = nullptr;
190     ao_limit_west = nullptr;
191     SxAoVersion = -1;
192 
193     // reset connection status
194     ready = false;
195 }
196 
CheckState()197 void StepGuiderSxAoINDI::CheckState()
198 {
199     // Check if the device has all the required properties for our usage.
200     if (IsConnected() && (ao_driverVersion && aoN_prop && aoS_prop && aoW_prop && aoE_prop && aoCenter_prop)) {
201         if (atof(ao_driverVersion->text) < 1.12) {
202             wxMessageBox(wxString::Format(
203                 _("We need at least INDI driver %s version 1.12 to get the Firmware version and the Limit switch states."),
204                 ao_driverExec->text), _("Error"));
205         } else if (ao_firmware) {
206             if (! ready) {
207                 if (FirmwareVersion(&SxAoVersion)) {
208                     throw ERROR_INFO("StepGuiderSxAoINDI::CheckState: unable to get firmware version");
209                 }
210                 if (SxAoVersion == 0) {
211                     wxMessageBox(wxString::Format(
212                         _("This AO device has firmware version %03u which means it needs to be flashed.\n"
213                           "The SXV-AO Utility v104 or newer, available at http://www.sxccd.com/drivers-downloads,\n"
214                           "contains the firmware."), SxAoVersion),
215                           _("Error"));
216                     throw ERROR_INFO("StepGuiderSxAoINDI::CheckState: V000 means AO device needs a flash");
217                 }
218                 Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::CheckState is ready, firmware %03u", SxAoVersion));
219                 ready = true;
220                 if (modal) {
221                     modal = false;
222                 }
223             }
224         }
225     }
226 }
227 
newDevice(INDI::BaseDevice * dp)228 void StepGuiderSxAoINDI::newDevice(INDI::BaseDevice *dp)
229 {
230     if (strcmp(dp->getDeviceName(), INDIaoDeviceName.mb_str(wxConvUTF8)) == 0) {
231         ao_device = dp;
232     }
233 }
234 
newProperty(INDI::Property * property)235 void StepGuiderSxAoINDI::newProperty(INDI::Property *property)
236 {
237     const char* PropName = property->getName();
238     #ifdef INDI_PRE_1_1_0
239       INDI_TYPE Proptype = property->getType();
240     #else
241       INDI_PROPERTY_TYPE Proptype = property->getType();
242     #endif
243 
244     /*
245     printf("SXAO PropName: %s Proptype: %d\n", PropName, Proptype);
246     fflush(stdout);
247      * 0 INDI_NUMBER, < INumberVectorProperty.
248      * 1 INDI_SWITCH, < ISwitchVectorProperty.
249      * 2 INDI_TEXT,   < ITextVectorProperty.
250      * 3 INDI_LIGHT,  < ILightVectorProperty.
251      * 4 INDI_BLOB,   < IBLOBVectorProperty.
252      * 5 INDI_UNKNOWN
253     SXAO PropName: CONNECTION Proptype: 1
254     SXAO PropName: DRIVER_INFO Proptype: 2
255     SXAO PropName: DEBUG Proptype: 1
256     SXAO PropName: SIMULATION Proptype: 1
257     SXAO PropName: DEVICE_PORT Proptype: 2
258     SXAO PropName: CONFIG_PROCESS Proptype: 1
259     SXAO PropName: TELESCOPE_TIMED_GUIDE_NS Proptype: 0
260     SXAO PropName: TELESCOPE_TIMED_GUIDE_WE Proptype: 0
261     SXAO PropName: AO_NS Proptype: 0
262     SXAO PropName: AO_WE Proptype: 0
263     SXAO PropName: AO_CENTER Proptype: 1
264     SXAO PropName: INFO Proptype: 2
265     SXAO PropName: AT_LIMIT Proptype: 3
266     */
267 
268     if (strcmp(PropName, "CONNECTION") == 0 && Proptype == INDI_SWITCH) {
269         ISwitch *connectswitch = IUFindSwitch(property->getSwitch(),"CONNECT");
270         if (connectswitch->s == ISS_ON) {
271             StepGuider::Connect();
272         }
273     } else if (strcmp(PropName, "DRIVER_INFO") == 0 && Proptype == INDI_TEXT) {
274         ao_driverInfo = property->getText();
275         ao_driverName = IUFindText(ao_driverInfo, "DRIVER_NAME");
276         ao_driverExec = IUFindText(ao_driverInfo, "DRIVER_EXEC");
277         ao_driverVersion = IUFindText(ao_driverInfo, "DRIVER_VERSION"); // we need >=1.12 for Firmware version and AT_LIMIT
278         ao_driverInterface = IUFindText(ao_driverInfo, "DRIVER_INTERFACE");
279     } else if (strcmp(PropName, "DEVICE_PORT") == 0 && Proptype == INDI_TEXT) {
280         ao_port = property->getText();
281     } else if ((strcmp(PropName, "TELESCOPE_TIMED_GUIDE_NS") == 0) && Proptype == INDI_NUMBER){
282         pulseGuideNS_prop = property->getNumber();
283         pulseN_prop = IUFindNumber(pulseGuideNS_prop,"TIMED_GUIDE_N");
284         pulseS_prop = IUFindNumber(pulseGuideNS_prop,"TIMED_GUIDE_S");
285     } else if ((strcmp(PropName, "TELESCOPE_TIMED_GUIDE_WE") == 0) && Proptype == INDI_NUMBER){
286         pulseGuideWE_prop = property->getNumber();
287         pulseW_prop = IUFindNumber(pulseGuideWE_prop,"TIMED_GUIDE_W");
288         pulseE_prop = IUFindNumber(pulseGuideWE_prop,"TIMED_GUIDE_E");
289     } else if ((strcmp(PropName, "AO_NS") == 0) && Proptype == INDI_NUMBER){
290         aoNS_prop = property->getNumber();
291         aoN_prop = IUFindNumber(aoNS_prop,"AO_N");
292         aoS_prop = IUFindNumber(aoNS_prop,"AO_S");
293     } else if ((strcmp(PropName, "AO_WE") == 0) && Proptype == INDI_NUMBER){
294         aoWE_prop = property->getNumber();
295         aoW_prop = IUFindNumber(aoWE_prop,"AO_W");
296         aoE_prop = IUFindNumber(aoWE_prop,"AO_E");
297     } else if (strcmp(PropName, "AO_CENTER") == 0 && Proptype == INDI_SWITCH) {
298         aoCenterUnjam_prop = property->getSwitch();
299         aoCenter_prop = IUFindSwitch(aoCenterUnjam_prop,"CENTER");
300         aoUnjam_prop  = IUFindSwitch(aoCenterUnjam_prop,"UNJAM");
301     } else if (strcmp(PropName, "INFO") == 0 && Proptype == INDI_TEXT) {
302         ao_info = property->getText();
303         ao_firmware = IUFindText(ao_info, "FIRMWARE");
304     } else if (strcmp(PropName, "AT_LIMIT") == 0 && Proptype == INDI_LIGHT) {
305         ao_limit = property->getLight();
306         ao_limit_north = IUFindLight(ao_limit, "AT_LIMIT_N");
307         ao_limit_south = IUFindLight(ao_limit, "AT_LIMIT_S");
308         ao_limit_east  = IUFindLight(ao_limit, "AT_LIMIT_E");
309         ao_limit_west  = IUFindLight(ao_limit, "AT_LIMIT_W");
310     }
311 
312     CheckState();
313 }
314 
newNumber(INumberVectorProperty * nvp)315 void StepGuiderSxAoINDI::newNumber(INumberVectorProperty *nvp) {}
newMessage(INDI::BaseDevice * dp,int messageID)316 void StepGuiderSxAoINDI::newMessage(INDI::BaseDevice *dp, int messageID) {}
317 
318 
Connect()319 bool StepGuiderSxAoINDI::Connect()
320 {
321     if (INDIaoDeviceName == wxT("INDI SXV-AO-LF")) {
322         SetupDialog(); // If not configured open the setup dialog
323     }
324     setServer(INDIhost.mb_str(wxConvUTF8), INDIport); // define server to connect to.
325     watchDevice(INDIaoDeviceName.mb_str(wxConvUTF8)); // Receive messages only for our device.
326     Debug.AddLine(wxString::Format("Connecting to INDI server %s on port %d, device %s", INDIhost.mb_str(wxConvUTF8), INDIport, INDIaoDeviceName.mb_str(wxConvUTF8)));
327     if (connectServer() ) { // Connect to INDI server.
328         return false; // and wait for serverConnected event
329     }
330     return true;
331 }
332 
Disconnect()333 bool StepGuiderSxAoINDI::Disconnect()
334 {
335     Debug.Write("StepGuiderSxAoINDI::Disconnect\n");
336     DisconnectIndiServer();
337     ClearStatus();
338     StepGuider::Disconnect();
339     return false;
340 }
341 
HasSetupDialog() const342 bool StepGuiderSxAoINDI::HasSetupDialog() const
343 {
344     return true;
345 }
346 
SetupDialog()347 void StepGuiderSxAoINDI::SetupDialog()
348 {
349     // show the server and device configuration
350     INDIConfig indiDlg(wxGetApp().GetTopWindow(), _("INDI AO Selection"), INDI_TYPE_AO);
351     indiDlg.INDIhost = INDIhost;
352     indiDlg.INDIport = INDIport;
353     indiDlg.INDIDevName = INDIaoDeviceName;
354     // initialize with actual values
355     indiDlg.SetSettings();
356     // try to connect to server
357     indiDlg.Connect();
358     if (indiDlg.ShowModal() == wxID_OK)
359     {
360         // if OK save the values to the current profile
361         indiDlg.SaveSettings();
362         INDIhost = indiDlg.INDIhost;
363         INDIport = indiDlg.INDIport;
364         INDIaoDeviceName = indiDlg.INDIDevName;
365         pConfig->Profile.SetString("/indi/INDIhost", INDIhost);
366         pConfig->Profile.SetLong("/indi/INDIport", INDIport);
367         pConfig->Profile.SetString("/indi/INDIao", INDIaoDeviceName);
368         m_Name = INDIaoDeviceName;
369     }
370 
371     indiDlg.Disconnect();
372 }
373 
ShowPropertyDialog()374 void StepGuiderSxAoINDI::ShowPropertyDialog()
375 {
376     SetupDialog();
377 }
378 
serverConnected()379 void StepGuiderSxAoINDI::serverConnected()
380 {
381     // wait for the DEVICE_PORT property
382     modal = true;
383     wxLongLong msec;
384     msec = wxGetUTCTimeMillis();
385     while (!ao_port && wxGetUTCTimeMillis() - msec < MaxDeviceInitWaitMilliSeconds)
386     {
387         wxMilliSleep(20);
388         ::wxSafeYield();
389     }
390     // connect to the device
391     connectDevice(INDIaoDeviceName.mb_str(wxConvUTF8));
392 
393     // wait for all defined properties in CheckState
394     msec = wxGetUTCTimeMillis();
395     while (modal && wxGetUTCTimeMillis() - msec < MaxDevicePropertiesWaitMilliSeconds)
396     {
397         wxMilliSleep(20);
398         ::wxSafeYield();
399     }
400     modal = false; // even if CheckState still says no
401 
402     if (ready) {
403         try {
404             Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::serverConnected connecting StepGuider"));
405             StepGuider::Connect();
406         } catch (const wxString& Msg) {
407             POSSIBLY_UNUSED(Msg);
408         }
409     } else {
410         Disconnect();
411     }
412 }
413 
IndiServerDisconnected(int exit_code)414 void StepGuiderSxAoINDI::IndiServerDisconnected(int exit_code)
415 {
416     // after disconnection we reset the connection status and the properties pointers
417     ClearStatus();
418     if (exit_code==-1) {
419        // in case the connection is lost we must reset the client socket
420        Disconnect();
421        Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::serverDisconnected disconnecting StepGuider"));
422        StepGuider::Disconnect();
423     }
424 }
425 
426 #ifndef INDI_PRE_1_0_0
removeDevice(INDI::BaseDevice * dp)427 void StepGuiderSxAoINDI::removeDevice(INDI::BaseDevice *dp)
428 {
429    ClearStatus();
430    Disconnect();
431    StepGuider::Disconnect();
432 }
433 #endif
434 
Step(GUIDE_DIRECTION direction,int steps)435 StepGuider::STEP_RESULT StepGuiderSxAoINDI::Step(GUIDE_DIRECTION direction, int steps)
436 {
437     STEP_RESULT result = STEP_OK;
438 
439     try
440     {
441         if (!aoNS_prop || !aoWE_prop)
442             throw ERROR_INFO("StepGuiderSxAO::step: missing ns or we property");
443 
444         switch (direction) {
445         case NORTH:
446             aoN_prop->value = steps;
447             aoS_prop->value = 0;
448             sendNewNumber(aoNS_prop);
449             break;
450         case SOUTH:
451             aoN_prop->value = 0;
452             aoS_prop->value = steps;
453             sendNewNumber(aoNS_prop);
454             break;
455         case EAST:
456             aoW_prop->value = 0;
457             aoE_prop->value = steps;
458             sendNewNumber(aoWE_prop);
459             break;
460         case WEST:
461             aoW_prop->value = steps;
462             aoE_prop->value = 0;
463             sendNewNumber(aoWE_prop);
464             break;
465         default:
466             throw ERROR_INFO("StepGuiderSxAO::step: invalid direction");
467             break;
468         }
469     }
470     catch (const wxString& Msg)
471     {
472         POSSIBLY_UNUSED(Msg);
473         result = STEP_ERROR;
474     }
475 
476     return result;
477 }
478 
MaxPosition(GUIDE_DIRECTION direction) const479 int StepGuiderSxAoINDI::MaxPosition(GUIDE_DIRECTION direction) const
480 {
481     return m_maxSteps;
482 }
483 
SetMaxPosition(int steps)484 bool StepGuiderSxAoINDI::SetMaxPosition(int steps)
485 {
486     Debug.Write(wxString::Format("StepGuiderSxAoINDI: setting max steps = %d\n", steps));
487     m_maxSteps = steps;
488     pConfig->Profile.SetInt("/stepguider/sxao/MaxSteps", m_maxSteps);
489     return false;
490 }
491 
IsAtLimit(GUIDE_DIRECTION direction,bool * isAtLimit)492 bool StepGuiderSxAoINDI::IsAtLimit(GUIDE_DIRECTION direction, bool *isAtLimit)
493 {
494     bool bError = false;
495     if (ao_limit) {
496         try {
497             switch (direction) {
498                 case NORTH:
499                     *isAtLimit = ao_limit_north->s == IPS_ALERT;
500                     Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::IsAtLimit North"));
501                     break;
502                 case SOUTH:
503                     *isAtLimit = ao_limit_south->s == IPS_ALERT;
504                     Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::IsAtLimit South"));
505                     break;
506                 case EAST:
507                     *isAtLimit = ao_limit_east->s == IPS_ALERT;
508                     Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::IsAtLimit East"));
509                     break;
510                 case WEST:
511                     *isAtLimit = ao_limit_west->s == IPS_ALERT;
512                     Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::IsAtLimit West"));
513                     break;
514                 default:
515                     throw ERROR_INFO("StepGuiderSxAoINDI::IsAtLimit: invalid direction");
516                     break;
517             }
518         } catch (const wxString& Msg) {
519             POSSIBLY_UNUSED(Msg);
520             bError = true;
521         }
522     } else {
523         Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::IsAtLimit called before we received any ao_limit"));
524         bError = true;
525     }
526 
527     return bError;
528 }
529 
FirmwareVersion(int * version)530 bool StepGuiderSxAoINDI::FirmwareVersion(int *version)
531 {
532     bool bError = false;
533     if (ao_firmware) {
534         try {
535             *version = 0;
536             for (int i=0; i<3; i++) {
537                 unsigned char ch = (ao_firmware->text)[1+i];
538                 if (ch < '0' || ch > '9') {
539                     throw ERROR_INFO("StepGuiderSxAO::firmwareVersion: invalid character");
540                 }
541                 *version *= 10;
542                 *version += ch - '0';
543             }
544             Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::Firmwareversion %u", *version));
545         } catch (const wxString& Msg) {
546             *version = -1;
547             POSSIBLY_UNUSED(Msg);
548             bError = true;
549         }
550     } else {
551         bError = true;
552     }
553     return bError;
554 }
555 
Center()556 bool StepGuiderSxAoINDI::Center()
557 {
558     Debug.AddLine(wxString::Format("StepGuiderSxAoINDI::Center"));
559     if (aoCenterUnjam_prop) {
560         aoCenter_prop->s = ISS_ON;
561         aoUnjam_prop->s = ISS_OFF;
562         sendNewSwitch(aoCenterUnjam_prop);
563         // TODO: detect failure of center and try Unjam
564         ZeroCurrentPosition();
565         return false;
566     }
567     return true;
568 }
569 
ST4HasGuideOutput()570 bool StepGuiderSxAoINDI::ST4HasGuideOutput()
571 {
572     return true;
573 }
574 
ST4HostConnected()575 bool StepGuiderSxAoINDI::ST4HostConnected()
576 {
577     return IsConnected();
578 }
579 
ST4HasNonGuiMove()580 bool StepGuiderSxAoINDI::ST4HasNonGuiMove()
581 {
582     return true;
583 }
584 
ST4PulseGuideScope(int direction,int duration)585 bool StepGuiderSxAoINDI::ST4PulseGuideScope(int direction, int duration)
586 {
587     bool bError = true;
588     if (pulseGuideNS_prop && pulseGuideWE_prop) {
589         bError = false;
590         try {
591             switch (direction) {
592             case NORTH:
593                 pulseN_prop->value = duration;
594                 pulseS_prop->value = 0;
595                 sendNewNumber(pulseGuideNS_prop);
596                 break;
597             case SOUTH:
598                 pulseN_prop->value = 0;
599                 pulseS_prop->value = duration;
600                 sendNewNumber(pulseGuideNS_prop);
601                 break;
602             case EAST:
603                 pulseW_prop->value = 0;
604                 pulseE_prop->value = duration;
605                 sendNewNumber(pulseGuideWE_prop);
606                 break;
607             case WEST:
608                 pulseW_prop->value = duration;
609                 pulseE_prop->value = 0;
610                 sendNewNumber(pulseGuideWE_prop);
611                 break;
612             default:
613                 throw ERROR_INFO("StepGuiderSxAO::ST4PulseGuideScope: invalid direction");
614                 break;
615             }
616         } catch (const wxString& Msg) {
617             POSSIBLY_UNUSED(Msg);
618             bError = true;
619         }
620     }
621 
622     return bError;
623 }
624 
HasNonGuiMove()625 bool StepGuiderSxAoINDI::HasNonGuiMove()
626 {
627     return true;
628 }
629 
MakeStepGuiderSxAoIndi()630 StepGuider *StepGuiderSxAoIndiFactory::MakeStepGuiderSxAoIndi()
631 {
632     return new StepGuiderSxAoINDI();
633 }
634 
635 #endif // #ifdef STEPGUIDER_SXAO_INDI
636