Instrument Neutral Distributed Interface INDI  2.0.2
ieqprolegacy.cpp
Go to the documentation of this file.
1 /*
2  INDI 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 "ieqprolegacy.h"
22 #include "indicom.h"
24 
25 #include <libnova/sidereal_time.h>
26 #include <libnova/transform.h>
27 #include <memory>
28 
29 #include <cmath>
30 #include <cstring>
31 
32 /* Simulation Parameters */
33 #define SLEWRATE 1 /* slew rate, degrees/s */
34 
35 #define MOUNTINFO_TAB "Mount Info"
36 
37 // We declare an auto pointer to IEQPro.
38 static std::unique_ptr<IEQProLegacy> scope(new IEQProLegacy());
39 
40 /* Constructor */
42 {
43  setVersion(1, 7);
44 
45  scopeInfo.gpsStatus = GPS_OFF;
46  scopeInfo.systemStatus = ST_STOPPED;
47  scopeInfo.trackRate = TR_SIDEREAL;
48  scopeInfo.slewRate = SR_1;
49  scopeInfo.timeSource = TS_RS232;
50  scopeInfo.hemisphere = HEMI_NORTH;
51 
52  DBG_SCOPE = INDI::Logger::getInstance().addDebugLevel("Scope Verbose", "SCOPE");
53 
57  9);
58 }
59 
61 {
62  return "iEQ";
63 }
64 
66 {
68 
69  /* Firmware */
70  IUFillText(&FirmwareT[FW_MODEL], "Model", "", nullptr);
71  IUFillText(&FirmwareT[FW_BOARD], "Board", "", nullptr);
72  IUFillText(&FirmwareT[FW_CONTROLLER], "Controller", "", nullptr);
73  IUFillText(&FirmwareT[FW_RA], "RA", "", nullptr);
74  IUFillText(&FirmwareT[FW_DEC], "DEC", "", nullptr);
75  IUFillTextVector(&FirmwareTP, FirmwareT, 5, getDeviceName(), "Firmware Info", "", MOUNTINFO_TAB, IP_RO, 0,
76  IPS_IDLE);
77 
78  /* Tracking Mode */
79  AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
80  AddTrackMode("TRACK_SOLAR", "Solar");
81  AddTrackMode("TRACK_LUNAR", "Lunar");
82  AddTrackMode("TRACK_KING", "King");
83  AddTrackMode("TRACK_CUSTOM", "Custom");
84 
85  // Slew Rates
86  strncpy(SlewRateS[0].label, "1x", MAXINDILABEL);
87  strncpy(SlewRateS[1].label, "2x", MAXINDILABEL);
88  strncpy(SlewRateS[2].label, "8x", MAXINDILABEL);
89  strncpy(SlewRateS[3].label, "16x", MAXINDILABEL);
90  strncpy(SlewRateS[4].label, "64x", MAXINDILABEL);
91  strncpy(SlewRateS[5].label, "128x", MAXINDILABEL);
92  strncpy(SlewRateS[6].label, "256x", MAXINDILABEL);
93  strncpy(SlewRateS[7].label, "512x", MAXINDILABEL);
94  strncpy(SlewRateS[8].label, "MAX", MAXINDILABEL);
96  // 64x is the default
97  SlewRateS[4].s = ISS_ON;
98 
99  // Set TrackRate limits within +/- 0.0100 of Sidereal rate
100  TrackRateN[AXIS_RA].min = TRACKRATE_SIDEREAL - 0.01;
101  TrackRateN[AXIS_RA].max = TRACKRATE_SIDEREAL + 0.01;
102  TrackRateN[AXIS_DE].min = -0.01;
103  TrackRateN[AXIS_DE].max = 0.01;
104 
105  /* GPS Status */
106  IUFillSwitch(&GPSStatusS[GPS_OFF], "Off", "", ISS_ON);
107  IUFillSwitch(&GPSStatusS[GPS_ON], "On", "", ISS_OFF);
108  IUFillSwitch(&GPSStatusS[GPS_DATA_OK], "Data OK", "", ISS_OFF);
109  IUFillSwitchVector(&GPSStatusSP, GPSStatusS, 3, getDeviceName(), "GPS_STATUS", "GPS", MOUNTINFO_TAB, IP_RO,
110  ISR_1OFMANY, 0, IPS_IDLE);
111 
112  /* Time Source */
113  IUFillSwitch(&TimeSourceS[TS_RS232], "RS232", "", ISS_ON);
114  IUFillSwitch(&TimeSourceS[TS_CONTROLLER], "Controller", "", ISS_OFF);
115  IUFillSwitch(&TimeSourceS[TS_GPS], "GPS", "", ISS_OFF);
116  IUFillSwitchVector(&TimeSourceSP, TimeSourceS, 3, getDeviceName(), "TIME_SOURCE", "Time Source", MOUNTINFO_TAB,
117  IP_RO, ISR_1OFMANY, 0, IPS_IDLE);
118 
119  /* Hemisphere */
120  IUFillSwitch(&HemisphereS[HEMI_SOUTH], "South", "", ISS_OFF);
121  IUFillSwitch(&HemisphereS[HEMI_NORTH], "North", "", ISS_ON);
122  IUFillSwitchVector(&HemisphereSP, HemisphereS, 2, getDeviceName(), "HEMISPHERE", "Hemisphere", MOUNTINFO_TAB, IP_RO,
123  ISR_1OFMANY, 0, IPS_IDLE);
124 
125  /* Home */
126  IUFillSwitch(&HomeS[IEQ_FIND_HOME], "FindHome", "Find Home", ISS_OFF);
127  IUFillSwitch(&HomeS[IEQ_SET_HOME], "SetCurrentAsHome", "Set current as Home", ISS_OFF);
128  IUFillSwitch(&HomeS[IEQ_GOTO_HOME], "GoToHome", "Go to Home", ISS_OFF);
129  IUFillSwitchVector(&HomeSP, HomeS, 3, getDeviceName(), "HOME", "Home", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0,
130  IPS_IDLE);
131 
132  /* How fast do we guide compared to sidereal rate */
133  IUFillNumber(&GuideRateN[RA_AXIS], "RA_GUIDE_RATE", "x Sidereal", "%.2f", 0.01, 0.9, 0.1, 0.5);
134  IUFillNumber(&GuideRateN[DEC_AXIS], "DE_GUIDE_RATE", "x Sidereal", "%.2f", 0.1, 0.99, 0.1, 0.5);
135  IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0,
136  IPS_IDLE);
137 
139 
141 
143 
145 
146  addAuxControls();
147 
149 
150  // Only CEM40 and GEM45 have 115200 baud, rest are 9600
151  if (strstr(getDeviceName(), "CEM40") || strstr(getDeviceName(), "GEM45"))
153 
155  currentDEC = LocationN[LOCATION_LATITUDE].value > 0 ? 90 : -90;
156  return true;
157 }
158 
160 {
162 
163  if (isConnected())
164  {
165  defineProperty(&HomeSP);
166 
169  defineProperty(&GuideRateNP);
170 
171  defineProperty(&FirmwareTP);
172  defineProperty(&GPSStatusSP);
173  defineProperty(&TimeSourceSP);
174  defineProperty(&HemisphereSP);
175 
176  getStartupData();
177  }
178  else
179  {
180  deleteProperty(HomeSP.name);
181 
184  deleteProperty(GuideRateNP.name);
185 
186  deleteProperty(FirmwareTP.name);
187  deleteProperty(GPSStatusSP.name);
188  deleteProperty(TimeSourceSP.name);
189  deleteProperty(HemisphereSP.name);
190  }
191 
192  return true;
193 }
194 
195 void IEQProLegacy::getStartupData()
196 {
197  LOG_DEBUG("Getting firmware data...");
198  if (get_ieqpro_firmware(PortFD, &firmwareInfo))
199  {
200  IUSaveText(&FirmwareT[0], firmwareInfo.Model.c_str());
201  IUSaveText(&FirmwareT[1], firmwareInfo.MainBoardFirmware.c_str());
202  IUSaveText(&FirmwareT[2], firmwareInfo.ControllerFirmware.c_str());
203  IUSaveText(&FirmwareT[3], firmwareInfo.RAFirmware.c_str());
204  IUSaveText(&FirmwareT[4], firmwareInfo.DEFirmware.c_str());
205 
206  FirmwareTP.s = IPS_OK;
207  IDSetText(&FirmwareTP, nullptr);
208  }
209 
210  LOG_DEBUG("Getting guiding rate...");
211  double raGuideRate = 0, deGuideRate = 0;
212  if (get_ieqpro_guide_rate(PortFD, &raGuideRate, &deGuideRate))
213  {
214  GuideRateN[RA_AXIS].value = raGuideRate;
215  GuideRateN[DEC_AXIS].value = deGuideRate;
216  IDSetNumber(&GuideRateNP, nullptr);
217  }
218 
219  double utc_offset;
220  int yy, dd, mm, hh, minute, ss;
221  if (get_ieqpro_utc_date_time(PortFD, &utc_offset, &yy, &mm, &dd, &hh, &minute, &ss))
222  {
223  char isoDateTime[32] = {0};
224  char utcOffset[8] = {0};
225 
226  snprintf(isoDateTime, 32, "%04d-%02d-%02dT%02d:%02d:%02d", yy, mm, dd, hh, minute, ss);
227  snprintf(utcOffset, 8, "%4.2f", utc_offset);
228 
229  IUSaveText(IUFindText(&TimeTP, "UTC"), isoDateTime);
230  IUSaveText(IUFindText(&TimeTP, "OFFSET"), utcOffset);
231 
232  LOGF_INFO("Mount UTC offset is %s. UTC time is %s", utcOffset, isoDateTime);
233 
234  TimeTP.s = IPS_OK;
235  IDSetText(&TimeTP, nullptr);
236  }
237 
238  // Get Longitude and Latitude from mount
239  double longitude = 0, latitude = 0;
240  if (get_ieqpro_latitude(PortFD, &latitude) && get_ieqpro_longitude(PortFD, &longitude))
241  {
242  // Convert to INDI standard longitude (0 to 360 Eastward)
243  if (longitude < 0)
244  longitude += 360;
245 
246  LOGF_INFO("Mount Longitude %g Latitude %g", longitude, latitude);
247 
248  LocationN[LOCATION_LATITUDE].value = latitude;
249  LocationN[LOCATION_LONGITUDE].value = longitude;
250  LocationNP.s = IPS_OK;
251 
252  IDSetNumber(&LocationNP, nullptr);
253 
254  saveConfig(true, "GEOGRAPHIC_COORD");
255  }
256  else if (IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude) == 0 &&
257  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude) == 0)
258  {
259  LocationN[LOCATION_LATITUDE].value = latitude;
260  LocationN[LOCATION_LONGITUDE].value = longitude;
261  LocationNP.s = IPS_OK;
262 
263  IDSetNumber(&LocationNP, nullptr);
264  }
265 
266  if (InitPark())
267  {
268  // If loading parking data is successful, we just set the default parking values.
269  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
271  }
272  else
273  {
274  // Otherwise, we set all parking data to default in case no parking data is found.
275  SetAxis1Park(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
277  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
279  }
280 
281  if (isSimulation())
282  {
283  if (isParked())
285  else
287  }
288 }
289 
290 bool IEQProLegacy::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
291 {
292  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
293  {
294  // Guiding Rate
295  if (!strcmp(name, GuideRateNP.name))
296  {
297  IUUpdateNumber(&GuideRateNP, values, names, n);
298 
299  if (set_ieqpro_guide_rate(PortFD, GuideRateN[RA_AXIS].value, GuideRateN[DEC_AXIS].value))
300  GuideRateNP.s = IPS_OK;
301  else
302  GuideRateNP.s = IPS_ALERT;
303 
304  IDSetNumber(&GuideRateNP, nullptr);
305 
306  return true;
307  }
308 
309  if (!strcmp(name, GuideNSNP.name) || !strcmp(name, GuideWENP.name))
310  {
311  processGuiderProperties(name, values, names, n);
312  return true;
313  }
314  }
315 
316  return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
317 }
318 
319 bool IEQProLegacy::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
320 {
321  if (!strcmp(getDeviceName(), dev))
322  {
323  if (!strcmp(name, HomeSP.name))
324  {
325  IUUpdateSwitch(&HomeSP, states, names, n);
326 
327  IEQ_HOME_OPERATION operation = static_cast<IEQ_HOME_OPERATION>(IUFindOnSwitchIndex(&HomeSP));
328 
329  IUResetSwitch(&HomeSP);
330 
331  switch (operation)
332  {
333  case IEQ_FIND_HOME:
334  if (firmwareInfo.Model.find("CEM") == std::string::npos)
335  {
336  HomeSP.s = IPS_IDLE;
337  IDSetSwitch(&HomeSP, nullptr);
338  LOG_WARN("Home search is not supported in this model.");
339  return true;
340  }
341 
342  if (find_ieqpro_home(PortFD) == false)
343  {
344  HomeSP.s = IPS_ALERT;
345  IDSetSwitch(&HomeSP, nullptr);
346  return false;
347  }
348 
349  HomeSP.s = IPS_OK;
350  IDSetSwitch(&HomeSP, nullptr);
351  LOG_INFO("Searching for home position...");
352  return true;
353 
354  case IEQ_SET_HOME:
355  if (set_ieqpro_current_home(PortFD) == false)
356  {
357  HomeSP.s = IPS_ALERT;
358  IDSetSwitch(&HomeSP, nullptr);
359  return false;
360  }
361 
362  HomeSP.s = IPS_OK;
363  IDSetSwitch(&HomeSP, nullptr);
364  LOG_INFO("Home position set to current coordinates.");
365  return true;
366 
367  case IEQ_GOTO_HOME:
368  if (goto_ieqpro_home(PortFD) == false)
369  {
370  HomeSP.s = IPS_ALERT;
371  IDSetSwitch(&HomeSP, nullptr);
372  return false;
373  }
374 
375  HomeSP.s = IPS_OK;
376  IDSetSwitch(&HomeSP, nullptr);
377  LOG_INFO("Slewing to home position...");
378  return true;
379  }
380 
381  return true;
382  }
383  }
384 
385  return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
386 }
387 
389 {
390  bool rc = false;
391 
392  IEQInfo newInfo;
393 
394  if (isSimulation())
395  mountSim();
396 
397  rc = get_ieqpro_status(PortFD, &newInfo);
398 
399  if (rc)
400  {
401  IUResetSwitch(&GPSStatusSP);
402  GPSStatusS[newInfo.gpsStatus].s = ISS_ON;
403  IDSetSwitch(&GPSStatusSP, nullptr);
404 
405  IUResetSwitch(&TimeSourceSP);
406  TimeSourceS[newInfo.timeSource].s = ISS_ON;
407  IDSetSwitch(&TimeSourceSP, nullptr);
408 
409  IUResetSwitch(&HemisphereSP);
410  HemisphereS[newInfo.hemisphere].s = ISS_ON;
411  IDSetSwitch(&HemisphereSP, nullptr);
412 
413  /*
414  TelescopeTrackMode trackMode = TRACK_SIDEREAL;
415 
416  switch (newInfo.trackRate)
417  {
418  case TR_SIDEREAL:
419  trackMode = TRACK_SIDEREAL;
420  break;
421  case TR_SOLAR:
422  trackMode = TRACK_SOLAR;
423  break;
424  case TR_LUNAR:
425  trackMode = TRACK_LUNAR;
426  break;
427  case TR_KING:
428  trackMode = TRACK_SIDEREAL;
429  break;
430  case TR_CUSTOM:
431  trackMode = TRACK_CUSTOM;
432  break;
433  }*/
434 
435  switch (newInfo.systemStatus)
436  {
437  case ST_STOPPED:
440  break;
441  case ST_PARKED:
444  if (!isParked())
445  SetParked(true);
446  break;
447  case ST_HOME:
450  break;
451  case ST_SLEWING:
455  break;
456  case ST_TRACKING_PEC_OFF:
457  case ST_TRACKING_PEC_ON:
458  case ST_GUIDING:
459  // If slew to parking position is complete, issue park command now.
460  if (TrackState == SCOPE_PARKING)
462  else
463  {
466  if (scopeInfo.systemStatus == ST_SLEWING)
467  LOG_INFO("Slew complete, tracking...");
468  else if (scopeInfo.systemStatus == ST_MERIDIAN_FLIPPING)
469  LOG_INFO("Meridian flip complete, tracking...");
470  }
471  break;
472  }
473 
475  TrackModeS[newInfo.trackRate].s = ISS_ON;
476  IDSetSwitch(&TrackModeSP, nullptr);
477 
478  scopeInfo = newInfo;
479  }
480 
481  rc = get_ieqpro_coords(PortFD, &currentRA, &currentDEC);
482 
483  if (rc)
484  NewRaDec(currentRA, currentDEC);
485 
486  return rc;
487 }
488 
489 bool IEQProLegacy::Goto(double r, double d)
490 {
491  targetRA = r;
492  targetDEC = d;
493  char RAStr[64] = {0}, DecStr[64] = {0};
494 
495  fs_sexa(RAStr, targetRA, 2, 3600);
496  fs_sexa(DecStr, targetDEC, 2, 3600);
497 
498  if (set_ieqpro_ra(PortFD, r) == false || set_ieqpro_dec(PortFD, d) == false)
499  {
500  LOG_ERROR("Error setting RA/DEC.");
501  return false;
502  }
503 
504  if (slew_ieqpro(PortFD) == false)
505  {
506  LOG_ERROR("Failed to slew.");
507  return false;
508  }
509 
511 
512  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
513  return true;
514 }
515 
516 bool IEQProLegacy::Sync(double ra, double dec)
517 {
518  if (set_ieqpro_ra(PortFD, ra) == false || set_ieqpro_dec(PortFD, dec) == false)
519  {
520  LOG_ERROR("Error setting RA/DEC.");
521  return false;
522  }
523 
524  if (sync_ieqpro(PortFD) == false)
525  {
526  LOG_ERROR("Failed to sync.");
527  }
528 
529  EqNP.s = IPS_OK;
530 
531  currentRA = ra;
532  currentDEC = dec;
533 
534  NewRaDec(currentRA, currentDEC);
535 
536  return true;
537 }
538 
540 {
541  return abort_ieqpro(PortFD);
542 }
543 
545 {
546 #if 0
547  targetRA = GetAxis1Park();
548  targetDEC = GetAxis2Park();
549 
550  if (set_ieqpro_ra(PortFD, targetRA) == false || set_ieqpro_dec(PortFD, targetDEC) == false)
551  {
552  LOG_ERROR("Error setting RA/DEC.");
553  return false;
554  }
555 
556  if (slew_ieqpro(PortFD) == false)
557  {
558  LOG_ERROR("Failed to slew tp parking position.");
559  return false;
560  }
561 
562  char RAStr[64] = {0}, DecStr[64] = {0};
563  fs_sexa(RAStr, targetRA, 2, 3600);
564  fs_sexa(DecStr, targetDEC, 2, 3600);
565 
567  LOGF_INFO("Telescope parking in progress to RA: %s DEC: %s", RAStr, DecStr);
568 
569  return true;
570 #endif
571 
572  double parkAz = GetAxis1Park();
573  double parkAlt = GetAxis2Park();
574 
575  char AzStr[16], AltStr[16];
576  fs_sexa(AzStr, parkAz, 2, 3600);
577  fs_sexa(AltStr, parkAlt, 2, 3600);
578  LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
579 
580  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
581  INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
582  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
583 
584  if (Goto(equatorialCoords.rightascension, equatorialCoords.declination))
585  {
587  LOG_INFO("Parking is in progress...");
588 
589  return true;
590  }
591  else
592  return false;
593 }
594 
596 {
597  if (unpark_ieqpro(PortFD))
598  {
599  SetParked(false);
601  return true;
602  }
603  else
604  return false;
605 }
606 
608 {
609  if (isSimulation())
610  {
617  }
618 
619  if (check_ieqpro_connection(PortFD) == false)
620  return false;
621 
622  return true;
623 }
624 
625 bool IEQProLegacy::updateTime(ln_date *utc, double utc_offset)
626 {
627  struct ln_zonedate ltm;
628 
629  ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
630 
631  // Set Local Time
632  if (set_ieqpro_local_time(PortFD, ltm.hours, ltm.minutes, ltm.seconds) == false)
633  {
634  LOG_ERROR("Error setting local time.");
635  return false;
636  }
637 
638  // Send it as YY (i.e. 2015 --> 15)
639  ltm.years -= 2000;
640 
641  // Set Local date
642  if (set_ieqpro_local_date(PortFD, ltm.years, ltm.months, ltm.days) == false)
643  {
644  LOG_ERROR("Error setting local date.");
645  return false;
646  }
647 
648  // UTC Offset
649  if (set_ieqpro_utc_offset(PortFD, utc_offset) == false)
650  {
651  LOG_ERROR("Error setting UTC Offset.");
652  return false;
653  }
654 
655  LOG_INFO("Time and date updated.");
656 
657  return true;
658 }
659 
660 bool IEQProLegacy::updateLocation(double latitude, double longitude, double elevation)
661 {
662  INDI_UNUSED(elevation);
663 
664  if (longitude > 180)
665  longitude -= 360;
666 
667  if (set_ieqpro_longitude(PortFD, longitude) == false)
668  {
669  LOG_ERROR("Failed to set longitude.");
670  return false;
671  }
672 
673  if (set_ieqpro_latitude(PortFD, latitude) == false)
674  {
675  LOG_ERROR("Failed to set latitude.");
676  return false;
677  }
678 
679  char l[32] = {0}, L[32] = {0};
680  fs_sexa(l, latitude, 3, 3600);
681  fs_sexa(L, longitude, 4, 3600);
682 
683  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
684 
685  return true;
686 }
687 
689 {
690  set_ieqpro_debug(enable);
691 }
692 
694 {
695  set_ieqpro_simulation(enable);
696 }
697 
699 {
700  if (TrackState == SCOPE_PARKED)
701  {
702  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
703  return false;
704  }
705 
706  switch (command)
707  {
708  case MOTION_START:
709  if (start_ieqpro_motion(PortFD, (dir == DIRECTION_NORTH ? IEQ_N : IEQ_S)) == false)
710  {
711  LOG_ERROR("Error setting N/S motion direction.");
712  return false;
713  }
714  else
715  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_NORTH) ? "North" : "South");
716  break;
717 
718  case MOTION_STOP:
719  if (stop_ieqpro_motion(PortFD, (dir == DIRECTION_NORTH ? IEQ_N : IEQ_S)) == false)
720  {
721  LOG_ERROR("Error stopping N/S motion.");
722  return false;
723  }
724  else
725  LOGF_INFO("%s motion stopped.", (dir == DIRECTION_NORTH) ? "North" : "South");
726  break;
727  }
728 
729  return true;
730 }
731 
733 {
734  if (TrackState == SCOPE_PARKED)
735  {
736  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
737  return false;
738  }
739 
740  switch (command)
741  {
742  case MOTION_START:
743  if (start_ieqpro_motion(PortFD, (dir == DIRECTION_WEST ? IEQ_W : IEQ_E)) == false)
744  {
745  LOG_ERROR("Error setting N/S motion direction.");
746  return false;
747  }
748  else
749  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_WEST) ? "West" : "East");
750  break;
751 
752  case MOTION_STOP:
753  if (stop_ieqpro_motion(PortFD, (dir == DIRECTION_WEST ? IEQ_W : IEQ_E)) == false)
754  {
755  LOG_ERROR("Error stopping W/E motion.");
756  return false;
757  }
758  else
759  LOGF_INFO("%s motion stopped.", (dir == DIRECTION_WEST) ? "West" : "East");
760  break;
761  }
762 
763  return true;
764 }
765 
767 {
768  bool rc = start_ieqpro_guide(PortFD, IEQ_N, ms);
769  return (rc ? IPS_OK : IPS_ALERT);
770 }
771 
773 {
774  bool rc = start_ieqpro_guide(PortFD, IEQ_S, ms);
775  return (rc ? IPS_OK : IPS_ALERT);
776 }
777 
779 {
780  bool rc = start_ieqpro_guide(PortFD, IEQ_E, ms);
781  return (rc ? IPS_OK : IPS_ALERT);
782 }
783 
785 {
786  bool rc = start_ieqpro_guide(PortFD, IEQ_W, ms);
787  return (rc ? IPS_OK : IPS_ALERT);
788 }
789 
791 {
792  IEQ_SLEW_RATE rate = static_cast<IEQ_SLEW_RATE>(index);
793  return set_ieqpro_slew_rate(PortFD, rate);
794 }
795 
797 {
799 
800  return true;
801 }
802 
804 {
805  static struct timeval ltv;
806  struct timeval tv;
807  double dt, da, dx;
808  int nlocked;
809 
810  /* update elapsed time since last poll, don't presume exactly POLLMS */
811  gettimeofday(&tv, nullptr);
812 
813  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
814  ltv = tv;
815 
816  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
817  ltv = tv;
818  da = SLEWRATE * dt;
819 
820  /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
821  switch (TrackState)
822  {
823  case SCOPE_IDLE:
824  currentRA += (TrackRateN[AXIS_RA].value / 3600.0 * dt) / 15.0;
826  break;
827 
828  case SCOPE_TRACKING:
829  if (TrackModeS[1].s == ISS_ON)
830  {
831  currentRA += ( ((TRACKRATE_SIDEREAL / 3600.0) - (TrackRateN[AXIS_RA].value / 3600.0)) * dt) / 15.0;
832  currentDEC += ( (TrackRateN[AXIS_DE].value / 3600.0) * dt);
833  }
834  break;
835 
836  case SCOPE_SLEWING:
837  case SCOPE_PARKING:
838  /* slewing - nail it when both within one pulse @ SLEWRATE */
839  nlocked = 0;
840 
841  dx = targetRA - currentRA;
842 
843  // Take shortest path
844  if (fabs(dx) > 12)
845  dx *= -1;
846 
847  if (fabs(dx) <= da)
848  {
849  currentRA = targetRA;
850  nlocked++;
851  }
852  else if (dx > 0)
853  currentRA += da / 15.;
854  else
855  currentRA -= da / 15.;
856 
857  if (currentRA < 0)
858  currentRA += 24;
859  else if (currentRA > 24)
860  currentRA -= 24;
861 
862  dx = targetDEC - currentDEC;
863  if (fabs(dx) <= da)
864  {
865  currentDEC = targetDEC;
866  nlocked++;
867  }
868  else if (dx > 0)
869  currentDEC += da;
870  else
871  currentDEC -= da;
872 
873  if (nlocked == 2)
874  {
875  if (TrackState == SCOPE_SLEWING)
877  else
879  }
880 
881  break;
882 
883  default:
884  break;
885  }
886 
889 }
890 
892 {
894  INDI::IHorizontalCoordinates horizontalCoords {0, 0};
895  INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
896  double parkAZ = horizontalCoords.azimuth;
897  double parkAlt = horizontalCoords.altitude;
898 
899  char AzStr[16], AltStr[16];
900  fs_sexa(AzStr, parkAZ, 2, 3600);
901  fs_sexa(AltStr, parkAlt, 2, 3600);
902 
903  LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr,
904  AltStr);
905 
906  SetAxis1Park(parkAZ);
907  SetAxis2Park(parkAlt);
908 
909  return true;
910 }
911 
913 {
914  // By defualt azimuth 0
915  SetAxis1Park(0);
916 
917  // Altitude = latitude of observer
919 
920  return true;
921 }
922 
923 bool IEQProLegacy::SetTrackMode(uint8_t mode)
924 {
925  IEQ_TRACK_RATE rate = static_cast<IEQ_TRACK_RATE>(mode);
926 
927  if (set_ieqpro_track_mode(PortFD, rate))
928  return true;
929 
930  return false;
931 }
932 
933 bool IEQProLegacy::SetTrackRate(double raRate, double deRate)
934 {
935  static bool deRateWarning = true;
936 
937  // Convert to arcsecs/s to +/- 0.0100 accepted by
938  double ieqRARate = raRate - TRACKRATE_SIDEREAL;
939  if (deRate != 0 && deRateWarning)
940  {
941  // Only send warning once per session
942  deRateWarning = false;
943  LOG_WARN("Custom Declination tracking rate is not implemented yet.");
944  }
945 
946  if (set_ieqpro_custom_ra_track_rate(PortFD, ieqRARate))
947  return true;
948 
949  return false;
950 }
951 
953 {
954  if (enabled)
955  {
956  // If we are engaging tracking, let us first set tracking mode, and if we have custom mode, then tracking rate.
957  // NOTE: Is this the correct order? or should tracking be switched on first before making these changes? Need to test.
959  if (TrackModeS[TR_CUSTOM].s == ISS_ON)
961  }
962 
963  return set_ieqpro_track_enabled(PortFD, enabled);
964 }
@ FW_MODEL
@ FW_DEC
@ FW_RA
@ SR_1
@ SR_3
@ GPS_OFF
@ GPS_ON
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool Park() override
Park the telescope to its home position.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual const char * getDefaultName() override
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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 addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
uint16_t getDriverInterface() const
INumberVectorProperty GuideNSNP
void initGuiderProperties(const char *deviceName, const char *groupName)
Initilize guider properties. It is recommended to call this function within initProperties() of your ...
INumberVectorProperty GuideWENP
void processGuiderProperties(const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
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.
INumberVectorProperty LocationNP
virtual bool initProperties() override
Called to initialize basic properties required all the time.
ITextVectorProperty TimeTP
double GetAxis1Park() const
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
ISwitchVectorProperty TrackModeSP
ISwitchVectorProperty SlewRateSP
Connection::Serial * serialConnection
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
INumber TrackRateN[2]
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
ISwitch * TrackModeS
ISwitch * SlewRateS
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
#define currentDEC
Definition: ieq45.cpp:48
#define currentRA
Definition: ieq45.cpp:47
#define SLEWRATE
#define MOUNTINFO_TAB
bool goto_ieqpro_home(int fd)
bool set_ieqpro_latitude(int fd, double latitude)
bool sync_ieqpro(int fd)
bool set_ieqpro_current_home(int fd)
void set_sim_gps_status(IEQ_GPS_STATUS value)
bool start_ieqpro_guide(int fd, IEQ_DIRECTION dir, uint32_t ms)
void set_sim_dec(double dec)
bool find_ieqpro_home(int fd)
bool slew_ieqpro(int fd)
void set_sim_hemisphere(IEQ_HEMISPHERE value)
bool set_ieqpro_local_date(int fd, int yy, int mm, int dd)
bool get_ieqpro_utc_date_time(int fd, double *utc_hours, int *yy, int *mm, int *dd, int *hh, int *minute, int *ss)
bool get_ieqpro_status(int fd, IEQInfo *info)
bool set_ieqpro_local_time(int fd, int hh, int mm, int ss)
void set_ieqpro_debug(bool enable)
bool get_ieqpro_coords(int fd, double *ra, double *dec)
bool park_ieqpro(int fd)
double ra
void set_ieqpro_device(const char *name)
void set_ieqpro_simulation(bool enable)
bool get_ieqpro_firmware(int fd, FirmwareInfo *info)
bool set_ieqpro_custom_ra_track_rate(int fd, double rate)
bool set_ieqpro_dec(int fd, double dec)
bool set_ieqpro_guide_rate(int fd, double raRate, double deRate)
bool abort_ieqpro(int fd)
bool unpark_ieqpro(int fd)
bool set_ieqpro_track_mode(int fd, IEQ_TRACK_RATE rate)
void set_sim_system_status(IEQ_SYSTEM_STATUS value)
void set_sim_slew_rate(IEQ_SLEW_RATE value)
bool get_ieqpro_latitude(int fd, double *latitude)
bool check_ieqpro_connection(int fd)
double dec
bool set_ieqpro_ra(int fd, double ra)
bool set_ieqpro_slew_rate(int fd, IEQ_SLEW_RATE rate)
bool get_ieqpro_guide_rate(int fd, double *raRate, double *deRate)
bool start_ieqpro_motion(int fd, IEQ_DIRECTION dir)
void set_sim_time_source(IEQ_TIME_SOURCE value)
bool set_ieqpro_utc_offset(int fd, double offset)
bool set_ieqpro_longitude(int fd, double longitude)
bool get_ieqpro_longitude(int fd, double *longitude)
void set_sim_ra(double ra)
bool set_ieqpro_track_enabled(int fd, bool enabled)
bool stop_ieqpro_motion(int fd, IEQ_DIRECTION dir)
void set_sim_track_rate(IEQ_TRACK_RATE value)
IEQ_HOME_OPERATION
@ IEQ_FIND_HOME
@ IEQ_SET_HOME
@ IEQ_GOTO_HOME
IEQ_TRACK_RATE
@ TR_SIDEREAL
@ TR_CUSTOM
@ TS_RS232
@ TS_GPS
@ TS_CONTROLLER
@ HEMI_NORTH
@ HEMI_SOUTH
IEQ_SLEW_RATE
@ FW_CONTROLLER
@ FW_BOARD
@ IEQ_E
@ IEQ_S
@ IEQ_N
@ IEQ_W
@ GPS_DATA_OK
@ ST_TRACKING_PEC_ON
@ ST_TRACKING_PEC_OFF
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
#define MAXINDILABEL
Definition: indiapi.h:192
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
@ AXIS_DE
Definition: indibasetypes.h:36
@ AXIS_RA
Definition: indibasetypes.h:35
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition: indicom.c:1235
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:141
Implementations for common driver routines.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
#define TRACKRATE_SIDEREAL
Definition: indicom.h:55
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:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
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:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition: indidevapi.c:56
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
Definition: indidriver.c:831
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#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 LOG_INFO(txt)
Definition: indilogger.h:74
#define RA_AXIS
#define DEC_AXIS
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition: libastro.cpp:140
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition: libastro.cpp:156
@ ST_HOME
Definition: ieqdriverbase.h:40
@ ST_SLEWING
Definition: ieqdriverbase.h:35
@ ST_MERIDIAN_FLIPPING
Definition: ieqdriverbase.h:37
@ ST_STOPPED
Definition: ieqdriverbase.h:33
@ ST_GUIDING
Definition: ieqdriverbase.h:36
@ ST_PARKED
Definition: ieqdriverbase.h:39
std::string RAFirmware
std::string ControllerFirmware
std::string Model
std::string DEFirmware
std::string MainBoardFirmware
IEQ_GPS_STATUS gpsStatus
IEQ_SLEW_RATE slewRate
IEQ_TIME_SOURCE timeSource
IEQ_SYSTEM_STATUS systemStatus
IEQ_TRACK_RATE trackRate
IEQ_HEMISPHERE hemisphere
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:339
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:72
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250