Instrument Neutral Distributed Interface INDI 2.2.5
lx200driver.cpp
Go to the documentation of this file.
1#if 0
2LX200 Driver
3Copyright (C) 2003 Jasem Mutlaq ([email protected])
4
5This library is free software;
6you can redistribute it and / or
7modify it under the terms of the GNU Lesser General Public
8License as published by the Free Software Foundation;
9either
10version 2.1 of the License, or (at your option) any later version.
11
12This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY;
14without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16Lesser General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this library;
20if not, write to the Free Software
21Foundation, 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#if defined(__FreeBSD__) || defined(__OpenBSD__)
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
49int eq_format; /* For possible values see enum TEquatorialFormat */
50int geo_format = LX200_GEO_SHORT_FORMAT; /* For possible values see enum TGeographicFormat */
52/* ESN DEBUG */
53unsigned int DBG_SCOPE = 8;
54
55/* Add mutex to communications */
56std::mutex lx200CommsLock;
57
58void setLX200Debug(const char *deviceName, unsigned int debug_level)
59{
60 snprintf(lx200Name, MAXINDIDEVICE, "%s", deviceName);
61 DBG_SCOPE = debug_level;
62}
63
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
108char 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
143int 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
180int 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
223int 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
315int 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
368int 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
415int 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
479int 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.
505int 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
526int 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
588int 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
649int 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 synchronous 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
694int 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
739int 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
787int 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
821int 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
892int 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
914int 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
977int 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 {
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;
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
1049int 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
1060int 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
1095int 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 which 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
1160int 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.
1184int 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
1232int 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
1279int 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
1301int 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
1323int 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
1361int 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
1405int 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
1435int 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
1471int 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
1508int 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
1550int 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
1568int 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
1614int 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
1653int 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 {
1697 if (duration_msec > max_wait_ms)
1698 duration_msec = max_wait_ms;
1699 struct timespec duration_ns = {.tv_sec = 0, .tv_nsec = duration_msec * 1000000};
1700 nanosleep(&duration_ns, NULL);
1701 }
1702 return 0;
1703}
1704
1705int HaltMovement(int fd, int direction)
1706{
1707 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1708 int error_type;
1709 int nbytes_write = 0;
1710
1711 /* Add mutex */
1712 std::unique_lock<std::mutex> guard(lx200CommsLock);
1713
1714 // Meade Telescope Serial Command Protocol Revision 2010.10
1715 // :Qn# // Halt northward Slews // Returns: Nothing
1716 // :Qw# // Halt westward Slews // Returns: Nothing
1717 // :Qe# // Halt eastward Slews // Returns: Nothing
1718 // :Qs# // Halt southward Slews // Returns: Nothing
1719 // :Q# // Halt all current slewing // Returns: Nothing
1720 switch (direction)
1721 {
1722 case LX200_NORTH:
1723 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qn#");
1724 if ((error_type = tty_write_string(fd, ":Qn#", &nbytes_write)) != TTY_OK)
1725 return error_type;
1726 break;
1727 case LX200_WEST:
1728 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qw#");
1729 if ((error_type = tty_write_string(fd, ":Qw#", &nbytes_write)) != TTY_OK)
1730 return error_type;
1731 break;
1732 case LX200_EAST:
1733 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qe#");
1734 if ((error_type = tty_write_string(fd, ":Qe#", &nbytes_write)) != TTY_OK)
1735 return error_type;
1736 break;
1737 case LX200_SOUTH:
1738 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Qs#");
1739 if ((error_type = tty_write_string(fd, ":Qs#", &nbytes_write)) != TTY_OK)
1740 return error_type;
1741 break;
1742 case LX200_ALL:
1743 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Q#");
1744 if ((error_type = tty_write_string(fd, ":Q#", &nbytes_write)) != TTY_OK)
1745 return error_type;
1746 break;
1747 default:
1748 return -1;
1749 }
1750
1751 tcflush(fd, TCIFLUSH);
1752 return 0;
1753}
1754
1756{
1757 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1758 int error_type;
1759 int nbytes_write = 0;
1760
1761 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":Q#");
1762 /* Add mutex */
1763 std::unique_lock<std::mutex> guard(lx200CommsLock);
1764
1765 // Meade Telescope Serial Command Protocol Revision 2010.10
1766 // :Q# // Halt all current slewing // Returns: Nothing
1767 if ((error_type = tty_write_string(fd, ":Q#", &nbytes_write)) != TTY_OK)
1768 return error_type;
1769
1770 tcflush(fd, TCIFLUSH);
1771 return 0;
1772}
1773
1774int Sync(int fd, char *matchedObject)
1775{
1776 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1777 const struct timespec timeout = {0, 10000000L};
1778 int error_type;
1779 int nbytes_write = 0, nbytes_read = 0;
1780
1781 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":CM#");
1782 /* Add mutex */
1783 std::unique_lock<std::mutex> guard(lx200CommsLock);
1784
1785 // Meade Telescope Serial Command Protocol Revision 2010.10
1786 // :CM#
1787 // Synchronizes the telescope's position with the currently selected database object's coordinates.
1788 // Returns:
1789 // LX200's - a "#" terminated string with the name of the object that was synced.
1790 // Autostars & Autostar II - At static string: " M31 EX GAL MAG 3.5 SZ178.0'#"
1791 //
1792 // 10Micron Mount Command Protocol software version 2.14.11 2016.11
1793 // :CM#
1794 // Synchronizes the position of the mount with the coordinates of the currently selected target.
1795 // Starting with version 2.8.15, this command has two possible behaviours depending on
1796 // the value passed to the last :CMCFGn# command. By default after startup, or after
1797 // the :CMCFG0# command has been given, the synchronization works by offsetting the
1798 // axis angles. If the :CMCFG1# command has been given, it works like the :CMS#
1799 // command, but returning the strings below.
1800 // Returns:
1801 // the string “Coordinates matched #” if the coordinates have been synchronized
1802 // the string “Match fail: dist. too large#” if the coordinates have not been synchronized
1803 if ((error_type = tty_write_string(fd, ":CM#", &nbytes_write)) != TTY_OK)
1804 return error_type;
1805
1806 error_type = tty_nread_section(fd, matchedObject, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
1807
1808 if (nbytes_read < 1)
1809 return error_type;
1810
1811 matchedObject[nbytes_read - 1] = '\0';
1812
1813 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", matchedObject);
1814
1815 /* Sleep 10ms before flushing. This solves some issues with LX200 compatible devices. */
1816 nanosleep(&timeout, nullptr);
1817 tcflush(fd, TCIFLUSH);
1818
1819 return 0;
1820}
1821
1822int selectSite(int fd, int siteNum)
1823{
1824 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1825 int error_type;
1826 int nbytes_write = 0;
1827
1828 /* Add mutex */
1829 std::unique_lock<std::mutex> guard(lx200CommsLock);
1830
1831 // Meade Telescope Serial Command Protocol Revision 2002.10
1832 // :W<n>#
1833 // Set current site to <n>, an ASCII digit in the range 0..3
1834 // Returns: Nothing
1835 //
1836 // Meade Telescope Serial Command Protocol Revision 2010.10
1837 // :W<n>#
1838 // Set current site to <n>, an ASCII digit in the range 1..4
1839 // Returns: Nothing
1840 //
1841 // So Meade changed their mind on the offset :(
1842 // The azwing comments below implements of the 2002.10 versions.
1843 // TODO: auto determine which spec version to use !
1844 switch (siteNum)
1845 {
1846 case 1:
1847 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W0#");
1848 if ((error_type = tty_write_string(fd, ":W0#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1849 return error_type;
1850 break;
1851 case 2:
1852 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W1#");
1853 if ((error_type = tty_write_string(fd, ":W1#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1854 return error_type;
1855 break;
1856 case 3:
1857 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W2#");
1858 if ((error_type = tty_write_string(fd, ":W2#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1859 return error_type;
1860 break;
1861 case 4:
1862 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":W3#");
1863 if ((error_type = tty_write_string(fd, ":W3#", &nbytes_write)) != TTY_OK) //azwing index starts at 0 not 1
1864 return error_type;
1865 break;
1866 default:
1867 return -1;
1868 }
1869
1870 tcflush(fd, TCIFLUSH);
1871 return 0;
1872}
1873
1874int selectCatalogObject(int fd, int catalog, int NNNN)
1875{
1876 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1877 char read_buffer[RB_MAX_LEN] = {0};
1878 int error_type;
1879 int nbytes_write = 0;
1880
1881 // Meade Telescope Serial Command Protocol Revision 2010.10
1882 // :LSNNNN#
1883 // Select star NNNN as the current target object from the currently selected catalog
1884 // Returns: Nothing
1885 // Autostar II & AutoStar – Available in later firmwares
1886 //
1887 // :LCNNNN#
1888 // Set current target object to deep sky catalog object number NNNN
1889 // Returns : Nothing
1890 // Autostar II & Autostar – Implemented in later firmware revisions
1891 //
1892 // :LMNNNN#
1893 // Set current target object to Messier Object NNNN, an ASCII expressed decimal number.
1894 // Returns: Nothing.
1895 // Autostar II and Autostar – Implemented in later versions.
1896 switch (catalog)
1897 {
1898 case LX200_STAR_C:
1899 snprintf(read_buffer, sizeof(read_buffer), ":LS%d#", NNNN);
1900 break;
1901 case LX200_DEEPSKY_C:
1902 snprintf(read_buffer, sizeof(read_buffer), ":LC%d#", NNNN);
1903 break;
1904 case LX200_MESSIER_C:
1905 snprintf(read_buffer, sizeof(read_buffer), ":LM%d#", NNNN);
1906 break;
1907 default:
1908 return -1;
1909 }
1910
1911 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", read_buffer);
1912
1913 /* Add mutex */
1914 std::unique_lock<std::mutex> guard(lx200CommsLock);
1915
1916
1917 if ((error_type = tty_write_string(fd, read_buffer, &nbytes_write)) != TTY_OK)
1918 return error_type;
1919
1920 tcflush(fd, TCIFLUSH);
1921 return 0;
1922}
1923
1924int selectSubCatalog(int fd, int catalog, int subCatalog)
1925{
1926 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
1927 char read_buffer[RB_MAX_LEN] = {0};
1928
1929 // Meade Telescope Serial Command Protocol Revision 2010.10
1930 // :LsD#
1931 // Select star catalog D, an ASCII integer where D specifies:
1932 // 0 STAR library (Not supported on Autostar I & II)
1933 // 1 SAO library
1934 // 2 GCVS library
1935 // 3 Hipparcos (Autostar I & 2)
1936 // 4 HR (Autostar I & 2)
1937 // 5 HD (Autostar I & 2)
1938 // Returns:
1939 // 1 Catalog Available
1940 // 2 Catalog Not Found
1941 //
1942 // :LoD#
1943 // Select deep sky Library where D specifies
1944 // 0 - Objects CNGC / NGC in Autostar & Autostar II
1945 // 1 - Objects IC
1946 // 2 – UGC
1947 // 3 – Caldwell (Autostar & Autostar II)
1948 // 4 – Arp (LX200GPS/RCX)
1949 // 5 – Abell (LX200GPS/RCX)
1950 // Returns:
1951 // 1 Catalog available
1952 // 0 Catalog Not found
1953 switch (catalog)
1954 {
1955 case LX200_STAR_C:
1956 snprintf(read_buffer, sizeof(read_buffer), ":LsD%d#", subCatalog);
1957 break;
1958 case LX200_DEEPSKY_C:
1959 snprintf(read_buffer, sizeof(read_buffer), ":LoD%d#", subCatalog);
1960 break;
1961 case LX200_MESSIER_C:
1962 return 1;
1963 default:
1964 return 0;
1965 }
1966
1967 return (setStandardProcedure(fd, read_buffer));
1968}
1969
1971{
1972 return eq_format;
1973}
1974
1976{
1977 return geo_format;
1978}
1979
1981{
1982 char read_buffer[RB_MAX_LEN] = {0};
1984 int error_type;
1985 int nbytes_write = 0, nbytes_read = 0;
1986
1987 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GR#");
1988
1989 /* Add mutex */
1990 std::unique_lock<std::mutex> guard(lx200CommsLock);
1991
1992 tcflush(fd, TCIFLUSH);
1993
1994 // Meade Telescope Serial Command Protocol Revision 2010.10
1995 // :GR#
1996 // Get Telescope RA
1997 // Returns: HH:MM.T# or HH:MM:SS#
1998 // Depending which precision is set for the telescope
1999 //
2000 // 10Micron Mount Command Protocol software version 2.14.11 2016.11
2001 // :GR#
2002 // Get telescope right ascension. Returns the current telescope right ascension formatted as follows:
2003 // Emulation and precision Return value
2004 // Any emulation, low precision HH:MM.M# (hours, minutes and tenths of minutes)
2005 // LX200 emulation, high precision HH:MM:SS# (hours, minutes, seconds)
2006 // Extended emulation, high precision HH:MM:SS.S# (hours, minutes, seconds and tenths of seconds)
2007 // Any emulation, ultra precision HH:MM:SS.SS# (hours, minutes, seconds and hundredths of seconds)
2008 if ((error_type = tty_write_string(fd, ":GR#", &nbytes_write)) != TTY_OK)
2009 return error_type;
2010
2011 error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
2012
2013 if (nbytes_read < 1)
2014 {
2015 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES ERROR <%d>", error_type);
2016 return error_type;
2017 }
2018
2019 read_buffer[nbytes_read - 1] = '\0';
2020
2021 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
2022
2023 // 10micron returns on U2 15:46:18.03 . Prevent setting it to a lower precision later by detecting this mode here.
2024 if (nbytes_read >= 11 && read_buffer[8] == '.')
2025 {
2027 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is ultra high precision.");
2028 return 0;
2029 }
2030
2031 /* If it's short format, try to toggle to high precision format */
2032 if (read_buffer[5] == '.')
2033 {
2034 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Detected low precision equatorial format, attempting to switch to high precision.");
2035 // Meade Telescope Serial Command Protocol Revision 2010.10
2036 // :U#
2037 // Toggle between low/hi precision positions
2038 // Low - RA displays and messages HH:MM.T sDD*MM
2039 // High - Dec/Az/El displays and messages HH:MM:SS sDD*MM:SS
2040 // Returns Nothing
2041 //
2042 // 10Micron Mount Command Protocol software version 2.14.11 2016.11
2043 // :U#
2044 // Toggle between low and high precision modes. This controls the format of some values
2045 // that are returned by the mount. In extended LX200 emulation mode, switches always to
2046 // high precision (does not toggle).
2047 // Low precision: RA returned as HH:MM.T (hours, minutes and tenths of minutes),
2048 // Dec/Az/Alt returned as sDD*MM (sign, degrees, arcminutes).
2049 // High precision: RA returned as HH:MM:SS (hours, minutes, seconds), Dec/Az/Alt
2050 // returned as sDD*MM:SS (sign, degrees, arcminutes, arcseconds).
2051 // Returns: nothing
2052 // :U0#
2053 // Set low precision mode.
2054 // Returns: nothing
2055 // :U1#
2056 // Set high precision mode.
2057 // Returns: nothing
2058 // :U2#
2059 // Set ultra precision mode. In ultra precision mode, extra decimal digits are returned for
2060 // some commands, and there is no more difference between different emulation modes.
2061 // Returns: nothing
2062 // Available from version 2.10.
2063 if ((error_type = tty_write_string(fd, ":U#", &nbytes_write)) != TTY_OK)
2064 return error_type;
2065 }
2066 else if (read_buffer[8] == '.')
2067 {
2069 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is ultra high precision.");
2070 return 0;
2071 }
2072 else
2073 {
2075 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is high precision.");
2076 return 0;
2077 }
2078
2079 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":GR#");
2080
2081 tcflush(fd, TCIFLUSH);
2082
2083 if ((error_type = tty_write_string(fd, ":GR#", &nbytes_write)) != TTY_OK)
2084 return error_type;
2085
2086 error_type = tty_nread_section(fd, read_buffer, RB_MAX_LEN, '#', LX200_TIMEOUT, &nbytes_read);
2087
2088 if (nbytes_read < 1)
2089 {
2090 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES ERROR <%d>", error_type);
2091 return error_type;
2092 }
2093
2094 read_buffer[nbytes_read - 1] = '\0';
2095
2096 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "RES <%s>", read_buffer);
2097
2098 if (read_buffer[5] == '.')
2099 {
2101 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is low precision.");
2102 }
2103 else
2104 {
2106 DEBUGDEVICE(lx200Name, DBG_SCOPE, "Equatorial coordinate format is high precision.");
2107 }
2108
2109 tcflush(fd, TCIFLUSH);
2110
2111 return 0;
2112}
2113
2114int selectTrackingMode(int fd, int trackMode)
2115{
2116 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "<%s>", __FUNCTION__);
2117 int error_type;
2118 int nbytes_write = 0;
2119
2120 /* Add mutex */
2121 std::unique_lock<std::mutex> guard(lx200CommsLock);
2122
2123 // Meade Telescope Serial Command Protocol Revision 2010.10
2124 // :TQ# Selects sidereal tracking rate Returns: Nothing
2125 // :TS# Select Solar tracking rate. [LS Only] Returns: Nothing
2126 // :TL# Set Lunar Tracking Rate Returns: Nothing
2127 // :TM# Select custom tracking rate [no-op in Autostar II] Returns: Nothing
2128 switch (trackMode)
2129 {
2131 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TQ#");
2132 if ((error_type = tty_write_string(fd, ":TQ#", &nbytes_write)) != TTY_OK)
2133 return error_type;
2134 break;
2135 case LX200_TRACK_SOLAR:
2136 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TS#");
2137 if ((error_type = tty_write_string(fd, ":TS#", &nbytes_write)) != TTY_OK)
2138 return error_type;
2139 break;
2140 case LX200_TRACK_LUNAR:
2141 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TL#");
2142 if ((error_type = tty_write_string(fd, ":TL#", &nbytes_write)) != TTY_OK)
2143 return error_type;
2144 break;
2145 case LX200_TRACK_MANUAL:
2146 DEBUGFDEVICE(lx200Name, DBG_SCOPE, "CMD <%s>", ":TM#");
2147 if ((error_type = tty_write_string(fd, ":TM#", &nbytes_write)) != TTY_OK)
2148 return error_type;
2149 break;
2150 default:
2151 return -1;
2152 }
2153
2154 tcflush(fd, TCIFLUSH);
2155 return 0;
2156}
2157
2158int setLocalTime(int fd, int x, int y, int z, bool addSpace)
2159{
2160 return setCommandXYZ(fd, x, y, z, ":SL", addSpace);
2161}
2162
2163int setSDTime(int fd, int x, int y, int z, bool addSpace)
2164{
2165 return setCommandXYZ(fd, x, y, z, ":SS", addSpace);
2166}
double max(void)
#define setSDTime(fd, x, y, z)
#define setLocalTime(fd, x, y, z)
double ra
double dec
#define min(a, b)
#define MAXINDIDEVICE
Definition indiapi.h:191
void getSexComponentsIID(double value, int *d, int *m, double *s)
Definition indicom.c:266
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition indicom.c:194
void getSexComponents(double value, int *d, int *m, int *s)
Definition indicom.c:243
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
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:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
#define DEBUGDEVICE(device, priority, msg)
Definition indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition indilogger.h:61
int fd
int setFocuserMotion(int fd, int motionType)
int setAlignmentMode(int fd, unsigned int alignMode)
char lx200Name[MAXINDIDEVICE]
int setSlewMode(int fd, int slewMode)
int setObjAlt(int fd, double alt)
int setMinElevationLimit(int fd, int min)
std::mutex lx200CommsLock
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
int selectTrackingMode(int fd, int trackMode)
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 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
int abortSlew(int fd)
int setSiteLatitude(int fd, double Lat, bool addSpace)
int getCommandSexa(int fd, double *value, const char *cmd)
int Sync(int fd, char *matchedObject)
int setTrackFreq(int fd, double trackF)
int eq_format
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
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)
int setUTCOffset(int fd, double hours)
int check_lx200_connection(int in_fd)
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
@ 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
@ LX200_TRACK_SOLAR
@ LX200_TRACK_LUNAR
@ LX200_TRACK_SIDEREAL
@ 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
@ 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
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define ACK
Definition stvdriver.h:35