Instrument Neutral Distributed Interface INDI  2.0.2
ioptronv3.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  Updated for PEC V3
21 */
22 
23 #include "ioptronv3.h"
26 #include "indicom.h"
27 
28 #include <libnova/transform.h>
29 #include <libnova/sidereal_time.h>
30 
31 #include <memory>
32 
33 #include <cmath>
34 #include <cstring>
35 
36 using namespace IOPv3;
37 
38 #define MOUNTINFO_TAB "Mount Info"
39 // #define PEC_TAB "PEC" //Not Needed
40 
41 // We declare an auto pointer to IOptronV3.
42 static std::unique_ptr<IOptronV3> scope(new IOptronV3());
43 
44 /* Constructor */
46 {
47  setVersion(1, 6);
48 
49  driver.reset(new Driver(getDeviceName()));
50 
51  scopeInfo.gpsStatus = GPS_OFF;
52  scopeInfo.systemStatus = ST_STOPPED;
53  scopeInfo.trackRate = TR_SIDEREAL;
54  /* v3.0 use default PEC Settings */
55  scopeInfo.systemStatus = ST_TRACKING_PEC_OFF;
56  // End Mod */
57  scopeInfo.slewRate = SR_MAX;
58  scopeInfo.timeSource = TS_RS232;
59  scopeInfo.hemisphere = HEMI_NORTH;
60 
61  DBG_SCOPE = INDI::Logger::getInstance().addDebugLevel("Scope Verbose", "SCOPE");
62 
63  SetTelescopeCapability(TELESCOPE_CAN_PARK | TELESCOPE_CAN_SYNC | TELESCOPE_CAN_GOTO | TELESCOPE_CAN_ABORT |
64  /* v3.0 use default PEC Settings */
65  TELESCOPE_HAS_PEC |
66  // End Mod */
67  TELESCOPE_HAS_TIME | TELESCOPE_HAS_LOCATION | TELESCOPE_HAS_TRACK_MODE |
68  TELESCOPE_CAN_CONTROL_TRACK | TELESCOPE_HAS_TRACK_RATE | TELESCOPE_HAS_PIER_SIDE,
69  9);
70 }
71 
73 {
74  return "iOptronV3";
75 }
76 
78 {
80 
81  // Slew Rates
82  strncpy(SlewRateS[0].label, "1x", MAXINDILABEL);
83  strncpy(SlewRateS[1].label, "2x", MAXINDILABEL);
84  strncpy(SlewRateS[2].label, "8x", MAXINDILABEL);
85  strncpy(SlewRateS[3].label, "16x", MAXINDILABEL);
86  strncpy(SlewRateS[4].label, "64x", MAXINDILABEL);
87  strncpy(SlewRateS[5].label, "128x", MAXINDILABEL);
88  strncpy(SlewRateS[6].label, "256x", MAXINDILABEL);
89  strncpy(SlewRateS[7].label, "512x", MAXINDILABEL);
90  strncpy(SlewRateS[8].label, "MAX", MAXINDILABEL);
91  IUResetSwitch(&SlewRateSP);
92  // Max is the default
93  SlewRateS[8].s = ISS_ON;
94 
95  /* Firmware */
96  IUFillText(&FirmwareT[FW_MODEL], "Model", "", nullptr);
97  IUFillText(&FirmwareT[FW_BOARD], "Board", "", nullptr);
98  IUFillText(&FirmwareT[FW_CONTROLLER], "Controller", "", nullptr);
99  IUFillText(&FirmwareT[FW_RA], "RA", "", nullptr);
100  IUFillText(&FirmwareT[FW_DEC], "DEC", "", nullptr);
101  IUFillTextVector(&FirmwareTP, FirmwareT, 5, getDeviceName(), "Firmware Info", "", MOUNTINFO_TAB, IP_RO, 0,
102  IPS_IDLE);
103 
104  /* Tracking Mode */
105  AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
106  AddTrackMode("TRACK_LUNAR", "Lunar");
107  AddTrackMode("TRACK_SOLAR", "Solar");
108  AddTrackMode("TRACK_KING", "King");
109  AddTrackMode("TRACK_CUSTOM", "Custom");
110 
111  /* GPS Status */
112  IUFillSwitch(&GPSStatusS[GPS_OFF], "Off", "", ISS_ON);
113  IUFillSwitch(&GPSStatusS[GPS_ON], "On", "", ISS_OFF);
114  IUFillSwitch(&GPSStatusS[GPS_DATA_OK], "Data OK", "", ISS_OFF);
115  IUFillSwitchVector(&GPSStatusSP, GPSStatusS, 3, getDeviceName(), "GPS_STATUS", "GPS", MOUNTINFO_TAB, IP_RO,
116  ISR_1OFMANY, 0, IPS_IDLE);
117 
118  /* Time Source */
119  IUFillSwitch(&TimeSourceS[TS_RS232], "RS232", "", ISS_ON);
120  IUFillSwitch(&TimeSourceS[TS_CONTROLLER], "Controller", "", ISS_OFF);
121  IUFillSwitch(&TimeSourceS[TS_GPS], "GPS", "", ISS_OFF);
122  IUFillSwitchVector(&TimeSourceSP, TimeSourceS, 3, getDeviceName(), "TIME_SOURCE", "Time Source", MOUNTINFO_TAB,
123  IP_RO, ISR_1OFMANY, 0, IPS_IDLE);
124 
125  /* Hemisphere */
126  IUFillSwitch(&HemisphereS[HEMI_SOUTH], "South", "", ISS_OFF);
127  IUFillSwitch(&HemisphereS[HEMI_NORTH], "North", "", ISS_ON);
128  IUFillSwitchVector(&HemisphereSP, HemisphereS, 2, getDeviceName(), "HEMISPHERE", "Hemisphere", MOUNTINFO_TAB, IP_RO,
129  ISR_1OFMANY, 0, IPS_IDLE);
130 
131  /* Home */
132  IUFillSwitch(&HomeS[IOP_FIND_HOME], "FindHome", "Find Home", ISS_OFF);
133  IUFillSwitch(&HomeS[IOP_SET_HOME], "SetCurrentAsHome", "Set current as Home", ISS_OFF);
134  IUFillSwitch(&HomeS[IOP_GOTO_HOME], "GoToHome", "Go to Home", ISS_OFF);
135  IUFillSwitchVector(&HomeSP, HomeS, 3, getDeviceName(), "HOME", "Home", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0,
136  IPS_IDLE);
137 
138  /* v3.0 Create PEC Training switches */
139  // PEC Training
140  IUFillSwitch(&PECTrainingS[0], "PEC_Recording", "Record", ISS_OFF);
141  IUFillSwitch(&PECTrainingS[1], "PEC_Status", "Status", ISS_OFF);
142  IUFillSwitchVector(&PECTrainingSP, PECTrainingS, 2, getDeviceName(), "PEC_TRAINING", "Training", MOTION_TAB, IP_RW,
143  ISR_ATMOST1, 0,
144  IPS_IDLE);
145 
146  // Create PEC Training Information */
147  IUFillText(&PECInfoT[0], "PEC_INFO", "Status", "");
148  IUFillTextVector(&PECInfoTP, PECInfoT, 1, getDeviceName(), "PEC_INFOS", "Data", MOTION_TAB,
149  IP_RO, 60, IPS_IDLE);
150  // End Mod */
151 
152  /* How fast do we guide compared to sidereal rate */
153  IUFillNumber(&GuideRateN[0], "RA_GUIDE_RATE", "x Sidereal", "%g", 0.01, 0.9, 0.1, 0.5);
154  IUFillNumber(&GuideRateN[1], "DE_GUIDE_RATE", "x Sidereal", "%g", 0.1, 0.99, 0.1, 0.5);
155  IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0,
156  IPS_IDLE);
157 
158 
159  /* Slew Mode. Normal vs Counter Weight up */
160  IUFillSwitch(&SlewModeS[IOP_CW_NORMAL], "Normal", "Normal", ISS_ON);
161  IUFillSwitch(&SlewModeS[IOP_CW_UP], "Counterweight UP", "Counterweight up", ISS_OFF);
162  IUFillSwitchVector(&SlewModeSP, SlewModeS, 2, getDeviceName(), "Slew Type", "Slew Type", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
163  IPS_IDLE);
164 
165  /* Daylight Savings */
166  IUFillSwitch(&DaylightS[0], "ON", "ON", ISS_OFF);
167  IUFillSwitch(&DaylightS[1], "OFF", "OFF", ISS_ON);
168  IUFillSwitchVector(&DaylightSP, DaylightS, 2, getDeviceName(), "DaylightSaving", "Daylight Savings", SITE_TAB, IP_RW,
169  ISR_1OFMANY, 0,
170  IPS_IDLE);
171 
172  /* Counter Weight State */
173  IUFillSwitch(&CWStateS[IOP_CW_NORMAL], "Normal", "Normal", ISS_ON);
174  IUFillSwitch(&CWStateS[IOP_CW_UP], "Up", "Up", ISS_OFF);
175  IUFillSwitchVector(&CWStateSP, CWStateS, 2, getDeviceName(), "CWState", "Counter weights", MOTION_TAB, IP_RO, ISR_1OFMANY,
176  0, IPS_IDLE);
177 
178  /* Meridian Behavior */
179  IUFillSwitch(&MeridianActionS[IOP_MB_STOP], "IOP_MB_STOP", "Stop", ISS_ON);
180  IUFillSwitch(&MeridianActionS[IOP_MB_FLIP], "IOP_MB_FLIP", "Flip", ISS_OFF);
181  IUFillSwitchVector(&MeridianActionSP, MeridianActionS, 2, getDeviceName(), "MERIDIAN_ACTION", "Action", MB_TAB, IP_RW,
182  ISR_1OFMANY,
183  0, IPS_IDLE);
184 
185  /* Meridian Limit */
186  IUFillNumber(&MeridianLimitN[0], "VALUE", "Degrees", "%.f", 0, 10, 1, 0);
187  IUFillNumberVector(&MeridianLimitNP, MeridianLimitN, 1, getDeviceName(), "MERIDIAN_LIMIT", "Limit", MB_TAB, IP_RW, 60,
188  IPS_IDLE);
189 
190  // Baud rates.
191  // 230400 for 120
192  // 115000 for everything else
193  if (strstr(getDeviceName(), "120"))
194  serialConnection->setDefaultBaudRate(Connection::Serial::B_230400);
195  else
196  serialConnection->setDefaultBaudRate(Connection::Serial::B_115200);
197 
198  // Default WiFi connection parametes
199  tcpConnection->setDefaultHost("10.10.100.254");
200  tcpConnection->setDefaultPort(8899);
201 
202  TrackState = SCOPE_IDLE;
203 
204  initGuiderProperties(getDeviceName(), MOTION_TAB);
205 
206  setDriverInterface(getDriverInterface() | GUIDER_INTERFACE);
207 
208  SetParkDataType(PARK_AZ_ALT);
209 
210  addAuxControls();
211 
212  currentRA = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
213  currentDEC = LocationN[LOCATION_LATITUDE].value > 0 ? 90 : -90;
214  driver->setSimLongLat(LocationN[LOCATION_LONGITUDE].value > 180 ? LocationN[LOCATION_LONGITUDE].value - 360 :
215  LocationN[LOCATION_LONGITUDE].value, LocationN[LOCATION_LATITUDE].value);
216 
217  return true;
218 }
219 
221 {
223 
224  if (isConnected())
225  {
226  defineProperty(&HomeSP);
227 
228  /* v3.0 Create PEC switches */
229  defineProperty(&PECTrainingSP);
230  defineProperty(&PECInfoTP);
231  // End Mod */
232 
233  defineProperty(&GuideNSNP);
234  defineProperty(&GuideWENP);
235  defineProperty(&GuideRateNP);
236 
237  defineProperty(&FirmwareTP);
238  defineProperty(&GPSStatusSP);
239  defineProperty(&TimeSourceSP);
240  defineProperty(&HemisphereSP);
241  defineProperty(&SlewModeSP);
242  defineProperty(&DaylightSP);
243  defineProperty(&CWStateSP);
244 
245  defineProperty(&MeridianActionSP);
246  defineProperty(&MeridianLimitNP);
247 
248  getStartupData();
249  }
250  else
251  {
252  deleteProperty(HomeSP.name);
253 
254  /* v3.0 Delete PEC switches */
255  deleteProperty(PECTrainingSP.name);
256  deleteProperty(PECInfoTP.name);
257  // End Mod*/
258 
259  deleteProperty(GuideNSNP.name);
260  deleteProperty(GuideWENP.name);
261  deleteProperty(GuideRateNP.name);
262 
263  deleteProperty(FirmwareTP.name);
264  deleteProperty(GPSStatusSP.name);
265  deleteProperty(TimeSourceSP.name);
266  deleteProperty(HemisphereSP.name);
267  deleteProperty(SlewModeSP.name);
268  deleteProperty(DaylightSP.name);
269  deleteProperty(CWStateSP.name);
270 
271  deleteProperty(MeridianActionSP.name);
272  deleteProperty(MeridianLimitNP.name);
273  }
274 
275  return true;
276 }
277 
278 void IOptronV3::getStartupData()
279 {
280  LOG_DEBUG("Getting firmware data...");
281  if (driver->getFirmwareInfo(&firmwareInfo))
282  {
283  IUSaveText(&FirmwareT[0], firmwareInfo.Model.c_str());
284  IUSaveText(&FirmwareT[1], firmwareInfo.MainBoardFirmware.c_str());
285  IUSaveText(&FirmwareT[2], firmwareInfo.ControllerFirmware.c_str());
286  IUSaveText(&FirmwareT[3], firmwareInfo.RAFirmware.c_str());
287  IUSaveText(&FirmwareT[4], firmwareInfo.DEFirmware.c_str());
288 
289  FirmwareTP.s = IPS_OK;
290  IDSetText(&FirmwareTP, nullptr);
291  }
292 
293  LOG_DEBUG("Getting guiding rate...");
294  double RARate = 0, DERate = 0;
295  if (driver->getGuideRate(&RARate, &DERate))
296  {
297  GuideRateN[RA_AXIS].value = RARate;
298  GuideRateN[DEC_AXIS].value = DERate;
299  IDSetNumber(&GuideRateNP, nullptr);
300  }
301 
302  int utcOffsetMinutes = 0;
303  bool dayLightSavings = false;
304  double JD = 0;
305  if (driver->getUTCDateTime(&JD, &utcOffsetMinutes, &dayLightSavings))
306  {
307  time_t utc_time;
308  ln_get_timet_from_julian(JD, &utc_time);
309 
310  // UTC Time
311  char ts[32] = {0};
312  struct tm *utc;
313  utc = gmtime(&utc_time);
314  strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", utc);
315  IUSaveText(&TimeT[0], ts);
316  LOGF_INFO("Mount UTC: %s", ts);
317 
318  // UTC Offset
319  char offset[8] = {0};
320  // 2021-05-12 JM: Account for daylight savings
321  if (dayLightSavings)
322  utcOffsetMinutes += 60;
323 
324  snprintf(offset, 8, "%.2f", utcOffsetMinutes / 60.0);
325  IUSaveText(&TimeT[1], offset);
326  LOGF_INFO("Mount UTC Offset: %s", offset);
327 
328  TimeTP.s = IPS_OK;
329  IDSetText(&TimeTP, nullptr);
330 
331  LOGF_INFO("Mount Daylight Savings: %s", dayLightSavings ? "ON" : "OFF");
332  DaylightS[0].s = dayLightSavings ? ISS_ON : ISS_OFF;
333  DaylightS[1].s = !dayLightSavings ? ISS_ON : ISS_OFF;
334  DaylightSP.s = IPS_OK;
335  IDSetSwitch(&DaylightSP, nullptr);
336  }
337 
338  // Get Longitude and Latitude from mount
339  double longitude = 0, latitude = 0;
340  if (driver->getStatus(&scopeInfo))
341  {
342  LocationN[LOCATION_LATITUDE].value = scopeInfo.latitude;
343  // Convert to INDI standard longitude (0 to 360 Eastward)
344  LocationN[LOCATION_LONGITUDE].value = (scopeInfo.longitude < 0) ? scopeInfo.longitude + 360 : scopeInfo.longitude;
345  LocationNP.s = IPS_OK;
346 
347  IDSetNumber(&LocationNP, nullptr);
348 
349  char l[32] = {0}, L[32] = {0};
350  fs_sexa(l, LocationN[LOCATION_LATITUDE].value, 3, 3600);
351  fs_sexa(L, LocationN[LOCATION_LONGITUDE].value, 4, 3600);
352 
353  LOGF_INFO("Mount Location: Lat %.32s - Long %.32s", l, L);
354 
355  saveConfig(true, "GEOGRAPHIC_COORD");
356  }
357  else if (IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude) == 0 &&
358  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude) == 0)
359  {
360  LocationN[LOCATION_LATITUDE].value = latitude;
361  LocationN[LOCATION_LONGITUDE].value = longitude;
362  LocationNP.s = IPS_OK;
363 
364  IDSetNumber(&LocationNP, nullptr);
365  }
366 
367  IOP_MB_STATE action;
368  uint8_t degrees = 0;
369  if (driver->getMeridianBehavior(action, degrees))
370  {
371  IUResetSwitch(&MeridianActionSP);
372  MeridianActionS[action].s = ISS_ON;
373  MeridianActionSP.s = IPS_OK;
374 
375  MeridianLimitN[0].value = degrees;
376  }
377 
378  double parkAZ = LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180;
379  double parkAL = LocationN[LOCATION_LATITUDE].value;
380  if (InitPark())
381  {
382  // If loading parking data is successful, we just set the default parking values.
383  SetAxis1ParkDefault(parkAZ);
384  SetAxis2ParkDefault(parkAL);
385  }
386  else
387  {
388  // Otherwise, we set all parking data to default in case no parking data is found.
389  SetAxis1Park(parkAZ);
390  SetAxis2Park(parkAL);
391  SetAxis1ParkDefault(parkAZ);
392  SetAxis2ParkDefault(parkAL);
393 
394  driver->setParkAz(parkAZ);
395  driver->setParkAlt(parkAL);
396  }
397 
398  /* v3.0 Read PEC State at startup */
399  IOPInfo newInfo;
400  if (driver->getStatus(&newInfo))
401  {
402  switch (newInfo.systemStatus)
403  {
404  case ST_STOPPED:
405  case ST_PARKED:
406  case ST_HOME:
407  case ST_SLEWING:
409  case ST_GUIDING:
410 
411  case ST_TRACKING_PEC_OFF:
412  setPECState(PEC_OFF);
413  GetPECDataStatus(true);
414  break;
415 
416  case ST_TRACKING_PEC_ON:
417  setPECState(PEC_ON);
418  GetPECDataStatus(true);
419  break;
420  }
421  scopeInfo = newInfo;
422  }
423 
424  if (isSimulation())
425  {
426  if (isParked())
427  driver->setSimSytemStatus(ST_PARKED);
428  else
429  driver->setSimSytemStatus(ST_STOPPED);
430  }
431 }
432 
433 bool IOptronV3::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
434 {
435  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
436  {
437  // Guiding Rate
438  if (!strcmp(name, GuideRateNP.name))
439  {
440  IUUpdateNumber(&GuideRateNP, values, names, n);
441 
442  if (driver->setGuideRate(GuideRateN[RA_AXIS].value, GuideRateN[DEC_AXIS].value))
443  GuideRateNP.s = IPS_OK;
444  else
445  GuideRateNP.s = IPS_ALERT;
446 
447  IDSetNumber(&GuideRateNP, nullptr);
448 
449  return true;
450  }
451 
452  /****************************************
453  Meridian Flip Limit
454  *****************************************/
455  if (!strcmp(name, MeridianLimitNP.name))
456  {
457  IUUpdateNumber(&MeridianLimitNP, values, names, n);
458  MeridianLimitNP.s = driver->setMeridianBehavior(static_cast<IOP_MB_STATE>(IUFindOnSwitchIndex(&MeridianActionSP)),
459  MeridianLimitN[0].value) ? IPS_OK : IPS_ALERT;
460  if (MeridianLimitNP.s == IPS_OK)
461  {
462  LOGF_INFO("Mount Meridian Behavior: When mount reaches %.f degrees past meridian, it will %s.",
463  MeridianLimitN[0].value, MeridianActionS[IOP_MB_STOP].s == ISS_ON ? "stop" : "flip");
464  }
465  IDSetNumber(&MeridianLimitNP, nullptr);
466  saveConfig(true, MeridianLimitNP.name);
467  return true;
468  }
469 
470  if (!strcmp(name, GuideNSNP.name) || !strcmp(name, GuideWENP.name))
471  {
472  processGuiderProperties(name, values, names, n);
473  return true;
474  }
475  }
476 
477  return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
478 }
479 
480 bool IOptronV3::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
481 {
482  if (!strcmp(getDeviceName(), dev))
483  {
484  /*******************************************************
485  * Home Operations
486  *******************************************************/
487  if (!strcmp(name, HomeSP.name))
488  {
489  IUUpdateSwitch(&HomeSP, states, names, n);
490 
492 
493  IUResetSwitch(&HomeSP);
494 
495  switch (operation)
496  {
497  case IOP_FIND_HOME:
498  if (firmwareInfo.Model.find("CEM") == std::string::npos &&
499  firmwareInfo.Model.find("GEM45") == std::string::npos)
500  {
501  HomeSP.s = IPS_IDLE;
502  IDSetSwitch(&HomeSP, nullptr);
503  LOG_WARN("Home search is not supported in this model.");
504  return true;
505  }
506 
507  if (driver->findHome() == false)
508  {
509  HomeSP.s = IPS_ALERT;
510  IDSetSwitch(&HomeSP, nullptr);
511  return false;
512  }
513 
514  HomeSP.s = IPS_OK;
515  IDSetSwitch(&HomeSP, nullptr);
516  LOG_INFO("Searching for home position...");
517  return true;
518 
519  case IOP_SET_HOME:
520  if (driver->setCurrentHome() == false)
521  {
522  HomeSP.s = IPS_ALERT;
523  IDSetSwitch(&HomeSP, nullptr);
524  return false;
525  }
526 
527  HomeSP.s = IPS_OK;
528  IDSetSwitch(&HomeSP, nullptr);
529  LOG_INFO("Home position set to current coordinates.");
530  return true;
531 
532  case IOP_GOTO_HOME:
533  if (driver->gotoHome() == false)
534  {
535  HomeSP.s = IPS_ALERT;
536  IDSetSwitch(&HomeSP, nullptr);
537  return false;
538  }
539 
540  HomeSP.s = IPS_OK;
541  IDSetSwitch(&HomeSP, nullptr);
542  LOG_INFO("Slewing to home position...");
543  return true;
544  }
545 
546  return true;
547  }
548 
549  /*******************************************************
550  * Slew Mode Operations
551  *******************************************************/
552  if (!strcmp(name, SlewModeSP.name))
553  {
554  IUUpdateSwitch(&SlewModeSP, states, names, n);
555  SlewModeSP.s = IPS_OK;
556  IDSetSwitch(&SlewModeSP, nullptr);
557  return true;
558  }
559 
560  /*******************************************************
561  * Daylight Savings Operations
562  *******************************************************/
563  if (!strcmp(name, DaylightSP.name))
564  {
565  IUUpdateSwitch(&DaylightSP, states, names, n);
566 
567  if (driver->setDaylightSaving(DaylightS[0].s == ISS_ON))
568  DaylightSP.s = IPS_OK;
569  else
570  DaylightSP.s = IPS_ALERT;
571 
572  IDSetSwitch(&DaylightSP, nullptr);
573  return true;
574  }
575 
576  /*******************************************************
577  * Meridian Action Operations
578  *******************************************************/
579  if (!strcmp(name, MeridianActionSP.name))
580  {
581  IUUpdateSwitch(&MeridianActionSP, states, names, n);
582  MeridianActionSP.s = (driver->setMeridianBehavior(static_cast<IOP_MB_STATE>(IUFindOnSwitchIndex(&MeridianActionSP)),
583  MeridianLimitN[0].value)) ? IPS_OK : IPS_ALERT;
584  if (MeridianActionSP.s == IPS_OK)
585  {
586  LOGF_INFO("Mount Meridian Behavior: When mount reaches %.f degrees past meridian, it will %s.",
587  MeridianLimitN[0].value, MeridianActionS[IOP_MB_STOP].s == ISS_ON ? "stop" : "flip");
588  }
589  IDSetSwitch(&MeridianActionSP, nullptr);
590  saveConfig(true, MeridianActionSP.name);
591  return true;
592  }
593 
594  /* v3.0 PEC add controls and calls to the driver */
595  if (!strcmp(name, PECStateSP.name))
596  {
597  IUUpdateSwitch(&PECStateSP, states, names, n);
598 
599  if(PECStateS[PEC_OFF].s == ISS_ON)
600  {
601  // PEC OFF
602  if(isTraining)
603  {
604  // Training check
605  LOGF_WARN("Mount PEC busy recording, %d s", PECTime);
606  }
607  else
608  {
609  driver->setPECEnabled(false);
610  PECStateSP.s = IPS_OK;
611  LOG_INFO("Disabling PEC Chip");
612  }
613  }
614  else
615  {
616  // PEC ON
617  if (GetPECDataStatus(true))
618  {
619  // Data Check
620  driver->setPECEnabled(true);
621  PECStateSP.s = IPS_BUSY;
622  LOG_INFO("Enabling PEC Chip");
623  }
624  }
625  IDSetSwitch(&PECStateSP, nullptr);
626  return true;
627  }
628 
629  /* v3.0 PEC add Training Controls to the Driver */
630  if (!strcmp(name, PECTrainingSP.name))
631  {
632  IUUpdateSwitch(&PECTrainingSP, states, names, n);
633  if(isTraining)
634  {
635  // Check if already training
636  if(IUFindOnSwitchIndex(&PECTrainingSP) == 1)
637  {
638  // Train Check Status
639  LOGF_WARN("Mount PEC busy recording, %d s", PECTime);
640  }
641 
642  if(IUFindOnSwitchIndex(&PECTrainingSP) == 0)
643  {
644  // Train Cancel
645  driver->setPETEnabled(false);
646  isTraining = false;
647  PECTrainingSP.s = IPS_ALERT;
648  LOG_WARN("PEC Training cancelled by user, chip disabled");
649  }
650  }
651  else
652  {
653  if(IUFindOnSwitchIndex(&PECTrainingSP) == 0)
654  {
655  if(TrackState == SCOPE_TRACKING)
656  {
657  // Train if tracking /guiding
658  driver->setPETEnabled(true);
659  isTraining = true;
660  PECTime = 0;
661  PECTrainingSP.s = IPS_BUSY;
662  LOG_INFO("PEC recording started...");
663  }
664  else
665  {
666  LOG_WARN("PEC Training only possible while guiding");
667  PECTrainingSP.s = IPS_IDLE;
668  }
669  }
670  if(IUFindOnSwitchIndex(&PECTrainingSP) == 1)
671  {
672  // Train Status
673  GetPECDataStatus(true);
674  }
675  }
676  IDSetSwitch(&PECTrainingSP, nullptr);
677  return true;
678  }
679  }
680 
681  return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
682 }
683 
685 {
686  bool rc = false;
687 
688  IOPInfo newInfo;
689 
690  if (isSimulation())
691  mountSim();
692 
693  rc = driver->getStatus(&newInfo);
694 
695  if (rc)
696  {
697  if (IUFindOnSwitchIndex(&GPSStatusSP) != newInfo.gpsStatus)
698  {
699  IUResetSwitch(&GPSStatusSP);
700  GPSStatusS[newInfo.gpsStatus].s = ISS_ON;
701  IDSetSwitch(&GPSStatusSP, nullptr);
702  }
703 
704  if (IUFindOnSwitchIndex(&TimeSourceSP) != newInfo.timeSource)
705  {
706  IUResetSwitch(&TimeSourceSP);
707  TimeSourceS[newInfo.timeSource].s = ISS_ON;
708  IDSetSwitch(&TimeSourceSP, nullptr);
709  }
710 
711  if (IUFindOnSwitchIndex(&HemisphereSP) != newInfo.hemisphere)
712  {
713  IUResetSwitch(&HemisphereSP);
714  HemisphereS[newInfo.hemisphere].s = ISS_ON;
715  IDSetSwitch(&HemisphereSP, nullptr);
716  }
717 
718  if (IUFindOnSwitchIndex(&SlewRateSP) != newInfo.slewRate - 1)
719  {
720  IUResetSwitch(&SlewRateSP);
721  SlewRateS[newInfo.slewRate - 1].s = ISS_ON;
722  IDSetSwitch(&SlewRateSP, nullptr);
723  }
724 
725  switch (newInfo.systemStatus)
726  {
727  case ST_STOPPED:
728  TrackModeSP.s = IPS_IDLE;
729  TrackState = SCOPE_IDLE;
730  break;
731  case ST_PARKED:
732  TrackModeSP.s = IPS_IDLE;
733  TrackState = SCOPE_PARKED;
734  if (!isParked())
735  SetParked(true);
736  break;
737  case ST_HOME:
738  TrackModeSP.s = IPS_IDLE;
739  TrackState = SCOPE_IDLE;
740  break;
741  case ST_SLEWING:
743  if (TrackState != SCOPE_SLEWING && TrackState != SCOPE_PARKING)
744  TrackState = SCOPE_SLEWING;
745  break;
746  case ST_TRACKING_PEC_OFF:
747  case ST_TRACKING_PEC_ON:
748  case ST_GUIDING:
750  setPECState(newInfo.systemStatus == ST_TRACKING_PEC_ON ? PEC_ON : PEC_OFF);
751  TrackModeSP.s = IPS_BUSY;
752  TrackState = SCOPE_TRACKING;
753  if (scopeInfo.systemStatus == ST_SLEWING)
754  LOG_INFO("Slew complete, tracking...");
755  else if (scopeInfo.systemStatus == ST_MERIDIAN_FLIPPING)
756  LOG_INFO("Meridian flip complete, tracking...");
757  break;
758  }
759 
760  if (IUFindOnSwitchIndex(&TrackModeSP) != newInfo.trackRate)
761  {
762  IUResetSwitch(&TrackModeSP);
763  TrackModeS[newInfo.trackRate].s = ISS_ON;
764  IDSetSwitch(&TrackModeSP, nullptr);
765  }
766 
767  scopeInfo = newInfo;
768  }
769 
770  /* v3.0 Monitor PEC Training */
771  if (isTraining)
772  {
773  if (TrackState == SCOPE_TRACKING)
774  {
775  if(GetPECDataStatus(false))
776  {
777  LOGF_INFO("%d second worm cycle recorded", PECTime);
778  PECTrainingSP.s = IPS_OK;
779  isTraining = false;
780  }
781  else
782  {
783  PECTime = PECTime + 1 * getCurrentPollingPeriod() / 1000;
784  char PECText[MAXINDILABEL] = {0};
785  snprintf(PECText, MAXINDILABEL, "Recording: %d s", PECTime);
786  IUSaveText(&PECInfoT[0], PECText);
787  }
788  }
789  else
790  {
791  driver->setPETEnabled(false);
792  PECTrainingSP.s = IPS_ALERT;
793  LOGF_ERROR("Tracking error, recording cancelled %d s", PECTime);
794  IUSaveText(&PECInfoT[0], "Cancelled");
795  }
796  IDSetText(&PECInfoTP, nullptr);
797  IDSetSwitch(&PECTrainingSP, nullptr);
798  }
799  // End Mod */
800 
801  IOP_PIER_STATE pierState = IOP_PIER_UNKNOWN;
802  IOP_CW_STATE cwState = IOP_CW_NORMAL;
803 
804  double previousRA = currentRA, previousDE = currentDEC;
805  rc = driver->getCoords(&currentRA, &currentDEC, &pierState, &cwState);
806  if (rc)
807  {
808  // 2021.11.30 JM: This is a hack to circumvent a bug in iOptorn firmware
809  // the "system status" bit is set to SLEWING even when parking is done (2), it never
810  // changes to (6) which indicates it has parked. So we use a counter to check if there
811  // is no longer any motion.
812  if (TrackState == SCOPE_PARKING)
813  {
814  if (std::abs(previousRA - currentRA) < 0.01 && std::abs(previousDE - currentDEC) < 0.01)
815  {
816  m_ParkingCounter++;
817  if (m_ParkingCounter >= MAX_PARK_COUNTER)
818  {
819  m_ParkingCounter = 0;
820  SetTrackEnabled(false);
821  SetParked(true);
822  }
823  }
824  }
825  if (pierState == IOP_PIER_UNKNOWN)
826  setPierSide(PIER_UNKNOWN);
827  else
828  setPierSide(pierState == IOP_PIER_EAST ? PIER_EAST : PIER_WEST);
829 
830  if (IUFindOnSwitchIndex(&CWStateSP) != cwState)
831  {
832  IUResetSwitch(&CWStateSP);
833  CWStateS[cwState].s = ISS_ON;
834  IDSetSwitch(&CWStateSP, nullptr);
835  }
836 
837  NewRaDec(currentRA, currentDEC);
838  }
839 
840  return rc;
841 }
842 
843 bool IOptronV3::Goto(double ra, double de)
844 {
845  targetRA = ra;
846  targetDEC = de;
847  char RAStr[64] = {0}, DecStr[64] = {0};
848 
849  fs_sexa(RAStr, targetRA, 2, 3600);
850  fs_sexa(DecStr, targetDEC, 2, 3600);
851 
852  if (driver->setRA(ra) == false || driver->setDE(de) == false)
853  {
854  LOG_ERROR("Error setting RA/DEC.");
855  return false;
856  }
857 
858  bool rc = false;
859  if (IUFindOnSwitchIndex(&SlewModeSP) == IOP_CW_NORMAL)
860  rc = driver->slewNormal();
861  else
862  rc = driver->slewCWUp();
863 
864  if (rc == false)
865  {
866  LOG_ERROR("Failed to slew.");
867  return false;
868  }
869 
870  TrackState = SCOPE_SLEWING;
871 
872  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
873  return true;
874 }
875 
876 bool IOptronV3::Sync(double ra, double de)
877 {
878  if (driver->setRA(ra) == false || driver->setDE(de) == false)
879  {
880  LOG_ERROR("Error setting RA/DEC.");
881  return false;
882  }
883 
884  if (driver->sync() == false)
885  {
886  LOG_ERROR("Failed to sync.");
887  }
888 
889  EqNP.s = IPS_OK;
890 
891  currentRA = ra;
892  currentDEC = de;
893 
894  NewRaDec(currentRA, currentDEC);
895 
896  return true;
897 }
898 
900 {
901  return driver->abort();
902 }
903 
905 {
906  // if (firmwareInfo.Model.find("CEM") == std::string::npos &&
907  // firmwareInfo.Model.find("iEQ45 Pro") == std::string::npos &&
908  // firmwareInfo.Model.find("iEQ35") == std::string::npos)
909  // {
910  // LOG_ERROR("Parking is not supported in this mount model.");
911  // return false;
912  // }
913 
914  if (driver->park())
915  {
916  TrackState = SCOPE_PARKING;
917  m_ParkingCounter = 0;
918  LOG_INFO("Parking is in progress...");
919 
920  return true;
921  }
922  else
923  return false;
924 }
925 
927 {
928  // if (firmwareInfo.Model.find("CEM") == std::string::npos &&
929  // firmwareInfo.Model.find("iEQ45 Pro") == std::string::npos &&
930  // firmwareInfo.Model.find("iEQ35") == std::string::npos)
931  // {
932  // LOG_ERROR("Unparking is not supported in this mount model.");
933  // return false;
934  // }
935 
936  if (driver->unpark())
937  {
938  SetParked(false);
939  TrackState = SCOPE_IDLE;
940  return true;
941  }
942  else
943  return false;
944 }
945 
947 {
948  driver->setSimulation(isSimulation());
949 
950  if (driver->checkConnection(PortFD) == false)
951  return false;
952 
953  return true;
954 }
955 
956 bool IOptronV3::updateTime(ln_date *utc, double utc_offset)
957 {
958  bool rc1 = driver->setUTCDateTime(ln_get_julian_day(utc));
959 
960  bool rc2 = driver->setUTCOffset(utc_offset * 60);
961 
962  return (rc1 && rc2);
963 }
964 
965 bool IOptronV3::updateLocation(double latitude, double longitude, double elevation)
966 {
967  INDI_UNUSED(elevation);
968 
969  if (longitude > 180)
970  longitude -= 360;
971 
972  if (driver->setLongitude(longitude) == false)
973  {
974  LOG_ERROR("Failed to set longitude.");
975  return false;
976  }
977 
978  if (driver->setLatitude(latitude) == false)
979  {
980  LOG_ERROR("Failed to set longitude.");
981  return false;
982  }
983 
984  char l[32] = {0}, L[32] = {0};
985  fs_sexa(l, latitude, 3, 3600);
986  fs_sexa(L, longitude, 4, 3600);
987 
988  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
989 
990  return true;
991 }
992 
993 void IOptronV3::debugTriggered(bool enable)
994 {
995  driver->setDebug(enable);
996 }
997 
999 {
1000  driver->setSimulation(enable);
1001 }
1002 
1004 {
1005  if (TrackState == SCOPE_PARKED)
1006  {
1007  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
1008  return false;
1009  }
1010 
1011  switch (command)
1012  {
1013  case MOTION_START:
1014  if (driver->startMotion(dir == DIRECTION_NORTH ? IOP_N : IOP_S) == false)
1015  {
1016  LOG_ERROR("Error setting N/S motion direction.");
1017  return false;
1018  }
1019  else
1020  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_NORTH) ? "North" : "South");
1021  break;
1022 
1023  case MOTION_STOP:
1024  if (driver->stopMotion(dir == DIRECTION_NORTH ? IOP_N : IOP_S) == false)
1025  {
1026  LOG_ERROR("Error stopping N/S motion.");
1027  return false;
1028  }
1029  else
1030  LOGF_INFO("%s motion stopped.", (dir == DIRECTION_NORTH) ? "North" : "South");
1031  break;
1032  }
1033 
1034  return true;
1035 }
1036 
1038 {
1039  if (TrackState == SCOPE_PARKED)
1040  {
1041  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
1042  return false;
1043  }
1044 
1045  switch (command)
1046  {
1047  case MOTION_START:
1048  if (driver->startMotion(dir == DIRECTION_WEST ? IOP_W : IOP_E) == false)
1049  {
1050  LOG_ERROR("Error setting N/S motion direction.");
1051  return false;
1052  }
1053  else
1054  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_WEST) ? "West" : "East");
1055  break;
1056 
1057  case MOTION_STOP:
1058  if (driver->stopMotion(dir == DIRECTION_WEST ? IOP_W : IOP_E) == false)
1059  {
1060  LOG_ERROR("Error stopping W/E motion.");
1061  return false;
1062  }
1063  else
1064  LOGF_INFO("%s motion stopped.", (dir == DIRECTION_WEST) ? "West" : "East");
1065  break;
1066  }
1067 
1068  return true;
1069 }
1070 
1072 {
1073  bool rc = driver->startGuide(IOP_N, (uint32_t)ms);
1074  return (rc ? IPS_OK : IPS_ALERT);
1075 }
1076 
1078 {
1079  bool rc = driver->startGuide(IOP_S, (uint32_t)ms);
1080  return (rc ? IPS_OK : IPS_ALERT);
1081 }
1082 
1084 {
1085  bool rc = driver->startGuide(IOP_E, (uint32_t)ms);
1086  return (rc ? IPS_OK : IPS_ALERT);
1087 }
1088 
1090 {
1091  bool rc = driver->startGuide(IOP_W, (uint32_t)ms);
1092  return (rc ? IPS_OK : IPS_ALERT);
1093 }
1094 
1095 bool IOptronV3::SetSlewRate(int index)
1096 {
1097  IOP_SLEW_RATE rate = static_cast<IOP_SLEW_RATE>(index);
1098  return driver->setSlewRate(rate);
1099 }
1100 
1102 {
1104 
1105  IUSaveConfigSwitch(fp, &SlewModeSP);
1106  IUSaveConfigSwitch(fp, &DaylightSP);
1107 
1108  IUSaveConfigSwitch(fp, &MeridianActionSP);
1109  IUSaveConfigNumber(fp, &MeridianLimitNP);
1110 
1111  return true;
1112 }
1113 
1115 {
1116  static struct timeval ltv;
1117  struct timeval tv;
1118  double dt, da, dx;
1119  int nlocked;
1120 
1121  /* update elapsed time since last poll, don't presume exactly POLLMS */
1122  gettimeofday(&tv, nullptr);
1123 
1124  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1125  ltv = tv;
1126 
1127  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1128  ltv = tv;
1129  double currentSlewRate = Driver::IOP_SLEW_RATES[IUFindOnSwitchIndex(&SlewRateSP)] * TRACKRATE_SIDEREAL / 3600.0;
1130  da = currentSlewRate * dt;
1131 
1132  /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
1133  switch (TrackState)
1134  {
1135  case SCOPE_IDLE:
1136  currentRA += (TrackRateN[AXIS_RA].value / 3600.0 * dt) / 15.0;
1138  break;
1139 
1140  case SCOPE_TRACKING:
1141  if (TrackModeS[TR_CUSTOM].s == ISS_ON)
1142  {
1143  currentRA += ( ((TRACKRATE_SIDEREAL / 3600.0) - (TrackRateN[AXIS_RA].value / 3600.0)) * dt) / 15.0;
1144  currentDEC += ( (TrackRateN[AXIS_DE].value / 3600.0) * dt);
1145  }
1146  break;
1147 
1148  case SCOPE_SLEWING:
1149  case SCOPE_PARKING:
1150  /* slewing - nail it when both within one pulse @ SLEWRATE */
1151  nlocked = 0;
1152 
1153  dx = targetRA - currentRA;
1154 
1155  // Take shortest path
1156  if (fabs(dx) > 12)
1157  dx *= -1;
1158 
1159  if (fabs(dx) <= da)
1160  {
1161  currentRA = targetRA;
1162  nlocked++;
1163  }
1164  else if (dx > 0)
1165  currentRA += da / 15.;
1166  else
1167  currentRA -= da / 15.;
1168 
1169  if (currentRA < 0)
1170  currentRA += 24;
1171  else if (currentRA > 24)
1172  currentRA -= 24;
1173 
1174  dx = targetDEC - currentDEC;
1175  if (fabs(dx) <= da)
1176  {
1177  currentDEC = targetDEC;
1178  nlocked++;
1179  }
1180  else if (dx > 0)
1181  currentDEC += da;
1182  else
1183  currentDEC -= da;
1184 
1185  if (nlocked == 2)
1186  {
1187  if (TrackState == SCOPE_SLEWING)
1188  driver->setSimSytemStatus(ST_TRACKING_PEC_OFF);
1189  else
1190  driver->setSimSytemStatus(ST_PARKED);
1191  }
1192 
1193  break;
1194 
1195  default:
1196  break;
1197  }
1198 
1199  driver->setSimRA(currentRA);
1200  driver->setSimDE(currentDEC);
1201 }
1202 
1204 {
1206  INDI::IHorizontalCoordinates horizontalCoords {0, 0};
1207  INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
1208  double parkAZ = horizontalCoords.azimuth;
1209  double parkAlt = horizontalCoords.altitude;
1210  char AzStr[16], AltStr[16];
1211  fs_sexa(AzStr, parkAZ, 2, 3600);
1212  fs_sexa(AltStr, parkAlt, 2, 3600);
1213  LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
1214  SetAxis1Park(parkAZ);
1215  SetAxis2Park(parkAlt);
1216  driver->setParkAz(parkAZ);
1217  driver->setParkAlt(parkAlt);
1218  return true;
1219 }
1220 
1222 {
1223  // By defualt azimuth 0
1224  SetAxis1Park(0);
1225  // Altitude = latitude of observer
1226  SetAxis2Park(LocationN[LOCATION_LATITUDE].value);
1227  driver->setParkAz(0);
1228  driver->setParkAlt(LocationN[LOCATION_LATITUDE].value);
1229  return true;
1230 }
1231 
1232 bool IOptronV3::SetTrackMode(uint8_t mode)
1233 {
1234  IOP_TRACK_RATE rate = static_cast<IOP_TRACK_RATE>(mode);
1235 
1236  if (driver->setTrackMode(rate))
1237  return true;
1238 
1239  return false;
1240 }
1241 
1242 bool IOptronV3::SetTrackRate(double raRate, double deRate)
1243 {
1244  INDI_UNUSED(deRate);
1245 
1246  // Convert to arcsecs/s to rate
1247  double ieqRARate = raRate / TRACKRATE_SIDEREAL;
1248 
1249  if (ieqRARate < 0.1 || ieqRARate > 1.9)
1250  {
1251  LOG_ERROR("Rate is outside permitted limits of 0.1 to 1.9 sidereal rate (1.504 to 28.578 arcsecs/s)");
1252  return false;
1253  }
1254 
1255 
1256  if (driver->setCustomRATrackRate(ieqRARate))
1257  return true;
1258 
1259  return false;
1260 }
1261 
1262 bool IOptronV3::SetTrackEnabled(bool enabled)
1263 {
1264  if (enabled)
1265  {
1266  // If we are engaging tracking, let us first set tracking mode, and if we have custom mode, then tracking rate.
1267  // NOTE: Is this the correct order? or should tracking be switched on first before making these changes? Need to test.
1268  SetTrackMode(IUFindOnSwitchIndex(&TrackModeSP));
1269  if (TrackModeS[TR_CUSTOM].s == ISS_ON)
1270  SetTrackRate(TrackRateN[AXIS_RA].value, TrackRateN[AXIS_DE].value);
1271  }
1272 
1273  return driver->setTrackEnabled(enabled);
1274 }
1275 
1276 /* v3.0 PEC add data status to the Driver */
1277 bool IOptronV3::GetPECDataStatus(bool enabled)
1278 {
1279  if(driver->getPETEnabled(true))
1280  {
1281  if (enabled)
1282  {
1283  IUSaveText(&PECInfoT[0], "Recorded");
1284  IDSetText(&PECInfoTP, nullptr);
1285  LOG_INFO("Mount PEC Chip Ready and Trained");
1286  }
1287  return true;
1288  }
1289  else
1290  {
1291  if (enabled)
1292  {
1293  IUSaveText(&PECInfoT[0], "None");
1294  IDSetText(&PECInfoTP, nullptr);
1295  LOG_INFO("Mount PEC Chip Needs Training");
1296  }
1297  }
1298  return false;
1299 }
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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.
static const uint16_t IOP_SLEW_RATES[]
virtual bool Goto(double ra, double de) override
Move the scope to the supplied RA and DEC coordinates.
Definition: ioptronv3.cpp:843
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
Definition: ioptronv3.cpp:1077
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: ioptronv3.cpp:1101
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition: ioptronv3.cpp:1003
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition: ioptronv3.cpp:1242
virtual const char * getDefaultName() override
Definition: ioptronv3.cpp:72
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition: ioptronv3.cpp:1232
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: ioptronv3.cpp:433
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition: ioptronv3.cpp:1037
virtual bool Park() override
Park the telescope to its home position.
Definition: ioptronv3.cpp:904
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition: ioptronv3.cpp:926
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
Definition: ioptronv3.cpp:998
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: ioptronv3.cpp:1203
void mountSim()
Definition: ioptronv3.cpp:1114
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
Definition: ioptronv3.cpp:1071
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition: ioptronv3.cpp:965
virtual bool Sync(double ra, double de) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition: ioptronv3.cpp:876
virtual bool Handshake() override
perform handshake with device to check communication
Definition: ioptronv3.cpp:946
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition: ioptronv3.cpp:1262
virtual bool ReadScopeStatus() override
Read telescope status.
Definition: ioptronv3.cpp:684
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: ioptronv3.cpp:480
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: ioptronv3.cpp:1221
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition: ioptronv3.cpp:220
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition: ioptronv3.cpp:993
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition: ioptronv3.cpp:956
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
Definition: ioptronv3.cpp:1083
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
Definition: ioptronv3.cpp:899
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition: ioptronv3.cpp:77
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
Definition: ioptronv3.cpp:1089
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition: ioptronv3.cpp:1095
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.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
#define currentDEC
Definition: ieq45.cpp:48
#define currentRA
Definition: ieq45.cpp:47
double ra
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 IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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 IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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
#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 LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MOUNTINFO_TAB
Definition: ioptronv3.cpp:38
unsigned int DBG_SCOPE
Definition: lx200driver.cpp:53
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition: libastro.cpp:140
Encapsulates classes and structures required for iOptron Command Set v3 implementation.
IOP_HOME_OPERATION
@ IOP_GOTO_HOME
@ IOP_FIND_HOME
@ IOP_SET_HOME
@ TS_CONTROLLER
@ FW_CONTROLLER
@ IOP_CW_NORMAL
@ IOP_PIER_EAST
@ IOP_PIER_UNKNOWN
@ ST_TRACKING_PEC_ON
@ ST_TRACKING_PEC_OFF
@ ST_MERIDIAN_FLIPPING
const char * getDeviceName()
bool isParked(const int fd)
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
IOP_GPS_STATUS gpsStatus
IOP_HEMISPHERE hemisphere
IOP_SYSTEM_STATUS systemStatus
IOP_TRACK_RATE trackRate
IOP_SLEW_RATE slewRate
IOP_TIME_SOURCE timeSource