Instrument Neutral Distributed Interface INDI  2.0.2
lx200zeq25.cpp
Go to the documentation of this file.
1 /*
2  ZEQ25 INDI driver
3 
4  Copyright (C) 2015 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 "lx200zeq25.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 
38 {
39  setVersion(1, 6);
40 
42 
51  9);
52 }
53 
55 {
57 
58  //SetParkDataType(PARK_AZ_ALT);
60 
61  // Slew Rates
62  strncpy(SlewRateS[0].label, "1x", MAXINDILABEL);
63  strncpy(SlewRateS[1].label, "2x", MAXINDILABEL);
64  strncpy(SlewRateS[2].label, "8x", MAXINDILABEL);
65  strncpy(SlewRateS[3].label, "16x", MAXINDILABEL);
66  strncpy(SlewRateS[4].label, "64x", MAXINDILABEL);
67  strncpy(SlewRateS[5].label, "128x", MAXINDILABEL);
68  strncpy(SlewRateS[6].label, "256x", MAXINDILABEL);
69  strncpy(SlewRateS[7].label, "512x", MAXINDILABEL);
70  strncpy(SlewRateS[8].label, "MAX", MAXINDILABEL);
72  // 64x is the default
73  SlewRateS[4].s = ISS_ON;
74 
75  IUFillSwitch(&HomeS[0], "Home", "", ISS_OFF);
76  IUFillSwitchVector(&HomeSP, HomeS, 1, getDeviceName(), "Home", "Home", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0,
77  IPS_IDLE);
78 
79  /* How fast do we guide compared to sidereal rate */
80  IUFillNumber(&GuideRateN[0], "GUIDE_RATE", "x Sidereal", "%g", 0.1, 1.0, 0.1, 0.5);
81  IUFillNumberVector(&GuideRateNP, GuideRateN, 1, getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0,
82  IPS_IDLE);
83 
84  return true;
85 }
86 
88 {
90 
91  if (isConnected())
92  {
93 
94  defineProperty(&HomeSP);
95  defineProperty(&GuideRateNP);
96  }
97  else
98  {
99  deleteProperty(HomeSP.name);
100  deleteProperty(GuideRateNP.name);
101  }
102 
103  return true;
104 }
105 
107 {
108  return "ZEQ25";
109 }
110 
112 {
113  if (isSimulation())
114  return true;
115 
116  const struct timespec timeout = {0, 50000000L};
117  char initCMD[] = ":V#";
118  int errcode = 0;
119  char errmsg[MAXRBUF];
120  char response[8];
121  int nbytes_read = 0;
122  int nbytes_written = 0;
123 
124  LOG_DEBUG("Initializing IOptron using :V# CMD...");
125 
126  for (int i = 0; i < 2; i++)
127  {
128  if ((errcode = tty_write(PortFD, initCMD, 3, &nbytes_written)) != TTY_OK)
129  {
130  tty_error_msg(errcode, errmsg, MAXRBUF);
131  LOGF_ERROR("%s", errmsg);
132  nanosleep(&timeout, nullptr);
133  continue;
134  }
135 
136  if ((errcode = tty_read_section(PortFD, response, '#', 3, &nbytes_read)))
137  {
138  tty_error_msg(errcode, errmsg, MAXRBUF);
139  LOGF_ERROR("%s", errmsg);
140  nanosleep(&timeout, nullptr);
141  continue;
142  }
143 
144  if (nbytes_read > 0)
145  {
146  response[nbytes_read] = '\0';
147  LOGF_DEBUG("RES (%s)", response);
148 
149  if (!strcmp(response, "V1.00#"))
150  return true;
151  }
152 
153  nanosleep(&timeout, nullptr);
154  }
155 
156  return false;
157 }
158 
159 bool LX200ZEQ25::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
160 {
161  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
162  {
163  if (strcmp(HomeSP.name, name) == 0)
164  {
165  // If already home, nothing to be done
166  //if (HomeS[0].s == ISS_ON)
167  if (isZEQ25Home())
168  {
169  LOG_WARN("Telescope is already homed.");
170  HomeS[0].s = ISS_ON;
171  HomeSP.s = IPS_OK;
172  IDSetSwitch(&HomeSP, nullptr);
173  return true;
174  }
175 
176  if (gotoZEQ25Home() < 0)
177  {
178  HomeSP.s = IPS_ALERT;
179  LOG_ERROR("Error slewing to home position.");
180  }
181  else
182  {
183  HomeSP.s = IPS_BUSY;
184  LOG_INFO("Slewing to home position.");
185  }
186 
187  IDSetSwitch(&HomeSP, nullptr);
188  return true;
189  }
190  }
191 
192  return LX200Generic::ISNewSwitch(dev, name, states, names, n);
193 }
194 
195 bool LX200ZEQ25::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
196 {
197  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
198  {
199  // Guiding Rate
200  if (!strcmp(name, GuideRateNP.name))
201  {
202  IUUpdateNumber(&GuideRateNP, values, names, n);
203 
204  if (setZEQ25GuideRate(GuideRateN[0].value) == TTY_OK)
205  GuideRateNP.s = IPS_OK;
206  else
207  GuideRateNP.s = IPS_ALERT;
208 
209  IDSetNumber(&GuideRateNP, nullptr);
210 
211  return true;
212  }
213  }
214 
215  return LX200Generic::ISNewNumber(dev, name, values, names, n);
216 }
217 
218 bool LX200ZEQ25::isZEQ25Home()
219 {
220  const struct timespec timeout = {0, 10000000L};
221  char bool_return[2];
222  int error_type;
223  int nbytes_write = 0, nbytes_read = 0;
224 
225  if (isSimulation())
226  return true;
227 
228  DEBUG(DBG_SCOPE, "CMD <:AH#>");
229 
230  if ((error_type = tty_write_string(PortFD, ":AH#", &nbytes_write)) != TTY_OK)
231  return false;
232 
233  error_type = tty_read(PortFD, bool_return, 1, 5, &nbytes_read);
234 
235  // JM: Hack from Jon in the INDI forums to fix longitude/latitude settings failure on ZEQ25
236  nanosleep(&timeout, nullptr);
237  tcflush(PortFD, TCIFLUSH);
238  nanosleep(&timeout, nullptr);
239 
240  if (nbytes_read < 1)
241  return false;
242 
243  DEBUGF(DBG_SCOPE, "RES <%c>", bool_return[0]);
244 
245  return (bool_return[0] == '1');
246 }
247 
248 int LX200ZEQ25::gotoZEQ25Home()
249 {
250  return setZEQ25StandardProcedure(PortFD, ":MH#");
251 }
252 
254 {
255  int errcode = 0;
256  char errmsg[MAXRBUF];
257  char response[8];
258  int nbytes_read = 0;
259  int nbytes_written = 0;
260 
261  //strncpy(cmd, ":SE#", 16);
262  const char *cmd = ":SE#";
263 
264  LOGF_DEBUG("CMD <%s>", cmd);
265 
266  tcflush(PortFD, TCIOFLUSH);
267 
268  if ((errcode = tty_write(PortFD, cmd, 4, &nbytes_written)) != TTY_OK)
269  {
270  tty_error_msg(errcode, errmsg, MAXRBUF);
271  LOGF_ERROR("%s", errmsg);
272  return false;
273  }
274 
275  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
276  {
277  tty_error_msg(errcode, errmsg, MAXRBUF);
278  LOGF_ERROR("%s", errmsg);
279  return false;
280  }
281 
282  if (nbytes_read > 0)
283  {
284  response[nbytes_read] = '\0';
285  LOGF_DEBUG("RES (%s)", response);
286 
287  tcflush(PortFD, TCIFLUSH);
288 
289  if (response[0] == '0')
290  return true;
291  else
292  return false;
293  }
294 
295  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
296  return false;
297 }
298 
299 bool LX200ZEQ25::getMountInfo()
300 {
301  char cmd[] = ":MountInfo#";
302  int errcode = 0;
303  char errmsg[MAXRBUF];
304  char response[16];
305  int nbytes_read = 0;
306  int nbytes_written = 0;
307 
308  LOGF_DEBUG("CMD <%s>", cmd);
309 
310  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
311  {
312  tty_error_msg(errcode, errmsg, MAXRBUF);
313  LOGF_ERROR("%s", errmsg);
314  return false;
315  }
316 
317  if ((errcode = tty_read(PortFD, response, 4, 3, &nbytes_read)))
318  {
319  tty_error_msg(errcode, errmsg, MAXRBUF);
320  LOGF_ERROR("%s", errmsg);
321  return false;
322  }
323 
324  if (nbytes_read > 0)
325  {
326  response[nbytes_read] = '\0';
327  LOGF_DEBUG("RES (%s)", response);
328 
329  if (nbytes_read == 4)
330  {
331  if (!strcmp(response, "8407"))
332  LOG_INFO("Detected iEQ45/iEQ30 Mount.");
333  else if (!strcmp(response, "8497"))
334  LOG_INFO("Detected iEQ45 AA Mount.");
335  else if (!strcmp(response, "8408"))
336  LOG_INFO("Detected ZEQ25 Mount.");
337  else if (!strcmp(response, "8498"))
338  LOG_INFO("Detected SmartEQ Mount.");
339  else
340  LOG_INFO("Unknown mount detected.");
341 
342  tcflush(PortFD, TCIFLUSH);
343 
344  return true;
345  }
346  }
347 
348  LOGF_ERROR("Only received #%d bytes, expected 4.", nbytes_read);
349  return false;
350 }
351 
353 {
354  getMountInfo();
355 
356  int moveRate = getZEQ25MoveRate();
357  if (moveRate >= 0)
358  {
361  SlewRateSP.s = IPS_OK;
362  IDSetSwitch(&SlewRateSP, nullptr);
363  }
364 
365  if (InitPark())
366  {
367  // If loading parking data is successful, we just set the default parking values.
368  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
370  }
371  else
372  {
373  // Otherwise, we set all parking data to default in case no parking data is found.
374  SetAxis1Park(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
376  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
378  }
379 
380  bool isMountParked = isZEQ25Parked();
381  if (isMountParked != isParked())
382  SetParked(isMountParked);
383 
384  // Is home?
385  LOG_DEBUG("Checking if mount is at home position...");
386  if (isZEQ25Home())
387  {
388  HomeS[0].s = ISS_ON;
389  HomeSP.s = IPS_OK;
390  IDSetSwitch(&HomeSP, nullptr);
391  }
392 
393  LOG_DEBUG("Getting guiding rate...");
394  double guideRate = 0;
395  if (getZEQ25GuideRate(&guideRate) == TTY_OK)
396  {
397  GuideRateN[0].value = guideRate;
398  IDSetNumber(&GuideRateNP, nullptr);
399  }
400 
404  sendScopeTime();
405 }
406 
407 bool LX200ZEQ25::Sync(double ra, double dec)
408 {
409  if (!isSimulation() && (setObjectRA(PortFD, ra, true) < 0 || (setObjectDEC(PortFD, dec, true)) < 0))
410  {
411  EqNP.s = IPS_ALERT;
412  IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync.");
413  return false;
414  }
415 
416  if (!isSimulation() && setZEQ25StandardProcedure(PortFD, ":CM#") < 0)
417  {
418  EqNP.s = IPS_ALERT;
419  IDSetNumber(&EqNP, "Synchronization failed.");
420  return false;
421  }
422 
423  currentRA = ra;
424  currentDEC = dec;
425 
426  LOG_INFO("Synchronization successful.");
427 
428  EqNP.s = IPS_OK;
429 
431 
432  return true;
433 }
434 
435 bool LX200ZEQ25::Goto(double r, double d)
436 {
437  const struct timespec timeout = {0, 100000000L};
438 
439  targetRA = r;
440  targetDEC = d;
441  char RAStr[64], DecStr[64];
442 
443  fs_sexa(RAStr, targetRA, 2, 3600);
444  fs_sexa(DecStr, targetDEC, 2, 3600);
445 
446  // If moving, let's stop it first.
447  if (EqNP.s == IPS_BUSY)
448  {
449  if (!isSimulation() && abortSlew(PortFD) < 0)
450  {
451  AbortSP.s = IPS_ALERT;
452  IDSetSwitch(&AbortSP, "Abort slew failed.");
453  return false;
454  }
455 
456  AbortSP.s = IPS_OK;
457  EqNP.s = IPS_IDLE;
458  IDSetSwitch(&AbortSP, "Slew aborted.");
459  IDSetNumber(&EqNP, nullptr);
460 
462  {
465  EqNP.s = IPS_IDLE;
468  IDSetSwitch(&MovementNSSP, nullptr);
469  IDSetSwitch(&MovementWESP, nullptr);
470  }
471 
472  // sleep for 100 mseconds
473  nanosleep(&timeout, nullptr);
474  }
475 
476  if (!isSimulation())
477  {
478  if (setObjectRA(PortFD, targetRA, true) < 0 || (setObjectDEC(PortFD, targetDEC, true)) < 0)
479  {
480  EqNP.s = IPS_ALERT;
481  IDSetNumber(&EqNP, "Error setting RA/DEC.");
482  return false;
483  }
484 
485  if (slewZEQ25() == false)
486  {
487  EqNP.s = IPS_ALERT;
488  LOGF_DEBUG("Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
489  slewError(1);
490  return false;
491  }
492  }
493 
495  //EqNP.s = IPS_BUSY;
496 
497  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
498  return true;
499 }
500 
501 bool LX200ZEQ25::slewZEQ25()
502 {
503  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
504  char slewNum[2];
505  int error_type;
506  int nbytes_write = 0, nbytes_read = 0;
507 
508  DEBUGF(DBG_SCOPE, "CMD <%s>", ":MS#");
509 
510  if ((error_type = tty_write_string(PortFD, ":MS#", &nbytes_write)) != TTY_OK)
511  return error_type;
512 
513  error_type = tty_read(PortFD, slewNum, 1, 3, &nbytes_read);
514 
515  if (nbytes_read < 1)
516  {
517  DEBUGF(DBG_SCOPE, "RES ERROR <%d>", error_type);
518  return error_type;
519  }
520 
521  /* We don't need to read the string message, just return corresponding error code */
522  tcflush(PortFD, TCIFLUSH);
523 
524  DEBUGF(DBG_SCOPE, "RES <%c>", slewNum[0]);
525 
526  return (slewNum[0] == '1');
527 }
528 
529 bool LX200ZEQ25::SetSlewRate(int index)
530 {
531  if (isSimulation())
532  return true;
533 
534  char cmd[8];
535  int errcode = 0;
536  char errmsg[MAXRBUF];
537  char response[2];
538  int nbytes_read = 0;
539  int nbytes_written = 0;
540 
541  snprintf(cmd, 8, ":SR%d#", index + 1);
542 
543  LOGF_DEBUG("CMD <%s>", cmd);
544 
545  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
546  {
547  tty_error_msg(errcode, errmsg, MAXRBUF);
548  LOGF_ERROR("%s", errmsg);
549  return false;
550  }
551 
552  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
553  {
554  tty_error_msg(errcode, errmsg, MAXRBUF);
555  LOGF_ERROR("%s", errmsg);
556  return false;
557  }
558 
559  if (nbytes_read > 0)
560  {
561  response[nbytes_read] = '\0';
562  LOGF_DEBUG("RES (%s)", response);
563 
564  tcflush(PortFD, TCIFLUSH);
565 
566  return (response[0] == '1');
567  }
568 
569  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
570  return false;
571 }
572 
573 int LX200ZEQ25::getZEQ25MoveRate()
574 {
575  if (isSimulation())
576  {
578  }
579 
580  char cmd[] = ":Gr#";
581  int errcode = 0;
582  char errmsg[MAXRBUF];
583  char response[3];
584  int nbytes_read = 0;
585  int nbytes_written = 0;
586 
587  LOGF_DEBUG("CMD <%s>", cmd);
588 
589  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
590  {
591  tty_error_msg(errcode, errmsg, MAXRBUF);
592  LOGF_ERROR("%s", errmsg);
593  return -1;
594  }
595 
596  if ((errcode = tty_read_section(PortFD, response, '#', 3, &nbytes_read)))
597  {
598  tty_error_msg(errcode, errmsg, MAXRBUF);
599  LOGF_ERROR("%s", errmsg);
600  return -1;
601  }
602 
603  if (nbytes_read > 0)
604  {
605  response[nbytes_read - 1] = '\0';
606  LOGF_DEBUG("RES (%s)", response);
607 
608  tcflush(PortFD, TCIFLUSH);
609 
610  int moveRate = -1;
611 
612  sscanf(response, "%d", &moveRate);
613 
614  return moveRate;
615  }
616 
617  LOGF_ERROR("Only received #%d bytes, expected 2.", nbytes_read);
618  return -1;
619 }
620 
621 bool LX200ZEQ25::updateTime(ln_date *utc, double utc_offset)
622 {
623  struct ln_zonedate ltm;
624 
625  if (isSimulation())
626  return true;
627 
628  ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
629 
630  JD = ln_get_julian_day(utc);
631 
632  LOGF_DEBUG("New JD is %.2f", JD);
633 
634  // Set Local Time
635  if (setLocalTime(PortFD, ltm.hours, ltm.minutes, ltm.seconds, true) < 0)
636  {
637  LOG_ERROR("Error setting local time.");
638  return false;
639  }
640 
641  if (setZEQ25Date(ltm.days, ltm.months, ltm.years) < 0)
642  {
643  LOG_ERROR("Error setting local date.");
644  return false;
645  }
646 
647  if (setZEQ25UTCOffset(utc_offset) < 0)
648  {
649  LOG_ERROR("Error setting UTC Offset.");
650  return false;
651  }
652 
653  return true;
654 }
655 
656 bool LX200ZEQ25::updateLocation(double latitude, double longitude, double elevation)
657 {
658  INDI_UNUSED(elevation);
659 
660  if (isSimulation())
661  return true;
662 
663  double final_longitude;
664 
665  if (longitude > 180)
666  final_longitude = longitude - 360.0;
667  else
668  final_longitude = longitude;
669 
670  if (!isSimulation() && setZEQ25Longitude(final_longitude) < 0)
671  {
672  LOG_ERROR("Error setting site longitude coordinates");
673  return false;
674  }
675 
676  if (!isSimulation() && setZEQ25Latitude(latitude) < 0)
677  {
678  LOG_ERROR("Error setting site latitude coordinates");
679  return false;
680  }
681 
682  char l[32], L[32];
683  fs_sexa(l, latitude, 3, 3600);
684  fs_sexa(L, longitude, 4, 3600);
685 
686  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
687 
688  return true;
689 }
690 
691 int LX200ZEQ25::setZEQ25Longitude(double Long)
692 {
693  int d, m, s;
694  char sign;
695  char temp_string[32];
696 
697  if (Long > 0)
698  sign = '+';
699  else
700  sign = '-';
701 
702  getSexComponents(Long, &d, &m, &s);
703 
704  snprintf(temp_string, sizeof(temp_string), ":Sg %c%03d:%02d:%02d#", sign, abs(d), m, s);
705 
706  return (setZEQ25StandardProcedure(PortFD, temp_string));
707 }
708 
709 int LX200ZEQ25::setZEQ25Latitude(double Lat)
710 {
711  int d, m, s;
712  char sign;
713  char temp_string[32];
714 
715  if (Lat > 0)
716  sign = '+';
717  else
718  sign = '-';
719 
720  getSexComponents(Lat, &d, &m, &s);
721 
722  snprintf(temp_string, sizeof(temp_string), ":St %c%02d:%02d:%02d#", sign, abs(d), m, s);
723 
724  return (setZEQ25StandardProcedure(PortFD, temp_string));
725 }
726 
727 int LX200ZEQ25::setZEQ25UTCOffset(double hours)
728 {
729  char temp_string[16];
730  char sign;
731  int h = 0, m = 0, s = 0;
732 
733  if (hours > 0)
734  sign = '+';
735  else
736  sign = '-';
737 
738  getSexComponents(hours, &h, &m, &s);
739 
740  snprintf(temp_string, sizeof(temp_string), ":SG %c%02d:%02d#", sign, abs(h), m);
741 
742  return (setZEQ25StandardProcedure(PortFD, temp_string));
743 }
744 
745 int LX200ZEQ25::setZEQ25Date(int days, int months, int years)
746 {
747  char command[16] = {0};
748  snprintf(command, sizeof(command), ":SC %02d/%02d/%02d#", months, days, years % 100);
749  return (setZEQ25StandardProcedure(PortFD, command));
750 }
751 
752 int LX200ZEQ25::setZEQ25StandardProcedure(int fd, const char *data)
753 {
754  const struct timespec timeout = {0, 10000000L};
755  char bool_return[2];
756  int error_type;
757  int nbytes_write = 0, nbytes_read = 0;
758 
759  DEBUGF(DBG_SCOPE, "CMD <%s>", data);
760 
761  if ((error_type = tty_write_string(fd, data, &nbytes_write)) != TTY_OK)
762  return error_type;
763 
764  error_type = tty_read(fd, bool_return, 1, 5, &nbytes_read);
765 
766  // JM: Hack from Jon in the INDI forums to fix longitude/latitude settings failure on ZEQ25
767  nanosleep(&timeout, nullptr);
768  tcflush(fd, TCIFLUSH);
769  nanosleep(&timeout, nullptr);
770 
771  if (nbytes_read < 1)
772  return error_type;
773 
774  DEBUGF(DBG_SCOPE, "RES <%c>", bool_return[0]);
775 
776  if (bool_return[0] == '0')
777  {
778  DEBUGF(DBG_SCOPE, "CMD <%s> failed.", data);
779  return -1;
780  }
781 
782  DEBUGF(DBG_SCOPE, "CMD <%s> successful.", data);
783 
784  return 0;
785 }
786 
788 {
789  int current_move = (dir == DIRECTION_NORTH) ? LX200_NORTH : LX200_SOUTH;
790 
791  switch (command)
792  {
793  case MOTION_START:
794  if (!isSimulation() && moveZEQ25To(current_move) < 0)
795  {
796  LOG_ERROR("Error setting N/S motion direction.");
797  return false;
798  }
799  else
800  LOGF_INFO("Moving toward %s.",
801  (current_move == LX200_NORTH) ? "North" : "South");
802  break;
803 
804  case MOTION_STOP:
805  if (!isSimulation() && haltZEQ25Movement() < 0)
806  {
807  LOG_ERROR("Error stopping N/S motion.");
808  return false;
809  }
810  else
811  LOGF_INFO("Movement toward %s halted.",
812  (current_move == LX200_NORTH) ? "North" : "South");
813  break;
814  }
815 
816  return true;
817 }
818 
820 {
821  int current_move = (dir == DIRECTION_WEST) ? LX200_WEST : LX200_EAST;
822 
823  switch (command)
824  {
825  case MOTION_START:
826  if (!isSimulation() && moveZEQ25To(current_move) < 0)
827  {
828  LOG_ERROR("Error setting W/E motion direction.");
829  return false;
830  }
831  else
832  LOGF_INFO("Moving toward %s.", (current_move == LX200_WEST) ? "West" : "East");
833  break;
834 
835  case MOTION_STOP:
836  if (!isSimulation() && haltZEQ25Movement() < 0)
837  {
838  LOG_ERROR("Error stopping W/E motion.");
839  return false;
840  }
841  else
842  LOGF_INFO("Movement toward %s halted.",
843  (current_move == LX200_WEST) ? "West" : "East");
844  break;
845  }
846 
847  return true;
848 }
849 
850 int LX200ZEQ25::moveZEQ25To(int direction)
851 {
852  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
853  int nbytes_write = 0;
854 
855  switch (direction)
856  {
857  case LX200_NORTH:
858  DEBUGF(DBG_SCOPE, "CMD <%s>", ":mn#");
859  tty_write_string(PortFD, ":mn#", &nbytes_write);
860  break;
861  case LX200_WEST:
862  DEBUGF(DBG_SCOPE, "CMD <%s>", ":mw#");
863  tty_write_string(PortFD, ":mw#", &nbytes_write);
864  break;
865  case LX200_EAST:
866  DEBUGF(DBG_SCOPE, "CMD <%s>", ":me#");
867  tty_write_string(PortFD, ":me#", &nbytes_write);
868  break;
869  case LX200_SOUTH:
870  DEBUGF(DBG_SCOPE, "CMD <%s>", ":ms#");
871  tty_write_string(PortFD, ":ms#", &nbytes_write);
872  break;
873  default:
874  break;
875  }
876 
877  tcflush(PortFD, TCIFLUSH);
878  return 0;
879 }
880 
881 int LX200ZEQ25::haltZEQ25Movement()
882 {
883  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
884  int error_type;
885  int nbytes_write = 0;
886 
887  if ((error_type = tty_write_string(PortFD, ":q#", &nbytes_write)) != TTY_OK)
888  return error_type;
889 
890  tcflush(PortFD, TCIFLUSH);
891  return 0;
892 }
893 
894 bool LX200ZEQ25::SetTrackMode(uint8_t mode)
895 {
896  return (setZEQ25TrackMode(mode) == 0);
897 }
898 
899 int LX200ZEQ25::setZEQ25TrackMode(int mode)
900 {
901  DEBUGF(DBG_SCOPE, "<%s>", __FUNCTION__);
902 
903  // We don't support KING mode :RT3, so we turn mode=3 to custom :RT4#
904  if (mode == 3)
905  mode = 4;
906 
907  char cmd[6];
908  snprintf(cmd, 6, ":RT%d#", mode);
909 
910  return setZEQ25StandardProcedure(PortFD, cmd);
911 }
912 
913 int LX200ZEQ25::setZEQ25Park()
914 {
915  int error_type;
916  int nbytes_write = 0;
917 
918  LOGF_DEBUG("CMD <%s>", ":MP1#");
919 
920  if ((error_type = tty_write_string(PortFD, ":MP1#", &nbytes_write)) != TTY_OK)
921  return error_type;
922 
923  tcflush(PortFD, TCIFLUSH);
924  return 0;
925 }
926 
927 int LX200ZEQ25::setZEQ25UnPark()
928 {
929  int error_type;
930  int nbytes_write = 0;
931 
932  LOGF_DEBUG("CMD <%s>", ":MP0#");
933 
934  if ((error_type = tty_write_string(PortFD, ":MP0#", &nbytes_write)) != TTY_OK)
935  return error_type;
936 
937  tcflush(PortFD, TCIFLUSH);
938  return 0;
939 }
940 
941 bool LX200ZEQ25::isZEQ25Parked()
942 {
943  if (isSimulation())
944  {
945  return isParked();
946  }
947 
948  char cmd[] = ":AP#";
949  int errcode = 0;
950  char errmsg[MAXRBUF];
951  char response[2];
952  int nbytes_read = 0;
953  int nbytes_written = 0;
954 
955  LOGF_DEBUG("CMD <%s>", cmd);
956 
957  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
958  {
959  tty_error_msg(errcode, errmsg, MAXRBUF);
960  LOGF_ERROR("%s", errmsg);
961  return false;
962  }
963 
964  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
965  {
966  tty_error_msg(errcode, errmsg, MAXRBUF);
967  LOGF_ERROR("%s", errmsg);
968  return false;
969  }
970 
971  if (nbytes_read > 0)
972  {
973  response[nbytes_read] = '\0';
974  LOGF_DEBUG("RES (%s)", response);
975 
976  tcflush(PortFD, TCIFLUSH);
977 
978  return (response[0] == '1');
979  }
980 
981  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
982  return false;
983 }
984 
986 {
987  INDI::IHorizontalCoordinates horizontalPos;
988  INDI::IEquatorialCoordinates equatorialPos;
989  equatorialPos.rightascension = currentRA;
990  equatorialPos.declination = currentDEC;
991  INDI::EquatorialToHorizontal(&equatorialPos, &m_Location, ln_get_julian_from_sys(), &horizontalPos);
992  double parkAZ = horizontalPos.azimuth;
993  double parkAlt = horizontalPos.altitude;
994 
995  char AzStr[16], AltStr[16];
996  fs_sexa(AzStr, parkAZ, 2, 3600);
997  fs_sexa(AltStr, parkAlt, 2, 3600);
998 
999  LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr,
1000  AltStr);
1001 
1002  SetAxis1Park(parkAZ);
1003  SetAxis2Park(parkAlt);
1004 
1005  return true;
1006 }
1007 
1009 {
1010  // Az = 0 for North hemisphere
1011  SetAxis1Park(LocationN[LOCATION_LATITUDE].value > 0 ? 0 : 180);
1012 
1013  // Alt = Latitude
1015 
1016  return true;
1017 }
1018 
1020 {
1021  // JM 2012-12-16: Using Homing instead of custom parking
1022  // to fix reported issues with parking
1023  if (gotoZEQ25Home() < 0)
1024  {
1025  LOG_ERROR("Error parking...");
1026  return false;
1027  }
1028  else
1029  {
1030  HomeSP.s = IPS_BUSY;
1031  IDSetSwitch(&HomeSP, nullptr);
1032  }
1033 
1035  LOG_INFO("Parking is in progress...");
1036  return true;
1037 
1038 #if 0
1039  double parkAz = GetAxis1Park();
1040  double parkAlt = GetAxis2Park();
1041 
1042  char AzStr[16], AltStr[16];
1043  fs_sexa(AzStr, parkAz, 2, 3600);
1044  fs_sexa(AltStr, parkAlt, 2, 3600);
1045 
1046  LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
1047 
1048  INDI::IHorizontalCoordinates horizontalPos;
1049  // Libnova south = 0, west = 90, north = 180, east = 270
1050 
1051  horizontalPos.alt = parkAlt;
1052  horizontalPos.az = parkAz + 180;
1053  if (horizontalPos.az > 360)
1054  horizontalPos.az -= 360;
1055 
1056  IGeographicCoordinates observer;
1057  observer.lat = LocationN[LOCATION_LATITUDE].value;
1058  observer.lng = LocationN[LOCATION_LONGITUDE].value;
1059  if (observer.lng > 180)
1060  observer.lng -= 360;
1061 
1062  INDI::IEquatorialCoordinates equatorialPos;
1063  ln_get_equ_from_hrz(&horizontalPos, &observer, ln_get_julian_from_sys(), &equatorialPos);
1064  equatorialPos.rightascension /= 15.0;
1065 
1066  if (setObjectRA(PortFD, equatorialPos.rightascension) < 0 || (setObjectDEC(PortFD, equatorialPos.dec)) < 0)
1067  {
1068  LOG_ERROR("Error setting RA/Dec.");
1069  return false;
1070  }
1071 
1072  /* Slew reads the '0', that is not the end of the slew */
1073  if (slewZEQ25() == false)
1074  {
1075  LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
1076  slewError(1);
1077  return false;
1078  }
1079 
1080  EqNP.s = IPS_BUSY;
1082  LOG_INFO("Parking is in progress...");
1083 
1084  return true;
1085 #endif
1086 }
1087 
1089 {
1090  // First we unpark astrophysics
1091  if (!isSimulation())
1092  {
1093  if (setZEQ25UnPark() < 0)
1094  {
1095  LOG_ERROR("UnParking Failed.");
1096  return false;
1097  }
1098  }
1099 
1100  // Then we sync with to our last stored position
1101 #if 0
1102  double parkAz = GetAxis1Park();
1103  double parkAlt = GetAxis2Park();
1104 
1105  char AzStr[16], AltStr[16];
1106  fs_sexa(AzStr, parkAz, 2, 3600);
1107  fs_sexa(AltStr, parkAlt, 2, 3600);
1108  LOGF_DEBUG("Syncing to parked coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
1109 
1110  INDI::IHorizontalCoordinates horizontalPos;
1111  // Libnova south = 0, west = 90, north = 180, east = 270
1112 
1113  horizontalPos.alt = parkAlt;
1114  horizontalPos.az = parkAz + 180;
1115  if (horizontalPos.az > 360)
1116  horizontalPos.az -= 360;
1117 
1118  IGeographicCoordinates observer;
1119  observer.lat = LocationN[LOCATION_LATITUDE].value;
1120  observer.lng = LocationN[LOCATION_LONGITUDE].value;
1121  if (observer.lng > 180)
1122  observer.lng -= 360;
1123 
1124  INDI::IEquatorialCoordinates equatorialPos;
1125  ln_get_equ_from_hrz(&horizontalPos, &observer, ln_get_julian_from_sys(), &equatorialPos);
1126  equatorialPos.rightascension /= 15.0;
1127 
1128  if (setObjectRA(PortFD, equatorialPos.rightascension) < 0 || (setObjectDEC(PortFD, equatorialPos.dec)) < 0)
1129  {
1130  LOG_ERROR("Error setting RA/DEC.");
1131  return false;
1132  }
1133 
1134  if (Sync(equatorialPos.rightascension, equatorialPos.dec) == false)
1135  {
1136  LOG_WARN("Sync failed.");
1137  return false;
1138  }
1139 #endif
1140 
1141  SetParked(false);
1142  return true;
1143 }
1144 
1146 {
1147  if (!isConnected())
1148  return false;
1149 
1150  if (isSimulation())
1151  {
1152  mountSim();
1153  return true;
1154  }
1155 
1156  //if (check_lx200_connection(PortFD))
1157  //return false;
1158 
1159  if (HomeSP.s == IPS_BUSY)
1160  {
1161  if (isZEQ25Home())
1162  {
1163  HomeS[0].s = ISS_ON;
1164  HomeSP.s = IPS_OK;
1165  LOG_INFO("Telescope arrived at home position.");
1166  IDSetSwitch(&HomeSP, nullptr);
1167  }
1168  }
1169 
1170  if (TrackState == SCOPE_SLEWING)
1171  {
1172  // Check if LX200 is done slewing
1173  if (isSlewComplete())
1174  {
1176  LOG_INFO("Slew is complete. Tracking...");
1177  }
1178  }
1179  else if (TrackState == SCOPE_PARKING)
1180  {
1181  // JM 2019-12-16: Parking is not working correctly, so we just use home
1182  // if (isSlewComplete())
1183  // {
1184  // setZEQ25Park();
1185  // SetParked(true);
1186  // }
1187 
1188  if (HomeSP.s == IPS_OK && HomeS[0].s == ISS_ON)
1189  {
1190  SetParked(true);
1191  }
1192  }
1193 
1194  if (getLX200RA(PortFD, &currentRA) < 0 || getLX200DEC(PortFD, &currentDEC) < 0)
1195  {
1196  EqNP.s = IPS_ALERT;
1197  IDSetNumber(&EqNP, "Error reading RA/DEC.");
1198  return false;
1199  }
1200 
1201  // Get Pier side
1202 
1204  if (getZEQ25PierSide(side))
1205  setPierSide(side);
1206 
1208 
1209  return true;
1210 }
1211 
1212 void LX200ZEQ25::mountSim()
1213 {
1214  static struct timeval ltv;
1215  struct timeval tv;
1216  double dt, da, dx;
1217  int nlocked;
1218 
1219  /* update elapsed time since last poll, don't presume exactly POLLMS */
1220  gettimeofday(&tv, nullptr);
1221 
1222  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1223  ltv = tv;
1224 
1225  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1226  ltv = tv;
1227  da = SLEWRATE * dt;
1228 
1229  /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
1230  switch (TrackState)
1231  {
1232  case SCOPE_TRACKING:
1233  /* RA moves at sidereal, Dec stands still */
1234  currentRA += (SIDRATE * dt / 15.);
1235  break;
1236 
1237  case SCOPE_SLEWING:
1238  case SCOPE_PARKING:
1239  /* slewing - nail it when both within one pulse @ SLEWRATE */
1240  nlocked = 0;
1241 
1242  dx = targetRA - currentRA;
1243 
1244  if (fabs(dx) <= da)
1245  {
1246  currentRA = targetRA;
1247  nlocked++;
1248  }
1249  else if (dx > 0)
1250  currentRA += da / 15.;
1251  else
1252  currentRA -= da / 15.;
1253 
1254  dx = targetDEC - currentDEC;
1255  if (fabs(dx) <= da)
1256  {
1258  nlocked++;
1259  }
1260  else if (dx > 0)
1261  currentDEC += da;
1262  else
1263  currentDEC -= da;
1264 
1265  if (nlocked == 2)
1266  {
1267  if (TrackState == SCOPE_SLEWING)
1269  else
1270  SetParked(true);
1271  }
1272 
1273  break;
1274 
1275  default:
1276  break;
1277  }
1278 
1280  if (getZEQ25PierSide(side))
1281  setPierSide(side);
1282 
1284 }
1285 
1286 int LX200ZEQ25::getZEQ25GuideRate(double *rate)
1287 {
1288  char cmd[] = ":AG#";
1289  int errcode = 0;
1290  char errmsg[MAXRBUF];
1291  char response[8];
1292  int nbytes_read = 0;
1293  int nbytes_written = 0;
1294 
1295  LOGF_DEBUG("CMD <%s>", cmd);
1296 
1297  if (isSimulation())
1298  {
1299  snprintf(response, 8, "%3d#", static_cast<int>(GuideRateN[0].value * 100));
1300  nbytes_read = strlen(response);
1301  }
1302  else
1303  {
1304  tcflush(PortFD, TCIFLUSH);
1305 
1306  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1307  {
1308  tty_error_msg(errcode, errmsg, MAXRBUF);
1309  LOGF_ERROR("%s", errmsg);
1310  return errcode;
1311  }
1312 
1313  if ((errcode = tty_read(PortFD, response, 4, 3, &nbytes_read)))
1314  {
1315  tty_error_msg(errcode, errmsg, MAXRBUF);
1316  LOGF_ERROR("%s", errmsg);
1317  return errcode;
1318  }
1319  }
1320 
1321  if (nbytes_read > 0)
1322  {
1323  response[nbytes_read] = '\0';
1324  LOGF_DEBUG("RES (%s)", response);
1325 
1326  int rate_num;
1327 
1328  if (sscanf(response, "%d#", &rate_num) > 0)
1329  {
1330  *rate = rate_num / 100.0;
1331  tcflush(PortFD, TCIFLUSH);
1332  return TTY_OK;
1333  }
1334  else
1335  {
1336  LOGF_ERROR("Error: Malformed result (%s).", response);
1337  return -1;
1338  }
1339  }
1340 
1341  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
1342  return -1;
1343 }
1344 
1345 int LX200ZEQ25::setZEQ25GuideRate(double rate)
1346 {
1347  char cmd[16];
1348  int errcode = 0;
1349  char errmsg[MAXRBUF];
1350  char response[8];
1351  int nbytes_read = 0;
1352  int nbytes_written = 0;
1353 
1354  int num = rate * 100;
1355  snprintf(cmd, 16, ":RG%03d#", num);
1356 
1357  LOGF_DEBUG("CMD <%s>", cmd);
1358 
1359  if (isSimulation())
1360  {
1361  strcpy(response, "1");
1362  nbytes_read = strlen(response);
1363  }
1364  else
1365  {
1366  tcflush(PortFD, TCIFLUSH);
1367 
1368  if ((errcode = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1369  {
1370  tty_error_msg(errcode, errmsg, MAXRBUF);
1371  LOGF_ERROR("%s", errmsg);
1372  return errcode;
1373  }
1374 
1375  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
1376  {
1377  tty_error_msg(errcode, errmsg, MAXRBUF);
1378  LOGF_ERROR("%s", errmsg);
1379  return errcode;
1380  }
1381  }
1382 
1383  if (nbytes_read > 0)
1384  {
1385  response[nbytes_read] = '\0';
1386  LOGF_DEBUG("RES (%s)", response);
1387 
1388  tcflush(PortFD, TCIFLUSH);
1389  return TTY_OK;
1390  }
1391 
1392  LOGF_ERROR("Only received #%d bytes, expected 1.", nbytes_read);
1393  return -1;
1394 }
1395 
1396 int LX200ZEQ25::SendPulseCmd(int8_t direction, uint32_t duration_msec)
1397 {
1398  int nbytes_write = 0;
1399  char cmd[20];
1400  switch (direction)
1401  {
1402  case LX200_NORTH:
1403  sprintf(cmd, ":Mn%04d#", duration_msec);
1404  break;
1405  case LX200_SOUTH:
1406  sprintf(cmd, ":Ms%04d#", duration_msec);
1407  break;
1408  case LX200_EAST:
1409  sprintf(cmd, ":Me%04d#", duration_msec);
1410  break;
1411  case LX200_WEST:
1412  sprintf(cmd, ":Mw%04d#", duration_msec);
1413  break;
1414  default:
1415  return 1;
1416  }
1417 
1418  LOGF_DEBUG("CMD <%s>", cmd);
1419 
1420  tty_write_string(PortFD, cmd, &nbytes_write);
1421 
1422  tcflush(PortFD, TCIFLUSH);
1423  return TTY_OK;
1424 }
1425 
1426 bool LX200ZEQ25::getZEQ25PierSide(TelescopePierSide &side)
1427 {
1428  int nbytes_written = 0;
1429  int errcode = 0;
1430  char errmsg[MAXRBUF];
1431  char response[8] = {0};
1432  int nbytes_read = 0;
1433 
1434  if (isSimulation())
1435  {
1436  strcpy(response, "1");
1437  nbytes_read = strlen(response);
1438  }
1439  else
1440  {
1441  tcflush(PortFD, TCIFLUSH);
1442 
1443  if ((errcode = tty_write_string(PortFD, ":pS#", &nbytes_written)) != TTY_OK)
1444  {
1445  tty_error_msg(errcode, errmsg, MAXRBUF);
1446  LOGF_ERROR("%s", errmsg);
1447  return false;
1448  }
1449 
1450  if ((errcode = tty_read(PortFD, response, 1, 3, &nbytes_read)))
1451  {
1452  tty_error_msg(errcode, errmsg, MAXRBUF);
1453  LOGF_ERROR("%s", errmsg);
1454  return false;
1455  }
1456  }
1457 
1458  if (nbytes_read > 0)
1459  {
1460  response[nbytes_read] = '\0';
1461  LOGF_DEBUG("RES (%s)", response);
1462 
1463  if ( response[0] == '0')
1464  side = PIER_EAST;
1465  else if ( response[0] == '1')
1466  side = PIER_WEST;
1467  else
1468  side = PIER_UNKNOWN;
1469 
1470  return true;
1471  }
1472 
1473  return false;
1474 }
1475 
1476 bool LX200ZEQ25::setUTCOffset(double offset)
1477 {
1478  int h, m, s;
1479  char command[64] = {0};
1480  getSexComponents(offset, &h, &m, &s);
1481 
1482  snprintf(command, 64, ":SG %c%02d:%02d#", offset >= 0 ? '+' : '-', h, m);
1483  return setZEQ25StandardProcedure(PortFD, command);
1484 }
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
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
double GetAxis1Park() const
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
ISwitchVectorProperty SlewRateSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
IGeographicCoordinates m_Location
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
void setPierSide(TelescopePierSide side)
ISwitch * SlewRateS
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
ISwitchVectorProperty MovementWESP
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking 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 sendScopeLocation()
bool sendLocationOnStartup
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 sendScopeTime()
void setLX200Capability(uint32_t cap)
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition: lx200zeq25.cpp:621
virtual const char * getDefaultName() override
Definition: lx200zeq25.cpp:106
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition: lx200zeq25.cpp:787
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: lx200zeq25.cpp:985
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: lx200zeq25.cpp:195
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition: lx200zeq25.cpp:819
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition: lx200zeq25.cpp:529
virtual void getBasicData() override
Definition: lx200zeq25.cpp:352
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition: lx200zeq25.cpp:54
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: lx200zeq25.cpp:159
virtual bool Park() override
Park the telescope to its home position.
virtual bool checkConnection() override
Definition: lx200zeq25.cpp:111
virtual bool setUTCOffset(double offset) override
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition: lx200zeq25.cpp:656
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
Definition: lx200zeq25.cpp:435
virtual bool Sync(double, double) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition: lx200zeq25.cpp:407
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition: lx200zeq25.cpp:87
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition: lx200zeq25.cpp:894
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool isSlewComplete() override
Definition: lx200zeq25.cpp:253
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.
#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
#define MAXINDILABEL
Definition: indiapi.h:192
@ ISR_ATMOST1
Definition: indiapi.h:174
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
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 IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void 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 IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition: indilogger.h:56
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition: indilogger.h:57
#define MAXRBUF
Definition: indiserver.cpp:102
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)
#define getLX200DEC(fd, x)
Definition: lx200driver.h:118
#define getLX200RA(fd, x)
Definition: lx200driver.h:117
@ LX200_WEST
Definition: lx200driver.h:42
@ LX200_SOUTH
Definition: lx200driver.h:44
@ LX200_NORTH
Definition: lx200driver.h:41
@ LX200_EAST
Definition: lx200driver.h:43
#define SIDRATE
Definition: lx200zeq25.cpp:35
#define SLEWRATE
Definition: lx200zeq25.cpp:34
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition: libastro.cpp:140
double moveRate
Definition: pmc8driver.cpp:123
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371