Instrument Neutral Distributed Interface INDI  1.9.2
gemini.cpp
Go to the documentation of this file.
1 /*
2  Optec Gemini Focuser Rotator INDI driver
3  Copyright (C) 2017 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #include "gemini.h"
21 
22 #include "indicom.h"
24 
25 #include <cmath>
26 #include <memory>
27 #include <cstring>
28 #include <termios.h>
29 #include <unistd.h>
30 
31 #define GEMINI_MAX_RETRIES 1
32 #define GEMINI_TIMEOUT 3
33 #define GEMINI_MAXBUF 16
34 #define GEMINI_TEMPERATURE_FREQ 20 /* Update every 20 POLLMS cycles. For POLLMS 500ms = 10 seconds freq */
35 #define GEMINI_POSITION_THRESHOLD 5 /* Only send position updates to client if the diff exceeds 5 steps */
36 
37 #define FOCUS_SETTINGS_TAB "Settings"
38 #define STATUS_TAB "Status"
39 #define ROTATOR_TAB "Rotator"
40 #define HUB_TAB "Hub"
41 
42 static std::unique_ptr<Gemini> geminiFR(new Gemini());
43 
44 /************************************************************************************
45  *
46 * ***********************************************************************************/
48 {
49  focusMoveRequest = 0;
50  focuserSimPosition = 0;
51 
52  // Can move in Absolute & Relative motions and can AbortFocuser motion.
54 
55  // Rotator capabilities
57 
58  isFocuserAbsolute = true;
59  isFocuserHoming = false;
60 
61  focuserSimStatus[STATUS_MOVING] = ISS_OFF;
62  focuserSimStatus[STATUS_HOMING] = ISS_OFF;
63  focuserSimStatus[STATUS_HOMED] = ISS_OFF;
64  focuserSimStatus[STATUS_FFDETECT] = ISS_OFF;
65  focuserSimStatus[STATUS_TMPPROBE] = ISS_ON;
66  focuserSimStatus[STATUS_REMOTEIO] = ISS_ON;
67  focuserSimStatus[STATUS_HNDCTRL] = ISS_ON;
68  focuserSimStatus[STATUS_REVERSE] = ISS_OFF;
69 
70  DBG_FOCUS = INDI::Logger::getInstance().addDebugLevel("Verbose", "Verbose");
71 }
72 
73 /************************************************************************************
74  *
75 * ***********************************************************************************/
77 {
78 }
79 
80 /************************************************************************************
81  *
82 * ***********************************************************************************/
84 {
86 
88  // Focuser Properties
90 
91  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
92  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
94 
95  // Enable/Disable temperature compensation
96  IUFillSwitch(&TemperatureCompensateS[0], "Enable", "", ISS_OFF);
97  IUFillSwitch(&TemperatureCompensateS[1], "Disable", "", ISS_ON);
98  IUFillSwitchVector(&TemperatureCompensateSP, TemperatureCompensateS, 2, getDeviceName(), "T. Compensation", "",
100 
101  // Enable/Disable temperature compensation on start
102  IUFillSwitch(&TemperatureCompensateOnStartS[0], "Enable", "", ISS_OFF);
103  IUFillSwitch(&TemperatureCompensateOnStartS[1], "Disable", "", ISS_ON);
104  IUFillSwitchVector(&TemperatureCompensateOnStartSP, TemperatureCompensateOnStartS, 2, getDeviceName(),
105  "T. Compensation @Start", "", FOCUS_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
106 
107  // Temperature Coefficient
108  IUFillNumber(&TemperatureCoeffN[0], "A", "", "%.f", -9999, 9999, 100., 0.);
109  IUFillNumber(&TemperatureCoeffN[1], "B", "", "%.f", -9999, 9999, 100., 0.);
110  IUFillNumber(&TemperatureCoeffN[2], "C", "", "%.f", -9999, 9999, 100., 0.);
111  IUFillNumber(&TemperatureCoeffN[3], "D", "", "%.f", -9999, 9999, 100., 0.);
112  IUFillNumber(&TemperatureCoeffN[4], "E", "", "%.f", -9999, 9999, 100., 0.);
113  IUFillNumberVector(&TemperatureCoeffNP, TemperatureCoeffN, 5, getDeviceName(), "T. Coeff", "", FOCUS_SETTINGS_TAB,
114  IP_RW, 0, IPS_IDLE);
115 
116  // Enable/Disable Home on Start
117  IUFillSwitch(&FocuserHomeOnStartS[0], "Enable", "", ISS_OFF);
118  IUFillSwitch(&FocuserHomeOnStartS[1], "Disable", "", ISS_ON);
119  IUFillSwitchVector(&FocuserHomeOnStartSP, FocuserHomeOnStartS, 2, getDeviceName(), "FOCUSER_HOME_ON_START", "Home on Start",
121 
122  // Enable/Disable temperature Mode
123  IUFillSwitch(&TemperatureCompensateModeS[0], "A", "", ISS_OFF);
124  IUFillSwitch(&TemperatureCompensateModeS[1], "B", "", ISS_OFF);
125  IUFillSwitch(&TemperatureCompensateModeS[2], "C", "", ISS_OFF);
126  IUFillSwitch(&TemperatureCompensateModeS[3], "D", "", ISS_OFF);
127  IUFillSwitch(&TemperatureCompensateModeS[4], "E", "", ISS_OFF);
128  IUFillSwitchVector(&TemperatureCompensateModeSP, TemperatureCompensateModeS, 5, getDeviceName(), "Compensate Mode",
130 
131  // // Enable/Disable backlash
132  // IUFillSwitch(&FocusBacklashS[0], "Enable", "", ISS_OFF);
133  // IUFillSwitch(&FocusBacklashS[1], "Disable", "", ISS_ON);
134  // IUFillSwitchVector(&FocusBacklashSP, FocusBacklashS, 2, getDeviceName(), "FOCUSER_BACKLASH_COMPENSATION", "Backlash Compensation",
135  // FOCUS_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
136 
137  // // Backlash Value
138  // IUFillNumber(&FocusBacklashN[0], "Value", "", "%.f", 0, 99, 5., 0.);
139  // IUFillNumberVector(&FocusBacklashNP, FocusBacklashN, 1, getDeviceName(), "FOCUSER_BACKLASH", "Backlash", FOCUS_SETTINGS_TAB, IP_RW, 0,
140  // IPS_IDLE);
141 
142  // Go to home/center
143  IUFillSwitch(&FocuserGotoS[GOTO_CENTER], "Center", "", ISS_OFF);
144  IUFillSwitch(&FocuserGotoS[GOTO_HOME], "Home", "", ISS_OFF);
145  IUFillSwitchVector(&FocuserGotoSP, FocuserGotoS, 2, getDeviceName(), "FOCUSER_GOTO", "Goto", MAIN_CONTROL_TAB, IP_RW,
146  ISR_1OFMANY, 0,
147  IPS_IDLE);
148 
149  // Focus Status indicators
150  IUFillLight(&FocuserStatusL[STATUS_MOVING], "Is Moving", "", IPS_IDLE);
151  IUFillLight(&FocuserStatusL[STATUS_HOMING], "Is Homing", "", IPS_IDLE);
152  IUFillLight(&FocuserStatusL[STATUS_HOMED], "Is Homed", "", IPS_IDLE);
153  IUFillLight(&FocuserStatusL[STATUS_FFDETECT], "FF Detect", "", IPS_IDLE);
154  IUFillLight(&FocuserStatusL[STATUS_TMPPROBE], "Tmp Probe", "", IPS_IDLE);
155  IUFillLight(&FocuserStatusL[STATUS_REMOTEIO], "Remote IO", "", IPS_IDLE);
156  IUFillLight(&FocuserStatusL[STATUS_HNDCTRL], "Hnd Ctrl", "", IPS_IDLE);
157  IUFillLight(&FocuserStatusL[STATUS_REVERSE], "Reverse", "", IPS_IDLE);
158  IUFillLightVector(&FocuserStatusLP, FocuserStatusL, 8, getDeviceName(), "FOCUSER_STATUS", "Focuser", STATUS_TAB, IPS_IDLE);
159 
161  // Rotator Properties
163 
164  // Enable/Disable Home on Start
165  IUFillSwitch(&RotatorHomeOnStartS[0], "Enable", "", ISS_OFF);
166  IUFillSwitch(&RotatorHomeOnStartS[1], "Disable", "", ISS_ON);
167  IUFillSwitchVector(&RotatorHomeOnStartSP, RotatorHomeOnStartS, 2, getDeviceName(), "ROTATOR_HOME_ON_START", "Home on Start",
169 
170  // Rotator Status indicators
171  IUFillLight(&RotatorStatusL[STATUS_MOVING], "Is Moving", "", IPS_IDLE);
172  IUFillLight(&RotatorStatusL[STATUS_HOMING], "Is Homing", "", IPS_IDLE);
173  IUFillLight(&RotatorStatusL[STATUS_HOMED], "Is Homed", "", IPS_IDLE);
174  IUFillLight(&RotatorStatusL[STATUS_FFDETECT], "FF Detect", "", IPS_IDLE);
175  IUFillLight(&RotatorStatusL[STATUS_TMPPROBE], "Tmp Probe", "", IPS_IDLE);
176  IUFillLight(&RotatorStatusL[STATUS_REMOTEIO], "Remote IO", "", IPS_IDLE);
177  IUFillLight(&RotatorStatusL[STATUS_HNDCTRL], "Hnd Ctrl", "", IPS_IDLE);
178  IUFillLight(&RotatorStatusL[STATUS_REVERSE], "Reverse", "", IPS_IDLE);
179  IUFillLightVector(&RotatorStatusLP, RotatorStatusL, 8, getDeviceName(), "ROTATOR_STATUS", "Rotator", STATUS_TAB, IPS_IDLE);
180 
182 
183  // Rotator Ticks
184  IUFillNumber(&RotatorAbsPosN[0], "ROTATOR_ABSOLUTE_POSITION", "Ticks", "%.f", 0., 0., 0., 0.);
185  IUFillNumberVector(&RotatorAbsPosNP, RotatorAbsPosN, 1, getDeviceName(), "ABS_ROTATOR_POSITION", "Goto", ROTATOR_TAB, IP_RW,
186  0, IPS_IDLE );
187 #if 0
188 
189 
190  // Rotator Degree
191  IUFillNumber(&RotatorAbsAngleN[0], "ANGLE", "Degrees", "%.2f", 0, 360., 10., 0.);
192  IUFillNumberVector(&RotatorAbsAngleNP, RotatorAbsAngleN, 1, getDeviceName(), "ABS_ROTATOR_ANGLE", "Angle", ROTATOR_TAB,
193  IP_RW, 0, IPS_IDLE );
194 
195  // Abort Rotator
196  IUFillSwitch(&AbortRotatorS[0], "ABORT", "Abort", ISS_OFF);
197  IUFillSwitchVector(&AbortRotatorSP, AbortRotatorS, 1, getDeviceName(), "ROTATOR_ABORT_MOTION", "Abort Motion", ROTATOR_TAB,
198  IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
199 
200  // Rotator Go to home/center
201  IUFillSwitch(&RotatorGotoS[GOTO_CENTER], "Center", "", ISS_OFF);
202  IUFillSwitch(&RotatorGotoS[GOTO_HOME], "Home", "", ISS_OFF);
203  IUFillSwitchVector(&RotatorGotoSP, RotatorGotoS, 2, getDeviceName(), "ROTATOR_GOTO", "Goto", ROTATOR_TAB, IP_RW,
204  ISR_1OFMANY, 0,
205  IPS_IDLE);
206 #endif
207 
209  // Hub Properties
211 
212  // Focus name configure in the HUB
213  IUFillText(&HFocusNameT[DEVICE_FOCUSER], "FocusName", "Focuser name", "");
214  IUFillText(&HFocusNameT[DEVICE_ROTATOR], "RotatorName", "Rotator name", "");
215  IUFillTextVector(&HFocusNameTP, HFocusNameT, 2, getDeviceName(), "HUBNAMES", "HUB", HUB_TAB, IP_RW, 0,
216  IPS_IDLE);
217 
218  // Led intensity value
219  IUFillNumber(&LedN[0], "Intensity", "", "%.f", 0, 100, 5., 0.);
220  IUFillNumberVector(&LedNP, LedN, 1, getDeviceName(), "Led", "", HUB_TAB, IP_RW, 0, IPS_IDLE);
221 
222  // Reset to Factory setting
223  IUFillSwitch(&ResetS[0], "Factory", "", ISS_OFF);
224  IUFillSwitchVector(&ResetSP, ResetS, 1, getDeviceName(), "Reset", "", HUB_TAB, IP_RW, ISR_ATMOST1, 0,
225  IPS_IDLE);
226 
227  addAuxControls();
228 
230 
232 
233  return true;
234 }
235 
236 /************************************************************************************
237  *
238 * ***********************************************************************************/
240 {
242 
243  if (isConnected())
244  {
245  // Focuser Properties
246  defineProperty(&TemperatureNP);
247  defineProperty(&TemperatureCoeffNP);
248  defineProperty(&TemperatureCompensateModeSP);
249  defineProperty(&TemperatureCompensateSP);
250  defineProperty(&TemperatureCompensateOnStartSP);
251  // defineProperty(&FocusBacklashSP);
252  // defineProperty(&FocusBacklashNP);
253  defineProperty(&FocuserHomeOnStartSP);
254  defineProperty(&FocuserGotoSP);
255  defineProperty(&FocuserStatusLP);
256 
257  // Rotator Properties
259  /*
260 
261  defineProperty(&RotatorAbsAngleNP);
262  defineProperty(&AbortRotatorSP);
263  defineProperty(&RotatorGotoSP);
264  defineProperty(&ReverseRotatorSP);
265  */
266  defineProperty(&RotatorAbsPosNP);
267  defineProperty(&RotatorHomeOnStartSP);
268  defineProperty(&RotatorStatusLP);
269 
270  // Hub Properties
271  defineProperty(&HFocusNameTP);
272  defineProperty(&ResetSP);
273  defineProperty(&LedNP);
274 
275  if (getFocusConfig() && getRotatorConfig())
276  LOG_INFO("Gemini parameters updated, rotating focuser ready for use.");
277  else
278  {
279  LOG_ERROR("Failed to retrieve rotating focuser configuration settings...");
280  return false;
281  }
282  }
283  else
284  {
285  // Focuser Properties
286  deleteProperty(TemperatureNP.name);
287  deleteProperty(TemperatureCoeffNP.name);
288  deleteProperty(TemperatureCompensateModeSP.name);
289  deleteProperty(TemperatureCompensateSP.name);
290  deleteProperty(TemperatureCompensateOnStartSP.name);
291  // deleteProperty(FocusBacklashSP.name);
292  // deleteProperty(FocusBacklashNP.name);
293  deleteProperty(FocuserGotoSP.name);
294  deleteProperty(FocuserHomeOnStartSP.name);
295  deleteProperty(FocuserStatusLP.name);
296 
297  // Rotator Properties
299  /*
300  deleteProperty(RotatorAbsAngleNP.name);
301  deleteProperty(AbortRotatorSP.name);
302  deleteProperty(RotatorGotoSP.name);
303  deleteProperty(ReverseRotatorSP.name);
304  */
305 
306  deleteProperty(RotatorAbsPosNP.name);
307  deleteProperty(RotatorHomeOnStartSP.name);
308 
309  deleteProperty(RotatorStatusLP.name);
310 
311  // Hub Properties
312  deleteProperty(HFocusNameTP.name);
313  deleteProperty(LedNP.name);
314  deleteProperty(ResetSP.name);
315  }
316 
317  return true;
318 }
319 
320 /************************************************************************************
321  *
322 * ***********************************************************************************/
324 {
325  if (ack())
326  {
327  LOG_INFO("Gemini is online. Getting focus parameters...");
328  return true;
329  }
330 
331  LOG_INFO("Error retrieving data from Gemini, please ensure Gemini controller is "
332  "powered and the port is correct.");
333  return false;
334 }
335 
336 /************************************************************************************
337  *
338 * ***********************************************************************************/
340 {
341  // Has to be overide by child instance
342  return "Gemini Focusing Rotator";
343 }
344 
345 /************************************************************************************
346  *
347 * ***********************************************************************************/
348 bool Gemini::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
349 {
350  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
351  {
352  // Temperature Compensation
353  if (strcmp(TemperatureCompensateSP.name, name) == 0)
354  {
355  int prevIndex = IUFindOnSwitchIndex(&TemperatureCompensateSP);
356  IUUpdateSwitch(&TemperatureCompensateSP, states, names, n);
357  if (setTemperatureCompensation(TemperatureCompensateS[0].s == ISS_ON))
358  {
359  TemperatureCompensateSP.s = IPS_OK;
360  }
361  else
362  {
363  IUResetSwitch(&TemperatureCompensateSP);
364  TemperatureCompensateSP.s = IPS_ALERT;
365  TemperatureCompensateS[prevIndex].s = ISS_ON;
366  }
367 
368  IDSetSwitch(&TemperatureCompensateSP, nullptr);
369  return true;
370  }
371 
372  // Temperature Compensation on Start
373  if (!strcmp(TemperatureCompensateOnStartSP.name, name))
374  {
375  int prevIndex = IUFindOnSwitchIndex(&TemperatureCompensateOnStartSP);
376  IUUpdateSwitch(&TemperatureCompensateOnStartSP, states, names, n);
377  if (setTemperatureCompensationOnStart(TemperatureCompensateOnStartS[0].s == ISS_ON))
378  {
379  TemperatureCompensateOnStartSP.s = IPS_OK;
380  }
381  else
382  {
383  IUResetSwitch(&TemperatureCompensateOnStartSP);
384  TemperatureCompensateOnStartSP.s = IPS_ALERT;
385  TemperatureCompensateOnStartS[prevIndex].s = ISS_ON;
386  }
387 
388  IDSetSwitch(&TemperatureCompensateOnStartSP, nullptr);
389  return true;
390  }
391 
392  // Temperature Compensation Mode
393  if (!strcmp(TemperatureCompensateModeSP.name, name))
394  {
395  int prevIndex = IUFindOnSwitchIndex(&TemperatureCompensateModeSP);
396  IUUpdateSwitch(&TemperatureCompensateModeSP, states, names, n);
397  char mode = IUFindOnSwitchIndex(&TemperatureCompensateModeSP) + 'A';
398  if (setTemperatureCompensationMode(mode))
399  {
400  TemperatureCompensateModeSP.s = IPS_OK;
401  }
402  else
403  {
404  IUResetSwitch(&TemperatureCompensateModeSP);
405  TemperatureCompensateModeSP.s = IPS_ALERT;
406  TemperatureCompensateModeS[prevIndex].s = ISS_ON;
407  }
408 
409  IDSetSwitch(&TemperatureCompensateModeSP, nullptr);
410  return true;
411  }
412 
413  // Focuser Home on Start Enable/Disable
414  if (!strcmp(FocuserHomeOnStartSP.name, name))
415  {
416  int prevIndex = IUFindOnSwitchIndex(&FocuserHomeOnStartSP);
417  IUUpdateSwitch(&FocuserHomeOnStartSP, states, names, n);
418  if (homeOnStart(DEVICE_FOCUSER, FocuserHomeOnStartS[0].s == ISS_ON))
419  {
420  FocuserHomeOnStartSP.s = IPS_OK;
421  }
422  else
423  {
424  IUResetSwitch(&FocuserHomeOnStartSP);
425  FocuserHomeOnStartSP.s = IPS_ALERT;
426  FocuserHomeOnStartS[prevIndex].s = ISS_ON;
427  }
428 
429  IDSetSwitch(&FocuserHomeOnStartSP, nullptr);
430  return true;
431  }
432 
433  // Rotator Home on Start Enable/Disable
434  if (!strcmp(RotatorHomeOnStartSP.name, name))
435  {
436  int prevIndex = IUFindOnSwitchIndex(&RotatorHomeOnStartSP);
437  IUUpdateSwitch(&RotatorHomeOnStartSP, states, names, n);
438  if (homeOnStart(DEVICE_ROTATOR, RotatorHomeOnStartS[0].s == ISS_ON))
439  {
440  RotatorHomeOnStartSP.s = IPS_OK;
441  }
442  else
443  {
444  IUResetSwitch(&RotatorHomeOnStartSP);
445  RotatorHomeOnStartSP.s = IPS_ALERT;
446  RotatorHomeOnStartS[prevIndex].s = ISS_ON;
447  }
448 
449  IDSetSwitch(&RotatorHomeOnStartSP, nullptr);
450  return true;
451  }
452 
453  // Focuser Backlash enable/disable
454  // if (!strcmp(FocusBacklashSP.name, name))
455  // {
456  // int prevIndex = IUFindOnSwitchIndex(&FocusBacklashSP);
457  // IUUpdateSwitch(&FocusBacklashSP, states, names, n);
458  // if (setBacklashCompensation(DEVICE_FOCUSER, FocusBacklashS[0].s == ISS_ON))
459  // {
460  // FocusBacklashSP.s = IPS_OK;
461  // }
462  // else
463  // {
464  // IUResetSwitch(&FocusBacklashSP);
465  // FocusBacklashSP.s = IPS_ALERT;
466  // FocusBacklashS[prevIndex].s = ISS_ON;
467  // }
468 
469  // IDSetSwitch(&FocusBacklashSP, nullptr);
470  // return true;
471  // }
472 
473  // Reset to Factory setting
474  if (strcmp(ResetSP.name, name) == 0)
475  {
476  IUResetSwitch(&ResetSP);
477  if (resetFactory())
478  ResetSP.s = IPS_OK;
479  else
480  ResetSP.s = IPS_ALERT;
481 
482  IDSetSwitch(&ResetSP, nullptr);
483  return true;
484  }
485 
486  // Focser Go to home/center
487  if (!strcmp(FocuserGotoSP.name, name))
488  {
489  IUUpdateSwitch(&FocuserGotoSP, states, names, n);
490 
491  if (FocuserGotoS[GOTO_HOME].s == ISS_ON)
492  {
493  if (home(DEVICE_FOCUSER))
494  {
495  FocuserGotoSP.s = IPS_BUSY;
497  IDSetNumber(&FocusAbsPosNP, nullptr);
498  isFocuserHoming = true;
499  LOG_INFO("Focuser moving to home position...");
500  }
501  else
502  FocuserGotoSP.s = IPS_ALERT;
503  }
504  else
505  {
506  if (center(DEVICE_FOCUSER))
507  {
508  FocuserGotoSP.s = IPS_BUSY;
509  LOG_INFO("Focuser moving to center position...");
511  IDSetNumber(&FocusAbsPosNP, nullptr);
512  }
513  else
514  FocuserGotoSP.s = IPS_ALERT;
515  }
516 
517  IDSetSwitch(&FocuserGotoSP, nullptr);
518  return true;
519  }
520 
521  // Process all rotator properties
522  if (strstr(name, "ROTATOR"))
523  {
524  if (INDI::RotatorInterface::processSwitch(dev, name, states, names, n))
525  return true;
526  }
527 
528  // Rotator Go to home/center
529 #if 0
530  if (!strcmp(RotatorGotoSP.name, name))
531  {
532  IUUpdateSwitch(&RotatorGotoSP, states, names, n);
533 
534  if (RotatorGotoS[GOTO_HOME].s == ISS_ON)
535  {
536  if (home(DEVICE_ROTATOR))
537  {
538  RotatorGotoSP.s = IPS_BUSY;
539  RotatorAbsPosNP.s = IPS_BUSY;
540  IDSetNumber(&RotatorAbsPosNP, nullptr);
541  isRotatorHoming = true;
542  LOG_INFO("Rotator moving to home position...");
543  }
544  else
545  RotatorGotoSP.s = IPS_ALERT;
546  }
547  else
548  {
549  if (center(DEVICE_ROTATOR))
550  {
551  RotatorGotoSP.s = IPS_BUSY;
552  LOG_INFO("Rotator moving to center position...");
553  RotatorAbsPosNP.s = IPS_BUSY;
554  IDSetNumber(&RotatorAbsPosNP, nullptr);
555  }
556  else
557  RotatorGotoSP.s = IPS_ALERT;
558  }
559 
560  IDSetSwitch(&RotatorGotoSP, nullptr);
561  return true;
562  }
563 
564  // Reverse Direction
565  if (!strcmp(ReverseRotatorSP.name, name))
566  {
567  IUUpdateSwitch(&ReverseRotatorSP, states, names, n);
568 
569  if (reverseRotator(ReverseRotatorS[0].s == ISS_ON))
571  else
573 
574  IDSetSwitch(&ReverseRotatorSP, nullptr);
575  return true;
576  }
577 
578  // Halt Rotator
579  if (!strcmp(AbortRotatorSP.name, name))
580  {
581  if (halt(DEVICE_ROTATOR))
582  {
583  RotatorAbsPosNP.s = RotatorAbsAngleNP.s = RotatorGotoSP.s = IPS_IDLE;
584  IDSetNumber(&RotatorAbsPosNP, nullptr);
585  IDSetNumber(&RotatorAbsAngleNP, nullptr);
586  IUResetSwitch(&RotatorGotoSP);
587  IDSetSwitch(&RotatorGotoSP, nullptr);
588 
590  }
591  else
593 
594  IDSetSwitch(&AbortRotatorSP, nullptr);
595  return true;
596  }
597 #endif
598  }
599 
600  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
601 }
602 
603 /************************************************************************************
604  *
605 * ***********************************************************************************/
606 bool Gemini::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
607 {
608  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
609  {
610  // Set device nickname to the HUB itself
611  if (!strcmp(name, HFocusNameTP.name))
612  {
613  IUUpdateText(&HFocusNameTP, texts, names, n);
614  if (setNickname(DEVICE_FOCUSER, HFocusNameT[DEVICE_FOCUSER].text)
615  && setNickname(DEVICE_ROTATOR, HFocusNameT[DEVICE_ROTATOR].text))
616  HFocusNameTP.s = IPS_OK;
617  else
618  HFocusNameTP.s = IPS_ALERT;
619  IDSetText(&HFocusNameTP, nullptr);
620  return true;
621  }
622  }
623  return INDI::Focuser::ISNewText(dev, name, texts, names, n);
624 }
625 
626 /************************************************************************************
627  *
628 * ***********************************************************************************/
629 bool Gemini::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
630 {
631  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
632  {
633  // Temperature Coefficient
634  if (!strcmp(TemperatureCoeffNP.name, name))
635  {
636  IUUpdateNumber(&TemperatureCoeffNP, values, names, n);
637  for (int i = 0; i < n; i++)
638  {
639  if (setTemperatureCompensationCoeff('A' + i, TemperatureCoeffN[i].value) == false)
640  {
641  LOG_ERROR("Failed to set temperature coefficients.");
642  TemperatureCoeffNP.s = IPS_ALERT;
643  IDSetNumber(&TemperatureCoeffNP, nullptr);
644  return false;
645  }
646  }
647 
648  TemperatureCoeffNP.s = IPS_OK;
649  IDSetNumber(&TemperatureCoeffNP, nullptr);
650  return true;
651  }
652 
653  // Focuser Backlash Value
654  // if (!strcmp(FocusBacklashNP.name, name))
655  // {
656  // IUUpdateNumber(&FocusBacklashNP, values, names, n);
657  // if (setBacklashCompensationSteps(DEVICE_FOCUSER, FocusBacklashN[0].value) == false)
658  // {
659  // LOG_ERROR("Failed to set focuser backlash value.");
660  // FocusBacklashNP.s = IPS_ALERT;
661  // IDSetNumber(&FocusBacklashNP, nullptr);
662  // return false;
663  // }
664 
665  // FocusBacklashNP.s = IPS_OK;
666  // IDSetNumber(&FocusBacklashNP, nullptr);
667  // return true;
668  // }
669 
670  // Rotator Backlash Value
671  if (!strcmp(RotatorBacklashNP.name, name))
672  {
673  IUUpdateNumber(&RotatorBacklashNP, values, names, n);
674  if (setBacklashCompensationSteps(DEVICE_ROTATOR, RotatorBacklashN[0].value) == false)
675  {
676  LOG_ERROR("Failed to set rotator backlash value.");
678  IDSetNumber(&RotatorBacklashNP, nullptr);
679  return false;
680  }
681 
683  IDSetNumber(&RotatorBacklashNP, nullptr);
684  return true;
685  }
686 
687  // Set LED intensity to the HUB itself via function setLedLevel()
688  if (!strcmp(LedNP.name, name))
689  {
690  IUUpdateNumber(&LedNP, values, names, n);
691  if (setLedLevel(LedN[0].value))
692  LedNP.s = IPS_OK;
693  else
694  LedNP.s = IPS_ALERT;
695  LOGF_INFO("Focuser LED level intensity : %f", LedN[0].value);
696  IDSetNumber(&LedNP, nullptr);
697  return true;
698  }
699 
700  // Set Rotator Absolute Steps
701  if (!strcmp(RotatorAbsPosNP.name, name))
702  {
703  IUUpdateNumber(&RotatorAbsPosNP, values, names, n);
704  RotatorAbsPosNP.s = MoveAbsRotatorTicks(static_cast<uint32_t>(RotatorAbsPosN[0].value));
705  IDSetNumber(&RotatorAbsPosNP, nullptr);
706  return true;
707  }
708 
709  if (strstr(name, "ROTATOR"))
710  {
711  if (INDI::RotatorInterface::processNumber(dev, name, values, names, n))
712  return true;
713  }
714 
715 #if 0
716  // Set Rotator Absolute Steps
717  if (!strcmp(RotatorAbsPosNP.name, name))
718  {
719  IUUpdateNumber(&RotatorAbsPosNP, values, names, n);
720  RotatorAbsPosNP.s = MoveAbsRotatorTicks(static_cast<uint32_t>(RotatorAbsPosN[0].value));
721  IDSetNumber(&RotatorAbsPosNP, nullptr);
722  return true;
723  }
724 
725  // Set Rotator Absolute Angle
726  if (!strcmp(RotatorAbsAngleNP.name, name))
727  {
728  IUUpdateNumber(&RotatorAbsAngleNP, values, names, n);
729  RotatorAbsAngleNP.s = MoveAbsRotatorAngle(RotatorAbsAngleN[0].value);
730  IDSetNumber(&RotatorAbsAngleNP, nullptr);
731  return true;
732  }
733 #endif
734 
735  }
736 
737  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
738 }
739 
740 /************************************************************************************
741  *
742 * ***********************************************************************************/
744 {
745  const char *cmd = "<F100GETDNN>";
746  int errcode = 0;
747  char errmsg[MAXRBUF];
748  char response[16];
749  int nbytes_read = 0;
750  int nbytes_written = 0;
751 
752  memset(response, 0, sizeof(response));
753 
754  LOGF_DEBUG("CMD (%s)", cmd);
755 
756  if (isSimulation())
757  {
758  strncpy(response, "Castor", 16);
759  nbytes_read = strlen(response) + 1;
760  }
761  else
762  {
763  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
764  {
765  tty_error_msg(errcode, errmsg, MAXRBUF);
766  LOGF_ERROR("%s", errmsg);
767  return false;
768  }
769 
770  if (isResponseOK() == false)
771  return false;
772 
773  if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
774  {
775  tty_error_msg(errcode, errmsg, MAXRBUF);
776  LOGF_ERROR("%s", errmsg);
777  return false;
778  }
779  }
780 
781  if (nbytes_read > 0)
782  {
783  response[nbytes_read - 1] = '\0';
784  LOGF_DEBUG("RES (%s)", response);
785  LOGF_INFO("%s is detected.", response);
786 
787  // Read 'END'
788  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
789 
790  tcflush(PortFD, TCIFLUSH);
791 
792  return true;
793  }
794 
795  tcflush(PortFD, TCIFLUSH);
796  return false;
797 }
798 
799 /************************************************************************************
800  *
801 * ***********************************************************************************/
802 bool Gemini::getFocusConfig()
803 {
804  const char *cmd = "<F100GETCFG>";
805  int errcode = 0;
806  char errmsg[MAXRBUF];
807  char response[64];
808  int nbytes_read = 0;
809  int nbytes_written = 0;
810  char key[16];
811 
812  memset(response, 0, sizeof(response));
813 
814  LOGF_DEBUG("CMD (%s)", cmd);
815 
816  if (isSimulation())
817  {
818  strncpy(response, "!00", sizeof(response));
819  nbytes_read = strlen(response) + 1;
820  }
821  else
822  {
823  tcflush(PortFD, TCIFLUSH);
824 
825  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
826  {
827  tty_error_msg(errcode, errmsg, MAXRBUF);
828  LOGF_ERROR("%s", errmsg);
829  return false;
830  }
831 
832  if (isResponseOK() == false)
833  return false;
834 
835  /*if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
836  {
837  tty_error_msg(errcode, errmsg, MAXRBUF);
838  LOGF_ERROR("%s", errmsg);
839  return false;
840  }*/
841  }
842 
843  /*if (nbytes_read > 0)
844  {
845  response[nbytes_read - 1] = '\0';
846  LOGF_DEBUG("RES (%s)", response);
847 
848  if ((strcmp(response, "CONFIG1")) && (strcmp(response, "CONFIG2")))
849  return false;
850  }*/
851 
852  memset(response, 0, sizeof(response));
853 
854  // Nickname
855  if (isSimulation())
856  {
857  strncpy(response, "NickName=Tommy\n", sizeof(response));
858  nbytes_read = strlen(response);
859  }
860  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
861  {
862  tty_error_msg(errcode, errmsg, MAXRBUF);
863  LOGF_ERROR("%s", errmsg);
864  return false;
865  }
866  response[nbytes_read - 1] = '\0';
867  LOGF_DEBUG("RES (%s)", response);
868 
869  char nickname[16];
870  int rc = sscanf(response, "%16[^=]=%16[^\n]s", key, nickname);
871 
872  if (rc != 2)
873  return false;
874 
875  IUSaveText(&HFocusNameT[0], nickname);
876  IDSetText(&HFocusNameTP, nullptr);
877 
878  HFocusNameTP.s = IPS_OK;
879  IDSetText(&HFocusNameTP, nullptr);
880 
881  memset(response, 0, sizeof(response));
882 
883  // Get Max Position
884  if (isSimulation())
885  {
886  snprintf(response, sizeof(response), "Max Pos = %06d\n", 100000);
887  nbytes_read = strlen(response);
888  }
889  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
890  {
891  tty_error_msg(errcode, errmsg, MAXRBUF);
892  LOGF_ERROR("%s", errmsg);
893  return false;
894  }
895  response[nbytes_read - 1] = '\0';
896  LOGF_DEBUG("RES (%s)", response);
897 
898  uint32_t maxPos = 0;
899  rc = sscanf(response, "%16[^=]=%d", key, &maxPos);
900  if (rc == 2)
901  {
902  FocusAbsPosN[0].max = maxPos;
903  FocusAbsPosN[0].step = maxPos / 50.0;
904  FocusAbsPosN[0].min = 0;
905 
906  FocusRelPosN[0].max = maxPos / 2;
907  FocusRelPosN[0].step = maxPos / 100.0;
908  FocusRelPosN[0].min = 0;
909 
912 
913  maxControllerTicks = maxPos;
914  }
915  else
916  return false;
917 
918  memset(response, 0, sizeof(response));
919 
920  // Get Device Type
921  if (isSimulation())
922  {
923  strncpy(response, "Dev Typ = A\n", sizeof(response));
924  nbytes_read = strlen(response);
925  }
926  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
927  {
928  tty_error_msg(errcode, errmsg, MAXRBUF);
929  LOGF_ERROR("%s", errmsg);
930  return false;
931  }
932  response[nbytes_read - 1] = '\0';
933  LOGF_DEBUG("RES (%s)", response);
934 
935  // Get Status Parameters
936  memset(response, 0, sizeof(response));
937 
938  // Temperature Compensation On?
939  if (isSimulation())
940  {
941  snprintf(response, sizeof(response), "TComp ON = %d\n", TemperatureCompensateS[0].s == ISS_ON ? 1 : 0);
942  nbytes_read = strlen(response);
943  }
944  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
945  {
946  tty_error_msg(errcode, errmsg, MAXRBUF);
947  LOGF_ERROR("%s", errmsg);
948  return false;
949  }
950  response[nbytes_read - 1] = '\0';
951  LOGF_DEBUG("RES (%s)", response);
952 
953  int TCompOn;
954  rc = sscanf(response, "%16[^=]=%d", key, &TCompOn);
955  if (rc != 2)
956  return false;
957 
958  IUResetSwitch(&TemperatureCompensateSP);
959  TemperatureCompensateS[0].s = TCompOn ? ISS_ON : ISS_OFF;
960  TemperatureCompensateS[0].s = TCompOn ? ISS_OFF : ISS_ON;
961  TemperatureCompensateSP.s = IPS_OK;
962  IDSetSwitch(&TemperatureCompensateSP, nullptr);
963 
964  memset(response, 0, sizeof(response));
965 
966  // Temperature Coeff A
967  if (isSimulation())
968  {
969  snprintf(response, sizeof(response), "TempCo A = %d\n", (int)TemperatureCoeffN[FOCUS_A_COEFF].value);
970  nbytes_read = strlen(response);
971  }
972  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
973  {
974  tty_error_msg(errcode, errmsg, MAXRBUF);
975  LOGF_ERROR("%s", errmsg);
976  return false;
977  }
978  response[nbytes_read - 1] = '\0';
979  LOGF_DEBUG("RES (%s)", response);
980 
981  int TCoeffA;
982  rc = sscanf(response, "%16[^=]=%d", key, &TCoeffA);
983  if (rc != 2)
984  return false;
985 
986  TemperatureCoeffN[FOCUS_A_COEFF].value = TCoeffA;
987 
988  memset(response, 0, sizeof(response));
989 
990  // Temperature Coeff B
991  if (isSimulation())
992  {
993  snprintf(response, sizeof(response), "TempCo B = %d\n", (int)TemperatureCoeffN[FOCUS_B_COEFF].value);
994  nbytes_read = strlen(response);
995  }
996  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
997  {
998  tty_error_msg(errcode, errmsg, MAXRBUF);
999  LOGF_ERROR("%s", errmsg);
1000  return false;
1001  }
1002  response[nbytes_read - 1] = '\0';
1003  LOGF_DEBUG("RES (%s)", response);
1004 
1005  int TCoeffB;
1006  rc = sscanf(response, "%16[^=]=%d", key, &TCoeffB);
1007  if (rc != 2)
1008  return false;
1009 
1010  TemperatureCoeffN[FOCUS_B_COEFF].value = TCoeffB;
1011 
1012  memset(response, 0, sizeof(response));
1013 
1014  // Temperature Coeff C
1015  if (isSimulation())
1016  {
1017  snprintf(response, sizeof(response), "TempCo C = %d\n", (int)TemperatureCoeffN[FOCUS_C_COEFF].value);
1018  nbytes_read = strlen(response);
1019  }
1020  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1021  {
1022  tty_error_msg(errcode, errmsg, MAXRBUF);
1023  LOGF_ERROR("%s", errmsg);
1024  return false;
1025  }
1026  response[nbytes_read - 1] = '\0';
1027  LOGF_DEBUG("RES (%s)", response);
1028 
1029  int TCoeffC;
1030  rc = sscanf(response, "%16[^=]=%d", key, &TCoeffC);
1031  if (rc != 2)
1032  return false;
1033 
1034  TemperatureCoeffN[FOCUS_C_COEFF].value = TCoeffC;
1035 
1036  memset(response, 0, sizeof(response));
1037 
1038  // Temperature Coeff D
1039  if (isSimulation())
1040  {
1041  snprintf(response, sizeof(response), "TempCo D = %d\n", (int)TemperatureCoeffN[FOCUS_D_COEFF].value);
1042  nbytes_read = strlen(response);
1043  }
1044  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1045  {
1046  tty_error_msg(errcode, errmsg, MAXRBUF);
1047  LOGF_ERROR("%s", errmsg);
1048  return false;
1049  }
1050  response[nbytes_read - 1] = '\0';
1051  LOGF_DEBUG("RES (%s)", response);
1052 
1053  int TCoeffD;
1054  rc = sscanf(response, "%16[^=]=%d", key, &TCoeffD);
1055  if (rc != 2)
1056  return false;
1057 
1058  TemperatureCoeffN[FOCUS_D_COEFF].value = TCoeffD;
1059 
1060  memset(response, 0, sizeof(response));
1061 
1062  // Temperature Coeff E
1063  if (isSimulation())
1064  {
1065  snprintf(response, sizeof(response), "TempCo E = %d\n", (int)TemperatureCoeffN[FOCUS_E_COEFF].value);
1066  nbytes_read = strlen(response);
1067  }
1068  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1069  {
1070  tty_error_msg(errcode, errmsg, MAXRBUF);
1071  LOGF_ERROR("%s", errmsg);
1072  return false;
1073  }
1074  response[nbytes_read - 1] = '\0';
1075  LOGF_DEBUG("RES (%s)", response);
1076 
1077  int TCoeffE;
1078  rc = sscanf(response, "%16[^=]=%d", key, &TCoeffE);
1079  if (rc != 2)
1080  return false;
1081 
1082  TemperatureCoeffN[FOCUS_E_COEFF].value = TCoeffE;
1083 
1084  TemperatureCoeffNP.s = IPS_OK;
1085  IDSetNumber(&TemperatureCoeffNP, nullptr);
1086 
1087  memset(response, 0, sizeof(response));
1088 
1089  // Temperature Compensation Mode
1090  if (isSimulation())
1091  {
1092  snprintf(response, sizeof(response), "TC Mode = %c\n", 'C');
1093  nbytes_read = strlen(response);
1094  }
1095  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1096  {
1097  tty_error_msg(errcode, errmsg, MAXRBUF);
1098  LOGF_ERROR("%s", errmsg);
1099  return false;
1100  }
1101  response[nbytes_read - 1] = '\0';
1102  LOGF_DEBUG("RES (%s)", response);
1103 
1104  char compensateMode;
1105  rc = sscanf(response, "%16[^=]= %c", key, &compensateMode);
1106  if (rc != 2)
1107  return false;
1108 
1109  IUResetSwitch(&TemperatureCompensateModeSP);
1110  int index = compensateMode - 'A';
1111  if (index >= 0 && index <= 5)
1112  {
1113  TemperatureCompensateModeS[index].s = ISS_ON;
1114  TemperatureCompensateModeSP.s = IPS_OK;
1115  }
1116  else
1117  {
1118  LOGF_ERROR("Invalid index %d for compensation mode.", index);
1119  TemperatureCompensateModeSP.s = IPS_ALERT;
1120  }
1121 
1122  IDSetSwitch(&TemperatureCompensateModeSP, nullptr);
1123 
1124  // Backlash Compensation
1125  memset(response, 0, sizeof(response));
1126  if (isSimulation())
1127  {
1128  snprintf(response, sizeof(response), "BLC En = %d\n", FocusBacklashS[INDI_ENABLED].s == ISS_ON ? 1 : 0);
1129  nbytes_read = strlen(response);
1130  }
1131  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1132  {
1133  tty_error_msg(errcode, errmsg, MAXRBUF);
1134  LOGF_ERROR("%s", errmsg);
1135  return false;
1136  }
1137  response[nbytes_read - 1] = '\0';
1138  LOGF_DEBUG("RES (%s)", response);
1139 
1140  int BLCCompensate;
1141  rc = sscanf(response, "%16[^=]=%d", key, &BLCCompensate);
1142  if (rc != 2)
1143  return false;
1144 
1146  FocusBacklashS[INDI_ENABLED].s = BLCCompensate ? ISS_ON : ISS_OFF;
1147  FocusBacklashS[INDI_DISABLED].s = BLCCompensate ? ISS_OFF : ISS_ON;
1149  IDSetSwitch(&FocusBacklashSP, nullptr);
1150 
1151  // Backlash Value
1152  memset(response, 0, sizeof(response));
1153  if (isSimulation())
1154  {
1155  snprintf(response, sizeof(response), "BLC Stps = %d\n", 50);
1156  nbytes_read = strlen(response);
1157  }
1158  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1159  {
1160  tty_error_msg(errcode, errmsg, MAXRBUF);
1161  LOGF_ERROR("%s", errmsg);
1162  return false;
1163  }
1164  response[nbytes_read - 1] = '\0';
1165  LOGF_DEBUG("RES (%s)", response);
1166 
1167  int BLCValue;
1168  rc = sscanf(response, "%16[^=]=%d", key, &BLCValue);
1169  if (rc != 2)
1170  return false;
1171 
1172  FocusBacklashN[0].value = BLCValue;
1174  IDSetNumber(&FocusBacklashNP, nullptr);
1175 
1176  // Temperature Compensation on Start
1177  memset(response, 0, sizeof(response));
1178  if (isSimulation())
1179  {
1180  snprintf(response, sizeof(response), "TC Start = %d\n", TemperatureCompensateOnStartS[0].s == ISS_ON ? 1 : 0);
1181  nbytes_read = strlen(response);
1182  }
1183  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1184  {
1185  tty_error_msg(errcode, errmsg, MAXRBUF);
1186  LOGF_ERROR("%s", errmsg);
1187  return false;
1188  }
1189  response[nbytes_read - 1] = '\0';
1190  LOGF_DEBUG("RES (%s)", response);
1191 
1192  int TCOnStart;
1193  rc = sscanf(response, "%16[^=]=%d", key, &TCOnStart);
1194  if (rc != 2)
1195  return false;
1196 
1197  IUResetSwitch(&TemperatureCompensateOnStartSP);
1198  TemperatureCompensateOnStartS[0].s = TCOnStart ? ISS_ON : ISS_OFF;
1199  TemperatureCompensateOnStartS[1].s = TCOnStart ? ISS_OFF : ISS_ON;
1200  TemperatureCompensateOnStartSP.s = IPS_OK;
1201  IDSetSwitch(&TemperatureCompensateOnStartSP, nullptr);
1202 
1203  // Get Status Parameters
1204  memset(response, 0, sizeof(response));
1205 
1206  // Home on start on?
1207  if (isSimulation())
1208  {
1209  snprintf(response, sizeof(response), "HOnStart = %d\n", FocuserHomeOnStartS[0].s == ISS_ON ? 1 : 0);
1210  nbytes_read = strlen(response);
1211  }
1212  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1213  {
1214  tty_error_msg(errcode, errmsg, MAXRBUF);
1215  LOGF_ERROR("%s", errmsg);
1216  return false;
1217  }
1218  response[nbytes_read - 1] = '\0';
1219  LOGF_DEBUG("RES (%s)", response);
1220 
1221  int StartOnHome;
1222  rc = sscanf(response, "%16[^=]=%d", key, &StartOnHome);
1223  if (rc != 2)
1224  return false;
1225 
1226  IUResetSwitch(&FocuserHomeOnStartSP);
1227  FocuserHomeOnStartS[0].s = StartOnHome ? ISS_ON : ISS_OFF;
1228  FocuserHomeOnStartS[1].s = StartOnHome ? ISS_OFF : ISS_ON;
1229  FocuserHomeOnStartSP.s = IPS_OK;
1230  IDSetSwitch(&FocuserHomeOnStartSP, nullptr);
1231 
1232  // Added By Philippe Besson the 28th of June for 'END' evalution
1233  // END is reached
1234  memset(response, 0, sizeof(response));
1235  if (isSimulation())
1236  {
1237  strncpy(response, "END\n", 16);
1238  nbytes_read = strlen(response);
1239  }
1240  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1241  {
1242  tty_error_msg(errcode, errmsg, MAXRBUF);
1243  LOGF_ERROR("%s", errmsg);
1244  return false;
1245  }
1246 
1247  if (nbytes_read > 0)
1248  {
1249  response[nbytes_read - 1] = '\0';
1250 
1251  // Display the response to be sure to have read the complet TTY Buffer.
1252  LOGF_DEBUG("RES (%s)", response);
1253 
1254  if (strcmp(response, "END"))
1255  return false;
1256  }
1257  // End of added code by Philippe Besson
1258 
1259  tcflush(PortFD, TCIFLUSH);
1260 
1262 
1263  return true;
1264 }
1265 
1266 /************************************************************************************
1267  *
1268 * ***********************************************************************************/
1269 bool Gemini::getRotatorStatus()
1270 {
1271  const char *cmd = "<R100GETSTA>";
1272  int errcode = 0;
1273  char errmsg[MAXRBUF];
1274  char response[32];
1275  int nbytes_read = 0;
1276  int nbytes_written = 0;
1277  char key[16];
1278 
1279  memset(response, 0, sizeof(response));
1280 
1281  LOGF_DEBUG("CMD (%s)", cmd);
1282 
1283  if (isSimulation())
1284  {
1285  strncpy(response, "!00", 16);
1286  nbytes_read = strlen(response) + 1;
1287  }
1288  else
1289  {
1290  tcflush(PortFD, TCIFLUSH);
1291 
1292  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1293  {
1294  tty_error_msg(errcode, errmsg, MAXRBUF);
1295  LOGF_ERROR("%s", errmsg);
1296  return false;
1297  }
1298 
1299  if (isResponseOK() == false)
1300  return false;
1301 
1302  /*if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1303  {
1304  tty_error_msg(errcode, errmsg, MAXRBUF);
1305  LOGF_ERROR("%s", errmsg);
1306  return false;
1307  }*/
1308  }
1309 
1311  // #1 Get Current Position
1313  memset(response, 0, sizeof(response));
1314  if (isSimulation())
1315  {
1316  snprintf(response, 32, "CurrStep = %06d\n", rotatorSimPosition);
1317  nbytes_read = strlen(response);
1318  }
1319  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1320  {
1321  tty_error_msg(errcode, errmsg, MAXRBUF);
1322  LOGF_ERROR("%s", errmsg);
1323  return false;
1324  }
1325  response[nbytes_read - 1] = '\0';
1326  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1327 
1328  int currPos = 0;
1329  int rc = sscanf(response, "%16[^=]=%d", key, &currPos);
1330  if (rc == 2)
1331  {
1332  // Do not spam unless there is an actual change
1333  if (RotatorAbsPosN[0].value != currPos)
1334  {
1335  RotatorAbsPosN[0].value = currPos;
1336  IDSetNumber(&RotatorAbsPosNP, nullptr);
1337  }
1338  }
1339  else
1340  return false;
1341 
1343  // #2 Get Target Position
1345  memset(response, 0, sizeof(response));
1346  if (isSimulation())
1347  {
1348  snprintf(response, 32, "TargStep = %06d\n", targetFocuserPosition);
1349  nbytes_read = strlen(response);
1350  }
1351  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1352  {
1353  tty_error_msg(errcode, errmsg, MAXRBUF);
1354  LOGF_ERROR("%s", errmsg);
1355  return false;
1356  }
1357  response[nbytes_read - 1] = '\0';
1358  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1359 
1361  // #3 Get Current PA
1363  memset(response, 0, sizeof(response));
1364  if (isSimulation())
1365  {
1366  snprintf(response, 32, "CurenPA = %06d\n", rotatorSimPA);
1367  nbytes_read = strlen(response);
1368  }
1369  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1370  {
1371  tty_error_msg(errcode, errmsg, MAXRBUF);
1372  LOGF_ERROR("%s", errmsg);
1373  return false;
1374  }
1375  response[nbytes_read - 1] = '\0';
1376  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1377 
1378  int currPA = 0;
1379  rc = sscanf(response, "%16[^=]=%d", key, &currPA);
1380  if (rc == 2)
1381  {
1382  // Only send when above a threshold
1383  double diffPA = fabs(GotoRotatorN[0].value - currPA / 1000.0);
1384  if (diffPA >= 0.01)
1385  {
1386  GotoRotatorN[0].value = currPA / 1000.0;
1387  IDSetNumber(&GotoRotatorNP, nullptr);
1388  }
1389  }
1390  else
1391  return false;
1392 
1394  // #3 Get Target PA
1396  memset(response, 0, sizeof(response));
1397  if (isSimulation())
1398  {
1399  snprintf(response, 32, "TargetPA = %06d\n", targetFocuserPosition);
1400  nbytes_read = strlen(response);
1401  }
1402  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1403  {
1404  tty_error_msg(errcode, errmsg, MAXRBUF);
1405  LOGF_ERROR("%s", errmsg);
1406  return false;
1407  }
1408  response[nbytes_read - 1] = '\0';
1409  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1410 
1411  // Get Status Parameters
1412 
1414  // #5 is Moving?
1416  memset(response, 0, sizeof(response));
1417  if (isSimulation())
1418  {
1419  snprintf(response, 32, "IsMoving = %d\n", (rotatorSimStatus[STATUS_MOVING] == ISS_ON) ? 1 : 0);
1420  nbytes_read = strlen(response);
1421  }
1422  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1423  {
1424  tty_error_msg(errcode, errmsg, MAXRBUF);
1425  LOGF_ERROR("%s", errmsg);
1426  return false;
1427  }
1428  response[nbytes_read - 1] = '\0';
1429  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1430 
1431  int isMoving;
1432  rc = sscanf(response, "%16[^=]=%d", key, &isMoving);
1433  if (rc != 2)
1434  return false;
1435 
1436  RotatorStatusL[STATUS_MOVING].s = isMoving ? IPS_BUSY : IPS_IDLE;
1437 
1439  // #6 is Homing?
1441  memset(response, 0, sizeof(response));
1442  if (isSimulation())
1443  {
1444  snprintf(response, 32, "IsHoming = %d\n", (rotatorSimStatus[STATUS_HOMING] == ISS_ON) ? 1 : 0);
1445  nbytes_read = strlen(response);
1446  }
1447  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1448  {
1449  tty_error_msg(errcode, errmsg, MAXRBUF);
1450  LOGF_ERROR("%s", errmsg);
1451  return false;
1452  }
1453  response[nbytes_read - 1] = '\0';
1454  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1455 
1456  int _isHoming;
1457  rc = sscanf(response, "%16[^=]=%d", key, &_isHoming);
1458  if (rc != 2)
1459  return false;
1460 
1461  RotatorStatusL[STATUS_HOMING].s = _isHoming ? IPS_BUSY : IPS_IDLE;
1462 
1463  // We set that isHoming in process, but we don't set it to false here it must be reset in TimerHit
1464  if (RotatorStatusL[STATUS_HOMING].s == IPS_BUSY)
1465  isRotatorHoming = true;
1466 
1468  // #6 is Homed?
1470  memset(response, 0, sizeof(response));
1471  if (isSimulation())
1472  {
1473  snprintf(response, 32, "IsHomed = %d\n", (rotatorSimStatus[STATUS_HOMED] == ISS_ON) ? 1 : 0);
1474  nbytes_read = strlen(response);
1475  }
1476  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1477  {
1478  tty_error_msg(errcode, errmsg, MAXRBUF);
1479  LOGF_ERROR("%s", errmsg);
1480  return false;
1481  }
1482  response[nbytes_read - 1] = '\0';
1483  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1484 
1485  int isHomed;
1486  rc = sscanf(response, "%16[^=]=%d", key, &isHomed);
1487  if (rc != 2)
1488  return false;
1489 
1490  RotatorStatusL[STATUS_HOMED].s = isHomed ? IPS_OK : IPS_IDLE;
1491  IDSetLight(&RotatorStatusLP, nullptr);
1492 
1493  // Added By Philippe Besson the 28th of June for 'END' evalution
1494  // END is reached
1495  memset(response, 0, sizeof(response));
1496  if (isSimulation())
1497  {
1498  strncpy(response, "END\n", 16);
1499  nbytes_read = strlen(response);
1500  }
1501  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1502  {
1503  tty_error_msg(errcode, errmsg, MAXRBUF);
1504  LOGF_ERROR("%s", errmsg);
1505  return false;
1506  }
1507 
1508  if (nbytes_read > 0)
1509  {
1510  response[nbytes_read - 1] = '\0';
1511 
1512  // Display the response to be sure to have read the complet TTY Buffer.
1513  LOGF_DEBUG("RES (%s)", response);
1514 
1515  if (strcmp(response, "END"))
1516  {
1517  LOG_WARN("Invalid END response.");
1518  return false;
1519  }
1520  }
1521  // End of added code by Philippe Besson
1522 
1523  tcflush(PortFD, TCIFLUSH);
1524 
1525  return true;
1526 
1527 }
1528 
1529 /************************************************************************************
1530  *
1531 * ***********************************************************************************/
1532 bool Gemini::getRotatorConfig()
1533 {
1534  const char *cmd = "<R100GETCFG>";
1535  int errcode = 0;
1536  char errmsg[MAXRBUF];
1537  char response[64];
1538  int nbytes_read = 0;
1539  int nbytes_written = 0;
1540  char key[16];
1541 
1542  memset(response, 0, sizeof(response));
1543 
1544  LOGF_DEBUG("CMD (%s)", cmd);
1545 
1546  if (isSimulation())
1547  {
1548  strncpy(response, "!00", sizeof(response));
1549  nbytes_read = strlen(response) + 1;
1550  }
1551  else
1552  {
1553  tcflush(PortFD, TCIFLUSH);
1554 
1555  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1556  {
1557  tty_error_msg(errcode, errmsg, MAXRBUF);
1558  LOGF_ERROR("%s", errmsg);
1559  return false;
1560  }
1561 
1562  if (isResponseOK() == false)
1563  return false;
1564 
1565  /*if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1566  {
1567  tty_error_msg(errcode, errmsg, MAXRBUF);
1568  LOGF_ERROR("%s", errmsg);
1569  return false;
1570  }*/
1571  }
1572 
1573  /*if (nbytes_read > 0)
1574  {
1575  response[nbytes_read - 1] = '\0';
1576  LOGF_DEBUG("RES (%s)", response);
1577 
1578  if ((strcmp(response, "CONFIG1")) && (strcmp(response, "CONFIG2")))
1579  return false;
1580  }*/
1581 
1582  memset(response, 0, sizeof(response));
1584  // Nickname
1586  if (isSimulation())
1587  {
1588  strncpy(response, "NickName=Juli\n", sizeof(response));
1589  nbytes_read = strlen(response);
1590  }
1591  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1592  {
1593  tty_error_msg(errcode, errmsg, MAXRBUF);
1594  LOGF_ERROR("%s", errmsg);
1595  return false;
1596  }
1597  response[nbytes_read - 1] = '\0';
1598  LOGF_DEBUG("RES (%s)", response);
1599 
1600  char nickname[16];
1601  int rc = sscanf(response, "%16[^=]=%16[^\n]s", key, nickname);
1602 
1603  if (rc != 2)
1604  return false;
1605 
1606  IUSaveText(&HFocusNameT[DEVICE_ROTATOR], nickname);
1607  HFocusNameTP.s = IPS_OK;
1608  IDSetText(&HFocusNameTP, nullptr);
1609 
1610  memset(response, 0, sizeof(response));
1611 
1613  // Get Max steps
1615  if (isSimulation())
1616  {
1617  snprintf(response, sizeof(response), "MaxSteps = %06d\n", 100000);
1618  nbytes_read = strlen(response);
1619  }
1620  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1621  {
1622  tty_error_msg(errcode, errmsg, MAXRBUF);
1623  LOGF_ERROR("%s", errmsg);
1624  return false;
1625  }
1626  response[nbytes_read - 1] = '\0';
1627  LOGF_DEBUG("RES (%s)", response);
1628 
1629  uint32_t maxPos = 0;
1630  rc = sscanf(response, "%16[^=]=%d", key, &maxPos);
1631  if (rc == 2)
1632  {
1633  RotatorAbsPosN[0].min = 0;
1634  RotatorAbsPosN[0].max = maxPos;
1635  RotatorAbsPosN[0].step = maxPos / 50.0;
1636  IUUpdateMinMax(&RotatorAbsPosNP);
1637  }
1638  else
1639  return false;
1640 
1641  memset(response, 0, sizeof(response));
1642 
1644  // Get Device Type
1646  if (isSimulation())
1647  {
1648  strncpy(response, "Dev Type = B\n", sizeof(response));
1649  nbytes_read = strlen(response);
1650  }
1651  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1652  {
1653  tty_error_msg(errcode, errmsg, MAXRBUF);
1654  LOGF_ERROR("%s", errmsg);
1655  return false;
1656  }
1657  response[nbytes_read - 1] = '\0';
1658  LOGF_DEBUG("RES (%s)", response);
1659 
1660  // Get Status Parameters
1661  memset(response, 0, sizeof(response));
1662 
1664  // Backlash Compensation
1666  memset(response, 0, sizeof(response));
1667  if (isSimulation())
1668  {
1669  snprintf(response, sizeof(response), "BLCSteps = %d\n", RotatorBacklashS[INDI_ENABLED].s == ISS_ON ? 1 : 0);
1670  nbytes_read = strlen(response);
1671  }
1672  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1673  {
1674  tty_error_msg(errcode, errmsg, MAXRBUF);
1675  LOGF_ERROR("%s", errmsg);
1676  return false;
1677  }
1678  response[nbytes_read - 1] = '\0';
1679  LOGF_DEBUG("RES (%s)", response);
1680 
1681  int BLCCompensate;
1682  rc = sscanf(response, "%16[^=]=%d", key, &BLCCompensate);
1683  if (rc != 2)
1684  return false;
1685 
1687  RotatorBacklashS[INDI_ENABLED].s = BLCCompensate ? ISS_ON : ISS_OFF;
1688  RotatorBacklashS[INDI_DISABLED].s = BLCCompensate ? ISS_OFF : ISS_ON;
1690  IDSetSwitch(&RotatorBacklashSP, nullptr);
1691 
1693  // Backlash Value
1695  memset(response, 0, sizeof(response));
1696  if (isSimulation())
1697  {
1698  snprintf(response, sizeof(response), "BLCSteps = %d\n", 50);
1699  nbytes_read = strlen(response);
1700  }
1701  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1702  {
1703  tty_error_msg(errcode, errmsg, MAXRBUF);
1704  LOGF_ERROR("%s", errmsg);
1705  return false;
1706  }
1707  response[nbytes_read - 1] = '\0';
1708  LOGF_DEBUG("RES (%s)", response);
1709 
1710  int BLCValue;
1711  rc = sscanf(response, "%16[^=]=%d", key, &BLCValue);
1712  if (rc != 2)
1713  return false;
1714 
1715  RotatorBacklashN[0].value = BLCValue;
1717  IDSetNumber(&RotatorBacklashNP, nullptr);
1718 
1720  // Home on start on?
1722  memset(response, 0, sizeof(response));
1723  if (isSimulation())
1724  {
1725  snprintf(response, sizeof(response), "HOnStart = %d\n", RotatorHomeOnStartS[0].s == ISS_ON ? 1 : 0);
1726  nbytes_read = strlen(response);
1727  }
1728  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1729  {
1730  tty_error_msg(errcode, errmsg, MAXRBUF);
1731  LOGF_ERROR("%s", errmsg);
1732  return false;
1733  }
1734  response[nbytes_read - 1] = '\0';
1735  LOGF_DEBUG("RES (%s)", response);
1736 
1737  int StartOnHome;
1738  rc = sscanf(response, "%16[^=]=%d", key, &StartOnHome);
1739  if (rc != 2)
1740  return false;
1741 
1742  IUResetSwitch(&RotatorHomeOnStartSP);
1743  RotatorHomeOnStartS[0].s = StartOnHome ? ISS_ON : ISS_OFF;
1744  RotatorHomeOnStartS[1].s = StartOnHome ? ISS_OFF : ISS_ON;
1745  RotatorHomeOnStartSP.s = IPS_OK;
1746  IDSetSwitch(&RotatorHomeOnStartSP, nullptr);
1747 
1749  // Reverse?
1751  memset(response, 0, sizeof(response));
1752  if (isSimulation())
1753  {
1754  snprintf(response, 32, "Reverse = %d\n", (rotatorSimStatus[STATUS_REVERSE] == ISS_ON) ? 1 : 0);
1755  nbytes_read = strlen(response);
1756  }
1757  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1758  {
1759  tty_error_msg(errcode, errmsg, MAXRBUF);
1760  LOGF_ERROR("%s", errmsg);
1761  return false;
1762  }
1763  response[nbytes_read - 1] = '\0';
1764  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1765 
1766  int reverse;
1767  rc = sscanf(response, "%16[^=]=%d", key, &reverse);
1768  if (rc != 2)
1769  return false;
1770 
1771  RotatorStatusL[STATUS_REVERSE].s = reverse ? IPS_OK : IPS_IDLE;
1772 
1773  // If reverse is enable and switch shows disabled, let's change that
1774  // same thing is reverse is disabled but switch is enabled
1775  if ((reverse && ReverseRotatorS[1].s == ISS_ON) || (!reverse && ReverseRotatorS[0].s == ISS_ON))
1776  {
1778  ReverseRotatorS[0].s = (reverse == 1) ? ISS_ON : ISS_OFF;
1779  ReverseRotatorS[1].s = (reverse == 0) ? ISS_ON : ISS_OFF;
1780  IDSetSwitch(&ReverseRotatorSP, nullptr);
1781  }
1782 
1783  RotatorStatusLP.s = IPS_OK;
1784  IDSetLight(&RotatorStatusLP, nullptr);
1785 
1787  // Max Speed - Not used
1789  memset(response, 0, sizeof(response));
1790  if (isSimulation())
1791  {
1792  snprintf(response, 32, "MaxSpeed = %d\n", 800);
1793  nbytes_read = strlen(response);
1794  }
1795  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1796  {
1797  tty_error_msg(errcode, errmsg, MAXRBUF);
1798  LOGF_ERROR("%s", errmsg);
1799  return false;
1800  }
1801  response[nbytes_read - 1] = '\0';
1802  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1803 
1804  // Added By Philippe Besson the 28th of June for 'END' evalution
1805  // END is reached
1806  memset(response, 0, sizeof(response));
1807  if (isSimulation())
1808  {
1809  strncpy(response, "END\n", 16);
1810  nbytes_read = strlen(response);
1811  }
1812  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1813  {
1814  tty_error_msg(errcode, errmsg, MAXRBUF);
1815  LOGF_ERROR("%s", errmsg);
1816  return false;
1817  }
1818 
1819  if (nbytes_read > 0)
1820  {
1821  response[nbytes_read - 1] = '\0';
1822 
1823  // Display the response to be sure to have read the complet TTY Buffer.
1824  LOGF_DEBUG("RES (%s)", response);
1825 
1826  if (strcmp(response, "END"))
1827  return false;
1828  }
1829  // End of added code by Philippe Besson
1830 
1831  tcflush(PortFD, TCIFLUSH);
1832 
1834 
1835  return true;
1836 }
1837 
1838 /************************************************************************************
1839  *
1840 * ***********************************************************************************/
1841 bool Gemini::getFocusStatus()
1842 {
1843  const char *cmd = "<F100GETSTA>";
1844  int errcode = 0;
1845  char errmsg[MAXRBUF];
1846  char response[32];
1847  int nbytes_read = 0;
1848  int nbytes_written = 0;
1849  char key[16];
1850 
1851  memset(response, 0, sizeof(response));
1852 
1853  LOGF_DEBUG("CMD (%s)", cmd);
1854 
1855  if (isSimulation())
1856  {
1857  strncpy(response, "!00", 16);
1858  nbytes_read = strlen(response) + 1;
1859  }
1860  else
1861  {
1862  tcflush(PortFD, TCIFLUSH);
1863 
1864  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1865  {
1866  tty_error_msg(errcode, errmsg, MAXRBUF);
1867  LOGF_ERROR("%s", errmsg);
1868  return false;
1869  }
1870 
1871  if (isResponseOK() == false)
1872  return false;
1873 
1874  /*if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1875  {
1876  tty_error_msg(errcode, errmsg, MAXRBUF);
1877  LOGF_ERROR("%s", errmsg);
1878  return false;
1879  }*/
1880  }
1881 
1882  // Get Temperature
1883  memset(response, 0, sizeof(response));
1884  if (isSimulation())
1885  {
1886  //strncpy(response, "CurrTemp = +21.7\n", 16); // #PS: incorrect, lost last character
1887  strcpy(response, "CurrTemp = +21.7\n");
1888  nbytes_read = strlen(response);
1889  }
1890  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1891  {
1892  tty_error_msg(errcode, errmsg, MAXRBUF);
1893  LOGF_ERROR("%s", errmsg);
1894  return false;
1895  }
1896 
1897  if (nbytes_read > 0)
1898  response[nbytes_read - 1] = '\0';
1899 
1900  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1901 
1902  float temperature = 0;
1903  int rc = sscanf(response, "%16[^=]=%f", key, &temperature);
1904  if (rc == 2)
1905  {
1906  TemperatureN[0].value = temperature;
1907  IDSetNumber(&TemperatureNP, nullptr);
1908  }
1909  else
1910  {
1911  char np[8];
1912  int rc = sscanf(response, "%16[^=]= %s", key, np);
1913 
1914  if (rc != 2 || strcmp(np, "NP"))
1915  {
1916  if (TemperatureNP.s != IPS_ALERT)
1917  {
1918  TemperatureNP.s = IPS_ALERT;
1919  IDSetNumber(&TemperatureNP, nullptr);
1920  }
1921  return false;
1922  }
1923  }
1924 
1926  // #1 Get Current Position
1928  memset(response, 0, sizeof(response));
1929  if (isSimulation())
1930  {
1931  snprintf(response, 32, "CurrStep = %06d\n", focuserSimPosition);
1932  nbytes_read = strlen(response);
1933  }
1934  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1935  {
1936  tty_error_msg(errcode, errmsg, MAXRBUF);
1937  LOGF_ERROR("%s", errmsg);
1938  return false;
1939  }
1940  response[nbytes_read - 1] = '\0';
1941  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1942 
1943  uint32_t currPos = 0;
1944  rc = sscanf(response, "%16[^=]=%d", key, &currPos);
1945  if (rc == 2)
1946  {
1947  FocusAbsPosN[0].value = currPos;
1948  IDSetNumber(&FocusAbsPosNP, nullptr);
1949  }
1950  else
1951  return false;
1952 
1954  // #2 Get Target Position
1956  memset(response, 0, sizeof(response));
1957  if (isSimulation())
1958  {
1959  snprintf(response, 32, "TargStep = %06d\n", targetFocuserPosition);
1960  nbytes_read = strlen(response);
1961  }
1962  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1963  {
1964  tty_error_msg(errcode, errmsg, MAXRBUF);
1965  LOGF_ERROR("%s", errmsg);
1966  return false;
1967  }
1968  response[nbytes_read - 1] = '\0';
1969  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1970 
1971  // Get Status Parameters
1972 
1974  // #3 is Moving?
1976  memset(response, 0, sizeof(response));
1977  if (isSimulation())
1978  {
1979  snprintf(response, 32, "IsMoving = %d\n", (focuserSimStatus[STATUS_MOVING] == ISS_ON) ? 1 : 0);
1980  nbytes_read = strlen(response);
1981  }
1982  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1983  {
1984  tty_error_msg(errcode, errmsg, MAXRBUF);
1985  LOGF_ERROR("%s", errmsg);
1986  return false;
1987  }
1988  response[nbytes_read - 1] = '\0';
1989  DEBUGF(DBG_FOCUS, "RES (%s)", response);
1990 
1991  int isMoving;
1992  rc = sscanf(response, "%16[^=]=%d", key, &isMoving);
1993  if (rc != 2)
1994  return false;
1995 
1996  FocuserStatusL[STATUS_MOVING].s = isMoving ? IPS_BUSY : IPS_IDLE;
1997 
1999  // #4 is Homing?
2001  memset(response, 0, sizeof(response));
2002  if (isSimulation())
2003  {
2004  snprintf(response, 32, "IsHoming = %d\n", (focuserSimStatus[STATUS_HOMING] == ISS_ON) ? 1 : 0);
2005  nbytes_read = strlen(response);
2006  }
2007  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2008  {
2009  tty_error_msg(errcode, errmsg, MAXRBUF);
2010  LOGF_ERROR("%s", errmsg);
2011  return false;
2012  }
2013  response[nbytes_read - 1] = '\0';
2014  DEBUGF(DBG_FOCUS, "RES (%s)", response);
2015 
2016  int _isHoming;
2017  rc = sscanf(response, "%16[^=]=%d", key, &_isHoming);
2018  if (rc != 2)
2019  return false;
2020 
2021  FocuserStatusL[STATUS_HOMING].s = _isHoming ? IPS_BUSY : IPS_IDLE;
2022  // For relative focusers home is not applicable.
2023  if (isFocuserAbsolute == false)
2024  FocuserStatusL[STATUS_HOMING].s = IPS_IDLE;
2025 
2026  // We set that isHoming in process, but we don't set it to false here it must be reset in TimerHit
2027  if (FocuserStatusL[STATUS_HOMING].s == IPS_BUSY)
2028  isFocuserHoming = true;
2029 
2031  // #6 is Homed?
2033  memset(response, 0, sizeof(response));
2034  if (isSimulation())
2035  {
2036  snprintf(response, 32, "IsHomed = %d\n", (focuserSimStatus[STATUS_HOMED] == ISS_ON) ? 1 : 0);
2037  nbytes_read = strlen(response);
2038  }
2039  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2040  {
2041  tty_error_msg(errcode, errmsg, MAXRBUF);
2042  LOGF_ERROR("%s", errmsg);
2043  return false;
2044  }
2045  response[nbytes_read - 1] = '\0';
2046  DEBUGF(DBG_FOCUS, "RES (%s)", response);
2047 
2048  int isHomed;
2049  rc = sscanf(response, "%16[^=]=%d", key, &isHomed);
2050  if (rc != 2)
2051  return false;
2052 
2053  FocuserStatusL[STATUS_HOMED].s = isHomed ? IPS_OK : IPS_IDLE;
2054  // For relative focusers home is not applicable.
2055  if (isFocuserAbsolute == false)
2056  FocuserStatusL[STATUS_HOMED].s = IPS_IDLE;
2057 
2059  // #7 Temperature probe?
2061  memset(response, 0, sizeof(response));
2062  if (isSimulation())
2063  {
2064  snprintf(response, 32, "TempProb = %d\n", (focuserSimStatus[STATUS_TMPPROBE] == ISS_ON) ? 1 : 0);
2065  nbytes_read = strlen(response);
2066  }
2067  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2068  {
2069  tty_error_msg(errcode, errmsg, MAXRBUF);
2070  LOGF_ERROR("%s", errmsg);
2071  return false;
2072  }
2073  response[nbytes_read - 1] = '\0';
2074  DEBUGF(DBG_FOCUS, "RES (%s)", response);
2075 
2076  int TmpProbe;
2077  rc = sscanf(response, "%16[^=]=%d", key, &TmpProbe);
2078  if (rc != 2)
2079  return false;
2080 
2081  FocuserStatusL[STATUS_TMPPROBE].s = TmpProbe ? IPS_OK : IPS_IDLE;
2082 
2084  // #8 Remote IO?
2086  memset(response, 0, sizeof(response));
2087  if (isSimulation())
2088  {
2089  snprintf(response, 32, "RemoteIO = %d\n", (focuserSimStatus[STATUS_REMOTEIO] == ISS_ON) ? 1 : 0);
2090  nbytes_read = strlen(response);
2091  }
2092  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2093  {
2094  tty_error_msg(errcode, errmsg, MAXRBUF);
2095  LOGF_ERROR("%s", errmsg);
2096  return false;
2097  }
2098  response[nbytes_read - 1] = '\0';
2099  DEBUGF(DBG_FOCUS, "RES (%s)", response);
2100 
2101  int RemoteIO;
2102  rc = sscanf(response, "%16[^=]=%d", key, &RemoteIO);
2103  if (rc != 2)
2104  return false;
2105 
2106  FocuserStatusL[STATUS_REMOTEIO].s = RemoteIO ? IPS_OK : IPS_IDLE;
2107 
2109  // #9 Hand controller?
2111  memset(response, 0, sizeof(response));
2112  if (isSimulation())
2113  {
2114  snprintf(response, 32, "HCStatus = %d\n", (focuserSimStatus[STATUS_HNDCTRL] == ISS_ON) ? 1 : 0);
2115  nbytes_read = strlen(response);
2116  }
2117  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2118  {
2119  tty_error_msg(errcode, errmsg, MAXRBUF);
2120  LOGF_ERROR("%s", errmsg);
2121  return false;
2122  }
2123  response[nbytes_read - 1] = '\0';
2124  DEBUGF(DBG_FOCUS, "RES (%s)", response);
2125 
2126  int HndCtlr;
2127  rc = sscanf(response, "%16[^=]=%d", key, &HndCtlr);
2128  if (rc != 2)
2129  return false;
2130 
2131  FocuserStatusL[STATUS_HNDCTRL].s = HndCtlr ? IPS_OK : IPS_IDLE;
2132 
2133  FocuserStatusLP.s = IPS_OK;
2134  IDSetLight(&FocuserStatusLP, nullptr);
2135 
2136  // Added By Philippe Besson the 28th of June for 'END' evalution
2137  // END is reached
2138  memset(response, 0, sizeof(response));
2139  if (isSimulation())
2140  {
2141  strncpy(response, "END\n", 16);
2142  nbytes_read = strlen(response);
2143  }
2144  else if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2145  {
2146  tty_error_msg(errcode, errmsg, MAXRBUF);
2147  LOGF_ERROR("%s", errmsg);
2148  return false;
2149  }
2150 
2151  if (nbytes_read > 0)
2152  {
2153  response[nbytes_read - 1] = '\0';
2154 
2155  // Display the response to be sure to have read the complet TTY Buffer.
2156  LOGF_DEBUG("RES (%s)", response);
2157 
2158  if (strcmp(response, "END"))
2159  {
2160  LOG_WARN("Invalid END response.");
2161  return false;
2162  }
2163  }
2164  // End of added code by Philippe Besson
2165 
2166  tcflush(PortFD, TCIFLUSH);
2167 
2168  return true;
2169 
2170 }
2171 
2172 /************************************************************************************
2173  *
2174 * ***********************************************************************************/
2175 bool Gemini::setLedLevel(int level)
2176 // Write via the serial port to the HUB the selected LED intensity level
2177 
2178 {
2179  char cmd[16];
2180  int errcode = 0;
2181  char errmsg[MAXRBUF];
2182  char response[16];
2183  int nbytes_read = 0;
2184  int nbytes_written = 0;
2185 
2186  memset(response, 0, sizeof(response));
2187 
2188  snprintf(cmd, 16, "<H100SETLED%d>", level);
2189 
2190  LOGF_DEBUG("CMD (%s)", cmd);
2191 
2192  if (isSimulation())
2193  {
2194  strncpy(response, "SET", 16);
2195  nbytes_read = strlen(response) + 1;
2196  }
2197  else
2198  {
2199  tcflush(PortFD, TCIFLUSH);
2200 
2201  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2202  {
2203  tty_error_msg(errcode, errmsg, MAXRBUF);
2204  LOGF_ERROR("%s", errmsg);
2205  return false;
2206  }
2207 
2208  if (isResponseOK() == false)
2209  return false;
2210 
2211  if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2212  {
2213  tty_error_msg(errcode, errmsg, MAXRBUF);
2214  LOGF_ERROR("%s", errmsg);
2215  return false;
2216  }
2217  }
2218 
2219  if (nbytes_read > 0)
2220  {
2221  response[nbytes_read - 1] = '\0';
2222  LOGF_DEBUG("RES (%s)", response);
2223  tcflush(PortFD, TCIFLUSH);
2224 
2225  if (!strcmp(response, "SET"))
2226  return true;
2227  else
2228  return false;
2229  }
2230 
2231  return false;
2232 }
2233 
2234 /************************************************************************************
2235  *
2236 * ***********************************************************************************/
2237 bool Gemini::setNickname(DeviceType type, const char *nickname)
2238 // Write via the serial port to the HUB the choiced nikname of he focuser
2239 {
2240  char cmd[32];
2241  int errcode = 0;
2242  char errmsg[MAXRBUF];
2243  char response[16];
2244  int nbytes_read = 0;
2245  int nbytes_written = 0;
2246 
2247  memset(response, 0, sizeof(response));
2248 
2249  snprintf(cmd, 32, "<%c100SETDNN%s>", (type == DEVICE_FOCUSER ? 'F' : 'R'), nickname);
2250 
2251  LOGF_DEBUG("CMD (%s)", cmd);
2252 
2253  if (!isSimulation())
2254  {
2255  tcflush(PortFD, TCIFLUSH);
2256 
2257  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2258  {
2259  tty_error_msg(errcode, errmsg, MAXRBUF);
2260  LOGF_ERROR("%s", errmsg);
2261  return false;
2262  }
2263 
2264  if (isResponseOK() == false)
2265  return false;
2266 
2267  // Read the 'END'
2268  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2269  }
2270 
2271  tcflush(PortFD, TCIFLUSH);
2272  return true;
2273 }
2274 
2275 /************************************************************************************
2276  *
2277 * ***********************************************************************************/
2278 bool Gemini::halt(DeviceType type)
2279 {
2280  char cmd[32];
2281  int errcode = 0;
2282  char errmsg[MAXRBUF];
2283  char response[16];
2284  int nbytes_read = 0;
2285  int nbytes_written = 0;
2286 
2287  memset(response, 0, sizeof(response));
2288 
2289  snprintf(cmd, 32, "<%c100DOHALT>", (type == DEVICE_FOCUSER ? 'F' : 'R'));
2290  LOGF_DEBUG("CMD (%s)", cmd);
2291 
2292  if (isSimulation())
2293  {
2294  if (type == DEVICE_FOCUSER)
2295  focuserSimStatus[STATUS_MOVING] = ISS_OFF;
2296  else
2297  {
2298  rotatorSimStatus[STATUS_MOVING] = ISS_OFF;
2299  isRotatorHoming = false;
2300  }
2301  }
2302  else
2303  {
2304  tcflush(PortFD, TCIFLUSH);
2305 
2306  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2307  {
2308  tty_error_msg(errcode, errmsg, MAXRBUF);
2309  LOGF_ERROR("%s", errmsg);
2310  return false;
2311  }
2312 
2313  if (isResponseOK() == false)
2314  return false;
2315 
2316  // Read the 'END'
2317  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2318  }
2319 
2320  isRotatorHoming = false;
2321 
2322  tcflush(PortFD, TCIFLUSH);
2323 
2324  return true;
2325 }
2326 
2327 /************************************************************************************
2328  *
2329 * ***********************************************************************************/
2330 bool Gemini::home(DeviceType type)
2331 {
2332  char cmd[32];
2333  int errcode = 0;
2334  char errmsg[MAXRBUF];
2335  char response[16];
2336  int nbytes_read = 0;
2337  int nbytes_written = 0;
2338 
2339  memset(response, 0, sizeof(response));
2340 
2341  snprintf(cmd, 32, "<%c100DOHOME>", (type == DEVICE_FOCUSER ? 'F' : 'R'));
2342  LOGF_DEBUG("CMD (%s)", cmd);
2343 
2344  if (isSimulation())
2345  {
2346  if (type == DEVICE_FOCUSER)
2347  {
2348  focuserSimStatus[STATUS_HOMING] = ISS_ON;
2349  targetFocuserPosition = 0;
2350  }
2351  else
2352  {
2353  rotatorSimStatus[STATUS_HOMING] = ISS_ON;
2354  targetRotatorPosition = 0;
2355  }
2356  }
2357  else
2358  {
2359  tcflush(PortFD, TCIFLUSH);
2360 
2361  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2362  {
2363  tty_error_msg(errcode, errmsg, MAXRBUF);
2364  LOGF_ERROR("%s", errmsg);
2365  return false;
2366  }
2367 
2368  if (isResponseOK() == false)
2369  return false;
2370 
2371  // Read the 'END'
2372  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2373  }
2374 
2375  tcflush(PortFD, TCIFLUSH);
2376 
2377  return true;
2378 }
2379 
2380 /************************************************************************************
2381  *
2382 * ***********************************************************************************/
2383 bool Gemini::homeOnStart(DeviceType type, bool enable)
2384 {
2385  char cmd[32];
2386  int errcode = 0;
2387  char errmsg[MAXRBUF];
2388  char response[16];
2389  int nbytes_read = 0;
2390  int nbytes_written = 0;
2391 
2392  memset(response, 0, sizeof(response));
2393 
2394  snprintf(cmd, 32, "<%c100SETHOS%d>", (type == DEVICE_FOCUSER ? 'F' : 'R'), enable ? 1 : 0);
2395  LOGF_DEBUG("CMD (%s)", cmd);
2396 
2397  if (isSimulation() == false)
2398  {
2399  tcflush(PortFD, TCIFLUSH);
2400 
2401  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2402  {
2403  tty_error_msg(errcode, errmsg, MAXRBUF);
2404  LOGF_ERROR("%s", errmsg);
2405  return false;
2406  }
2407 
2408  if (isResponseOK() == false)
2409  return false;
2410 
2411  // Read the 'END'
2412  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2413  }
2414 
2415  tcflush(PortFD, TCIFLUSH);
2416 
2417  return true;
2418 }
2419 
2420 /************************************************************************************
2421  *
2422 * ***********************************************************************************/
2423 bool Gemini::center(DeviceType type)
2424 {
2425  if (type == DEVICE_ROTATOR)
2426  return MoveAbsRotatorTicks(RotatorAbsPosN[0].max / 2);
2427 
2428  const char * cmd = "<F100CENTER>";
2429  int errcode = 0;
2430  char errmsg[MAXRBUF];
2431  char response[16];
2432  int nbytes_read = 0;
2433  int nbytes_written = 0;
2434 
2435  memset(response, 0, sizeof(response));
2436 
2437  LOGF_DEBUG("CMD (%s)", cmd);
2438 
2439  if (isSimulation())
2440  {
2441  if (type == DEVICE_FOCUSER)
2442  {
2443  focuserSimStatus[STATUS_MOVING] = ISS_ON;
2444  targetFocuserPosition = FocusAbsPosN[0].max / 2;
2445  }
2446  else
2447  {
2448  rotatorSimStatus[STATUS_MOVING] = ISS_ON;
2449  targetRotatorPosition = RotatorAbsPosN[0].max / 2;
2450  }
2451 
2452  }
2453  else
2454  {
2455  tcflush(PortFD, TCIFLUSH);
2456 
2457  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2458  {
2459  tty_error_msg(errcode, errmsg, MAXRBUF);
2460  LOGF_ERROR("%s", errmsg);
2461  return false;
2462  }
2463 
2464  if (isResponseOK() == false)
2465  return false;
2466 
2467  // Read the 'END'
2468  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2469  }
2470 
2471  tcflush(PortFD, TCIFLUSH);
2472 
2473  return true;
2474 }
2475 
2476 /************************************************************************************
2477  *
2478 * ***********************************************************************************/
2479 bool Gemini::setTemperatureCompensation(bool enable)
2480 {
2481  char cmd[16];
2482  int errcode = 0;
2483  char errmsg[MAXRBUF];
2484  char response[16];
2485  int nbytes_read = 0;
2486  int nbytes_written = 0;
2487 
2488  memset(response, 0, sizeof(response));
2489 
2490  snprintf(cmd, 16, "<F100SETTCE%d>", enable ? 1 : 0);
2491 
2492  LOGF_DEBUG("CMD (%s)", cmd);
2493 
2494  if (isSimulation() == false)
2495  {
2496  tcflush(PortFD, TCIFLUSH);
2497 
2498  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2499  {
2500  tty_error_msg(errcode, errmsg, MAXRBUF);
2501  LOGF_ERROR("%s", errmsg);
2502  return false;
2503  }
2504 
2505  if (isResponseOK() == false)
2506  return false;
2507 
2508  // Read the 'END'
2509  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2510  }
2511 
2512  return true;
2513 }
2514 
2515 /************************************************************************************
2516  *
2517 * ***********************************************************************************/
2518 bool Gemini::setTemperatureCompensationMode(char mode)
2519 {
2520  char cmd[16];
2521  int errcode = 0;
2522  char errmsg[MAXRBUF];
2523  char response[16];
2524  int nbytes_read = 0;
2525  int nbytes_written = 0;
2526 
2527  memset(response, 0, sizeof(response));
2528 
2529  snprintf(cmd, 16, "<F100SETTCM%c>", mode);
2530 
2531  LOGF_DEBUG("CMD (%s)", cmd);
2532 
2533  if (isSimulation() == false)
2534  {
2535  tcflush(PortFD, TCIFLUSH);
2536 
2537  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2538  {
2539  tty_error_msg(errcode, errmsg, MAXRBUF);
2540  LOGF_ERROR("%s", errmsg);
2541  return false;
2542  }
2543 
2544  if (isResponseOK() == false)
2545  return false;
2546 
2547  // Read the 'END'
2548  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2549  }
2550 
2551  return true;
2552 }
2553 
2554 /************************************************************************************
2555  *
2556 * ***********************************************************************************/
2557 bool Gemini::setTemperatureCompensationCoeff(char mode, int16_t coeff)
2558 {
2559  char cmd[32];
2560  int errcode = 0;
2561  char errmsg[MAXRBUF];
2562  char response[16];
2563  int nbytes_read = 0;
2564  int nbytes_written = 0;
2565 
2566  memset(response, 0, sizeof(response));
2567 
2568  snprintf(cmd, 32, "<F100SETTCC%c%c%04d>", mode, coeff >= 0 ? '+' : '-', (int)std::abs(coeff));
2569 
2570  LOGF_DEBUG("CMD (%s)", cmd);
2571 
2572  if (isSimulation() == false)
2573  {
2574  tcflush(PortFD, TCIFLUSH);
2575 
2576  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2577  {
2578  tty_error_msg(errcode, errmsg, MAXRBUF);
2579  LOGF_ERROR("%s", errmsg);
2580  return false;
2581  }
2582 
2583  if (isResponseOK() == false)
2584  return false;
2585 
2586  // Read the 'END'
2587  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2588  }
2589 
2590  return true;
2591 }
2592 
2593 /************************************************************************************
2594  *
2595 * ***********************************************************************************/
2596 bool Gemini::setTemperatureCompensationOnStart(bool enable)
2597 {
2598  char cmd[16];
2599  int errcode = 0;
2600  char errmsg[MAXRBUF];
2601  char response[16];
2602  int nbytes_read = 0;
2603  int nbytes_written = 0;
2604 
2605  memset(response, 0, sizeof(response));
2606 
2607  snprintf(cmd, 16, "<F100SETTCS%d>", enable ? 1 : 0);
2608 
2609  LOGF_DEBUG("CMD (%s)", cmd);
2610 
2611  tcflush(PortFD, TCIFLUSH);
2612 
2613  if (isSimulation() == false)
2614  {
2615  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2616  {
2617  tty_error_msg(errcode, errmsg, MAXRBUF);
2618  LOGF_ERROR("%s", errmsg);
2619  return false;
2620  }
2621 
2622  if (isResponseOK() == false)
2623  return false;
2624 
2625  // Read the 'END'
2626  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2627  }
2628 
2629  return true;
2630 }
2631 
2632 /************************************************************************************
2633  *
2634 * ***********************************************************************************/
2635 bool Gemini::SetRotatorBacklash(int32_t steps)
2636 {
2637  return setBacklashCompensationSteps(DEVICE_ROTATOR, steps);
2638 }
2639 
2640 /************************************************************************************
2641  *
2642 * ***********************************************************************************/
2644 {
2645  return setBacklashCompensation(DEVICE_ROTATOR, enabled);
2646 }
2647 
2648 /************************************************************************************
2649  *
2650 * ***********************************************************************************/
2651 bool Gemini::setBacklashCompensation(DeviceType type, bool enable)
2652 {
2653  char cmd[16];
2654  int errcode = 0;
2655  char errmsg[MAXRBUF];
2656  char response[16];
2657  int nbytes_read = 0;
2658  int nbytes_written = 0;
2659 
2660  memset(response, 0, sizeof(response));
2661 
2662  snprintf(cmd, 16, "<%c100SETBCE%d>", (type == DEVICE_FOCUSER ? 'F' : 'R'), enable ? 1 : 0);
2663 
2664  LOGF_DEBUG("CMD (%s)", cmd);
2665 
2666  if (isSimulation() == false)
2667  {
2668  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2669  {
2670  tty_error_msg(errcode, errmsg, MAXRBUF);
2671  LOGF_ERROR("%s", errmsg);
2672  return false;
2673  }
2674 
2675  if (isResponseOK() == false)
2676  return false;
2677 
2678  // Read the 'END'
2679  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2680  }
2681 
2682  return true;
2683 }
2684 
2685 /************************************************************************************
2686  *
2687 * ***********************************************************************************/
2688 bool Gemini::setBacklashCompensationSteps(DeviceType type, uint16_t steps)
2689 {
2690  char cmd[16];
2691  int errcode = 0;
2692  char errmsg[MAXRBUF];
2693  char response[16];
2694  int nbytes_read = 0;
2695  int nbytes_written = 0;
2696 
2697  memset(response, 0, sizeof(response));
2698 
2699  snprintf(cmd, 16, "<%c100SETBCS%02d>", (type == DEVICE_FOCUSER ? 'F' : 'R'), steps);
2700 
2701  LOGF_DEBUG("CMD (%s)", cmd);
2702 
2703  if (isSimulation() == false)
2704  {
2705  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2706  {
2707  tty_error_msg(errcode, errmsg, MAXRBUF);
2708  LOGF_ERROR("%s", errmsg);
2709  return false;
2710  }
2711 
2712  if (isResponseOK() == false)
2713  return false;
2714 
2715  // Read the 'END'
2716  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2717  }
2718 
2719  return true;
2720 }
2721 
2722 /************************************************************************************
2723  *
2724 * ***********************************************************************************/
2725 bool Gemini::reverseRotator(bool enable)
2726 {
2727  char cmd[16];
2728  int errcode = 0;
2729  char errmsg[MAXRBUF];
2730  char response[16];
2731  int nbytes_read = 0;
2732  int nbytes_written = 0;
2733 
2734  memset(response, 0, sizeof(response));
2735 
2736  snprintf(cmd, 16, "<R100SETREV%d>", enable ? 1 : 0);
2737 
2738  LOGF_DEBUG("CMD (%s)", cmd);
2739 
2740  if (isSimulation() == false)
2741  {
2742  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2743  {
2744  tty_error_msg(errcode, errmsg, MAXRBUF);
2745  LOGF_ERROR("%s", errmsg);
2746  return false;
2747  }
2748 
2749  if (isResponseOK() == false)
2750  return false;
2751 
2752  // Read the 'END'
2753  tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read);
2754  }
2755 
2756  return true;
2757 }
2758 
2759 /************************************************************************************
2760  *
2761 * ***********************************************************************************/
2762 bool Gemini::resetFactory()
2763 {
2764  const char *cmd = "<H100RESETH>";
2765  int errcode = 0;
2766  char errmsg[MAXRBUF];
2767  char response[16];
2768  int nbytes_read = 0;
2769  int nbytes_written = 0;
2770 
2771  memset(response, 0, sizeof(response));
2772 
2773  LOGF_DEBUG("CMD (%s)", cmd);
2774 
2775  if (isSimulation())
2776  {
2777  strncpy(response, "SET", 16);
2778  nbytes_read = strlen(response) + 1;
2779  }
2780  else
2781  {
2782  tcflush(PortFD, TCIFLUSH);
2783 
2784  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2785  {
2786  tty_error_msg(errcode, errmsg, MAXRBUF);
2787  LOGF_ERROR("%s", errmsg);
2788  return false;
2789  }
2790 
2791  if (isResponseOK() == false)
2792  return false;
2793 
2794  if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2795  {
2796  tty_error_msg(errcode, errmsg, MAXRBUF);
2797  LOGF_ERROR("%s", errmsg);
2798  return false;
2799  }
2800  }
2801 
2802  if (nbytes_read > 0)
2803  {
2804  response[nbytes_read - 1] = '\0';
2805  LOGF_DEBUG("RES (%s)", response);
2806  tcflush(PortFD, TCIFLUSH);
2807 
2808  if (!strcmp(response, "SET"))
2809  {
2810  //return true;
2811  getFocusConfig();
2812  getRotatorConfig();
2813  }
2814  else
2815  return false;
2816  }
2817 
2818  return false;
2819 }
2820 
2821 /************************************************************************************
2822  *
2823 * ***********************************************************************************/
2825 {
2826  int errcode = 0;
2827  char errmsg[MAXRBUF];
2828  char response[32];
2829  int nbytes_read = 0;
2830 
2831  memset(response, 0, sizeof(response));
2832 
2833  if (isSimulation())
2834  {
2835  strcpy(response, "!00");
2836  nbytes_read = strlen(response) + 1;
2837  }
2838  else
2839  {
2840  if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2841  {
2842  tty_error_msg(errcode, errmsg, MAXRBUF);
2843  LOGF_ERROR("TTY error: %s", errmsg);
2844  return false;
2845  }
2846  }
2847 
2848  if (nbytes_read > 0)
2849  {
2850  response[nbytes_read - 1] = '\0';
2851  LOGF_DEBUG("RES (%s)", response);
2852 
2853  if (!strcmp(response, "!00"))
2854  return true;
2855  else
2856  {
2857  memset(response, 0, sizeof(response));
2858  while (strstr(response, "END") == nullptr)
2859  {
2860  if ((errcode = tty_read_section(PortFD, response, 0xA, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2861  {
2862  tty_error_msg(errcode, errmsg, MAXRBUF);
2863  LOGF_ERROR("TTY error: %s", errmsg);
2864  return false;
2865  }
2866  response[nbytes_read - 1] = '\0';
2867  LOGF_ERROR("Controller error: %s", response);
2868  }
2869 
2870  return false;
2871  }
2872  }
2873  return true;
2874 }
2875 
2876 /************************************************************************************
2877 *
2878 * ***********************************************************************************/
2879 IPState Gemini::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
2880 {
2881  char cmd[16];
2882  int errcode = 0;
2883  char errmsg[MAXRBUF];
2884  char response[16];
2885  int nbytes_written = 0;
2886 
2887  INDI_UNUSED(speed);
2888 
2889  memset(response, 0, sizeof(response));
2890 
2891  snprintf(cmd, 16, "<F100DOMOVE%c>", (dir == FOCUS_INWARD) ? '0' : '1');
2892 
2893  LOGF_DEBUG("CMD (%s)", cmd);
2894 
2895  if (!isSimulation())
2896  {
2897  tcflush(PortFD, TCIFLUSH);
2898 
2899  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2900  {
2901  tty_error_msg(errcode, errmsg, MAXRBUF);
2902  LOGF_ERROR("%s", errmsg);
2903  return IPS_ALERT;
2904  }
2905 
2906  if (isResponseOK() == false)
2907  return IPS_ALERT;
2908 
2909  gettimeofday(&focusMoveStart, nullptr);
2910  focusMoveRequest = duration / 1000.0;
2911  }
2912 
2913  if (duration <= getCurrentPollingPeriod())
2914  {
2915  usleep(getCurrentPollingPeriod() * 1000);
2916  AbortFocuser();
2917  return IPS_OK;
2918  }
2919 
2920  tcflush(PortFD, TCIFLUSH);
2921 
2922  return IPS_BUSY;
2923 }
2924 
2925 /************************************************************************************
2926 *
2927 * ***********************************************************************************/
2928 IPState Gemini::MoveAbsFocuser(uint32_t targetTicks)
2929 {
2930  char cmd[32];
2931  int errcode = 0;
2932  char errmsg[MAXRBUF];
2933  char response[16];
2934  int nbytes_written = 0;
2935 
2936  targetFocuserPosition = targetTicks;
2937 
2938  memset(response, 0, sizeof(response));
2939 
2940  snprintf(cmd, 32, "<F100MOVABS%06d>", targetTicks);
2941 
2942  LOGF_DEBUG("CMD (%s)", cmd);
2943 
2944  if (!isSimulation())
2945  {
2946  tcflush(PortFD, TCIFLUSH);
2947 
2948  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2949  {
2950  tty_error_msg(errcode, errmsg, MAXRBUF);
2951  LOGF_ERROR("%s", errmsg);
2952  return IPS_ALERT;
2953  }
2954 
2955  if (isResponseOK() == false)
2956  return IPS_ALERT;
2957  }
2958 
2960 
2961  tcflush(PortFD, TCIFLUSH);
2962 
2963  return IPS_BUSY;
2964 }
2965 
2966 /************************************************************************************
2967 *
2968 * ***********************************************************************************/
2970 {
2971  uint32_t newPosition = 0;
2972 
2973  if (dir == FOCUS_INWARD)
2974  newPosition = FocusAbsPosN[0].value - ticks;
2975  else
2976  newPosition = FocusAbsPosN[0].value + ticks;
2977 
2978  return MoveAbsFocuser(newPosition);
2979 }
2980 
2981 /************************************************************************************
2982 *
2983 * ***********************************************************************************/
2985 {
2986  if (!isConnected())
2987  return;
2988 
2990  {
2992  return;
2993  }
2994 
2995  // Focuser Status
2996  bool statusrc = false;
2997  for (int i = 0; i < 2; i++)
2998  {
2999  statusrc = getFocusStatus();
3000  if (statusrc)
3001  break;
3002  }
3003 
3004  if (statusrc == false)
3005  {
3006  LOG_WARN("Unable to read focuser status....");
3008  return;
3009  }
3010 
3012  {
3013  if (isSimulation())
3014  {
3015  if (FocusAbsPosN[0].value < targetFocuserPosition)
3016  focuserSimPosition += 100;
3017  else
3018  focuserSimPosition -= 100;
3019 
3020  focuserSimStatus[STATUS_MOVING] = ISS_ON;
3021 
3022  if (std::abs((int64_t)focuserSimPosition - (int64_t)targetFocuserPosition) < 100)
3023  {
3024  FocusAbsPosN[0].value = targetFocuserPosition;
3025  focuserSimPosition = FocusAbsPosN[0].value;
3026  focuserSimStatus[STATUS_MOVING] = ISS_OFF;
3027  FocuserStatusL[STATUS_MOVING].s = IPS_IDLE;
3028  if (focuserSimStatus[STATUS_HOMING] == ISS_ON)
3029  {
3030  FocuserStatusL[STATUS_HOMED].s = IPS_OK;
3031  focuserSimStatus[STATUS_HOMING] = ISS_OFF;
3032  }
3033  }
3034  }
3035 
3036  if (isFocuserHoming && FocuserStatusL[STATUS_HOMED].s == IPS_OK)
3037  {
3038  isFocuserHoming = false;
3039  FocuserGotoSP.s = IPS_OK;
3040  IUResetSwitch(&FocuserGotoSP);
3041  FocuserGotoS[GOTO_HOME].s = ISS_ON;
3042  IDSetSwitch(&FocuserGotoSP, nullptr);
3044  IDSetNumber(&FocusRelPosNP, nullptr);
3045  LOG_INFO("Focuser reached home position.");
3046  }
3047  else if (FocuserStatusL[STATUS_MOVING].s == IPS_IDLE)
3048  {
3051  IDSetNumber(&FocusAbsPosNP, nullptr);
3052  IDSetNumber(&FocusRelPosNP, nullptr);
3053  if (FocuserGotoSP.s == IPS_BUSY)
3054  {
3055  IUResetSwitch(&FocuserGotoSP);
3056  FocuserGotoSP.s = IPS_OK;
3057  IDSetSwitch(&FocuserGotoSP, nullptr);
3058  }
3059  LOG_INFO("Focuser reached requested position.");
3060  }
3061  }
3062  if (FocuserStatusL[STATUS_HOMING].s == IPS_BUSY && FocuserGotoSP.s != IPS_BUSY)
3063  {
3064  FocuserGotoSP.s = IPS_BUSY;
3065  IDSetSwitch(&FocuserGotoSP, nullptr);
3066  }
3067 
3068  // Rotator Status
3069  statusrc = false;
3070  for (int i = 0; i < 2; i++)
3071  {
3072  statusrc = getRotatorStatus();
3073  if (statusrc)
3074  break;
3075  }
3076 
3077  if (statusrc == false)
3078  {
3079  LOG_WARN("Unable to read rotator status....");
3081  return;
3082  }
3083 
3084  if (RotatorAbsPosNP.s == IPS_BUSY || GotoRotatorNP.s == IPS_BUSY)
3085  {
3086  /*if (isSimulation())
3087  {
3088  if (RotatorAbsPosN[0].value < targetRotatorPosition)
3089  RotatorSimPosition += 100;
3090  else
3091  RotatorSimPosition -= 100;
3092 
3093  RotatorSimStatus[STATUS_MOVING] = ISS_ON;
3094 
3095  if (std::abs((int64_t)RotatorSimPosition - (int64_t)targetRotatorPosition) < 100)
3096  {
3097  RotatorAbsPosN[0].value = targetRotatorPosition;
3098  RotatorSimPosition = RotatorAbsPosN[0].value;
3099  RotatorSimStatus[STATUS_MOVING] = ISS_OFF;
3100  RotatorStatusL[STATUS_MOVING].s = IPS_IDLE;
3101  if (RotatorSimStatus[STATUS_HOMING] == ISS_ON)
3102  {
3103  RotatorStatusL[STATUS_HOMED].s = IPS_OK;
3104  RotatorSimStatus[STATUS_HOMING] = ISS_OFF;
3105  }
3106  }
3107  }*/
3108 
3109  if (isRotatorHoming && RotatorStatusL[STATUS_HOMED].s == IPS_OK)
3110  {
3111  isRotatorHoming = false;
3114  IDSetSwitch(&HomeRotatorSP, nullptr);
3115  RotatorAbsPosNP.s = IPS_OK;
3116  IDSetNumber(&RotatorAbsPosNP, nullptr);
3118  IDSetNumber(&GotoRotatorNP, nullptr);
3119  LOG_INFO("Rotator reached home position.");
3120  }
3121  else if (RotatorStatusL[STATUS_MOVING].s == IPS_IDLE)
3122  {
3123  RotatorAbsPosNP.s = IPS_OK;
3124  IDSetNumber(&RotatorAbsPosNP, nullptr);
3126  IDSetNumber(&GotoRotatorNP, nullptr);
3127  if (HomeRotatorSP.s == IPS_BUSY)
3128  {
3131  IDSetSwitch(&HomeRotatorSP, nullptr);
3132  }
3133  LOG_INFO("Rotator reached requested position.");
3134  }
3135  }
3136  if (RotatorStatusL[STATUS_HOMING].s == IPS_BUSY && HomeRotatorSP.s != IPS_BUSY)
3137  {
3139  IDSetSwitch(&HomeRotatorSP, nullptr);
3140  }
3141 
3143 }
3144 
3145 /************************************************************************************
3146  *
3147 * ***********************************************************************************/
3149 {
3150  const char *cmd = "<F100DOHALT>";
3151  int errcode = 0;
3152  char errmsg[MAXRBUF];
3153  char response[16];
3154  int nbytes_read = 0;
3155  int nbytes_written = 0;
3156 
3157  memset(response, 0, sizeof(response));
3158 
3159  LOGF_DEBUG("CMD (%s)", cmd);
3160 
3161  if (isSimulation())
3162  {
3163  strncpy(response, "!00", 16);
3164  nbytes_read = strlen(response) + 1;
3165  focuserSimStatus[STATUS_MOVING] = ISS_OFF;
3166  focuserSimStatus[STATUS_HOMING] = ISS_OFF;
3167  }
3168  else
3169  {
3170  tcflush(PortFD, TCIFLUSH);
3171 
3172  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3173  {
3174  tty_error_msg(errcode, errmsg, MAXRBUF);
3175  LOGF_ERROR("%s", errmsg);
3176  return false;
3177  }
3178 
3179  if (isResponseOK() == false)
3180  return false;
3181  }
3182 
3183 
3184  if (FocusRelPosNP.s == IPS_BUSY)
3185  {
3187  IDSetNumber(&FocusRelPosNP, nullptr);
3188  }
3189 
3192  FocuserGotoSP.s = IPS_IDLE;
3193  IUResetSwitch(&FocuserGotoSP);
3194  IDSetNumber(&FocusAbsPosNP, nullptr);
3195  IDSetSwitch(&FocuserGotoSP, nullptr);
3196 
3197  tcflush(PortFD, TCIFLUSH);
3198 
3199  return true;
3200 }
3201 
3202 /************************************************************************************
3203  *
3204 * ***********************************************************************************/
3205 float Gemini::calcTimeLeft(timeval start, float req)
3206 {
3207  double timesince;
3208  double timeleft;
3209  struct timeval now
3210  {
3211  0, 0
3212  };
3213  gettimeofday(&now, nullptr);
3214 
3215  timesince =
3216  (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
3217  timesince = timesince / 1000;
3218  timeleft = req - timesince;
3219  return timeleft;
3220 }
3221 
3222 /************************************************************************************
3223 *
3224 * ***********************************************************************************/
3225 IPState Gemini::MoveAbsRotatorTicks(uint32_t targetTicks)
3226 {
3227  char cmd[32];
3228  int errcode = 0;
3229  char errmsg[MAXRBUF];
3230  char response[16];
3231  int nbytes_written = 0;
3232 
3233  targetRotatorPosition = targetTicks;
3234 
3235  memset(response, 0, sizeof(response));
3236 
3237  snprintf(cmd, 32, "<R100MOVABS%06d>", targetTicks);
3238 
3239  LOGF_DEBUG("CMD (%s)", cmd);
3240 
3241  if (!isSimulation())
3242  {
3243  tcflush(PortFD, TCIFLUSH);
3244 
3245  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3246  {
3247  tty_error_msg(errcode, errmsg, MAXRBUF);
3248  LOGF_ERROR("%s", errmsg);
3249  return IPS_ALERT;
3250  }
3251 
3252  if (isResponseOK() == false)
3253  return IPS_ALERT;
3254  }
3255 
3256  RotatorAbsPosNP.s = IPS_BUSY;
3257 
3258  tcflush(PortFD, TCIFLUSH);
3259 
3260  return IPS_BUSY;
3261 }
3262 
3263 /************************************************************************************
3264 *
3265 * ***********************************************************************************/
3266 IPState Gemini::MoveAbsRotatorAngle(double angle)
3267 {
3268  char cmd[32];
3269  char response[16];
3270  int nbytes_written = 0;
3271 
3272  targetRotatorAngle = angle * 1000.0;
3273 
3274  memset(response, 0, sizeof(response));
3275 
3276  snprintf(cmd, 32, "<R100MOVEPA%06ud>", targetRotatorAngle);
3277 
3278  LOGF_DEBUG("CMD (%s)", cmd);
3279 
3280  if (!isSimulation())
3281  {
3282  int errcode = 0;
3283  char errmsg[MAXRBUF];
3284  tcflush(PortFD, TCIFLUSH);
3285 
3286  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3287  {
3288  tty_error_msg(errcode, errmsg, MAXRBUF);
3289  LOGF_ERROR("%s", errmsg);
3290  return IPS_ALERT;
3291  }
3292 
3293  if (isResponseOK() == false)
3294  return IPS_ALERT;
3295  }
3296 
3298 
3299  tcflush(PortFD, TCIFLUSH);
3300 
3301  return IPS_BUSY;
3302 }
3303 
3304 /************************************************************************************
3305  *
3306 * ***********************************************************************************/
3308 {
3309  // Save Focuser configs
3311  // Rotator Configs
3312  RI::saveConfigItems(fp);
3313 
3314  IUSaveConfigSwitch(fp, &TemperatureCompensateSP);
3315  IUSaveConfigSwitch(fp, &TemperatureCompensateOnStartSP);
3316  IUSaveConfigNumber(fp, &TemperatureCoeffNP);
3317  IUSaveConfigSwitch(fp, &TemperatureCompensateModeSP);
3318  IUSaveConfigSwitch(fp, &FocuserHomeOnStartSP);
3319  IUSaveConfigSwitch(fp, &RotatorHomeOnStartSP);
3320 
3321  return true;
3322 }
3323 
3324 /************************************************************************************
3325  *
3326 * ***********************************************************************************/
3328 {
3329  IPState state = MoveAbsRotatorAngle(angle);
3330  RotatorAbsPosNP.s = state;
3331  IDSetNumber(&RotatorAbsPosNP, nullptr);
3332 
3333  return state;
3334 }
3335 
3336 /************************************************************************************
3337  *
3338 * ***********************************************************************************/
3340 {
3341  return (home(DEVICE_ROTATOR) ? IPS_BUSY : IPS_ALERT);
3342 }
3343 
3344 /************************************************************************************
3345  *
3346 * ***********************************************************************************/
3347 bool Gemini::ReverseRotator(bool enabled)
3348 {
3349  return reverseRotator(enabled);
3350 }
3351 
3352 /************************************************************************************
3353  *
3354 * ***********************************************************************************/
3355 bool Gemini::SetFocuserBacklash(int32_t steps)
3356 {
3357  return setBacklashCompensationSteps(DEVICE_FOCUSER, steps);
3358 }
3359 
3360 /************************************************************************************
3361  *
3362 * ***********************************************************************************/
3364 {
3365  return setBacklashCompensation(DEVICE_FOCUSER, enabled);
3366 }
INDI::RotatorInterface::saveConfigItems
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
Definition: indirotatorinterface.cpp:332
Gemini::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: gemini.cpp:2984
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::BaseDevice::ROTATOR_INTERFACE
@ ROTATOR_INTERFACE
Definition: basedevice.h:84
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
gemini.h
Gemini::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: gemini.cpp:83
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
INDI::RotatorInterface::RotatorBacklashSP
ISwitchVectorProperty RotatorBacklashSP
Definition: indirotatorinterface.h:213
INDI::DefaultDevice::addAuxControls
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Definition: defaultdevice.cpp:665
IUUpdateMinMax
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1849
Gemini::FOCUS_B_COEFF
@ FOCUS_B_COEFF
Definition: gemini.h:37
INDI::Logger::getInstance
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:354
IPState
IPState
Property state.
Definition: indiapi.h:158
Gemini::~Gemini
~Gemini()
Definition: gemini.cpp:76
Gemini::GOTO_HOME
@ GOTO_HOME
Definition: gemini.h:58
Gemini::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: gemini.cpp:3148
Gemini
Definition: gemini.h:28
INDI::FocuserInterface::FocusBacklashN
INumber FocusBacklashN[1]
Definition: indifocuserinterface.h:311
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
RotatorInterface
Provides interface to implement Rotator functionality.
Gemini::GOTO_CENTER
@ GOTO_CENTER
Definition: gemini.h:57
Gemini::STATUS_REVERSE
@ STATUS_REVERSE
Definition: gemini.h:52
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
indicom.h
Implementations for common driver routines.
INDI::RotatorInterface::initProperties
void initProperties(const char *groupName)
Initilize Rotator properties. It is recommended to call this function within initProperties() of your...
Definition: indirotatorinterface.cpp:35
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
Gemini::STATUS_HNDCTRL
@ STATUS_HNDCTRL
Definition: gemini.h:51
Gemini::focuserConfigurationComplete
bool focuserConfigurationComplete
Definition: gemini.h:96
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
IUFillNumber
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidriver.c:348
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
INDI::RotatorInterface::updateProperties
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
Definition: indirotatorinterface.cpp:248
GEMINI_TIMEOUT
#define GEMINI_TIMEOUT
Definition: gemini.cpp:32
MAIN_CONTROL_TAB
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
Definition: defaultdevice.cpp:34
Gemini::MoveRotator
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition: gemini.cpp:3327
Connection::Serial::B_115200
@ B_115200
Definition: connectionserial.h:82
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
Gemini::FOCUS_E_COEFF
@ FOCUS_E_COEFF
Definition: gemini.h:40
INDI::RotatorInterface::ROTATOR_CAN_ABORT
@ ROTATOR_CAN_ABORT
Definition: indirotatorinterface.h:55
IUFillTextVector
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:477
INDI::Focuser::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:222
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::Focuser::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: indifocuser.cpp:241
Gemini::ReverseRotator
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition: gemini.cpp:3347
Gemini::Handshake
virtual bool Handshake() override
perform handshake with device to check communication
Definition: gemini.cpp:323
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
STATUS_TAB
#define STATUS_TAB
Definition: gemini.cpp:38
_ILightVectorProperty::s
IPState s
Definition: indiapi.h:426
INDI::RotatorInterface::GotoRotatorN
INumber GotoRotatorN[1]
Definition: indirotatorinterface.h:197
Gemini::SetFocuserBacklashEnabled
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
Definition: gemini.cpp:3363
INDI::Focuser::serialConnection
Connection::Serial * serialConnection
Definition: indifocuser.h:113
IUFillLight
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
Definition: indidriver.c:334
INDI::FocuserInterface::FOCUSER_HAS_BACKLASH
@ FOCUSER_HAS_BACKLASH
Definition: indifocuserinterface.h:80
tty_read_section
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:541
Gemini::SetFocuserBacklash
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: gemini.cpp:3355
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1434
IUFillText
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidriver.c:369
IUUpdateText
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:259
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
max
double max(void)
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1421
_ILightVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:420
tty_error_msg
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1135
Gemini::SetRotatorBacklash
virtual bool SetRotatorBacklash(int32_t steps) override
SetRotatorBacklash Set the Rotatorer backlash compensation value.
Definition: gemini.cpp:2635
Gemini::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: gemini.cpp:2969
Gemini::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: gemini.cpp:239
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
INDI::FocuserInterface::FocusBacklashSP
ISwitchVectorProperty FocusBacklashSP
Definition: indifocuserinterface.h:306
Gemini::DEVICE_ROTATOR
@ DEVICE_ROTATOR
Definition: gemini.h:63
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
Gemini::MoveFocuser
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
Definition: gemini.cpp:2879
type
__le16 type
Definition: pwc-ioctl.h:2
Connection::Serial::setDefaultBaudRate
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Definition: connectionserial.cpp:375
Gemini::rotatorConfigurationComplete
bool rotatorConfigurationComplete
Definition: gemini.h:97
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
INDI::Focuser::PortFD
int PortFD
Definition: indifocuser.h:116
Gemini::isResponseOK
bool isResponseOK()
Definition: gemini.cpp:2824
Gemini::HomeRotator
virtual IPState HomeRotator() override
HomeRotator Go to home position.
Definition: gemini.cpp:3339
INDI::RotatorInterface::processNumber
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
Definition: indirotatorinterface.cpp:79
IDSetLight
void void void void void void void IDSetLight(const ILightVectorProperty *l, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing light vector property.
INDI::FocuserInterface::FocusTimerNP
INumberVectorProperty FocusTimerNP
Definition: indifocuserinterface.h:278
tty_write
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:404
INDI::DefaultDevice::getDriverInterface
virtual uint16_t getDriverInterface() override
Definition: defaultdevice.cpp:896
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
IUFillSwitchVector
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:412
INDI::RotatorInterface::ROTATOR_CAN_HOME
@ ROTATOR_CAN_HOME
Definition: indirotatorinterface.h:56
IUFillNumberVector
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:455
ROTATOR_TAB
#define ROTATOR_TAB
Definition: gemini.cpp:39
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
connectionserial.h
INDI::RotatorInterface::RotatorBacklashN
INumber RotatorBacklashN[1]
Definition: indirotatorinterface.h:218
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::RotatorInterface::HomeRotatorSP
ISwitchVectorProperty HomeRotatorSP
Definition: indirotatorinterface.h:207
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
Gemini::FOCUS_A_COEFF
@ FOCUS_A_COEFF
Definition: gemini.h:36
INDI::RotatorInterface::ROTATOR_HAS_BACKLASH
@ ROTATOR_HAS_BACKLASH
Definition: indirotatorinterface.h:59
Gemini::SetRotatorBacklashEnabled
virtual bool SetRotatorBacklashEnabled(bool enabled) override
SetRotatorBacklashEnabled Enables or disables the Rotator backlash compensation.
Definition: gemini.cpp:2643
Gemini::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: gemini.cpp:606
Gemini::ack
bool ack()
Definition: gemini.cpp:743
ISR_ATMOST1
@ ISR_ATMOST1
Definition: indiapi.h:173
INDI::Focuser::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indifocuser.cpp:120
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
Gemini::FOCUS_D_COEFF
@ FOCUS_D_COEFF
Definition: gemini.h:39
INDI::RotatorInterface::RotatorBacklashS
ISwitch RotatorBacklashS[2]
Definition: indirotatorinterface.h:214
INDI::RotatorInterface::ROTATOR_CAN_REVERSE
@ ROTATOR_CAN_REVERSE
Definition: indirotatorinterface.h:58
Gemini::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: gemini.cpp:629
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
INDI::RotatorInterface::GotoRotatorNP
INumberVectorProperty GotoRotatorNP
Definition: indirotatorinterface.h:198
INDI::RotatorInterface::RotatorBacklashNP
INumberVectorProperty RotatorBacklashNP
Definition: indirotatorinterface.h:217
INDI::RotatorInterface::AbortRotatorSP
ISwitchVectorProperty AbortRotatorSP
Definition: indirotatorinterface.h:204
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
INDI::FocuserInterface::FocusBacklashS
ISwitch FocusBacklashS[2]
Definition: indifocuserinterface.h:307
Gemini::STATUS_MOVING
@ STATUS_MOVING
Definition: gemini.h:45
Gemini::STATUS_HOMING
@ STATUS_HOMING
Definition: gemini.h:46
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
INDI::RotatorInterface::SetCapability
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
Definition: indirotatorinterface.h:74
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
IUSaveText
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1428
Gemini::STATUS_TMPPROBE
@ STATUS_TMPPROBE
Definition: gemini.h:49
INDI::RotatorInterface::processSwitch
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
Definition: indirotatorinterface.cpp:153
Gemini::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: gemini.cpp:3307
INDI::RotatorInterface::ReverseRotatorSP
ISwitchVectorProperty ReverseRotatorSP
Definition: indirotatorinterface.h:210
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
_ITextVectorProperty::s
IPState s
Definition: indiapi.h:259
Gemini::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetPosition) override
MoveFocuser the focuser to an absolute position.
Definition: gemini.cpp:2928
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
INDI::Logger::addDebugLevel
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:87
IUUpdateNumber
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:225
Gemini::STATUS_FFDETECT
@ STATUS_FFDETECT
Definition: gemini.h:48
Gemini::STATUS_HOMED
@ STATUS_HOMED
Definition: gemini.h:47
DEBUGF
#define DEBUGF(priority, msg,...)
Definition: indilogger.h:57
Gemini::FOCUS_C_COEFF
@ FOCUS_C_COEFF
Definition: gemini.h:38
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
Gemini::DEVICE_FOCUSER
@ DEVICE_FOCUSER
Definition: gemini.h:62
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
HUB_TAB
#define HUB_TAB
Definition: gemini.cpp:40
Gemini::Gemini
Gemini()
Definition: gemini.cpp:47
INDI::BaseDevice::INDI_DISABLED
@ INDI_DISABLED
Definition: basedevice.h:65
ISState
ISState
Switch state.
Definition: indiapi.h:148
Gemini::STATUS_REMOTEIO
@ STATUS_REMOTEIO
Definition: gemini.h:50
Gemini::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: gemini.cpp:348
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1403
FOCUS_SETTINGS_TAB
#define FOCUS_SETTINGS_TAB
Definition: gemini.cpp:37
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1444
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
TTY_OK
@ TTY_OK
Definition: indicom.h:94
INDI::FocuserInterface::FocusBacklashNP
INumberVectorProperty FocusBacklashNP
Definition: indifocuserinterface.h:310
INDI::RotatorInterface::ReverseRotatorS
ISwitch ReverseRotatorS[2]
Definition: indirotatorinterface.h:209
Gemini::getDefaultName
virtual const char * getDefaultName() override
Definition: gemini.cpp:339
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
IUFillLightVector
void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:435
INDI::Focuser::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:168
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
INDI::RotatorInterface::AbortRotatorS
ISwitch AbortRotatorS[1]
Definition: indirotatorinterface.h:203
INDI::DefaultDevice::setDriverInterface
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
Definition: defaultdevice.cpp:902
IUFillSwitch
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidriver.c:320
INDI::Focuser::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indifocuser.cpp:145
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151