Instrument Neutral Distributed Interface INDI  2.0.2
ioptronv3driver.cpp
Go to the documentation of this file.
1 /*
2  INDI IOptron v3 Driver for firmware version 20171001 or later.
3 
4  Copyright (C) 2018 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 "ioptronv3driver.h"
22 #include "indicom.h"
23 
24 #include <libnova/julian_day.h>
25 #include <cinttypes>
26 
27 #include <cmath>
28 #include <cstring>
29 #include <termios.h>
30 #include <unistd.h>
31 
32 namespace IOPv3
33 {
34 
35 const std::map<std::string, std::string> Driver::models =
36 {
37  {"0010", "Cube II EQ"},
38  {"0011", "SmartEQ Pro+"},
39  {"0025", "CEM25"},
40  {"0026", "CEM26"},
41  {"0027", "CEM26-EC"},
42  {"0028", "GEM28"},
43  {"0029", "GEM28-EC"},
44  {"0030", "iEQ30 Pro"},
45  {"0040", "CEM40"},
46  {"0041", "CEM40-EC"},
47  {"0043", "GEM45"},
48  {"0045", "iEQ45 Pro EQ"},
49  {"0046", "iEQ45 Pro AA"},
50  {"0060", "CEM60"},
51  {"0061", "CEM60-EC"},
52  {"0070", "CEM70"},
53  {"0071", "CEM70-EC"},
54  {"0120", "CEM120"},
55  {"0121", "CEM120-EC"},
56  {"0122", "CEM120-EC2"},
57  {"5010", "Cube II AA"},
58  {"5035", "AZ Mount Pro"},
59  {"5045", "iEQ45 Pro AA"}
60 };
61 
62 const uint16_t Driver::IOP_SLEW_RATES[] = {1, 2, 8, 16, 64, 128, 256, 512, 1024};
63 
64 Driver::Driver(const char *deviceName): m_DeviceName(deviceName) {}
65 
66 bool Driver::sendCommandOk(const char *command)
67 {
68  char res[IOP_BUFFER] = {0};
69 
70  if (sendCommand(command, 1, res))
71  return res[0] == '1';
72 
73  return false;
74 }
75 
76 bool Driver::sendCommand(const char *command, int count, char *response, uint8_t timeout, uint8_t debugLog)
77 {
78  int errCode = 0;
79  int nbytes_read = 0;
80  int nbytes_written = 0;
81  char errMsg[MAXRBUF];
82  char res[IOP_BUFFER] = {0};
83 
84  DEBUGFDEVICE(m_DeviceName, debugLog, "CMD <%s>", command);
85 
86  if (m_Simulation)
87  return true;
88 
89  // Try to dispatch command and read twice in case of timeouts.
90  for (int i = 0; i < 2; i++)
91  {
92  tcflush(PortFD, TCIOFLUSH);
93 
94  if ((errCode = tty_write(PortFD, command, strlen(command), &nbytes_written)) != TTY_OK)
95  {
96  tty_error_msg(errCode, errMsg, MAXRBUF);
97  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "Write Command Error: %s", errMsg);
98  return false;
99  }
100 
101  if (count == 0)
102  return true;
103 
104  if (count == -1)
105  errCode = tty_read_section(PortFD, res, '#', timeout, &nbytes_read);
106  else
107  errCode = tty_read(PortFD, res, count, timeout, &nbytes_read);
108 
109  if (errCode == TTY_OK)
110  break;
111  }
112 
113  if (errCode != TTY_OK)
114  {
115  tty_error_msg(errCode, errMsg, MAXRBUF);
116  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "Read Command Error: %s", errMsg);
117  return false;
118  }
119 
120  // Remove the extra #
121  if (count == -1)
122  res[nbytes_read - 1] = 0;
123 
124  DEBUGFDEVICE(m_DeviceName, debugLog, "RES <%s>", res);
125 
126  tcflush(PortFD, TCIOFLUSH);
127 
128  // Copy response to buffer
129  if (response)
130  strncpy(response, res, IOP_BUFFER);
131 
132  return true;
133 }
134 
136 {
137  char res[IOP_BUFFER] = {0};
138 
139  DEBUGDEVICE(m_DeviceName, INDI::Logger::DBG_DEBUG, "Initializing IOptron using :MountInfo# CMD...");
140 
141  // Set FD for use
142  PortFD = fd;
143 
144  if (m_Simulation)
145  return true;
146 
147  for (int i = 0; i < 2; i++)
148  {
149  if (sendCommand(":MountInfo#", 4, res, 3) == false)
150  {
151  usleep(50000);
152  continue;
153  }
154 
155  return true;
156  }
157 
158  return false;
159 }
160 
161 void Driver::setDebug(bool enable)
162 {
163  m_Debug = enable;
164 }
165 
166 void Driver::setSimulation(bool enable)
167 {
168  m_Simulation = enable;
169 
170  simData.ra_guide_rate = 0.5;
171  simData.de_guide_rate = 0.5;
172  simData.pier_state = IOP_PIER_WEST;
173  simData.cw_state = IOP_CW_NORMAL;
174  simData.JD = ln_get_julian_from_sys();
175  simData.utc_offset_minutes = 3 * 60;
176  simData.day_light_saving = false;
177  simData.mb_state = IOP_MB_FLIP;
178  simData.mb_limit = 3;
179 
180  simData.simInfo.gpsStatus = GPS_DATA_OK;
181  simData.simInfo.hemisphere = HEMI_NORTH;
182  simData.simInfo.slewRate = SR_6;
183  simData.simInfo.timeSource = TS_GPS;
184  simData.simInfo.trackRate = TR_SIDEREAL;
185  simData.simInfo.longitude = 48.1;
186  simData.simInfo.latitude = 29.5;
187 }
188 
190 {
191  simData.simInfo.gpsStatus = value;
192 }
193 
195 {
196  simData.simInfo.systemStatus = value;
197 }
198 
200 {
201  simData.simInfo.trackRate = value;
202 }
203 
205 {
206  simData.simInfo.slewRate = value;
207 }
208 
210 {
211  simData.simInfo.timeSource = value;
212 }
213 
215 {
216  simData.simInfo.hemisphere = value;
217 }
218 
219 void Driver::setSimRA(double ra)
220 {
221  simData.ra = ra;
222 }
223 
224 void Driver::setSimDE(double de)
225 {
226  simData.de = de;
227 }
228 
229 void Driver::setSimGuideRate(double raRate, double deRate)
230 {
231  simData.ra_guide_rate = raRate;
232  simData.de_guide_rate = deRate;
233 }
234 
235 void Driver::setSimLongLat(double longitude, double latitude)
236 {
237  simData.simInfo.longitude = longitude;
238  simData.simInfo.latitude = latitude;
239 }
240 
242 {
243  char res[IOP_BUFFER] = {0};
244 
245  if (m_Simulation)
246  {
247  int iopLongitude = simData.simInfo.longitude * 360000;
248  int iopLatitude = (simData.simInfo.latitude + 90) * 360000;
249  snprintf(res, IOP_BUFFER, "%c%08d%08d%d%d%d%d%d%d", simData.simInfo.longitude > 0 ? '+' : '-',
250  iopLongitude, iopLatitude, simData.simInfo.gpsStatus, simData.simInfo.systemStatus, simData.simInfo.trackRate,
251  simData.simInfo.slewRate, simData.simInfo.timeSource, simData.simInfo.hemisphere);
252  }
253  else if (sendCommand(":GLS#", -1, res) == false)
254  return false;
255 
256 
257  if (strlen(res) != 23)
258  {
259  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "%s: Expected 23 bytes but received %d.", __PRETTY_FUNCTION__,
260  strlen(res));
261  return false;
262  }
263 
264  char longPart[16] = {0}, latPart[16] = {0};
265  strncpy(longPart, res, 9);
266  strncpy(latPart, res + 9, 8);
267 
268  int arcsecLongitude = atoi(longPart);
269  int arcsecLatitude = atoi(latPart);
270 
271  info->longitude = arcsecLongitude / 360000.0;
272  info->latitude = arcsecLatitude / 360000.0 - 90.0;
273  info->gpsStatus = static_cast<IOP_GPS_STATUS>(res[17] - '0');
274  info->systemStatus = static_cast<IOP_SYSTEM_STATUS>(res[18] - '0');
275  info->trackRate = static_cast<IOP_TRACK_RATE>(res[19] - '0');
276  info->slewRate = static_cast<IOP_SLEW_RATE>(res[20] - '0');
277  info->timeSource = static_cast<IOP_TIME_SOURCE>(res[21] - '0');
278  info->hemisphere = static_cast<IOP_HEMISPHERE>(res[22] - '0');
279 
280  return true;
281 }
282 
284 {
285  bool rc1 = getModel(info->Model);
286 
287  bool rc2 = getMainFirmware(info->MainBoardFirmware, info->ControllerFirmware);
288 
289  bool rc3 = getRADEFirmware(info->RAFirmware, info->DEFirmware);
290 
291  return (rc1 && rc2 && rc3);
292 }
293 
294 bool Driver::getModel(std::string &model)
295 {
296  char res[IOP_BUFFER] = {0};
297 
298  if (m_Simulation)
299  strcpy(res, "0120");
300  else if (sendCommand(":MountInfo#", 4, res) == false)
301  return false;
302 
303  if (models.find(res) != models.end())
304  model = models.at(res);
305  else
306  model = "Unknown";
307 
308  return true;
309 }
310 
311 bool Driver::getMainFirmware(std::string &mainFirmware, std::string &controllerFirmware)
312 {
313  char res[IOP_BUFFER] = {0};
314 
315  if (m_Simulation)
316  strcpy(res, "180321171001");
317  else if (sendCommand(":FW1#", -1, res) == false)
318  return false;
319 
320  char mStr[16] = {0}, cStr[16] = {0};
321  strncpy(mStr, res, 6);
322  strncpy(cStr, res + 6, 6);
323 
324  mainFirmware = mStr;
325  controllerFirmware = cStr;
326 
327  return true;
328 }
329 
330 bool Driver::getRADEFirmware(std::string &RAFirmware, std::string &DEFirmware)
331 {
332  char res[IOP_BUFFER] = {0};
333 
334  if (m_Simulation)
335  strcpy(res, "140324140101");
336  else if (sendCommand(":FW2#", -1, res) == false)
337  return false;
338 
339  char mStr[16] = {0}, cStr[16] = {0};
340  strncpy(mStr, res, 6);
341  strncpy(cStr, res + 6, 6);
342 
343  RAFirmware = mStr;
344  DEFirmware = cStr;
345 
346  return true;
347 }
348 
350 {
351  switch (dir)
352  {
353  case IOP_N:
354  return sendCommand(":mn#", 0);
355  case IOP_S:
356  return sendCommand(":ms#", 0);
357  // JM 2020-10-12
358  // We are reversing this since CEM120 moves CW when commanded WEST
359  // leading to INCREASING RA, when it is expected to move CCW leading
360  // to DECREASING RA
361  case IOP_W:
362  return sendCommand(":me#", 0);
363  case IOP_E:
364  return sendCommand(":mw#", 0);
365  }
366 
367  return false;
368 }
369 
371 {
372  switch (dir)
373  {
374  case IOP_N:
375  case IOP_S:
376  return sendCommandOk(":qD#");
377 
378  case IOP_W:
379  case IOP_E:
380  return sendCommandOk(":qR#");
381  }
382 
383  return false;
384 }
385 
387 {
388  return sendCommandOk(":MSH#");
389 }
390 
392 {
393  return sendCommandOk(":MH#");
394 }
395 
397 {
398  return sendCommandOk(":SZP#");
399 }
400 
401 /* v3.0 Added in control for PEC , Train and Data Integrity */
402 bool Driver::setPECEnabled(bool enabled)
403 {
404  return sendCommandOk(enabled ? ":SPP1#" : ":SPP0#");
405 }
406 
407 bool Driver::setPETEnabled(bool enabled)
408 {
409  return sendCommandOk(enabled ? ":SPR1#" : ":SPR0#");
410 }
411 
412 bool Driver::getPETEnabled(bool enabled)
413 {
414  char res[IOP_BUFFER] = {0};
415  // If enabled true then check data quality -> :GPE#
416  // If enabled false then check if training -> :GPR#
417  if(enabled)
418  {
419  if (sendCommand(":GPE#", 1, res))
420  {
421  if (res[0] == '1')
422  {
423  return true;
424  }
425  }
426  }
427  else
428  {
429  if (sendCommand(":GPR#", 1, res))
430  {
431  if (res[0] == '1')
432  {
433  return true;
434  }
435  }
436  }
437  return false;
438 }
439 // End Mod */
440 
442 {
443  char cmd[IOP_BUFFER] = {0};
444  snprintf(cmd, IOP_BUFFER, ":SR%u#", static_cast<uint32_t>(rate + 1));
445 
446  simData.simInfo.slewRate = rate;
447 
448  return sendCommandOk(cmd);
449 }
450 
452 {
453  simData.simInfo.trackRate = rate;
454 
455  switch (rate)
456  {
457  case TR_SIDEREAL:
458  return sendCommandOk(":RT0#");
459  case TR_LUNAR:
460  return sendCommandOk(":RT1#");
461  case TR_SOLAR:
462  return sendCommandOk(":RT2#");
463  case TR_KING:
464  return sendCommandOk(":RT3#");
465  case TR_CUSTOM:
466  return sendCommandOk(":RT4#");
467  }
468 
469  return false;
470 }
471 
473 {
474  if (rate < 0.1 || rate > 1.9)
475  return false;
476 
477  char cmd[IOP_BUFFER] = {0};
478  snprintf(cmd, IOP_BUFFER, ":RR%05u#", static_cast<uint32_t>(rate * 10000));
479 
480  return sendCommandOk(cmd);
481 }
482 
483 bool Driver::setGuideRate(double RARate, double DERate)
484 {
485  if (RARate < 0.01 || RARate > 0.9 || DERate < 0.01 || DERate > 0.9)
486  return false;
487 
488  char cmd[IOP_BUFFER] = {0};
489  snprintf(cmd, IOP_BUFFER, ":RG%02u%02u#", static_cast<uint32_t>(RARate * 100), static_cast<uint32_t>(DERate * 100));
490 
491  return sendCommandOk(cmd);
492 }
493 
494 bool Driver::getGuideRate(double *RARate, double *DERate)
495 {
496  char res[IOP_BUFFER] = {0};
497 
498  if (m_Simulation)
499  snprintf(res, IOP_BUFFER, "%02u%02u", static_cast<uint32_t>(simData.ra_guide_rate * 100),
500  static_cast<uint32_t>(simData.de_guide_rate * 100));
501  else if (sendCommand(":AG#", -1, res) == false)
502  return false;
503 
504  char raStr[8] = {0}, deStr[8] = {0};
505  strncpy(raStr, res, 2);
506  strncpy(deStr, res + 2, 2);
507 
508  *RARate = atoi(raStr) / 100.0;
509  *DERate = atoi(deStr) / 100.0;
510 
511  return true;
512 }
513 
514 bool Driver::startGuide(IOP_DIRECTION dir, uint32_t ms)
515 {
516  char cmd[IOP_BUFFER] = {0};
517  char dir_c = 0;
518 
519  switch (dir)
520  {
521  // Dec+
522  case IOP_N:
523  dir_c = 'E';
524  break;
525 
526  // Dec-
527  case IOP_S:
528  dir_c = 'C';
529  break;
530 
531  // RA-
532  case IOP_W:
533  dir_c = 'Q';
534  break;
535 
536  // RA+
537  case IOP_E:
538  dir_c = 'S';
539  break;
540  }
541 
542  snprintf(cmd, IOP_BUFFER, ":Z%c%05u#", dir_c, ms);
543 
544  return sendCommand(cmd, 0);
545 }
546 
548 {
549  return sendCommandOk(":MP1#");
550 }
551 
553 {
554  //NB: This command only available in CEM120 series, CEM60 series, iEQ45 Pro, iEQ45 Pro
555  //AA and iEQ30 Pro.
557  return sendCommandOk(":MP0#");
558 }
559 
560 bool Driver::setParkAz(double az)
561 {
562  char cmd[IOP_BUFFER] = {0};
563 
564  // Send as 0.01 arcsec resolution
565  int ieqValue = static_cast<int>(az * 60 * 60 * 100);
566 
567  snprintf(cmd, IOP_BUFFER, ":SPA%09d#", ieqValue);
568 
569  return sendCommandOk(cmd);
570 }
571 
572 bool Driver::setParkAlt(double alt)
573 {
574  char cmd[IOP_BUFFER] = {0};
575 
576  alt = std::max(0.0, alt);
577 
578  // Send as 0.01 arcsec resolution
579  int ieqValue = static_cast<int>(alt * 60 * 60 * 100);
580  snprintf(cmd, IOP_BUFFER, ":SPH%08d#", ieqValue);
581  return sendCommandOk(cmd);
582 }
583 
585 {
586  if (simData.simInfo.systemStatus == ST_SLEWING)
587  simData.simInfo.systemStatus = simData.simInfo.rememberSystemStatus;
588 
589  return sendCommandOk(":Q#");
590 }
591 
593 {
594  simData.simInfo.rememberSystemStatus = simData.simInfo.systemStatus;
595  simData.simInfo.systemStatus = ST_SLEWING;
596 
597  return sendCommandOk(":MS1#");
598 }
599 
601 {
602  simData.simInfo.rememberSystemStatus = simData.simInfo.systemStatus;
603  simData.simInfo.systemStatus = ST_SLEWING;
604 
605  return sendCommandOk(":MS2#");
606 }
607 
609 {
610  return sendCommand(":CM#", 1);
611 }
612 
613 bool Driver::setTrackEnabled(bool enabled)
614 {
615  simData.simInfo.systemStatus = enabled ? ST_TRACKING_PEC_ON : ST_STOPPED;
616  char cmd[IOP_BUFFER] = {0};
617  snprintf(cmd, IOP_BUFFER, ":ST%d#", enabled ? 1 : 0);
618 
619  return sendCommand(cmd);
620 }
621 
622 bool Driver::setRA(double ra)
623 {
624  // Send RA in centi-arcsecond (0.01) resolution.
625  // ra is passed as hours. casRA is in centi-arcseconds in degrees.
626  uint32_t casRA = ra * 15 * 60 * 60 * 100;
627 
628  simData.ra = ra;
629 
630  char cmd[IOP_BUFFER] = {0};
631  snprintf(cmd, IOP_BUFFER, ":SRA%09u#", casRA);
632 
633  return sendCommandOk(cmd);
634 }
635 
636 bool Driver::setDE(double de)
637 {
638  // Send DE in centi-arcsecond (0.01) resolution.
639  // de is passed as degrees. casDE is in centi-arcseconds in degrees.
640  uint32_t casDE = fabs(de) * 60 * 60 * 100;
641 
642  simData.de = de;
643 
644  char cmd[IOP_BUFFER] = {0};
645  snprintf(cmd, IOP_BUFFER, ":Sd%c%08u#", de >= 0 ? '+' : '-', casDE);
646 
647  return sendCommandOk(cmd);
648 }
649 
650 bool Driver::setLongitude(double longitude)
651 {
652  uint32_t casLongitude = fabs(longitude) * 60 * 60 * 100;
653 
654  simData.simInfo.longitude = longitude;
655 
656  char cmd[IOP_BUFFER] = {0};
657  snprintf(cmd, IOP_BUFFER, ":SLO%c%08u#", longitude >= 0 ? '+' : '-', casLongitude);
658 
659  return sendCommandOk(cmd);
660 }
661 
662 bool Driver::setLatitude(double latitude)
663 {
664  uint32_t casLatitude = fabs(latitude) * 60 * 60 * 100;
665 
666  simData.simInfo.latitude = latitude;
667 
668  char cmd[IOP_BUFFER] = {0};
669  snprintf(cmd, IOP_BUFFER, ":SLA%c%08u#", latitude >= 0 ? '+' : '-', casLatitude);
670 
671  return sendCommandOk(cmd);
672 }
673 
674 bool Driver::setUTCDateTime(double JD)
675 {
676  uint64_t msJD = (JD - J2000) * 8.64e+7;
677 
678  char cmd[IOP_BUFFER] = {0};
679  snprintf(cmd, IOP_BUFFER, ":SUT%013" PRIu64 "#", msJD);
680 
681  simData.JD = JD;
682 
683  return sendCommandOk(cmd);
684 }
685 
686 bool Driver::setUTCOffset(int offsetMinutes)
687 {
688  char cmd[IOP_BUFFER] = {0};
689  snprintf(cmd, IOP_BUFFER, ":SG%c%03d#", offsetMinutes >= 0 ? '+' : '-', abs(offsetMinutes));
690 
691  simData.utc_offset_minutes = offsetMinutes;
692 
693  return sendCommandOk(cmd);
694 }
695 
696 bool Driver::setDaylightSaving(bool enabled)
697 {
698  char cmd[IOP_BUFFER] = {0};
699  snprintf(cmd, IOP_BUFFER, ":SDS%c#", enabled ? '1' : '0');
700 
701  simData.day_light_saving = enabled;
702 
703  return sendCommandOk(cmd);
704 }
705 
706 bool Driver::getCoords(double *ra, double *de, IOP_PIER_STATE *pierState, IOP_CW_STATE *cwState)
707 {
708  char res[IOP_BUFFER] = {0};
709  if (m_Simulation)
710  {
711  snprintf(res, IOP_BUFFER, "%c%08u%09u%d%d", (simData.de >= 0 ? '+' : '-'),
712  static_cast<uint32_t>(fabs(simData.de) * 60 * 60 * 100),
713  static_cast<uint32_t>(simData.ra * 15 * 60 * 60 * 100), simData.pier_state, simData.cw_state);
714  }
715  else if (sendCommand(":GEP#", -1, res, IOP_TIMEOUT, INDI::Logger::DBG_EXTRA_1) == false)
716  return false;
717 
718  if (strlen(res) != 20)
719  {
720  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "%s: Expected 20 bytes but received %d.", __PRETTY_FUNCTION__,
721  strlen(res));
722  return false;
723  }
724 
725  char deStr[16] = {0}, raStr[16] = {0};
726 
727  strncpy(deStr, res, 9);
728  strncpy(raStr, res + 9, 9);
729 
730  try
731  {
732  *de = std::atoi(deStr) / (60.0 * 60.0 * 100.0);
733  *ra = std::atoi(raStr) / (15.0 * 60.0 * 60.0 * 100.0);
734  }
735  catch(...)
736  {
737  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "Failed to parse coordinates RA: %s DE: %s", raStr, deStr);
738  return false;
739  }
740 
741  *pierState = static_cast<IOP_PIER_STATE>(res[18] - '0');
742  *cwState = static_cast<IOP_CW_STATE>(res[19] - '0');
743 
744  return true;
745 }
746 
747 bool Driver::getUTCDateTime(double *JD, int *utcOffsetMinutes, bool *dayLightSaving)
748 {
749  char res[IOP_BUFFER] = {0};
750  if (m_Simulation)
751  {
752  snprintf(res, IOP_BUFFER, "%c%03d%c%013" PRIu64, (simData.utc_offset_minutes >= 0 ? '+' : '-'),
753  abs(simData.utc_offset_minutes),
754  (simData.day_light_saving ? '1' : '0'), static_cast<uint64_t>((simData.JD - J2000) * 8.64e+7));
755  }
756  else if (sendCommand(":GUT#", -1, res) == false)
757  return false;
758 
759  if (strlen(res) != 18)
760  {
761  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "%s: Expected 18 bytes but received %d.", __PRETTY_FUNCTION__,
762  strlen(res));
763  return false;
764  }
765 
766  char offsetStr[16] = {0}, JDStr[16] = {0};
767 
768  strncpy(offsetStr, res, 4);
769  strncpy(JDStr, res + 5, 13);
770 
771  *utcOffsetMinutes = atoi(offsetStr);
772  *dayLightSaving = (res[4] == '1');
773 
774  try
775  {
776  uint64_t iopJD = std::stoull(JDStr);
777  *JD = (iopJD / 8.64e+7) + J2000;
778  }
779  catch(...)
780  {
781  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "Failed to parse JD String: %s", JDStr);
782  return false;
783  }
784 
785  return true;
786 }
787 
788 bool Driver::getMeridianBehavior(IOP_MB_STATE &action, uint8_t &degrees)
789 {
790  char res[IOP_BUFFER] = {0};
791  if (m_Simulation)
792  {
793  snprintf(res, IOP_BUFFER, "%d%02d", simData.mb_state, simData.mb_limit);
794  }
795  else if (sendCommand(":GMT#", -1, res) == false)
796  return false;
797 
798  try
799  {
800  action = static_cast<IOP_MB_STATE>(res[0] - '0');
801  degrees = std::stoi(res + 1);
802  }
803  catch(...)
804  {
805  DEBUGFDEVICE(m_DeviceName, INDI::Logger::DBG_ERROR, "Failed to parse MF Behavior: %s", res);
806  return false;
807  }
808  return true;
809 }
810 
811 bool Driver::setMeridianBehavior(IOP_MB_STATE action, uint8_t degrees)
812 {
813  if (m_Simulation)
814  {
815  simData.mb_state = action;
816  simData.mb_limit = degrees;
817  return true;
818  }
819  else
820  {
821  char cmd[IOP_BUFFER] = {0};
822  snprintf(cmd, IOP_BUFFER, ":SMT%d%02d#", action, degrees);
823  return sendCommandOk(cmd);
824  }
825 }
826 
827 }
bool setTrackMode(IOP_TRACK_RATE rate)
void setSimulation(bool enable)
static const std::map< std::string, std::string > models
bool setPETEnabled(bool enabled)
bool setCustomRATrackRate(double rate)
bool setDaylightSaving(bool enabled)
bool startGuide(IOP_DIRECTION dir, uint32_t ms)
bool setRA(double ra)
bool getUTCDateTime(double *JD, int *utcOffsetMinutes, bool *dayLightSaving)
bool setMeridianBehavior(IOP_MB_STATE action, uint8_t degrees)
void setSimGPSstatus(IOP_GPS_STATUS value)
void setSimSlewRate(IOP_SLEW_RATE value)
bool getPETEnabled(bool enabled)
bool getGuideRate(double *RARate, double *DERate)
bool getRADEFirmware(std::string &RAFirmware, std::string &DEFirmware)
bool sendCommand(const char *command, int count=1, char *response=nullptr, uint8_t timeout=IOP_TIMEOUT, uint8_t debugLog=INDI::Logger::DBG_DEBUG)
bool setGuideRate(double RARate, double DERate)
void setSimTrackRate(IOP_TRACK_RATE value)
bool getStatus(IOPInfo *info)
void setSimTimeSource(IOP_TIME_SOURCE value)
void setSimGuideRate(double raRate, double deRate)
bool setParkAz(double az)
bool setParkAlt(double alt)
bool setLatitude(double latitude)
Driver(const char *deviceName)
bool setLongitude(double longitude)
bool getCoords(double *ra, double *de, IOP_PIER_STATE *pierState, IOP_CW_STATE *cwState)
bool sendCommandOk(const char *command)
void setDebug(bool enable)
bool getMeridianBehavior(IOP_MB_STATE &action, uint8_t &degrees)
void setSimRA(double ra)
bool stopMotion(IOP_DIRECTION dir)
bool setTrackEnabled(bool enabled)
bool setSlewRate(IOP_SLEW_RATE rate)
bool checkConnection(int fd)
void setSimHemisphere(IOP_HEMISPHERE value)
bool setDE(double de)
bool setUTCDateTime(double JD)
void setSimSytemStatus(IOP_SYSTEM_STATUS value)
void setSimLongLat(double longitude, double latitude)
bool setUTCOffset(int offsetMinutes)
struct IOPv3::Driver::@168 simData
void setSimDE(double de)
bool getFirmwareInfo(FirmwareInfo *info)
bool setPECEnabled(bool enabled)
bool startMotion(IOP_DIRECTION dir)
static const uint16_t IOP_SLEW_RATES[]
bool getMainFirmware(std::string &mainFirmware, std::string &controllerFirmware)
bool getModel(std::string &model)
double max(void)
double ra
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:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
#define J2000
Definition: indicom.h:47
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
#define MAXRBUF
Definition: indiserver.cpp:102
int fd
Definition: intelliscope.c:43
Encapsulates classes and structures required for iOptron Command Set v3 implementation.
@ IOP_CW_NORMAL
@ IOP_PIER_WEST
@ ST_TRACKING_PEC_ON
__u8 cmd[4]
Definition: pwc-ioctl.h:2
std::string DEFirmware
std::string RAFirmware
std::string ControllerFirmware
std::string MainBoardFirmware
IOP_GPS_STATUS gpsStatus
IOP_HEMISPHERE hemisphere
IOP_SYSTEM_STATUS systemStatus
IOP_TRACK_RATE trackRate
IOP_SLEW_RATE slewRate
IOP_TIME_SOURCE timeSource