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