Instrument Neutral Distributed Interface INDI 2.2.5
lx200gotonova.cpp
Go to the documentation of this file.
1/*
2 GotoNova INDI driver
3
4 Copyright (C) 2017 Jasem Mutlaq
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "lx200gotonova.h"
22
23#include "indicom.h"
24#include "lx200driver.h"
25
26#include <libnova/transform.h>
27
28#include <cmath>
29#include <cstring>
30#include <termios.h>
31#include <unistd.h>
32
33/* Simulation Parameters */
34#define SLEWRATE 1 /* slew rate, degrees/s */
35#define SIDRATE 0.004178 /* sidereal rate, degrees/s */
36#define GOTONOVA_TIMEOUT 5 /* timeout */
37#define GOTONOVA_CALDATE_RESULT " # #" /* result of calendar date */
38
49
51{
53
54
55 // strcpy(SlewRateS[0].label, "16x");
56 // strcpy(SlewRateS[1].label, "64x");
57 // strcpy(SlewRateS[2].label, "256x");
58 // strcpy(SlewRateS[3].label, "512x");
59 SlewRateSP[0].setLabel("16x");
60 SlewRateSP[1].setLabel("64x");
61 SlewRateSP[2].setLabel("256x");
62 SlewRateSP[3].setLabel("512x");
63
64 // Sync Type
65 IUFillSwitch(&SyncCMRS[USE_REGULAR_SYNC], ":CM#", ":CM#", ISS_ON);
66 IUFillSwitch(&SyncCMRS[USE_CMR_SYNC], ":CMR#", ":CMR#", ISS_OFF);
67 IUFillSwitchVector(&SyncCMRSP, SyncCMRS, 2, getDeviceName(), "SYNCCMR", "Sync", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
68 IPS_IDLE);
69
70 // Park Position
71 IUFillSwitch(&ParkPositionS[PS_NORTH_POLE], "North Pole", "", ISS_ON);
72 IUFillSwitch(&ParkPositionS[PS_LEFT_VERTICAL], "Left and Vertical", "", ISS_OFF);
73 IUFillSwitch(&ParkPositionS[PS_LEFT_HORIZON], "Left and Horizon", "", ISS_OFF);
74 IUFillSwitch(&ParkPositionS[PS_RIGHT_VERTICAL], "Right and Vertical", "", ISS_OFF);
75 IUFillSwitch(&ParkPositionS[PS_RIGHT_HORIZON], "Right and Horizon", "", ISS_OFF);
76 IUFillSwitchVector(&ParkPositionSP, ParkPositionS, 5, getDeviceName(), "PARKING_POSITION", "Parking Position", SITE_TAB,
78 IPS_IDLE);
79
80 // Guide Rate
81 IUFillSwitch(&GuideRateS[0], "1.0x", "", ISS_ON);
82 IUFillSwitch(&GuideRateS[1], "0.8x", "", ISS_OFF);
83 IUFillSwitch(&GuideRateS[2], "0.6x", "", ISS_OFF);
84 IUFillSwitch(&GuideRateS[3], "0.4x", "", ISS_OFF);
85 IUFillSwitchVector(&GuideRateSP, GuideRateS, 4, getDeviceName(), "GUIDE_RATE", "Guide Rate", MOTION_TAB, IP_RW, ISR_1OFMANY,
86 0,
87 IPS_IDLE);
88
89 // Track Mode -- We do not support Custom so let's just define the first 3 properties
90 TrackModeSP = 3;
91
92 return true;
93}
94
96{
98
99 if (isConnected())
100 {
101 defineProperty(&SyncCMRSP);
102 defineProperty(&ParkPositionSP);
103 defineProperty(&GuideRateSP);
104 }
105 else
106 {
107 deleteProperty(SyncCMRSP.name);
108 deleteProperty(ParkPositionSP.name);
109 deleteProperty(GuideRateSP.name);
110 }
111
112 return true;
113}
114
116{
117 return (const char *)"GotoNova";
118}
119
121{
122 if (isSimulation())
123 return true;
124
125 const struct timespec timeout = {0, 50000000L};
126 char initCMD[] = ":V#";
127 int errcode = 0;
128 char errmsg[MAXRBUF];
129 char response[8];
130 int nbytes_read = 0;
131 int nbytes_written = 0;
132
133 LOG_DEBUG("Initializing IOptron using :V# CMD...");
134
135 for (int i = 0; i < 2; i++)
136 {
137 if ((errcode = tty_write(PortFD, initCMD, 3, &nbytes_written)) != TTY_OK)
138 {
139 tty_error_msg(errcode, errmsg, MAXRBUF);
140 LOGF_ERROR("%s", errmsg);
141 nanosleep(&timeout, nullptr);
142 continue;
143 }
144
145 if ((errcode = tty_read_section(PortFD, response, '#', 3, &nbytes_read)))
146 {
147 tty_error_msg(errcode, errmsg, MAXRBUF);
148 LOGF_ERROR("%s", errmsg);
149 nanosleep(&timeout, nullptr);
150 continue;
151 }
152
153 if (nbytes_read > 0)
154 {
155 response[nbytes_read] = '\0';
156 LOGF_DEBUG("RES (%s)", response);
157
158 if (!strcmp(response, "V1.00#"))
159 return true;
160 }
161
162 nanosleep(&timeout, nullptr);
163 }
164
165 return false;
166}
167
168bool LX200GotoNova::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
169{
170 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
171 {
172 // Park Position
173 if (!strcmp(ParkPositionSP.name, name))
174 {
175 int currentSwitch = IUFindOnSwitchIndex(&ParkPositionSP);
176 IUUpdateSwitch(&ParkPositionSP, states, names, n);
177 if (setGotoNovaParkPosition(IUFindOnSwitchIndex(&ParkPositionSP)) == TTY_OK)
178 ParkPositionSP.s = IPS_OK;
179 else
180 {
181 IUResetSwitch(&ParkPositionSP);
182 ParkPositionS[currentSwitch].s = ISS_ON;
183 ParkPositionSP.s = IPS_ALERT;
184 }
185
186 IDSetSwitch(&ParkPositionSP, nullptr);
187 return true;
188 }
189
190 // Guide Rate
191 if (!strcmp(GuideRateSP.name, name))
192 {
193 int currentSwitch = IUFindOnSwitchIndex(&ParkPositionSP);
194 IUUpdateSwitch(&GuideRateSP, states, names, n);
195 if (setGotoNovaGuideRate(IUFindOnSwitchIndex(&GuideRateSP)) == TTY_OK)
196 GuideRateSP.s = IPS_OK;
197 else
198 {
199 IUResetSwitch(&GuideRateSP);
200 GuideRateS[currentSwitch].s = ISS_ON;
201 GuideRateSP.s = IPS_ALERT;
202 }
203
204 IDSetSwitch(&GuideRateSP, nullptr);
205 return true;
206 }
207
208 // Sync type
209 if (!strcmp(name, SyncCMRSP.name))
210 {
211 IUResetSwitch(&SyncCMRSP);
212 IUUpdateSwitch(&SyncCMRSP, states, names, n);
213 IUFindOnSwitchIndex(&SyncCMRSP);
214 SyncCMRSP.s = IPS_OK;
215 IDSetSwitch(&SyncCMRSP, nullptr);
216 return true;
217 }
218 }
219
220 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
221}
222
224{
225 int errcode = 0;
226 char errmsg[MAXRBUF];
227 char response[8];
228 int nbytes_read = 0;
229 int nbytes_written = 0;
230
231 const char *cmd = ":SE?#";
232
233 LOGF_DEBUG("CMD (%s)", cmd);
234
235 if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
236 {
237 tty_error_msg(errcode, errmsg, MAXRBUF);
238 LOGF_ERROR("%s", errmsg);
239 return false;
240 }
241
242 if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
243 {
244 tty_error_msg(errcode, errmsg, MAXRBUF);
245 LOGF_ERROR("%s", errmsg);
246 return false;
247 }
248
249 if (nbytes_read > 0)
250 {
251 response[nbytes_read] = '\0';
252 LOGF_DEBUG("RES (%s)", response);
253
254 tcflush(PortFD, TCIFLUSH);
255
256 if (response[0] == '0')
257 return true;
258 else
259 return false;
260 }
261
262 LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
263 return false;
264}
265
267{
268 int guideRate = -1;
269 int rc = getGotoNovaGuideRate(&guideRate);
270 if (rc == TTY_OK)
271 {
272 IUResetSwitch(&GuideRateSP);
273 GuideRateS[guideRate].s = ISS_ON;
274 GuideRateSP.s = IPS_OK;
275 IDSetSwitch(&GuideRateSP, nullptr);
276 }
277}
278
279bool LX200GotoNova::Goto(double r, double d)
280{
281 const struct timespec timeout = {0, 100000000L};
282
283 targetRA = r;
284 targetDEC = d;
285 char RAStr[64], DecStr[64];
286
287 fs_sexa(RAStr, targetRA, 2, 3600);
288 fs_sexa(DecStr, targetDEC, 2, 3600);
289
290 // If moving, let's stop it first.
291 if (EqNP.getState() == IPS_BUSY)
292 {
293 if (!isSimulation() && abortSlew(PortFD) < 0)
294 {
296 LOG_ERROR("Abort slew failed.");
297 AbortSP.apply();
298 return false;
299 }
300
303 LOG_ERROR("Slew aborted.");
304 AbortSP.apply();
305 EqNP.apply();
306
308 {
316 }
317
318 // sleep for 100 mseconds
319 nanosleep(&timeout, nullptr);
320 }
321
322 if (!isSimulation())
323 {
324 if (setObjectRA(PortFD, targetRA, true) < 0 || (setObjectDEC(PortFD, targetDEC, true)) < 0)
325 {
327 LOG_ERROR("Error setting RA/DEC");
328 EqNP.apply();
329 return false;
330 }
331
332 if (slewGotoNova() == 0)
333 {
335 LOGF_ERROR("Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
336 EqNP.apply();
337 slewError(1);
338 return false;
339 }
340 }
341
344
345 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
346 return true;
347}
348
349bool LX200GotoNova::Sync(double ra, double dec)
350{
351 char syncString[256];
352
353 int syncType = IUFindOnSwitchIndex(&SyncCMRSP);
354
355 if (!isSimulation())
356 {
357 if (setObjectRA(PortFD, ra, true) < 0 || setObjectDEC(PortFD, dec, true) < 0)
358 {
360 LOG_ERROR("Error setting RA/DEC. Unable to Sync.");
361 EqNP.apply();
362 return false;
363 }
364
365 bool syncOK = true;
366
367 switch (syncType)
368 {
369 case USE_REGULAR_SYNC:
370 if (::Sync(PortFD, syncString) < 0)
371 syncOK = false;
372 break;
373
374 case USE_CMR_SYNC:
375 if (GotonovaSyncCMR(syncString) < 0)
376 syncOK = false;
377 break;
378
379 default:
380 break;
381 }
382
383 if (syncOK == false)
384 {
386 LOG_ERROR("Synchronization failed");
387 EqNP.apply();
388 return false;
389 }
390
391 }
392
393 currentRA = ra;
394 currentDEC = dec;
395
396 LOGF_DEBUG("%s Synchronization successful %s", (syncType == USE_REGULAR_SYNC ? "CM" : "CMR"), syncString);
397 LOG_INFO("Synchronization successful.");
398
400
402
403 return true;
404}
405
406int LX200GotoNova::GotonovaSyncCMR(char *matchedObject)
407{
408 const struct timespec timeout = {0, 10000000L};
409 int error_type;
410 int nbytes_write = 0;
411 int nbytes_read = 0;
412
413 LOGF_DEBUG("CMD <%s>", "#:CMR#");
414
415 if ((error_type = tty_write_string(PortFD, ":CMR#", &nbytes_write)) != TTY_OK)
416 return error_type;
417
418 if ((error_type = tty_read_section(PortFD, matchedObject, '#', 3, &nbytes_read)) != TTY_OK)
419 return error_type;
420
421 matchedObject[nbytes_read - 1] = '\0';
422
423 LOGF_DEBUG("RES <%s>", matchedObject);
424
425 /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
426 nanosleep(&timeout, nullptr);
427
428 tcflush(PortFD, TCIFLUSH);
429
430 return 0;
431}
432
433
434int LX200GotoNova::slewGotoNova()
435{
436 DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
437 char slewNum[2];
438 int error_type;
439 int nbytes_write = 0, nbytes_read = 0;
440
441 DEBUGF(DBG_SCOPE, "CMD <%s>", ":MS#");
442
443 if ((error_type = tty_write_string(PortFD, ":MS#", &nbytes_write)) != TTY_OK)
444 return error_type;
445
446 error_type = tty_read(PortFD, slewNum, 1, 3, &nbytes_read);
447
448 if (nbytes_read < 1)
449 {
450 DEBUGF(DBG_SCOPE, "RES ERROR <%d>", error_type);
451 return error_type;
452 }
453
454 /* We don't need to read the string message, just return corresponding error code */
455 tcflush(PortFD, TCIFLUSH);
456
457 DEBUGF(DBG_SCOPE, "RES <%c>", slewNum[0]);
458
459 return slewNum[0];
460}
461
463{
464 if (isSimulation())
465 return true;
466
467 char cmd[8];
468 int errcode = 0;
469 char errmsg[MAXRBUF];
470 int nbytes_written = 0;
471
472 snprintf(cmd, 8, ":RC%d#", index);
473
474 LOGF_DEBUG("CMD (%s)", cmd);
475
476 if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
477 {
478 tty_error_msg(errcode, errmsg, MAXRBUF);
479 LOGF_ERROR("%s", errmsg);
480 return false;
481 }
482
483 return true;
484}
485
486bool LX200GotoNova::updateTime(ln_date *utc, double utc_offset)
487{
488 struct ln_zonedate ltm;
489
490 if (isSimulation())
491 return true;
492
493 ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
494
495 JD = ln_get_julian_day(utc);
496
497 LOGF_DEBUG("New JD is %f", (float)JD);
498
499 // Set Local Time
500 if (setLocalTime(PortFD, ltm.hours, ltm.minutes, ltm.seconds, true) < 0)
501 {
502 LOG_ERROR("Error setting local time.");
503 return false;
504 }
505
506 if (setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
507 {
508 LOG_ERROR("Error setting local date.");
509 return false;
510 }
511
512 if (setGotoNovaUTCOffset(utc_offset) < 0)
513 {
514 LOG_ERROR("Error setting UTC Offset.");
515 return false;
516 }
517
518 return true;
519}
520
521int LX200GotoNova::setCalenderDate(int fd, int dd, int mm, int yy)
522{
523 const struct timespec timeout = {0, 10000000L};
524 char read_buffer[16];
525 char response[67];
526 char good_result[] = GOTONOVA_CALDATE_RESULT;
527 int error_type;
528 int nbytes_write = 0, nbytes_read = 0;
529 yy = yy % 100;
530
531 snprintf(read_buffer, sizeof(read_buffer), ":SC %02d/%02d/%02d#", mm, dd, yy);
532
533 DEBUGF(DBG_SCOPE, "CMD <%s>", read_buffer);
534
535 tcflush(fd, TCIFLUSH);
536
537 if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
538 return error_type;
539
540 error_type = tty_read(fd, response, sizeof(response), GOTONOVA_TIMEOUT, &nbytes_read);
541
542 tcflush(fd, TCIFLUSH);
543
544 if (nbytes_read < 1)
545 {
546 LOG_ERROR("Unable to read response");
547 return error_type;
548 }
549
550 response[nbytes_read] = '\0';
551
552 DEBUGF(DBG_SCOPE, "RES <%s>", response);
553
554 if (strncmp(response, good_result, strlen(good_result)) == 0)
555 {
556 return 0;
557 }
558
559 /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
560 nanosleep(&timeout, nullptr);
561 tcflush(fd, TCIFLUSH);
562
563 LOGF_DEBUG("Set date failed! Response: <%s>", response);
564
565 return -1;
566}
567
568bool LX200GotoNova::updateLocation(double latitude, double longitude, double elevation)
569{
570 INDI_UNUSED(elevation);
571
572 if (isSimulation())
573 return true;
574
575 double final_longitude;
576
577 if (longitude > 180)
578 final_longitude = longitude - 360.0;
579 else
580 final_longitude = longitude;
581
582 if (!isSimulation() && setGotoNovaLongitude(final_longitude) < 0)
583 {
584 LOG_ERROR("Error setting site longitude coordinates");
585 return false;
586 }
587
588 if (!isSimulation() && setGotoNovaLatitude(latitude) < 0)
589 {
590 LOG_ERROR("Error setting site latitude coordinates");
591 return false;
592 }
593
594 char l[32], L[32];
595 fs_sexa(l, latitude, 3, 3600);
596 fs_sexa(L, longitude, 4, 3600);
597
598 LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
599
600 return true;
601}
602
603int LX200GotoNova::setGotoNovaLongitude(double Long)
604{
605 int d, m, s;
606 char sign;
607 char temp_string[32];
608
609 if (Long > 0)
610 sign = '+';
611 else
612 sign = '-';
613
614 getSexComponents(Long, &d, &m, &s);
615
616 snprintf(temp_string, sizeof(temp_string), ":Sg %c%03d*%02d:%02d#", sign, abs(d), m, s);
617
618 return (setGotoNovaStandardProcedure(PortFD, temp_string));
619}
620
621int LX200GotoNova::setGotoNovaLatitude(double Lat)
622{
623 int d, m, s;
624 char sign;
625 char temp_string[32];
626
627 if (Lat > 0)
628 sign = '+';
629 else
630 sign = '-';
631
632 getSexComponents(Lat, &d, &m, &s);
633
634 snprintf(temp_string, sizeof(temp_string), ":St %c%02d*%02d:%02d#", sign, abs(d), m, s);
635
636 return (setGotoNovaStandardProcedure(PortFD, temp_string));
637}
638
639int LX200GotoNova::setGotoNovaUTCOffset(double hours)
640{
641 char temp_string[16];
642 char sign;
643 int h = 0, m = 0, s = 0;
644
645 if (hours > 0)
646 sign = '+';
647 else
648 sign = '-';
649
650 getSexComponents(hours, &h, &m, &s);
651
652 snprintf(temp_string, sizeof(temp_string), ":SG %c%02d#", sign, abs(h));
653
654 return (setGotoNovaStandardProcedure(PortFD, temp_string));
655}
656
657int LX200GotoNova::setGotoNovaStandardProcedure(int fd, const char *data)
658{
659 const struct timespec timeout = {0, 10000000L};
660 char bool_return[2];
661 int error_type;
662 int nbytes_write = 0, nbytes_read = 0;
663
664 DEBUGF(DBG_SCOPE, "CMD <%s>", data);
665
666 if ((error_type = tty_write_string(fd, data, &nbytes_write)) != TTY_OK)
667 return error_type;
668
669 error_type = tty_read(fd, bool_return, 1, 5, &nbytes_read);
670
671 // JM: Hack from Jon in the INDI forums to fix longitude/latitude settings failure on GotoNova
672 nanosleep(&timeout, nullptr);
673 tcflush(fd, TCIFLUSH);
674 nanosleep(&timeout, nullptr);
675
676 if (nbytes_read < 1)
677 return error_type;
678
679 DEBUGF(DBG_SCOPE, "RES <%c>", bool_return[0]);
680
681 if (bool_return[0] == '0')
682 {
683 DEBUGF(DBG_SCOPE, "CMD <%s> failed.", data);
684 return -1;
685 }
686
687 DEBUGF(DBG_SCOPE, "CMD <%s> successful.", data);
688
689 return 0;
690}
691
693{
694 return (setGotoNovaTrackMode(mode) == 0);
695}
696
697int LX200GotoNova::setGotoNovaTrackMode(int mode)
698{
699 DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
700
701 char cmd[8];
702 snprintf(cmd, 8, ":STR%d#", mode);
703
704 return setGotoNovaStandardProcedure(PortFD, cmd);
705}
706
708{
709 DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
710 int error_type;
711 int nbytes_write = 0;
712
713 if ((error_type = tty_write_string(PortFD, ":PK#", &nbytes_write)) != TTY_OK)
714 return error_type;
715
716 tcflush(PortFD, TCIFLUSH);
717
720 LOG_INFO("Parking is in progress...");
721
722 return true;
723}
724
726{
727 SetParked(false);
728 return true;
729}
730
732{
733 if (!isConnected())
734 return false;
735
736 if (isSimulation())
737 {
738 mountSim();
739 return true;
740 }
741
743 {
744 // Check if LX200 is done slewing
745 if (isSlewComplete())
746 {
748 LOG_INFO("Slew is complete. Tracking...");
749 }
750 }
751 else if (TrackState == SCOPE_PARKING)
752 {
753 if (isSlewComplete())
754 {
755 SetParked(true);
756 }
757 }
758
760 {
762 LOG_ERROR("Error reading RA/DEC.");
763 EqNP.apply();
764 return false;
765 }
766
768
769 syncSideOfPier();
770
771 return true;
772}
773
774void LX200GotoNova::mountSim()
775{
776 static struct timeval ltv;
777 struct timeval tv;
778 double dt, da, dx;
779 int nlocked;
780
781 /* update elapsed time since last poll, don't presume exactly POLLMS */
782 gettimeofday(&tv, nullptr);
783
784 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
785 ltv = tv;
786
787 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
788 ltv = tv;
789 da = SLEWRATE * dt;
790
791 /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
792 switch (TrackState)
793 {
794 case SCOPE_TRACKING:
795 /* RA moves at sidereal, Dec stands still */
796 currentRA += (SIDRATE * dt / 15.);
797 break;
798
799 case SCOPE_SLEWING:
800 case SCOPE_PARKING:
801 /* slewing - nail it when both within one pulse @ SLEWRATE */
802 nlocked = 0;
803
804 dx = targetRA - currentRA;
805
806 if (fabs(dx) <= da)
807 {
809 nlocked++;
810 }
811 else if (dx > 0)
812 currentRA += da / 15.;
813 else
814 currentRA -= da / 15.;
815
816 dx = targetDEC - currentDEC;
817 if (fabs(dx) <= da)
818 {
820 nlocked++;
821 }
822 else if (dx > 0)
823 currentDEC += da;
824 else
825 currentDEC -= da;
826
827 if (nlocked == 2)
828 {
831 else
832 SetParked(true);
833 }
834
835 break;
836
837 default:
838 break;
839 }
840
842}
843
844int LX200GotoNova::getGotoNovaGuideRate(int *rate)
845{
846 char cmd[] = ":GGS#";
847 int errcode = 0;
848 char errmsg[MAXRBUF];
849 char response[8];
850 int nbytes_read = 0;
851 int nbytes_written = 0;
852
853 LOGF_DEBUG("CMD (%s)", cmd);
854
855 if (isSimulation())
856 {
857 snprintf(response, 8, "%d#", IUFindOnSwitchIndex(&GuideRateSP));
858 nbytes_read = strlen(response);
859 }
860 else
861 {
862 tcflush(PortFD, TCIFLUSH);
863
864 if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
865 {
866 tty_error_msg(errcode, errmsg, MAXRBUF);
867 LOGF_ERROR("%s", errmsg);
868 return errcode;
869 }
870
871 if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
872 {
873 tty_error_msg(errcode, errmsg, MAXRBUF);
874 LOGF_ERROR("%s", errmsg);
875 return errcode;
876 }
877 }
878
879 if (nbytes_read > 0)
880 {
881 response[nbytes_read] = '\0';
882 LOGF_DEBUG("RES (%s)", response);
883
884 *rate = atoi(response);
885
886 return 0;
887 }
888
889 LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
890 return -1;
891}
892
893int LX200GotoNova::setGotoNovaGuideRate(int rate)
894{
895 char cmd[16];
896 int errcode = 0;
897 char errmsg[MAXRBUF];
898 int nbytes_written = 0;
899
900 snprintf(cmd, 16, ":SGS%0d#", rate);
901
902 LOGF_DEBUG("CMD (%s)", cmd);
903
904 if (isSimulation())
905 {
906 return 0;
907 }
908
909 tcflush(PortFD, TCIFLUSH);
910
911 if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
912 {
913 tty_error_msg(errcode, errmsg, MAXRBUF);
914 LOGF_ERROR("%s", errmsg);
915 return errcode;
916 }
917
918 return 0;
919}
920
921int LX200GotoNova::setGotoNovaParkPosition(int position)
922{
923 char temp_string[16];
924
925 snprintf(temp_string, sizeof(temp_string), ":STPKP%d#", position);
926
927 return (setGotoNovaStandardProcedure(PortFD, temp_string));
928}
929
930void LX200GotoNova::syncSideOfPier()
931{
932 const char *cmd = ":pS#";
933 // Response
934 char response[16] = { 0 };
935 int rc = 0, nbytes_read = 0, nbytes_written = 0;
936
937 LOGF_DEBUG("CMD: <%s>", cmd);
938
939 tcflush(PortFD, TCIOFLUSH);
940
941 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
942 {
943 char errmsg[256];
944 tty_error_msg(rc, errmsg, 256);
945 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
946 return;
947 }
948
949 // Read Side
950 if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
951 {
952 char errmsg[256];
953 tty_error_msg(rc, errmsg, 256);
954 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
955 return;
956 }
957
958 response[nbytes_read - 1] = '\0';
959
960 tcflush(PortFD, TCIOFLUSH);
961
962 LOGF_DEBUG("RES: <%s>", response);
963
964 if (!strcmp(response, "East"))
966 else
968
969}
970
972{
974
975 IUSaveConfigSwitch(fp, &SyncCMRSP);
976 IUSaveConfigSwitch(fp, &ParkPositionSP);
977
978 return true;
979}
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
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setLabel(const char *label)
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....
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
virtual bool isSlewComplete() override
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool checkConnection() override
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool Park() override
Park the telescope to its home position.
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual void getBasicData() override
virtual const char * getDefaultName() override
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void slewError(int slewCode)
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
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 * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertySwitch MovementWESP
#define setLocalTime(fd, x, y, z)
#define SLEWRATE
Definition ieqpro.cpp:35
double ra
double dec
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
@ 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
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 tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void getSexComponents(double value, int *d, int *m, int *s)
Definition indicom.c:243
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
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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 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 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
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
int fd
#define SIDRATE
int setObjectRA(int fd, double ra, bool addSpace)
int setObjectDEC(int fd, double dec, bool addSpace)
int abortSlew(int fd)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
#define getLX200DEC(fd, x)
#define getLX200RA(fd, x)
#define GOTONOVA_CALDATE_RESULT
#define GOTONOVA_TIMEOUT
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:369