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