Instrument Neutral Distributed Interface INDI  2.0.2
lx200gemini.cpp
Go to the documentation of this file.
1 /*
2  Losmandy Gemini INDI driver
3 
4  Copyright (C) 2017 Jasem Mutlaq
5  Copyright (C) 2018 Eric Vickery
6 
7  Difference from LX200 Generic:
8 
9  1. Added Side of Pier
10  2. Reimplemented isSlewComplete to use :Gv# since it is more reliable
11  3. Support networked connections.
12  4. Side of pier
13  5. Variable GOTO/SLEW/MOVE speeds.
14 
15  v1.4:
16 
17  + Added MOUNT_STATE_UPDATE_FREQ to reduce number of calls to updateMountState to reduce traffic
18  + All TCIFLUSH --> TCIOFLUSH to make sure both pipes are flushed since we received logs with mismatched traffic.
19 
20  This library is free software; you can redistribute it and/or
21  modify it under the terms of the GNU Lesser General Public
22  License as published by the Free Software Foundation; either
23  version 2.1 of the License, or (at your option) any later version.
24 
25  This library is distributed in the hope that it will be useful,
26  but WITHOUT ANY WARRANTY; without even the implied warranty of
27  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28  Lesser General Public License for more details.
29 
30  You should have received a copy of the GNU Lesser General Public
31  License along with this library; if not, write to the Free Software
32  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34 #include "lx200gemini.h"
35 
36 #include "indicom.h"
37 #include "lx200driver.h"
40 
41 #include <cstring>
42 #include <termios.h>
43 
44 #define LX200_TIMEOUT 5 /* FD timeout in seconds */
45 
46 #define MANUAL_SLEWING_SPEED_ID 120
47 #define GOTO_SLEWING_SPEED_ID 140
48 #define MOVE_SPEED_ID 145 // L5
49 #define GUIDING_SPEED_ID 150
50 #define GUIDING_SPEED_RA_ID 151 // L5
51 #define GUIDING_SPEED_DEC_ID 152 // L5
52 #define CENTERING_SPEED_ID 170
53 #define SERVO_POINTING_PRECISION_ID 401 // L6
54 #define PEC_MAX_STEPS_ID 27
55 #define PEC_COUNTER_ID 501
56 #define PEC_STATUS_ID 509
57 #define PEC_START_TRAINING_ID 530 // L5
58 #define PEC_ABORT_TRAINING_ID 535 // L5
59 #define PEC_REPLAY_ON_ID 531 // L5
60 #define PEC_REPLAY_OFF_ID 532 // L5
61 #define PEC_ENABLE_AT_BOOT_ID 508 // L5.2
62 #define PEC_GUIDING_SPEED_ID 502
63 #define SERVO_FIRMWARE 400 // L6 <ra>;<dec> (L6)
64 #define FLIP_POINT_EAST_ID 227 // L6
65 #define FLIP_POINT_WEST_ID 228 // L6
66 #define FLIP_POINTS_ENABLED_ID 229 // L6
67 #define SET_SAFETY_LIMIT_TO_CURRENT_ID 220
68 #define EAST_SAFETY_LIMIT_ID 221
69 #define WEST_SAFETY_LIMIT_ID 222
70 #define WEST_GOTO_SAFETY_LIMIT_ID 223
71 
72 
73 
74 
76 {
77  setVersion(1, 6);
78 
80 
84  4);
85 }
86 
87 
89 {
90  return "Losmandy Gemini";
91 }
92 
94 {
95  Connection::Interface *activeConnection = getActiveConnection();
96 
97  if (!activeConnection->name().compare("CONNECTION_TCP"))
98  {
100  }
101 
102  return LX200Generic::Connect();
103 }
104 
105 void LX200Gemini::ISGetProperties(const char *dev)
106 {
108 
109  // Read config from file
110  int index = 0;
111  if (IUGetConfigOnSwitch(&StartupModeSP, &index) == 0)
112  {
113  IUResetSwitch(&StartupModeSP);
114  StartupModeSP.sp[index].s = ISS_ON;
115  defineProperty(&StartupModeSP);
116  }
117 }
118 
120 {
122 
123  // Show firmware
124  IUFillText(&VersionT[FIRMWARE_DATE], "Build Date", "", "");
125  IUFillText(&VersionT[FIRMWARE_TIME], "Build Time", "", "");
126  IUFillText(&VersionT[FIRMWARE_LEVEL], "Software Level", "", "");
127  IUFillText(&VersionT[FIRMWARE_NAME], "Product Name", "", "");
128  IUFillTextVector(&VersionTP, VersionT, 5, getDeviceName(), "Firmware Info", "", CONNECTION_TAB, IP_RO, 0, IPS_IDLE);
129 
130  // Park Option
131  IUFillSwitch(&ParkSettingsS[PARK_HOME], "HOME", "Home", ISS_ON);
132  IUFillSwitch(&ParkSettingsS[PARK_STARTUP], "STARTUP", "Startup", ISS_OFF);
133  IUFillSwitch(&ParkSettingsS[PARK_ZENITH], "ZENITH", "Zenith", ISS_OFF);
134  IUFillSwitchVector(&ParkSettingsSP, ParkSettingsS, 3, getDeviceName(), "PARK_SETTINGS", "Park Settings",
136 
137  IUFillSwitch(&StartupModeS[COLD_START], "COLD_START", "Cold", ISS_ON);
138  IUFillSwitch(&StartupModeS[PARK_STARTUP], "WARM_START", "Warm", ISS_OFF);
139  IUFillSwitch(&StartupModeS[PARK_ZENITH], "WARM_RESTART", "Restart", ISS_OFF);
140  IUFillSwitchVector(&StartupModeSP, StartupModeS, 3, getDeviceName(), "STARTUP_MODE", "Startup Mode",
142 
143  IUFillNumber(&ManualSlewingSpeedN[0], "MANUAL_SLEWING_SPEED", "Manual Slewing Speed", "%g", 20, 2000., 10., 800);
144  IUFillNumberVector(&ManualSlewingSpeedNP, ManualSlewingSpeedN, 1, getDeviceName(), "MANUAL_SLEWING_SPEED",
145  "Slew Speed", MOTION_TAB, IP_RW, 0, IPS_IDLE);
146 
147  IUFillNumber(&GotoSlewingSpeedN[0], "GOTO_SLEWING_SPEED", "Goto Slewing Speed", "%g", 20, 2000., 10., 800);
148  IUFillNumberVector(&GotoSlewingSpeedNP, GotoSlewingSpeedN, 1, getDeviceName(), "GOTO_SLEWING_SPEED",
149  "Goto Speed", MOTION_TAB, IP_RW, 0, IPS_IDLE);
150 
151  IUFillNumber(&MoveSpeedN[0], "MOVE_SPEED", "Move Speed", "%g", 20, 2000., 10., 10);
152  IUFillNumberVector(&MoveSpeedNP, MoveSpeedN, 1, getDeviceName(), "MOVE_SLEWING_SPEED",
153  "Move Speed", MOTION_TAB, IP_RW, 0, IPS_IDLE);
154 
155  IUFillNumber(&GuidingSpeedBothN[GUIDING_BOTH], "GUIDING_SPEED", "Guide Speed RA/DEC", "%g", 0.2, 0.8, 0.1, 0.5);
156  IUFillNumberVector(&GuidingSpeedBothNP, GuidingSpeedBothN, 1, getDeviceName(), "GUIDING_SLEWING_SPEED_BOTH",
157  "Guide Speed",
158  GUIDE_TAB, IP_RW, 0, IPS_IDLE);
159 
160  IUFillNumber(&GuidingSpeedN[GUIDING_WE], "GUIDE_RATE_WE", "W/E Rate", "%g", 0.2, 0.8, 0.1, 0.5);
161  IUFillNumber(&GuidingSpeedN[GUIDING_NS], "GUIDE_RATE_NS", "N/S Rate", "%g", 0.2, 0.8, 0.1, 0.5);
162  IUFillNumberVector(&GuidingSpeedNP, GuidingSpeedN, 2, getDeviceName(), "GUIDE_RATE",
163  "Guide Speed", GUIDE_TAB, IP_RW, 0, IPS_IDLE);
164 
165  IUFillNumber(&CenteringSpeedN[0], "CENTERING_SPEED", "Centering Speed", "%g", 20, 2000., 10., 10);
166  IUFillNumberVector(&CenteringSpeedNP, CenteringSpeedN, 1, getDeviceName(), "CENTERING_SLEWING_SPEED",
167  "Center Speed", MOTION_TAB, IP_RW, 0, IPS_IDLE);
168 
169  IUFillSwitch(&TrackModeS[GEMINI_TRACK_SIDEREAL], "TRACK_SIDEREAL", "Sidereal", ISS_ON);
170  IUFillSwitch(&TrackModeS[GEMINI_TRACK_KING], "TRACK_CUSTOM", "King", ISS_OFF);
171  IUFillSwitch(&TrackModeS[GEMINI_TRACK_LUNAR], "TRACK_LUNAR", "Lunar", ISS_OFF);
172  IUFillSwitch(&TrackModeS[GEMINI_TRACK_SOLAR], "TRACK_SOLAR", "Solar", ISS_OFF);
173 
174  //PEC
175  IUFillSwitch(&PECControlS[PEC_START_TRAINING], "PEC_START_TRAINING", "Start Training", ISS_OFF);
176  IUFillSwitch(&PECControlS[PEC_ABORT_TRAINING], "PEC_ABORT_TRAINING", "Abort Training", ISS_OFF);
177  IUFillSwitchVector(&PECControlSP, PECControlS, 2, getDeviceName(), "PEC_COMMANDS",
178  "PEC Cmds", MOTION_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
179 
180  IUFillText(&PECStateT[PEC_STATUS_ACTIVE], "PEC_STATUS_ACTIVE", "PEC active", "");
181  IUFillText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "PEC_STATUS_FRESH_TRAINED", "PEC freshly trained", "");
182  IUFillText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "PEC_STATUS_TRAINING_IN_PROGRESS", "PEC training in progress", "");
183  IUFillText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "PEC_STATUS_TRAINING_COMPLETED", "PEC training just completed", "");
184  IUFillText(&PECStateT[PEC_STATUS_WILL_TRAIN], "PEC_STATUS_WILL_TRAIN", "PEC will train soon", "");
185 
186  IUFillText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "PEC_STATUS_DATA_AVAILABLE", "PEC Data available", "");
187  IUFillTextVector(&PECStateTP, PECStateT, 6, getDeviceName(), "PEC_STATE",
188  "PEC State", MOTION_TAB, IP_RO, 0, IPS_OK);
189 
190  IUFillText(&PECCounterT[0], "PEC_COUNTER", "Counter", "");
191  IUFillTextVector(&PECCounterTP, PECCounterT, 1, getDeviceName(), "PEC_COUNTER",
192  "PEC Counter", MOTION_TAB, IP_RO, 0, IPS_IDLE);
193 
194  IUFillNumber(&PECMaxStepsN[0], "PEC_MAX_STEPS", "PEC MaxSteps", "%f", 0, 4294967296, 1, 0);
195  IUFillNumberVector(&PECMaxStepsNP, PECMaxStepsN, 1, getDeviceName(), "PEC_MAX_STEPS",
196  "PEC Steps", MOTION_TAB, IP_RO, 0, IPS_IDLE);
197 
198  IUFillSwitch(&ServoPrecisionS[SERVO_RA], "SERVO_RA", "4x RA Precision", ISS_OFF);
199  IUFillSwitch(&ServoPrecisionS[SERVO_DEC], "SERVO_DEC", "4x DEC Precision", ISS_OFF);
200  IUFillSwitchVector(&ServoPrecisionSP, ServoPrecisionS, 2, getDeviceName(), "SERVO",
201  "Servo", MOTION_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
202 
203  IUFillNumber(&PECGuidingSpeedN[0], "PEC_GUIDING_SPEED", "PEC GuidingSpeed", "%f", 0.2, 0.8, 0.1, 0);
204  IUFillNumberVector(&PECGuidingSpeedNP, PECGuidingSpeedN, 1, getDeviceName(), "PEC_GUIDING_SPEED",
205  "PEC Speed", MOTION_TAB, IP_RO, 0, IPS_IDLE);
206 
207  IUFillSwitch(&PECEnableAtBootS[0], "ENABLE_PEC_AT_BOOT", "Enable PEC at boot", ISS_OFF);
208  IUFillSwitchVector(&PECEnableAtBootSP, PECEnableAtBootS, 1, getDeviceName(), "ENABLE_PEC_AT_BOOT",
209  "PEC Setting", MOTION_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
210 
211  // Flip points
212  IUFillSwitch(&FlipControlS[FLIP_EAST_CONTROL], "FLIP_EAST_CONTROL", "East", ISS_OFF);
213  IUFillSwitch(&FlipControlS[FLIP_WEST_CONTROL], "FLIP_WEST_CONTROL", "West", ISS_OFF);
214  IUFillSwitchVector(&FlipControlSP, FlipControlS, 2, getDeviceName(), "FLIP_CONTROL",
215  "Flip Control", MOTION_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
216 
217  IUFillNumber(&FlipPositionN[FLIP_EAST_VALUE], "FLIP_EAST_VALUE", "East (dd:mm)", "%060.4m", 0, 90, 0, 0.0);
218  IUFillNumber(&FlipPositionN[FLIP_WEST_VALUE], "FLIP_WEST_VALUE", "West (dd:mm)", "%060.4m", 0, 90, 0, 0.0);
219  IUFillNumberVector(&FlipPositionNP, FlipPositionN, 2, getDeviceName(), "FLIP_POSITION",
220  "Flip Position", MOTION_TAB, IP_RW, 0, IPS_IDLE);
221 
222  // Refraction
223  IUFillSwitch(&RefractionControlS[0], "REF_COORDS", "Refract Coords", ISS_OFF);
224  IUFillSwitchVector(&RefractionControlSP, RefractionControlS, 1, getDeviceName(), "REFRACT",
225  "Refraction", MOTION_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
226 
227  // Safety
228  IUFillSwitch(&SetSafetyLimitToCurrentS[0], "SET_SAFETY", "Set to Current", ISS_OFF);
229  IUFillSwitchVector(&SetSafetyLimitToCurrentSP, SetSafetyLimitToCurrentS, 1, getDeviceName(), "SET_CURR_SAFETY",
230  "Safety Limit", MOTION_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
231 
232  IUFillNumber(&SafetyLimitsN[EAST_SAFETY], "EAST_SAFTEY", "East Safety(dd:mm)", "%060.4m", 0, 180, 0, 0.0);
233  IUFillNumber(&SafetyLimitsN[WEST_SAFETY], "WEST_SAFTEY", "West Safety(dd:mm)", "%060.4m", 0, 180, 0, 0.0);
234  IUFillNumber(&SafetyLimitsN[WEST_GOTO], "WEST_GOTO", "West Goto (dd:mm)", "%060.4m", 0, 180, 0, 0.0);
235  IUFillNumberVector(&SafetyLimitsNP, SafetyLimitsN, 3, getDeviceName(), "SAFETY_LIMITS",
236  "Limits", MOTION_TAB, IP_RW, 0, IPS_IDLE);
237 
238  gemini_software_level_ = 0.0;
239 
240  return true;
241 }
242 
243 bool LX200Gemini::getRefractionJNOW(int &data)
244 {
245  if (isSimulation())
246  return true;
247 
248  // Response
249  char response[2] = { 0 };
250  int rc = 0, nbytes_read = 0, nbytes_written = 0;
251 
252 
253  tcflush(PortFD, TCIFLUSH);
254 
255  const char *cmd = ":p?#";
256  data = 0;
257  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
258  {
259  char errmsg[256];
260  tty_error_msg(rc, errmsg, 256);
261  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
262  return false;
263  }
264 
265  // Read response
266  if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
267  {
268  char errmsg[256];
269  tty_error_msg(rc, errmsg, 256);
270  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
271  return false;
272  }
273  tcflush(PortFD, TCIFLUSH);
274  response[1] = '\0';
275 
276  data = atoi(response);
277  return true;
278 }
279 
280 bool LX200Gemini::getRefraction(bool &on)
281 {
282  if (isSimulation())
283  return true;
284 
285  int data=0;
286 
287  bool success = getRefractionJNOW(data);
288 
289  if(data & 2)
290  {
291  on = true;
292  if((data != 2) && (data != 0))
293  {
294  LOGF_WARN("Mount Precess being reset to JNOW: %i", data);
295  }
296  return setRefraction(2);
297  } else {
298  on = false;
299  if((data != 2) && (data != 0)) {
300  LOGF_WARN("Mount Precess being reset to JNOW: %i", data);
301  }
302  return setRefraction(0);
303  }
304 
305  return success;
306 }
307 
308 bool LX200Gemini::setRefraction(int data)
309 {
310  if (isSimulation())
311  return true;
312 
313  int rc = 0, nbytes_written = 0;
314 
315  tcflush(PortFD, TCIFLUSH);
316 
317  char cmd[5] = { 0 };
318  snprintf(cmd, 5, ":p%i#", data);
319  data = 0;
320  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
321  {
322  char errmsg[256];
323  tty_error_msg(rc, errmsg, 256);
324  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
325  return false;
326  }
327  tcflush(PortFD, TCIFLUSH);
328 
329  return true;
330 }
331 
332 bool LX200Gemini::setRefraction(bool on)
333 {
334  if (isSimulation())
335  return true;
336 
337  int data;
338 
339  getRefractionJNOW(data);
340 
341  if(on) {
342  if((data != 2) && (data != 0))
343  {
344  LOGF_WARN("Mount Precess being reset to JNOW %i ", data);
345  }
346  return setRefraction(2);
347  } else {
348  if((data != 2) && (data != 0)) {
349  LOGF_WARN("Mount Precess being reset to JNOW %i", data);
350  }
351  return setRefraction(0);
352  }
353 }
354 
355 void LX200Gemini::syncState() {
356  const int MAX_VALUE_LENGTH = 32;
357  char value[MAX_VALUE_LENGTH] = {0};
358  char value2[MAX_VALUE_LENGTH] = {0};
359  char value3[MAX_VALUE_LENGTH] = {0};
360 
361  if (gemini_software_level_ >= 5.0)
362  {
363  if (getGeminiProperty(GUIDING_SPEED_RA_ID, value))
364  {
365  float guiding_value;
366  sscanf(value, "%f", &guiding_value);
367  GuidingSpeedN[GUIDING_WE].value = guiding_value;
368  IDSetNumber(&GuidingSpeedNP, nullptr);
369  } else {
370  GuidingSpeedNP.s = IPS_ALERT;
371  IDSetNumber(&GuidingSpeedNP, nullptr);
372  }
373  if (getGeminiProperty(GUIDING_SPEED_DEC_ID, value))
374  {
375  float guiding_value;
376  sscanf(value, "%f", &guiding_value);
377  GuidingSpeedN[GUIDING_NS].value = guiding_value;
378  IDSetNumber(&GuidingSpeedNP, nullptr);
379  } else {
380  GuidingSpeedNP.s = IPS_ALERT;
381  IDSetNumber(&GuidingSpeedNP, nullptr);
382  }
383  }
384  if (gemini_software_level_ >= 5.2)
385  {
386  if (getGeminiProperty(PEC_ENABLE_AT_BOOT_ID, value))
387  {
388  uint32_t pec_at_boot_value;
389  sscanf(value, "%u", &pec_at_boot_value);
390  if(pec_at_boot_value)
391  {
392  PECEnableAtBootS[0].s = ISS_ON;
393  } else {
394  PECEnableAtBootS[0].s = ISS_OFF;
395  }
396  PECEnableAtBootSP.s = IPS_OK;
397  } else {
398  PECEnableAtBootSP.s = IPS_ALERT;
399  }
400  IDSetSwitch(&PECEnableAtBootSP, nullptr);
401  }
402  if(gemini_software_level_ >= 6.0)
403  {
404  if (getGeminiProperty(SERVO_POINTING_PRECISION_ID, value))
405  {
406  uint8_t servo_value;
407  sscanf(value, "%c", &servo_value);
408  if(servo_value & RA_PRECISION_ENABLED)
409  {
410  ServoPrecisionS[SERVO_RA].s = ISS_ON;
411  } else {
412  ServoPrecisionS[SERVO_RA].s = ISS_OFF;
413  }
414  if(servo_value & DEC_PRECISION_ENABLED)
415  {
416  ServoPrecisionS[SERVO_DEC].s = ISS_ON;
417  } else {
418  ServoPrecisionS[SERVO_DEC].s = ISS_OFF;
419  }
420  ServoPrecisionSP.s = IPS_OK;
421  IDSetSwitch(&ServoPrecisionSP, nullptr);
422 
423  } else {
424  ServoPrecisionSP.s = IPS_ALERT;
425  IDSetSwitch(&ServoPrecisionSP, nullptr);
426  }
427  if(getGeminiProperty(FLIP_POINTS_ENABLED_ID, value))
428  {
429  char valueString[32] = {0};
430  uint32_t flip_value = 0;
431  sscanf(value, "%u", &flip_value);
432  snprintf(valueString, 32, "%i", flip_value);
433 
434  if(flip_value) {
435  if(flip_value & FLIP_EAST) {
436  FlipControlS[FLIP_EAST_CONTROL].s = ISS_ON;
437  } else {
438  FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
439  }
440  if(flip_value & FLIP_WEST) {
441  FlipControlS[FLIP_WEST_CONTROL].s = ISS_ON;
442  } else {
443  FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
444  }
445  } else {
446  FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
447  FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
448  }
449  FlipControlSP.s = IPS_OK;
450  IDSetSwitch(&FlipControlSP, nullptr);
451  } else {
452  FlipControlSP.s = IPS_ALERT;
453  IDSetSwitch(&FlipControlSP, nullptr);
454  }
455  if(getGeminiProperty(FLIP_POINT_EAST_ID, value) &&
456  getGeminiProperty(FLIP_POINT_WEST_ID, value2))
457  {
458  double eastSexa;
459  double westSexa;
460 
461  f_scansexa(value, &eastSexa);
462  FlipPositionN[FLIP_EAST_VALUE].value = eastSexa;
463 
464  f_scansexa(value2, &westSexa);
465  FlipPositionN[FLIP_WEST_VALUE].value = westSexa;
466 
467  FlipPositionNP.s = IPS_OK;
468  IDSetNumber(&FlipPositionNP, nullptr);
469  } else {
470  FlipPositionNP.s = IPS_ALERT;
471  IDSetNumber(&FlipPositionNP, nullptr);
472  }
473  }
474 
475  if (getGeminiProperty(PEC_MAX_STEPS_ID, value))
476  {
477  float max_value;
478  sscanf(value, "%f", &max_value);
479  PECMaxStepsN[0].value = max_value;
480  IDSetNumber(&PECMaxStepsNP, nullptr);
481  } else {
482  PECMaxStepsNP.s = IPS_ALERT;
483  IDSetNumber(&PECMaxStepsNP, nullptr);
484  }
485  if (getGeminiProperty(PEC_COUNTER_ID, value))
486  {
487  char valueString[32] = {0};
488  uint32_t pec_counter = 0;
489  sscanf(value, "%u", &pec_counter);
490  snprintf(valueString, 32, "%i", pec_counter);
491 
492  IUSaveText(&PECCounterT[0], valueString);
493  IDSetText(&PECCounterTP, nullptr);
494  } else {
495  PECCounterTP.s = IPS_ALERT;
496  IDSetText(&PECCounterTP, nullptr);
497  }
498  if (getGeminiProperty(PEC_GUIDING_SPEED_ID, value))
499  {
500  float guiding_value;
501  sscanf(value, "%f", &guiding_value);
502  PECGuidingSpeedN[0].value = guiding_value;
503  IDSetNumber(&PECGuidingSpeedNP, nullptr);
504  } else {
505  PECGuidingSpeedNP.s = IPS_ALERT;
506  IDSetNumber(&PECGuidingSpeedNP, nullptr);
507  }
508  if (getGeminiProperty(GUIDING_SPEED_ID, value))
509  {
510  float guiding_value;
511  sscanf(value, "%f", &guiding_value);
512  GuidingSpeedBothN[GUIDING_BOTH].value = guiding_value;
513  IDSetNumber(&GuidingSpeedBothNP, nullptr);
514  } else {
515  GuidingSpeedBothNP.s = IPS_ALERT;
516  IDSetNumber(&GuidingSpeedBothNP, nullptr);
517  }
518  if (getGeminiProperty(PEC_STATUS_ID, value))
519  {
520  uint32_t pec_status = 0;
521  sscanf(value, "%u", &pec_status);
522  if(pec_status & 1) { // PEC_ACTIVE
523  IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "Yes");
525  } else {
526  IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "No");
528  }
529  if(pec_status & 2) { // Freshly_Trained
530  IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "Yes");
531  } else {
532  IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "No");
533  }
534  if(pec_status & 4) { // Training_In_Progress
535  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "Yes");
536  } else {
537  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "No");
538  }
539  if(pec_status & 8) { // Training_just_completed
540  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "Yes");
541  } else {
542  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "No");
543  }
544  if(pec_status & 16) { // Training will start soon
545  IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "Yes");
546  } else {
547  IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "No");
548  }
549  if(pec_status & 32) { // PEC Data Available
550  IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
551  } else {
552  IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "No");
553  }
554  IDSetText(&PECStateTP, nullptr);
555  } else {
556  PECStateTP.s = IPS_ALERT;
557  IDSetText(&PECStateTP, nullptr);
558  }
559 
560  if(getGeminiProperty(EAST_SAFETY_LIMIT_ID, value) &&
561  getGeminiProperty(WEST_SAFETY_LIMIT_ID, value2) &&
562  getGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, value3))
563  {
564  double eastSafeSexa;
565  double westSafeSexa;
566  double westGotoSexa;
567 
568  f_scansexa(value, &eastSafeSexa);
569  SafetyLimitsN[EAST_SAFETY].value = eastSafeSexa;
570 
571  f_scansexa(value2, &westSafeSexa);
572  SafetyLimitsN[WEST_SAFETY].value = westSafeSexa;
573 
574  f_scansexa(value3, &westGotoSexa);
575  SafetyLimitsN[WEST_GOTO].value = westGotoSexa;
576 
577  SafetyLimitsNP.s = IPS_OK;
578  IDSetNumber(&SafetyLimitsNP, nullptr);
579  } else {
580  SafetyLimitsNP.s = IPS_ALERT;
581  IDSetNumber(&SafetyLimitsNP, nullptr);
582  }
583 }
584 
585 
587 {
588  const int MAX_VALUE_LENGTH = 32;
590 
591  if (isConnected())
592  {
593  uint32_t speed = 0;
594  char value[MAX_VALUE_LENGTH] = {0};
595  char value2[MAX_VALUE_LENGTH] = {0};
596  char value3[MAX_VALUE_LENGTH] = {0};
597  if (!isSimulation())
598  {
599  VersionTP.tp[FIRMWARE_DATE].text = new char[64];
600  getVersionDate(PortFD, VersionTP.tp[FIRMWARE_DATE].text);
601  VersionTP.tp[FIRMWARE_TIME].text = new char[64];
602  getVersionTime(PortFD, VersionTP.tp[FIRMWARE_TIME].text);
603  VersionTP.tp[FIRMWARE_LEVEL].text = new char[64];
604  getVersionNumber(PortFD, VersionTP.tp[FIRMWARE_LEVEL].text);
605  VersionTP.tp[FIRMWARE_NAME].text = new char[128];
606  getProductName(PortFD, VersionTP.tp[FIRMWARE_NAME].text);
607  sscanf(VersionTP.tp[FIRMWARE_LEVEL].text, "%f", &gemini_software_level_);
608  IDSetText(&VersionTP, nullptr);
609  }
610  defineProperty(&VersionTP);
611  defineProperty(&ParkSettingsSP);
612 
613  if (gemini_software_level_ < 5.0)
614  {
616  }
617  if (gemini_software_level_ >= 5.2)
618  {
619  if(getGeminiProperty(PEC_ENABLE_AT_BOOT_ID, value))
620  {
621  uint32_t pec_at_boot_value;
622  sscanf(value, "%i", &pec_at_boot_value);
623  if(pec_at_boot_value)
624  {
625  PECEnableAtBootS[0].s = ISS_ON;
626  } else {
627  PECEnableAtBootS[0].s = ISS_OFF;
628  }
629  PECEnableAtBootSP.s = IPS_OK;
630  IDSetSwitch(&PECEnableAtBootSP, nullptr);
631  } else {
632  PECEnableAtBootSP.s = IPS_ALERT;
633  IDSetSwitch(&PECEnableAtBootSP, nullptr);
634  }
635  defineProperty(&PECEnableAtBootSP);
636 
637  }
638  if (getGeminiProperty(PEC_GUIDING_SPEED_ID, value))
639  {
640  float guiding_speed_value;
641  sscanf(value, "%f", &guiding_speed_value);
642  PECGuidingSpeedN[0].value = guiding_speed_value;
643  PECGuidingSpeedNP.s = IPS_OK;
644  IDSetNumber(&PECGuidingSpeedNP, nullptr);
645  } else {
646  PECGuidingSpeedNP.s = IPS_ALERT;
647  IDSetNumber(&PECGuidingSpeedNP, nullptr);
648  }
649  defineProperty(&PECGuidingSpeedNP);
650  if (gemini_software_level_ >= 5.0) {
651  if(getGeminiProperty(PEC_COUNTER_ID, value))
652  {
653  char valueString[32] = {0};
654  uint32_t pec_counter = 0;
655  sscanf(value, "%u", &pec_counter);
656  snprintf(valueString, 32, "%i", pec_counter);
657  IUSaveText(&PECCounterT[0], valueString);
658  PECControlSP.s = IPS_OK;
659  IDSetSwitch(&PECControlSP, nullptr);
660  } else {
661  PECControlSP.s = IPS_ALERT;
662  IDSetSwitch(&PECControlSP, nullptr);
663  }
664  defineProperty(&PECControlSP);
665  defineProperty(&PECCounterTP);
666  }
667  if (getGeminiProperty(PEC_MAX_STEPS_ID, value))
668  {
669  float max_steps_value;
670  sscanf(value, "%f", &max_steps_value);
671  PECMaxStepsN[0].value = max_steps_value;
672  PECMaxStepsNP.s = IPS_OK;
673  IDSetNumber(&PECMaxStepsNP, nullptr);
674  } else {
675  PECMaxStepsNP.s = IPS_ALERT;
676  IDSetNumber(&PECMaxStepsNP, nullptr);
677  }
678  defineProperty(&PECMaxStepsNP);
679  if (getGeminiProperty(PEC_STATUS_ID, value))
680  {
681  uint32_t pec_status = 0;
682  sscanf(value, "%u", &pec_status);
683  if(pec_status & 1) { // PEC_ACTIVE
684  IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "Yes");
686  } else {
687  IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "No");
689  }
690 
691  if(pec_status & 2) { // Freshly_Trained
692  IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "Yes");
693  } else {
694  IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "No");
695  }
696 
697  if(pec_status & 4) { // Training_In_Progress
698  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "Yes");
699  } else {
700  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "No");
701  }
702  if(pec_status & 6) { // Training_just_completed
703  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "Yes");
704  } else {
705  IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "No");
706  }
707  if(pec_status & 16) { // Training will start soon
708  IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "Yes");
709  } else {
710  IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "No");
711  }
712  if(pec_status & 32) { // PEC Data Available
713  IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
714  } else {
715  IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
716  }
717  PECStateTP.s = IPS_OK;
718  IDSetText(&PECStateTP, nullptr);
719  } else {
720  PECStateTP.s = IPS_ALERT;
721  IDSetText(&PECStateTP, nullptr);
722  }
723  defineProperty(&PECStateTP);
724  if (gemini_software_level_ >= 6.0)
725  {
726  if(getGeminiProperty(FLIP_POINTS_ENABLED_ID, value))
727  {
728  char valueString[32] = {0};
729  uint32_t flip_value = 0;
730  sscanf(value, "%u", &flip_value);
731  snprintf(valueString, 32, "%i", flip_value);
732 
733  if(flip_value) {
734  if(flip_value & FLIP_EAST) {
735  FlipControlS[FLIP_EAST_CONTROL].s = ISS_ON;
736  } else {
737  FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
738  }
739  if(flip_value & FLIP_WEST) {
740  FlipControlS[FLIP_WEST_CONTROL].s = ISS_ON;
741  } else {
742  FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
743  }
744  } else {
745  FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
746  FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
747  }
748  FlipControlSP.s = IPS_OK;
749  IDSetSwitch(&FlipControlSP, nullptr);
750  } else {
751  FlipControlSP.s = IPS_ALERT;
752  IDSetSwitch(&FlipControlSP, nullptr);
753  }
754  defineProperty(&FlipControlSP);
755  }
756 
757  if (gemini_software_level_ >= 6.0)
758  {
759  if(getGeminiProperty(FLIP_POINT_EAST_ID, value) &&
760  getGeminiProperty(FLIP_POINT_WEST_ID, value2))
761  {
762  double eastSexa;
763  double westSexa;
764 
765  f_scansexa(value, &eastSexa);
766  FlipPositionN[FLIP_EAST_VALUE].value = eastSexa;
767 
768  f_scansexa(value2, &westSexa);
769  FlipPositionN[FLIP_WEST_VALUE].value = westSexa;
770 
771  FlipPositionNP.s = IPS_OK;
772  IDSetNumber(&FlipPositionNP, nullptr);
773  } else {
774  FlipPositionNP.s = IPS_ALERT;
775  IDSetNumber(&FlipPositionNP, nullptr);
776  }
777  defineProperty(&FlipPositionNP);
778  }
779 
780  if(getGeminiProperty(EAST_SAFETY_LIMIT_ID, value) &&
781  getGeminiProperty(WEST_SAFETY_LIMIT_ID, value2) &&
782  getGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, value3))
783  {
784  double eastSafeSexa;
785  double westSafeSexa;
786  double westGotoSexa;
787 
788  f_scansexa(value, &eastSafeSexa);
789  SafetyLimitsN[EAST_SAFETY].value = eastSafeSexa;
790 
791  f_scansexa(value2, &westSafeSexa);
792  SafetyLimitsN[WEST_SAFETY].value = westSafeSexa;
793 
794  f_scansexa(value3, &westGotoSexa);
795  SafetyLimitsN[WEST_GOTO].value = westGotoSexa;
796 
797  SafetyLimitsNP.s = IPS_OK;
798  IDSetNumber(&SafetyLimitsNP, nullptr);
799  } else {
800  SafetyLimitsNP.s = IPS_ALERT;
801  IDSetNumber(&SafetyLimitsNP, nullptr);
802  }
803  defineProperty(&SetSafetyLimitToCurrentSP);
804  defineProperty(&SafetyLimitsNP);
805 
806  if (gemini_software_level_ >= 6.0)
807  {
808  if(getGeminiProperty(SERVO_POINTING_PRECISION_ID, value))
809  {
810  uint8_t servo_value;
811  sscanf(value, "%c", &servo_value);
812  if(servo_value & RA_PRECISION_ENABLED) {
813  ServoPrecisionS[SERVO_RA].s = ISS_ON;
814  } else {
815  ServoPrecisionS[SERVO_RA].s = ISS_OFF;
816  }
817  if(servo_value & DEC_PRECISION_ENABLED) {
818  ServoPrecisionS[SERVO_DEC].s = ISS_ON;
819  } else {
820  ServoPrecisionS[SERVO_DEC].s = ISS_OFF;
821  }
822  ServoPrecisionSP.s = IPS_OK;
823  IDSetSwitch(&ServoPrecisionSP, nullptr);
824  } else {
825  ServoPrecisionSP.s = IPS_ALERT;
826  IDSetSwitch(&ServoPrecisionSP, nullptr);
827  }
828  defineProperty(&ServoPrecisionSP);
829 
830  bool refractionSetting = false;
831  if(getRefraction(refractionSetting))
832  {
833  if(refractionSetting)
834  {
835  RefractionControlS[0].s = ISS_ON;
836  } else {
837  RefractionControlS[0].s = ISS_OFF;
838  }
839  RefractionControlSP.s = IPS_OK;
840  IDSetSwitch(&RefractionControlSP, nullptr);
841  } else {
842  RefractionControlSP.s = IPS_ALERT;
843  IDSetSwitch(&RefractionControlSP, nullptr);
844  }
845  defineProperty(&RefractionControlSP);
846 
847  }
848  if (getGeminiProperty(MANUAL_SLEWING_SPEED_ID, value))
849  {
850  sscanf(value, "%u", &speed);
851  ManualSlewingSpeedN[0].value = speed;
852  ManualSlewingSpeedNP.s = IPS_OK;
853  IDSetNumber(&ManualSlewingSpeedNP, nullptr);
854  } else {
855  ManualSlewingSpeedNP.s = IPS_ALERT;
856  IDSetNumber(&ManualSlewingSpeedNP, nullptr);
857  }
858  defineProperty(&ManualSlewingSpeedNP);
859  if (getGeminiProperty(GOTO_SLEWING_SPEED_ID, value))
860  {
861  sscanf(value, "%u", &speed);
862  GotoSlewingSpeedN[0].value = speed;
863  GotoSlewingSpeedNP.s = IPS_OK;
864  } else {
865  GotoSlewingSpeedNP.s = IPS_ALERT;
866  IDSetNumber(&GotoSlewingSpeedNP, nullptr);
867  }
868  defineProperty(&GotoSlewingSpeedNP);
869  if (gemini_software_level_ >= 5.0)
870  {
871  if(getGeminiProperty(MOVE_SPEED_ID, value))
872  {
873  sscanf(value, "%u", &speed);
874  MoveSpeedN[0].value = speed;
875  MoveSpeedNP.s = IPS_OK;
876  IDSetNumber(&MoveSpeedNP, nullptr);
877  } else {
878  MoveSpeedNP.s = IPS_ALERT;
879  IDSetNumber(&MoveSpeedNP, nullptr);
880  }
881  }
882  defineProperty(&MoveSpeedNP);
883  if (getGeminiProperty(GUIDING_SPEED_ID, value))
884  {
885  float guidingSpeed = 0.0;
886  sscanf(value, "%f", &guidingSpeed);
887  GuidingSpeedBothN[GUIDING_BOTH].value = guidingSpeed;
888  GuidingSpeedBothNP.s = IPS_OK;
889  IDSetNumber(&GuidingSpeedBothNP, nullptr);
890  } else {
891  GuidingSpeedBothNP.s = IPS_ALERT;
892  IDSetNumber(&GuidingSpeedBothNP, nullptr);
893  }
894  defineProperty(&GuidingSpeedBothNP);
895  if (gemini_software_level_ >= 5.0)
896  {
897  if(getGeminiProperty(GUIDING_SPEED_RA_ID, value))
898  {
899  float guidingSpeed = 0.0;
900  sscanf(value, "%f", &guidingSpeed);
901  GuidingSpeedN[GUIDING_WE].value = guidingSpeed;
902  GuidingSpeedNP.s = IPS_OK;
903  IDSetNumber(&GuidingSpeedNP, nullptr);
904  } else {
905  GuidingSpeedNP.s = IPS_ALERT;
906  IDSetNumber(&GuidingSpeedNP, nullptr);
907  }
908  }
909  if (gemini_software_level_ >= 5.0)
910  {
911  if(getGeminiProperty(GUIDING_SPEED_DEC_ID, value))
912  {
913  float guidingSpeed = 0.0;
914  sscanf(value, "%f", &guidingSpeed);
915  GuidingSpeedN[GUIDING_NS].value = guidingSpeed;
916  GuidingSpeedNP.s = IPS_OK;
917  IDSetNumber(&GuidingSpeedNP, nullptr);
918  } else {
919  GuidingSpeedNP.s = IPS_ALERT;
920  IDSetNumber(&GuidingSpeedNP, nullptr);
921  }
922  }
923  defineProperty(&GuidingSpeedNP);
924  if (getGeminiProperty(CENTERING_SPEED_ID, value))
925  {
926  sscanf(value, "%u", &speed);
927  CenteringSpeedN[0].value = speed;
928  CenteringSpeedNP.s = IPS_OK;
929  IDSetNumber(&CenteringSpeedNP, nullptr);
930  } else {
931  CenteringSpeedNP.s = IPS_ALERT;
932  IDSetNumber(&CenteringSpeedNP, nullptr);
933  }
934  defineProperty(&CenteringSpeedNP);
935 
936  updateParkingState();
937  updateMovementState();
938  }
939  else
940  {
941  deleteProperty(ParkSettingsSP.name);
942  deleteProperty(ManualSlewingSpeedNP.name);
943  deleteProperty(GotoSlewingSpeedNP.name);
944  deleteProperty(MoveSpeedNP.name);
945  deleteProperty(GuidingSpeedNP.name);
946  deleteProperty(GuidingSpeedBothNP.name);
947  deleteProperty(CenteringSpeedNP.name);
948  deleteProperty(PECStateTP.name);
949  deleteProperty(PECCounterTP.name);
950  deleteProperty(PECMaxStepsNP.name);
951  deleteProperty(PECGuidingSpeedNP.name);
952  deleteProperty(ServoPrecisionSP.name);
953  deleteProperty(PECEnableAtBootSP.name);
954  deleteProperty(PECGuidingSpeedNP.name);
955  deleteProperty(VersionTP.name);
956  deleteProperty(FlipPositionNP.name);
957  deleteProperty(FlipControlSP.name);
958  deleteProperty(PECControlSP.name);
959  deleteProperty(SetSafetyLimitToCurrentSP.name);
960  deleteProperty(SafetyLimitsNP.name);
961  }
962 
963  return true;
964 }
965 bool LX200Gemini::ISNewText(const char *dev, const char *name, char **texts, char **names, int n)
966 {
967  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
968  {
969  if (!strcmp(name, PECStateTP.name))
970  {
971  IUUpdateText(&PECStateTP, texts, names, n);
972  IDSetText(&PECStateTP, nullptr);
973  }
974  if (!strcmp(name, PECCounterTP.name))
975  {
976  IUUpdateText(&PECCounterTP, texts, names, n);
977  IDSetText(&PECCounterTP, nullptr);
978  }
979  }
980 
981  return LX200Generic::ISNewText(dev, name, texts, names, n);
982 }
983 
984 bool LX200Gemini::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
985 {
986  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
987  {
988  if (!strcmp(name, StartupModeSP.name))
989  {
990  IUUpdateSwitch(&StartupModeSP, states, names, n);
991  StartupModeSP.s = IPS_OK;
992  if (isConnected())
993  LOG_INFO("Startup mode will take effect on future connections.");
994  IDSetSwitch(&StartupModeSP, nullptr);
995  return true;
996  }
997 
998  if (!strcmp(name, ParkSettingsSP.name))
999  {
1000  IUUpdateSwitch(&ParkSettingsSP, states, names, n);
1001  ParkSettingsSP.s = IPS_OK;
1002  IDSetSwitch(&ParkSettingsSP, nullptr);
1003  return true;
1004  }
1005 
1006  if (gemini_software_level_ >= 5.0 && !strcmp(name, PECStateSP.name))
1007  {
1008  IUUpdateSwitch(&PECStateSP, states, names, n);
1009  for(int i = 0; i<n; ++i)
1010  {
1011  if (!strcmp(names[i], PECStateS[PEC_ON].name))
1012  {
1013  if(PECStateS[PEC_ON].s == ISS_ON)
1014  {
1015  LOG_INFO("PEC State.s ON.");
1016  char valueString[16] = {0};
1017  if(!setGeminiProperty(PEC_REPLAY_ON_ID, valueString))
1018  {
1020  IDSetSwitch(&PECStateSP, nullptr);
1021  return false;
1022  } else {
1023  return true;
1024  }
1025 
1026  }
1027  }
1028  if (!strcmp(names[i], PECStateS[PEC_OFF].name))
1029  {
1030  if(PECStateS[PEC_OFF].s == ISS_ON)
1031  {
1032  LOG_INFO("PEC State.s ON.");
1033  char valueString[16] = {0};
1034  if(!setGeminiProperty(PEC_REPLAY_OFF_ID, valueString))
1035  {
1037  IDSetSwitch(&PECStateSP, nullptr);
1038  return false;
1039  }
1040  return true;
1041  }
1042  }
1043  }
1044  PECStateSP.s = IPS_OK;
1045  IDSetSwitch(&PECStateSP, nullptr);
1046  return true;
1047  }
1048 
1049  if (gemini_software_level_ >= 6.0 && !strcmp(name, ServoPrecisionSP.name))
1050  {
1051  IUUpdateSwitch(&ServoPrecisionSP, states, names, n);
1052  ServoPrecisionSP.s = IPS_BUSY;
1053 
1054  uint8_t precisionEnabled = 0;
1055  for(int i = 0; i<n; ++i) {
1056  if (!strcmp(names[i], ServoPrecisionS[SERVO_RA].name))
1057  {
1058  if(ServoPrecisionS[SERVO_RA].s == ISS_ON)
1059  {
1060  precisionEnabled |= 1;
1061  }
1062  }
1063 
1064  if (!strcmp(names[i], ServoPrecisionS[SERVO_DEC].name)) {
1065  if(ServoPrecisionS[SERVO_DEC].s == ISS_ON) {
1066  precisionEnabled |= 2;
1067  }
1068  }
1069  }
1070  char valueString[16] = {0};
1071 
1072  snprintf(valueString, 16, "%u", precisionEnabled);
1073 
1074  if(precisionEnabled & 1)
1075  {
1076  LOGF_INFO("ServoPrecision: RA ON <%i>", (int)precisionEnabled);
1077  } else {
1078  LOGF_INFO("ServoPrecision: RA OFF <%i>", (int)precisionEnabled);
1079  }
1080 
1081  if(precisionEnabled & 2)
1082  {
1083  LOGF_INFO("ServoPrecision: DEC ON <%i>", (int)precisionEnabled);
1084  } else {
1085  LOGF_INFO("ServoPrecision: DEC OFF <%i>", (int)precisionEnabled);
1086  }
1087 
1088  if(!setGeminiProperty(SERVO_POINTING_PRECISION_ID, valueString))
1089  {
1090  ServoPrecisionSP.s = IPS_ALERT;
1091  IDSetSwitch(&ServoPrecisionSP, nullptr);
1092  return false;
1093  } else {
1094  ServoPrecisionSP.s = IPS_OK;
1095  IDSetSwitch(&ServoPrecisionSP, nullptr);
1096  return true;
1097  }
1098  }
1099 
1100  if (gemini_software_level_ >= 6.0 && !strcmp(name, RefractionControlSP.name))
1101  {
1102  IUUpdateSwitch(&RefractionControlSP, states, names, n);
1103  for(int i = 0; i<n; ++i) {
1104  if (!strcmp(names[i], RefractionControlS[0].name))
1105  {
1106  if(RefractionControlS[0].s == ISS_ON)
1107  {
1108  if(!setRefraction(true))
1109  {
1110  RefractionControlSP.s = IPS_ALERT;
1111  IDSetSwitch(&RefractionControlSP, nullptr);
1112  return false;
1113  } else {
1114  RefractionControlSP.s = IPS_OK;
1115  IDSetSwitch(&RefractionControlSP, nullptr);
1116  return true;
1117  }
1118  } else if(RefractionControlS[0].s == ISS_OFF) {
1119  if(!setRefraction(false))
1120  {
1121  RefractionControlSP.s = IPS_ALERT;
1122  IDSetSwitch(&RefractionControlSP, nullptr);
1123  return false;
1124  } else {
1125  RefractionControlSP.s = IPS_OK;
1126  IDSetSwitch(&RefractionControlSP, nullptr);
1127  return true;
1128  }
1129  }
1130  }
1131  }
1132  }
1133 
1134  if (gemini_software_level_ >= 6.0 && !strcmp(name, FlipControlSP.name))
1135  {
1136  IUUpdateSwitch(&FlipControlSP, states, names, n);
1137  FlipControlSP.s = IPS_OK;
1138  IDSetSwitch(&FlipControlSP, nullptr);
1139  int32_t flipEnabled = 0;
1140  for(int i = 0; i<n; ++i)
1141  {
1142  if (!strcmp(names[i], FlipControlS[FLIP_EAST_CONTROL].name)) {
1143  if(FlipControlS[FLIP_EAST_CONTROL].s == ISS_ON)
1144  {
1145  flipEnabled |= 1;
1146  LOGF_INFO("FlipControl: EAST ON <%i>", flipEnabled);
1147  } else {
1148  flipEnabled &= 0xfffffffe;
1149  LOGF_INFO("FlipControl: EAST OFF <%i>", flipEnabled);
1150  }
1151  }
1152 
1153  if (!strcmp(names[i], FlipControlS[FLIP_WEST_CONTROL].name)) {
1154  if(FlipControlS[FLIP_WEST_CONTROL].s == ISS_ON)
1155  {
1156  flipEnabled |= 2;
1157  LOGF_INFO("FlipControl: WEST ON <%i>", flipEnabled);
1158  } else {
1159  flipEnabled &= 0xfffffffd;
1160  LOGF_INFO("FlipControl: WEST OFF <%i>", flipEnabled);
1161  }
1162  }
1163  }
1164  char valueString[16] = {0};
1165  snprintf(valueString, 16, "%i", flipEnabled);
1166  LOGF_INFO("FlipControl: <%s>", valueString);
1167  if(!setGeminiProperty(FLIP_POINTS_ENABLED_ID, valueString))
1168  {
1169  FlipControlSP.s = IPS_ALERT;
1170  IDSetSwitch(&FlipControlSP, nullptr);
1171  return false;
1172  } else {
1173  FlipControlSP.s = IPS_OK;
1174  IDSetSwitch(&FlipControlSP, nullptr);
1175  return true;
1176  }
1177  }
1178  if (!strcmp(name, SetSafetyLimitToCurrentSP.name))
1179  {
1180  char valueString[16] = {0};
1181  IUUpdateSwitch(&SetSafetyLimitToCurrentSP, states, names, n);
1182  IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1183  SetSafetyLimitToCurrentS[0].s = ISS_OFF;
1184  if(!setGeminiProperty(SET_SAFETY_LIMIT_TO_CURRENT_ID, valueString))
1185  {
1186  SetSafetyLimitToCurrentSP.s = IPS_ALERT;
1187  IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1188  return false;
1189  } else {
1190  SetSafetyLimitToCurrentSP.s = IPS_OK;
1191  IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1192  }
1193 
1194  }
1195 
1196  if (gemini_software_level_ >= 5.0 && !strcmp(name, PECControlSP.name))
1197  {
1198  IUUpdateSwitch(&PECControlSP, states, names, n);
1199  for(int i = 0; i<n; ++i) {
1200  if (!strcmp(names[i], PECControlS[PEC_START_TRAINING].name))
1201  {
1202  char valueString[16] = {0};
1203  if(!setGeminiProperty(PEC_START_TRAINING_ID, valueString))
1204  {
1205  PECControlSP.s = IPS_ALERT;
1206  IDSetSwitch(&PECControlSP, nullptr);
1207  return false;
1208  }
1209  } else if (!strcmp(names[i], PECControlS[PEC_ABORT_TRAINING].name))
1210  {
1211  char valueString[16] = {0};
1212  if(!setGeminiProperty(PEC_ABORT_TRAINING_ID, valueString))
1213  {
1214  PECControlSP.s = IPS_ALERT;
1215  IDSetSwitch(&PECControlSP, nullptr);
1216  return false;
1217  }
1218 
1219  }
1220 
1221  }
1222  PECControlSP.s = IPS_OK;
1223  IDSetSwitch(&PECControlSP, nullptr);
1224  return true;
1225  }
1226  }
1227 
1228  return LX200Generic::ISNewSwitch(dev, name, states, names, n);
1229 }
1230 
1231 bool LX200Gemini::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1232 {
1233  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1234  {
1235  char valueString[16] = {0};
1236  snprintf(valueString, 16, "%2.0f", values[0]);
1237 
1238  if (!strcmp(name, ManualSlewingSpeedNP.name))
1239  {
1240  LOGF_DEBUG("Trying to set manual slewing speed of: %f", values[0]);
1241 
1242  if (!isSimulation() && !setGeminiProperty(MANUAL_SLEWING_SPEED_ID, valueString))
1243  {
1244  ManualSlewingSpeedNP.s = IPS_ALERT;
1245  IDSetNumber(&ManualSlewingSpeedNP, "Error setting manual slewing speed");
1246  return false;
1247  }
1248 
1249  ManualSlewingSpeedNP.s = IPS_OK;
1250  ManualSlewingSpeedN[0].value = values[0];
1251  IDSetNumber(&ManualSlewingSpeedNP, "Manual slewing speed set to %f", values[0]);
1252 
1253  return true;
1254  }
1255  if (!strcmp(name, GotoSlewingSpeedNP.name))
1256  {
1257  LOGF_DEBUG("Trying to set goto slewing speed of: %f", values[0]);
1258 
1259  if (!isSimulation() && !setGeminiProperty(GOTO_SLEWING_SPEED_ID, valueString))
1260  {
1261  GotoSlewingSpeedNP.s = IPS_ALERT;
1262  IDSetNumber(&GotoSlewingSpeedNP, "Error setting goto slewing speed");
1263  return false;
1264  }
1265 
1266  GotoSlewingSpeedNP.s = IPS_OK;
1267  GotoSlewingSpeedN[0].value = values[0];
1268  IDSetNumber(&GotoSlewingSpeedNP, "Goto slewing speed set to %f", values[0]);
1269 
1270  return true;
1271  }
1272  if (gemini_software_level_ >= 5.0 && !strcmp(name, MoveSpeedNP.name))
1273  {
1274  LOGF_DEBUG("Trying to set move speed of: %f", values[0]);
1275 
1276  if (!isSimulation() && !setGeminiProperty(MOVE_SPEED_ID, valueString))
1277  {
1278  MoveSpeedNP.s = IPS_ALERT;
1279  IDSetNumber(&MoveSpeedNP, "Error setting move speed");
1280  return false;
1281  }
1282 
1283  MoveSpeedNP.s = IPS_OK;
1284  MoveSpeedN[0].value = values[0];
1285  IDSetNumber(&MoveSpeedNP, "Move speed set to %f", values[0]);
1286 
1287  return true;
1288  }
1289  if (gemini_software_level_ >= 5.0 && !strcmp(name, GuidingSpeedBothNP.name))
1290  {
1291  LOGF_DEBUG("Trying to set guiding speed of: %f", values[0]);
1292 
1293  for(int i = 0; i<n; ++i) {
1294  if (!strcmp(names[i], GuidingSpeedBothN[GUIDING_BOTH].name))
1295  {
1296  // Special formatting for guiding speed
1297  snprintf(valueString, 16, "%1.1f", values[0]);
1298 
1299  if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_ID, valueString))
1300  {
1301  GuidingSpeedBothNP.s = IPS_ALERT;
1302  IDSetNumber(&GuidingSpeedBothNP, "Error setting guiding speed");
1303  return false;
1304  }
1305 
1306  }
1307  }
1308 
1309  GuidingSpeedBothN[GUIDING_BOTH].value = values[0];
1310  GuidingSpeedBothNP.s = IPS_OK;
1311  IDSetNumber(&GuidingSpeedBothNP, "Guiding speed set to %f", values[0]);
1312 
1313  return true;
1314  }
1315  if (!strcmp(name, GuidingSpeedNP.name))
1316  {
1317 
1318  for(int i = 0; i<n; ++i) {
1319  if (!strcmp(names[i], GuidingSpeedN[GUIDING_WE].name))
1320  {
1321  // Special formatting for guiding speed
1322  snprintf(valueString, 16, "%1.1f", values[i]);
1323  if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_RA_ID, valueString)) {
1324  GuidingSpeedN[GUIDING_WE].value = values[i];
1325  GuidingSpeedNP.s = IPS_ALERT;
1326  IDSetNumber(&GuidingSpeedNP, "Error Setting Guiding WE");
1327  return false;
1328  }
1329 
1330  }
1331  if (!strcmp(names[i], GuidingSpeedN[GUIDING_NS].name))
1332  {
1333  // Special formatting for guiding speed
1334  snprintf(valueString, 16, "%1.1f", values[i]);
1335  GuidingSpeedNP.s = IPS_ALERT;
1336  if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_DEC_ID, valueString))
1337  {
1338  GuidingSpeedN[GUIDING_NS].value = values[i];
1339  GuidingSpeedNP.s = IPS_ALERT;
1340  IDSetNumber(&GuidingSpeedNP, "Error Setting Guiding WE");
1341  return false;
1342  }
1343  }
1344  }
1345 
1346  GuidingSpeedNP.s = IPS_OK;
1347  IDSetNumber(&GuidingSpeedNP, "Guiding speed set to RA:%f DEC:%f", GuidingSpeedN[GUIDING_WE].value, GuidingSpeedN[GUIDING_NS].value);
1348 
1349  return true;
1350  }
1351  if (!strcmp(name, SafetyLimitsNP.name))
1352  {
1353  double eastSafeD = 0;
1354  double westSafeD = 0;
1355  double westGotoD = 0;
1356 
1357  for(int i = 0; i<n; ++i) {
1358  if (!strcmp(names[i], SafetyLimitsN[EAST_SAFETY].name))
1359  {
1360  eastSafeD = values[i];
1361  }
1362  if (!strcmp(names[i], SafetyLimitsN[WEST_SAFETY].name))
1363  {
1364  westSafeD = values[i];
1365  }
1366  if (!strcmp(names[i], SafetyLimitsN[WEST_GOTO].name))
1367  {
1368  westGotoD = values[i];
1369  }
1370  }
1371  char eastSafe[32] = {0};
1372  SafetyLimitsN[EAST_SAFETY].value = eastSafeD;
1373  fs_sexa(eastSafe, eastSafeD, 2, 60);
1374 
1375  char westSafe[32] = {0};
1376  SafetyLimitsN[WEST_SAFETY].value = westSafeD;
1377  fs_sexa(westSafe, westSafeD, 2, 60);
1378 
1379  char westGoto[32] = {0};
1380  SafetyLimitsN[WEST_GOTO].value = westGotoD;
1381  fs_sexa(westGoto, westGotoD, 2, 60);
1382 
1383  char *colon = strchr(eastSafe, ':');
1384  if(colon) {
1385  *colon = 'd';
1386  }
1387  colon = strchr(westSafe, ':');
1388  if(colon) {
1389  *colon = 'd';
1390  }
1391  colon = strchr(westGoto, ':');
1392  if(colon) {
1393  *colon = 'd';
1394  }
1395 
1396  if (!isSimulation() &&
1397  (!setGeminiProperty(EAST_SAFETY_LIMIT_ID, eastSafe) ||
1398  !setGeminiProperty(WEST_SAFETY_LIMIT_ID, westSafe) ||
1399  !setGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, westGoto)))
1400  {
1401  SafetyLimitsNP.s = IPS_ALERT;
1402  IDSetNumber(&SafetyLimitsNP, "Error Setting Limits");
1403  return false;
1404  }
1405  IDSetNumber(&SafetyLimitsNP, "Limits EastSafe:%s, WestSafe:%s, WestGoto:%s", eastSafe, westSafe, westGoto);
1406  SafetyLimitsNP.s = IPS_OK;
1407  return true;
1408  }
1409  if (gemini_software_level_ >= 6.0 && !strcmp(name, FlipPositionNP.name))
1410  {
1411  double eastD = 0;
1412  double westD = 0;
1413 
1414  for(int i = 0; i<n; ++i) {
1415  if (!strcmp(names[i], FlipPositionN[FLIP_EAST_VALUE].name))
1416  {
1417  eastD = values[i];
1418  }
1419  if (!strcmp(names[i], FlipPositionN[FLIP_WEST_VALUE].name))
1420  {
1421  westD = values[i];
1422  }
1423  }
1424  char east[32] = {0};
1425  FlipPositionN[FLIP_EAST_VALUE].value = eastD;
1426  fs_sexa(east, eastD, 2, 60);
1427 
1428  char west[32] = {0};
1429  FlipPositionN[FLIP_WEST_VALUE].value = westD;
1430  fs_sexa(west, westD, 2, 60);
1431 
1432  char *colon = strchr(east, ':');
1433  if(colon) {
1434  *colon = 'd';
1435  }
1436  colon = strchr(west, ':');
1437  if(colon) {
1438  *colon = 'd';
1439  }
1440  if (!isSimulation() &&
1441  (!setGeminiProperty(FLIP_POINT_EAST_ID, east) ||
1442  !setGeminiProperty(FLIP_POINT_WEST_ID, west)))
1443  {
1444  FlipPositionNP.s = IPS_ALERT;
1445  IDSetNumber(&FlipPositionNP, "Error Setting Flip Points");
1446  return false;
1447  }
1448  IDSetNumber(&FlipPositionNP, "FlipPoints East:%s, West:%s", east, west);
1449  FlipPositionNP.s = IPS_OK;
1450  return true;
1451  }
1452  if (!strcmp(name, CenteringSpeedNP.name))
1453  {
1454  LOGF_DEBUG("Trying to set centering speed of: %f", values[0]);
1455 
1456  if (!isSimulation() && !setGeminiProperty(CENTERING_SPEED_ID, valueString))
1457  {
1458  CenteringSpeedNP.s = IPS_ALERT;
1459  IDSetNumber(&CenteringSpeedNP, "Error setting centering speed");
1460  return false;
1461  }
1462 
1463  CenteringSpeedNP.s = IPS_OK;
1464  CenteringSpeedN[0].value = values[0];
1465  IDSetNumber(&CenteringSpeedNP, "Centering speed set to %f", values[0]);
1466 
1467  return true;
1468  }
1469  if (!strcmp(name, PECMaxStepsNP.name))
1470  {
1471  PECMaxStepsNP.s = IPS_OK;
1472  PECMaxStepsN[0].value = values[0];
1473  IDSetNumber(&PECMaxStepsNP, "Max steps set to %f", values[0]);
1474  return true;
1475  }
1476  if (gemini_software_level_ >= 5.0 && !strcmp(name, PECGuidingSpeedNP.name))
1477  {
1478  PECGuidingSpeedNP.s = IPS_OK;
1479  PECGuidingSpeedN[0].value = values[0];
1480  IDSetNumber(&PECGuidingSpeedNP, "Guiding Speed set to %f", values[0]);
1481  return true;
1482  }
1483  }
1484 
1485  // If we didn't process it, continue up the chain, let somebody else give it a shot
1486  return LX200Generic::ISNewNumber(dev, name, values, names, n);
1487 }
1488 
1490 {
1491  if (isSimulation())
1492  return true;
1493 
1494  // Response
1495  char response[8] = { 0 };
1496  int rc = 0, nbytes_read = 0, nbytes_written = 0;
1497 
1498  LOGF_DEBUG("CMD: <%#02X>", 0x06);
1499 
1500  tcflush(PortFD, TCIFLUSH);
1501 
1502  char ack[1] = { 0x06 };
1503 
1504  if ((rc = tty_write(PortFD, ack, 1, &nbytes_written)) != TTY_OK)
1505  {
1506  char errmsg[256];
1507  tty_error_msg(rc, errmsg, 256);
1508  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1509  return false;
1510  }
1511 
1512  // Read response
1513  if ((rc = tty_read_section(PortFD, response, '#', GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1514  {
1515  char errmsg[256];
1516  tty_error_msg(rc, errmsg, 256);
1517  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1518  return false;
1519  }
1520 
1521  //response[1] = '\0';
1522 
1523  tcflush(PortFD, TCIFLUSH);
1524 
1525  LOGF_DEBUG("RES: <%s>", response);
1526 
1527  // If waiting for selection of startup mode, let us select it
1528  if (response[0] == 'b')
1529  {
1530  LOG_DEBUG("Mount is waiting for selection of the startup mode.");
1531  char cmd[4] = "bC#";
1532  int startupMode = IUFindOnSwitchIndex(&StartupModeSP);
1533  if (startupMode == WARM_START)
1534  strncpy(cmd, "bW#", 4);
1535  else if (startupMode == WARM_RESTART)
1536  strncpy(cmd, "bR#", 4);
1537 
1538  LOGF_DEBUG("CMD: <%s>", cmd);
1539 
1540  if ((rc = tty_write(PortFD, cmd, 4, &nbytes_written)) != TTY_OK)
1541  {
1542  char errmsg[256];
1543  tty_error_msg(rc, errmsg, 256);
1544  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1545  return false;
1546  }
1547 
1548  tcflush(PortFD, TCIFLUSH);
1549 
1550  // Send ack again and check response
1551  return checkConnection();
1552  }
1553  else if (response[0] == 'B')
1554  {
1555  LOG_DEBUG("Initial startup message is being displayed.");
1556  }
1557  else if (response[0] == 'S')
1558  {
1559  LOG_DEBUG("Cold start in progress.");
1560  }
1561  else if (response[0] == 'G')
1562  {
1563  updateParkingState();
1564  updateMovementState();
1565  LOG_DEBUG("Startup complete with equatorial mount selected.");
1566  }
1567  else if (response[0] == 'A')
1568  {
1569  LOG_DEBUG("Startup complete with Alt-Az mount selected.");
1570  }
1571 
1572  return true;
1573 }
1574 
1576 {
1577  LX200Gemini::MovementState movementState = getMovementState();
1578 
1579  if (movementState == TRACKING || movementState == GUIDING || movementState == NO_MOVEMENT)
1580  return true;
1581  else
1582  return false;
1583 }
1584 
1586 {
1587  LOGF_DEBUG("ReadScopeStatus: TrackState is <%d>", TrackState);
1588 
1589  if (!isConnected())
1590  return false;
1591 
1592  if (isSimulation())
1594 
1595  if (TrackState == SCOPE_SLEWING)
1596  {
1597  updateMovementState();
1598 
1599  EqNP.s = IPS_BUSY;
1600  IDSetNumber(&EqNP, nullptr);
1601 
1602  // Check if LX200 is done slewing
1603  if (isSlewComplete())
1604  {
1605  // Set slew mode to "Centering"
1608  IDSetSwitch(&SlewRateSP, nullptr);
1609 
1610  EqNP.s = IPS_OK;
1611  IDSetNumber(&EqNP, nullptr);
1612 
1613  if (TrackState == SCOPE_IDLE) {
1614  SetTrackEnabled(true);
1615  updateMovementState();
1616  }
1617 
1618  LOG_INFO("Slew is complete. Tracking...");
1619  }
1620  }
1621  else if (TrackState == SCOPE_PARKING)
1622  {
1623  updateParkingState();
1624 
1625  if (isSlewComplete())
1626  {
1627  LOG_DEBUG("Park is complete ...");
1628  SetParked(true);
1629  sleepMount();
1630 
1631  EqNP.s = IPS_IDLE;
1632  IDSetNumber(&EqNP, nullptr);
1633 
1634  return true;
1635  }
1636  }
1637 
1638  if (getLX200RA(PortFD, &currentRA) < 0 || getLX200DEC(PortFD, &currentDEC) < 0)
1639  {
1640  LOG_ERROR("Error reading RA/DEC.");
1641  return false;
1642  }
1643 
1645 
1646  syncSideOfPier();
1647  syncState();
1648  return true;
1649 }
1650 
1651 void LX200Gemini::syncSideOfPier()
1652 {
1653  // Send ':Gm#'
1654  const char *cmd = ":Gm#";
1655  // Response
1656  char response[8] = { 0 };
1657  int rc = 0, nbytes_read = 0, nbytes_written = 0;
1658 
1659  LOGF_DEBUG("CMD: <%s>", cmd);
1660 
1661  tcflush(PortFD, TCIOFLUSH);
1662 
1663  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1664  {
1665  char errmsg[256];
1666  tty_error_msg(rc, errmsg, 256);
1667  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1668  return;
1669  }
1670 
1671  if ((rc = tty_read_section(PortFD, response, '#', GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1672  {
1673  char errmsg[256];
1674  tty_error_msg(rc, errmsg, 256);
1675  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1676  return;
1677  }
1678 
1679  response[nbytes_read - 1] = '\0';
1680 
1681  tcflush(PortFD, TCIOFLUSH);
1682 
1683  //LOGF_DEBUG("RES: <%s>", response);
1684 
1685  // fix to pier side read from the mount using the hour angle as a guide
1686  // see https://www.indilib.org/forum/general/6785-side-of-pier-problem-bug.html?start=12#52492
1687  // for a description of the problem and the proposed fix
1688  //
1689  auto lst = get_local_sidereal_time(this->LocationN[LOCATION_LONGITUDE].value);
1690  auto ha = rangeHA(lst - currentRA);
1691  auto pointingState = PIER_UNKNOWN;
1692 
1693  if (ha >= -5.0 && ha <= 5.0)
1694  {
1695  // mount pier side is used unchanged
1696  pointingState = response[0] == 'E' ? PIER_EAST : PIER_WEST;
1697  }
1698  else if (ha <= -7.0 || ha >= 7.0)
1699  {
1700  // mount pier side is reversed
1701  pointingState = response[0] == 'W' ? PIER_EAST : PIER_WEST;
1702  }
1703  else
1704  {
1705  // use hour angle because the pier side changes spontaneously near +-6h
1706  pointingState = ha > 0 ? PIER_EAST : PIER_WEST;
1707  }
1708 
1709  LOGF_DEBUG("RES: <%s>, lst %f, ha %f, pierSide %d", response, lst, ha, pointingState);
1710 
1711  setPierSide(pointingState);
1712 }
1713 
1714 
1716 {
1717  char cmd[6] = ":hP#";
1718 
1719  int parkSetting = IUFindOnSwitchIndex(&ParkSettingsSP);
1720 
1721  if (parkSetting == PARK_STARTUP)
1722  strncpy(cmd, ":hC#", 5);
1723  else if (parkSetting == PARK_ZENITH)
1724  strncpy(cmd, ":hZ#", 5);
1725 
1726  // Response
1727  int rc = 0, nbytes_written = 0;
1728 
1729  LOGF_DEBUG("CMD: <%s>", cmd);
1730 
1731  tcflush(PortFD, TCIOFLUSH);
1732 
1733  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1734  {
1735  char errmsg[256];
1736  tty_error_msg(rc, errmsg, 256);
1737  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1738  return false;
1739  }
1740 
1741  tcflush(PortFD, TCIOFLUSH);
1742 
1743  ParkSP.s = IPS_BUSY;
1745 
1746  updateParkingState();
1747  return true;
1748 }
1749 
1751 {
1752  wakeupMount();
1753 
1754  SetParked(false);
1755  SetTrackEnabled(true);
1756 
1757  updateParkingState();
1758  updateMovementState();
1759  return true;
1760 }
1761 
1762 bool LX200Gemini::sleepMount()
1763 {
1764  const char *cmd = ":hN#";
1765 
1766  // Response
1767  int rc = 0, nbytes_written = 0;
1768 
1769  LOGF_DEBUG("CMD: <%s>", cmd);
1770 
1771  tcflush(PortFD, TCIOFLUSH);
1772 
1773  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1774  {
1775  char errmsg[256];
1776  tty_error_msg(rc, errmsg, 256);
1777  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1778  return false;
1779  }
1780 
1781  tcflush(PortFD, TCIOFLUSH);
1782 
1783  LOG_INFO("Mount is sleeping...");
1784  return true;
1785 }
1786 
1787 bool LX200Gemini::wakeupMount()
1788 {
1789  const char *cmd = ":hW#";
1790 
1791  // Response
1792  int rc = 0, nbytes_written = 0;
1793 
1794  LOGF_DEBUG("CMD: <%s>", cmd);
1795 
1796  tcflush(PortFD, TCIOFLUSH);
1797 
1798  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1799  {
1800  char errmsg[256];
1801  tty_error_msg(rc, errmsg, 256);
1802  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1803  return false;
1804  }
1805 
1806  tcflush(PortFD, TCIOFLUSH);
1807 
1808  LOG_INFO("Mount is awake...");
1809  return true;
1810 }
1811 
1812 void LX200Gemini::setTrackState(INDI::Telescope::TelescopeStatus state)
1813 {
1814  if (TrackState != state)
1815  TrackState = state;
1816 }
1817 
1818 void LX200Gemini::updateMovementState()
1819 {
1820  LX200Gemini::MovementState movementState = getMovementState();
1821 
1822  switch (movementState)
1823  {
1824  case NO_MOVEMENT:
1825  if (priorParkingState == PARKED)
1826  setTrackState(SCOPE_PARKED);
1827  else
1828  setTrackState(SCOPE_IDLE);
1829  break;
1830 
1831  case TRACKING:
1832  case GUIDING:
1833  setTrackState(SCOPE_TRACKING);
1834  break;
1835 
1836  case CENTERING:
1837  case SLEWING:
1838  setTrackState(SCOPE_SLEWING);
1839  break;
1840 
1841  case STALLED:
1842  setTrackState(SCOPE_IDLE);
1843  break;
1844  }
1845 }
1846 
1847 void LX200Gemini::updateParkingState()
1848 {
1849  LX200Gemini::ParkingState parkingState = getParkingState();
1850 
1851  if (parkingState != priorParkingState)
1852  {
1853  if (parkingState == PARKED)
1854  SetParked(true);
1855  else if (parkingState == NOT_PARKED)
1856  SetParked(false);
1857  }
1858  priorParkingState = parkingState;
1859 }
1860 
1861 LX200Gemini::MovementState LX200Gemini::getMovementState()
1862 {
1863  const char *cmd = ":Gv#";
1864  char response[2] = { 0 };
1865  int rc = 0, nbytes_read = 0, nbytes_written = 0;
1866 
1867  LOGF_DEBUG("CMD: <%s>", cmd);
1868 
1869  tcflush(PortFD, TCIOFLUSH);
1870 
1871  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1872  {
1873  char errmsg[256];
1874  tty_error_msg(rc, errmsg, 256);
1875  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1876  return LX200Gemini::MovementState::NO_MOVEMENT;
1877  }
1878 
1879  if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1880  {
1881  char errmsg[256];
1882  tty_error_msg(rc, errmsg, 256);
1883  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1884  return LX200Gemini::MovementState::NO_MOVEMENT;
1885  }
1886 
1887  response[1] = '\0';
1888 
1889  tcflush(PortFD, TCIOFLUSH);
1890 
1891  LOGF_DEBUG("RES: <%s>", response);
1892 
1893  switch (response[0])
1894  {
1895  case 'N':
1896  return LX200Gemini::MovementState::NO_MOVEMENT;
1897 
1898  case 'T':
1899  return LX200Gemini::MovementState::TRACKING;
1900 
1901  case 'G':
1902  return LX200Gemini::MovementState::GUIDING;
1903 
1904  case 'C':
1905  return LX200Gemini::MovementState::CENTERING;
1906 
1907  case 'S':
1908  return LX200Gemini::MovementState::SLEWING;
1909 
1910  case '!':
1911  return LX200Gemini::MovementState::STALLED;
1912 
1913  default:
1914  return LX200Gemini::MovementState::NO_MOVEMENT;
1915  }
1916 }
1917 
1918 LX200Gemini::ParkingState LX200Gemini::getParkingState()
1919 {
1920  const char *cmd = ":h?#";
1921  char response[2] = { 0 };
1922  int rc = 0, nbytes_read = 0, nbytes_written = 0;
1923 
1924  LOGF_DEBUG("CMD: <%s>", cmd);
1925 
1926  tcflush(PortFD, TCIOFLUSH);
1927 
1928  if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1929  {
1930  char errmsg[256];
1931  tty_error_msg(rc, errmsg, 256);
1932  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1933  return LX200Gemini::ParkingState::NOT_PARKED;
1934  }
1935 
1936  if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1937  {
1938  char errmsg[256];
1939  tty_error_msg(rc, errmsg, 256);
1940  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1941  return LX200Gemini::ParkingState::NOT_PARKED;
1942  }
1943 
1944  response[1] = '\0';
1945 
1946  tcflush(PortFD, TCIOFLUSH);
1947 
1948  LOGF_DEBUG("RES: <%s>", response);
1949 
1950  switch (response[0])
1951  {
1952  case '0':
1953  return LX200Gemini::ParkingState::NOT_PARKED;
1954 
1955  case '1':
1956  return LX200Gemini::ParkingState::PARKED;
1957 
1958  case '2':
1959  return LX200Gemini::ParkingState::PARK_IN_PROGRESS;
1960 
1961  default:
1962  return LX200Gemini::ParkingState::NOT_PARKED;
1963  }
1964 }
1965 
1967 {
1969 
1970  IUSaveConfigSwitch(fp, &StartupModeSP);
1971  IUSaveConfigSwitch(fp, &ParkSettingsSP);
1972 
1973  return true;
1974 }
1975 
1976 bool LX200Gemini::getGeminiProperty(uint32_t propertyNumber, char* value)
1977 {
1978  int rc = TTY_OK;
1979  int nbytes = 0;
1980  char prefix[16] = {0};
1981  char cmd[16] = {0};
1982 
1983  switch(propertyNumber) {
1984  case MOVE_SPEED_ID:
1985  case GUIDING_SPEED_RA_ID:
1986  case GUIDING_SPEED_DEC_ID:
1987  case PEC_START_TRAINING_ID:
1988  case PEC_ABORT_TRAINING_ID:
1989  case PEC_REPLAY_ON_ID:
1990  case PEC_REPLAY_OFF_ID:
1991  if(gemini_software_level_ < 5.0)
1992  {
1993  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
1994  return false;
1995  }
1996  break;
1997  case PEC_ENABLE_AT_BOOT_ID:
1998  if(gemini_software_level_ < 5.2)
1999  {
2000  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2001  return false;
2002  }
2003  break;
2004  case FLIP_POINT_EAST_ID:
2005  case FLIP_POINT_WEST_ID:
2008  case SERVO_FIRMWARE:
2009  if(gemini_software_level_ < 6)
2010  {
2011  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2012  return false;
2013  }
2014  break;
2015  default:
2016  ;
2017  }
2018 
2019  snprintf(prefix, 16, "<%d:", propertyNumber);
2020 
2021  uint8_t checksum = calculateChecksum(prefix);
2022 
2023  snprintf(cmd, 16, "%s%c#", prefix, checksum);
2024 
2025  LOGF_DEBUG("CMD: <%s>", cmd);
2026 
2027  if ((rc = tty_write_string(PortFD, cmd, &nbytes)) != TTY_OK)
2028  {
2029  char errmsg[256];
2030  tty_error_msg(rc, errmsg, 256);
2031  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2032  return false;
2033  }
2034 
2035  if ((rc = tty_read_section(PortFD, value, '#', GEMINI_TIMEOUT, &nbytes)) != TTY_OK)
2036  {
2037  char errmsg[256];
2038  tty_error_msg(rc, errmsg, 256);
2039  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2040  return false;
2041  }
2042 
2043  value[nbytes - 1] = '\0';
2044 
2045  tcflush(PortFD, TCIFLUSH);
2046 
2047  LOGF_DEBUG("RES: <%s>", value);
2048  return true;
2049 }
2050 
2051 int LX200Gemini::SendPulseCmd(int8_t direction, uint32_t duration_msec)
2052 {
2053  return ::SendPulseCmd(PortFD, direction, duration_msec, true, 1000);
2054 }
2055 
2056 bool LX200Gemini::Flip(double ra, double dec)
2057 {
2058  return GotoInternal(ra, dec, true);
2059 }
2060 
2061 bool LX200Gemini::Goto(double ra, double dec)
2062 {
2063  return GotoInternal(ra, dec, false);
2064 }
2065 
2066 int LX200Gemini::Flip(int fd)
2067 {
2068  DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "<%s>", __FUNCTION__);
2069  char FlipNum[17] = {0};
2070  int error_type;
2071  int nbytes_write = 0, nbytes_read = 0;
2072  const char *command = ":MM#";
2073 
2074  DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "CMD <%s>", command);
2075 
2076  // Gemini Serial Command
2077  // Returns
2078  // 0 Flip is Possible
2079  // 1 Object below horizon.#
2080  // 2 No object selected.#
2081  // 3 Manual Control.#
2082  // 4 Position unreachable.#
2083  // 5 Not aligned.#
2084  // 6 Outside Limits.#
2085  // 7 Rejected - Mount is parked!#
2086 
2087  if ((error_type = tty_write_string(fd, command, &nbytes_write)) != TTY_OK)
2088  return error_type;
2089 
2090  error_type = tty_read(fd, FlipNum, 1, LX200_TIMEOUT, &nbytes_read);
2091 
2092  if (nbytes_read < 1)
2093  {
2094  DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "RES ERROR <%d>", error_type);
2095  return error_type;
2096  }
2097 
2098  /* We don't need to read the string message, just return corresponding error code */
2099  tcflush(fd, TCIFLUSH);
2100 
2101  DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "RES <%c>", FlipNum[0]);
2102 
2103  error_type = FlipNum[0] - '0';
2104  if ((error_type >= 0) && (error_type <= 9))
2105  {
2106  return error_type;
2107  }
2108  else
2109  {
2110  return -1;
2111  }
2112 }
2113 
2114 bool LX200Gemini::GotoInternal(double ra, double dec, bool flip)
2115 {
2116  const struct timespec timeout = {0, 100000000L};
2117 
2118  targetRA = ra;
2119  targetDEC = dec;
2120  char RAStr[64] = {0}, DecStr[64] = {0};
2121  int fracbase = 0;
2122 
2123  switch (getLX200EquatorialFormat())
2124  {
2126  fracbase = 360000;
2127  break;
2128  case LX200_EQ_LONG_FORMAT:
2129  case LX200_EQ_SHORT_FORMAT:
2130  default:
2131  fracbase = 3600;
2132  break;
2133  }
2134 
2135  fs_sexa(RAStr, targetRA, 2, fracbase);
2136  fs_sexa(DecStr, targetDEC, 2, fracbase);
2137 
2138  // If moving, let's stop it first.
2139  if (EqNP.s == IPS_BUSY)
2140  {
2141  if (!isSimulation() && abortSlew(PortFD) < 0)
2142  {
2143  AbortSP.s = IPS_ALERT;
2144  IDSetSwitch(&AbortSP, "Abort slew failed.");
2145  return false;
2146  }
2147 
2148  AbortSP.s = IPS_OK;
2149  EqNP.s = IPS_IDLE;
2150  IDSetSwitch(&AbortSP, "Slew aborted.");
2151  IDSetNumber(&EqNP, nullptr);
2152 
2154  {
2157  EqNP.s = IPS_IDLE;
2160  IDSetSwitch(&MovementNSSP, nullptr);
2161  IDSetSwitch(&MovementWESP, nullptr);
2162  }
2163 
2164  // sleep for 100 mseconds
2165  nanosleep(&timeout, nullptr);
2166  }
2167 
2168  if (!isSimulation())
2169  {
2170  if (setObjectRA(PortFD, targetRA) < 0 || (setObjectDEC(PortFD, targetDEC)) < 0)
2171  {
2172  EqNP.s = IPS_ALERT;
2173  IDSetNumber(&EqNP, "Error setting RA/DEC.");
2174  return false;
2175  }
2176 
2177  int err = 0;
2178 
2179  /* Slew reads the '0', that is not the end of the slew */
2180  if ((err = flip ? Flip(PortFD) : Slew(PortFD)))
2181  {
2182  LOGF_ERROR("Error %s to JNow RA %s - DEC %s", flip ? "Flipping" : "Slewing", RAStr, DecStr);
2183  slewError(err);
2184  return false;
2185  }
2186  }
2187 
2189  //EqNP.s = IPS_BUSY;
2190 
2191  LOGF_INFO("%s to RA: %s - DEC: %s", flip ? "Flipping" : "Slewing", RAStr, DecStr);
2192 
2193  return true;
2194 }
2195 
2196 bool LX200Gemini::setGeminiProperty(uint32_t propertyNumber, char* value)
2197 {
2198  int rc = TTY_OK;
2199  int nbytes_written = 0;
2200  char prefix[16] = {0};
2201  char cmd[16] = {0};
2202  switch(propertyNumber) {
2203  case MOVE_SPEED_ID:
2204  case GUIDING_SPEED_RA_ID:
2205  case GUIDING_SPEED_DEC_ID:
2206  case PEC_START_TRAINING_ID:
2207  case PEC_ABORT_TRAINING_ID:
2208  case PEC_REPLAY_ON_ID:
2209  case PEC_REPLAY_OFF_ID:
2210  if(gemini_software_level_ < 5.0)
2211  {
2212  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2213  return false;
2214  }
2215  break;
2216  case PEC_ENABLE_AT_BOOT_ID:
2217  if(gemini_software_level_ < 5.2)
2218  {
2219  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2220  return false;
2221  }
2222  break;
2224  case SERVO_FIRMWARE:
2225  if(gemini_software_level_ < 6)
2226  {
2227  LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2228  return false;
2229  }
2230  break;
2231  default:
2232  ;
2233  }
2234 
2235  snprintf(prefix, 16, ">%d:%s", propertyNumber, value);
2236 
2237  uint8_t checksum = calculateChecksum(prefix);
2238 
2239  snprintf(cmd, 16, "%s%c#", prefix, checksum);
2240 
2241  LOGF_DEBUG("CMD: <%s>", cmd);
2242 
2243  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
2244  {
2245  char errmsg[256];
2246  tty_error_msg(rc, errmsg, 256);
2247  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2248  return false;
2249  }
2250 
2251  tcflush(PortFD, TCIFLUSH);
2252 
2253  return true;
2254 }
2255 
2256 bool LX200Gemini::SetTrackMode(uint8_t mode)
2257 {
2258  char empty[] = "";
2259  return setGeminiProperty(131 + mode, empty);
2260 }
2261 
2263 {
2264  return SetTrackMode(enabled ? IUFindOnSwitchIndex(&TrackModeSP) : GEMINI_TRACK_TERRESTRIAL);
2265 }
2266 
2267 uint8_t LX200Gemini::calculateChecksum(char *cmd)
2268 {
2269  uint8_t result = cmd[0];
2270 
2271  for (size_t i = 1; i < strlen(cmd); i++)
2272  result = result ^ cmd[i];
2273 
2274  result = result % 128;
2275  result += 64;
2276 
2277  return result;
2278 }
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
Connection::Interface * getActiveConnection()
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
TelescopeStatus TrackState
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
ISwitchVectorProperty PECStateSP
ISwitchVectorProperty TrackModeSP
ISwitchVectorProperty SlewRateSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
void setPECState(TelescopePECState state)
ISwitchVectorProperty ParkSP
ISwitch PECStateS[2]
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
ISwitch * TrackModeS
void setPierSide(TelescopePierSide side)
ISwitch * SlewRateS
ISwitchVectorProperty MovementWESP
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: lx200gemini.cpp:93
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool ISNewText(const char *dev, const char *name, char **texts, char **names, int n) override
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool checkConnection() override
virtual const char * getDefaultName() override
Definition: lx200gemini.cpp:88
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool isSlewComplete() override
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool Flip(double ra, double dec) override
Move and flip the scope to the supplied RA and DEC coordinates.
virtual bool Park() override
Park the telescope to its home position.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void slewError(int slewCode)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void setLX200Capability(uint32_t cap)
const char * GUIDE_TAB
GUIDE_TAB Where all the properties for guiding are located.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
double ra
double dec
#define MAX_VALUE_LENGTH
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
@ 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_NOFMANY
Definition: indiapi.h:175
@ 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 f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition: indicom.c:205
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition: indicom.c:1225
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition: indicom.c:141
void tty_set_gemini_udp_format(int enabled)
Definition: indicom.c:365
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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 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 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
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
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 IUGetConfigOnSwitch(const ISwitchVectorProperty *property, int *index)
IUGetConfigOnSwitch Opens configuration file and reads a single switch vector property to find the in...
Definition: indidriver.c:592
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#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 DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
int fd
Definition: intelliscope.c:43
int SendPulseCmd(int fd, int direction, int duration_msec, bool wait_after_command, int max_wait_ms)
int setObjectRA(int fd, double ra, bool addSpace)
int getLX200EquatorialFormat()
int setObjectDEC(int fd, double dec, bool addSpace)
int abortSlew(int fd)
int Slew(int fd)
#define getVersionDate(fd, x)
Definition: lx200driver.h:129
#define getVersionTime(fd, x)
Definition: lx200driver.h:130
#define getLX200DEC(fd, x)
Definition: lx200driver.h:118
#define getVersionNumber(fd, x)
Definition: lx200driver.h:132
#define getLX200RA(fd, x)
Definition: lx200driver.h:117
#define getProductName(fd, x)
Definition: lx200driver.h:133
@ LX200_EQ_LONGER_FORMAT
Definition: lx200driver.h:52
@ LX200_EQ_SHORT_FORMAT
Definition: lx200driver.h:50
@ LX200_EQ_LONG_FORMAT
Definition: lx200driver.h:51
#define GUIDING_SPEED_RA_ID
Definition: lx200gemini.cpp:50
#define CENTERING_SPEED_ID
Definition: lx200gemini.cpp:52
#define FLIP_POINT_EAST_ID
Definition: lx200gemini.cpp:64
#define FLIP_POINT_WEST_ID
Definition: lx200gemini.cpp:65
#define EAST_SAFETY_LIMIT_ID
Definition: lx200gemini.cpp:68
#define PEC_MAX_STEPS_ID
Definition: lx200gemini.cpp:54
#define SERVO_POINTING_PRECISION_ID
Definition: lx200gemini.cpp:53
#define PEC_START_TRAINING_ID
Definition: lx200gemini.cpp:57
#define PEC_GUIDING_SPEED_ID
Definition: lx200gemini.cpp:62
#define MANUAL_SLEWING_SPEED_ID
Definition: lx200gemini.cpp:46
#define WEST_SAFETY_LIMIT_ID
Definition: lx200gemini.cpp:69
#define PEC_ENABLE_AT_BOOT_ID
Definition: lx200gemini.cpp:61
#define GOTO_SLEWING_SPEED_ID
Definition: lx200gemini.cpp:47
#define PEC_ABORT_TRAINING_ID
Definition: lx200gemini.cpp:58
#define SET_SAFETY_LIMIT_TO_CURRENT_ID
Definition: lx200gemini.cpp:67
#define MOVE_SPEED_ID
Definition: lx200gemini.cpp:48
#define GUIDING_SPEED_ID
Definition: lx200gemini.cpp:49
#define GUIDING_SPEED_DEC_ID
Definition: lx200gemini.cpp:51
#define SERVO_FIRMWARE
Definition: lx200gemini.cpp:63
#define FLIP_POINTS_ENABLED_ID
Definition: lx200gemini.cpp:66
#define PEC_REPLAY_OFF_ID
Definition: lx200gemini.cpp:60
#define PEC_COUNTER_ID
Definition: lx200gemini.cpp:55
#define WEST_GOTO_SAFETY_LIMIT_ID
Definition: lx200gemini.cpp:70
#define PEC_STATUS_ID
Definition: lx200gemini.cpp:56
#define PEC_REPLAY_ON_ID
Definition: lx200gemini.cpp:59
#define LX200_TIMEOUT
Definition: lx200gemini.cpp:44
const char * CONNECTION_TAB
@ value
the parser finished reading a JSON value
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250