28 #include <libnova/transform.h>
29 #include <libnova/sidereal_time.h>
36 using namespace IOPv3;
38 #define MOUNTINFO_TAB "Mount Info"
42 static std::unique_ptr<IOptronV3> scope(
new IOptronV3());
57 scopeInfo.slewRate =
SR_MAX;
63 SetTelescopeCapability(TELESCOPE_CAN_PARK | TELESCOPE_CAN_SYNC | TELESCOPE_CAN_GOTO | TELESCOPE_CAN_ABORT |
67 TELESCOPE_HAS_TIME | TELESCOPE_HAS_LOCATION | TELESCOPE_HAS_TRACK_MODE |
68 TELESCOPE_CAN_CONTROL_TRACK | TELESCOPE_HAS_TRACK_RATE | TELESCOPE_HAS_PIER_SIDE,
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");
135 IUFillSwitchVector(&HomeSP, HomeS, 3,
getDeviceName(),
"HOME",
"Home",
MAIN_CONTROL_TAB,
IP_RW,
ISR_ATMOST1, 0,
147 IUFillText(&PECInfoT[0],
"PEC_INFO",
"Status",
"");
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);
162 IUFillSwitchVector(&SlewModeSP, SlewModeS, 2,
getDeviceName(),
"Slew Type",
"Slew Type",
MOTION_TAB,
IP_RW,
ISR_1OFMANY, 0,
186 IUFillNumber(&MeridianLimitN[0],
"VALUE",
"Degrees",
"%.f", 0, 10, 1, 0);
199 tcpConnection->setDefaultHost(
"10.10.100.254");
200 tcpConnection->setDefaultPort(8899);
202 TrackState = SCOPE_IDLE;
206 setDriverInterface(getDriverInterface() | GUIDER_INTERFACE);
208 SetParkDataType(PARK_AZ_ALT);
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);
226 defineProperty(&HomeSP);
229 defineProperty(&PECTrainingSP);
230 defineProperty(&PECInfoTP);
233 defineProperty(&GuideNSNP);
234 defineProperty(&GuideWENP);
235 defineProperty(&GuideRateNP);
237 defineProperty(&FirmwareTP);
238 defineProperty(&GPSStatusSP);
239 defineProperty(&TimeSourceSP);
240 defineProperty(&HemisphereSP);
241 defineProperty(&SlewModeSP);
242 defineProperty(&DaylightSP);
243 defineProperty(&CWStateSP);
245 defineProperty(&MeridianActionSP);
246 defineProperty(&MeridianLimitNP);
252 deleteProperty(HomeSP.name);
255 deleteProperty(PECTrainingSP.name);
256 deleteProperty(PECInfoTP.name);
259 deleteProperty(GuideNSNP.name);
260 deleteProperty(GuideWENP.name);
261 deleteProperty(GuideRateNP.name);
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);
271 deleteProperty(MeridianActionSP.name);
272 deleteProperty(MeridianLimitNP.name);
278 void IOptronV3::getStartupData()
281 if (driver->getFirmwareInfo(&firmwareInfo))
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());
294 double RARate = 0, DERate = 0;
295 if (driver->getGuideRate(&RARate, &DERate))
297 GuideRateN[
RA_AXIS].value = RARate;
298 GuideRateN[
DEC_AXIS].value = DERate;
302 int utcOffsetMinutes = 0;
303 bool dayLightSavings =
false;
305 if (driver->getUTCDateTime(&JD, &utcOffsetMinutes, &dayLightSavings))
308 ln_get_timet_from_julian(JD, &utc_time);
313 utc = gmtime(&utc_time);
314 strftime(ts,
sizeof(ts),
"%Y-%m-%dT%H:%M:%S", utc);
319 char offset[8] = {0};
322 utcOffsetMinutes += 60;
324 snprintf(offset, 8,
"%.2f", utcOffsetMinutes / 60.0);
326 LOGF_INFO(
"Mount UTC Offset: %s", offset);
331 LOGF_INFO(
"Mount Daylight Savings: %s", dayLightSavings ?
"ON" :
"OFF");
339 double longitude = 0, latitude = 0;
340 if (driver->getStatus(&scopeInfo))
342 LocationN[LOCATION_LATITUDE].value = scopeInfo.latitude;
344 LocationN[LOCATION_LONGITUDE].value = (scopeInfo.longitude < 0) ? scopeInfo.longitude + 360 : scopeInfo.longitude;
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);
353 LOGF_INFO(
"Mount Location: Lat %.32s - Long %.32s", l, L);
355 saveConfig(
true,
"GEOGRAPHIC_COORD");
360 LocationN[LOCATION_LATITUDE].value = latitude;
361 LocationN[LOCATION_LONGITUDE].value = longitude;
369 if (driver->getMeridianBehavior(action, degrees))
372 MeridianActionS[action].s =
ISS_ON;
373 MeridianActionSP.s =
IPS_OK;
375 MeridianLimitN[0].value = degrees;
378 double parkAZ = LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180;
379 double parkAL = LocationN[LOCATION_LATITUDE].value;
383 SetAxis1ParkDefault(parkAZ);
384 SetAxis2ParkDefault(parkAL);
389 SetAxis1Park(parkAZ);
390 SetAxis2Park(parkAL);
391 SetAxis1ParkDefault(parkAZ);
392 SetAxis2ParkDefault(parkAL);
394 driver->setParkAz(parkAZ);
395 driver->setParkAlt(parkAL);
400 if (driver->getStatus(&newInfo))
412 setPECState(PEC_OFF);
413 GetPECDataStatus(
true);
418 GetPECDataStatus(
true);
438 if (!strcmp(name, GuideRateNP.name))
442 if (driver->setGuideRate(GuideRateN[
RA_AXIS].value, GuideRateN[
DEC_AXIS].value))
455 if (!strcmp(name, MeridianLimitNP.name))
460 if (MeridianLimitNP.s ==
IPS_OK)
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");
466 saveConfig(
true, MeridianLimitNP.name);
470 if (!strcmp(name, GuideNSNP.name) || !strcmp(name, GuideWENP.name))
472 processGuiderProperties(name, values, names, n);
487 if (!strcmp(name, HomeSP.name))
498 if (firmwareInfo.Model.find(
"CEM") == std::string::npos &&
499 firmwareInfo.Model.find(
"GEM45") == std::string::npos)
503 LOG_WARN(
"Home search is not supported in this model.");
507 if (driver->findHome() ==
false)
516 LOG_INFO(
"Searching for home position...");
520 if (driver->setCurrentHome() ==
false)
529 LOG_INFO(
"Home position set to current coordinates.");
533 if (driver->gotoHome() ==
false)
542 LOG_INFO(
"Slewing to home position...");
552 if (!strcmp(name, SlewModeSP.name))
563 if (!strcmp(name, DaylightSP.name))
567 if (driver->setDaylightSaving(DaylightS[0].s ==
ISS_ON))
579 if (!strcmp(name, MeridianActionSP.name))
584 if (MeridianActionSP.s ==
IPS_OK)
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");
590 saveConfig(
true, MeridianActionSP.name);
595 if (!strcmp(name, PECStateSP.name))
599 if(PECStateS[PEC_OFF].s ==
ISS_ON)
605 LOGF_WARN(
"Mount PEC busy recording, %d s", PECTime);
609 driver->setPECEnabled(
false);
617 if (GetPECDataStatus(
true))
620 driver->setPECEnabled(
true);
630 if (!strcmp(name, PECTrainingSP.name))
639 LOGF_WARN(
"Mount PEC busy recording, %d s", PECTime);
645 driver->setPETEnabled(
false);
648 LOG_WARN(
"PEC Training cancelled by user, chip disabled");
655 if(TrackState == SCOPE_TRACKING)
658 driver->setPETEnabled(
true);
662 LOG_INFO(
"PEC recording started...");
666 LOG_WARN(
"PEC Training only possible while guiding");
673 GetPECDataStatus(
true);
693 rc = driver->getStatus(&newInfo);
729 TrackState = SCOPE_IDLE;
733 TrackState = SCOPE_PARKED;
739 TrackState = SCOPE_IDLE;
743 if (TrackState != SCOPE_SLEWING && TrackState != SCOPE_PARKING)
744 TrackState = SCOPE_SLEWING;
752 TrackState = SCOPE_TRACKING;
754 LOG_INFO(
"Slew complete, tracking...");
756 LOG_INFO(
"Meridian flip complete, tracking...");
773 if (TrackState == SCOPE_TRACKING)
775 if(GetPECDataStatus(
false))
777 LOGF_INFO(
"%d second worm cycle recorded", PECTime);
783 PECTime = PECTime + 1 * getCurrentPollingPeriod() / 1000;
785 snprintf(PECText,
MAXINDILABEL,
"Recording: %d s", PECTime);
791 driver->setPETEnabled(
false);
793 LOGF_ERROR(
"Tracking error, recording cancelled %d s", PECTime);
812 if (TrackState == SCOPE_PARKING)
817 if (m_ParkingCounter >= MAX_PARK_COUNTER)
819 m_ParkingCounter = 0;
820 SetTrackEnabled(
false);
826 setPierSide(PIER_UNKNOWN);
828 setPierSide(pierState ==
IOP_PIER_EAST ? PIER_EAST : PIER_WEST);
833 CWStateS[cwState].s =
ISS_ON;
847 char RAStr[64] = {0}, DecStr[64] = {0};
849 fs_sexa(RAStr, targetRA, 2, 3600);
850 fs_sexa(DecStr, targetDEC, 2, 3600);
852 if (driver->setRA(
ra) ==
false || driver->setDE(de) ==
false)
860 rc = driver->slewNormal();
862 rc = driver->slewCWUp();
870 TrackState = SCOPE_SLEWING;
872 LOGF_INFO(
"Slewing to RA: %s - DEC: %s", RAStr, DecStr);
878 if (driver->setRA(
ra) ==
false || driver->setDE(de) ==
false)
884 if (driver->sync() ==
false)
901 return driver->abort();
916 TrackState = SCOPE_PARKING;
917 m_ParkingCounter = 0;
918 LOG_INFO(
"Parking is in progress...");
936 if (driver->unpark())
939 TrackState = SCOPE_IDLE;
948 driver->setSimulation(isSimulation());
950 if (driver->checkConnection(PortFD) ==
false)
958 bool rc1 = driver->setUTCDateTime(ln_get_julian_day(utc));
960 bool rc2 = driver->setUTCOffset(utc_offset * 60);
972 if (driver->setLongitude(longitude) ==
false)
978 if (driver->setLatitude(latitude) ==
false)
984 char l[32] = {0}, L[32] = {0};
986 fs_sexa(L, longitude, 4, 3600);
988 LOGF_INFO(
"Site location updated to Lat %.32s - Long %.32s", l, L);
995 driver->setDebug(enable);
1000 driver->setSimulation(enable);
1005 if (TrackState == SCOPE_PARKED)
1007 LOG_ERROR(
"Please unpark the mount before issuing any motion commands.");
1016 LOG_ERROR(
"Error setting N/S motion direction.");
1026 LOG_ERROR(
"Error stopping N/S motion.");
1039 if (TrackState == SCOPE_PARKED)
1041 LOG_ERROR(
"Please unpark the mount before issuing any motion commands.");
1050 LOG_ERROR(
"Error setting N/S motion direction.");
1060 LOG_ERROR(
"Error stopping W/E motion.");
1073 bool rc = driver->startGuide(
IOP_N, (uint32_t)ms);
1079 bool rc = driver->startGuide(
IOP_S, (uint32_t)ms);
1085 bool rc = driver->startGuide(
IOP_E, (uint32_t)ms);
1091 bool rc = driver->startGuide(
IOP_W, (uint32_t)ms);
1098 return driver->setSlewRate(rate);
1116 static struct timeval ltv;
1122 gettimeofday(&tv,
nullptr);
1124 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1127 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1130 da = currentSlewRate * dt;
1140 case SCOPE_TRACKING:
1187 if (TrackState == SCOPE_SLEWING)
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);
1226 SetAxis2Park(LocationN[LOCATION_LATITUDE].value);
1227 driver->setParkAz(0);
1228 driver->setParkAlt(LocationN[LOCATION_LATITUDE].value);
1236 if (driver->setTrackMode(rate))
1249 if (ieqRARate < 0.1 || ieqRARate > 1.9)
1251 LOG_ERROR(
"Rate is outside permitted limits of 0.1 to 1.9 sidereal rate (1.504 to 28.578 arcsecs/s)");
1256 if (driver->setCustomRATrackRate(ieqRARate))
1270 SetTrackRate(TrackRateN[
AXIS_RA].value, TrackRateN[
AXIS_DE].value);
1273 return driver->setTrackEnabled(enabled);
1277 bool IOptronV3::GetPECDataStatus(
bool enabled)
1279 if(driver->getPETEnabled(
true))
1285 LOG_INFO(
"Mount PEC Chip Ready and Trained");
1295 LOG_INFO(
"Mount PEC Chip Needs Training");
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.
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
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 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.
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.
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 * 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.
double range24(double r)
range24 Limits a number to be between 0-24 range.
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[].
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
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
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.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
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.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
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.
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.
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.
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.
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.
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,...)
#define LOGF_WARN(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Encapsulates classes and structures required for iOptron Command Set v3 implementation.
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.
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
IOP_HEMISPHERE hemisphere
IOP_SYSTEM_STATUS systemStatus
IOP_TIME_SOURCE timeSource