Instrument Neutral Distributed Interface INDI 2.2.5
ieqdriverbase.cpp
Go to the documentation of this file.
1/*
2 IEQ Pro 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 "ieqdriverbase.h"
22
23#include "indicom.h"
24#include "indilogger.h"
25
26#include <libnova/julian_day.h>
27
28#include <cctype>
29#include <cmath>
30#include <map>
31#include <cstring>
32#include <algorithm>
33#include <termios.h>
34#include <unistd.h>
35
36namespace iEQ
37{
38
40{
41}
42
44{
45 m_PortFD = fd;
46 bool rc = getModel();
47 if (rc)
48 {
50 if (rc)
51 {
52 for (const auto &oneMount : m_MountList)
53 {
54 if (oneMount.model == m_FirmwareInfo.Model)
55 {
56 // Make sure current mount firmware is larger
57 // than the minimum firmware required by this mount model.
58 if (m_FirmwareInfo.MainBoardFirmware >= oneMount.firmware)
59 return true;
60 else
61 LOGF_ERROR("Main board firmware is %s while minimum required firmware is %s. Please upgrade the mount firmware.",
62 m_FirmwareInfo.MainBoardFirmware.c_str(), oneMount.firmware.c_str());
63 }
64 }
65
66 return false;
67 }
68 }
69
70 return rc;
71}
72
74{
75 char res[DRIVER_LEN] = {0};
76
77 // Do we support this mount?
78 if (sendCommand(":MountInfo#", res, -1, 4))
79 {
80 std::string code = res;
81 auto result = std::find_if(m_MountList.begin(), m_MountList.end(), [code](const MountInfo & oneMount)
82 {
83 return oneMount.code == code;
84 });
85
86 if (result != m_MountList.end())
87 {
88 m_FirmwareInfo.Model = result->model;
89 return true;
90 }
91
92 LOGF_ERROR("Mount with code %s is not recognized.", res);
93 return false;
94 }
95
96 return false;
97}
98
100{
101 char res[DRIVER_LEN] = {0};
102
103 if (sendCommand(":FW1#", res))
104 {
105 char board[8] = {0}, controller[8] = {0};
106
107 snprintf(board, sizeof(board), "%.6s", res);
108 snprintf(controller, sizeof(controller), "%.6s", res + 6);
109
110 m_FirmwareInfo.MainBoardFirmware.assign(board, 6);
111 m_FirmwareInfo.ControllerFirmware.assign(controller, 6);
112
113 return true;
114 }
115
116 return false;
117}
118
120{
121 char res[DRIVER_LEN] = {0};
122
123 if (sendCommand(":FW2#", res))
124 {
125 char ra[8] = {0}, de[8] = {0};
126
127 snprintf(ra, sizeof(ra), "%.6s", res);
128 snprintf(de, sizeof(de), "%.6s", res + 6);
129
130 m_FirmwareInfo.RAFirmware.assign(ra, 6);
131 m_FirmwareInfo.DEFirmware.assign(de, 6);
132
133 return true;
134 }
135
136 return false;
137}
138
140{
141 char cmd[DRIVER_LEN] = {0};
142 switch (dir)
143 {
144 case IEQ_N:
145 strcpy(cmd, ":mn#");
146 break;
147 case IEQ_S:
148 strcpy(cmd, ":ms#");
149 break;
150 // case IEQ_W:
151 // strcpy(cmd, ":mw#");
152 // break;
153 // case IEQ_E:
154 // strcpy(cmd, ":me#");
155 // break;
156 // JM 2019-01-17: Appears iOptron implementation is reversed?
157 case IEQ_W:
158 strcpy(cmd, ":me#");
159 break;
160 case IEQ_E:
161 strcpy(cmd, ":mw#");
162 break;
163 }
164
165 return sendCommand(cmd);
166}
167
169{
170 char cmd[DRIVER_LEN] = {0};
171 char res[DRIVER_LEN] = {0};
172
173 switch (dir)
174 {
175 case IEQ_N:
176 case IEQ_S:
177 strcpy(cmd, ":qD#");
178 break;
179
180 case IEQ_W:
181 case IEQ_E:
182 strcpy(cmd, ":qR#");
183 break;
184 }
185
186 return sendCommand(cmd, res, -1, 1);
187}
188
190{
191 if (!isCommandSupported("MSH"))
192 return false;
193
194 char res[DRIVER_LEN] = {0};
195
196 return sendCommand(":MSH#", res, -1, 1);
197}
198
200{
201 char res[DRIVER_LEN] = {0};
202 return sendCommand(":MH#", res, -1, 1);
203}
204
206{
207 char res[DRIVER_LEN] = {0};
208 return sendCommand(":SZP#", res, -1, 1);
209}
210
212{
213 char cmd[DRIVER_LEN] = {0};
214 char res[DRIVER_LEN] = {0};
215 snprintf(cmd, DRIVER_LEN, ":SR%d#", (static_cast<int>(rate) + 1));
216 return sendCommand(cmd, res, -1, 1);
217}
218
220{
221 char cmd[DRIVER_LEN] = {0};
222 char res[DRIVER_LEN] = {0};
223
224 switch (rate)
225 {
226 case TR_SIDEREAL:
227 strcpy(cmd, ":RT0#");
228 break;
229 case TR_LUNAR:
230 strcpy(cmd, ":RT1#");
231 break;
232 case TR_SOLAR:
233 strcpy(cmd, ":RT2#");
234 break;
235 case TR_KING:
236 strcpy(cmd, ":RT3#");
237 break;
238 case TR_CUSTOM:
239 strcpy(cmd, ":RT4#");
240 break;
241 }
242
243 return sendCommand(cmd, res, -1, 1);
244}
245
247{
248 if (!isCommandSupported("RR"))
249 return false;
250
251 // Limit to 0.5 to 1.5 as per docs
252 rate = std::max(0.5, std::min(rate, 1.5));
253
254 char cmd[DRIVER_LEN] = {0};
255 char res[DRIVER_LEN] = {0};
256 // Need to be in n.nnnn * sidereal_rate format.
257 // e.g. 0.5 * 1e5 ==> 50000
258 snprintf(cmd, DRIVER_LEN, ":RR%05d#", static_cast<int>(rate * 1e5));
259
260 return sendCommand(cmd, res, -1, 1);
261}
262
263bool Base::setGuideRate(double raRate, double deRate)
264{
265 if (!isCommandSupported("RG"))
266 return false;
267
268 // Limit to 0.01 to 0.90 as per docs
269 raRate = std::max(0.01, std::min(raRate, 0.9));
270 // Limit to 0.10 to 0.99 as per docs
271 deRate = std::max(0.1, std::min(deRate, 0.99));
272
273 char cmd[DRIVER_LEN] = {0};
274 char res[DRIVER_LEN] = {0};
275 snprintf(cmd, DRIVER_LEN, ":RG%02d%02d#", static_cast<int>(raRate * 100.0), static_cast<int>(deRate * 100.0));
276
277 return sendCommand(cmd, res, -1, 1);
278}
279
280bool Base::getGuideRate(double *raRate, double *deRate)
281{
282 if (!isCommandSupported("AG"))
283 return false;
284
285 char res[DRIVER_LEN] = {0};
286
287 if (sendCommand(":AG#", res))
288 {
289 *raRate = DecodeString(res, 2, 100.0);
290 *deRate = DecodeString(res + 2, 2, 100.0);
291
292 return true;
293 }
294
295 return false;
296}
297
298bool Base::startGuide(Direction dir, uint32_t ms)
299{
300 char cmd[DRIVER_LEN] = {0};
301 char dir_c = 0;
302
303 switch (dir)
304 {
305 case IEQ_N:
306 dir_c = 'n';
307 break;
308
309 case IEQ_S:
310 dir_c = 's';
311 break;
312
313 case IEQ_W:
314 dir_c = 'w';
315 break;
316
317 case IEQ_E:
318 dir_c = 'e';
319 break;
320 }
321
322 snprintf(cmd, DRIVER_LEN, ":M%c%05ud#", dir_c, ms);
323
324 return sendCommand(cmd);
325}
326
328{
329 if (!isCommandSupported("MP1"))
330 return false;
331
332 char res[DRIVER_LEN] = {0};
333
334 if (sendCommand(":MP1#", res, -1, 1))
335 {
336 return res[0] == '1';
337 }
338
339 return false;
340}
341
343{
344 if (!isCommandSupported("MP0"))
345 return false;
346
347 char res[DRIVER_LEN] = {0};
348 return sendCommand(":MP0#", res, -1, 1);
349}
350
352{
353 char res[DRIVER_LEN] = {0};
354 return sendCommand(":Q#", res, -1, 1);
355}
356
358{
359 char res[DRIVER_LEN] = {0};
360
361 if (sendCommand(":MS#", res, -1, 1))
362 {
363 return res[0] == '1';
364 }
365
366 return false;
367}
368
370{
371 char res[DRIVER_LEN] = {0};
372 return sendCommand(":CM#", res, -1, 1);
373}
374
375bool Base::setTrackEnabled(bool enabled)
376{
377 char res[DRIVER_LEN] = {0};
378 return sendCommand(enabled ? ":ST1#" : ":ST0#", res, -1, 1);
379}
380
381bool Base::setRA(double ra)
382{
383 char cmd[DRIVER_LEN] = {0};
384 char res[DRIVER_LEN] = {0};
385
386 // Send as milliseconds resolution
387 int ieqValue = static_cast<int>(ra * 60 * 60 * 1000);
388
389 snprintf(cmd, DRIVER_LEN, ":Sr%08d#", ieqValue);
390
391 return sendCommand(cmd, res, -1, 1);
392}
393
394bool Base::setDE(double dec)
395{
396 char cmd[DRIVER_LEN] = {0};
397 char res[DRIVER_LEN] = {0};
398
399 // Send as 0.01 arcseconds resolution
400 int ieqValue = static_cast<int>(fabs(dec) * 60 * 60 * 100);
401
402 snprintf(cmd, DRIVER_LEN, ":Sd%c%08d#", (dec >= 0) ? '+' : '-', ieqValue);
403
404 return sendCommand(cmd, res, -1, 1);
405}
406
407bool Base::setAz(double az)
408{
409 char cmd[DRIVER_LEN] = {0};
410 char res[DRIVER_LEN] = {0};
411
412 // Send as 0.01 arcsec resolution
413 int ieqValue = static_cast<int>(az * 60 * 60 * 100);
414
415 snprintf(cmd, DRIVER_LEN, ":Sz%09d#", ieqValue);
416
417 return sendCommand(cmd, res, -1, 1);
418}
419
420bool Base::setAlt(double alt)
421{
422 char cmd[DRIVER_LEN] = {0};
423 char res[DRIVER_LEN] = {0};
424
425 // Send as 0.01 arcsec resolution
426 int ieqValue = static_cast<int>(alt * 60 * 60 * 100);
427
428 snprintf(cmd, DRIVER_LEN, ":Sa%c%08d#", (alt >= 0) ? '+' : '-', ieqValue);
429
430 return sendCommand(cmd, res, -1, 1);
431}
432
433bool Base::setParkAz(double az)
434{
435 if (!isCommandSupported("SPA"))
436 return false;
437
438 char cmd[DRIVER_LEN] = {0};
439 char res[DRIVER_LEN] = {0};
440
441 // Send as 0.01 arcsec resolution
442 int ieqValue = static_cast<int>(az * 60 * 60 * 100);
443
444 snprintf(cmd, DRIVER_LEN, ":SPA%09d#", ieqValue);
445
446 return sendCommand(cmd, res, -1, 1);
447}
448
449bool Base::setParkAlt(double alt)
450{
451 if (!isCommandSupported("SPH"))
452 return false;
453
454 char cmd[DRIVER_LEN] = {0};
455 char res[DRIVER_LEN] = {0};
456
457 alt = std::max(0.0, alt);
458
459 // Send as 0.01 arcsec resolution
460 int ieqValue = static_cast<int>(alt * 60 * 60 * 100);
461
462 snprintf(cmd, DRIVER_LEN, ":SPH%08d#", ieqValue);
463
464 return sendCommand(cmd, res, -1, 1);
465}
466
467bool Base::setLongitude(double longitude)
468{
469 char cmd[DRIVER_LEN] = {0};
470 char res[DRIVER_LEN] = {0};
471
472 int arcsecs = static_cast<int>(fabs(longitude) * 60 * 60);
473 snprintf(cmd, DRIVER_LEN, ":Sg%c%06d#", (longitude >= 0) ? '+' : '-', arcsecs);
474
475 return sendCommand(cmd, res, -1, 1);
476}
477
478bool Base::setLatitude(double latitude)
479{
480 char cmd[DRIVER_LEN] = {0};
481 char res[DRIVER_LEN] = {0};
482
483 int arcsecs = static_cast<int>(fabs(latitude) * 60 * 60);
484 snprintf(cmd, DRIVER_LEN, ":St%c%06d#", (latitude >= 0) ? '+' : '-', arcsecs);
485
486 return sendCommand(cmd, res, -1, 1);
487}
488
489bool Base::setLocalDate(int yy, int mm, int dd)
490{
491 char cmd[DRIVER_LEN] = {0};
492 char res[DRIVER_LEN] = {0};
493
494 snprintf(cmd, DRIVER_LEN, ":SC%02d%02d%02d#", yy, mm, dd);
495
496 return sendCommand(cmd, res, -1, 1);
497}
498
499bool Base::setLocalTime(int hh, int mm, int ss)
500{
501 char cmd[DRIVER_LEN] = {0};
502 char res[DRIVER_LEN] = {0};
503
504 snprintf(cmd, DRIVER_LEN, ":SL%02d%02d%02d#", hh, mm, ss);
505
506 return sendCommand(cmd, res, -1, 1);
507}
508
509bool Base::setDST(bool enabled)
510{
511 char res[DRIVER_LEN] = {0};
512 return sendCommand(enabled ? ":SDS1#" : ":SDS0#", res, -1, 1);
513}
514
515bool Base::setUTCOffset(double offset_hours)
516{
517 char cmd[DRIVER_LEN] = {0};
518 char res[DRIVER_LEN] = {0};
519
520 int offset_minutes = static_cast<int>(fabs(offset_hours) * 60.0);
521 snprintf(cmd, 16, ":SG%c%03d#", (offset_hours >= 0) ? '+' : '-', offset_minutes);
522
523 return sendCommand(cmd, res, -1, 1);
524}
525
526bool Base::getCoords(double *ra, double *dec)
527{
528 char res[DRIVER_LEN] = {0};
529
530 if (sendCommand(":GEC#", res))
531 {
532 // Response format: sTTTTTTTTXXXXXXXX (17 chars)
533 // s = sign, TTTTTTTT = DEC (8 digits), XXXXXXXX = RA (8 digits)
534 // Valid DEC range: [-32,400,000, +32,400,000]
535 // Valid RA range: [0, 86,400,000]
536 static constexpr size_t EXPECTED_LEN = 17;
537 static constexpr int32_t DEC_MIN = -32400000;
538 static constexpr int32_t DEC_MAX = 32400000;
539 static constexpr int32_t RA_MIN = 0;
540 static constexpr int32_t RA_MAX = 86400000;
541
542 // Check response length
543 size_t resLen = strlen(res);
544 if (resLen < EXPECTED_LEN)
545 {
546 LOGF_ERROR("Invalid GEC response: expected %zu chars, got %zu ('%s')",
547 EXPECTED_LEN, resLen, res);
548 return false;
549 }
550
551 // Validate sign character for DEC
552 if (res[0] != '+' && res[0] != '-')
553 {
554 LOGF_ERROR("Invalid GEC response: expected sign at position 0, got '%c' ('%s')",
555 res[0], res);
556 return false;
557 }
558
559 // Validate DEC digits (positions 1-8)
560 for (size_t i = 1; i < 9; i++)
561 {
562 if (!isdigit(static_cast<unsigned char>(res[i])))
563 {
564 LOGF_ERROR("Invalid GEC response: non-digit at position %zu ('%s')", i, res);
565 return false;
566 }
567 }
568
569 // Validate RA digits (positions 9-16)
570 for (size_t i = 9; i < 17; i++)
571 {
572 if (!isdigit(static_cast<unsigned char>(res[i])))
573 {
574 LOGF_ERROR("Invalid GEC response: non-digit at position %zu ('%s')", i, res);
575 return false;
576 }
577 }
578
579 // Decode and validate ranges
580 int32_t rawDec = DecodeString(res, 9);
581 int32_t rawRa = DecodeString(res + 9, 8);
582
583 if (rawDec < DEC_MIN || rawDec > DEC_MAX)
584 {
585 LOGF_ERROR("Invalid GEC response: DEC %d out of range [%d, %d]",
586 rawDec, DEC_MIN, DEC_MAX);
587 return false;
588 }
589
590 if (rawRa < RA_MIN || rawRa > RA_MAX)
591 {
592 LOGF_ERROR("Invalid GEC response: RA %d out of range [%d, %d]",
593 rawRa, RA_MIN, RA_MAX);
594 return false;
595 }
596
597 *ra = Ra = rawRa / ieqHours;
598 *dec = Dec = rawDec / ieqDegrees;
599 return true;
600 }
601
602 return false;
603}
604
605bool Base::getUTCDateTime(double *utc_hours, int *yy, int *mm, int *dd, int *hh, int *minute, int *ss)
606{
607 char res[DRIVER_LEN] = {0};
608
609 if (sendCommand(":GLT#", res))
610 {
611 *utc_hours = DecodeString(res, 4, 60.0);
612 *yy = DecodeString(res + 5, 2) + 2000;
613 *mm = DecodeString(res + 7, 2);
614 *dd = DecodeString(res + 9, 2);
615 *hh = DecodeString(res + 11, 2);
616 *minute = DecodeString(res + 13, 2);
617 *ss = DecodeString(res + 15, 2);
618
619 ln_zonedate localTime;
620 ln_date utcTime;
621
622 localTime.years = *yy;
623 localTime.months = *mm;
624 localTime.days = *dd;
625 localTime.hours = *hh;
626 localTime.minutes = *minute;
627 localTime.seconds = *ss;
628 localTime.gmtoff = static_cast<long>(*utc_hours * 3600);
629
630 ln_zonedate_to_date(&localTime, &utcTime);
631
632 *yy = utcTime.years;
633 *mm = utcTime.months;
634 *dd = utcTime.days;
635 *hh = utcTime.hours;
636 *minute = utcTime.minutes;
637 *ss = static_cast<int>(utcTime.seconds);
638
639 return true;
640
641 }
642
643 return false;
644}
645
647{
648 char res[DRIVER_LEN] = {0};
649
650 if (sendCommand(":GLS#", res))
651 {
652 char longitude[8] = {0}, latitude[8] = {0}, status[8] = {0};
653
654 snprintf(longitude, sizeof(longitude), "%.7s", res);
655 snprintf(latitude, sizeof(latitude), "%.6s", res + 7);
656 snprintf(status, sizeof(status), "%.6s", res + 13);
657
658 info->longitude = DecodeString(res, 7, 3600.0);
659 info->latitude = DecodeString(res + 7, 6, 3600.0) - 90;
660 info->gpsStatus = static_cast<GPSStatus>(status[0] - '0');
661 info->systemStatus = static_cast<SystemStatus>(status[1] - '0');
662 info->trackRate = static_cast<TrackRate>(status[2] - '0');
663 info->slewRate = static_cast<SlewRate>(status[3] - '0' - 1);
664 info->timeSource = static_cast<TimeSource>(status[4] - '0');
665 info->hemisphere = static_cast<Hemisphere>(status[5] - '0');
666
667 this->info = *info; // keep a local copy
668
669 return true;
670 }
671
672 return false;
673}
674
676{
677 switch (ps)
678 {
679 case IEQ_PIER_EAST:
680 return "EAST";
681 case IEQ_PIER_WEST:
682 return "WEST";
683 case IEQ_PIER_UNKNOWN:
684 return "UNKNOWN";
686 return "UNCERTAIN";
687 }
688 return "Impossible";
689}
690
692{
693 char res[DRIVER_LEN] = {0};
694
695 // use the GEA command, hoping that it returns the dec and polar angle axis positions
696 // see https://www.indilib.org/forum/mounts/6720-ioptron-cem60-question.html#52154
697 // the polar angle is in units of 1/100 arc second, signed, and the hour angle is in milliseconds,
698 // possibly with an offset. The home axis positions are PA +0.0, HA 12.0.
699 // For the West pointing state the ha = 18 - haAxis.
700 //
701 if (sendCommand(":GEA#", res))
702 {
703 // get the hour angle in hours
704 haAxis = DecodeString(res + 9, 8, ieqHours);
705 // this is the pole angle in degrees
706 decAxis = DecodeString(res, 9, ieqDegrees);
707
708 double axisHa = 0;
709
710 if (decAxis >= 0)
711 {
712 *pierSide = IEQ_PIER_WEST;
713 axisHa = 18 - haAxis; // OK for the West PS
714 }
715 else
716 {
717 *pierSide = IEQ_PIER_EAST;
718 axisHa = haAxis - 6; // OK for the West PS
719 }
720
721 // The pole angle is not exactly at 0 when the dec is 90 and this gives problems with incorrect pier side close to the pole.
722 //
723 // Attempt to handle this by using the hour angle where the HA can be relied on - away from the meridian
724 // Use pole angle when within 2 hours of a meridian.
725 // If the pole angle is less than the difference between the pole angle and the dec report the pier side as unknown
726 //
727 // I know, horrible, but the data the mount reports is so difficult to interpret that this seems to be the least
728 // worst solution, anyway, let's see if it works CR
729
730 double lst = get_local_sidereal_time(info.longitude);
731 double ha = rangeHA(get_local_hour_angle(lst, Ra));
732
733 const char* reason;
734 double decPA = info.latitude >= 0 ? 90 - Dec : 90 +
735 Dec; // the distance from the pole determined using the declination, ok for both hemispheres
736
737 if ((ha > 2 && ha < 10) || (ha < -2 && ha > -10))
738 {
739 // use Ha to determine pier side
740 *pierSide = ha > 0 ? IEQ_PIER_EAST : IEQ_PIER_WEST;
741 reason = "Hour Angle";
742 }
743 else
744 {
745 double decDiff = std::fabs(decPA - std::fabs(decAxis)); // not sure about this in the Southern hemisphere
746 if (decPA > decDiff)
747 {
748 // use the pole angle
749 *pierSide = decAxis > 0 ? IEQ_PIER_WEST : IEQ_PIER_EAST;
750 reason = "pole angle";
751 }
752 else
753 {
754 *pierSide = IEQ_PIER_UNCERTAIN;
755 reason = "uncertain";
756 }
757 }
758
759 LOGF_DEBUG("getPierSide pole Axis %f, haAxis %f, axisHa %f, ha %f, decPa %f, %s pierSide %s", decAxis, haAxis, axisHa, ha,
760 decPA, reason,
761 pierSideStr(*pierSide));
762
763 return true;
764 }
765 *pierSide = IEQ_PIER_UNKNOWN;
766 return false;
767}
768
769bool Base::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
770{
771 int nbytes_written = 0, nbytes_read = 0, rc = -1;
772
773 tcflush(m_PortFD, TCIOFLUSH);
774
775 if (cmd_len > 0)
776 {
777 char hex_cmd[DRIVER_LEN * 3] = {0};
778 hexDump(hex_cmd, cmd, cmd_len);
779 LOGF_DEBUG("CMD <%s>", hex_cmd);
780 rc = tty_write(m_PortFD, cmd, cmd_len, &nbytes_written);
781 }
782 else
783 {
784 LOGF_DEBUG("CMD <%s>", cmd);
785 rc = tty_write_string(m_PortFD, cmd, &nbytes_written);
786 }
787
788 if (rc != TTY_OK)
789 {
790 char errstr[MAXRBUF] = {0};
791 tty_error_msg(rc, errstr, MAXRBUF);
792 LOGF_ERROR("Serial write error: %s.", errstr);
793 return false;
794 }
795
796 if (res == nullptr)
797 return true;
798
799 if (res_len > 0)
800 rc = tty_read(m_PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
801 else
803
804 if (rc != TTY_OK)
805 {
806 char errstr[MAXRBUF] = {0};
807 tty_error_msg(rc, errstr, MAXRBUF);
808 LOGF_ERROR("Serial read error: %s.", errstr);
809 return false;
810 }
811
812 if (res_len > 0)
813 {
814 char hex_res[DRIVER_LEN * 3] = {0};
815 hexDump(hex_res, res, res_len);
816 LOGF_DEBUG("RES <%s>", hex_res);
817 }
818 else
819 {
820 LOGF_DEBUG("RES <%s>", res);
821 }
822
823 tcflush(m_PortFD, TCIOFLUSH);
824
825 return true;
826}
827
828void Base::hexDump(char * buf, const char * data, int size)
829{
830 for (int i = 0; i < size; i++)
831 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
832
833 if (size > 0)
834 buf[3 * size - 1] = '\0';
835}
836
837bool Base::isCommandSupported(const std::string &command, bool silent)
838{
839 // Find Home
840 if (command == "MSH")
841 {
842 if (m_FirmwareInfo.Model.find("CEM60") == std::string::npos &&
843 m_FirmwareInfo.Model.find("CEM40") == std::string::npos &&
844 m_FirmwareInfo.Model.find("GEM45") == std::string::npos)
845 {
846 if (!silent)
847 LOG_ERROR("Finding home is only supported on CEM40, GEM45 and CEM60 mounts.");
848 return false;
849
850 }
851 }
852 else if (command == "RR")
853 {
854 if (m_FirmwareInfo.Model.find("AA") != std::string::npos)
855 {
856 if (!silent)
857 LOG_ERROR("Tracking rate is not supported on Altitude-Azimuth mounts.");
858 return false;
859 }
860 }
861 else if (command == "RG" || command == "AG")
862 {
863 if (m_FirmwareInfo.Model.find("AA") != std::string::npos)
864 {
865 if (!silent)
866 LOG_ERROR("Guide rate is not supported on Altitude-Azimuth mounts.");
867 return false;
868 }
869 }
870 if (command == "MP0" || command == "MP1" || command == "SPA" || command == "SPH")
871 {
872 if (m_FirmwareInfo.Model.find("CEM60") == std::string::npos &&
873 m_FirmwareInfo.Model.find("CEM40") == std::string::npos &&
874 m_FirmwareInfo.Model.find("GEM45") == std::string::npos &&
875 m_FirmwareInfo.Model.find("iEQ") == std::string::npos)
876 {
877 if (!silent)
878 LOG_ERROR("Parking only supported on CEM40, GEM45, CEM60, iEQPro 30 and iEQ Pro 45.");
879 return false;
880 }
881 }
882
883 return true;
884}
885
886double Base::DecodeString(const char * data, size_t size, double factor)
887{
888 return DecodeString(data, size) / factor;
889}
890
891int Base::DecodeString(const char *data, size_t size)
892{
893 char str[DRIVER_LEN / 2] = {0};
894 snprintf(str, sizeof(str), "%.*s", static_cast<int>(size), data);
895
896 int iVal = atoi(str);
897 return iVal;
898}
899
900}
static const uint8_t DRIVER_LEN
bool getRADEFirmware()
void hexDump(char *buf, const char *data, int size)
hexDump Helper function to print non-string commands to the logger so it is easier to debug
virtual bool setCurrentHome()
virtual bool setRA(double ra)
virtual bool setSlewRate(SlewRate rate)
bool getStatus(Info *info)
virtual bool slew()
FirmwareInfo m_FirmwareInfo
virtual bool setTrackEnabled(bool enabled)
bool initCommunication(int fd)
initCommunication Checks if communication with the mount is working
virtual bool setLocalDate(int yy, int mm, int dd)
virtual bool abort()
virtual bool park()
bool isCommandSupported(const std::string &command, bool silent=false)
isCommandSupported Check if specific iOptron command is supported for this mount model
virtual bool sync()
virtual bool setAlt(double alt)
bool getCoords(double *ra, double *dec)
virtual bool unpark()
bool getPierSide(IEQ_PIER_SIDE *pierSide)
double DecodeString(const char *data, size_t size, double factor)
DecodeString converts the string to a double by dividing by the factor.
virtual bool setParkAlt(double alt)
virtual bool setLatitude(double latitude)
virtual bool setParkAz(double az)
virtual bool getGuideRate(double *raRate, double *deRate)
bool getMainFirmware()
virtual bool setAz(double az)
static constexpr const double ieqDegrees
virtual bool setDE(double dec)
virtual bool setUTCOffset(double offset_hours)
virtual bool stopMotion(Direction dir)
virtual bool setGuideRate(double raRate, double deRate)
static const char DRIVER_STOP_CHAR
const std::vector< MountInfo > m_MountList
bool sendCommand(const char *cmd, char *res=nullptr, int cmd_len=-1, int res_len=-1)
sendCommand Send a string command to device.
virtual bool setDST(bool enabled)
static constexpr const double ieqHours
bool getUTCDateTime(double *utc_hours, int *yy, int *mm, int *dd, int *hh, int *minute, int *ss)
virtual bool setLongitude(double longitude)
virtual bool setCustomRATrackRate(double rate)
static const uint8_t DRIVER_TIMEOUT
virtual bool startGuide(Direction dir, uint32_t ms)
virtual bool gotoHome()
bool getModel()
virtual bool startMotion(Direction dir)
virtual bool setLocalTime(int hh, int mm, int ss)
virtual bool setTrackMode(TrackRate rate)
virtual bool findHome()
double ra
double dec
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition indicom.c:1301
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 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
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition indicom.c:1379
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
#define MAXRBUF
Definition indidriver.c:52
#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
int fd
@ TR_SIDEREAL
@ TR_SOLAR
@ TR_KING
@ TR_LUNAR
@ TR_CUSTOM
IEQ_PIER_SIDE
@ IEQ_PIER_EAST
@ IEQ_PIER_WEST
@ IEQ_PIER_UNCERTAIN
@ IEQ_PIER_UNKNOWN
const char * pierSideStr(IEQ_PIER_SIDE ps)
uint8_t cmd[4]
Definition pwc-ioctl.h:2
std::string MainBoardFirmware
std::string ControllerFirmware
TrackRate trackRate
SystemStatus systemStatus
TimeSource timeSource
Hemisphere hemisphere
GPSStatus gpsStatus