Instrument Neutral Distributed Interface INDI 2.2.5
ieqpro.cpp
Go to the documentation of this file.
1/*
2 INDI IEQ Pro driver
3
4 Copyright (C) 2015 Jasem Mutlaq
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "ieqpro.h"
22#include "indicom.h"
24
25#include <libnova/sidereal_time.h>
26#include <libnova/transform.h>
27#include <memory>
28#include <thread>
29#include <chrono>
30
31#include <cmath>
32#include <cstring>
33
34/* Simulation Parameters */
35#define SLEWRATE 1 /* slew rate, degrees/s */
36
37#define MOUNTINFO_TAB "Mount Info"
38
39using namespace iEQ;
40
41// We declare an auto pointer to IEQPro.
42static std::unique_ptr<IEQPro> scope(new IEQPro());
43
65
67{
68 return "iEQ";
69}
70
72{
74
75 /* Firmware */
76 // Note: the second field of the ':FW1#' reply is the Go2Nova hand controller
77 // firmware, which is distinct from the main board firmware. Element names are
78 // kept for config back-compat; only the display labels are clarified.
79 IUFillText(&FirmwareT[FW_MODEL], "Model", "Model", nullptr);
80 IUFillText(&FirmwareT[FW_BOARD], "Board", "Main Board", nullptr);
81 IUFillText(&FirmwareT[FW_CONTROLLER], "Controller", "Hand Controller", nullptr);
82 IUFillText(&FirmwareT[FW_RA], "RA", "RA", nullptr);
83 IUFillText(&FirmwareT[FW_DEC], "DEC", "DEC", nullptr);
84 IUFillTextVector(&FirmwareTP, FirmwareT, 5, getDeviceName(), "Firmware Info", "", MOUNTINFO_TAB, IP_RO, 0,
85 IPS_IDLE);
86
87 /* Tracking Mode */
88 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
89 AddTrackMode("TRACK_LUNAR", "Lunar");
90 AddTrackMode("TRACK_SOLAR", "Solar");
91 AddTrackMode("TRACK_KING", "King");
92 AddTrackMode("TRACK_CUSTOM", "Custom");
93
94 // Slew Rates
95 SlewRateSP[0].setLabel("1x");
96 SlewRateSP[1].setLabel("2x");
97 SlewRateSP[2].setLabel("8x");
98
99 SlewRateSP[3].setLabel("16x");
100 SlewRateSP[4].setLabel("64x");
101 SlewRateSP[5].setLabel("128x");
102 SlewRateSP[6].setLabel("256x");
103 SlewRateSP[7].setLabel("512x");
104 SlewRateSP[8].setLabel("MAX");
105
107 // 64x is the default
109
110 // Set TrackRate limits within +/- 0.0100 of Sidereal rate
111 TrackRateNP[AXIS_RA].setMin(TRACKRATE_SIDEREAL - 0.01);
112 TrackRateNP[AXIS_RA].setMax(TRACKRATE_SIDEREAL + 0.01);
113 TrackRateNP[AXIS_DE].setMin(-0.01);
114 TrackRateNP[AXIS_DE].setMax(0.01);
115
116 /* GPS Status */
117 IUFillSwitch(&GPSStatusS[GPS_OFF], "Off", "", ISS_ON);
118 IUFillSwitch(&GPSStatusS[GPS_ON], "On", "", ISS_OFF);
119 IUFillSwitch(&GPSStatusS[GPS_DATA_OK], "Data OK", "", ISS_OFF);
120 IUFillSwitchVector(&GPSStatusSP, GPSStatusS, 3, getDeviceName(), "GPS_STATUS", "GPS", MOUNTINFO_TAB, IP_RO,
122
123 /* Time Source */
124 IUFillSwitch(&TimeSourceS[TS_RS232], "RS232", "", ISS_ON);
125 IUFillSwitch(&TimeSourceS[TS_CONTROLLER], "Controller", "", ISS_OFF);
126 IUFillSwitch(&TimeSourceS[TS_GPS], "GPS", "", ISS_OFF);
127 IUFillSwitchVector(&TimeSourceSP, TimeSourceS, 3, getDeviceName(), "TIME_SOURCE", "Time Source", MOUNTINFO_TAB,
129
130 /* Hemisphere */
131 IUFillSwitch(&HemisphereS[HEMI_SOUTH], "South", "", ISS_OFF);
132 IUFillSwitch(&HemisphereS[HEMI_NORTH], "North", "", ISS_ON);
133 IUFillSwitchVector(&HemisphereSP, HemisphereS, 2, getDeviceName(), "HEMISPHERE", "Hemisphere", MOUNTINFO_TAB, IP_RO,
135
136 /* How fast do we guide compared to sidereal rate */
137 IUFillNumber(&GuideRateN[RA_AXIS], "RA_GUIDE_RATE", "RA", "%.2f", 0.01, 0.9, 0.1, 0.5);
138 IUFillNumber(&GuideRateN[DEC_AXIS], "DE_GUIDE_RATE", "DE", "%.2f", 0.1, 0.99, 0.1, 0.5);
139 IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guiding Rate", MOTION_TAB, IP_RW, 0,
140 IPS_IDLE);
141
143
145
147
149
151
152 driver->setDeviceName(getDeviceName());
153
154 // Only CEM40 and GEM45 have 115200 baud, rest are 9600
155 if (strstr(getDeviceName(), "CEM40") || strstr(getDeviceName(), "GEM45"))
157
159 currentDEC = LocationNP[LOCATION_LATITUDE].getValue() > 0 ? 90 : -90;
160 return true;
161}
162
164{
165
166 if (isConnected())
167 {
168 getStartupData();
169
171
172 if (canGuideRate)
173 defineProperty(&GuideRateNP);
174
175 defineProperty(&FirmwareTP);
176 defineProperty(&GPSStatusSP);
177 defineProperty(&TimeSourceSP);
178 defineProperty(&HemisphereSP);
179 }
180 else
181 {
183
184 if (canGuideRate)
185 deleteProperty(GuideRateNP.name);
186
187 deleteProperty(FirmwareTP.name);
188 deleteProperty(GPSStatusSP.name);
189 deleteProperty(TimeSourceSP.name);
190 deleteProperty(HemisphereSP.name);
191 }
192
194
195 return true;
196}
197
198void IEQPro::getStartupData()
199{
200 LOG_DEBUG("Getting firmware data...");
201
202 firmwareInfo = driver->getFirmwareInfo();
203
204 IUSaveText(&FirmwareT[0], firmwareInfo.Model.c_str());
205 IUSaveText(&FirmwareT[1], firmwareInfo.MainBoardFirmware.c_str());
206 // The mount reports "xxxxxx" for the hand controller firmware when no Go2Nova
207 // hand controller is present in the serial path (e.g. connected directly to the
208 // mount's RS-232 port). Show "N/A" instead of the raw placeholder.
209 const bool controllerReported = !firmwareInfo.ControllerFirmware.empty() &&
210 firmwareInfo.ControllerFirmware.find_first_not_of('x') != std::string::npos;
211 IUSaveText(&FirmwareT[2], controllerReported ? firmwareInfo.ControllerFirmware.c_str() : "N/A");
212 IUSaveText(&FirmwareT[3], firmwareInfo.RAFirmware.c_str());
213 IUSaveText(&FirmwareT[4], firmwareInfo.DEFirmware.c_str());
214
215 FirmwareTP.s = IPS_OK;
216 IDSetText(&FirmwareTP, nullptr);
217
218
219 LOG_DEBUG("Getting guiding rate...");
220 double raGuideRate = 0, deGuideRate = 0;
221 if (driver->getGuideRate(&raGuideRate, &deGuideRate))
222 {
223 GuideRateN[RA_AXIS].value = raGuideRate;
224 GuideRateN[DEC_AXIS].value = deGuideRate;
225 IDSetNumber(&GuideRateNP, nullptr);
226 }
227
228 double utc_offset;
229 int yy, dd, mm, hh, minute, ss;
230 if (driver->getUTCDateTime(&utc_offset, &yy, &mm, &dd, &hh, &minute, &ss))
231 {
232 char isoDateTime[32] = {0};
233 char utcOffset[8] = {0};
234
235 snprintf(isoDateTime, 32, "%04d-%02d-%02dT%02d:%02d:%02d", yy, mm, dd, hh, minute, ss);
236 snprintf(utcOffset, 8, "%4.2f", utc_offset);
237
238 TimeTP[UTC].setText(isoDateTime);
239 TimeTP[OFFSET].setText(utcOffset);
240
241 LOGF_INFO("Mount UTC offset is %s. UTC time is %s", utcOffset, isoDateTime);
242
244 TimeTP.apply();
245 }
246
247 // Get Longitude and Latitude from mount
248 double longitude = 0, latitude = 0;
249 if (driver->getStatus(&scopeInfo))
250 {
251 longitude = scopeInfo.longitude;
252 latitude = scopeInfo.latitude;
253
254 // Convert to INDI standard longitude (0 to 360 Eastward)
255 if (longitude < 0)
256 longitude += 360;
257
258 LOGF_INFO("Mount Longitude %g Latitude %g", longitude, latitude);
259
260 LocationNP[LOCATION_LATITUDE].setValue(latitude);
261 LocationNP[LOCATION_LONGITUDE].setValue(longitude);
263
265
266 saveConfig(true, "GEOGRAPHIC_COORD");
267 }
268 else if (IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude) == 0 &&
269 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude) == 0)
270 {
271 LocationNP[LOCATION_LATITUDE].setValue(latitude);
272 LocationNP[LOCATION_LONGITUDE].setValue(longitude);
274
276 }
277
278 if (InitPark())
279 {
280 // If loading parking data is successful, we just set the default parking values.
281 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
283 }
284 else
285 {
286 // Otherwise, we set all parking data to default in case no parking data is found.
287 SetAxis1Park(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
289 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
291 }
292
293 // can we read pier side?
295 if (driver->getPierSide(&pierSide) && pierSide != IEQ_PIER_UNKNOWN)
296 {
297 // add the pier side capability
298 auto cap = GetTelescopeCapability();
301 }
302
303 // if (isSimulation())
304 // {
305 // if (isParked())
306 // set_sim_system_status(ST_PARKED);
307 // else
308 // set_sim_system_status(ST_STOPPED);
309 // }
310}
311
312bool IEQPro::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
313{
314 // Check guider interface
315 if (GI::processNumber(dev, name, values, names, n))
316 return true;
317
318 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
319 {
320 // Guiding Rate
321 if (!strcmp(name, GuideRateNP.name))
322 {
323 IUUpdateNumber(&GuideRateNP, values, names, n);
324
325 if (driver->setGuideRate(GuideRateN[RA_AXIS].value, GuideRateN[DEC_AXIS].value))
326 GuideRateNP.s = IPS_OK;
327 else
328 GuideRateNP.s = IPS_ALERT;
329
330 IDSetNumber(&GuideRateNP, nullptr);
331
332 return true;
333 }
334 }
335
336 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
337}
338
340{
341 iEQ::Base::Info newInfo;
342
343 bool rc = driver->getStatus(&newInfo);
344
345 if (rc)
346 {
347 IUResetSwitch(&GPSStatusSP);
348 GPSStatusS[newInfo.gpsStatus].s = ISS_ON;
349 IDSetSwitch(&GPSStatusSP, nullptr);
350
351 IUResetSwitch(&TimeSourceSP);
352 TimeSourceS[newInfo.timeSource].s = ISS_ON;
353 IDSetSwitch(&TimeSourceSP, nullptr);
354
355 IUResetSwitch(&HemisphereSP);
356 HemisphereS[newInfo.hemisphere].s = ISS_ON;
357 IDSetSwitch(&HemisphereSP, nullptr);
358
359 /*
360 TelescopeTrackMode trackMode = TRACK_SIDEREAL;
361
362 switch (newInfo.trackRate)
363 {
364 case TR_SIDEREAL:
365 trackMode = TRACK_SIDEREAL;
366 break;
367 case TR_SOLAR:
368 trackMode = TRACK_SOLAR;
369 break;
370 case TR_LUNAR:
371 trackMode = TRACK_LUNAR;
372 break;
373 case TR_KING:
374 trackMode = TRACK_SIDEREAL;
375 break;
376 case TR_CUSTOM:
377 trackMode = TRACK_CUSTOM;
378 break;
379 }*/
380
381 switch (newInfo.systemStatus)
382 {
383 case ST_STOPPED:
385 // If we cannot park natively and we already parked
386 // scope, we do not want its state to change to IDLE
387 // For scopes that can park natively, ST_PARKED would be
388 // set already.
389 if (canParkNatively || TrackState != SCOPE_PARKED)
391 break;
392 case ST_PARKED:
395 if (!isParked())
396 SetParked(true);
397 // If a Home operation was in progress, mark it complete.
398 if (HomeSP.getState() == IPS_BUSY)
399 {
400 HomeSP.reset();
402 HomeSP.apply();
403 LOG_INFO("Mount arrived at home position.");
404 }
405 break;
406 case ST_HOME:
409 // The mount reports ST_HOME once it settles on the home switch.
410 // Clear the Home property only if a Home operation was actually
411 // requested (it is BUSY); the mount also reports ST_HOME at rest.
412 if (HomeSP.getState() == IPS_BUSY)
413 {
414 HomeSP.reset();
416 HomeSP.apply();
417 LOG_INFO("Mount arrived at home position.");
418 }
419 break;
420 case ST_SLEWING:
422 slewDirty = true;
425 break;
428 case ST_GUIDING:
429 if (TrackState == SCOPE_PARKING && canParkNatively == false)
430 {
431 if (slewDirty)
432 {
433 LOG_INFO("Manual parking complete. Shut the mount down.");
436 SetTrackEnabled(false);
437 SetParked(true);
438 slewDirty = false;
439 }
440 }
441 else
442 {
445 if (scopeInfo.systemStatus == ST_SLEWING)
446 LOG_INFO("Slew complete, tracking...");
447 else if (scopeInfo.systemStatus == ST_MERIDIAN_FLIPPING)
448 LOG_INFO("Meridian flip complete, tracking...");
449 }
450 break;
451 }
452
456
457 scopeInfo = newInfo;
458 }
459
460 if (HasPierSide())
461 {
462 IEQ_PIER_SIDE pierSide;
463 if (driver->getPierSide(&pierSide))
464 {
466 switch (pierSide)
467 {
468 case IEQ_PIER_UNKNOWN:
470 tps = PIER_UNKNOWN;
471 break;
472 case IEQ_PIER_EAST:
473 tps = PIER_EAST;
474 break;
475 case IEQ_PIER_WEST:
476 tps = PIER_WEST;
477 break;
478 }
479 setPierSide(tps);
480 }
481 }
482
483 rc = driver->getCoords(&currentRA, &currentDEC);
484
485 if (rc)
486 NewRaDec(currentRA, currentDEC);
487
488 return rc;
489}
490
491bool IEQPro::Goto(double r, double d)
492{
493 targetRA = r;
494 targetDEC = d;
495 char RAStr[64] = {0}, DecStr[64] = {0};
496
497 fs_sexa(RAStr, targetRA, 2, 3600);
498 fs_sexa(DecStr, targetDEC, 2, 3600);
499
500 if (driver->setRA(r) == false || driver->setDE(d) == false)
501 {
502 LOG_ERROR("Error setting RA/DEC.");
503 return false;
504 }
505
506 if (driver->slew() == false)
507 {
508 LOG_ERROR("Failed to slew.");
509 return false;
510 }
511
512 iEQ::Base::Info newInfo;
513
514 // Wait until the mount system status changes to SLEWING
515 // up to 500ms
516 for (int i = 0; i < 5; i++)
517 {
518 bool rc = driver->getStatus(&newInfo);
519 if (rc && newInfo.systemStatus == ST_SLEWING)
520 break;
521 std::this_thread::sleep_for(std::chrono::milliseconds(100));
522 }
523
524 if (newInfo.systemStatus == ST_SLEWING)
525 {
527 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
528 return true;
529 }
530 else
531 {
532 LOG_ERROR("Mount status failed to update to slewing.");
533 return false;
534 }
535}
536
537bool IEQPro::Sync(double ra, double dec)
538{
539 if (driver->setRA(ra) == false || driver->setDE(dec) == false)
540 {
541 LOG_ERROR("Error setting RA/DEC.");
542 return false;
543 }
544
545 if (driver->sync() == false)
546 {
547 LOG_ERROR("Failed to sync.");
548 }
549
551
552 currentRA = ra;
553 currentDEC = dec;
554
555 NewRaDec(currentRA, currentDEC);
556
557 return true;
558}
559
561{
562 return driver->abort();
563}
564
566{
567#if 0
568 targetRA = GetAxis1Park();
569 targetDEC = GetAxis2Park();
570
571 if (set_ieqpro_ra(PortFD, targetRA) == false || set_ieqpro_dec(PortFD, targetDEC) == false)
572 {
573 LOG_ERROR("Error setting RA/DEC.");
574 return false;
575 }
576
577 if (slew_ieqpro(PortFD) == false)
578 {
579 LOG_ERROR("Failed to slew tp parking position.");
580 return false;
581 }
582
583 char RAStr[64] = {0}, DecStr[64] = {0};
584 fs_sexa(RAStr, targetRA, 2, 3600);
585 fs_sexa(DecStr, targetDEC, 2, 3600);
586
588 LOGF_INFO("Telescope parking in progress to RA: %s DEC: %s", RAStr, DecStr);
589
590 return true;
591#endif
592
593 double parkAz = GetAxis1Park();
594 double parkAlt = GetAxis2Park();
595
596 char AzStr[16], AltStr[16];
597 fs_sexa(AzStr, parkAz, 2, 3600);
598 fs_sexa(AltStr, parkAlt, 2, 3600);
599 LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
600
601 // Check if mount supports native Alt/Az parking
602 if (canParkNatively)
603 {
604 if (driver->setParkAz(parkAz) && driver->setParkAlt(parkAlt))
605 {
606 if (driver->park())
607 {
609 LOG_INFO("Parking is in progress...");
610 return true;
611 }
612 else
613 return false;
614 }
615 else
616 return false;
617 }
618
619 // Otherwise fallback to Alt/Az --> RA/DE parking
620 INDI::IHorizontalCoordinates horizontalPos;
621 horizontalPos.azimuth = parkAz;
622 horizontalPos.altitude = parkAlt;
623 INDI::IEquatorialCoordinates equatorialPos;
624
625 INDI::HorizontalToEquatorial(&horizontalPos, &m_Location, ln_get_julian_from_sys(), &equatorialPos);
626
627 if (Goto(equatorialPos.rightascension, equatorialPos.declination))
628 {
630 LOG_INFO("Parking is in progress...");
631
632 slewDirty = false;
633 return true;
634 }
635 else
636 return false;
637}
638
640{
641 // If we can park natively, let's try to unpark the mount first.
642 // If that fails, we return. Otherwise, we proceed even when
643 // we are manually unparking.
644 if (canParkNatively && driver->unpark() == false)
645 return false;
646
647 SetParked(false);
649 return true;
650}
651
653{
654 // if (isSimulation())
655 // {
656 // set_sim_gps_status(GPS_DATA_OK);
657 // set_sim_system_status(ST_STOPPED);
658 // set_sim_track_rate(TR_SIDEREAL);
659 // set_sim_slew_rate(SR_3);
660 // set_sim_time_source(TS_GPS);
661 // set_sim_hemisphere(HEMI_NORTH);
662 // }
663
664 if (driver->initCommunication(PortFD) == false)
665 return false;
666
667 canParkNatively = driver->isCommandSupported("MP1", true);
668 canFindHome = driver->isCommandSupported("MSH", true);
669 canGuideRate = driver->isCommandSupported("RG", true);
670
671 return true;
672}
673
674bool IEQPro::updateTime(ln_date *utc, double utc_offset)
675{
676 struct ln_zonedate ltm;
677
678 ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
679
680 // Set Local Time
681 if (driver->setLocalTime(ltm.hours, ltm.minutes, ltm.seconds) == false)
682 {
683 LOG_ERROR("Error setting local time.");
684 return false;
685 }
686
687 // Send it as YY (i.e. 2015 --> 15)
688 ltm.years -= 2000;
689
690 // Set Local date
691 if (driver->setLocalDate(ltm.years, ltm.months, ltm.days) == false)
692 {
693 LOG_ERROR("Error setting local date.");
694 return false;
695 }
696
697 // UTC Offset
698 if (driver->setUTCOffset(utc_offset) == false)
699 {
700 LOG_ERROR("Error setting UTC Offset.");
701 return false;
702 }
703
704 LOG_INFO("Time and date updated.");
705
706 return true;
707}
708
709bool IEQPro::updateLocation(double latitude, double longitude, double elevation)
710{
711 INDI_UNUSED(elevation);
712
713 if (longitude > 180)
714 longitude -= 360;
715
716 if (driver->setLongitude(longitude) == false)
717 {
718 LOG_ERROR("Failed to set longitude.");
719 return false;
720 }
721
722 if (driver->setLatitude(latitude) == false)
723 {
724 LOG_ERROR("Failed to set latitude.");
725 return false;
726 }
727
728 char l[32] = {0}, L[32] = {0};
729 fs_sexa(l, latitude, 3, 3600);
730 fs_sexa(L, longitude, 4, 3600);
731
732 LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
733
734 return true;
735}
736
737void IEQPro::debugTriggered(bool enable)
738{
739 driver->setDebugEnabled(enable);
740}
741
743{
744 INDI_UNUSED(enable);
745 //driver->setSi(enable);
746}
747
749{
751 {
752 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
753 return false;
754 }
755
756 switch (command)
757 {
758 case MOTION_START:
759 if (driver->startMotion((dir == DIRECTION_NORTH ? IEQ_N : IEQ_S)) == false)
760 {
761 LOG_ERROR("Error setting N/S motion direction.");
762 return false;
763 }
764 else
765 LOGF_INFO("Moving toward %s.", (dir == DIRECTION_NORTH) ? "North" : "South");
766 break;
767
768 case MOTION_STOP:
769 if (driver->stopMotion((dir == DIRECTION_NORTH ? IEQ_N : IEQ_S)) == false)
770 {
771 LOG_ERROR("Error stopping N/S motion.");
772 return false;
773 }
774 else
775 LOGF_INFO("%s motion stopped.", (dir == DIRECTION_NORTH) ? "North" : "South");
776 break;
777 }
778
779 return true;
780}
781
783{
785 {
786 LOG_ERROR("Please unpark the mount before issuing any motion commands.");
787 return false;
788 }
789
790 switch (command)
791 {
792 case MOTION_START:
793 if (driver->startMotion((dir == DIRECTION_WEST ? IEQ_W : IEQ_E)) == false)
794 {
795 LOG_ERROR("Error setting N/S motion direction.");
796 return false;
797 }
798 else
799 LOGF_INFO("Moving toward %s.", (dir == DIRECTION_WEST) ? "West" : "East");
800 break;
801
802 case MOTION_STOP:
803 if (driver->stopMotion((dir == DIRECTION_WEST ? IEQ_W : IEQ_E)) == false)
804 {
805 LOG_ERROR("Error stopping W/E motion.");
806 return false;
807 }
808 else
809 LOGF_INFO("%s motion stopped.", (dir == DIRECTION_WEST) ? "West" : "East");
810 break;
811 }
812
813 return true;
814}
815
817{
818 bool rc = driver->startGuide(IEQ_N, ms);
819 return (rc ? IPS_OK : IPS_ALERT);
820}
821
823{
824 bool rc = driver->startGuide(IEQ_S, ms);
825 return (rc ? IPS_OK : IPS_ALERT);
826}
827
829{
830 bool rc = driver->startGuide(IEQ_E, ms);
831 return (rc ? IPS_OK : IPS_ALERT);
832}
833
835{
836 bool rc = driver->startGuide(IEQ_W, ms);
837 return (rc ? IPS_OK : IPS_ALERT);
838}
839
840bool IEQPro::SetSlewRate(int index)
841{
842 SlewRate rate = static_cast<SlewRate>(index);
843 return driver->setSlewRate(rate);
844}
845
847{
849
850 return true;
851}
852
853//void IEQPro::mountSim()
854//{
855// static struct timeval ltv;
856// struct timeval tv;
857// double dt, da, dx;
858// int nlocked;
859
860// /* update elapsed time since last poll, don't presume exactly POLLMS */
861// gettimeofday(&tv, nullptr);
862
863// if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
864// ltv = tv;
865
866// dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
867// ltv = tv;
868// da = SLEWRATE * dt;
869
870// /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
871// switch (TrackState)
872// {
873// case SCOPE_IDLE:
874// currentRA += (TrackRateN[AXIS_RA].value / 3600.0 * dt) / 15.0;
875// currentRA = range24(currentRA);
876// break;
877
878// case SCOPE_TRACKING:
879// if (TrackModeS[1].s == ISS_ON)
880// {
881// currentRA += ( ((TRACKRATE_SIDEREAL / 3600.0) - (TrackRateN[AXIS_RA].value / 3600.0)) * dt) / 15.0;
882// currentDEC += ( (TrackRateN[AXIS_DE].value / 3600.0) * dt);
883// }
884// break;
885
886// case SCOPE_SLEWING:
887// case SCOPE_PARKING:
888// /* slewing - nail it when both within one pulse @ SLEWRATE */
889// nlocked = 0;
890
891// dx = targetRA - currentRA;
892
893// // Take shortest path
894// if (fabs(dx) > 12)
895// dx *= -1;
896
897// if (fabs(dx) <= da)
898// {
899// currentRA = targetRA;
900// nlocked++;
901// }
902// else if (dx > 0)
903// currentRA += da / 15.;
904// else
905// currentRA -= da / 15.;
906
907// if (currentRA < 0)
908// currentRA += 24;
909// else if (currentRA > 24)
910// currentRA -= 24;
911
912// dx = targetDEC - currentDEC;
913// if (fabs(dx) <= da)
914// {
915// currentDEC = targetDEC;
916// nlocked++;
917// }
918// else if (dx > 0)
919// currentDEC += da;
920// else
921// currentDEC -= da;
922
923// if (nlocked == 2)
924// {
925// if (TrackState == SCOPE_SLEWING)
926// set_sim_system_status(ST_TRACKING_PEC_OFF);
927// else
928// set_sim_system_status(ST_PARKED);
929// }
930
931// break;
932
933// default:
934// break;
935// }
936
937// set_sim_ra(currentRA);
938// set_sim_dec(currentDEC);
939//}
940
942{
944 INDI::IHorizontalCoordinates horizontalCoords {0, 0};
945 INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
946 double parkAZ = horizontalCoords.azimuth;
947 double parkAlt = horizontalCoords.altitude;
948 char AzStr[16], AltStr[16];
949 fs_sexa(AzStr, parkAZ, 2, 3600);
950 fs_sexa(AltStr, parkAlt, 2, 3600);
951 LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
952 SetAxis1Park(parkAZ);
953 SetAxis2Park(parkAlt);
954
955 return true;
956}
957
959{
960 // By default azimuth 0
961 SetAxis1Park(0);
962
963 // Altitude = latitude of observer
965
966 return true;
967}
968
969bool IEQPro::SetTrackMode(uint8_t mode)
970{
971 TrackRate rate = static_cast<TrackRate>(mode);
972
973 if (driver->setTrackMode(rate))
974 return true;
975
976 return false;
977}
978
979bool IEQPro::SetTrackRate(double raRate, double deRate)
980{
981 static bool deRateWarning = true;
982
983 // Convert to arcsecs/s to +/- 0.0100 accepted by
984 double ieqRARate = raRate - TRACKRATE_SIDEREAL;
985 if (deRate != 0 && deRateWarning)
986 {
987 // Only send warning once per session
988 deRateWarning = false;
989 LOG_WARN("Custom Declination tracking rate is not implemented yet.");
990 }
991
992 if (driver->setCustomRATrackRate(ieqRARate))
993 return true;
994
995 return false;
996}
997
998bool IEQPro::SetTrackEnabled(bool enabled)
999{
1000 if (enabled)
1001 {
1002 // If we are engaging tracking, let us first set tracking mode, and if we have custom mode, then tracking rate.
1003 // NOTE: Is this the correct order? or should tracking be switched on first before making these changes? Need to test.
1005 if (TrackModeSP[TR_CUSTOM].getState() == ISS_ON)
1006 SetTrackRate(TrackRateNP[AXIS_RA].getValue(), TrackRateNP[AXIS_DE].getValue());
1007 }
1008
1009 return driver->setTrackEnabled(enabled);
1010}
1011
1013{
1014 switch (action)
1015 {
1016 case HOME_FIND:
1017 if (!canFindHome && (firmwareInfo.Model.find("CEM") == std::string::npos &&
1018 firmwareInfo.Model.find("GEM45") == std::string::npos &&
1019 firmwareInfo.Model.find("HAE") == std::string::npos &&
1020 firmwareInfo.Model.find("HAZ") == std::string::npos &&
1021 firmwareInfo.Model.find("HEM") == std::string::npos))
1022 {
1023 LOG_WARN("Home search is not supported in this model.");
1024 return IPS_ALERT;
1025 }
1026
1027 if (driver->findHome() == false)
1028 {
1029 return IPS_ALERT;
1030 }
1031
1032 LOG_INFO("Searching for home position...");
1033 return IPS_BUSY;
1034
1035 case HOME_SET:
1036 if (driver->setCurrentHome() == false)
1037 {
1038 return IPS_ALERT;
1039 }
1040
1041 LOG_INFO("Home position set to current coordinates.");
1042 return IPS_OK;
1043
1044 case HOME_GO:
1045 if (driver->gotoHome() == false)
1046 {
1047 return IPS_ALERT;
1048 }
1049
1050 LOG_INFO("Slewing to home position...");
1051 return IPS_BUSY;
1052
1053 default:
1054 return IPS_ALERT;
1055 }
1056
1057 return IPS_ALERT;
1058}
@ FW_MODEL
@ FW_DEC
@ FW_RA
@ SR_1
@ 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...
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
Definition ieqpro.cpp:822
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition ieqpro.cpp:998
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition ieqpro.cpp:846
IEQPro()
Definition ieqpro.cpp:44
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
Definition ieqpro.cpp:491
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition ieqpro.cpp:71
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition ieqpro.cpp:537
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
Definition ieqpro.cpp:816
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition ieqpro.cpp:958
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition ieqpro.cpp:163
virtual bool Handshake() override
perform handshake with device to check communication
Definition ieqpro.cpp:652
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition ieqpro.cpp:639
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition ieqpro.cpp:737
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
Definition ieqpro.cpp:742
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
Definition ieqpro.cpp:834
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition ieqpro.cpp:941
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition ieqpro.cpp:840
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
Definition ieqpro.cpp:560
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
Definition ieqpro.cpp:828
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition ieqpro.cpp:782
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition ieqpro.cpp:674
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition ieqpro.cpp:312
virtual const char * getDefaultName() override
Definition ieqpro.cpp:66
virtual bool ReadScopeStatus() override
Read telescope status.
Definition ieqpro.cpp:339
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition ieqpro.cpp:979
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition ieqpro.cpp:748
virtual IPState ExecuteHomeAction(TelescopeHomeAction action) override
Execute a homing action.
Definition ieqpro.cpp:1012
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition ieqpro.cpp:969
virtual bool Park() override
Park the telescope to its home position.
Definition ieqpro.cpp:565
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition ieqpro.cpp:709
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)
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
void setLabel(const char *label)
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.
double GetAxis1Park() const
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
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....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
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....
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.
The BaseFirmware class provides control for iOptron version 2014 v2.0 protocol.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
INDI::PropertyNumber TrackRateNP
INDI::PropertyNumber EqNP
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
bool slew_ieqpro(int fd)
double ra
bool set_ieqpro_dec(int fd, double dec)
double dec
bool set_ieqpro_ra(int fd, double ra)
#define RA_AXIS
#define DEC_AXIS
@ 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
@ ISR_1OFMANY
Definition indiapi.h:171
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
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 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
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
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 LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOG_INFO(txt)
Definition indilogger.h:74
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition libastro.cpp:161
@ TR_SIDEREAL
@ TR_CUSTOM
@ TS_CONTROLLER
@ TS_RS232
@ FW_BOARD
@ FW_CONTROLLER
@ GPS_DATA_OK
@ HEMI_NORTH
@ HEMI_SOUTH
@ ST_HOME
@ ST_SLEWING
@ ST_MERIDIAN_FLIPPING
@ ST_TRACKING_PEC_OFF
@ ST_STOPPED
@ ST_TRACKING_PEC_ON
@ ST_GUIDING
@ ST_PARKED
IEQ_PIER_SIDE
@ IEQ_PIER_EAST
@ IEQ_PIER_WEST
@ IEQ_PIER_UNCERTAIN
@ IEQ_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.
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248
std::string MainBoardFirmware
std::string ControllerFirmware
TrackRate trackRate
SystemStatus systemStatus
TimeSource timeSource
Hemisphere hemisphere
GPSStatus gpsStatus