Instrument Neutral Distributed Interface INDI  2.0.2
lx200driver.cpp
Go to the documentation of this file.
1 #if 0
2 LX200 Driver
3 Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5 This library is free software;
6 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;
9 either
10 version 2.1 of the License, or (at your option) any later version.
11 
12 This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY;
14 without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library;
20 if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
22 
23 #endif
24 
25 #include "lx200driver.h"
26 
27 #include "indicom.h"
28 #include "indilogger.h"
29 
30 #include <cstring>
31 #include <unistd.h>
32 
33 #ifndef _WIN32
34 #include <termios.h>
35 #endif
36 
37 #ifdef __FreeBSD__
38 #include <string.h>
39 #endif
40 
41 /* Add mutex */
42 
43 #include <mutex>
44 
45 #define LX200_TIMEOUT 5 /* FD timeout in seconds */
46 #define RB_MAX_LEN 64
47 
48 
49 int eq_format; /* For possible values see enum TEquatorialFormat */
50 int geo_format = LX200_GEO_SHORT_FORMAT; /* For possible values see enum TGeographicFormat */
52 /* ESN DEBUG */
53 unsigned int DBG_SCOPE = 8;
54 
55 /* Add mutex to communications */
56 std::mutex lx200CommsLock;
57 
58 void setLX200Debug(const char *deviceName, unsigned int debug_level)
59 {
60  strncpy(lx200Name, deviceName, MAXINDIDEVICE);
61  DBG_SCOPE = debug_level;
62 }
63 
64 int check_lx200_connection(int in_fd)
65 {
66  const struct timespec timeout = {0, 50000000L};
67  int i = 0;
68  char ack[1] = { 0x06 };
69  char MountAlign[64];
70  int nbytes_read = 0;
71 
72  DEBUGDEVICE(lx200Name, INDI::Logger::DBG_DEBUG, "Testing telescope connection using ACK...");
73 
74  /* Add mutex */
75  std::unique_lock<std::mutex> guard(lx200CommsLock);
76 
77  if (in_fd <= 0)
78  return -1;
79 
80  for (i = 0; i < 2; i++)
81  {
82  // Meade Telescope Serial Command Protocol Revision 2010.10
83  // ACK <0x06> Query of alignment mounting mode.
84  // Returns:
85  // A If scope in AltAz Mode
86  // D If scope is currently in the Downloader [Autostar II & Autostar]
87  // L If scope in Land Mode
88  // P If scope in Polar Mode
89  if (write(in_fd, ack, 1) < 0)
90  return -1;
91  tty_read(in_fd, MountAlign, 1, LX200_TIMEOUT, &nbytes_read);
92  if (nbytes_read == 1)
93  {
94  DEBUGDEVICE(lx200Name, INDI::Logger::DBG_DEBUG, "Testing successful!");
95  return 0;
96  }
97  nanosleep(&timeout, nullptr);
98  }
99 
100  DEBUGDEVICE(lx200Name, INDI::Logger::DBG_DEBUG, "Failure. Telescope is not responding to ACK!");
101  return -1;
102 }
103 
104 /**********************************************************************
105 * GET
106 **********************************************************************/
107 
108 char ACK(int fd)
109 {
110  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
111 
112  char ack[1] = { 0x06 };
113  char MountAlign[2];
114  int nbytes_write = 0, nbytes_read = 0, error_type;
115 
116  /* Add mutex */
117  std::unique_lock<std::mutex> guard(lx200CommsLock);
118 
119  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%#02X>", ack[0]);
120 
121  // Meade Telescope Serial Command Protocol Revision 2010.10
122  // ACK <0x06> Query of alignment mounting mode.
123  // Returns:
124  // A If scope in AltAz Mode
125  // D If scope is currently in the Downloader [Autostar II & Autostar]
126  // L If scope in Land Mode
127  // P If scope in Polar Mode
128  nbytes_write = write(fd, ack, 1);
129 
130  if (nbytes_write < 0)
131  return -1;
132 
133  error_type = tty_read(fd, MountAlign, 1, LX200_TIMEOUT, &nbytes_read);
134 
135  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%c>", MountAlign[0]);
136 
137  if (nbytes_read == 1)
138  return MountAlign[0];
139  else
140  return error_type;
141 }
142 
143 int getCommandSexa(int fd, double *value, const char *cmd)
144 {
145  char read_buffer[RB_MAX_LEN] = {0};
146  int error_type;
147  int nbytes_write = 0, nbytes_read = 0;
148 
149  /* Add mutex */
150  std::unique_lock<std::mutex> guard(lx200CommsLock);
151 
152  tcflush(fd, TCIFLUSH);
153 
154  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
155 
156  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
157  return error_type;
158 
159  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
160  tcflush(fd, TCIFLUSH);
161  if (error_type != TTY_OK)
162  return error_type;
163 
164  read_buffer[nbytes_read - 1] = '\0';
165 
166  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
167 
168  if (f_scansexa(read_buffer, value))
169  {
170  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Unable to parse response");
171  return -1;
172  }
173 
174  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%g]", *value);
175 
176  tcflush(fd, TCIFLUSH);
177  return 0;
178 }
179 
180 int getCommandInt(int fd, int *value, const char *cmd)
181 {
182  char read_buffer[RB_MAX_LEN] = {0};
183  float temp_number;
184  int error_type;
185  int nbytes_write = 0, nbytes_read = 0;
186 
187  /* Add mutex */
188  std::unique_lock<std::mutex> guard(lx200CommsLock);
189 
190  tcflush(fd, TCIFLUSH);
191 
192  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
193 
194  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
195  return error_type;
196 
197  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
198  tcflush(fd, TCIFLUSH);
199  if (error_type != TTY_OK)
200  return error_type;
201 
202  read_buffer[nbytes_read - 1] = '\0';
203 
204  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
205 
206  /* Float */
207  if (strchr(read_buffer, '.'))
208  {
209  if (sscanf(read_buffer, "%f", &temp_number) != 1)
210  return -1;
211 
212  *value = static_cast<int>(temp_number);
213  }
214  /* Int */
215  else if (sscanf(read_buffer, "%d", value) != 1)
216  return -1;
217 
218  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%d]", *value);
219 
220  return 0;
221 }
222 
223 int getCommandString(int fd, char *data, const char *cmd)
224 {
225  char *term;
226  int error_type;
227  int nbytes_write = 0, nbytes_read = 0;
228 
229  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
230 
231  /* Add mutex */
232  std::unique_lock<std::mutex> guard(lx200CommsLock);
233 
234  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
235  return error_type;
236 
237  error_type = tty_nread_section(fd, data, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
238  tcflush(fd, TCIFLUSH);
239 
240  if (error_type != TTY_OK)
241  return error_type;
242 
243  term = strchr(data, '#');
244  if (term)
245  *term = '\0';
246 
247  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", data);
248 
249  return 0;
250 }
251 
253 {
254  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
255  /* update for classic lx200, total string returned is 33 bytes */
256  char data[33] = { 0 };
257  int error_type;
258  int nbytes_write = 0, nbytes_read = 0;
259  const char *cmd = ":D#";
260 
261  /* update for slew complete lx200 classic 3.2. roms */
262  int i;
263 
264 
265  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
266 
267  /* Add mutex */
268  std::unique_lock<std::mutex> guard(lx200CommsLock);
269 
270  // Meade Telescope Serial Command Protocol Revision 2010.10
271  // :D#
272  // Requests a string of bars indicating the distance to the current target location.
273  // Returns:
274  // LX200's – a string of bar characters indicating the distance.
275  // Autostars and Autostar II – a string containing one bar until a slew is complete, then a null string is returned
276  //
277  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
278  // :D#
279  // Requests a string indicating the progress of the current slew operation.
280  // Returns:
281  // the string “■#”, where the block character has ascii code 127 (0x7F), if a slew is in
282  // progress or a slew has ended from less than the settle time set in command :Sstm.
283  // the string “#” if a slew has been completed or no slew is underway.
284  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
285  return error_type;
286 
287  error_type = tty_nread_section(fd, data, 33, '#', LX200_TIMEOUT, &nbytes_read);
288  tcflush(fd, TCIOFLUSH);
289 
290  if (error_type != TTY_OK)
291  return error_type;
292 
293  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", data);
294  /* update for slewComplete
295 
296  The below should handle classic lx200, autostar and autostar 2
297  classic returns string of 33 bytes, and non space (0x20) before terminator is not done yet
298  autostar and autostar 2 return a few bytes, with '#' terminator
299  first char
300  */
301  for(i = 0; i < 33; i++)
302  {
303  if(data[i] == '#') return 1;
304  if(data[i] != 0x20) return 0;
305  }
306  return 1;
307  /* out for slewComplete update
308  if (data[0] == '#')
309  return 1;
310  else
311  return 0;
312  END out for slewComplete update */
313 }
314 
315 int getCalendarDate(int fd, char *date)
316 {
317  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
318  int dd, mm, yy, YYYY;
319  int error_type;
320  int nbytes_read = 0;
321  char mell_prefix[3] = {0};
322  int len = 0;
323 
324  /* Add mutex */
325  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
326 
327  // Meade Telescope Serial Command Protocol Revision 2010.10
328  // :GC#
329  // Get current date.
330  // Returns: MM/DD/YY#
331  // The current local calendar date for the telescope.
332  //
333  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
334  // :GC#
335  // Get current date. Returns the current date formatted as follows:
336  // Emulation and precision Return value
337  // LX200 emulation, low and high precision MM/DD/YY# (month, day, year)
338  // Extended emulation, low and high precision MM:DD:YY# (month, day, year) – note that the separator character is ':' instead of '/'.
339  // Any emulation, ultra precision YYYY-MM-DD# (year, month, day) – note that the separator character is '-' instead of '/'.
340  if ((error_type = getCommandString(fd, date, ":GC#")))
341  return error_type;
342  len = strnlen(date, 32);
343  if (len == 10)
344  {
345  /* 10Micron Ultra Precision mode calendar date format is YYYY-MM-DD */
346  nbytes_read = sscanf(date, "%4d-%2d-%2d", &YYYY, &mm, &dd);
347  if (nbytes_read < 3)
348  return -1;
349  /* We're done, date is already in ISO format */
350  }
351  else
352  {
353  /* Meade format is MM/DD/YY */
354  nbytes_read = sscanf(date, "%d%*c%d%*c%d", &mm, &dd, &yy);
355  if (nbytes_read < 3)
356  return -1;
357  /* We consider years 50 or more to be in the last century, anything less in the 21st century.*/
358  if (yy > 50)
359  strncpy(mell_prefix, "19", 3);
360  else
361  strncpy(mell_prefix, "20", 3);
362  /* We need to have it in YYYY-MM-DD ISO format */
363  snprintf(date, 32, "%s%02d-%02d-%02d", mell_prefix, yy, mm, dd);
364  }
365  return (0);
366 }
367 
368 int getTimeFormat(int fd, int *format)
369 {
370  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
371  char read_buffer[RB_MAX_LEN] = {0};
372  int error_type;
373  int nbytes_write = 0, nbytes_read = 0;
374  int tMode;
375 
376  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Gc#");
377 
378  /* Add mutex */
379  std::unique_lock<std::mutex> guard(lx200CommsLock);
380 
381  // Meade Telescope Serial Command Protocol Revision 2010.10
382  // :Gc#
383  // Get Clock Format
384  // Returns: 12# or 24#
385  // Depending on the current telescope format setting.
386  if ((error_type = tty_write_string(fd, ":Gc#", &nbytes_write)) != TTY_OK)
387  return error_type;
388 
389  if ((error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read)) != TTY_OK)
390  return error_type;
391 
392  tcflush(fd, TCIFLUSH);
393 
394  if (nbytes_read < 1)
395  return error_type;
396 
397  read_buffer[nbytes_read - 1] = '\0';
398 
399  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
400 
401  // The Losmandy Gemini puts () around it's time format
402  if (strstr(read_buffer, "("))
403  nbytes_read = sscanf(read_buffer, "(%d)", &tMode);
404  else
405  nbytes_read = sscanf(read_buffer, "%d", &tMode);
406 
407  if (nbytes_read < 1)
408  return -1;
409  else
410  *format = tMode;
411 
412  return 0;
413 }
414 
415 int getSiteName(int fd, char *siteName, int siteNum)
416 {
417  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
418  char *term;
419  int error_type;
420  int nbytes_write = 0, nbytes_read = 0;
421 
422  /* Add mutex */
423  std::unique_lock<std::mutex> guard(lx200CommsLock);
424 
425  // Meade Telescope Serial Command Protocol Revision 2010.10
426  // :GM# // Get Site 1 Name // Returns: <string># // A ‘#’ terminated string with the name of the requested site.
427  // :GN# // Get Site 2 Name // Returns: <string># // A ‘#’ terminated string with the name of the requested site.
428  // :GO# // Get Site 3 Name // Returns: <string># // A ‘#’ terminated string with the name of the requested site.
429  // :GP# // Get Site 4 Name // Returns: <string># // A ‘#’ terminated string with the name of the requested site.
430  switch (siteNum)
431  {
432  case 1:
433  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GM#");
434  if ((error_type = tty_write_string(fd, ":GM#", &nbytes_write)) != TTY_OK)
435  return error_type;
436  break;
437  case 2:
438  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GN#");
439  if ((error_type = tty_write_string(fd, ":GN#", &nbytes_write)) != TTY_OK)
440  return error_type;
441  break;
442  case 3:
443  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GO#");
444  if ((error_type = tty_write_string(fd, ":GO#", &nbytes_write)) != TTY_OK)
445  return error_type;
446  break;
447  case 4:
448  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GP#");
449  if ((error_type = tty_write_string(fd, ":GP#", &nbytes_write)) != TTY_OK)
450  return error_type;
451  break;
452  default:
453  return -1;
454  }
455 
456  error_type = tty_nread_section(fd, siteName, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
457  tcflush(fd, TCIFLUSH);
458 
459  if (nbytes_read < 1)
460  return error_type;
461 
462  siteName[nbytes_read - 1] = '\0';
463 
464  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", siteName);
465 
466  term = strchr(siteName, ' ');
467  if (term)
468  *term = '\0';
469 
470  term = strchr(siteName, '<');
471  if (term)
472  strcpy(siteName, "unused site");
473 
474  DEBUGFDEVICE(lx200Name, INDI::Logger::DBG_DEBUG, "Site Name <%s>", siteName);
475 
476  return 0;
477 }
478 
479 int getSiteLatitude(int fd, int *dd, int *mm, double *ssf)
480 {
481  // Meade Telescope Serial Command Protocol Revision 2010.10
482  // :Gt#
483  // Get Current Site Latitude
484  // Returns: sDD*MM#
485  // The latitude of the current site. Positive inplies North latitude.
486  //
487  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
488  // :Gt#
489  // Get current site latitude.
490  // Returns the latitude of the current site formatted as follows:
491  // Emulation and precision Return value
492  // Any emulation, low precision sDD*MM# (sign, degrees, minutes)
493  // LX200 emulation, high precision sDD*MM# (sign, degrees, minutes)
494  // Extended emulation, high precision sDD*MM:SS# (sign, degrees, arcminutes, arcseconds)
495  // Any emulation, ultra precision sDD:MM:SS.S# (sign, degrees, arcminutes, arcseconds, tenths of arcsecond)
496  // Positive implies north latitude.
497  return getSiteLatitudeAlt( fd, dd, mm, ssf, ":Gt#");
498 }
499 
500 // Meade classic handset defines longitude as 0 to 360 WESTWARD. However,
501 // Meade API expresses East Longitudes as negative, West Longitudes as positive.
502 // Source: https://www.meade.com/support/LX200CommandSet.pdf from 2002 at :Gg#
503 // (And also 10Micron has East Longitudes expressed as negative.)
504 // Also note that this is the opposite of cartography where East is positive.
505 int getSiteLongitude(int fd, int *ddd, int *mm, double *ssf)
506 {
507  // Meade Telescope Serial Command Protocol Revision 2010.10
508  // :Gg#
509  // Get Current Site Longitude
510  // Returns: sDDD*MM#
511  // The current site Longitude. East Longitudes are expressed as negative
512  //
513  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
514  // :Gg#
515  // Get current site longitude. Note: East Longitudes are expressed as negative. Returns
516  // the current site longitude formatted as follows:
517  // Emulation and precision Return value
518  // Any emulation, low precision or LX200 sDDD*MM# (sign, degrees, arcminutes)
519  // emulation, high precision
520  // Extended emulation, high precision sDDD*MM:SS# (sign, degrees, arcminutes, arcseconds)
521  // Any emulation, ultra precision sDDD:MM:SS.S# (sign, degrees, arcminutes, arcseconds, tenths of arcsecond)
522  return getSiteLongitudeAlt(fd, ddd, mm, ssf, ":Gg#");
523 }
524 
525 
526 int getSiteLatitudeAlt(int fd, int *dd, int *mm, double *ssf, const char *cmd)
527 {
528  // :Gt# see getSiteLatitude()
529  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
530  char read_buffer[RB_MAX_LEN] = {0};
531  int error_type;
532  int nbytes_write = 0, nbytes_read = 0;
533 
534  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
535 
536  /* Add mutex */
537  std::unique_lock<std::mutex> guard(lx200CommsLock);
538 
539  tcflush(fd, TCIFLUSH);
540 
541  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
542  return error_type;
543 
544  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
545 
546  tcflush(fd, TCIFLUSH);
547 
548  if (nbytes_read < 1)
549  return error_type;
550 
551  read_buffer[nbytes_read - 1] = '\0';
552 
553  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
554 
555  *ssf = 0.0;
556  if (sscanf(read_buffer, "%d%*c%d:%lf", dd, mm, ssf) < 2)
557  {
558  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unable to parse %s response", cmd);
559  return -1;
560  }
561 
562  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%d,%d,%.1lf]", *dd, *mm, *ssf);
563 
564  int new_geo_format;
565  switch (nbytes_read)
566  {
567  case 9:
568  case 10:
569  new_geo_format = LX200_GEO_LONG_FORMAT;
570  break;
571  case 11:
572  case 12:
573  new_geo_format = LX200_GEO_LONGER_FORMAT;
574  break;
575  default:
576  new_geo_format = LX200_GEO_SHORT_FORMAT;
577  break;
578  }
579  if (new_geo_format != geo_format)
580  {
581  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Updated geographic precision from setting %d to %d", geo_format, new_geo_format);
582  geo_format = new_geo_format;
583  }
584 
585  return 0;
586 }
587 
588 int getSiteLongitudeAlt(int fd, int *ddd, int *mm, double *ssf, const char *cmd)
589 {
590  // :Gg# see getSiteLongitude()
591  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
592  char read_buffer[RB_MAX_LEN] = {0};
593  int error_type;
594  int nbytes_write = 0, nbytes_read = 0;
595 
596  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
597 
598  /* Add mutex */
599  std::unique_lock<std::mutex> guard(lx200CommsLock);
600 
601  if ((error_type = tty_write_string(fd, cmd, &nbytes_write)) != TTY_OK)
602  return error_type;
603 
604  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
605 
606  tcflush(fd, TCIFLUSH);
607 
608  if (nbytes_read < 1)
609  return error_type;
610 
611  read_buffer[nbytes_read - 1] = '\0';
612 
613  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
614 
615  *ssf = 0.0;
616  if (sscanf(read_buffer, "%d%*c%d:%lf", ddd, mm, ssf) < 2)
617  {
618  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unable to parse %s response", cmd);
619  return -1;
620  }
621  *ddd *= -1.0; // Convert LX200Longitude to CartographicLongitude
622 
623  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL in CartographicLongitude format [%d,%d,%.1lf]", *ddd, *mm, *ssf);
624 
625  int new_geo_format;
626  switch (nbytes_read)
627  {
628  case 10:
629  case 11:
630  new_geo_format = LX200_GEO_LONG_FORMAT;
631  break;
632  case 12:
633  case 13:
634  new_geo_format = LX200_GEO_LONGER_FORMAT;
635  break;
636  default:
637  new_geo_format = LX200_GEO_SHORT_FORMAT;
638  break;
639  }
640  if (new_geo_format != geo_format)
641  {
642  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Updated geographic precision from setting %d to %d", geo_format, new_geo_format);
643  geo_format = new_geo_format;
644  }
645 
646  return 0;
647 }
648 
649 int getTrackFreq(int fd, double *value)
650 {
651  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
652  float Freq;
653  char read_buffer[RB_MAX_LEN] = {0};
654  int error_type;
655  int nbytes_write = 0, nbytes_read = 0;
656 
657  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GT#");
658 
659  /* Add mutex */
660  std::unique_lock<std::mutex> guard(lx200CommsLock);
661 
662  // Meade Telescope Serial Command Protocol Revision 2010.10
663  // :GT#
664  // Get tracking rate
665  // Returns: TT.T#
666  // Current Track Frequency expressed in hertz assuming a synchonous motor design where a 60.0 Hz motor clock
667  // would produce 1 revolution of the telescope in 24 hours.
668  if ((error_type = tty_write_string(fd, ":GT#", &nbytes_write)) != TTY_OK)
669  return error_type;
670 
671  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
672  tcflush(fd, TCIFLUSH);
673 
674  if (nbytes_read < 1)
675  return error_type;
676 
677  read_buffer[nbytes_read] = '\0';
678 
679  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
680 
681  if (sscanf(read_buffer, "%f#", &Freq) < 1)
682  {
683  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Unable to parse response");
684  return -1;
685  }
686 
687  *value = static_cast<double>(Freq);
688 
689  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%g]", *value);
690 
691  return 0;
692 }
693 
694 int getHomeSearchStatus(int fd, int *status)
695 {
696  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
697  char read_buffer[RB_MAX_LEN] = {0};
698  int error_type;
699  int nbytes_write = 0, nbytes_read = 0;
700 
701  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":h?#");
702 
703  /* Add mutex */
704  std::unique_lock<std::mutex> guard(lx200CommsLock);
705 
706  // Meade Telescope Serial Command Protocol Revision 2010.10
707  // :h?#
708  // Autostar, Autostar II and LX 16” Query Home Status
709  // Returns:
710  // 0 Home Search Failed
711  // 1 Home Search Found
712  // 2 Home Search in Progress
713  // LX200 Not Supported
714  if ((error_type = tty_write_string(fd, ":h?#", &nbytes_write)) != TTY_OK)
715  return error_type;
716 
717  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
718  tcflush(fd, TCIFLUSH);
719 
720  if (nbytes_read < 1)
721  return error_type;
722 
723  read_buffer[1] = '\0';
724 
725  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
726 
727  if (read_buffer[0] == '0')
728  *status = 0;
729  else if (read_buffer[0] == '1')
730  *status = 1;
731  else if (read_buffer[0] == '2')
732  *status = 1;
733 
734  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%d]", *status);
735 
736  return 0;
737 }
738 
739 int getOTATemp(int fd, double *value)
740 {
741  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
742  char read_buffer[RB_MAX_LEN] = {0};
743  int error_type;
744  int nbytes_write = 0, nbytes_read = 0;
745  float temp;
746 
747  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":fT#");
748 
749  /* Add mutex */
750  std::unique_lock<std::mutex> guard(lx200CommsLock);
751 
752  // Meade Telescope Serial Command Protocol Revision 2010.10
753  // :fT#
754  // Autostar II – Return Optical Tube Assembly Temperature
755  // Max/RCX – Return OTA Temperature
756  // Returns <sdd.ddd># - a ‘#’ terminated signed ASCII real number indicating the Celsius ambient temperature.
757  // All others – Not supported
758  if ((error_type = tty_write_string(fd, ":fT#", &nbytes_write)) != TTY_OK)
759  return error_type;
760 
761  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
762 
763  if (nbytes_read < 1)
764  return error_type;
765 
766  read_buffer[nbytes_read - 1] = '\0';
767 
768  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
769 
770  if (sscanf(read_buffer, "%f", &temp) < 1)
771  {
772  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Unable to parse response");
773  return -1;
774  }
775 
776  *value = static_cast<double>(temp);
777 
778  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "VAL [%g]", *value);
779 
780  return 0;
781 }
782 
783 /**********************************************************************
784 * SET
785 **********************************************************************/
786 
787 int setStandardProcedure(int fd, const char *data)
788 {
789  char bool_return[2];
790  int error_type;
791  int nbytes_write = 0, nbytes_read = 0;
792 
793  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", data);
794 
795  /* Add mutex */
796  std::unique_lock<std::mutex> guard(lx200CommsLock);
797 
798  tcflush(fd, TCIFLUSH);
799 
800  if ((error_type = tty_write_string(fd, data, &nbytes_write)) != TTY_OK)
801  return error_type;
802 
803  error_type = tty_read(fd, bool_return, 1, LX200_TIMEOUT, &nbytes_read);
804 
805  tcflush(fd, TCIFLUSH);
806 
807  if (nbytes_read < 1)
808  return error_type;
809 
810  if (bool_return[0] == '0')
811  {
812  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s> failed.", data);
813  return -1;
814  }
815 
816  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s> successful.", data);
817 
818  return 0;
819 }
820 
821 int setCommandInt(int fd, int data, const char *cmd)
822 {
823  char read_buffer[RB_MAX_LEN] = {0};
824  int error_type;
825  int nbytes_write = 0;
826 
827  /* Add mutex */
828  std::unique_lock<std::mutex> guard(lx200CommsLock);
829 
830  snprintf(read_buffer, sizeof(read_buffer), "%s%d#", cmd, data);
831 
832  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", read_buffer);
833 
834  tcflush(fd, TCIFLUSH);
835 
836  if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
837  {
838  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s> failed.", read_buffer);
839  return error_type;
840  }
841 
842  tcflush(fd, TCIFLUSH);
843 
844  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s> successful.", read_buffer);
845 
846  return 0;
847 }
848 
850 {
851  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
852  char read_buffer[RB_MAX_LEN] = {0};
853 
854  /* Add mutex */
855  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
856 
857  // Meade Telescope Serial Command Protocol Revision 2010.10
858  // :SoDD*#
859  // Set lowest elevation to which the telescope will slew
860  // Returns:
861  // 0 – Invalid
862  // 1 - Valid
863  //
864  // 10Micron adds a sign and limits but removes the * in their docs.
865  // :SosDD#
866  // Set the minimum altitude above the horizon to which the telescope will slew to sDD degrees.
867  // Valid values are between –5 and +45 degrees.
868  snprintf(read_buffer, sizeof(read_buffer), ":So%02d*#", min);
869 
870  return (setStandardProcedure(fd, read_buffer));
871 }
872 
874 {
875  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
876  char read_buffer[RB_MAX_LEN] = {0};
877 
878  /* Add mutex */
879  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
880 
881  // Meade Telescope Serial Command Protocol Revision 2010.10
882  // :ShDD#
883  // Set the maximum object elevation limit to DD#
884  // Returns:
885  // 0 – Invalid
886  // 1 - Valid
887  snprintf(read_buffer, sizeof(read_buffer), ":Sh%02d#", max);
888 
889  return (setStandardProcedure(fd, read_buffer));
890 }
891 
892 int setMaxSlewRate(int fd, int slewRate)
893 {
894  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
895  char read_buffer[RB_MAX_LEN] = {0};
896 
897  /* Add mutex */
898  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
899 
900  if (slewRate < 2 || slewRate > 8)
901  return -1;
902 
903  // Meade Telescope Serial Command Protocol Revision 2010.10
904  // :SwN#
905  // Set maximum slew rate to N degrees per second. N is the range (2..8)
906  // Returns:
907  // 0 – Invalid
908  // 1 - Valid
909  snprintf(read_buffer, sizeof(read_buffer), ":Sw%d#", slewRate);
910 
911  return (setStandardProcedure(fd, read_buffer));
912 }
913 
914 int setObjectRA(int fd, double ra, bool addSpace)
915 {
916  // Meade Telescope Serial Command Protocol Revision 2010.10
917  // :SrHH:MM.T#
918  // :SrHH:MM:SS#
919  // Set target object RA to HH:MM.T or HH:MM:SS depending on the current precision setting.
920  // Returns:
921  // 0 – Invalid
922  // 1 - Valid
923  //
924  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
925  // :SrHH:MM.T# or :SrHH:MM:SS# or :SrHH:MM:SS.S# or :SrHH:MM:SS.SS#
926  // Set target object RA to HH:MM.T (hours, minutes and tenths of minutes), HH:MM:SS
927  // (hours, minutes, seconds), HH:MM:SS.S (hours, minutes, seconds and tenths of second)
928  // or HH:MM:SS.SS (hours, minutes, seconds and hundredths of second).
929  // Returns:
930  // 0 invalid
931  // 1 valid
932  //
933  // We support these formats:
934  // LX200_EQ_SHORT_FORMAT :SrHH:MM.T# (hours, minutes and tenths of minutes)
935  // LX200_EQ_LONG_FORMAT :SrHH:MM:SS# (hours, minutes, seconds)
936  // LX200_EQ_LONGER_FORMAT :SrHH:MM:SS.SS# (hours, minutes, seconds and hundredths of second)
937  // Add space is used to add space between the command the and rest of the arguments.
938  // i.e. :Sr HH:MM:SS# for example since some mounts require space.
939  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
940 
941  int h, m, s;
942  char read_buffer[22] = {0};
943  char cmd[8] = {0};
944  if (addSpace)
945  strcpy(cmd, "Sr ");
946  else
947  strcpy(cmd, "Sr");
948 
949  /* Add mutex */
950  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
951 
952  switch (eq_format)
953  {
955  int frac_m;
956  getSexComponents(ra, &h, &m, &s);
957  frac_m = (s / 60.0) * 10.;
958  snprintf(read_buffer, sizeof(read_buffer), ":%s%02d:%02d.%01d#", cmd, h, m, frac_m);
959  break;
961  getSexComponents(ra, &h, &m, &s);
962  snprintf(read_buffer, sizeof(read_buffer), ":%s%02d:%02d:%02d#", cmd, h, m, s);
963  break;
965  double d_s;
966  getSexComponentsIID(ra, &h, &m, &d_s);
967  snprintf(read_buffer, sizeof(read_buffer), ":%s%02d:%02d:%05.02f#", cmd, h, m, d_s);
968  break;
969  default:
970  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unknown controller_format <%d>", eq_format);
971  return -1;
972  }
973 
974  return (setStandardProcedure(fd, read_buffer));
975 }
976 
977 int setObjectDEC(int fd, double dec, bool addSpace)
978 {
979  // Meade Telescope Serial Command Protocol Revision 2010.10
980  // :SdsDD*MM#
981  // Set target object declination to sDD*MM or sDD*MM:SS depending on the current precision setting
982  // Returns:
983  // 1 - Dec Accepted
984  // 0 – Dec invalid
985  //
986  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
987  // :SdsDD*MM# or :SdsDD*MM:SS# or :Sd sDD*MM:SS.S#
988  // Set target object declination to sDD*MM (sign, degrees, arcminutes), sDD*MM:SS
989  // (sign, degrees, arcminutes, arcseconds) or sDD*MM:SS.S (sign, degrees, arcminutes,
990  // arcseconds and tenths of arcsecond)
991  // Returns:
992  // 0 invalid
993  // 1 valid
994  //
995  // We support these formats:
996  // LX200_EQ_SHORT_FORMAT :SdsDD*MM# (sign, degrees, arcminutes)
997  // LX200_EQ_LONG_FORMAT :SdsDD*MM:SS# (sign, degrees, arcminutes, arcseconds)
998  // LX200_EQ_LONGER_FORMAT :Sd sDD*MM:SS.S# (sign, degrees, arcminutes, arcseconds, tenths of arcsecond)
999  // Add space is used to add space between the command the and rest of the arguments.
1000  // i.e. :Sd DD:MM:SS# for example since some mounts require space.
1001  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1002 
1003  /* Add mutex */
1004  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1005 
1006  char cmd[8] = {0};
1007  if (addSpace)
1008  strcpy(cmd, "Sd ");
1009  else
1010  strcpy(cmd, "Sd");
1011  int d, m, s;
1012  char read_buffer[22] = {0};
1013 
1014  switch (eq_format)
1015  {
1016  case LX200_EQ_SHORT_FORMAT:
1017  getSexComponents(dec, &d, &m, &s);
1018  /* case with negative zero */
1019  if (!d && dec < 0)
1020  snprintf(read_buffer, sizeof(read_buffer), ":%s-%02d*%02d#", cmd, d, m);
1021  else
1022  snprintf(read_buffer, sizeof(read_buffer), ":%s%+03d*%02d#", cmd, d, m);
1023  break;
1024  case LX200_EQ_LONG_FORMAT:
1025  getSexComponents(dec, &d, &m, &s);
1026  /* case with negative zero */
1027  if (!d && dec < 0)
1028  snprintf(read_buffer, sizeof(read_buffer), ":%s-%02d*%02d:%02d#", cmd, d, m, s);
1029  else
1030  snprintf(read_buffer, sizeof(read_buffer), ":%s%+03d*%02d:%02d#", cmd, d, m, s);
1031  break;
1033  double d_s;
1034  getSexComponentsIID(dec, &d, &m, &d_s);
1035  /* case with negative zero */
1036  if (!d && dec < 0)
1037  snprintf(read_buffer, sizeof(read_buffer), ":%s-%02d*%02d:%04.1f#", cmd, d, m, d_s);
1038  else
1039  snprintf(read_buffer, sizeof(read_buffer), ":%s%+03d*%02d:%04.1f#", cmd, d, m, d_s);
1040  break;
1041  default:
1042  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unknown controller_format <%d>", eq_format);
1043  return -1;
1044  }
1045 
1046  return (setStandardProcedure(fd, read_buffer));
1047 }
1048 
1049 int setCommandXYZ(int fd, int x, int y, int z, const char *cmd, bool addSpace)
1050 {
1051  char read_buffer[RB_MAX_LEN] = {0};
1052  snprintf(read_buffer, sizeof(read_buffer), addSpace ? "%s %02d:%02d:%02d#" : "%s%02d:%02d:%02d#", cmd, x, y, z);
1053 
1054  /* Add mutex */
1055  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1056 
1057  return (setStandardProcedure(fd, read_buffer));
1058 }
1059 
1060 int setAlignmentMode(int fd, unsigned int alignMode)
1061 {
1062  int error_type;
1063  int nbytes_write = 0;
1064 
1065  /* Add mutex */
1066  std::unique_lock<std::mutex> guard(lx200CommsLock);
1067 
1068  // Meade Telescope Serial Command Protocol Revision 2010.10
1069  // :AL# // Sets telescope to Land alignment mode // Returns: nothing
1070  // :AP# // Sets telescope to Polar alignment mode // Returns: nothing
1071  // :AA# // Sets telescope the AltAz alignment mode // Returns: nothing
1072  switch (alignMode)
1073  {
1074  case LX200_ALIGN_POLAR:
1075  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":AP#");
1076  if ((error_type = tty_write_string(fd, ":AP#", &nbytes_write)) != TTY_OK)
1077  return error_type;
1078  break;
1079  case LX200_ALIGN_ALTAZ:
1080  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":AA#");
1081  if ((error_type = tty_write_string(fd, ":AA#", &nbytes_write)) != TTY_OK)
1082  return error_type;
1083  break;
1084  case LX200_ALIGN_LAND:
1085  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":AL#");
1086  if ((error_type = tty_write_string(fd, ":AL#", &nbytes_write)) != TTY_OK)
1087  return error_type;
1088  break;
1089  }
1090 
1091  tcflush(fd, TCIFLUSH);
1092  return 0;
1093 }
1094 
1095 int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
1096 {
1097  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1098  const struct timespec timeout = {0, 10000000L};
1099  char read_buffer[RB_MAX_LEN];
1100  char dummy_buffer[RB_MAX_LEN];
1101  int error_type;
1102  int nbytes_write = 0, nbytes_read = 0;
1103  yy = yy % 100;
1104 
1105  /* Add mutex */
1106  std::unique_lock<std::mutex> guard(lx200CommsLock);
1107 
1108  // Meade Telescope Serial Command Protocol Revision 2010.10
1109  // :SCMM/DD/YY#
1110  // Change Handbox Date to MM/DD/YY
1111  // Returns: <D><string>
1112  // D = '0' if the date is invalid. The string is the null string.
1113  // D = '1' for valid dates and the string is "Updating Planetary Data# #"
1114  // Note: For LX200GPS/Autostar II this is the UTC data!
1115  //
1116  // 10Micron, the extended formats are documented here but not yet implemented.
1117  // :SCMM/DD/YY# or :SCMM/DD/YYYY# or :SCYYYY-MM-DD#
1118  // Set date to MM/DD/YY (month, day, year), MM/DD/YYYY (month, day, year) or YYYY-MM-DD (year, month, day).
1119  // The date is expressed in local time. Returns:
1120  // 0 if the date is invalid
1121  // The string "1Updating Planetary Data. # #" if the date is valid.
1122  // The string "1<32 spaces>#<32 spaces>#" in extended LX200 emulation mode.
1123  // The character "1" without additional strings in ultra-precision mode (regardless of emulation).
1124  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":SC %02d/%02d/%02d#" : ":SC%02d/%02d/%02d#", mm, dd, yy);
1125 
1126  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", read_buffer);
1127 
1128  tcflush(fd, TCIFLUSH);
1129 
1130  if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
1131  return error_type;
1132 
1133  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
1134  // Read the next section whih has 24 blanks and then a #
1135  // Can't just use the tcflush to clear the stream because it doesn't seem to work correctly on sockets
1136  tty_nread_section(fd, dummy_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
1137 
1138  tcflush(fd, TCIFLUSH);
1139 
1140  if (nbytes_read < 1)
1141  {
1142  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Unable to parse response");
1143  return error_type;
1144  }
1145 
1146  read_buffer[1] = '\0';
1147 
1148  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
1149 
1150  if (read_buffer[0] == '0')
1151  return -1;
1152 
1153  /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
1154  nanosleep(&timeout, nullptr);
1155  tcflush(fd, TCIFLUSH);
1156 
1157  return 0;
1158 }
1159 
1160 int setUTCOffset(int fd, double hours)
1161 {
1162  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1163  char read_buffer[RB_MAX_LEN] = {0};
1164 
1165  /* Add mutex */
1166  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1167 
1168  // Meade Telescope Serial Command Protocol Revision 2010.10
1169  // :SGsHH.H#
1170  // Set the number of hours added to local time to yield UTC
1171  // Returns:
1172  // 0 – Invalid
1173  // 1 - Valid
1174  snprintf(read_buffer, sizeof(read_buffer), ":SG%+04.01lf#", hours);
1175 
1176  return (setStandardProcedure(fd, read_buffer));
1177 }
1178 
1179 // Meade classic handset defines longitude as 0 to 360 WESTWARD. However,
1180 // Meade API expresses East Longitudes as negative, West Longitudes as positive.
1181 // Source: https://www.meade.com/support/LX200CommandSet.pdf from 2002 at :Gg#
1182 // (And also 10Micron has East Longitudes expressed as negative.)
1183 // Also note that this is the opposite of cartography where East is positive.
1184 int setSiteLongitude(int fd, double CartographicLongitude, bool addSpace)
1185 {
1186  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1187  int d, m, s;
1188  char read_buffer[RB_MAX_LEN] = {0};
1189  double LX200Longitude = -1.0 * CartographicLongitude;
1190 
1191  /* Add mutex */
1192  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1193 
1194  // Meade Telescope Serial Command Protocol Revision 2010.10
1195  // :SgDDD*MM#
1196  // Set current site’s longitude to DDD*MM an ASCII position string
1197  // Returns:
1198  // 0 – Invalid
1199  // 1 - Valid
1200  //
1201  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
1202  // :SgsDDD*MM# or :SgsDDD*MM:SS# or :SgsDDD*MM:SS.S#
1203  // Set current site’s longitude to sDDD*MM (sign, degrees, arcminutes), sDDD*MM:SS
1204  // (sign, degrees, arcminutes, arcseconds) or sDDD*MM:SS.S (sign, degrees, arcminutes,
1205  // arcseconds and tenths of arcsecond). Note: East Longitudes are expressed as negative.
1206  // Returns:
1207  // 0 invalid
1208  // 1 valid
1209  switch (geo_format)
1210  {
1211  case LX200_GEO_SHORT_FORMAT: // d m
1212  getSexComponents(LX200Longitude, &d, &m, &s);
1213  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":Sg %03d*%02d#" : ":Sg%03d*%02d#", d, m);
1214  break;
1215  case LX200_GEO_LONG_FORMAT: // d m s
1216  getSexComponents(LX200Longitude, &d, &m, &s);
1217  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":Sg %03d*%02d:%02d#" : ":Sg%03d*%02d:%02d#", d, m, s);
1218  break;
1219  case LX200_GEO_LONGER_FORMAT: // d m s.f with f being tenths
1220  double s_f;
1221  getSexComponentsIID(LX200Longitude, &d, &m, &s_f);
1222  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":Sg %03d*%02d:%04.01lf#" : ":Sg%03d*%02d:%04.01lf#", d, m, s_f);
1223  break;
1224  default:
1225  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unknown geographic format <%d>", geo_format);
1226  return -1;
1227  }
1228 
1229  return (setStandardProcedure(fd, read_buffer));
1230 }
1231 
1232 int setSiteLatitude(int fd, double Lat, bool addSpace)
1233 {
1234  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1235  int d, m, s;
1236  char read_buffer[RB_MAX_LEN] = {0};
1237 
1238  /* Add mutex */
1239  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1240 
1241  // Meade Telescope Serial Command Protocol Revision 2010.10
1242  // :StsDD*MM#
1243  // Sets the current site latitude to sDD*MM#
1244  // Returns:
1245  // 0 – Invalid
1246  // 1 - Valid
1247  //
1248  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
1249  // :StsDD*MM# or :StsDD*MM:SS# or :StsDD*MM:SS.S#
1250  // Sets the current site latitude to sDD*MM (sign, degrees, arcminutes), sDD*MM:SS
1251  // (sign, degrees, arcminutes, arcseconds), or sDD*MM:SS.S (sign, degrees, arcminutes,
1252  // arcseconds and tenths of arcsecond)
1253  // Returns:
1254  // 0 invalid
1255  // 1 valid
1256  switch (geo_format)
1257  {
1258  case LX200_GEO_SHORT_FORMAT: // d m
1259  getSexComponents(Lat, &d, &m, &s);
1260  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":St %+03d*%02d#" : ":St%+03d*%02d#", d, m);
1261  break;
1262  case LX200_GEO_LONG_FORMAT: // d m s
1263  getSexComponents(Lat, &d, &m, &s);
1264  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":St %+03d*%02d:%02d#" : ":St%+03d*%02d:%02d#", d, m, s);
1265  break;
1266  case LX200_GEO_LONGER_FORMAT: // d m s.f with f being tenths
1267  double s_f;
1268  getSexComponentsIID(Lat, &d, &m, &s_f);
1269  snprintf(read_buffer, sizeof(read_buffer), addSpace ? ":St %+03d*%02d:%04.01lf#" : ":St%+03d*%02d:%04.01lf#", d, m, s_f);
1270  break;
1271  default:
1272  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "Unknown geographic format <%d>", geo_format);
1273  return -1;
1274  }
1275 
1276  return (setStandardProcedure(fd, read_buffer));
1277 }
1278 
1279 int setObjAz(int fd, double az)
1280 {
1281  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1282  int d, m, s;
1283  char read_buffer[RB_MAX_LEN] = {0};
1284 
1285  /* Add mutex */
1286  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1287 
1288  getSexComponents(az, &d, &m, &s);
1289 
1290  // Meade Telescope Serial Command Protocol Revision 2010.10
1291  // :SzDDD*MM#
1292  // Sets the target Object Azimuth [LX 16” and Autostar II only]
1293  // Returns:
1294  // 0 – Invalid
1295  // 1 - Valid
1296  snprintf(read_buffer, sizeof(read_buffer), ":Sz%03d*%02d#", d, m);
1297 
1298  return (setStandardProcedure(fd, read_buffer));
1299 }
1300 
1301 int setObjAlt(int fd, double alt)
1302 {
1303  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1304  int d, m, s;
1305  char read_buffer[RB_MAX_LEN] = {0};
1306 
1307  /* Add mutex */
1308  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1309 
1310  getSexComponents(alt, &d, &m, &s);
1311 
1312  // Meade Telescope Serial Command Protocol Revision 2010.10
1313  // :SasDD*MM#
1314  // Set target object altitude to sDD*MM# or sDD*MM’SS# [LX 16”, Autostar, Autostar II]
1315  // Returns:
1316  // 1 Object within slew range
1317  // 0 Object out of slew range
1318  snprintf(read_buffer, sizeof(read_buffer), ":Sa%+02d*%02d#", d, m);
1319 
1320  return (setStandardProcedure(fd, read_buffer));
1321 }
1322 
1323 int setSiteName(int fd, char *siteName, int siteNum)
1324 {
1325  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1326  char read_buffer[RB_MAX_LEN] = {0};
1327 
1328  /* Add mutex */
1329  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1330 
1331  // Meade Telescope Serial Command Protocol Revision 2010.10
1332  // :SM<string># for site 1
1333  // :SN<string># for site 2
1334  // :SO<string># for site 3
1335  // :SP<string># for site 4
1336  // Set site name to be <string>. LX200s only accept 3 character strings. Other scopes accept up to 15 characters.
1337  // Returns:
1338  // 0 – Invalid
1339  // 1 - Valid
1340  switch (siteNum)
1341  {
1342  case 1:
1343  snprintf(read_buffer, sizeof(read_buffer), ":SM%s#", siteName);
1344  break;
1345  case 2:
1346  snprintf(read_buffer, sizeof(read_buffer), ":SN%s#", siteName);
1347  break;
1348  case 3:
1349  snprintf(read_buffer, sizeof(read_buffer), ":SO%s#", siteName);
1350  break;
1351  case 4:
1352  snprintf(read_buffer, sizeof(read_buffer), ":SP%s#", siteName);
1353  break;
1354  default:
1355  return -1;
1356  }
1357 
1358  return (setStandardProcedure(fd, read_buffer));
1359 }
1360 
1361 int setSlewMode(int fd, int slewMode)
1362 {
1363  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1364  int error_type;
1365  int nbytes_write = 0;
1366 
1367  /* Add mutex */
1368  std::unique_lock<std::mutex> guard(lx200CommsLock);
1369 
1370  // Meade Telescope Serial Command Protocol Revision 2010.10
1371  // :RS# // Set Slew rate to max (fastest) // Returns: Nothing
1372  // :RM# // Set Slew rate to Guiding Rate (slowest) // Returns: Nothing
1373  // :RC# // Set Slew rate to Centering rate (2nd slowest) // Returns: Nothing
1374  // :RG# // Set Slew rate to Find Rate (2nd Fastest) // Returns: Nothing
1375  switch (slewMode)
1376  {
1377  case LX200_SLEW_MAX:
1378  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":RS#");
1379  if ((error_type = tty_write_string(fd, ":RS#", &nbytes_write)) != TTY_OK)
1380  return error_type;
1381  break;
1382  case LX200_SLEW_FIND:
1383  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":RM#");
1384  if ((error_type = tty_write_string(fd, ":RM#", &nbytes_write)) != TTY_OK)
1385  return error_type;
1386  break;
1387  case LX200_SLEW_CENTER:
1388  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":RC#");
1389  if ((error_type = tty_write_string(fd, ":RC#", &nbytes_write)) != TTY_OK)
1390  return error_type;
1391  break;
1392  case LX200_SLEW_GUIDE:
1393  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":RG#");
1394  if ((error_type = tty_write_string(fd, ":RG#", &nbytes_write)) != TTY_OK)
1395  return error_type;
1396  break;
1397  default:
1398  break;
1399  }
1400 
1401  tcflush(fd, TCIFLUSH);
1402  return 0;
1403 }
1404 
1405 int setFocuserMotion(int fd, int motionType)
1406 {
1407  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1408  int error_type;
1409  int nbytes_write = 0;
1410 
1411  /* Add mutex */
1412  std::unique_lock<std::mutex> guard(lx200CommsLock);
1413 
1414  // Meade Telescope Serial Command Protocol Revision 2010.10
1415  // :F+# // Start Focuser moving inward (toward objective) // Returns: None
1416  // :F-# // Start Focuser moving outward (away from objective) // Returns: None
1417  switch (motionType)
1418  {
1419  case LX200_FOCUSIN:
1420  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":F+#");
1421  if ((error_type = tty_write_string(fd, ":F+#", &nbytes_write)) != TTY_OK)
1422  return error_type;
1423  break;
1424  case LX200_FOCUSOUT:
1425  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":F-#");
1426  if ((error_type = tty_write_string(fd, ":F-#", &nbytes_write)) != TTY_OK)
1427  return error_type;
1428  break;
1429  }
1430 
1431  tcflush(fd, TCIFLUSH);
1432  return 0;
1433 }
1434 
1435 int setFocuserSpeedMode(int fd, int speedMode)
1436 {
1437  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1438  int error_type;
1439  int nbytes_write = 0;
1440 
1441  /* Add mutex */
1442  std::unique_lock<std::mutex> guard(lx200CommsLock);
1443 
1444  // Meade Telescope Serial Command Protocol Revision 2010.10
1445  // :FQ# // Halt Focuser Motion // Returns: Nothing
1446  // :FS# // Set Focus speed to slowest setting // Returns: Nothing
1447  // :FF# // Set Focus speed to fastest setting // Returns: Nothing
1448  switch (speedMode)
1449  {
1450  case LX200_HALTFOCUS:
1451  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":FQ#");
1452  if ((error_type = tty_write_string(fd, ":FQ#", &nbytes_write)) != TTY_OK)
1453  return error_type;
1454  break;
1455  case LX200_FOCUSSLOW:
1456  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":FS#");
1457  if ((error_type = tty_write_string(fd, ":FS#", &nbytes_write)) != TTY_OK)
1458  return error_type;
1459  break;
1460  case LX200_FOCUSFAST:
1461  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":FF#");
1462  if ((error_type = tty_write_string(fd, ":FF#", &nbytes_write)) != TTY_OK)
1463  return error_type;
1464  break;
1465  }
1466 
1467  tcflush(fd, TCIFLUSH);
1468  return 0;
1469 }
1470 
1471 int setGPSFocuserSpeed(int fd, int speed)
1472 {
1473  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1474  char speed_str[8];
1475  int error_type;
1476  int nbytes_write = 0;
1477 
1478  /* Add mutex */
1479  std::unique_lock<std::mutex> guard(lx200CommsLock);
1480 
1481  // Meade Telescope Serial Command Protocol Revision 2010.10
1482  // :FQ# // Halt Focuser Motion // Returns: Nothing
1483  if (speed == 0)
1484  {
1485  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":FQ#");
1486  if ((error_type = tty_write_string(fd, ":FQ#", &nbytes_write)) != TTY_OK)
1487  return error_type;
1488 
1489  tcflush(fd, TCIFLUSH);
1490  return 0;
1491  }
1492 
1493  // Meade Telescope Serial Command Protocol Revision 2010.10
1494  // :F<n># Autostar, Autostar II – set focuser speed to <n> where <n> is an ASCII digit 1..4
1495  // Returns: Nothing
1496  // All others – Not Supported
1497  snprintf(speed_str, 8, ":F%d#", speed);
1498 
1499  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", speed_str);
1500 
1501  if ((error_type = tty_write_string(fd, speed_str, &nbytes_write)) != TTY_OK)
1502  return error_type;
1503 
1504  tcflush(fd, TCIFLUSH);
1505  return 0;
1506 }
1507 
1508 int setTrackFreq(int fd, double trackF)
1509 {
1510  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1511  char read_buffer[RB_MAX_LEN] = {0};
1512 
1513  /* Add mutex */
1514  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1515 
1516  // Meade Telescope Serial Command Protocol Revision 2002.10
1517  // :STTT.T#
1518  // Sets the current tracking rate to TTT.T hertz, assuming a model where a 60.0 Hertz synchronous motor will cause the RA
1519  // axis to make exactly one revolution in 24 hours.
1520  // Returns:
1521  // 0 – Invalid
1522  // 1 - Valid
1523  // Note: the definition :STTT.T# does not match the text.
1524  //
1525  // Meade Telescope Serial Command Protocol Revision 2010.10
1526  // :STdddd.ddddddd# [Autostar II Only]
1527  // Sets the current tracking rate to ddd.dddd hertz, assuming a model where a 60.0000 Hertz synchronous motor will cause
1528  // the RA axis to make exactly one revolution in 24 hours.
1529  // Returns:
1530  // 0 – Invalid
1531  // 2 – Valid
1532  // Note1: the definition :STdddd.ddddddd# looks bogus and does not match the text.
1533  // Note2: the 'Valid' response value of 2 looks bogus.
1534  // Note3: its appendix A lists :STDDD.DDD# which differs from both the previous definition as well as the text.
1535  //
1536  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
1537  // :STDDD.DDD#
1538  // Set the tracking rate to DDD.DDD, where DDD.DDD is a decimal number which is
1539  // four times the tracking rate expressed in arcseconds per second of time.
1540  // Returns:
1541  // 0 invalid
1542  // 1 valid
1543  //
1544  // Note: given the above definition mess the choice was made to implement :STTTT.T# which is probably what the 2002.10 spec intended.
1545  snprintf(read_buffer, sizeof(read_buffer), ":ST%05.01lf#", trackF);
1546 
1547  return (setStandardProcedure(fd, read_buffer));
1548 }
1549 
1550 int setPreciseTrackFreq(int fd, double trackF)
1551 {
1552  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1553  char read_buffer[RB_MAX_LEN] = {0};
1554 
1555  // TODO see spec of setTrackFreq where none describe a :STdd.ddddd#
1556  snprintf(read_buffer, sizeof(read_buffer), ":ST%08.5f#", trackF);
1557 
1558  /* Add mutex */
1559  /* std::unique_lock<std::mutex> guard(lx200CommsLock); */
1560 
1561  return (setStandardProcedure(fd, read_buffer));
1562 }
1563 
1564 /**********************************************************************
1565 * Misc
1566 *********************************************************************/
1567 
1568 int Slew(int fd)
1569 {
1570  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1571  char slewNum[2];
1572  int error_type;
1573  int nbytes_write = 0, nbytes_read = 0;
1574 
1575  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":MS#");
1576 
1577  /* Add mutex */
1578  std::unique_lock<std::mutex> guard(lx200CommsLock);
1579 
1580  // Meade Telescope Serial Command Protocol Revision 2010.10
1581  // :MS#
1582  // Slew to Target Object
1583  // Returns:
1584  // 0 Slew is Possible
1585  // 1<string># Object Below Horizon w/string message
1586  // 2<string># Object Below Higher w/string message
1587  if ((error_type = tty_write_string(fd, ":MS#", &nbytes_write)) != TTY_OK)
1588  return error_type;
1589 
1590  error_type = tty_read(fd, slewNum, 1, LX200_TIMEOUT, &nbytes_read);
1591 
1592  if (nbytes_read < 1)
1593  {
1594  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES ERROR <%d>", error_type);
1595  return error_type;
1596  }
1597 
1598  /* We don't need to read the string message, just return corresponding error code */
1599  tcflush(fd, TCIFLUSH);
1600 
1601  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%c>", slewNum[0]);
1602 
1603  error_type = slewNum[0] - '0';
1604  if ((error_type >= 0) && (error_type <= 9))
1605  {
1606  return error_type;
1607  }
1608  else
1609  {
1610  return -1;
1611  }
1612 }
1613 
1614 int MoveTo(int fd, int direction)
1615 {
1616  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1617  int nbytes_write = 0;
1618 
1619  /* Add mutex */
1620  std::unique_lock<std::mutex> guard(lx200CommsLock);
1621 
1622  // Meade Telescope Serial Command Protocol Revision 2010.10
1623  // :Mn# // Move Telescope North at current slew rate // Returns: Nothing
1624  // :Mw# // Move Telescope West at current slew rate // Returns: Nothing
1625  // :Me# // Move Telescope East at current slew rate // Returns: Nothing
1626  // :Ms# // Move Telescope South at current slew rate // Returns: Nothing
1627  switch (direction)
1628  {
1629  case LX200_NORTH:
1630  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Mn#");
1631  tty_write_string(fd, ":Mn#", &nbytes_write);
1632  break;
1633  case LX200_WEST:
1634  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Mw#");
1635  tty_write_string(fd, ":Mw#", &nbytes_write);
1636  break;
1637  case LX200_EAST:
1638  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Me#");
1639  tty_write_string(fd, ":Me#", &nbytes_write);
1640  break;
1641  case LX200_SOUTH:
1642  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Ms#");
1643  tty_write_string(fd, ":Ms#", &nbytes_write);
1644  break;
1645  default:
1646  break;
1647  }
1648 
1649  tcflush(fd, TCIFLUSH);
1650  return 0;
1651 }
1652 
1653 int SendPulseCmd(int fd, int direction, int duration_msec, bool wait_after_command, int max_wait_ms)
1654 {
1655  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1656  int nbytes_write = 0;
1657  char cmd[20];
1658 
1659  // Meade Telescope Serial Command Protocol Revision 2010.10
1660  // :MgnDDDD#
1661  // :MgsDDDD#
1662  // :MgeDDDD#
1663  // :MgwDDDD#
1664  // Guide telescope in the commanded direction (nsew) for the number of milliseconds indicated by the unsigned number
1665  // passed in the command. These commands support serial port driven guiding.
1666  // Returns – Nothing
1667  // LX200 – Not Supported
1668  switch (direction)
1669  {
1670  case LX200_NORTH:
1671  sprintf(cmd, ":Mgn%04d#", duration_msec);
1672  break;
1673  case LX200_SOUTH:
1674  sprintf(cmd, ":Mgs%04d#", duration_msec);
1675  break;
1676  case LX200_EAST:
1677  sprintf(cmd, ":Mge%04d#", duration_msec);
1678  break;
1679  case LX200_WEST:
1680  sprintf(cmd, ":Mgw%04d#", duration_msec);
1681  break;
1682  default:
1683  return 1;
1684  }
1685 
1686  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", cmd);
1687 
1688  /* Add mutex */
1689  std::unique_lock<std::mutex> guard(lx200CommsLock);
1690 
1691  tty_write_string(fd, cmd, &nbytes_write);
1692 
1693  tcflush(fd, TCIFLUSH);
1694 
1695  if(wait_after_command){
1696  if (duration_msec > max_wait_ms)
1697  duration_msec = max_wait_ms;
1698  struct timespec duration_ns = {.tv_sec = 0,.tv_nsec = duration_msec*1000000};
1699  nanosleep(&duration_ns, NULL);
1700  }
1701  return 0;
1702 }
1703 
1704 int HaltMovement(int fd, int direction)
1705 {
1706  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1707  int error_type;
1708  int nbytes_write = 0;
1709 
1710  /* Add mutex */
1711  std::unique_lock<std::mutex> guard(lx200CommsLock);
1712 
1713  // Meade Telescope Serial Command Protocol Revision 2010.10
1714  // :Qn# // Halt northward Slews // Returns: Nothing
1715  // :Qw# // Halt westward Slews // Returns: Nothing
1716  // :Qe# // Halt eastward Slews // Returns: Nothing
1717  // :Qs# // Halt southward Slews // Returns: Nothing
1718  // :Q# // Halt all current slewing // Returns: Nothing
1719  switch (direction)
1720  {
1721  case LX200_NORTH:
1722  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qn#");
1723  if ((error_type = tty_write_string(fd, ":Qn#", &nbytes_write)) != TTY_OK)
1724  return error_type;
1725  break;
1726  case LX200_WEST:
1727  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qw#");
1728  if ((error_type = tty_write_string(fd, ":Qw#", &nbytes_write)) != TTY_OK)
1729  return error_type;
1730  break;
1731  case LX200_EAST:
1732  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qe#");
1733  if ((error_type = tty_write_string(fd, ":Qe#", &nbytes_write)) != TTY_OK)
1734  return error_type;
1735  break;
1736  case LX200_SOUTH:
1737  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qs#");
1738  if ((error_type = tty_write_string(fd, ":Qs#", &nbytes_write)) != TTY_OK)
1739  return error_type;
1740  break;
1741  case LX200_ALL:
1742  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Q#");
1743  if ((error_type = tty_write_string(fd, ":Q#", &nbytes_write)) != TTY_OK)
1744  return error_type;
1745  break;
1746  default:
1747  return -1;
1748  }
1749 
1750  tcflush(fd, TCIFLUSH);
1751  return 0;
1752 }
1753 
1754 int abortSlew(int fd)
1755 {
1756  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1757  int error_type;
1758  int nbytes_write = 0;
1759 
1760  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Q#");
1761  /* Add mutex */
1762  std::unique_lock<std::mutex> guard(lx200CommsLock);
1763 
1764  // Meade Telescope Serial Command Protocol Revision 2010.10
1765  // :Q# // Halt all current slewing // Returns: Nothing
1766  if ((error_type = tty_write_string(fd, ":Q#", &nbytes_write)) != TTY_OK)
1767  return error_type;
1768 
1769  tcflush(fd, TCIFLUSH);
1770  return 0;
1771 }
1772 
1773 int Sync(int fd, char *matchedObject)
1774 {
1775  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1776  const struct timespec timeout = {0, 10000000L};
1777  int error_type;
1778  int nbytes_write = 0, nbytes_read = 0;
1779 
1780  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":CM#");
1781  /* Add mutex */
1782  std::unique_lock<std::mutex> guard(lx200CommsLock);
1783 
1784  // Meade Telescope Serial Command Protocol Revision 2010.10
1785  // :CM#
1786  // Synchronizes the telescope's position with the currently selected database object's coordinates.
1787  // Returns:
1788  // LX200's - a "#" terminated string with the name of the object that was synced.
1789  // Autostars & Autostar II - At static string: " M31 EX GAL MAG 3.5 SZ178.0'#"
1790  //
1791  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
1792  // :CM#
1793  // Synchronizes the position of the mount with the coordinates of the currently selected target.
1794  // Starting with version 2.8.15, this command has two possible behaviours depending on
1795  // the value passed to the last :CMCFGn# command. By default after startup, or after
1796  // the :CMCFG0# command has been given, the synchronization works by offsetting the
1797  // axis angles. If the :CMCFG1# command has been given, it works like the :CMS#
1798  // command, but returning the strings below.
1799  // Returns:
1800  // the string “Coordinates matched #” if the coordinates have been synchronized
1801  // the string “Match fail: dist. too large#” if the coordinates have not been synchronized
1802  if ((error_type = tty_write_string(fd, ":CM#", &nbytes_write)) != TTY_OK)
1803  return error_type;
1804 
1805  error_type = tty_nread_section(fd, matchedObject, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
1806 
1807  if (nbytes_read < 1)
1808  return error_type;
1809 
1810  matchedObject[nbytes_read - 1] = '\0';
1811 
1812  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", matchedObject);
1813 
1814  /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
1815  nanosleep(&timeout, nullptr);
1816  tcflush(fd, TCIFLUSH);
1817 
1818  return 0;
1819 }
1820 
1821 int selectSite(int fd, int siteNum)
1822 {
1823  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1824  int error_type;
1825  int nbytes_write = 0;
1826 
1827  /* Add mutex */
1828  std::unique_lock<std::mutex> guard(lx200CommsLock);
1829 
1830  // Meade Telescope Serial Command Protocol Revision 2002.10
1831  // :W<n>#
1832  // Set current site to <n>, an ASCII digit in the range 0..3
1833  // Returns: Nothing
1834  //
1835  // Meade Telescope Serial Command Protocol Revision 2010.10
1836  // :W<n>#
1837  // Set current site to <n>, an ASCII digit in the range 1..4
1838  // Returns: Nothing
1839  //
1840  // So Meade changed their mind on the offset :(
1841  // The azwing comments below implements of the 2002.10 versions.
1842  // TODO: auto determine which spec version to use !
1843  switch (siteNum)
1844  {
1845  case 1:
1846  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W0#");
1847  if ((error_type = tty_write_string(fd, ":W0#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1848  return error_type;
1849  break;
1850  case 2:
1851  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W1#");
1852  if ((error_type = tty_write_string(fd, ":W1#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1853  return error_type;
1854  break;
1855  case 3:
1856  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W2#");
1857  if ((error_type = tty_write_string(fd, ":W2#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1858  return error_type;
1859  break;
1860  case 4:
1861  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W3#");
1862  if ((error_type = tty_write_string(fd, ":W3#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1863  return error_type;
1864  break;
1865  default:
1866  return -1;
1867  }
1868 
1869  tcflush(fd, TCIFLUSH);
1870  return 0;
1871 }
1872 
1873 int selectCatalogObject(int fd, int catalog, int NNNN)
1874 {
1875  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1876  char read_buffer[RB_MAX_LEN] = {0};
1877  int error_type;
1878  int nbytes_write = 0;
1879 
1880  // Meade Telescope Serial Command Protocol Revision 2010.10
1881  // :LSNNNN#
1882  // Select star NNNN as the current target object from the currently selected catalog
1883  // Returns: Nothing
1884  // Autostar II & AutoStar – Available in later firmwares
1885  //
1886  // :LCNNNN#
1887  // Set current target object to deep sky catalog object number NNNN
1888  // Returns : Nothing
1889  // Autostar II & Autostar – Implemented in later firmware revisions
1890  //
1891  // :LMNNNN#
1892  // Set current target object to Messier Object NNNN, an ASCII expressed decimal number.
1893  // Returns: Nothing.
1894  // Autostar II and Autostar – Implemented in later versions.
1895  switch (catalog)
1896  {
1897  case LX200_STAR_C:
1898  snprintf(read_buffer, sizeof(read_buffer), ":LS%d#", NNNN);
1899  break;
1900  case LX200_DEEPSKY_C:
1901  snprintf(read_buffer, sizeof(read_buffer), ":LC%d#", NNNN);
1902  break;
1903  case LX200_MESSIER_C:
1904  snprintf(read_buffer, sizeof(read_buffer), ":LM%d#", NNNN);
1905  break;
1906  default:
1907  return -1;
1908  }
1909 
1910  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", read_buffer);
1911 
1912  /* Add mutex */
1913  std::unique_lock<std::mutex> guard(lx200CommsLock);
1914 
1915 
1916  if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
1917  return error_type;
1918 
1919  tcflush(fd, TCIFLUSH);
1920  return 0;
1921 }
1922 
1923 int selectSubCatalog(int fd, int catalog, int subCatalog)
1924 {
1925  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1926  char read_buffer[RB_MAX_LEN] = {0};
1927 
1928  // Meade Telescope Serial Command Protocol Revision 2010.10
1929  // :LsD#
1930  // Select star catalog D, an ASCII integer where D specifies:
1931  // 0 STAR library (Not supported on Autostar I & II)
1932  // 1 SAO library
1933  // 2 GCVS library
1934  // 3 Hipparcos (Autostar I & 2)
1935  // 4 HR (Autostar I & 2)
1936  // 5 HD (Autostar I & 2)
1937  // Returns:
1938  // 1 Catalog Available
1939  // 2 Catalog Not Found
1940  //
1941  // :LoD#
1942  // Select deep sky Library where D specifies
1943  // 0 - Objects CNGC / NGC in Autostar & Autostar II
1944  // 1 - Objects IC
1945  // 2 – UGC
1946  // 3 – Caldwell (Autostar & Autostar II)
1947  // 4 – Arp (LX200GPS/RCX)
1948  // 5 – Abell (LX200GPS/RCX)
1949  // Returns:
1950  // 1 Catalog available
1951  // 0 Catalog Not found
1952  switch (catalog)
1953  {
1954  case LX200_STAR_C:
1955  snprintf(read_buffer, sizeof(read_buffer), ":LsD%d#", subCatalog);
1956  break;
1957  case LX200_DEEPSKY_C:
1958  snprintf(read_buffer, sizeof(read_buffer), ":LoD%d#", subCatalog);
1959  break;
1960  case LX200_MESSIER_C:
1961  return 1;
1962  default:
1963  return 0;
1964  }
1965 
1966  return (setStandardProcedure(fd, read_buffer));
1967 }
1968 
1970 {
1971  return eq_format;
1972 }
1973 
1975 {
1976  return geo_format;
1977 }
1978 
1980 {
1981  char read_buffer[RB_MAX_LEN] = {0};
1983  int error_type;
1984  int nbytes_write = 0, nbytes_read = 0;
1985 
1986  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GR#");
1987 
1988  /* Add mutex */
1989  std::unique_lock<std::mutex> guard(lx200CommsLock);
1990 
1991  tcflush(fd, TCIFLUSH);
1992 
1993  // Meade Telescope Serial Command Protocol Revision 2010.10
1994  // :GR#
1995  // Get Telescope RA
1996  // Returns: HH:MM.T# or HH:MM:SS#
1997  // Depending which precision is set for the telescope
1998  //
1999  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
2000  // :GR#
2001  // Get telescope right ascension. Returns the current telescope right ascension formatted as follows:
2002  // Emulation and precision Return value
2003  // Any emulation, low precision HH:MM.M# (hours, minutes and tenths of minutes)
2004  // LX200 emulation, high precision HH:MM:SS# (hours, minutes, seconds)
2005  // Extended emulation, high precision HH:MM:SS.S# (hours, minutes, seconds and tenths of seconds)
2006  // Any emulation, ultra precision HH:MM:SS.SS# (hours, minutes, seconds and hundredths of seconds)
2007  if ((error_type = tty_write_string(fd, ":GR#", &nbytes_write)) != TTY_OK)
2008  return error_type;
2009 
2010  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
2011 
2012  if (nbytes_read < 1)
2013  {
2014  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES ERROR <%d>", error_type);
2015  return error_type;
2016  }
2017 
2018  read_buffer[nbytes_read - 1] = '\0';
2019 
2020  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
2021 
2022  // 10micron returns on U2 15:46:18.03 . Prevent setting it to a lower precision later by detecting this mode here.
2023  if (nbytes_read >= 11 && read_buffer[8] == '.')
2024  {
2026  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is ultra high precision.");
2027  return 0;
2028  }
2029 
2030  /* If it's short format, try to toggle to high precision format */
2031  if (read_buffer[5] == '.')
2032  {
2033  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Detected low precision equatorial format, attempting to switch to high precision.");
2034  // Meade Telescope Serial Command Protocol Revision 2010.10
2035  // :U#
2036  // Toggle between low/hi precision positions
2037  // Low - RA displays and messages HH:MM.T sDD*MM
2038  // High - Dec/Az/El displays and messages HH:MM:SS sDD*MM:SS
2039  // Returns Nothing
2040  //
2041  // 10Micron Mount Command Protocol software version 2.14.11 2016.11
2042  // :U#
2043  // Toggle between low and high precision modes. This controls the format of some values
2044  // that are returned by the mount. In extened LX200 emulation mode, switches always to
2045  // high precision (does not toggle).
2046  // Low precision: RA returned as HH:MM.T (hours, minutes and tenths of minutes),
2047  // Dec/Az/Alt returned as sDD*MM (sign, degrees, arcminutes).
2048  // High precision: RA returned as HH:MM:SS (hours, minutes, seconds), Dec/Az/Alt
2049  // returned as sDD*MM:SS (sign, degrees, arcminutes, arcseconds).
2050  // Returns: nothing
2051  // :U0#
2052  // Set low precision mode.
2053  // Returns: nothing
2054  // :U1#
2055  // Set high precision mode.
2056  // Returns: nothing
2057  // :U2#
2058  // Set ultra precision mode. In ultra precision mode, extra decimal digits are returned for
2059  // some commands, and there is no more difference between different emulation modes.
2060  // Returns: nothing
2061  // Available from version 2.10.
2062  if ((error_type = tty_write_string(fd, ":U#", &nbytes_write)) != TTY_OK)
2063  return error_type;
2064  }
2065  else if (read_buffer[8] == '.')
2066  {
2068  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is ultra high precision.");
2069  return 0;
2070  }
2071  else
2072  {
2074  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is high precision.");
2075  return 0;
2076  }
2077 
2078  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GR#");
2079 
2080  tcflush(fd, TCIFLUSH);
2081 
2082  if ((error_type = tty_write_string(fd, ":GR#", &nbytes_write)) != TTY_OK)
2083  return error_type;
2084 
2085  error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
2086 
2087  if (nbytes_read < 1)
2088  {
2089  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES ERROR <%d>", error_type);
2090  return error_type;
2091  }
2092 
2093  read_buffer[nbytes_read - 1] = '\0';
2094 
2095  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
2096 
2097  if (read_buffer[5] == '.')
2098  {
2100  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is low precision.");
2101  }
2102  else
2103  {
2105  DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is high precision.");
2106  }
2107 
2108  tcflush(fd, TCIFLUSH);
2109 
2110  return 0;
2111 }
2112 
2113 int selectTrackingMode(int fd, int trackMode)
2114 {
2115  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
2116  int error_type;
2117  int nbytes_write = 0;
2118 
2119  /* Add mutex */
2120  std::unique_lock<std::mutex> guard(lx200CommsLock);
2121 
2122  // Meade Telescope Serial Command Protocol Revision 2010.10
2123  // :TQ# Selects sidereal tracking rate Returns: Nothing
2124  // :TS# Select Solar tracking rate. [LS Only] Returns: Nothing
2125  // :TL# Set Lunar Tracking Rate Returns: Nothing
2126  // :TM# Select custom tracking rate [no-op in Autostar II] Returns: Nothing
2127  switch (trackMode)
2128  {
2129  case LX200_TRACK_SIDEREAL:
2130  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TQ#");
2131  if ((error_type = tty_write_string(fd, ":TQ#", &nbytes_write)) != TTY_OK)
2132  return error_type;
2133  break;
2134  case LX200_TRACK_SOLAR:
2135  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TS#");
2136  if ((error_type = tty_write_string(fd, ":TS#", &nbytes_write)) != TTY_OK)
2137  return error_type;
2138  break;
2139  case LX200_TRACK_LUNAR:
2140  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TL#");
2141  if ((error_type = tty_write_string(fd, ":TL#", &nbytes_write)) != TTY_OK)
2142  return error_type;
2143  break;
2144  case LX200_TRACK_MANUAL:
2145  DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TM#");
2146  if ((error_type = tty_write_string(fd, ":TM#", &nbytes_write)) != TTY_OK)
2147  return error_type;
2148  break;
2149  default:
2150  return -1;
2151  }
2152 
2153  tcflush(fd, TCIFLUSH);
2154  return 0;
2155 }
2156 
2157 int setLocalTime(int fd, int x, int y, int z, bool addSpace)
2158 {
2159  return setCommandXYZ(fd, x, y, z, ":SL", addSpace);
2160 }
2161 
2162 int setSDTime(int fd, int x, int y, int z, bool addSpace)
2163 {
2164  return setCommandXYZ(fd, x, y, z, ":SS", addSpace);
2165 }
double max(void)
double min(void)
double ra
double dec
#define MAXINDIDEVICE
Definition: indiapi.h:193
void getSexComponentsIID(double value, int *d, int *m, double *s)
Definition: indicom.c:277
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition: indicom.c:205
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
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
int fd
Definition: intelliscope.c:43
int setFocuserMotion(int fd, int motionType)
int setAlignmentMode(int fd, unsigned int alignMode)
char lx200Name[MAXINDIDEVICE]
Definition: lx200driver.cpp:51
int setSlewMode(int fd, int slewMode)
int setObjAlt(int fd, double alt)
int setMinElevationLimit(int fd, int min)
std::mutex lx200CommsLock
Definition: lx200driver.cpp:56
int getHomeSearchStatus(int fd, int *status)
int SendPulseCmd(int fd, int direction, int duration_msec, bool wait_after_command, int max_wait_ms)
int getSiteLongitude(int fd, int *ddd, int *mm, double *ssf)
int setObjAz(int fd, double az)
#define RB_MAX_LEN
Definition: lx200driver.cpp:46
int selectTrackingMode(int fd, int trackMode)
int setLocalTime(int fd, int x, int y, int z, bool addSpace)
int getSiteLatitudeAlt(int fd, int *dd, int *mm, double *ssf, const char *cmd)
int setMaxSlewRate(int fd, int slewRate)
int getSiteName(int fd, char *siteName, int siteNum)
int setMaxElevationLimit(int fd, int max)
int selectSite(int fd, int siteNum)
int getCommandInt(int fd, int *value, const char *cmd)
int setSDTime(int fd, int x, int y, int z, bool addSpace)
int setObjectRA(int fd, double ra, bool addSpace)
int getLX200EquatorialFormat()
int setStandardProcedure(int fd, const char *data)
int setSiteLongitude(int fd, double CartographicLongitude, bool addSpace)
int selectSubCatalog(int fd, int catalog, int subCatalog)
int setObjectDEC(int fd, double dec, bool addSpace)
int checkLX200EquatorialFormat(int fd)
unsigned int DBG_SCOPE
Definition: lx200driver.cpp:53
int abortSlew(int fd)
int setSiteLatitude(int fd, double Lat, bool addSpace)
int getCommandSexa(int fd, double *value, const char *cmd)
char ACK(int fd)
int Sync(int fd, char *matchedObject)
int setTrackFreq(int fd, double trackF)
int eq_format
Definition: lx200driver.cpp:49
int setFocuserSpeedMode(int fd, int speedMode)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int getCalendarDate(int fd, char *date)
int Slew(int fd)
int MoveTo(int fd, int direction)
int setCommandXYZ(int fd, int x, int y, int z, const char *cmd, bool addSpace)
int getTimeFormat(int fd, int *format)
int setCommandInt(int fd, int data, const char *cmd)
int getOTATemp(int fd, double *value)
int geo_format
Definition: lx200driver.cpp:50
int getLX200GeographicFormat()
int getTrackFreq(int fd, double *value)
int setPreciseTrackFreq(int fd, double trackF)
int setSiteName(int fd, char *siteName, int siteNum)
void setLX200Debug(const char *deviceName, unsigned int debug_level)
Definition: lx200driver.cpp:58
int setUTCOffset(int fd, double hours)
int check_lx200_connection(int in_fd)
Definition: lx200driver.cpp:64
int setGPSFocuserSpeed(int fd, int speed)
int getSiteLongitudeAlt(int fd, int *ddd, int *mm, double *ssf, const char *cmd)
int HaltMovement(int fd, int direction)
int getCommandString(int fd, char *data, const char *cmd)
int getSiteLatitude(int fd, int *dd, int *mm, double *ssf)
int isSlewComplete(int fd)
int selectCatalogObject(int fd, int catalog, int NNNN)
#define LX200_TIMEOUT
Definition: lx200driver.cpp:45
@ LX200_FOCUSOUT
Definition: lx200driver.h:72
@ LX200_FOCUSIN
Definition: lx200driver.h:71
@ LX200_ALIGN_POLAR
Definition: lx200driver.h:34
@ LX200_ALIGN_LAND
Definition: lx200driver.h:36
@ LX200_ALIGN_ALTAZ
Definition: lx200driver.h:35
@ LX200_DEEPSKY_C
Definition: lx200driver.h:84
@ LX200_STAR_C
Definition: lx200driver.h:83
@ LX200_FOCUSSLOW
Definition: lx200driver.h:77
@ LX200_HALTFOCUS
Definition: lx200driver.h:76
@ LX200_FOCUSFAST
Definition: lx200driver.h:78
@ LX200_TRACK_MANUAL
Definition: lx200driver.h:110
@ LX200_TRACK_SOLAR
Definition: lx200driver.h:108
@ LX200_TRACK_LUNAR
Definition: lx200driver.h:109
@ LX200_TRACK_SIDEREAL
Definition: lx200driver.h:107
@ LX200_WEST
Definition: lx200driver.h:42
@ LX200_ALL
Definition: lx200driver.h:45
@ LX200_SOUTH
Definition: lx200driver.h:44
@ LX200_NORTH
Definition: lx200driver.h:41
@ LX200_EAST
Definition: lx200driver.h:43
@ LX200_EQ_LONGER_FORMAT
Definition: lx200driver.h:52
@ LX200_EQ_SHORT_FORMAT
Definition: lx200driver.h:50
@ LX200_EQ_LONG_FORMAT
Definition: lx200driver.h:51
@ LX200_GEO_SHORT_FORMAT
Definition: lx200driver.h:57
@ LX200_GEO_LONG_FORMAT
Definition: lx200driver.h:58
@ LX200_GEO_LONGER_FORMAT
Definition: lx200driver.h:59
@ LX200_MESSIER_C
Definition: lx200driver.h:102
@ LX200_SLEW_CENTER
Definition: lx200driver.h:28
@ LX200_SLEW_GUIDE
Definition: lx200driver.h:29
@ LX200_SLEW_MAX
Definition: lx200driver.h:26
@ LX200_SLEW_FIND
Definition: lx200driver.h:27
__u8 cmd[4]
Definition: pwc-ioctl.h:2