Instrument Neutral Distributed Interface INDI  2.0.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, "%15[^=]=%15[^\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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]= %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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%15[^\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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]= %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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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, "%15[^=]=%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_written = 0;
3155 
3156  memset(response, 0, sizeof(response));
3157 
3158  LOGF_DEBUG("CMD (%s)", cmd);
3159 
3160  if (isSimulation())
3161  {
3162  strncpy(response, "!00", 16);
3163  focuserSimStatus[STATUS_MOVING] = ISS_OFF;
3164  focuserSimStatus[STATUS_HOMING] = ISS_OFF;
3165  }
3166  else
3167  {
3168  tcflush(PortFD, TCIFLUSH);
3169 
3170  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3171  {
3172  tty_error_msg(errcode, errmsg, MAXRBUF);
3173  LOGF_ERROR("%s", errmsg);
3174  return false;
3175  }
3176 
3177  if (isResponseOK() == false)
3178  return false;
3179  }
3180 
3181 
3182  if (FocusRelPosNP.s == IPS_BUSY)
3183  {
3185  IDSetNumber(&FocusRelPosNP, nullptr);
3186  }
3187 
3190  FocuserGotoSP.s = IPS_IDLE;
3191  IUResetSwitch(&FocuserGotoSP);
3192  IDSetNumber(&FocusAbsPosNP, nullptr);
3193  IDSetSwitch(&FocuserGotoSP, nullptr);
3194 
3195  tcflush(PortFD, TCIFLUSH);
3196 
3197  return true;
3198 }
3199 
3200 /************************************************************************************
3201  *
3202 * ***********************************************************************************/
3203 float Gemini::calcTimeLeft(timeval start, float req)
3204 {
3205  double timesince;
3206  double timeleft;
3207  struct timeval now
3208  {
3209  0, 0
3210  };
3211  gettimeofday(&now, nullptr);
3212 
3213  timesince =
3214  (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
3215  timesince = timesince / 1000;
3216  timeleft = req - timesince;
3217  return timeleft;
3218 }
3219 
3220 /************************************************************************************
3221 *
3222 * ***********************************************************************************/
3223 IPState Gemini::MoveAbsRotatorTicks(uint32_t targetTicks)
3224 {
3225  char cmd[32];
3226  int errcode = 0;
3227  char errmsg[MAXRBUF];
3228  char response[16];
3229  int nbytes_written = 0;
3230 
3231  targetRotatorPosition = targetTicks;
3232 
3233  memset(response, 0, sizeof(response));
3234 
3235  snprintf(cmd, 32, "<R100MOVABS%06d>", targetTicks);
3236 
3237  LOGF_DEBUG("CMD (%s)", cmd);
3238 
3239  if (!isSimulation())
3240  {
3241  tcflush(PortFD, TCIFLUSH);
3242 
3243  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3244  {
3245  tty_error_msg(errcode, errmsg, MAXRBUF);
3246  LOGF_ERROR("%s", errmsg);
3247  return IPS_ALERT;
3248  }
3249 
3250  if (isResponseOK() == false)
3251  return IPS_ALERT;
3252  }
3253 
3254  RotatorAbsPosNP.s = IPS_BUSY;
3255 
3256  tcflush(PortFD, TCIFLUSH);
3257 
3258  return IPS_BUSY;
3259 }
3260 
3261 /************************************************************************************
3262 *
3263 * ***********************************************************************************/
3264 IPState Gemini::MoveAbsRotatorAngle(double angle)
3265 {
3266  char cmd[32];
3267  char response[16];
3268  int nbytes_written = 0;
3269 
3270  targetRotatorAngle = angle * 1000.0;
3271 
3272  memset(response, 0, sizeof(response));
3273 
3274  snprintf(cmd, 32, "<R100MOVEPA%06ud>", targetRotatorAngle);
3275 
3276  LOGF_DEBUG("CMD (%s)", cmd);
3277 
3278  if (!isSimulation())
3279  {
3280  int errcode = 0;
3281  char errmsg[MAXRBUF];
3282  tcflush(PortFD, TCIFLUSH);
3283 
3284  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
3285  {
3286  tty_error_msg(errcode, errmsg, MAXRBUF);
3287  LOGF_ERROR("%s", errmsg);
3288  return IPS_ALERT;
3289  }
3290 
3291  if (isResponseOK() == false)
3292  return IPS_ALERT;
3293  }
3294 
3296 
3297  tcflush(PortFD, TCIFLUSH);
3298 
3299  return IPS_BUSY;
3300 }
3301 
3302 /************************************************************************************
3303  *
3304 * ***********************************************************************************/
3306 {
3307  // Save Focuser configs
3309  // Rotator Configs
3310  RI::saveConfigItems(fp);
3311 
3312  IUSaveConfigSwitch(fp, &TemperatureCompensateSP);
3313  IUSaveConfigSwitch(fp, &TemperatureCompensateOnStartSP);
3314  IUSaveConfigNumber(fp, &TemperatureCoeffNP);
3315  IUSaveConfigSwitch(fp, &TemperatureCompensateModeSP);
3316  IUSaveConfigSwitch(fp, &FocuserHomeOnStartSP);
3317  IUSaveConfigSwitch(fp, &RotatorHomeOnStartSP);
3318 
3319  return true;
3320 }
3321 
3322 /************************************************************************************
3323  *
3324 * ***********************************************************************************/
3326 {
3327  IPState state = MoveAbsRotatorAngle(angle);
3328  RotatorAbsPosNP.s = state;
3329  IDSetNumber(&RotatorAbsPosNP, nullptr);
3330 
3331  return state;
3332 }
3333 
3334 /************************************************************************************
3335  *
3336 * ***********************************************************************************/
3338 {
3339  return (home(DEVICE_ROTATOR) ? IPS_BUSY : IPS_ALERT);
3340 }
3341 
3342 /************************************************************************************
3343  *
3344 * ***********************************************************************************/
3345 bool Gemini::ReverseRotator(bool enabled)
3346 {
3347  return reverseRotator(enabled);
3348 }
3349 
3350 /************************************************************************************
3351  *
3352 * ***********************************************************************************/
3353 bool Gemini::SetFocuserBacklash(int32_t steps)
3354 {
3355  return setBacklashCompensationSteps(DEVICE_FOCUSER, steps);
3356 }
3357 
3358 /************************************************************************************
3359  *
3360 * ***********************************************************************************/
3362 {
3363  return setBacklashCompensation(DEVICE_FOCUSER, enabled);
3364 }
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Definition: gemini.h:30
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: gemini.cpp:2984
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: gemini.cpp:3305
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: gemini.cpp:2969
~Gemini()
Definition: gemini.cpp:76
bool isResponseOK()
Definition: gemini.cpp:2824
virtual IPState HomeRotator() override
HomeRotator Go to home position.
Definition: gemini.cpp:3337
Gemini()
Definition: gemini.cpp:47
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
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
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
Definition: gemini.cpp:3361
bool focuserConfigurationComplete
Definition: gemini.h:97
bool ack()
Definition: gemini.cpp:743
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition: gemini.cpp:3325
bool rotatorConfigurationComplete
Definition: gemini.h:98
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
@ FOCUS_C_COEFF
Definition: gemini.h:39
@ FOCUS_B_COEFF
Definition: gemini.h:38
@ FOCUS_A_COEFF
Definition: gemini.h:37
@ FOCUS_D_COEFF
Definition: gemini.h:40
@ FOCUS_E_COEFF
Definition: gemini.h:41
@ STATUS_TMPPROBE
Definition: gemini.h:50
@ STATUS_REVERSE
Definition: gemini.h:53
@ STATUS_REMOTEIO
Definition: gemini.h:51
@ STATUS_HOMING
Definition: gemini.h:47
@ STATUS_HOMED
Definition: gemini.h:48
@ STATUS_FFDETECT
Definition: gemini.h:49
@ STATUS_HNDCTRL
Definition: gemini.h:52
@ STATUS_MOVING
Definition: gemini.h:46
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: gemini.cpp:239
virtual bool SetRotatorBacklashEnabled(bool enabled) override
SetRotatorBacklashEnabled Enables or disables the Rotator backlash compensation.
Definition: gemini.cpp:2643
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: gemini.cpp:3148
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: gemini.cpp:3353
@ DEVICE_ROTATOR
Definition: gemini.h:64
@ DEVICE_FOCUSER
Definition: gemini.h:63
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition: gemini.cpp:3345
virtual IPState MoveAbsFocuser(uint32_t targetPosition) override
MoveFocuser the focuser to an absolute position.
Definition: gemini.cpp:2928
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
@ GOTO_HOME
Definition: gemini.h:59
@ GOTO_CENTER
Definition: gemini.h:58
virtual bool SetRotatorBacklash(int32_t steps) override
SetRotatorBacklash Set the Rotatorer backlash compensation value.
Definition: gemini.cpp:2635
virtual bool Handshake() override
perform handshake with device to check communication
Definition: gemini.cpp:323
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: gemini.cpp:83
virtual const char * getDefaultName() override
Definition: gemini.cpp:339
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
uint16_t getDriverInterface() const
INumberVectorProperty FocusBacklashNP
ISwitchVectorProperty FocusBacklashSP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Connection::Serial * serialConnection
Definition: indifocuser.h:116
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
INumberVectorProperty RotatorBacklashNP
INumberVectorProperty GotoRotatorNP
void initProperties(const char *groupName)
Initilize Rotator properties. It is recommended to call this function within initProperties() of your...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
ISwitchVectorProperty HomeRotatorSP
ISwitchVectorProperty ReverseRotatorSP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
ISwitchVectorProperty RotatorBacklashSP
ISwitchVectorProperty AbortRotatorSP
Provides interface to implement Rotator functionality.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
double max(void)
#define ROTATOR_TAB
Definition: gemini.cpp:39
#define HUB_TAB
Definition: gemini.cpp:40
#define GEMINI_TIMEOUT
Definition: gemini.cpp:32
#define STATUS_TAB
Definition: gemini.cpp:38
#define FOCUS_SETTINGS_TAB
Definition: gemini.cpp:37
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
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:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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: indidevapi.c:169
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: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
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: indidevapi.c:255
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: indidevapi.c:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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: indidevapi.c:158
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: indidevapi.c:198
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: indidevapi.c:180
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: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
Definition: indidriver.c:1251
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1296
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition: indilogger.h:57
#define MAXRBUF
Definition: indiserver.cpp:102
@ key
the parser read a key of a value in an object
__le16 type
Definition: pwc-ioctl.h:0
__u8 cmd[4]
Definition: pwc-ioctl.h:2
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:339
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:72
char name[MAXINDINAME]
Definition: indiapi.h:421
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250