Instrument Neutral Distributed Interface INDI 2.2.5
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
36using 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.
42static std::unique_ptr<IOptronV3> scope(new IOptronV3());
43
44/* Constructor */
77
79{
80 return "iOptronV3";
81}
82
84{
86
87 // Slew Rates
88 SlewRateSP[0].setLabel("1x");
89 SlewRateSP[1].setLabel("2x");
90 SlewRateSP[2].setLabel("8x");
91 SlewRateSP[3].setLabel("16x");
92 SlewRateSP[4].setLabel("64x");
93 SlewRateSP[5].setLabel("128x");
94 SlewRateSP[6].setLabel("256x");
95 SlewRateSP[7].setLabel("512x");
96 SlewRateSP[8].setLabel("MAX");
98 // 64x is the default
100
101 // Max is the default
103
104 /* Firmware */
105 IUFillText(&FirmwareT[FW_MODEL], "Model", "", nullptr);
106 IUFillText(&FirmwareT[FW_BOARD], "Board", "", nullptr);
107 IUFillText(&FirmwareT[FW_CONTROLLER], "Controller", "", nullptr);
108 IUFillText(&FirmwareT[FW_RA], "RA", "", nullptr);
109 IUFillText(&FirmwareT[FW_DEC], "DEC", "", nullptr);
110 IUFillTextVector(&FirmwareTP, FirmwareT, 5, getDeviceName(), "Firmware Info", "", MOUNTINFO_TAB, IP_RO, 0,
111 IPS_IDLE);
112
113 /* Tracking Mode */
114 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
115 AddTrackMode("TRACK_LUNAR", "Lunar");
116 AddTrackMode("TRACK_SOLAR", "Solar");
117 AddTrackMode("TRACK_KING", "King");
118 AddTrackMode("TRACK_CUSTOM", "Custom");
119
120 /* GPS Status */
121 IUFillSwitch(&GPSStatusS[GPS_OFF], "Off", "", ISS_ON);
122 IUFillSwitch(&GPSStatusS[GPS_ON], "On", "", ISS_OFF);
123 IUFillSwitch(&GPSStatusS[GPS_DATA_OK], "Data OK", "", ISS_OFF);
124 IUFillSwitchVector(&GPSStatusSP, GPSStatusS, 3, getDeviceName(), "GPS_STATUS", "GPS", MOUNTINFO_TAB, IP_RO,
126
127 /* Time Source */
128 IUFillSwitch(&TimeSourceS[TS_RS232], "RS232", "", ISS_ON);
129 IUFillSwitch(&TimeSourceS[TS_CONTROLLER], "Controller", "", ISS_OFF);
130 IUFillSwitch(&TimeSourceS[TS_GPS], "GPS", "", ISS_OFF);
131 IUFillSwitchVector(&TimeSourceSP, TimeSourceS, 3, getDeviceName(), "TIME_SOURCE", "Time Source", MOUNTINFO_TAB,
133
134 /* Hemisphere */
135 IUFillSwitch(&HemisphereS[HEMI_SOUTH], "South", "", ISS_OFF);
136 IUFillSwitch(&HemisphereS[HEMI_NORTH], "North", "", ISS_ON);
137 IUFillSwitchVector(&HemisphereSP, HemisphereS, 2, getDeviceName(), "HEMISPHERE", "Hemisphere", MOUNTINFO_TAB, IP_RO,
139
140 /* v3.0 Create PEC Training switches */
141 // PEC Training
142 IUFillSwitch(&PECTrainingS[0], "PEC_Recording", "Record", ISS_OFF);
143 IUFillSwitch(&PECTrainingS[1], "PEC_Status", "Status", ISS_OFF);
144 IUFillSwitchVector(&PECTrainingSP, PECTrainingS, 2, getDeviceName(), "PEC_TRAINING", "Training", MOTION_TAB, IP_RW,
145 ISR_ATMOST1, 0,
146 IPS_IDLE);
147
148 // Create PEC Training Information */
149 IUFillText(&PECInfoT[0], "PEC_INFO", "Status", "");
150 IUFillTextVector(&PECInfoTP, PECInfoT, 1, getDeviceName(), "PEC_INFOS", "Data", MOTION_TAB,
151 IP_RO, 60, IPS_IDLE);
152 // End Mod */
153
154 /* How fast do we guide compared to sidereal rate */
155 IUFillNumber(&GuideRateN[0], "RA_GUIDE_RATE", "x Sidereal", "%g", 0.01, 0.9, 0.1, 0.5);
156 IUFillNumber(&GuideRateN[1], "DE_GUIDE_RATE", "x Sidereal", "%g", 0.1, 0.99, 0.1, 0.5);
157 IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0,
158 IPS_IDLE);
159
160
161 /* Slew Mode. Normal vs Counter Weight up */
162 IUFillSwitch(&SlewModeS[IOP_CW_NORMAL], "Normal", "Normal", ISS_ON);
163 IUFillSwitch(&SlewModeS[IOP_CW_UP], "Counterweight UP", "Counterweight up", ISS_OFF);
164 IUFillSwitchVector(&SlewModeSP, SlewModeS, 2, getDeviceName(), "Slew Type", "Slew Type", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
165 IPS_IDLE);
166
167 /* Daylight Savings */
168 IUFillSwitch(&DaylightS[0], "ON", "ON", ISS_OFF);
169 IUFillSwitch(&DaylightS[1], "OFF", "OFF", ISS_ON);
170 IUFillSwitchVector(&DaylightSP, DaylightS, 2, getDeviceName(), "DaylightSaving", "Daylight Savings", SITE_TAB, IP_RW,
171 ISR_1OFMANY, 0,
172 IPS_IDLE);
173
174 /* Counter Weight State */
175 IUFillSwitch(&CWStateS[IOP_CW_NORMAL], "Normal", "Normal", ISS_ON);
176 IUFillSwitch(&CWStateS[IOP_CW_UP], "Up", "Up", ISS_OFF);
177 IUFillSwitchVector(&CWStateSP, CWStateS, 2, getDeviceName(), "CWState", "Counter weights", MOTION_TAB, IP_RO, ISR_1OFMANY,
178 0, IPS_IDLE);
179
180 /* Meridian Behavior */
181 MeridianActionSP[IOP_MB_STOP].fill("IOP_MB_STOP", "Stop", ISS_ON);
182 MeridianActionSP[IOP_MB_FLIP].fill("IOP_MB_FLIP", "Flip", ISS_OFF);
183 MeridianActionSP.fill(getDeviceName(), "MERIDIAN_ACTION", "Action", MB_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
184 MeridianActionSP.load();
185
186 /* Meridian Limit */
187 MeridianLimitNP[0].fill("VALUE", "Degrees", "%.f", 0, 10, 1, 0);
188 MeridianLimitNP.fill(getDeviceName(), "MERIDIAN_LIMIT", "Limit", MB_TAB, IP_RW, 60, IPS_IDLE);
189 MeridianLimitNP.load();
190
191 if (strstr(getDeviceName(), "iMate"))
192 serialConnection->setDefaultPort("/dev/ttyS7");
193
194 // Baud rates.
195 // 230400 for 120
196 // 115000 for everything else
197 if (strstr(getDeviceName(), "120"))
199 else
201
202 // Default WiFi connection parameters
203 tcpConnection->setDefaultHost("10.10.100.254");
205
207
209
211
213
215
217 currentDEC = LocationNP[LOCATION_LATITUDE].getValue() > 0 ? 90 : -90;
218 driver->setSimLongLat(LocationNP[LOCATION_LONGITUDE].getValue() > 180 ? LocationNP[LOCATION_LONGITUDE].getValue() - 360 :
220
221 return true;
222}
223
225{
227
228 if (isConnected())
229 {
230 /* v3.0 Create PEC switches */
231 defineProperty(&PECTrainingSP);
232 defineProperty(&PECInfoTP);
233 // End Mod */
234
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 /* v3.0 Delete PEC switches */
253 deleteProperty(PECTrainingSP.name);
254 deleteProperty(PECInfoTP.name);
255 // End Mod*/
256
257 deleteProperty(GuideRateNP.name);
258
259 deleteProperty(FirmwareTP.name);
260 deleteProperty(GPSStatusSP.name);
261 deleteProperty(TimeSourceSP.name);
262 deleteProperty(HemisphereSP.name);
263 deleteProperty(SlewModeSP.name);
264 deleteProperty(DaylightSP.name);
265 deleteProperty(CWStateSP.name);
266
267 deleteProperty(MeridianActionSP);
268 deleteProperty(MeridianLimitNP);
269 }
270
272
273 return true;
274}
275
276void IOptronV3::getStartupData()
277{
278 LOG_DEBUG("Getting firmware data...");
279 if (driver->getFirmwareInfo(&firmwareInfo))
280 {
281 IUSaveText(&FirmwareT[0], firmwareInfo.Model.c_str());
282 IUSaveText(&FirmwareT[1], firmwareInfo.MainBoardFirmware.c_str());
283 IUSaveText(&FirmwareT[2], firmwareInfo.ControllerFirmware.c_str());
284 IUSaveText(&FirmwareT[3], firmwareInfo.RAFirmware.c_str());
285 IUSaveText(&FirmwareT[4], firmwareInfo.DEFirmware.c_str());
286
287 FirmwareTP.s = IPS_OK;
288 IDSetText(&FirmwareTP, nullptr);
289 }
290
291 LOG_DEBUG("Getting guiding rate...");
292 double RARate = 0, DERate = 0;
293 if (driver->getGuideRate(&RARate, &DERate))
294 {
295 GuideRateN[RA_AXIS].value = RARate;
296 GuideRateN[DEC_AXIS].value = DERate;
297 IDSetNumber(&GuideRateNP, nullptr);
298 }
299
300 int utcOffsetMinutes = 0;
301 bool dayLightSavings = false;
302 double JD = 0;
303 if (driver->getUTCDateTime(&JD, &utcOffsetMinutes, &dayLightSavings))
304 {
305 time_t utc_time;
306 ln_get_timet_from_julian(JD, &utc_time);
307
308 // UTC Time
309 char ts[32] = {0};
310 struct tm *utc;
311 utc = gmtime(&utc_time);
312 strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", utc);
313 TimeTP[UTC].setText(ts);
314 LOGF_INFO("Mount UTC: %s", ts);
315
316 // UTC Offset
317 char offset[8] = {0};
318 // 2021-05-12 JM: Account for daylight savings
319 if (dayLightSavings)
320 utcOffsetMinutes += 60;
321
322 snprintf(offset, 8, "%.2f", utcOffsetMinutes / 60.0);
323 TimeTP[OFFSET].setText(offset);
324 LOGF_INFO("Mount UTC Offset: %s", offset);
325
327 TimeTP.apply();
328
329 LOGF_INFO("Mount Daylight Savings: %s", dayLightSavings ? "ON" : "OFF");
330 DaylightS[0].s = dayLightSavings ? ISS_ON : ISS_OFF;
331 DaylightS[1].s = !dayLightSavings ? ISS_ON : ISS_OFF;
332 DaylightSP.s = IPS_OK;
333 IDSetSwitch(&DaylightSP, nullptr);
334 }
335
336 // Get Longitude and Latitude from mount
337 double longitude = 0, latitude = 0;
338 if (driver->getStatus(&scopeInfo))
339 {
340 LocationNP[LOCATION_LATITUDE].setValue(scopeInfo.latitude);
341 // Convert to INDI standard longitude (0 to 360 Eastward)
342 LocationNP[LOCATION_LONGITUDE].setValue((scopeInfo.longitude < 0) ? scopeInfo.longitude + 360 : scopeInfo.longitude);
344
346
347 char l[32] = {0}, L[32] = {0};
348 fs_sexa(l, LocationNP[LOCATION_LATITUDE].getValue(), 3, 3600);
349 fs_sexa(L, LocationNP[LOCATION_LONGITUDE].getValue(), 4, 3600);
350
351 LOGF_INFO("Mount Location: Lat %.32s - Long %.32s", l, L);
352
353 saveConfig(true, "GEOGRAPHIC_COORD");
354 }
355 else if (IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude) == 0 &&
356 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude) == 0)
357 {
358 LocationNP[LOCATION_LATITUDE].setValue(latitude);
359 LocationNP[LOCATION_LONGITUDE].setValue(longitude);
361
363 }
364
365 IOP_MB_STATE action;
366 uint8_t degrees = 0;
367 if (driver->getMeridianBehavior(action, degrees))
368 {
369 MeridianActionSP.reset();
370 MeridianActionSP[action].setState(ISS_ON);
371 MeridianActionSP.setState(IPS_OK);
372 MeridianLimitNP[0].setValue(degrees);
373
374 LOGF_INFO("Reading mount meridian behavior: When mount reaches %.f degrees past meridian, it will %s.",
375 MeridianLimitNP[0].getValue(), MeridianActionSP[IOP_MB_STOP].getState() == ISS_ON ? "stop" : "flip");
376 }
377
378 double parkAZ = LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180;
379 double parkAL = LocationNP[LOCATION_LATITUDE].getValue();
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
413 GetPECDataStatus(true);
414 break;
415
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
433bool IOptronV3::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
434{
435 // Check guider interface
436 if (GI::processNumber(dev, name, values, names, n))
437 return true;
438
439 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
440 {
441 // Guiding Rate
442 if (!strcmp(name, GuideRateNP.name))
443 {
444 IUUpdateNumber(&GuideRateNP, values, names, n);
445
446 if (driver->setGuideRate(GuideRateN[RA_AXIS].value, GuideRateN[DEC_AXIS].value))
447 GuideRateNP.s = IPS_OK;
448 else
449 GuideRateNP.s = IPS_ALERT;
450
451 IDSetNumber(&GuideRateNP, nullptr);
452
453 return true;
454 }
455
456 /****************************************
457 Meridian Flip Limit
458 *****************************************/
459 if (MeridianLimitNP.isNameMatch(name))
460 {
461 auto lastLimit = MeridianLimitNP[0].getValue();
462 MeridianLimitNP.update(values, names, n);
463 // Only update driver if there is an actual change
464 if (lastLimit != MeridianLimitNP[0].getValue())
465 {
466 MeridianLimitNP.setState(driver->setMeridianBehavior(static_cast<IOP_MB_STATE>(MeridianActionSP.findOnSwitchIndex()),
467 MeridianLimitNP[0].getValue()) ? IPS_OK : IPS_ALERT);
468 if (MeridianLimitNP.getState() == IPS_OK)
469 {
470 LOGF_INFO("Setting mount meridian behavior: When mount reaches %.f degrees past meridian, it will %s.",
471 MeridianLimitNP[0].getValue(), MeridianActionSP[IOP_MB_STOP].getState() == ISS_ON ? "stop" : "flip");
472 }
473 }
474 else
475 MeridianLimitNP.setState(IPS_OK);
476
477 MeridianLimitNP.apply();
478 saveConfig(MeridianLimitNP);
479 return true;
480 }
481 }
482
483 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
484}
485
486bool IOptronV3::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
487{
488 if (!strcmp(getDeviceName(), dev))
489 {
490 /*******************************************************
491 * Slew Mode Operations
492 *******************************************************/
493 if (!strcmp(name, SlewModeSP.name))
494 {
495 IUUpdateSwitch(&SlewModeSP, states, names, n);
496 SlewModeSP.s = IPS_OK;
497 IDSetSwitch(&SlewModeSP, nullptr);
498 return true;
499 }
500
501 /*******************************************************
502 * Daylight Savings Operations
503 *******************************************************/
504 if (!strcmp(name, DaylightSP.name))
505 {
506 IUUpdateSwitch(&DaylightSP, states, names, n);
507
508 if (driver->setDaylightSaving(DaylightS[0].s == ISS_ON))
509 DaylightSP.s = IPS_OK;
510 else
511 DaylightSP.s = IPS_ALERT;
512
513 IDSetSwitch(&DaylightSP, nullptr);
514 return true;
515 }
516
517 /*******************************************************
518 * Meridian Action Operations
519 *******************************************************/
520 if (MeridianActionSP.isNameMatch(name))
521 {
522 auto lastAction = MeridianActionSP.findOnSwitchIndex();
523 MeridianActionSP.update(states, names, n);
524
525 if (lastAction != MeridianActionSP.findOnSwitchIndex())
526 {
527 MeridianActionSP.setState(driver->setMeridianBehavior(static_cast<IOP_MB_STATE>(MeridianActionSP.findOnSwitchIndex()),
528 MeridianLimitNP[0].getValue()) ? IPS_OK : IPS_ALERT);
529 if (MeridianActionSP.getState() == IPS_OK)
530 {
531 LOGF_INFO("Setting mount meridian behavior: When mount reaches %.f degrees past meridian, it will %s.",
532 MeridianLimitNP[0].getValue(), MeridianActionSP[IOP_MB_STOP].getState() == ISS_ON ? "stop" : "flip");
533 }
534 }
535 else
536 MeridianActionSP.setState(IPS_OK);
537 MeridianActionSP.apply();
538 saveConfig(MeridianActionSP);
539 return true;
540 }
541
542 /* v3.0 PEC add controls and calls to the driver */
543 if (PECStateSP.isNameMatch(name))
544 {
545 PECStateSP.update(states, names, n);
546
547 if(PECStateSP[PEC_OFF].getState() == ISS_ON)
548 {
549 // PEC OFF
550 if(isTraining)
551 {
552 // Training check
553 LOGF_WARN("Mount PEC busy recording, %d s", PECTime);
554 }
555 else
556 {
557 driver->setPECEnabled(false);
559 LOG_INFO("Disabling PEC Chip");
560 }
561 }
562 else
563 {
564 // PEC ON
565 if (GetPECDataStatus(true))
566 {
567 // Data Check
568 driver->setPECEnabled(true);
570 LOG_INFO("Enabling PEC Chip");
571 }
572 }
574 return true;
575 }
576
577 /* v3.0 PEC add Training Controls to the Driver */
578 if (!strcmp(name, PECTrainingSP.name))
579 {
580 IUUpdateSwitch(&PECTrainingSP, states, names, n);
581 if(isTraining)
582 {
583 // Check if already training
584 if(IUFindOnSwitchIndex(&PECTrainingSP) == 1)
585 {
586 // Train Check Status
587 LOGF_WARN("Mount PEC busy recording, %d s", PECTime);
588 }
589
590 if(IUFindOnSwitchIndex(&PECTrainingSP) == 0)
591 {
592 // Train Cancel
593 driver->setPETEnabled(false);
594 isTraining = false;
595 PECTrainingSP.s = IPS_ALERT;
596 LOG_WARN("PEC Training cancelled by user, chip disabled");
597 }
598 }
599 else
600 {
601 if(IUFindOnSwitchIndex(&PECTrainingSP) == 0)
602 {
604 {
605 // Train if tracking /guiding
606 driver->setPETEnabled(true);
607 isTraining = true;
608 PECTime = 0;
609 PECTrainingSP.s = IPS_BUSY;
610 LOG_INFO("PEC recording started...");
611 }
612 else
613 {
614 LOG_WARN("PEC Training only possible while guiding");
615 PECTrainingSP.s = IPS_IDLE;
616 }
617 }
618 if(IUFindOnSwitchIndex(&PECTrainingSP) == 1)
619 {
620 // Train Status
621 GetPECDataStatus(true);
622 }
623 }
624 IDSetSwitch(&PECTrainingSP, nullptr);
625 return true;
626 }
627 }
628
629 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
630}
631
633{
634 bool rc = false;
635
636 IOPInfo newInfo;
637
638 if (isSimulation())
639 mountSim();
640
641 // Do not query mount if parked already.
643 return true;
644
645 rc = driver->getStatus(&newInfo);
646
647 if (rc)
648 {
649 if (IUFindOnSwitchIndex(&GPSStatusSP) != newInfo.gpsStatus)
650 {
651 IUResetSwitch(&GPSStatusSP);
652 GPSStatusS[newInfo.gpsStatus].s = ISS_ON;
653 IDSetSwitch(&GPSStatusSP, nullptr);
654 }
655
656 if (IUFindOnSwitchIndex(&TimeSourceSP) != newInfo.timeSource)
657 {
658 IUResetSwitch(&TimeSourceSP);
659 TimeSourceS[newInfo.timeSource].s = ISS_ON;
660 IDSetSwitch(&TimeSourceSP, nullptr);
661 }
662
663 if (IUFindOnSwitchIndex(&HemisphereSP) != newInfo.hemisphere)
664 {
665 IUResetSwitch(&HemisphereSP);
666 HemisphereS[newInfo.hemisphere].s = ISS_ON;
667 IDSetSwitch(&HemisphereSP, nullptr);
668 }
669
670 if (SlewRateSP.findOnSwitchIndex() != newInfo.slewRate - 1)
671 {
673 SlewRateSP[newInfo.slewRate - 1].setState(ISS_ON);
675 }
676
677 switch (newInfo.systemStatus)
678 {
679 case ST_STOPPED:
682 break;
683 case ST_PARKED:
686 if (!isParked())
687 SetParked(true);
688 if (HomeSP.getState() == IPS_BUSY)
689 {
690 HomeSP.reset();
692 HomeSP.apply();
693 }
694 break;
695 case ST_HOME:
698 if (HomeSP.getState() == IPS_BUSY)
699 {
700 HomeSP.reset();
702 HomeSP.apply();
703 }
704 break;
705 case ST_SLEWING:
709 break;
712 case ST_GUIDING:
717 if (scopeInfo.systemStatus == ST_SLEWING)
718 LOG_INFO("Slew complete, tracking...");
719 else if (scopeInfo.systemStatus == ST_MERIDIAN_FLIPPING)
720 LOG_INFO("Meridian flip complete, tracking...");
721 break;
722 }
723
724 if (TrackModeSP.findOnSwitchIndex() != newInfo.trackRate)
725 {
729 }
730
731 scopeInfo = newInfo;
732 }
733
734 /* v3.0 Monitor PEC Training */
735 if (isTraining)
736 {
738 {
739 if(GetPECDataStatus(false))
740 {
741 LOGF_INFO("%d second worm cycle recorded", PECTime);
742 PECTrainingSP.s = IPS_OK;
743 isTraining = false;
744 }
745 else
746 {
747 PECTime = PECTime + 1 * getCurrentPollingPeriod() / 1000;
748 char PECText[MAXINDILABEL] = {0};
749 snprintf(PECText, MAXINDILABEL, "Recording: %d s", PECTime);
750 IUSaveText(&PECInfoT[0], PECText);
751 }
752 }
753 else
754 {
755 driver->setPETEnabled(false);
756 PECTrainingSP.s = IPS_ALERT;
757 LOGF_ERROR("Tracking error, recording cancelled %d s", PECTime);
758 IUSaveText(&PECInfoT[0], "Cancelled");
759 }
760 IDSetText(&PECInfoTP, nullptr);
761 IDSetSwitch(&PECTrainingSP, nullptr);
762 }
763 // End Mod */
764
766 IOP_CW_STATE cwState = IOP_CW_NORMAL;
767
768 double previousRA = currentRA, previousDE = currentDEC;
769 rc = driver->getCoords(&currentRA, &currentDEC, &pierState, &cwState);
770 if (rc)
771 {
772 // Add Extra Logging info
773 if (isDebug())
774 {
775 char RAStr[64] = {0}, DecStr[64] = {0}, AzStr[64] = {0}, AltStr[64] = {0};
776 fs_sexa(RAStr, currentRA, 2, 3600);
777 fs_sexa(DecStr, currentDEC, 2, 3600);
779 INDI::IHorizontalCoordinates horizontalCoords {0, 0};
780 INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
781 fs_sexa(AzStr, horizontalCoords.azimuth, 2, 3600);
782 fs_sexa(AltStr, horizontalCoords.altitude, 2, 3600);
783 std::string pierSide = "Uknkown";
784 if (pierState == IOP_PIER_EAST)
785 pierSide = "East";
786 else if (pierState == IOP_PIER_WEST)
787 pierSide = "West";
788 DEBUGF(DBG_SCOPE, "RA: %s DE: %s AZ: %s AL: %s PierSide: %s CWState %d", RAStr, DecStr, AzStr, AltStr, pierSide.c_str(), cwState);
789 }
790
791 // 2021.11.30 JM: This is a hack to circumvent a bug in iOptorn firmware
792 // the "system status" bit is set to SLEWING even when parking is done (2), it never
793 // changes to (6) which indicates it has parked. So we use a counter to check if there
794 // is no longer any motion.
796 {
797 if (std::abs(previousRA - currentRA) < 0.01 && std::abs(previousDE - currentDEC) < 0.01)
798 {
799 m_ParkingCounter++;
800 if (m_ParkingCounter >= MAX_PARK_COUNTER)
801 {
802 m_ParkingCounter = 0;
803 SetTrackEnabled(false);
804 SetParked(true);
805 }
806 }
807 }
808 if (pierState == IOP_PIER_UNKNOWN)
810 else
812
813 if (IUFindOnSwitchIndex(&CWStateSP) != cwState)
814 {
815 IUResetSwitch(&CWStateSP);
816 CWStateS[cwState].s = ISS_ON;
817 IDSetSwitch(&CWStateSP, nullptr);
818 }
819
821 }
822
823 return rc;
824}
825
826bool IOptronV3::Goto(double ra, double de)
827{
828 targetRA = ra;
829 targetDEC = de;
830 char RAStr[64] = {0}, DecStr[64] = {0};
831
832 fs_sexa(RAStr, targetRA, 2, 3600);
833 fs_sexa(DecStr, targetDEC, 2, 3600);
834
835 if (driver->setRA(ra) == false || driver->setDE(de) == false)
836 {
837 LOG_ERROR("Error setting RA/DEC.");
838 return false;
839 }
840
841 bool rc = false;
842 if (IUFindOnSwitchIndex(&SlewModeSP) == IOP_CW_NORMAL)
843 rc = driver->slewNormal();
844 else
845 rc = driver->slewCWUp();
846
847 if (rc == false)
848 {
849 LOG_ERROR("Failed to slew.");
850 return false;
851 }
852
854
855 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
856 return true;
857}
858
859bool IOptronV3::Sync(double ra, double de)
860{
861 if (driver->setRA(ra) == false || driver->setDE(de) == false)
862 {
863 LOG_ERROR("Error setting RA/DEC.");
864 return false;
865 }
866
867 if (driver->sync() == false)
868 {
869 LOG_ERROR("Failed to sync.");
870 }
871
873
874 currentRA = ra;
875 currentDEC = de;
876
878
879 return true;
880}
881
883{
884 return driver->abort();
885}
886
888{
889 // if (firmwareInfo.Model.find("CEM") == std::string::npos &&
890 // firmwareInfo.Model.find("iEQ45 Pro") == std::string::npos &&
891 // firmwareInfo.Model.find("iEQ35") == std::string::npos)
892 // {
893 // LOG_ERROR("Parking is not supported in this mount model.");
894 // return false;
895 // }
896
897 if (driver->park())
898 {
900 m_ParkingCounter = 0;
901 LOG_INFO("Parking is in progress...");
902
903 return true;
904 }
905 else
906 return false;
907}
908
910{
911 // if (firmwareInfo.Model.find("CEM") == std::string::npos &&
912 // firmwareInfo.Model.find("iEQ45 Pro") == std::string::npos &&
913 // firmwareInfo.Model.find("iEQ35") == std::string::npos)
914 // {
915 // LOG_ERROR("Unparking is not supported in this mount model.");
916 // return false;
917 // }
918
919 if (driver->unpark())
920 {
921 SetParked(false);
923 return true;
924 }
925 else
926 return false;
927}
928
930{
931 driver->setSimulation(isSimulation());
932
933 if (driver->checkConnection(PortFD) == false)
934 return false;
935
936 return true;
937}
938
939bool IOptronV3::updateTime(ln_date *utc, double utc_offset)
940{
941 // No communications while parked.
943 return true;
944
945 bool rc1 = driver->setUTCDateTime(ln_get_julian_day(utc));
946
947 bool rc2 = driver->setUTCOffset(utc_offset * 60);
948
949 return (rc1 && rc2);
950}
951
952bool IOptronV3::updateLocation(double latitude, double longitude, double elevation)
953{
954 INDI_UNUSED(elevation);
955
956 // No communications while parked.
958 return true;
959
960 if (longitude > 180)
961 longitude -= 360;
962
963 if (driver->setLongitude(longitude) == false)
964 {
965 LOG_ERROR("Failed to set longitude.");
966 return false;
967 }
968
969 if (driver->setLatitude(latitude) == false)
970 {
971 LOG_ERROR("Failed to set longitude.");
972 return false;
973 }
974
975 char l[32] = {0}, L[32] = {0};
976 fs_sexa(l, latitude, 3, 3600);
977 fs_sexa(L, longitude, 4, 3600);
978
979 LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
980
981 return true;
982}
983
985{
986 driver->setDebug(enable);
987}
988
990{
991 driver->setSimulation(enable);
992}
993
995{
997 {
998 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
999 return false;
1000 }
1001
1002 switch (command)
1003 {
1004 case MOTION_START:
1005 if (driver->startMotion(dir == DIRECTION_NORTH ? IOP_N : IOP_S) == false)
1006 {
1007 LOG_ERROR("Error setting N/S motion direction.");
1008 return false;
1009 }
1010 else
1011 LOGF_INFO("Moving toward %s.", (dir == DIRECTION_NORTH) ? "North" : "South");
1012 break;
1013
1014 case MOTION_STOP:
1015 if (driver->stopMotion(dir == DIRECTION_NORTH ? IOP_N : IOP_S) == false)
1016 {
1017 LOG_ERROR("Error stopping N/S motion.");
1018 return false;
1019 }
1020 else
1021 LOGF_INFO("%s motion stopped.", (dir == DIRECTION_NORTH) ? "North" : "South");
1022 break;
1023 }
1024
1025 return true;
1026}
1027
1029{
1030 if (TrackState == SCOPE_PARKED)
1031 {
1032 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
1033 return false;
1034 }
1035
1036 switch (command)
1037 {
1038 case MOTION_START:
1039 if (driver->startMotion(dir == DIRECTION_WEST ? IOP_W : IOP_E) == false)
1040 {
1041 LOG_ERROR("Error setting N/S motion direction.");
1042 return false;
1043 }
1044 else
1045 LOGF_INFO("Moving toward %s.", (dir == DIRECTION_WEST) ? "West" : "East");
1046 break;
1047
1048 case MOTION_STOP:
1049 if (driver->stopMotion(dir == DIRECTION_WEST ? IOP_W : IOP_E) == false)
1050 {
1051 LOG_ERROR("Error stopping W/E motion.");
1052 return false;
1053 }
1054 else
1055 LOGF_INFO("%s motion stopped.", (dir == DIRECTION_WEST) ? "West" : "East");
1056 break;
1057 }
1058
1059 return true;
1060}
1061
1063{
1064 bool rc = driver->startGuide(IOP_N, (uint32_t)ms);
1065 return (rc ? IPS_OK : IPS_ALERT);
1066}
1067
1069{
1070 bool rc = driver->startGuide(IOP_S, (uint32_t)ms);
1071 return (rc ? IPS_OK : IPS_ALERT);
1072}
1073
1075{
1076 bool rc = driver->startGuide(IOP_E, (uint32_t)ms);
1077 return (rc ? IPS_OK : IPS_ALERT);
1078}
1079
1081{
1082 bool rc = driver->startGuide(IOP_W, (uint32_t)ms);
1083 return (rc ? IPS_OK : IPS_ALERT);
1084}
1085
1087{
1088 IOP_SLEW_RATE rate = static_cast<IOP_SLEW_RATE>(index);
1089 return driver->setSlewRate(rate);
1090}
1091
1093{
1095
1096 IUSaveConfigSwitch(fp, &SlewModeSP);
1097 IUSaveConfigSwitch(fp, &DaylightSP);
1098
1099 MeridianLimitNP.save(fp);
1100 MeridianActionSP.save(fp);
1101
1102 return true;
1103}
1104
1106{
1107 static struct timeval ltv;
1108 struct timeval tv;
1109 double dt, da, dx;
1110 int nlocked;
1111
1112 /* update elapsed time since last poll, don't presume exactly POLLMS */
1113 gettimeofday(&tv, nullptr);
1114
1115 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1116 ltv = tv;
1117
1118 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1119 ltv = tv;
1120 double currentSlewRate = Driver::IOP_SLEW_RATES[SlewRateSP.findOnSwitchIndex()] * TRACKRATE_SIDEREAL / 3600.0;
1121 da = currentSlewRate * dt;
1122
1123 /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
1124 switch (TrackState)
1125 {
1126 case SCOPE_IDLE:
1127 currentRA += (TrackRateNP[AXIS_RA].getValue() / 3600.0 * dt) / 15.0;
1129 break;
1130
1131 case SCOPE_TRACKING:
1132 if (TrackModeSP[TR_CUSTOM].getState() == ISS_ON)
1133 {
1134 currentRA += ( ((TRACKRATE_SIDEREAL / 3600.0) - (TrackRateNP[AXIS_RA].getValue() / 3600.0)) * dt) / 15.0;
1135 currentDEC += ( (TrackRateNP[AXIS_DE].getValue() / 3600.0) * dt);
1136 }
1137 break;
1138
1139 case SCOPE_SLEWING:
1140 case SCOPE_PARKING:
1141 /* slewing - nail it when both within one pulse @ SLEWRATE */
1142 nlocked = 0;
1143
1144 dx = targetRA - currentRA;
1145
1146 // Take shortest path
1147 if (fabs(dx) > 12)
1148 dx *= -1;
1149
1150 if (fabs(dx) <= da)
1151 {
1152 currentRA = targetRA;
1153 nlocked++;
1154 }
1155 else if (dx > 0)
1156 currentRA += da / 15.;
1157 else
1158 currentRA -= da / 15.;
1159
1160 if (currentRA < 0)
1161 currentRA += 24;
1162 else if (currentRA > 24)
1163 currentRA -= 24;
1164
1165 dx = targetDEC - currentDEC;
1166 if (fabs(dx) <= da)
1167 {
1168 currentDEC = targetDEC;
1169 nlocked++;
1170 }
1171 else if (dx > 0)
1172 currentDEC += da;
1173 else
1174 currentDEC -= da;
1175
1176 if (nlocked == 2)
1177 {
1179 driver->setSimSytemStatus(ST_TRACKING_PEC_OFF);
1180 else
1181 driver->setSimSytemStatus(ST_PARKED);
1182 }
1183
1184 break;
1185
1186 default:
1187 break;
1188 }
1189
1190 driver->setSimRA(currentRA);
1191 driver->setSimDE(currentDEC);
1192}
1193
1195{
1197 INDI::IHorizontalCoordinates horizontalCoords {0, 0};
1198 INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
1199 double parkAZ = horizontalCoords.azimuth;
1200 // Wrap to 0
1201 if (parkAZ >= 360)
1202 parkAZ = 0;
1203 double parkAlt = horizontalCoords.altitude;
1204 char AzStr[16], AltStr[16];
1205 fs_sexa(AzStr, parkAZ, 2, 3600);
1206 fs_sexa(AltStr, parkAlt, 2, 3600);
1207 LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
1208 SetAxis1Park(parkAZ);
1209 SetAxis2Park(parkAlt);
1210 driver->setParkAz(parkAZ);
1211 driver->setParkAlt(parkAlt);
1212 return true;
1213}
1214
1216{
1217 // By default azimuth 0
1218 SetAxis1Park(0);
1219 // Altitude = latitude of observer
1221 driver->setParkAz(0);
1222 driver->setParkAlt(LocationNP[LOCATION_LATITUDE].getValue());
1223 return true;
1224}
1225
1226bool IOptronV3::SetTrackMode(uint8_t mode)
1227{
1228 IOP_TRACK_RATE rate = static_cast<IOP_TRACK_RATE>(mode);
1229
1230 if (driver->setTrackMode(rate))
1231 return true;
1232
1233 return false;
1234}
1235
1236bool IOptronV3::SetTrackRate(double raRate, double deRate)
1237{
1238 INDI_UNUSED(deRate);
1239
1240 // Convert to arcsecs/s to rate
1241 double ieqRARate = raRate / TRACKRATE_SIDEREAL;
1242
1243 if (ieqRARate < 0.1 || ieqRARate > 1.9)
1244 {
1245 LOG_ERROR("Rate is outside permitted limits of 0.1 to 1.9 sidereal rate (1.504 to 28.578 arcsecs/s)");
1246 return false;
1247 }
1248
1249
1250 if (driver->setCustomRATrackRate(ieqRARate))
1251 return true;
1252
1253 return false;
1254}
1255
1257{
1258 if (enabled)
1259 {
1260 // If we are engaging tracking, let us first set tracking mode, and if we have custom mode, then tracking rate.
1261 // NOTE: Is this the correct order? or should tracking be switched on first before making these changes? Need to test.
1263 if (TrackModeSP[TR_CUSTOM].getState() == ISS_ON)
1264 SetTrackRate(TrackRateNP[AXIS_RA].getValue(), TrackRateNP[AXIS_DE].getValue());
1265 }
1266
1267 return driver->setTrackEnabled(enabled);
1268}
1269
1270/* v3.0 PEC add data status to the Driver */
1271bool IOptronV3::GetPECDataStatus(bool enabled)
1272{
1273 if(driver->getPETEnabled(true))
1274 {
1275 if (enabled)
1276 {
1277 IUSaveText(&PECInfoT[0], "Recorded");
1278 IDSetText(&PECInfoTP, nullptr);
1279 LOG_INFO("Mount PEC Chip Ready and Trained");
1280 }
1281 return true;
1282 }
1283 else
1284 {
1285 if (enabled)
1286 {
1287 IUSaveText(&PECInfoT[0], "None");
1288 IDSetText(&PECInfoTP, nullptr);
1289 LOG_INFO("Mount PEC Chip Needs Training");
1290 }
1291 }
1292 return false;
1293}
1294
1296{
1297 switch (action)
1298 {
1299 case HOME_FIND:
1300 if (firmwareInfo.Model.find("CEM") == std::string::npos &&
1301 firmwareInfo.Model.find("GEM45") == std::string::npos &&
1302 firmwareInfo.Model.find("HAE") == std::string::npos &&
1303 firmwareInfo.Model.find("HAZ") == std::string::npos &&
1304 firmwareInfo.Model.find("HEM") == std::string::npos)
1305 {
1306 LOG_WARN("Home search is not supported in this model.");
1307 return IPS_ALERT;
1308 }
1309
1310 if (driver->findHome() == false)
1311 {
1312 return IPS_ALERT;
1313 }
1314
1315 LOG_INFO("Searching for home position...");
1316 return IPS_BUSY;
1317
1318 case HOME_SET:
1319 if (driver->setCurrentHome() == false)
1320 {
1321 return IPS_ALERT;
1322 }
1323
1324 LOG_INFO("Home position set to current coordinates.");
1325 return IPS_OK;
1326
1327 case HOME_GO:
1328 if (driver->gotoHome() == false)
1329 {
1330 return IPS_ALERT;
1331 }
1332
1333 LOG_INFO("Slewing to home position...");
1334 return IPS_BUSY;
1335
1336 default:
1337 return IPS_ALERT;
1338 }
1339
1340 return IPS_ALERT;
1341}
@ FW_MODEL
@ FW_DEC
@ FW_RA
@ GPS_OFF
@ GPS_ON
#define MOUNTINFO_TAB
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
bool isConnected() const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void initProperties(const char *groupName)
Initialize guider properties. It is recommended to call this function within initProperties() of your...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
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.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
bool isParked()
isParked is mount currently parked?
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
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....
Connection::TCP * tcpConnection
void setPECState(TelescopePECState state)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
void setPierSide(TelescopePierSide side)
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.
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.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
virtual const char * getDefaultName() override
Definition ioptronv3.cpp:78
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState ExecuteHomeAction(TelescopeHomeAction action) override
Execute a homing action.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Park() override
Park the telescope to its home position.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
void mountSim()
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool Sync(double ra, double de) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition ioptronv3.cpp:83
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
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.
INDI::PropertyNumber TrackRateNP
INDI::PropertyNumber EqNP
INDI::PropertySwitch PECStateSP
INDI::PropertySwitch HomeSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertyText TimeTP
INDI::PropertyNumber LocationNP
#define currentDEC
Definition ieq45.cpp:48
#define currentRA
Definition ieq45.cpp:47
double ra
@ TR_SIDEREAL
@ TR_CUSTOM
@ TS_RS232
@ TS_CONTROLLER
@ HEMI_NORTH
@ HEMI_SOUTH
@ FW_CONTROLLER
@ FW_BOARD
@ GPS_DATA_OK
@ ST_STOPPED
@ ST_MERIDIAN_FLIPPING
@ ST_PARKED
@ ST_TRACKING_PEC_ON
@ ST_TRACKING_PEC_OFF
@ ST_SLEWING
@ ST_GUIDING
@ ST_HOME
#define RA_AXIS
#define DEC_AXIS
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
#define MAXINDILABEL
Definition indiapi.h:190
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition indicom.c:1311
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:130
Implementations for common driver routines.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
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:376
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:416
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:398
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:453
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
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:846
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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 DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
unsigned int DBG_SCOPE
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
Encapsulates classes and structures required for iOptron Command Set v3 implementation.
@ IOP_PIER_UNKNOWN
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
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
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248