Instrument Neutral Distributed Interface INDI 2.2.5
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
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
105void 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",
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 TrackModeSP[GEMINI_TRACK_SIDEREAL].fill("TRACK_SIDEREAL", "Sidereal", ISS_ON);
170 TrackModeSP[GEMINI_TRACK_KING].fill("TRACK_CUSTOM", "King", ISS_OFF);
171 TrackModeSP[GEMINI_TRACK_LUNAR].fill("TRACK_LUNAR", "Lunar", ISS_OFF);
172 TrackModeSP[GEMINI_TRACK_SOLAR].fill("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
240
241 gemini_software_level_ = 0.0;
242
243 return true;
244}
245
246bool LX200Gemini::getRefractionJNOW(int &data)
247{
248 if (isSimulation())
249 return true;
250
251 // Response
252 char response[2] = { 0 };
253 int rc = 0, nbytes_read = 0, nbytes_written = 0;
254
255
256 tcflush(PortFD, TCIFLUSH);
257
258 const char *cmd = ":p?#";
259 data = 0;
260 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
261 {
262 char errmsg[256];
263 tty_error_msg(rc, errmsg, 256);
264 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
265 return false;
266 }
267
268 // Read response
269 if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
270 {
271 char errmsg[256];
272 tty_error_msg(rc, errmsg, 256);
273 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
274 return false;
275 }
276 tcflush(PortFD, TCIFLUSH);
277 response[1] = '\0';
278
279 data = atoi(response);
280 return true;
281}
282
283bool LX200Gemini::getRefraction(bool &on)
284{
285 if (isSimulation())
286 return true;
287
288 int data = 0;
289
290 bool success = getRefractionJNOW(data);
291
292 if(data & 2)
293 {
294 on = true;
295 if((data != 2) && (data != 0))
296 {
297 LOGF_WARN("Mount Precess being reset to JNOW: %i", data);
298 }
299 return setRefraction(2);
300 }
301 else
302 {
303 on = false;
304 if((data != 2) && (data != 0))
305 {
306 LOGF_WARN("Mount Precess being reset to JNOW: %i", data);
307 }
308 return setRefraction(0);
309 }
310
311 return success;
312}
313
314bool LX200Gemini::setRefraction(int data)
315{
316 if (isSimulation())
317 return true;
318
319 int rc = 0, nbytes_written = 0;
320
321 tcflush(PortFD, TCIFLUSH);
322
323 char cmd[5] = { 0 };
324 snprintf(cmd, 5, ":p%i#", data);
325 data = 0;
326 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
327 {
328 char errmsg[256];
329 tty_error_msg(rc, errmsg, 256);
330 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
331 return false;
332 }
333 tcflush(PortFD, TCIFLUSH);
334
335 return true;
336}
337
338bool LX200Gemini::setRefraction(bool on)
339{
340 if (isSimulation())
341 return true;
342
343 int data;
344
345 getRefractionJNOW(data);
346
347 if(on)
348 {
349 if((data != 2) && (data != 0))
350 {
351 LOGF_WARN("Mount Precess being reset to JNOW %i ", data);
352 }
353 return setRefraction(2);
354 }
355 else
356 {
357 if((data != 2) && (data != 0))
358 {
359 LOGF_WARN("Mount Precess being reset to JNOW %i", data);
360 }
361 return setRefraction(0);
362 }
363}
364
365void LX200Gemini::syncState()
366{
367 const int MAX_VALUE_LENGTH = 32;
368 char value[MAX_VALUE_LENGTH] = {0};
369 char value2[MAX_VALUE_LENGTH] = {0};
370 char value3[MAX_VALUE_LENGTH] = {0};
371
372 if (gemini_software_level_ >= 5.0)
373 {
374 if (getGeminiProperty(GUIDING_SPEED_RA_ID, value))
375 {
376 float guiding_value;
377 sscanf(value, "%f", &guiding_value);
378 GuidingSpeedN[GUIDING_WE].value = guiding_value;
379 IDSetNumber(&GuidingSpeedNP, nullptr);
380 }
381 else
382 {
383 GuidingSpeedNP.s = IPS_ALERT;
384 IDSetNumber(&GuidingSpeedNP, nullptr);
385 }
386 if (getGeminiProperty(GUIDING_SPEED_DEC_ID, value))
387 {
388 float guiding_value;
389 sscanf(value, "%f", &guiding_value);
390 GuidingSpeedN[GUIDING_NS].value = guiding_value;
391 IDSetNumber(&GuidingSpeedNP, nullptr);
392 }
393 else
394 {
395 GuidingSpeedNP.s = IPS_ALERT;
396 IDSetNumber(&GuidingSpeedNP, nullptr);
397 }
398 }
399 if (gemini_software_level_ >= 5.2)
400 {
401 if (getGeminiProperty(PEC_ENABLE_AT_BOOT_ID, value))
402 {
403 uint32_t pec_at_boot_value;
404 sscanf(value, "%u", &pec_at_boot_value);
405 if(pec_at_boot_value)
406 {
407 PECEnableAtBootS[0].s = ISS_ON;
408 }
409 else
410 {
411 PECEnableAtBootS[0].s = ISS_OFF;
412 }
413 PECEnableAtBootSP.s = IPS_OK;
414 }
415 else
416 {
417 PECEnableAtBootSP.s = IPS_ALERT;
418 }
419 IDSetSwitch(&PECEnableAtBootSP, nullptr);
420 }
421 if(gemini_software_level_ >= 6.0)
422 {
423 if (getGeminiProperty(SERVO_POINTING_PRECISION_ID, value))
424 {
425 uint8_t servo_value;
426 sscanf(value, "%c", &servo_value);
427 if(servo_value & RA_PRECISION_ENABLED)
428 {
429 ServoPrecisionS[SERVO_RA].s = ISS_ON;
430 }
431 else
432 {
433 ServoPrecisionS[SERVO_RA].s = ISS_OFF;
434 }
435 if(servo_value & DEC_PRECISION_ENABLED)
436 {
437 ServoPrecisionS[SERVO_DEC].s = ISS_ON;
438 }
439 else
440 {
441 ServoPrecisionS[SERVO_DEC].s = ISS_OFF;
442 }
443 ServoPrecisionSP.s = IPS_OK;
444 IDSetSwitch(&ServoPrecisionSP, nullptr);
445
446 }
447 else
448 {
449 ServoPrecisionSP.s = IPS_ALERT;
450 IDSetSwitch(&ServoPrecisionSP, nullptr);
451 }
452 if(getGeminiProperty(FLIP_POINTS_ENABLED_ID, value))
453 {
454 char valueString[32] = {0};
455 uint32_t flip_value = 0;
456 sscanf(value, "%u", &flip_value);
457 snprintf(valueString, 32, "%i", flip_value);
458
459 if(flip_value)
460 {
461 if(flip_value & FLIP_EAST)
462 {
463 FlipControlS[FLIP_EAST_CONTROL].s = ISS_ON;
464 }
465 else
466 {
467 FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
468 }
469 if(flip_value & FLIP_WEST)
470 {
471 FlipControlS[FLIP_WEST_CONTROL].s = ISS_ON;
472 }
473 else
474 {
475 FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
476 }
477 }
478 else
479 {
480 FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
481 FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
482 }
483 FlipControlSP.s = IPS_OK;
484 IDSetSwitch(&FlipControlSP, nullptr);
485 }
486 else
487 {
488 FlipControlSP.s = IPS_ALERT;
489 IDSetSwitch(&FlipControlSP, nullptr);
490 }
491 if(getGeminiProperty(FLIP_POINT_EAST_ID, value) &&
492 getGeminiProperty(FLIP_POINT_WEST_ID, value2))
493 {
494 double eastSexa;
495 double westSexa;
496
497 f_scansexa(value, &eastSexa);
498 FlipPositionN[FLIP_EAST_VALUE].value = eastSexa;
499
500 f_scansexa(value2, &westSexa);
501 FlipPositionN[FLIP_WEST_VALUE].value = westSexa;
502
503 FlipPositionNP.s = IPS_OK;
504 IDSetNumber(&FlipPositionNP, nullptr);
505 }
506 else
507 {
508 FlipPositionNP.s = IPS_ALERT;
509 IDSetNumber(&FlipPositionNP, nullptr);
510 }
511 }
512
513 if (getGeminiProperty(PEC_MAX_STEPS_ID, value))
514 {
515 float max_value;
516 sscanf(value, "%f", &max_value);
517 PECMaxStepsN[0].value = max_value;
518 IDSetNumber(&PECMaxStepsNP, nullptr);
519 }
520 else
521 {
522 PECMaxStepsNP.s = IPS_ALERT;
523 IDSetNumber(&PECMaxStepsNP, nullptr);
524 }
525 if (getGeminiProperty(PEC_COUNTER_ID, value))
526 {
527 char valueString[32] = {0};
528 uint32_t pec_counter = 0;
529 sscanf(value, "%u", &pec_counter);
530 snprintf(valueString, 32, "%i", pec_counter);
531
532 IUSaveText(&PECCounterT[0], valueString);
533 IDSetText(&PECCounterTP, nullptr);
534 }
535 else
536 {
537 PECCounterTP.s = IPS_ALERT;
538 IDSetText(&PECCounterTP, nullptr);
539 }
540 if (getGeminiProperty(PEC_GUIDING_SPEED_ID, value))
541 {
542 float guiding_value;
543 sscanf(value, "%f", &guiding_value);
544 PECGuidingSpeedN[0].value = guiding_value;
545 IDSetNumber(&PECGuidingSpeedNP, nullptr);
546 }
547 else
548 {
549 PECGuidingSpeedNP.s = IPS_ALERT;
550 IDSetNumber(&PECGuidingSpeedNP, nullptr);
551 }
552 if (getGeminiProperty(GUIDING_SPEED_ID, value))
553 {
554 float guiding_value;
555 sscanf(value, "%f", &guiding_value);
556 GuidingSpeedBothN[GUIDING_BOTH].value = guiding_value;
557 IDSetNumber(&GuidingSpeedBothNP, nullptr);
558 }
559 else
560 {
561 GuidingSpeedBothNP.s = IPS_ALERT;
562 IDSetNumber(&GuidingSpeedBothNP, nullptr);
563 }
564 if (getGeminiProperty(PEC_STATUS_ID, value))
565 {
566 uint32_t pec_status = 0;
567 sscanf(value, "%u", &pec_status);
568 if(pec_status & 1) // PEC_ACTIVE
569 {
570 IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "Yes");
572 }
573 else
574 {
575 IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "No");
577 }
578 if(pec_status & 2) // Freshly_Trained
579 {
580 IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "Yes");
581 }
582 else
583 {
584 IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "No");
585 }
586 if(pec_status & 4) // Training_In_Progress
587 {
588 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "Yes");
589 }
590 else
591 {
592 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "No");
593 }
594 if(pec_status & 8) // Training_just_completed
595 {
596 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "Yes");
597 }
598 else
599 {
600 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "No");
601 }
602 if(pec_status & 16) // Training will start soon
603 {
604 IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "Yes");
605 }
606 else
607 {
608 IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "No");
609 }
610 if(pec_status & 32) // PEC Data Available
611 {
612 IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
613 }
614 else
615 {
616 IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "No");
617 }
618 IDSetText(&PECStateTP, nullptr);
619 }
620 else
621 {
622 PECStateTP.s = IPS_ALERT;
623 IDSetText(&PECStateTP, nullptr);
624 }
625
626 if(getGeminiProperty(EAST_SAFETY_LIMIT_ID, value) &&
627 getGeminiProperty(WEST_SAFETY_LIMIT_ID, value2) &&
628 getGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, value3))
629 {
630 double eastSafeSexa;
631 double westSafeSexa;
632 double westGotoSexa;
633
634 f_scansexa(value, &eastSafeSexa);
635 SafetyLimitsN[EAST_SAFETY].value = eastSafeSexa;
636
637 f_scansexa(value2, &westSafeSexa);
638 SafetyLimitsN[WEST_SAFETY].value = westSafeSexa;
639
640 f_scansexa(value3, &westGotoSexa);
641 SafetyLimitsN[WEST_GOTO].value = westGotoSexa;
642
643 SafetyLimitsNP.s = IPS_OK;
644 IDSetNumber(&SafetyLimitsNP, nullptr);
645 }
646 else
647 {
648 SafetyLimitsNP.s = IPS_ALERT;
649 IDSetNumber(&SafetyLimitsNP, nullptr);
650 }
651}
652
653
655{
656 const int MAX_VALUE_LENGTH = 32;
658
659 if (isConnected())
660 {
661 uint32_t speed = 0;
662 char value[MAX_VALUE_LENGTH] = {0};
663 char value2[MAX_VALUE_LENGTH] = {0};
664 char value3[MAX_VALUE_LENGTH] = {0};
665 if (!isSimulation())
666 {
667 VersionTP.tp[FIRMWARE_DATE].text = new char[64];
668 getVersionDate(PortFD, VersionTP.tp[FIRMWARE_DATE].text);
669 VersionTP.tp[FIRMWARE_TIME].text = new char[64];
670 getVersionTime(PortFD, VersionTP.tp[FIRMWARE_TIME].text);
671 VersionTP.tp[FIRMWARE_LEVEL].text = new char[64];
672 getVersionNumber(PortFD, VersionTP.tp[FIRMWARE_LEVEL].text);
673 VersionTP.tp[FIRMWARE_NAME].text = new char[128];
674 getProductName(PortFD, VersionTP.tp[FIRMWARE_NAME].text);
675 sscanf(VersionTP.tp[FIRMWARE_LEVEL].text, "%f", &gemini_software_level_);
676 IDSetText(&VersionTP, nullptr);
677 }
678 defineProperty(&VersionTP);
679 defineProperty(&ParkSettingsSP);
680
681 if (gemini_software_level_ < 5.0)
682 {
684 }
685 if (gemini_software_level_ >= 5.2)
686 {
687 if(getGeminiProperty(PEC_ENABLE_AT_BOOT_ID, value))
688 {
689 uint32_t pec_at_boot_value;
690 sscanf(value, "%i", &pec_at_boot_value);
691 if(pec_at_boot_value)
692 {
693 PECEnableAtBootS[0].s = ISS_ON;
694 }
695 else
696 {
697 PECEnableAtBootS[0].s = ISS_OFF;
698 }
699 PECEnableAtBootSP.s = IPS_OK;
700 IDSetSwitch(&PECEnableAtBootSP, nullptr);
701 }
702 else
703 {
704 PECEnableAtBootSP.s = IPS_ALERT;
705 IDSetSwitch(&PECEnableAtBootSP, nullptr);
706 }
707 defineProperty(&PECEnableAtBootSP);
708
709 }
710 if (getGeminiProperty(PEC_GUIDING_SPEED_ID, value))
711 {
712 float guiding_speed_value;
713 sscanf(value, "%f", &guiding_speed_value);
714 PECGuidingSpeedN[0].value = guiding_speed_value;
715 PECGuidingSpeedNP.s = IPS_OK;
716 IDSetNumber(&PECGuidingSpeedNP, nullptr);
717 }
718 else
719 {
720 PECGuidingSpeedNP.s = IPS_ALERT;
721 IDSetNumber(&PECGuidingSpeedNP, nullptr);
722 }
723 defineProperty(&PECGuidingSpeedNP);
724 if (gemini_software_level_ >= 5.0)
725 {
726 if(getGeminiProperty(PEC_COUNTER_ID, value))
727 {
728 char valueString[32] = {0};
729 uint32_t pec_counter = 0;
730 sscanf(value, "%u", &pec_counter);
731 snprintf(valueString, 32, "%i", pec_counter);
732 IUSaveText(&PECCounterT[0], valueString);
733 PECControlSP.s = IPS_OK;
734 IDSetSwitch(&PECControlSP, nullptr);
735 }
736 else
737 {
738 PECControlSP.s = IPS_ALERT;
739 IDSetSwitch(&PECControlSP, nullptr);
740 }
741 defineProperty(&PECControlSP);
742 defineProperty(&PECCounterTP);
743 }
744 if (getGeminiProperty(PEC_MAX_STEPS_ID, value))
745 {
746 float max_steps_value;
747 sscanf(value, "%f", &max_steps_value);
748 PECMaxStepsN[0].value = max_steps_value;
749 PECMaxStepsNP.s = IPS_OK;
750 IDSetNumber(&PECMaxStepsNP, nullptr);
751 }
752 else
753 {
754 PECMaxStepsNP.s = IPS_ALERT;
755 IDSetNumber(&PECMaxStepsNP, nullptr);
756 }
757 defineProperty(&PECMaxStepsNP);
758 if (getGeminiProperty(PEC_STATUS_ID, value))
759 {
760 uint32_t pec_status = 0;
761 sscanf(value, "%u", &pec_status);
762 if(pec_status & 1) // PEC_ACTIVE
763 {
764 IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "Yes");
766 }
767 else
768 {
769 IUSaveText(&PECStateT[PEC_STATUS_ACTIVE], "No");
771 }
772
773 if(pec_status & 2) // Freshly_Trained
774 {
775 IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "Yes");
776 }
777 else
778 {
779 IUSaveText(&PECStateT[PEC_STATUS_FRESH_TRAINED], "No");
780 }
781
782 if(pec_status & 4) // Training_In_Progress
783 {
784 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "Yes");
785 }
786 else
787 {
788 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_IN_PROGRESS], "No");
789 }
790 if(pec_status & 6) // Training_just_completed
791 {
792 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "Yes");
793 }
794 else
795 {
796 IUSaveText(&PECStateT[PEC_STATUS_TRAINING_COMPLETED], "No");
797 }
798 if(pec_status & 16) // Training will start soon
799 {
800 IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "Yes");
801 }
802 else
803 {
804 IUSaveText(&PECStateT[PEC_STATUS_WILL_TRAIN], "No");
805 }
806 if(pec_status & 32) // PEC Data Available
807 {
808 IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
809 }
810 else
811 {
812 IUSaveText(&PECStateT[PEC_STATUS_DATA_AVAILABLE], "Yes");
813 }
814 PECStateTP.s = IPS_OK;
815 IDSetText(&PECStateTP, nullptr);
816 }
817 else
818 {
819 PECStateTP.s = IPS_ALERT;
820 IDSetText(&PECStateTP, nullptr);
821 }
822 defineProperty(&PECStateTP);
823 if (gemini_software_level_ >= 6.0)
824 {
825 if(getGeminiProperty(FLIP_POINTS_ENABLED_ID, value))
826 {
827 char valueString[32] = {0};
828 uint32_t flip_value = 0;
829 sscanf(value, "%u", &flip_value);
830 snprintf(valueString, 32, "%i", flip_value);
831
832 if(flip_value)
833 {
834 if(flip_value & FLIP_EAST)
835 {
836 FlipControlS[FLIP_EAST_CONTROL].s = ISS_ON;
837 }
838 else
839 {
840 FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
841 }
842 if(flip_value & FLIP_WEST)
843 {
844 FlipControlS[FLIP_WEST_CONTROL].s = ISS_ON;
845 }
846 else
847 {
848 FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
849 }
850 }
851 else
852 {
853 FlipControlS[FLIP_EAST_CONTROL].s = ISS_OFF;
854 FlipControlS[FLIP_WEST_CONTROL].s = ISS_OFF;
855 }
856 FlipControlSP.s = IPS_OK;
857 IDSetSwitch(&FlipControlSP, nullptr);
858 }
859 else
860 {
861 FlipControlSP.s = IPS_ALERT;
862 IDSetSwitch(&FlipControlSP, nullptr);
863 }
864 defineProperty(&FlipControlSP);
865 }
866
867 if (gemini_software_level_ >= 6.0)
868 {
869 if(getGeminiProperty(FLIP_POINT_EAST_ID, value) &&
870 getGeminiProperty(FLIP_POINT_WEST_ID, value2))
871 {
872 double eastSexa;
873 double westSexa;
874
875 f_scansexa(value, &eastSexa);
876 FlipPositionN[FLIP_EAST_VALUE].value = eastSexa;
877
878 f_scansexa(value2, &westSexa);
879 FlipPositionN[FLIP_WEST_VALUE].value = westSexa;
880
881 FlipPositionNP.s = IPS_OK;
882 IDSetNumber(&FlipPositionNP, nullptr);
883 }
884 else
885 {
886 FlipPositionNP.s = IPS_ALERT;
887 IDSetNumber(&FlipPositionNP, nullptr);
888 }
889 defineProperty(&FlipPositionNP);
890 }
891
892 if(getGeminiProperty(EAST_SAFETY_LIMIT_ID, value) &&
893 getGeminiProperty(WEST_SAFETY_LIMIT_ID, value2) &&
894 getGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, value3))
895 {
896 double eastSafeSexa;
897 double westSafeSexa;
898 double westGotoSexa;
899
900 f_scansexa(value, &eastSafeSexa);
901 SafetyLimitsN[EAST_SAFETY].value = eastSafeSexa;
902
903 f_scansexa(value2, &westSafeSexa);
904 SafetyLimitsN[WEST_SAFETY].value = westSafeSexa;
905
906 f_scansexa(value3, &westGotoSexa);
907 SafetyLimitsN[WEST_GOTO].value = westGotoSexa;
908
909 SafetyLimitsNP.s = IPS_OK;
910 IDSetNumber(&SafetyLimitsNP, nullptr);
911 }
912 else
913 {
914 SafetyLimitsNP.s = IPS_ALERT;
915 IDSetNumber(&SafetyLimitsNP, nullptr);
916 }
917 defineProperty(&SetSafetyLimitToCurrentSP);
918 defineProperty(&SafetyLimitsNP);
919
920 if (gemini_software_level_ >= 6.0)
921 {
922 if(getGeminiProperty(SERVO_POINTING_PRECISION_ID, value))
923 {
924 uint8_t servo_value;
925 sscanf(value, "%c", &servo_value);
926 if(servo_value & RA_PRECISION_ENABLED)
927 {
928 ServoPrecisionS[SERVO_RA].s = ISS_ON;
929 }
930 else
931 {
932 ServoPrecisionS[SERVO_RA].s = ISS_OFF;
933 }
934 if(servo_value & DEC_PRECISION_ENABLED)
935 {
936 ServoPrecisionS[SERVO_DEC].s = ISS_ON;
937 }
938 else
939 {
940 ServoPrecisionS[SERVO_DEC].s = ISS_OFF;
941 }
942 ServoPrecisionSP.s = IPS_OK;
943 IDSetSwitch(&ServoPrecisionSP, nullptr);
944 }
945 else
946 {
947 ServoPrecisionSP.s = IPS_ALERT;
948 IDSetSwitch(&ServoPrecisionSP, nullptr);
949 }
950 defineProperty(&ServoPrecisionSP);
951
952 bool refractionSetting = false;
953 if(getRefraction(refractionSetting))
954 {
955 if(refractionSetting)
956 {
957 RefractionControlS[0].s = ISS_ON;
958 }
959 else
960 {
961 RefractionControlS[0].s = ISS_OFF;
962 }
963 RefractionControlSP.s = IPS_OK;
964 IDSetSwitch(&RefractionControlSP, nullptr);
965 }
966 else
967 {
968 RefractionControlSP.s = IPS_ALERT;
969 IDSetSwitch(&RefractionControlSP, nullptr);
970 }
971 defineProperty(&RefractionControlSP);
972
973 }
974 if (getGeminiProperty(MANUAL_SLEWING_SPEED_ID, value))
975 {
976 sscanf(value, "%u", &speed);
977 ManualSlewingSpeedN[0].value = speed;
978 ManualSlewingSpeedNP.s = IPS_OK;
979 IDSetNumber(&ManualSlewingSpeedNP, nullptr);
980 }
981 else
982 {
983 ManualSlewingSpeedNP.s = IPS_ALERT;
984 IDSetNumber(&ManualSlewingSpeedNP, nullptr);
985 }
986 defineProperty(&ManualSlewingSpeedNP);
987 if (getGeminiProperty(GOTO_SLEWING_SPEED_ID, value))
988 {
989 sscanf(value, "%u", &speed);
990 GotoSlewingSpeedN[0].value = speed;
991 GotoSlewingSpeedNP.s = IPS_OK;
992 }
993 else
994 {
995 GotoSlewingSpeedNP.s = IPS_ALERT;
996 IDSetNumber(&GotoSlewingSpeedNP, nullptr);
997 }
998 defineProperty(&GotoSlewingSpeedNP);
999 if (gemini_software_level_ >= 5.0)
1000 {
1001 if(getGeminiProperty(MOVE_SPEED_ID, value))
1002 {
1003 sscanf(value, "%u", &speed);
1004 MoveSpeedN[0].value = speed;
1005 MoveSpeedNP.s = IPS_OK;
1006 IDSetNumber(&MoveSpeedNP, nullptr);
1007 }
1008 else
1009 {
1010 MoveSpeedNP.s = IPS_ALERT;
1011 IDSetNumber(&MoveSpeedNP, nullptr);
1012 }
1013 }
1014 defineProperty(&MoveSpeedNP);
1015 if (getGeminiProperty(GUIDING_SPEED_ID, value))
1016 {
1017 float guidingSpeed = 0.0;
1018 sscanf(value, "%f", &guidingSpeed);
1019 GuidingSpeedBothN[GUIDING_BOTH].value = guidingSpeed;
1020 GuidingSpeedBothNP.s = IPS_OK;
1021 IDSetNumber(&GuidingSpeedBothNP, nullptr);
1022 }
1023 else
1024 {
1025 GuidingSpeedBothNP.s = IPS_ALERT;
1026 IDSetNumber(&GuidingSpeedBothNP, nullptr);
1027 }
1028 defineProperty(&GuidingSpeedBothNP);
1029 if (gemini_software_level_ >= 5.0)
1030 {
1031 if(getGeminiProperty(GUIDING_SPEED_RA_ID, value))
1032 {
1033 float guidingSpeed = 0.0;
1034 sscanf(value, "%f", &guidingSpeed);
1035 GuidingSpeedN[GUIDING_WE].value = guidingSpeed;
1036 GuidingSpeedNP.s = IPS_OK;
1037 IDSetNumber(&GuidingSpeedNP, nullptr);
1038 }
1039 else
1040 {
1041 GuidingSpeedNP.s = IPS_ALERT;
1042 IDSetNumber(&GuidingSpeedNP, nullptr);
1043 }
1044 }
1045 if (gemini_software_level_ >= 5.0)
1046 {
1047 if(getGeminiProperty(GUIDING_SPEED_DEC_ID, value))
1048 {
1049 float guidingSpeed = 0.0;
1050 sscanf(value, "%f", &guidingSpeed);
1051 GuidingSpeedN[GUIDING_NS].value = guidingSpeed;
1052 GuidingSpeedNP.s = IPS_OK;
1053 IDSetNumber(&GuidingSpeedNP, nullptr);
1054 }
1055 else
1056 {
1057 GuidingSpeedNP.s = IPS_ALERT;
1058 IDSetNumber(&GuidingSpeedNP, nullptr);
1059 }
1060 }
1061 defineProperty(&GuidingSpeedNP);
1062 if (getGeminiProperty(CENTERING_SPEED_ID, value))
1063 {
1064 sscanf(value, "%u", &speed);
1065 CenteringSpeedN[0].value = speed;
1066 CenteringSpeedNP.s = IPS_OK;
1067 IDSetNumber(&CenteringSpeedNP, nullptr);
1068 }
1069 else
1070 {
1071 CenteringSpeedNP.s = IPS_ALERT;
1072 IDSetNumber(&CenteringSpeedNP, nullptr);
1073 }
1074 defineProperty(&CenteringSpeedNP);
1075
1076 updateParkingState();
1077 updateMovementState();
1078 }
1079 else
1080 {
1081 deleteProperty(ParkSettingsSP.name);
1082 deleteProperty(ManualSlewingSpeedNP.name);
1083 deleteProperty(GotoSlewingSpeedNP.name);
1084 deleteProperty(MoveSpeedNP.name);
1085 deleteProperty(GuidingSpeedNP.name);
1086 deleteProperty(GuidingSpeedBothNP.name);
1087 deleteProperty(CenteringSpeedNP.name);
1088 deleteProperty(PECStateTP.name);
1089 deleteProperty(PECCounterTP.name);
1090 deleteProperty(PECMaxStepsNP.name);
1091 deleteProperty(PECGuidingSpeedNP.name);
1092 deleteProperty(ServoPrecisionSP.name);
1093 deleteProperty(PECEnableAtBootSP.name);
1094 deleteProperty(PECGuidingSpeedNP.name);
1095 deleteProperty(VersionTP.name);
1096 deleteProperty(FlipPositionNP.name);
1097 deleteProperty(FlipControlSP.name);
1098 deleteProperty(PECControlSP.name);
1099 deleteProperty(SetSafetyLimitToCurrentSP.name);
1100 deleteProperty(SafetyLimitsNP.name);
1101 }
1102
1103 return true;
1104}
1105bool LX200Gemini::ISNewText(const char *dev, const char *name, char **texts, char **names, int n)
1106{
1107 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1108 {
1109 if (!strcmp(name, PECStateTP.name))
1110 {
1111 IUUpdateText(&PECStateTP, texts, names, n);
1112 IDSetText(&PECStateTP, nullptr);
1113 }
1114 if (!strcmp(name, PECCounterTP.name))
1115 {
1116 IUUpdateText(&PECCounterTP, texts, names, n);
1117 IDSetText(&PECCounterTP, nullptr);
1118 }
1119 }
1120
1121 return LX200Generic::ISNewText(dev, name, texts, names, n);
1122}
1123
1124bool LX200Gemini::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
1125{
1126 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1127 {
1128 if (!strcmp(name, StartupModeSP.name))
1129 {
1130 IUUpdateSwitch(&StartupModeSP, states, names, n);
1131 StartupModeSP.s = IPS_OK;
1132 if (isConnected())
1133 LOG_INFO("Startup mode will take effect on future connections.");
1134 IDSetSwitch(&StartupModeSP, nullptr);
1135 return true;
1136 }
1137
1138 if (!strcmp(name, ParkSettingsSP.name))
1139 {
1140 IUUpdateSwitch(&ParkSettingsSP, states, names, n);
1141 ParkSettingsSP.s = IPS_OK;
1142 IDSetSwitch(&ParkSettingsSP, nullptr);
1143 return true;
1144 }
1145
1146 if (gemini_software_level_ >= 5.0 && PECStateSP.isNameMatch(name))
1147 {
1148 PECStateSP.update(states, names, n);
1149 for(int i = 0; i < n; ++i)
1150 {
1151 // if (!strcmp(names[i], PECStateS[PEC_ON].name))
1152 if (PECStateSP[PEC_ON].isNameMatch(name))
1153 {
1154 if(PECStateSP[PEC_ON].getState() == ISS_ON)
1155 {
1156 LOG_INFO("PEC State.s ON.");
1157 char valueString[16] = {0};
1158 if(!setGeminiProperty(PEC_REPLAY_ON_ID, valueString))
1159 {
1161 PECStateSP.apply();
1162 return false;
1163 }
1164 else
1165 {
1166 return true;
1167 }
1168
1169 }
1170 }
1171 if (PECStateSP[PEC_OFF].isNameMatch(name))
1172 {
1173 if(PECStateSP[PEC_OFF].getState() == ISS_ON)
1174 {
1175 LOG_INFO("PEC State.s ON.");
1176 char valueString[16] = {0};
1177 if(!setGeminiProperty(PEC_REPLAY_OFF_ID, valueString))
1178 {
1180 PECStateSP.apply();
1181 return false;
1182 }
1183 return true;
1184 }
1185 }
1186 }
1188 PECStateSP.apply();
1189 return true;
1190 }
1191
1192 if (gemini_software_level_ >= 6.0 && !strcmp(name, ServoPrecisionSP.name))
1193 {
1194 IUUpdateSwitch(&ServoPrecisionSP, states, names, n);
1195 ServoPrecisionSP.s = IPS_BUSY;
1196
1197 uint8_t precisionEnabled = 0;
1198 for(int i = 0; i < n; ++i)
1199 {
1200 if (!strcmp(names[i], ServoPrecisionS[SERVO_RA].name))
1201 {
1202 if(ServoPrecisionS[SERVO_RA].s == ISS_ON)
1203 {
1204 precisionEnabled |= 1;
1205 }
1206 }
1207
1208 if (!strcmp(names[i], ServoPrecisionS[SERVO_DEC].name))
1209 {
1210 if(ServoPrecisionS[SERVO_DEC].s == ISS_ON)
1211 {
1212 precisionEnabled |= 2;
1213 }
1214 }
1215 }
1216 char valueString[16] = {0};
1217
1218 snprintf(valueString, 16, "%u", precisionEnabled);
1219
1220 if(precisionEnabled & 1)
1221 {
1222 LOGF_INFO("ServoPrecision: RA ON <%i>", (int)precisionEnabled);
1223 }
1224 else
1225 {
1226 LOGF_INFO("ServoPrecision: RA OFF <%i>", (int)precisionEnabled);
1227 }
1228
1229 if(precisionEnabled & 2)
1230 {
1231 LOGF_INFO("ServoPrecision: DEC ON <%i>", (int)precisionEnabled);
1232 }
1233 else
1234 {
1235 LOGF_INFO("ServoPrecision: DEC OFF <%i>", (int)precisionEnabled);
1236 }
1237
1238 if(!setGeminiProperty(SERVO_POINTING_PRECISION_ID, valueString))
1239 {
1240 ServoPrecisionSP.s = IPS_ALERT;
1241 IDSetSwitch(&ServoPrecisionSP, nullptr);
1242 return false;
1243 }
1244 else
1245 {
1246 ServoPrecisionSP.s = IPS_OK;
1247 IDSetSwitch(&ServoPrecisionSP, nullptr);
1248 return true;
1249 }
1250 }
1251
1252 if (gemini_software_level_ >= 6.0 && !strcmp(name, RefractionControlSP.name))
1253 {
1254 IUUpdateSwitch(&RefractionControlSP, states, names, n);
1255 for(int i = 0; i < n; ++i)
1256 {
1257 if (!strcmp(names[i], RefractionControlS[0].name))
1258 {
1259 if(RefractionControlS[0].s == ISS_ON)
1260 {
1261 if(!setRefraction(true))
1262 {
1263 RefractionControlSP.s = IPS_ALERT;
1264 IDSetSwitch(&RefractionControlSP, nullptr);
1265 return false;
1266 }
1267 else
1268 {
1269 RefractionControlSP.s = IPS_OK;
1270 IDSetSwitch(&RefractionControlSP, nullptr);
1271 return true;
1272 }
1273 }
1274 else if(RefractionControlS[0].s == ISS_OFF)
1275 {
1276 if(!setRefraction(false))
1277 {
1278 RefractionControlSP.s = IPS_ALERT;
1279 IDSetSwitch(&RefractionControlSP, nullptr);
1280 return false;
1281 }
1282 else
1283 {
1284 RefractionControlSP.s = IPS_OK;
1285 IDSetSwitch(&RefractionControlSP, nullptr);
1286 return true;
1287 }
1288 }
1289 }
1290 }
1291 }
1292
1293 if (gemini_software_level_ >= 6.0 && !strcmp(name, FlipControlSP.name))
1294 {
1295 IUUpdateSwitch(&FlipControlSP, states, names, n);
1296 FlipControlSP.s = IPS_OK;
1297 IDSetSwitch(&FlipControlSP, nullptr);
1298 int32_t flipEnabled = 0;
1299 for(int i = 0; i < n; ++i)
1300 {
1301 if (!strcmp(names[i], FlipControlS[FLIP_EAST_CONTROL].name))
1302 {
1303 if(FlipControlS[FLIP_EAST_CONTROL].s == ISS_ON)
1304 {
1305 flipEnabled |= 1;
1306 LOGF_INFO("FlipControl: EAST ON <%i>", flipEnabled);
1307 }
1308 else
1309 {
1310 flipEnabled &= 0xfffffffe;
1311 LOGF_INFO("FlipControl: EAST OFF <%i>", flipEnabled);
1312 }
1313 }
1314
1315 if (!strcmp(names[i], FlipControlS[FLIP_WEST_CONTROL].name))
1316 {
1317 if(FlipControlS[FLIP_WEST_CONTROL].s == ISS_ON)
1318 {
1319 flipEnabled |= 2;
1320 LOGF_INFO("FlipControl: WEST ON <%i>", flipEnabled);
1321 }
1322 else
1323 {
1324 flipEnabled &= 0xfffffffd;
1325 LOGF_INFO("FlipControl: WEST OFF <%i>", flipEnabled);
1326 }
1327 }
1328 }
1329 char valueString[16] = {0};
1330 snprintf(valueString, 16, "%i", flipEnabled);
1331 LOGF_INFO("FlipControl: <%s>", valueString);
1332 if(!setGeminiProperty(FLIP_POINTS_ENABLED_ID, valueString))
1333 {
1334 FlipControlSP.s = IPS_ALERT;
1335 IDSetSwitch(&FlipControlSP, nullptr);
1336 return false;
1337 }
1338 else
1339 {
1340 FlipControlSP.s = IPS_OK;
1341 IDSetSwitch(&FlipControlSP, nullptr);
1342 return true;
1343 }
1344 }
1345 if (!strcmp(name, SetSafetyLimitToCurrentSP.name))
1346 {
1347 char valueString[16] = {0};
1348 IUUpdateSwitch(&SetSafetyLimitToCurrentSP, states, names, n);
1349 IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1350 SetSafetyLimitToCurrentS[0].s = ISS_OFF;
1351 if(!setGeminiProperty(SET_SAFETY_LIMIT_TO_CURRENT_ID, valueString))
1352 {
1353 SetSafetyLimitToCurrentSP.s = IPS_ALERT;
1354 IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1355 return false;
1356 }
1357 else
1358 {
1359 SetSafetyLimitToCurrentSP.s = IPS_OK;
1360 IDSetSwitch(&SetSafetyLimitToCurrentSP, nullptr);
1361 }
1362
1363 }
1364
1365 if (gemini_software_level_ >= 5.0 && !strcmp(name, PECControlSP.name))
1366 {
1367 IUUpdateSwitch(&PECControlSP, states, names, n);
1368 for(int i = 0; i < n; ++i)
1369 {
1370 if (!strcmp(names[i], PECControlS[PEC_START_TRAINING].name))
1371 {
1372 char valueString[16] = {0};
1373 if(!setGeminiProperty(PEC_START_TRAINING_ID, valueString))
1374 {
1375 PECControlSP.s = IPS_ALERT;
1376 IDSetSwitch(&PECControlSP, nullptr);
1377 return false;
1378 }
1379 }
1380 else if (!strcmp(names[i], PECControlS[PEC_ABORT_TRAINING].name))
1381 {
1382 char valueString[16] = {0};
1383 if(!setGeminiProperty(PEC_ABORT_TRAINING_ID, valueString))
1384 {
1385 PECControlSP.s = IPS_ALERT;
1386 IDSetSwitch(&PECControlSP, nullptr);
1387 return false;
1388 }
1389
1390 }
1391
1392 }
1393 PECControlSP.s = IPS_OK;
1394 IDSetSwitch(&PECControlSP, nullptr);
1395 return true;
1396 }
1397 }
1398
1399 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
1400}
1401
1402bool LX200Gemini::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1403{
1404 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1405 {
1406 char valueString[16] = {0};
1407 snprintf(valueString, 16, "%2.0f", values[0]);
1408
1409 if (!strcmp(name, ManualSlewingSpeedNP.name))
1410 {
1411 LOGF_DEBUG("Trying to set manual slewing speed of: %f", values[0]);
1412
1413 if (!isSimulation() && !setGeminiProperty(MANUAL_SLEWING_SPEED_ID, valueString))
1414 {
1415 ManualSlewingSpeedNP.s = IPS_ALERT;
1416 IDSetNumber(&ManualSlewingSpeedNP, "Error setting manual slewing speed");
1417 return false;
1418 }
1419
1420 ManualSlewingSpeedNP.s = IPS_OK;
1421 ManualSlewingSpeedN[0].value = values[0];
1422 IDSetNumber(&ManualSlewingSpeedNP, "Manual slewing speed set to %f", values[0]);
1423
1424 return true;
1425 }
1426 if (!strcmp(name, GotoSlewingSpeedNP.name))
1427 {
1428 LOGF_DEBUG("Trying to set goto slewing speed of: %f", values[0]);
1429
1430 if (!isSimulation() && !setGeminiProperty(GOTO_SLEWING_SPEED_ID, valueString))
1431 {
1432 GotoSlewingSpeedNP.s = IPS_ALERT;
1433 IDSetNumber(&GotoSlewingSpeedNP, "Error setting goto slewing speed");
1434 return false;
1435 }
1436
1437 GotoSlewingSpeedNP.s = IPS_OK;
1438 GotoSlewingSpeedN[0].value = values[0];
1439 IDSetNumber(&GotoSlewingSpeedNP, "Goto slewing speed set to %f", values[0]);
1440
1441 return true;
1442 }
1443 if (gemini_software_level_ >= 5.0 && !strcmp(name, MoveSpeedNP.name))
1444 {
1445 LOGF_DEBUG("Trying to set move speed of: %f", values[0]);
1446
1447 if (!isSimulation() && !setGeminiProperty(MOVE_SPEED_ID, valueString))
1448 {
1449 MoveSpeedNP.s = IPS_ALERT;
1450 IDSetNumber(&MoveSpeedNP, "Error setting move speed");
1451 return false;
1452 }
1453
1454 MoveSpeedNP.s = IPS_OK;
1455 MoveSpeedN[0].value = values[0];
1456 IDSetNumber(&MoveSpeedNP, "Move speed set to %f", values[0]);
1457
1458 return true;
1459 }
1460 if (gemini_software_level_ >= 5.0 && !strcmp(name, GuidingSpeedBothNP.name))
1461 {
1462 LOGF_DEBUG("Trying to set guiding speed of: %f", values[0]);
1463
1464 for(int i = 0; i < n; ++i)
1465 {
1466 if (!strcmp(names[i], GuidingSpeedBothN[GUIDING_BOTH].name))
1467 {
1468 // Special formatting for guiding speed
1469 snprintf(valueString, 16, "%1.1f", values[0]);
1470
1471 if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_ID, valueString))
1472 {
1473 GuidingSpeedBothNP.s = IPS_ALERT;
1474 IDSetNumber(&GuidingSpeedBothNP, "Error setting guiding speed");
1475 return false;
1476 }
1477
1478 }
1479 }
1480
1481 GuidingSpeedBothN[GUIDING_BOTH].value = values[0];
1482 GuidingSpeedBothNP.s = IPS_OK;
1483 IDSetNumber(&GuidingSpeedBothNP, "Guiding speed set to %f", values[0]);
1484
1485 return true;
1486 }
1487 if (!strcmp(name, GuidingSpeedNP.name))
1488 {
1489
1490 for(int i = 0; i < n; ++i)
1491 {
1492 if (!strcmp(names[i], GuidingSpeedN[GUIDING_WE].name))
1493 {
1494 // Special formatting for guiding speed
1495 snprintf(valueString, 16, "%1.1f", values[i]);
1496 if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_RA_ID, valueString))
1497 {
1498 GuidingSpeedN[GUIDING_WE].value = values[i];
1499 GuidingSpeedNP.s = IPS_ALERT;
1500 IDSetNumber(&GuidingSpeedNP, "Error Setting Guiding WE");
1501 return false;
1502 }
1503
1504 }
1505 if (!strcmp(names[i], GuidingSpeedN[GUIDING_NS].name))
1506 {
1507 // Special formatting for guiding speed
1508 snprintf(valueString, 16, "%1.1f", values[i]);
1509 GuidingSpeedNP.s = IPS_ALERT;
1510 if (!isSimulation() && !setGeminiProperty(GUIDING_SPEED_DEC_ID, valueString))
1511 {
1512 GuidingSpeedN[GUIDING_NS].value = values[i];
1513 GuidingSpeedNP.s = IPS_ALERT;
1514 IDSetNumber(&GuidingSpeedNP, "Error Setting Guiding WE");
1515 return false;
1516 }
1517 }
1518 }
1519
1520 GuidingSpeedNP.s = IPS_OK;
1521 IDSetNumber(&GuidingSpeedNP, "Guiding speed set to RA:%f DEC:%f", GuidingSpeedN[GUIDING_WE].value, GuidingSpeedN[GUIDING_NS].value);
1522
1523 return true;
1524 }
1525 if (!strcmp(name, SafetyLimitsNP.name))
1526 {
1527 double eastSafeD = 0;
1528 double westSafeD = 0;
1529 double westGotoD = 0;
1530
1531 for(int i = 0; i < n; ++i)
1532 {
1533 if (!strcmp(names[i], SafetyLimitsN[EAST_SAFETY].name))
1534 {
1535 eastSafeD = values[i];
1536 }
1537 if (!strcmp(names[i], SafetyLimitsN[WEST_SAFETY].name))
1538 {
1539 westSafeD = values[i];
1540 }
1541 if (!strcmp(names[i], SafetyLimitsN[WEST_GOTO].name))
1542 {
1543 westGotoD = values[i];
1544 }
1545 }
1546 char eastSafe[32] = {0};
1547 SafetyLimitsN[EAST_SAFETY].value = eastSafeD;
1548 fs_sexa(eastSafe, eastSafeD, 2, 60);
1549
1550 char westSafe[32] = {0};
1551 SafetyLimitsN[WEST_SAFETY].value = westSafeD;
1552 fs_sexa(westSafe, westSafeD, 2, 60);
1553
1554 char westGoto[32] = {0};
1555 SafetyLimitsN[WEST_GOTO].value = westGotoD;
1556 fs_sexa(westGoto, westGotoD, 2, 60);
1557
1558 char *colon = strchr(eastSafe, ':');
1559 if(colon)
1560 {
1561 *colon = 'd';
1562 }
1563 colon = strchr(westSafe, ':');
1564 if(colon)
1565 {
1566 *colon = 'd';
1567 }
1568 colon = strchr(westGoto, ':');
1569 if(colon)
1570 {
1571 *colon = 'd';
1572 }
1573
1574 if (!isSimulation() &&
1575 (!setGeminiProperty(EAST_SAFETY_LIMIT_ID, eastSafe) ||
1576 !setGeminiProperty(WEST_SAFETY_LIMIT_ID, westSafe) ||
1577 !setGeminiProperty(WEST_GOTO_SAFETY_LIMIT_ID, westGoto)))
1578 {
1579 SafetyLimitsNP.s = IPS_ALERT;
1580 IDSetNumber(&SafetyLimitsNP, "Error Setting Limits");
1581 return false;
1582 }
1583 IDSetNumber(&SafetyLimitsNP, "Limits EastSafe:%s, WestSafe:%s, WestGoto:%s", eastSafe, westSafe, westGoto);
1584 SafetyLimitsNP.s = IPS_OK;
1585 return true;
1586 }
1587 if (gemini_software_level_ >= 6.0 && !strcmp(name, FlipPositionNP.name))
1588 {
1589 double eastD = 0;
1590 double westD = 0;
1591
1592 for(int i = 0; i < n; ++i)
1593 {
1594 if (!strcmp(names[i], FlipPositionN[FLIP_EAST_VALUE].name))
1595 {
1596 eastD = values[i];
1597 }
1598 if (!strcmp(names[i], FlipPositionN[FLIP_WEST_VALUE].name))
1599 {
1600 westD = values[i];
1601 }
1602 }
1603 char east[32] = {0};
1604 FlipPositionN[FLIP_EAST_VALUE].value = eastD;
1605 fs_sexa(east, eastD, 2, 60);
1606
1607 char west[32] = {0};
1608 FlipPositionN[FLIP_WEST_VALUE].value = westD;
1609 fs_sexa(west, westD, 2, 60);
1610
1611 char *colon = strchr(east, ':');
1612 if(colon)
1613 {
1614 *colon = 'd';
1615 }
1616 colon = strchr(west, ':');
1617 if(colon)
1618 {
1619 *colon = 'd';
1620 }
1621 if (!isSimulation() &&
1622 (!setGeminiProperty(FLIP_POINT_EAST_ID, east) ||
1623 !setGeminiProperty(FLIP_POINT_WEST_ID, west)))
1624 {
1625 FlipPositionNP.s = IPS_ALERT;
1626 IDSetNumber(&FlipPositionNP, "Error Setting Flip Points");
1627 return false;
1628 }
1629 IDSetNumber(&FlipPositionNP, "FlipPoints East:%s, West:%s", east, west);
1630 FlipPositionNP.s = IPS_OK;
1631 return true;
1632 }
1633 if (!strcmp(name, CenteringSpeedNP.name))
1634 {
1635 LOGF_DEBUG("Trying to set centering speed of: %f", values[0]);
1636
1637 if (!isSimulation() && !setGeminiProperty(CENTERING_SPEED_ID, valueString))
1638 {
1639 CenteringSpeedNP.s = IPS_ALERT;
1640 IDSetNumber(&CenteringSpeedNP, "Error setting centering speed");
1641 return false;
1642 }
1643
1644 CenteringSpeedNP.s = IPS_OK;
1645 CenteringSpeedN[0].value = values[0];
1646 IDSetNumber(&CenteringSpeedNP, "Centering speed set to %f", values[0]);
1647
1648 return true;
1649 }
1650 if (!strcmp(name, PECMaxStepsNP.name))
1651 {
1652 PECMaxStepsNP.s = IPS_OK;
1653 PECMaxStepsN[0].value = values[0];
1654 IDSetNumber(&PECMaxStepsNP, "Max steps set to %f", values[0]);
1655 return true;
1656 }
1657 if (gemini_software_level_ >= 5.0 && !strcmp(name, PECGuidingSpeedNP.name))
1658 {
1659 PECGuidingSpeedNP.s = IPS_OK;
1660 PECGuidingSpeedN[0].value = values[0];
1661 IDSetNumber(&PECGuidingSpeedNP, "Guiding Speed set to %f", values[0]);
1662 return true;
1663 }
1664 }
1665
1666 // If we didn't process it, continue up the chain, let somebody else give it a shot
1667 return LX200Generic::ISNewNumber(dev, name, values, names, n);
1668}
1669
1671{
1672 if (isSimulation())
1673 return true;
1674
1675 // Response
1676 char response[8] = { 0 };
1677 int rc = 0, nbytes_read = 0, nbytes_written = 0;
1678
1679 LOGF_DEBUG("CMD: <%#02X>", 0x06);
1680
1681 tcflush(PortFD, TCIFLUSH);
1682
1683 char ack[1] = { 0x06 };
1684
1685 if ((rc = tty_write(PortFD, ack, 1, &nbytes_written)) != TTY_OK)
1686 {
1687 char errmsg[256];
1688 tty_error_msg(rc, errmsg, 256);
1689 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1690 return false;
1691 }
1692
1693 // Read response
1694 if ((rc = tty_read_section(PortFD, response, '#', GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1695 {
1696 char errmsg[256];
1697 tty_error_msg(rc, errmsg, 256);
1698 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1699 return false;
1700 }
1701
1702 //response[1] = '\0';
1703
1704 tcflush(PortFD, TCIFLUSH);
1705
1706 LOGF_DEBUG("RES: <%s>", response);
1707
1708 // If waiting for selection of startup mode, let us select it
1709 if (response[0] == 'b')
1710 {
1711 LOG_DEBUG("Mount is waiting for selection of the startup mode.");
1712 char cmd[4] = "bC#";
1713 int startupMode = IUFindOnSwitchIndex(&StartupModeSP);
1714 if (startupMode == WARM_START)
1715 strncpy(cmd, "bW#", 4);
1716 else if (startupMode == WARM_RESTART)
1717 strncpy(cmd, "bR#", 4);
1718
1719 LOGF_DEBUG("CMD: <%s>", cmd);
1720
1721 if ((rc = tty_write(PortFD, cmd, 4, &nbytes_written)) != TTY_OK)
1722 {
1723 char errmsg[256];
1724 tty_error_msg(rc, errmsg, 256);
1725 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1726 return false;
1727 }
1728
1729 tcflush(PortFD, TCIFLUSH);
1730
1731 // Send ack again and check response
1732 return checkConnection();
1733 }
1734 else if (response[0] == 'B')
1735 {
1736 LOG_DEBUG("Initial startup message is being displayed.");
1737 }
1738 else if (response[0] == 'S')
1739 {
1740 LOG_DEBUG("Cold start in progress.");
1741 }
1742 else if (response[0] == 'G')
1743 {
1744 updateParkingState();
1745 updateMovementState();
1746 LOG_DEBUG("Startup complete with equatorial mount selected.");
1747 }
1748 else if (response[0] == 'A')
1749 {
1750 LOG_DEBUG("Startup complete with Alt-Az mount selected.");
1751 }
1752
1753 return true;
1754}
1755
1757{
1758 LX200Gemini::MovementState movementState = getMovementState();
1759
1760 if (movementState == TRACKING || movementState == GUIDING || movementState == NO_MOVEMENT)
1761 return true;
1762 else
1763 return false;
1764}
1765
1767{
1768 LOGF_DEBUG("ReadScopeStatus: TrackState is <%d>", TrackState);
1769
1770 if (!isConnected())
1771 return false;
1772
1773 if (isSimulation())
1775
1777 {
1778 updateMovementState();
1779
1781 EqNP.apply();
1782
1783 // Check if LX200 is done slewing
1784 if (isSlewComplete())
1785 {
1786 // Set slew mode to "Centering"
1787 SlewRateSP.reset();
1789 SlewRateSP.apply();
1790
1792 EqNP.apply();
1793
1794 if (TrackState == SCOPE_IDLE)
1795 {
1796 SetTrackEnabled(true);
1797 updateMovementState();
1798 }
1799
1800 LOG_INFO("Slew is complete. Tracking...");
1801 }
1802 }
1803 else if (TrackState == SCOPE_PARKING)
1804 {
1805 updateParkingState();
1806
1807 if (isSlewComplete())
1808 {
1809 LOG_DEBUG("Park is complete ...");
1810 SetParked(true);
1811 sleepMount();
1812
1814 EqNP.apply();
1815
1816 return true;
1817 }
1818 }
1819
1821 {
1822 LOG_ERROR("Error reading RA/DEC.");
1823 return false;
1824 }
1825
1827
1828 syncSideOfPier();
1829 syncState();
1830 return true;
1831}
1832
1833void LX200Gemini::syncSideOfPier()
1834{
1835 // Send ':Gm#'
1836 const char *cmd = ":Gm#";
1837 // Response
1838 char response[8] = { 0 };
1839 int rc = 0, nbytes_read = 0, nbytes_written = 0;
1840
1841 LOGF_DEBUG("CMD: <%s>", cmd);
1842
1843 tcflush(PortFD, TCIOFLUSH);
1844
1845 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1846 {
1847 char errmsg[256];
1848 tty_error_msg(rc, errmsg, 256);
1849 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1850 return;
1851 }
1852
1853 if ((rc = tty_read_section(PortFD, response, '#', GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
1854 {
1855 char errmsg[256];
1856 tty_error_msg(rc, errmsg, 256);
1857 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1858 return;
1859 }
1860
1861 response[nbytes_read - 1] = '\0';
1862
1863 tcflush(PortFD, TCIOFLUSH);
1864
1865 //LOGF_DEBUG("RES: <%s>", response);
1866
1867 // fix to pier side read from the mount using the hour angle as a guide
1868 // see https://www.indilib.org/forum/general/6785-side-of-pier-problem-bug.html?start=12#52492
1869 // for a description of the problem and the proposed fix
1870 //
1871 auto lst = get_local_sidereal_time(this->LocationNP[LOCATION_LONGITUDE].getValue());
1872 auto ha = rangeHA(lst - currentRA);
1873 auto pointingState = PIER_UNKNOWN;
1874
1875 if (ha >= -5.0 && ha <= 5.0)
1876 {
1877 // mount pier side is used unchanged
1878 pointingState = response[0] == 'E' ? PIER_EAST : PIER_WEST;
1879 }
1880 else if (ha <= -7.0 || ha >= 7.0)
1881 {
1882 // mount pier side is reversed
1883 pointingState = response[0] == 'W' ? PIER_EAST : PIER_WEST;
1884 }
1885 else
1886 {
1887 // use hour angle because the pier side changes spontaneously near +-6h
1888 pointingState = ha > 0 ? PIER_EAST : PIER_WEST;
1889 }
1890
1891 LOGF_DEBUG("RES: <%s>, lst %f, ha %f, pierSide %d", response, lst, ha, pointingState);
1892
1893 setPierSide(pointingState);
1894}
1895
1896
1898{
1899 char cmd[6] = ":hP#";
1900
1901 int parkSetting = IUFindOnSwitchIndex(&ParkSettingsSP);
1902
1903 if (parkSetting == PARK_STARTUP)
1904 strncpy(cmd, ":hC#", 5);
1905 else if (parkSetting == PARK_ZENITH)
1906 strncpy(cmd, ":hZ#", 5);
1907
1908 // Response
1909 int rc = 0, nbytes_written = 0;
1910
1911 LOGF_DEBUG("CMD: <%s>", cmd);
1912
1913 tcflush(PortFD, TCIOFLUSH);
1914
1915 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1916 {
1917 char errmsg[256];
1918 tty_error_msg(rc, errmsg, 256);
1919 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1920 return false;
1921 }
1922
1923 tcflush(PortFD, TCIOFLUSH);
1924
1927
1928 updateParkingState();
1929 return true;
1930}
1931
1933{
1934 wakeupMount();
1935
1936 SetParked(false);
1937 SetTrackEnabled(true);
1938
1939 updateParkingState();
1940 updateMovementState();
1941 return true;
1942}
1943
1944bool LX200Gemini::sleepMount()
1945{
1946 const char *cmd = ":hN#";
1947
1948 // Response
1949 int rc = 0, nbytes_written = 0;
1950
1951 LOGF_DEBUG("CMD: <%s>", cmd);
1952
1953 tcflush(PortFD, TCIOFLUSH);
1954
1955 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1956 {
1957 char errmsg[256];
1958 tty_error_msg(rc, errmsg, 256);
1959 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1960 return false;
1961 }
1962
1963 tcflush(PortFD, TCIOFLUSH);
1964
1965 LOG_INFO("Mount is sleeping...");
1966 return true;
1967}
1968
1969bool LX200Gemini::wakeupMount()
1970{
1971 const char *cmd = ":hW#";
1972
1973 // Response
1974 int rc = 0, nbytes_written = 0;
1975
1976 LOGF_DEBUG("CMD: <%s>", cmd);
1977
1978 tcflush(PortFD, TCIOFLUSH);
1979
1980 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
1981 {
1982 char errmsg[256];
1983 tty_error_msg(rc, errmsg, 256);
1984 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
1985 return false;
1986 }
1987
1988 tcflush(PortFD, TCIOFLUSH);
1989
1990 LOG_INFO("Mount is awake...");
1991 return true;
1992}
1993
1994void LX200Gemini::setTrackState(INDI::Telescope::TelescopeStatus state)
1995{
1996 if (TrackState != state)
1997 TrackState = state;
1998}
1999
2000void LX200Gemini::updateMovementState()
2001{
2002 LX200Gemini::MovementState movementState = getMovementState();
2003
2004 switch (movementState)
2005 {
2006 case NO_MOVEMENT:
2007 if (priorParkingState == PARKED)
2008 setTrackState(SCOPE_PARKED);
2009 else
2010 setTrackState(SCOPE_IDLE);
2011 break;
2012
2013 case TRACKING:
2014 case GUIDING:
2015 setTrackState(SCOPE_TRACKING);
2016 break;
2017
2018 case CENTERING:
2019 case SLEWING:
2020 setTrackState(SCOPE_SLEWING);
2021 break;
2022
2023 case STALLED:
2024 setTrackState(SCOPE_IDLE);
2025 break;
2026 }
2027}
2028
2029void LX200Gemini::updateParkingState()
2030{
2031 LX200Gemini::ParkingState parkingState = getParkingState();
2032
2033 if (parkingState != priorParkingState)
2034 {
2035 if (parkingState == PARKED)
2036 SetParked(true);
2037 else if (parkingState == NOT_PARKED)
2038 SetParked(false);
2039 }
2040 priorParkingState = parkingState;
2041}
2042
2043LX200Gemini::MovementState LX200Gemini::getMovementState()
2044{
2045 const char *cmd = ":Gv#";
2046 char response[2] = { 0 };
2047 int rc = 0, nbytes_read = 0, nbytes_written = 0;
2048
2049 LOGF_DEBUG("CMD: <%s>", cmd);
2050
2051 tcflush(PortFD, TCIOFLUSH);
2052
2053 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
2054 {
2055 char errmsg[256];
2056 tty_error_msg(rc, errmsg, 256);
2057 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2058 return LX200Gemini::MovementState::NO_MOVEMENT;
2059 }
2060
2061 if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2062 {
2063 char errmsg[256];
2064 tty_error_msg(rc, errmsg, 256);
2065 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2066 return LX200Gemini::MovementState::NO_MOVEMENT;
2067 }
2068
2069 response[1] = '\0';
2070
2071 tcflush(PortFD, TCIOFLUSH);
2072
2073 LOGF_DEBUG("RES: <%s>", response);
2074
2075 switch (response[0])
2076 {
2077 case 'N':
2078 return LX200Gemini::MovementState::NO_MOVEMENT;
2079
2080 case 'T':
2081 return LX200Gemini::MovementState::TRACKING;
2082
2083 case 'G':
2084 return LX200Gemini::MovementState::GUIDING;
2085
2086 case 'C':
2087 return LX200Gemini::MovementState::CENTERING;
2088
2089 case 'S':
2090 return LX200Gemini::MovementState::SLEWING;
2091
2092 case '!':
2093 return LX200Gemini::MovementState::STALLED;
2094
2095 default:
2096 return LX200Gemini::MovementState::NO_MOVEMENT;
2097 }
2098}
2099
2100LX200Gemini::ParkingState LX200Gemini::getParkingState()
2101{
2102 const char *cmd = ":h?#";
2103 char response[2] = { 0 };
2104 int rc = 0, nbytes_read = 0, nbytes_written = 0;
2105
2106 LOGF_DEBUG("CMD: <%s>", cmd);
2107
2108 tcflush(PortFD, TCIOFLUSH);
2109
2110 if ((rc = tty_write(PortFD, cmd, 5, &nbytes_written)) != TTY_OK)
2111 {
2112 char errmsg[256];
2113 tty_error_msg(rc, errmsg, 256);
2114 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2115 return LX200Gemini::ParkingState::NOT_PARKED;
2116 }
2117
2118 if ((rc = tty_read(PortFD, response, 1, GEMINI_TIMEOUT, &nbytes_read)) != TTY_OK)
2119 {
2120 char errmsg[256];
2121 tty_error_msg(rc, errmsg, 256);
2122 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2123 return LX200Gemini::ParkingState::NOT_PARKED;
2124 }
2125
2126 response[1] = '\0';
2127
2128 tcflush(PortFD, TCIOFLUSH);
2129
2130 LOGF_DEBUG("RES: <%s>", response);
2131
2132 switch (response[0])
2133 {
2134 case '0':
2135 return LX200Gemini::ParkingState::NOT_PARKED;
2136
2137 case '1':
2138 return LX200Gemini::ParkingState::PARKED;
2139
2140 case '2':
2141 return LX200Gemini::ParkingState::PARK_IN_PROGRESS;
2142
2143 default:
2144 return LX200Gemini::ParkingState::NOT_PARKED;
2145 }
2146}
2147
2149{
2151
2152 IUSaveConfigSwitch(fp, &StartupModeSP);
2153 IUSaveConfigSwitch(fp, &ParkSettingsSP);
2154
2155 return true;
2156}
2157
2158bool LX200Gemini::getGeminiProperty(uint32_t propertyNumber, char* value)
2159{
2160 int rc = TTY_OK;
2161 int nbytes = 0;
2162 char prefix[16] = {0};
2163 char cmd[16] = {0};
2164
2165 switch(propertyNumber)
2166 {
2167 case MOVE_SPEED_ID:
2172 case PEC_REPLAY_ON_ID:
2173 case PEC_REPLAY_OFF_ID:
2174 if(gemini_software_level_ < 5.0)
2175 {
2176 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2177 return false;
2178 }
2179 break;
2181 if(gemini_software_level_ < 5.2)
2182 {
2183 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2184 return false;
2185 }
2186 break;
2187 case FLIP_POINT_EAST_ID:
2188 case FLIP_POINT_WEST_ID:
2191 case SERVO_FIRMWARE:
2192 if(gemini_software_level_ < 6)
2193 {
2194 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2195 return false;
2196 }
2197 break;
2198 default:
2199 ;
2200 }
2201
2202 snprintf(prefix, 16, "<%d:", propertyNumber);
2203
2204 uint8_t checksum = calculateChecksum(prefix);
2205
2206 snprintf(cmd, 16, "%s%c#", prefix, checksum);
2207
2208 LOGF_DEBUG("CMD: <%s>", cmd);
2209
2210 if ((rc = tty_write_string(PortFD, cmd, &nbytes)) != TTY_OK)
2211 {
2212 char errmsg[256];
2213 tty_error_msg(rc, errmsg, 256);
2214 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2215 return false;
2216 }
2217
2218 if ((rc = tty_read_section(PortFD, value, '#', GEMINI_TIMEOUT, &nbytes)) != TTY_OK)
2219 {
2220 char errmsg[256];
2221 tty_error_msg(rc, errmsg, 256);
2222 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2223 return false;
2224 }
2225
2226 value[nbytes - 1] = '\0';
2227
2228 tcflush(PortFD, TCIFLUSH);
2229
2230 LOGF_DEBUG("RES: <%s>", value);
2231 return true;
2232}
2233
2234int LX200Gemini::SendPulseCmd(int8_t direction, uint32_t duration_msec)
2235{
2236 return ::SendPulseCmd(PortFD, direction, duration_msec, true, 1000);
2237}
2238
2239bool LX200Gemini::Flip(double ra, double dec)
2240{
2241 return GotoInternal(ra, dec, true);
2242}
2243
2244bool LX200Gemini::Goto(double ra, double dec)
2245{
2246 return GotoInternal(ra, dec, false);
2247}
2248
2249int LX200Gemini::Flip(int fd)
2250{
2251 DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "<%s>", __FUNCTION__);
2252 char FlipNum[17] = {0};
2253 int error_type;
2254 int nbytes_write = 0, nbytes_read = 0;
2255 const char *command = ":MM#";
2256
2257 DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "CMD <%s>", command);
2258
2259 // Gemini Serial Command
2260 // Returns
2261 // 0 Flip is Possible
2262 // 1 Object below horizon.#
2263 // 2 No object selected.#
2264 // 3 Manual Control.#
2265 // 4 Position unreachable.#
2266 // 5 Not aligned.#
2267 // 6 Outside Limits.#
2268 // 7 Rejected - Mount is parked!#
2269
2270 if ((error_type = tty_write_string(fd, command, &nbytes_write)) != TTY_OK)
2271 return error_type;
2272
2273 error_type = tty_read(fd, FlipNum, 1, LX200_TIMEOUT, &nbytes_read);
2274
2275 if (nbytes_read < 1)
2276 {
2277 DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "RES ERROR <%d>", error_type);
2278 return error_type;
2279 }
2280
2281 /* We don't need to read the string message, just return corresponding error code */
2282 tcflush(fd, TCIFLUSH);
2283
2284 DEBUGFDEVICE(getDeviceName(), DBG_SCOPE, "RES <%c>", FlipNum[0]);
2285
2286 error_type = FlipNum[0] - '0';
2287 if ((error_type >= 0) && (error_type <= 9))
2288 {
2289 return error_type;
2290 }
2291 else
2292 {
2293 return -1;
2294 }
2295}
2296
2297bool LX200Gemini::GotoInternal(double ra, double dec, bool flip)
2298{
2299 const struct timespec timeout = {0, 100000000L};
2300
2301 targetRA = ra;
2302 targetDEC = dec;
2303 char RAStr[64] = {0}, DecStr[64] = {0};
2304 int fracbase = 0;
2305
2306 switch (getLX200EquatorialFormat())
2307 {
2309 fracbase = 360000;
2310 break;
2313 default:
2314 fracbase = 3600;
2315 break;
2316 }
2317
2318 fs_sexa(RAStr, targetRA, 2, fracbase);
2319 fs_sexa(DecStr, targetDEC, 2, fracbase);
2320
2321 // If moving, let's stop it first.
2322 if (EqNP.getState() == IPS_BUSY)
2323 {
2324 if (!isSimulation() && abortSlew(PortFD) < 0)
2325 {
2327 LOG_ERROR("Abort slew failed.");
2328 AbortSP.apply();
2329 return false;
2330 }
2331
2334 LOG_ERROR("Slew aborted.");
2335 AbortSP.apply();
2336 EqNP.apply();
2337
2339 {
2347 }
2348
2349 // sleep for 100 mseconds
2350 nanosleep(&timeout, nullptr);
2351 }
2352
2353 if (!isSimulation())
2354 {
2356 {
2358 LOG_ERROR("Error setting RA/DEC.");
2359 EqNP.apply();
2360 return false;
2361 }
2362
2363 int err = 0;
2364
2365 /* Slew reads the '0', that is not the end of the slew */
2366 if ((err = flip ? Flip(PortFD) : Slew(PortFD)))
2367 {
2368 LOGF_ERROR("Error %s to JNow RA %s - DEC %s", flip ? "Flipping" : "Slewing", RAStr, DecStr);
2369 slewError(err);
2370 return false;
2371 }
2372 }
2373
2375 //EqNP.s = IPS_BUSY;
2376
2377 LOGF_INFO("%s to RA: %s - DEC: %s", flip ? "Flipping" : "Slewing", RAStr, DecStr);
2378
2379 return true;
2380}
2381
2382bool LX200Gemini::setGeminiProperty(uint32_t propertyNumber, char* value)
2383{
2384 int rc = TTY_OK;
2385 int nbytes_written = 0;
2386 char prefix[16] = {0};
2387 char cmd[16] = {0};
2388 switch(propertyNumber)
2389 {
2390 case MOVE_SPEED_ID:
2395 case PEC_REPLAY_ON_ID:
2396 case PEC_REPLAY_OFF_ID:
2397 if(gemini_software_level_ < 5.0)
2398 {
2399 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2400 return false;
2401 }
2402 break;
2404 if(gemini_software_level_ < 5.2)
2405 {
2406 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2407 return false;
2408 }
2409 break;
2411 case SERVO_FIRMWARE:
2412 if(gemini_software_level_ < 6)
2413 {
2414 LOGF_ERROR("Error Gemini Firmware Level %f does not support command %i ", gemini_software_level_, propertyNumber);
2415 return false;
2416 }
2417 break;
2418 default:
2419 ;
2420 }
2421
2422 snprintf(prefix, 16, ">%d:%s", propertyNumber, value);
2423
2424 uint8_t checksum = calculateChecksum(prefix);
2425
2426 snprintf(cmd, 16, "%s%c#", prefix, checksum);
2427
2428 LOGF_DEBUG("CMD: <%s>", cmd);
2429
2430 if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
2431 {
2432 char errmsg[256];
2433 tty_error_msg(rc, errmsg, 256);
2434 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2435 return false;
2436 }
2437
2438 tcflush(PortFD, TCIFLUSH);
2439
2440 return true;
2441}
2442
2444{
2445 char empty[] = "";
2446 return setGeminiProperty(131 + mode, empty);
2447}
2448
2450{
2451 return SetTrackMode(enabled ? TrackModeSP.findOnSwitchIndex() : GEMINI_TRACK_TERRESTRIAL);
2452}
2453
2454uint8_t LX200Gemini::calculateChecksum(char *cmd)
2455{
2456 uint8_t result = cmd[0];
2457
2458 for (size_t i = 1; i < strlen(cmd); i++)
2459 result = result ^ cmd[i];
2460
2461 result = result % 128;
2462 result += 64;
2463
2464 return result;
2465}
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
void setConnectionType(int type)
TODO should be renamed to setDefaultConnectionType.
void setDefaultPort(uint32_t addressPort)
bool isConnected() const
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...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
TelescopeStatus TrackState
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Connection::TCP * tcpConnection
void setPECState(TelescopePECState state)
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
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
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 * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
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.
INDI::PropertySwitch ParkSP
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch PECStateSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertySwitch MovementWESP
INDI::PropertyNumber LocationNP
double ra
double dec
#define MAX_VALUE_LENGTH
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
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:653
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:194
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition indicom.c:1301
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
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:130
void tty_set_gemini_udp_format(int enabled)
Definition indicom.c:359
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal 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:33
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:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
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:376
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:416
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:398
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:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
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,...)
#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
#define getDeviceName
int fd
#define LX200_TIMEOUT
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)
#define getVersionTime(fd, x)
#define getLX200DEC(fd, x)
#define getVersionNumber(fd, x)
#define getLX200RA(fd, x)
#define getProductName(fd, x)
@ 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
#define CENTERING_SPEED_ID
#define FLIP_POINT_EAST_ID
#define FLIP_POINT_WEST_ID
#define EAST_SAFETY_LIMIT_ID
#define PEC_MAX_STEPS_ID
#define SERVO_POINTING_PRECISION_ID
#define PEC_START_TRAINING_ID
#define PEC_GUIDING_SPEED_ID
#define MANUAL_SLEWING_SPEED_ID
#define WEST_SAFETY_LIMIT_ID
#define PEC_ENABLE_AT_BOOT_ID
#define GOTO_SLEWING_SPEED_ID
#define PEC_ABORT_TRAINING_ID
#define SET_SAFETY_LIMIT_TO_CURRENT_ID
#define MOVE_SPEED_ID
#define GUIDING_SPEED_ID
#define GUIDING_SPEED_DEC_ID
#define SERVO_FIRMWARE
#define FLIP_POINTS_ENABLED_ID
#define PEC_REPLAY_OFF_ID
#define PEC_COUNTER_ID
#define WEST_GOTO_SAFETY_LIMIT_ID
#define PEC_STATUS_ID
#define PEC_REPLAY_ON_ID
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
ISState s
Definition indiapi.h:353
char name[MAXINDINAME]
Definition indiapi.h:248
char * text
Definition indiapi.h:230