Instrument Neutral Distributed Interface INDI  2.0.2
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 
40 {
41  setVersion(1, 0);
42 
44 
47  4);
48 }
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 
60  // Sync Type
61  IUFillSwitch(&SyncCMRS[USE_REGULAR_SYNC], ":CM#", ":CM#", ISS_ON);
62  IUFillSwitch(&SyncCMRS[USE_CMR_SYNC], ":CMR#", ":CMR#", ISS_OFF);
63  IUFillSwitchVector(&SyncCMRSP, SyncCMRS, 2, getDeviceName(), "SYNCCMR", "Sync", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
64  IPS_IDLE);
65 
66  // Park Position
67  IUFillSwitch(&ParkPositionS[PS_NORTH_POLE], "North Pole", "", ISS_ON);
68  IUFillSwitch(&ParkPositionS[PS_LEFT_VERTICAL], "Left and Vertical", "", ISS_OFF);
69  IUFillSwitch(&ParkPositionS[PS_LEFT_HORIZON], "Left and Horizon", "", ISS_OFF);
70  IUFillSwitch(&ParkPositionS[PS_RIGHT_VERTICAL], "Right and Vertical", "", ISS_OFF);
71  IUFillSwitch(&ParkPositionS[PS_RIGHT_HORIZON], "Right and Horizon", "", ISS_OFF);
72  IUFillSwitchVector(&ParkPositionSP, ParkPositionS, 5, getDeviceName(), "PARKING_POSITION", "Parking Position", SITE_TAB,
73  IP_RW, ISR_1OFMANY, 0,
74  IPS_IDLE);
75 
76  // Guide Rate
77  IUFillSwitch(&GuideRateS[0], "1.0x", "", ISS_ON);
78  IUFillSwitch(&GuideRateS[1], "0.8x", "", ISS_OFF);
79  IUFillSwitch(&GuideRateS[2], "0.6x", "", ISS_OFF);
80  IUFillSwitch(&GuideRateS[3], "0.4x", "", ISS_OFF);
81  IUFillSwitchVector(&GuideRateSP, GuideRateS, 4, getDeviceName(), "GUIDE_RATE", "Guide Rate", MOTION_TAB, IP_RW, ISR_1OFMANY,
82  0,
83  IPS_IDLE);
84 
85  // Track Mode -- We do not support Custom so let's just define the first 3 properties
86  TrackModeSP.nsp = 3;
87 
88  return true;
89 }
90 
92 {
94 
95  if (isConnected())
96  {
97  defineProperty(&SyncCMRSP);
98  defineProperty(&ParkPositionSP);
99  defineProperty(&GuideRateSP);
100  }
101  else
102  {
103  deleteProperty(SyncCMRSP.name);
104  deleteProperty(ParkPositionSP.name);
105  deleteProperty(GuideRateSP.name);
106  }
107 
108  return true;
109 }
110 
112 {
113  return (const char *)"GotoNova";
114 }
115 
117 {
118  if (isSimulation())
119  return true;
120 
121  const struct timespec timeout = {0, 50000000L};
122  char initCMD[] = ":V#";
123  int errcode = 0;
124  char errmsg[MAXRBUF];
125  char response[8];
126  int nbytes_read = 0;
127  int nbytes_written = 0;
128 
129  LOG_DEBUG("Initializing IOptron using :V# CMD...");
130 
131  for (int i = 0; i < 2; i++)
132  {
133  if ((errcode = tty_write(PortFD, initCMD, 3, &nbytes_written)) != TTY_OK)
134  {
135  tty_error_msg(errcode, errmsg, MAXRBUF);
136  LOGF_ERROR("%s", errmsg);
137  nanosleep(&timeout, nullptr);
138  continue;
139  }
140 
141  if ((errcode = tty_read_section(PortFD, response, '#', 3, &nbytes_read)))
142  {
143  tty_error_msg(errcode, errmsg, MAXRBUF);
144  LOGF_ERROR("%s", errmsg);
145  nanosleep(&timeout, nullptr);
146  continue;
147  }
148 
149  if (nbytes_read > 0)
150  {
151  response[nbytes_read] = '\0';
152  LOGF_DEBUG("RES (%s)", response);
153 
154  if (!strcmp(response, "V1.00#"))
155  return true;
156  }
157 
158  nanosleep(&timeout, nullptr);
159  }
160 
161  return false;
162 }
163 
164 bool LX200GotoNova::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
165 {
166  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
167  {
168  // Park Position
169  if (!strcmp(ParkPositionSP.name, name))
170  {
171  int currentSwitch = IUFindOnSwitchIndex(&ParkPositionSP);
172  IUUpdateSwitch(&ParkPositionSP, states, names, n);
173  if (setGotoNovaParkPosition(IUFindOnSwitchIndex(&ParkPositionSP)) == TTY_OK)
174  ParkPositionSP.s = IPS_OK;
175  else
176  {
177  IUResetSwitch(&ParkPositionSP);
178  ParkPositionS[currentSwitch].s = ISS_ON;
179  ParkPositionSP.s = IPS_ALERT;
180  }
181 
182  IDSetSwitch(&ParkPositionSP, nullptr);
183  return true;
184  }
185 
186  // Guide Rate
187  if (!strcmp(GuideRateSP.name, name))
188  {
189  int currentSwitch = IUFindOnSwitchIndex(&ParkPositionSP);
190  IUUpdateSwitch(&GuideRateSP, states, names, n);
191  if (setGotoNovaGuideRate(IUFindOnSwitchIndex(&GuideRateSP)) == TTY_OK)
192  GuideRateSP.s = IPS_OK;
193  else
194  {
195  IUResetSwitch(&GuideRateSP);
196  GuideRateS[currentSwitch].s = ISS_ON;
197  GuideRateSP.s = IPS_ALERT;
198  }
199 
200  IDSetSwitch(&GuideRateSP, nullptr);
201  return true;
202  }
203 
204  // Sync type
205  if (!strcmp(name, SyncCMRSP.name))
206  {
207  IUResetSwitch(&SyncCMRSP);
208  IUUpdateSwitch(&SyncCMRSP, states, names, n);
209  IUFindOnSwitchIndex(&SyncCMRSP);
210  SyncCMRSP.s = IPS_OK;
211  IDSetSwitch(&SyncCMRSP, nullptr);
212  return true;
213  }
214  }
215 
216  return LX200Generic::ISNewSwitch(dev, name, states, names, n);
217 }
218 
220 {
221  int errcode = 0;
222  char errmsg[MAXRBUF];
223  char response[8];
224  int nbytes_read = 0;
225  int nbytes_written = 0;
226 
227  const char *cmd = ":SE?#";
228 
229  LOGF_DEBUG("CMD (%s)", cmd);
230 
231  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
232  {
233  tty_error_msg(errcode, errmsg, MAXRBUF);
234  LOGF_ERROR("%s", errmsg);
235  return false;
236  }
237 
238  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
239  {
240  tty_error_msg(errcode, errmsg, MAXRBUF);
241  LOGF_ERROR("%s", errmsg);
242  return false;
243  }
244 
245  if (nbytes_read > 0)
246  {
247  response[nbytes_read] = '\0';
248  LOGF_DEBUG("RES (%s)", response);
249 
250  tcflush(PortFD, TCIFLUSH);
251 
252  if (response[0] == '0')
253  return true;
254  else
255  return false;
256  }
257 
258  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
259  return false;
260 }
261 
263 {
264  int guideRate = -1;
265  int rc = getGotoNovaGuideRate(&guideRate);
266  if (rc == TTY_OK)
267  {
268  IUResetSwitch(&GuideRateSP);
269  GuideRateS[guideRate].s = ISS_ON;
270  GuideRateSP.s = IPS_OK;
271  IDSetSwitch(&GuideRateSP, nullptr);
272  }
273 }
274 
275 bool LX200GotoNova::Goto(double r, double d)
276 {
277  const struct timespec timeout = {0, 100000000L};
278 
279  targetRA = r;
280  targetDEC = d;
281  char RAStr[64], DecStr[64];
282 
283  fs_sexa(RAStr, targetRA, 2, 3600);
284  fs_sexa(DecStr, targetDEC, 2, 3600);
285 
286  // If moving, let's stop it first.
287  if (EqNP.s == IPS_BUSY)
288  {
289  if (!isSimulation() && abortSlew(PortFD) < 0)
290  {
291  AbortSP.s = IPS_ALERT;
292  IDSetSwitch(&AbortSP, "Abort slew failed.");
293  return false;
294  }
295 
296  AbortSP.s = IPS_OK;
297  EqNP.s = IPS_IDLE;
298  IDSetSwitch(&AbortSP, "Slew aborted.");
299  IDSetNumber(&EqNP, nullptr);
300 
302  {
305  EqNP.s = IPS_IDLE;
308  IDSetSwitch(&MovementNSSP, nullptr);
309  IDSetSwitch(&MovementWESP, nullptr);
310  }
311 
312  // sleep for 100 mseconds
313  nanosleep(&timeout, nullptr);
314  }
315 
316  if (!isSimulation())
317  {
318  if (setObjectRA(PortFD, targetRA, true) < 0 || (setObjectDEC(PortFD, targetDEC, true)) < 0)
319  {
320  EqNP.s = IPS_ALERT;
321  IDSetNumber(&EqNP, "Error setting RA/DEC.");
322  return false;
323  }
324 
325  if (slewGotoNova() == 0)
326  {
327  EqNP.s = IPS_ALERT;
328  IDSetNumber(&EqNP, "Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
329  slewError(1);
330  return false;
331  }
332  }
333 
335  EqNP.s = IPS_BUSY;
336 
337  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
338  return true;
339 }
340 
341 bool LX200GotoNova::Sync(double ra, double dec)
342 {
343  char syncString[256];
344 
345  int syncType = IUFindOnSwitchIndex(&SyncCMRSP);
346 
347  if (!isSimulation())
348  {
349  if (setObjectRA(PortFD, ra, true) < 0 || setObjectDEC(PortFD, dec, true) < 0)
350  {
351  EqNP.s = IPS_ALERT;
352  IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync.");
353  return false;
354  }
355 
356  bool syncOK = true;
357 
358  switch (syncType)
359  {
360  case USE_REGULAR_SYNC:
361  if (::Sync(PortFD, syncString) < 0)
362  syncOK = false;
363  break;
364 
365  case USE_CMR_SYNC:
366  if (GotonovaSyncCMR(syncString) < 0)
367  syncOK = false;
368  break;
369 
370  default:
371  break;
372  }
373 
374  if (syncOK == false)
375  {
376  EqNP.s = IPS_ALERT;
377  IDSetNumber(&EqNP, "Synchronization failed.");
378  return false;
379  }
380 
381  }
382 
383  currentRA = ra;
384  currentDEC = dec;
385 
386  LOGF_DEBUG("%s Synchronization successful %s", (syncType == USE_REGULAR_SYNC ? "CM" : "CMR"), syncString);
387  LOG_INFO("Synchronization successful.");
388 
389  EqNP.s = IPS_OK;
390 
392 
393  return true;
394 }
395 
396 int LX200GotoNova::GotonovaSyncCMR(char *matchedObject)
397 {
398  const struct timespec timeout = {0, 10000000L};
399  int error_type;
400  int nbytes_write = 0;
401  int nbytes_read = 0;
402 
403  LOGF_DEBUG("CMD <%s>", "#:CMR#");
404 
405  if ((error_type = tty_write_string(PortFD, ":CMR#", &nbytes_write)) != TTY_OK)
406  return error_type;
407 
408  if ((error_type = tty_read_section(PortFD, matchedObject, '#', 3, &nbytes_read)) != TTY_OK)
409  return error_type;
410 
411  matchedObject[nbytes_read - 1] = '\0';
412 
413  LOGF_DEBUG("RES <%s>", matchedObject);
414 
415  /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
416  nanosleep(&timeout, nullptr);
417 
418  tcflush(PortFD, TCIFLUSH);
419 
420  return 0;
421 }
422 
423 
424 int LX200GotoNova::slewGotoNova()
425 {
426  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
427  char slewNum[2];
428  int error_type;
429  int nbytes_write = 0, nbytes_read = 0;
430 
431  DEBUGF(DBG_SCOPE, "CMD <%s>", ":MS#");
432 
433  if ((error_type = tty_write_string(PortFD, ":MS#", &nbytes_write)) != TTY_OK)
434  return error_type;
435 
436  error_type = tty_read(PortFD, slewNum, 1, 3, &nbytes_read);
437 
438  if (nbytes_read < 1)
439  {
440  DEBUGF(DBG_SCOPE, "RES ERROR <%d>", error_type);
441  return error_type;
442  }
443 
444  /* We don't need to read the string message, just return corresponding error code */
445  tcflush(PortFD, TCIFLUSH);
446 
447  DEBUGF(DBG_SCOPE, "RES <%c>", slewNum[0]);
448 
449  return slewNum[0];
450 }
451 
453 {
454  if (isSimulation())
455  return true;
456 
457  char cmd[8];
458  int errcode = 0;
459  char errmsg[MAXRBUF];
460  int nbytes_written = 0;
461 
462  snprintf(cmd, 8, ":RC%d#", index);
463 
464  LOGF_DEBUG("CMD (%s)", cmd);
465 
466  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
467  {
468  tty_error_msg(errcode, errmsg, MAXRBUF);
469  LOGF_ERROR("%s", errmsg);
470  return false;
471  }
472 
473  return true;
474 }
475 
476 bool LX200GotoNova::updateTime(ln_date *utc, double utc_offset)
477 {
478  struct ln_zonedate ltm;
479 
480  if (isSimulation())
481  return true;
482 
483  ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
484 
485  JD = ln_get_julian_day(utc);
486 
487  LOGF_DEBUG("New JD is %f", (float)JD);
488 
489  // Set Local Time
490  if (setLocalTime(PortFD, ltm.hours, ltm.minutes, ltm.seconds, true) < 0)
491  {
492  LOG_ERROR("Error setting local time.");
493  return false;
494  }
495 
496  if (setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
497  {
498  LOG_ERROR("Error setting local date.");
499  return false;
500  }
501 
502  if (setGotoNovaUTCOffset(utc_offset) < 0)
503  {
504  LOG_ERROR("Error setting UTC Offset.");
505  return false;
506  }
507 
508  return true;
509 }
510 
511 int LX200GotoNova::setCalenderDate(int fd, int dd, int mm, int yy)
512 {
513  const struct timespec timeout = {0, 10000000L};
514  char read_buffer[16];
515  char response[67];
516  char good_result[] = GOTONOVA_CALDATE_RESULT;
517  int error_type;
518  int nbytes_write = 0, nbytes_read = 0;
519  yy = yy % 100;
520 
521  snprintf(read_buffer, sizeof(read_buffer), ":SC %02d/%02d/%02d#", mm, dd, yy);
522 
523  DEBUGF(DBG_SCOPE, "CMD <%s>", read_buffer);
524 
525  tcflush(fd, TCIFLUSH);
526 
527  if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
528  return error_type;
529 
530  error_type = tty_read(fd, response, sizeof(response), GOTONOVA_TIMEOUT, &nbytes_read);
531 
532  tcflush(fd, TCIFLUSH);
533 
534  if (nbytes_read < 1)
535  {
536  LOG_ERROR("Unable to read response");
537  return error_type;
538  }
539 
540  response[nbytes_read] = '\0';
541 
542  DEBUGF(DBG_SCOPE, "RES <%s>", response);
543 
544  if (strncmp(response, good_result, strlen(good_result)) == 0)
545  {
546  return 0;
547  }
548 
549  /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
550  nanosleep(&timeout, nullptr);
551  tcflush(fd, TCIFLUSH);
552 
553  LOGF_DEBUG("Set date failed! Response: <%s>", response);
554 
555  return -1;
556 }
557 
558 bool LX200GotoNova::updateLocation(double latitude, double longitude, double elevation)
559 {
560  INDI_UNUSED(elevation);
561 
562  if (isSimulation())
563  return true;
564 
565  double final_longitude;
566 
567  if (longitude > 180)
568  final_longitude = longitude - 360.0;
569  else
570  final_longitude = longitude;
571 
572  if (!isSimulation() && setGotoNovaLongitude(final_longitude) < 0)
573  {
574  LOG_ERROR("Error setting site longitude coordinates");
575  return false;
576  }
577 
578  if (!isSimulation() && setGotoNovaLatitude(latitude) < 0)
579  {
580  LOG_ERROR("Error setting site latitude coordinates");
581  return false;
582  }
583 
584  char l[32], L[32];
585  fs_sexa(l, latitude, 3, 3600);
586  fs_sexa(L, longitude, 4, 3600);
587 
588  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
589 
590  return true;
591 }
592 
593 int LX200GotoNova::setGotoNovaLongitude(double Long)
594 {
595  int d, m, s;
596  char sign;
597  char temp_string[32];
598 
599  if (Long > 0)
600  sign = '+';
601  else
602  sign = '-';
603 
604  getSexComponents(Long, &d, &m, &s);
605 
606  snprintf(temp_string, sizeof(temp_string), ":Sg %c%03d*%02d:%02d#", sign, abs(d), m, s);
607 
608  return (setGotoNovaStandardProcedure(PortFD, temp_string));
609 }
610 
611 int LX200GotoNova::setGotoNovaLatitude(double Lat)
612 {
613  int d, m, s;
614  char sign;
615  char temp_string[32];
616 
617  if (Lat > 0)
618  sign = '+';
619  else
620  sign = '-';
621 
622  getSexComponents(Lat, &d, &m, &s);
623 
624  snprintf(temp_string, sizeof(temp_string), ":St %c%02d*%02d:%02d#", sign, abs(d), m, s);
625 
626  return (setGotoNovaStandardProcedure(PortFD, temp_string));
627 }
628 
629 int LX200GotoNova::setGotoNovaUTCOffset(double hours)
630 {
631  char temp_string[16];
632  char sign;
633  int h = 0, m = 0, s = 0;
634 
635  if (hours > 0)
636  sign = '+';
637  else
638  sign = '-';
639 
640  getSexComponents(hours, &h, &m, &s);
641 
642  snprintf(temp_string, sizeof(temp_string), ":SG %c%02d#", sign, abs(h));
643 
644  return (setGotoNovaStandardProcedure(PortFD, temp_string));
645 }
646 
647 int LX200GotoNova::setGotoNovaStandardProcedure(int fd, const char *data)
648 {
649  const struct timespec timeout = {0, 10000000L};
650  char bool_return[2];
651  int error_type;
652  int nbytes_write = 0, nbytes_read = 0;
653 
654  DEBUGF(DBG_SCOPE, "CMD <%s>", data);
655 
656  if ((error_type = tty_write_string(fd, data, &nbytes_write)) != TTY_OK)
657  return error_type;
658 
659  error_type = tty_read(fd, bool_return, 1, 5, &nbytes_read);
660 
661  // JM: Hack from Jon in the INDI forums to fix longitude/latitude settings failure on GotoNova
662  nanosleep(&timeout, nullptr);
663  tcflush(fd, TCIFLUSH);
664  nanosleep(&timeout, nullptr);
665 
666  if (nbytes_read < 1)
667  return error_type;
668 
669  DEBUGF(DBG_SCOPE, "RES <%c>", bool_return[0]);
670 
671  if (bool_return[0] == '0')
672  {
673  DEBUGF(DBG_SCOPE, "CMD <%s> failed.", data);
674  return -1;
675  }
676 
677  DEBUGF(DBG_SCOPE, "CMD <%s> successful.", data);
678 
679  return 0;
680 }
681 
682 bool LX200GotoNova::SetTrackMode(uint8_t mode)
683 {
684  return (setGotoNovaTrackMode(mode) == 0);
685 }
686 
687 int LX200GotoNova::setGotoNovaTrackMode(int mode)
688 {
689  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
690 
691  char cmd[8];
692  snprintf(cmd, 8, ":STR%d#", mode);
693 
694  return setGotoNovaStandardProcedure(PortFD, cmd);
695 }
696 
698 {
699  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
700  int error_type;
701  int nbytes_write = 0;
702 
703  if ((error_type = tty_write_string(PortFD, ":PK#", &nbytes_write)) != TTY_OK)
704  return error_type;
705 
706  tcflush(PortFD, TCIFLUSH);
707 
708  EqNP.s = IPS_BUSY;
710  LOG_INFO("Parking is in progress...");
711 
712  return true;
713 }
714 
716 {
717  SetParked(false);
718  return true;
719 }
720 
722 {
723  if (!isConnected())
724  return false;
725 
726  if (isSimulation())
727  {
728  mountSim();
729  return true;
730  }
731 
732  if (TrackState == SCOPE_SLEWING)
733  {
734  // Check if LX200 is done slewing
735  if (isSlewComplete())
736  {
738  LOG_INFO("Slew is complete. Tracking...");
739  }
740  }
741  else if (TrackState == SCOPE_PARKING)
742  {
743  if (isSlewComplete())
744  {
745  SetParked(true);
746  }
747  }
748 
750  {
751  EqNP.s = IPS_ALERT;
752  IDSetNumber(&EqNP, "Error reading RA/DEC.");
753  return false;
754  }
755 
757 
758  syncSideOfPier();
759 
760  return true;
761 }
762 
763 void LX200GotoNova::mountSim()
764 {
765  static struct timeval ltv;
766  struct timeval tv;
767  double dt, da, dx;
768  int nlocked;
769 
770  /* update elapsed time since last poll, don't presume exactly POLLMS */
771  gettimeofday(&tv, nullptr);
772 
773  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
774  ltv = tv;
775 
776  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
777  ltv = tv;
778  da = SLEWRATE * dt;
779 
780  /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
781  switch (TrackState)
782  {
783  case SCOPE_TRACKING:
784  /* RA moves at sidereal, Dec stands still */
785  currentRA += (SIDRATE * dt / 15.);
786  break;
787 
788  case SCOPE_SLEWING:
789  case SCOPE_PARKING:
790  /* slewing - nail it when both within one pulse @ SLEWRATE */
791  nlocked = 0;
792 
793  dx = targetRA - currentRA;
794 
795  if (fabs(dx) <= da)
796  {
798  nlocked++;
799  }
800  else if (dx > 0)
801  currentRA += da / 15.;
802  else
803  currentRA -= da / 15.;
804 
805  dx = targetDEC - currentDEC;
806  if (fabs(dx) <= da)
807  {
809  nlocked++;
810  }
811  else if (dx > 0)
812  currentDEC += da;
813  else
814  currentDEC -= da;
815 
816  if (nlocked == 2)
817  {
818  if (TrackState == SCOPE_SLEWING)
820  else
821  SetParked(true);
822  }
823 
824  break;
825 
826  default:
827  break;
828  }
829 
831 }
832 
833 int LX200GotoNova::getGotoNovaGuideRate(int *rate)
834 {
835  char cmd[] = ":GGS#";
836  int errcode = 0;
837  char errmsg[MAXRBUF];
838  char response[8];
839  int nbytes_read = 0;
840  int nbytes_written = 0;
841 
842  LOGF_DEBUG("CMD (%s)", cmd);
843 
844  if (isSimulation())
845  {
846  snprintf(response, 8, "%d#", IUFindOnSwitchIndex(&GuideRateSP));
847  nbytes_read = strlen(response);
848  }
849  else
850  {
851  tcflush(PortFD, TCIFLUSH);
852 
853  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
854  {
855  tty_error_msg(errcode, errmsg, MAXRBUF);
856  LOGF_ERROR("%s", errmsg);
857  return errcode;
858  }
859 
860  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
861  {
862  tty_error_msg(errcode, errmsg, MAXRBUF);
863  LOGF_ERROR("%s", errmsg);
864  return errcode;
865  }
866  }
867 
868  if (nbytes_read > 0)
869  {
870  response[nbytes_read] = '\0';
871  LOGF_DEBUG("RES (%s)", response);
872 
873  *rate = atoi(response);
874 
875  return 0;
876  }
877 
878  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
879  return -1;
880 }
881 
882 int LX200GotoNova::setGotoNovaGuideRate(int rate)
883 {
884  char cmd[16];
885  int errcode = 0;
886  char errmsg[MAXRBUF];
887  int nbytes_written = 0;
888 
889  snprintf(cmd, 16, ":SGS%0d#", rate);
890 
891  LOGF_DEBUG("CMD (%s)", cmd);
892 
893  if (isSimulation())
894  {
895  return 0;
896  }
897 
898  tcflush(PortFD, TCIFLUSH);
899 
900  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
901  {
902  tty_error_msg(errcode, errmsg, MAXRBUF);
903  LOGF_ERROR("%s", errmsg);
904  return errcode;
905  }
906 
907  return 0;
908 }
909 
910 int LX200GotoNova::setGotoNovaParkPosition(int position)
911 {
912  char temp_string[16];
913 
914  snprintf(temp_string, sizeof(temp_string), ":STPKP%d#", position);
915 
916  return (setGotoNovaStandardProcedure(PortFD, temp_string));
917 }
918 
919 void LX200GotoNova::syncSideOfPier()
920 {
921  const char *cmd = ":pS#";
922  // Response
923  char response[16] = { 0 };
924  int rc = 0, nbytes_read = 0, nbytes_written = 0;
925 
926  LOGF_DEBUG("CMD: <%s>", cmd);
927 
928  tcflush(PortFD, TCIOFLUSH);
929 
930  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
931  {
932  char errmsg[256];
933  tty_error_msg(rc, errmsg, 256);
934  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
935  return;
936  }
937 
938  // Read Side
939  if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
940  {
941  char errmsg[256];
942  tty_error_msg(rc, errmsg, 256);
943  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
944  return;
945  }
946 
947  response[nbytes_read - 1] = '\0';
948 
949  tcflush(PortFD, TCIOFLUSH);
950 
951  LOGF_DEBUG("RES: <%s>", response);
952 
953  if (!strcmp(response, "East"))
955  else
957 
958 }
959 
961 {
963 
964  IUSaveConfigSwitch(fp, &SyncCMRSP);
965  IUSaveConfigSwitch(fp, &ParkPositionSP);
966 
967  return true;
968 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
TelescopeStatus TrackState
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
ISwitchVectorProperty TrackModeSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
ISwitch * SlewRateS
ISwitchVectorProperty MovementWESP
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.
#define setLocalTime(fd, x, y, z)
Definition: ieq45driver.h:141
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void getSexComponents(double value, int *d, int *m, int *s)
Definition: indicom.c:254
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition: indicom.c:141
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
#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 MAXRBUF
Definition: indiserver.cpp:102
int fd
Definition: intelliscope.c:43
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)
Definition: lx200driver.h:118
#define getLX200RA(fd, x)
Definition: lx200driver.h:117
#define SIDRATE
#define GOTONOVA_CALDATE_RESULT
#define SLEWRATE
#define GOTONOVA_TIMEOUT
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:371