41 static std::unique_ptr<SkywatcherAPIMount> SkywatcherAPIMountPtr(
new SkywatcherAPIMount());
45 static double SlewSpeeds[
SLEWMODES] = { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 600.0 };
53 pChildTelescope =
this;
54 SetTelescopeCapability(TELESCOPE_CAN_PARK |
59 TELESCOPE_HAS_LOCATION |
60 TELESCOPE_HAS_TRACK_MODE |
61 TELESCOPE_CAN_CONTROL_TRACK,
64 m_LastCustomDirection[AXIS1] = m_LastCustomDirection[AXIS2] = 0;
74 if (!getActiveConnection()->name().compare(
"CONNECTION_TCP"))
79 SetSerialPort(PortFD);
80 bool Result = InitMount();
81 DEBUGF(
DBG_SCOPE,
"SkywatcherAPIMount::Handshake - Result: %d", Result);
90 return "Skywatcher Alt-Az";
101 for (
int i = 0; i < SlewRateSP.nsp; ++i)
103 sprintf(SlewRateSP.sp[i].label,
"%.fx",
SlewSpeeds[i]);
106 strncpy(SlewRateSP.sp[SlewRateSP.nsp - 1].name,
"SLEW_MAX",
MAXINDINAME);
108 AddTrackMode(
"TRACK_SIDEREAL",
"Sidereal",
true);
109 AddTrackMode(
"TRACK_SOLAR",
"Solar");
110 AddTrackMode(
"TRACK_LUNAR",
"Lunar");
114 addConfigurationControl();
117 InitAlignmentProperties(
this);
120 getSwitch(
"ALIGNMENT_SUBSYSTEM_ACTIVE")[0].setState(
ISS_ON);
123 IUFillText(&BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION],
"MOTOR_CONTROL_FIRMWARE_VERSION",
124 "Motor control firmware version",
"-");
125 IUFillText(&BasicMountInfoT[MOUNT_CODE],
"MOUNT_CODE",
"Mount code",
"-");
126 IUFillText(&BasicMountInfoT[MOUNT_NAME],
"MOUNT_NAME",
"Mount name",
"-");
127 IUFillText(&BasicMountInfoT[IS_DC_MOTOR],
"IS_DC_MOTOR",
"Is DC motor",
"-");
129 "Basic mount information", MountInfoTab,
IP_RO, 60,
IPS_IDLE);
131 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_REVOLUTION],
"MICROSTEPS_PER_REVOLUTION",
"Microsteps per revolution",
132 "%.0f", 0, 0xFFFFFF, 1, 0);
133 IUFillNumber(&AxisOneInfoN[STEPPER_CLOCK_FREQUENCY],
"STEPPER_CLOCK_FREQUENCY",
"Stepper clock frequency",
"%.0f", 0,
135 IUFillNumber(&AxisOneInfoN[HIGH_SPEED_RATIO],
"HIGH_SPEED_RATIO",
"High speed ratio",
"%.0f", 0, 0xFFFFFF, 1, 0);
136 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION],
"MICROSTEPS_PER_WORM_REVOLUTION",
137 "Microsteps per worm revolution",
"%.0f", 0, 0xFFFFFF, 1, 0);
145 IUFillSwitch(&AxisOneStateS[SLEWING_FORWARD],
"SLEWING_FORWARD",
"SLEWING_FORWARD",
ISS_OFF);
147 IUFillSwitch(&AxisOneStateS[NOT_INITIALISED],
"NOT_INITIALISED",
"NOT_INITIALISED",
ISS_ON);
151 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION],
"MICROSTEPS_PER_REVOLUTION",
"Microsteps per revolution",
152 "%.0f", 0, 0xFFFFFF, 1, 0);
153 IUFillNumber(&AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY],
"STEPPER_CLOCK_FREQUENCY",
"Step timer frequency",
"%.0f", 0,
155 IUFillNumber(&AxisTwoInfoN[HIGH_SPEED_RATIO],
"HIGH_SPEED_RATIO",
"High speed ratio",
"%.0f", 0, 0xFFFFFF, 1, 0);
156 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION],
"MICROSTEPS_PER_WORM_REVOLUTION",
157 "Microsteps per worm revolution",
"%.0f", 0, 0xFFFFFF, 1, 0);
165 IUFillSwitch(&AxisTwoStateS[SLEWING_FORWARD],
"SLEWING_FORWARD",
"SLEWING_FORWARD",
ISS_OFF);
167 IUFillSwitch(&AxisTwoStateS[NOT_INITIALISED],
"NOT_INITIALISED",
"NOT_INITIALISED",
ISS_ON);
171 IUFillNumber(&AxisOneEncoderValuesN[RAW_MICROSTEPS],
"RAW_MICROSTEPS",
"Raw Microsteps",
"%.0f", 0, 0xFFFFFF, 1, 0);
172 IUFillNumber(&AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC],
"MICROSTEPS_PER_ARCSEC",
"Microsteps/arcsecond",
173 "%.4f", 0, 0xFFFFFF, 1, 0);
174 IUFillNumber(&AxisOneEncoderValuesN[OFFSET_FROM_INITIAL],
"OFFSET_FROM_INITIAL",
"Offset from initial",
"%.0f", 0,
176 IUFillNumber(&AxisOneEncoderValuesN[DEGREES_FROM_INITIAL],
"DEGREES_FROM_INITIAL",
"Degrees from initial",
"%.2f",
177 -1000.0, 1000.0, 1, 0);
182 IUFillNumber(&AxisTwoEncoderValuesN[RAW_MICROSTEPS],
"RAW_MICROSTEPS",
"Raw Microsteps",
"%.0f", 0, 0xFFFFFF, 1, 0);
183 IUFillNumber(&AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC],
"MICROSTEPS_PER_ARCSEC",
"Microsteps/arcsecond",
184 "%.4f", 0, 0xFFFFFF, 1, 0);
185 IUFillNumber(&AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL],
"OFFSET_FROM_INITIAL",
"Offset from initial",
"%.0f", 0,
187 IUFillNumber(&AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL],
"DEGREES_FROM_INITIAL",
"Degrees from initial",
"%.2f",
188 -1000.0, 1000.0, 1, 0);
201 IUFillSwitch(&SoftPECModesS[SOFTPEC_ENABLED],
"SOFTPEC_ENABLED",
"Enable for tracking",
ISS_OFF);
202 IUFillSwitch(&SoftPECModesS[SOFTPEC_DISABLED],
"SOFTPEC_DISABLED",
"Disabled",
ISS_ON);
207 IUFillNumber(&SoftPecN,
"SOFTPEC_VALUE",
"degree/minute (Alt)",
"%1.3f", 0.001, 1.0, 0.001, 0.009);
212 IUFillNumber(&GuidingRatesN[0],
"GUIDERA_RATE",
"arcsec/seconds (RA)",
"%1.3f", 1.0, 6000.0, 1.0, 120.0);
213 IUFillNumber(&GuidingRatesN[1],
"GUIDEDEC_RATE",
"arcsec/seconds (Dec)",
"%1.3f", 1.0, 6000.0, 1.0, 120.0);
218 AUXEncoderSP[INDI_ENABLED].fill(
"INDI_ENABLED",
"Enabled",
ISS_ON);
219 AUXEncoderSP[INDI_DISABLED].fill(
"INDI_DISABLED",
"Disabled",
ISS_OFF);
223 SnapPortSP[INDI_ENABLED].fill(
"INDI_ENABLED",
"On",
ISS_OFF);
224 SnapPortSP[INDI_DISABLED].fill(
"INDI_DISABLED",
"Off",
ISS_ON);
228 Axis1PIDNP[Propotional].fill(
"Propotional",
"Propotional",
"%.2f", 0.1, 100, 1, 1.1);
229 Axis1PIDNP[Derivative].fill(
"Derivative",
"Derivative",
"%.2f", 0, 500, 10, 0.01);
230 Axis1PIDNP[Integral].fill(
"Integral",
"Integral",
"%.2f", 0, 500, 10, 0.65);
233 Axis2PIDNP[Propotional].fill(
"Propotional",
"Propotional",
"%.2f", 0.1, 100, 1, 0.75);
234 Axis2PIDNP[Derivative].fill(
"Derivative",
"Derivative",
"%.2f", 0, 100, 10, 0.01);
235 Axis2PIDNP[Integral].fill(
"Integral",
"Integral",
"%.2f", 0, 100, 10, 0.13);
239 AxisDeadZoneNP[AXIS1].fill(
"AXIS1",
"AZ (steps)",
"%.f", 0, 100, 10, 10);
240 AxisDeadZoneNP[AXIS2].fill(
"AXIS2",
"AL (steps)",
"%.f", 0, 100, 10, 10);
244 AxisClockNP[AXIS1].fill(
"AXIS1",
"AZ %",
"%.f", 1, 200, 10, 100);
245 AxisClockNP[AXIS2].fill(
"AXIS2",
"AL %",
"%.f", 1, 200, 10, 100);
249 AxisOffsetNP[RAOffset].fill(
"RAOffset",
"RA (deg)",
"%.2f", -1, 1, 0.05, 0);
250 AxisOffsetNP[DEOffset].fill(
"DEOffset",
"DE (deg)",
"%.2f", -1, 1, 0.05, 0);
251 AxisOffsetNP[AZSteps].fill(
"AZEncoder",
"AZ (steps)",
"%.f", -10000, 10000, 1000, 0);
252 AxisOffsetNP[ALSteps].fill(
"ALEncoder",
"AL (steps)",
"%.f", -10000, 10000, 1000, -100.0);
253 AxisOffsetNP[JulianOffset].fill(
"JulianOffset",
"JD (s)",
"%.f", -5, 5, 0.1, 0);
257 Axis1TrackRateNP[TrackDirection].fill(
"TrackDirection",
"West/East",
"%.f", 0, 1, 1, 0);
258 Axis1TrackRateNP[TrackClockRate].fill(
"TrackClockRate",
"Freq/Step (Hz/s)",
"%.f", 0, 16000000, 500000, 0);
261 Axis2TrackRateNP[TrackDirection].fill(
"TrackDirection",
"North/South",
"%.f", 0, 1, 1, 0);
262 Axis2TrackRateNP[TrackClockRate].fill(
"TrackClockRate",
"Freq/Stel (Hz/s)",
"%.f", 0, 16000000, 500000, 0);
266 tcpConnection->setDefaultHost(
"192.168.4.1");
267 tcpConnection->setDefaultPort(11880);
272 setActiveConnection(serialConnection);
276 setActiveConnection(tcpConnection);
277 tcpConnection->setLANSearchEnabled(
true);
280 SetParkDataType(PARK_AZ_ALT_ENCODER);
284 setDriverInterface(getDriverInterface() | GUIDER_INTERFACE);
287 IUFindNumber(&ScopeParametersNP,
"TELESCOPE_APERTURE")->value = 200;
288 IUFindNumber(&ScopeParametersNP,
"TELESCOPE_FOCAL_LENGTH")->value = 2000;
289 IUFindNumber(&ScopeParametersNP,
"GUIDER_APERTURE")->value = 30;
290 IUFindNumber(&ScopeParametersNP,
"GUIDER_FOCAL_LENGTH")->value = 120;
299 char *formats[],
char *names[],
int n)
304 ProcessAlignmentBLOBProperties(
this, name, sizes, blobsizes, blobs, formats, names, n);
317 ProcessAlignmentNumberProperties(
this, name, values, names, n);
319 if (strcmp(name,
"SOFTPEC") == 0)
327 if (strcmp(name,
"GUIDE_RATES") == 0)
330 GuidingRatesNP.s =
IPS_OK;
337 if (AxisDeadZoneNP.isNameMatch(name))
339 AxisDeadZoneNP.update(values, names, n);
340 AxisDeadZoneNP.setState(
IPS_OK);
341 AxisDeadZoneNP.apply();
342 saveConfig(
true, AxisDeadZoneNP.getName());
347 if (AxisClockNP.isNameMatch(name))
349 AxisClockNP.update(values, names, n);
350 AxisClockNP.setState(
IPS_OK);
352 saveConfig(
true, AxisClockNP.getName());
357 if (AxisOffsetNP.isNameMatch(name))
359 AxisOffsetNP.update(values, names, n);
360 AxisOffsetNP.setState(
IPS_OK);
361 AxisOffsetNP.apply();
362 saveConfig(
true, AxisOffsetNP.getName());
367 if (Axis1TrackRateNP.isNameMatch(name))
369 Axis1TrackRateNP.update(values, names, n);
370 Axis1TrackRateNP.setState(
IPS_OK);
371 Axis1TrackRateNP.apply();
372 saveConfig(
true, Axis1TrackRateNP.getName());
377 if (Axis2TrackRateNP.isNameMatch(name))
379 Axis2TrackRateNP.update(values, names, n);
380 Axis2TrackRateNP.setState(
IPS_OK);
381 Axis2TrackRateNP.apply();
382 saveConfig(
true, Axis2TrackRateNP.getName());
387 if (Axis1PIDNP.isNameMatch(name))
389 Axis1PIDNP.update(values, names, n);
390 Axis1PIDNP.setState(
IPS_OK);
392 saveConfig(Axis1PIDNP);
394 m_Controllers[AXIS1].reset(
new PID(getPollingPeriod() / 1000, 50, -50,
395 Axis1PIDNP[Propotional].getValue(),
396 Axis1PIDNP[Derivative].getValue(),
397 Axis1PIDNP[Integral].getValue()));
402 if (Axis2PIDNP.isNameMatch(name))
404 Axis2PIDNP.update(values, names, n);
405 Axis2PIDNP.setState(
IPS_OK);
407 saveConfig(Axis2PIDNP);
409 m_Controllers[AXIS2].reset(
new PID(getPollingPeriod() / 1000, 50, -50,
410 Axis2PIDNP[Propotional].getValue(),
411 Axis2PIDNP[Derivative].getValue(),
412 Axis2PIDNP[Integral].getValue()));
418 if (strcmp(name,
"EQUATORIAL_EOD_COORD") == 0)
423 for (
int x = 0; x < n; x++)
435 if ((
ra >= 0) && (
ra <= 24) && (
dec >= -90) && (
dec <= 90))
446 processGuiderProperties(name, values, names, n);
460 if (AUXEncoderSP.isNameMatch(name))
462 AUXEncoderSP.update(states, names, n);
463 AUXEncoderSP.setState(
IPS_OK);
464 AUXEncoderSP.apply();
465 auto enabled = AUXEncoderSP.findOnSwitchIndex() == INDI_ENABLED;
466 TurnRAEncoder(enabled);
467 TurnDEEncoder(enabled);
468 saveConfig(
true, AUXEncoderSP.getName());
473 if (SnapPortSP.isNameMatch(name))
475 SnapPortSP.update(states, names, n);
476 auto enabled = SnapPortSP.findOnSwitchIndex() == INDI_ENABLED;
477 toggleSnapPort(enabled);
479 LOG_INFO(
"Toggling snap port on...");
481 LOG_INFO(
"Toggling snap port off...");
487 ProcessAlignmentSwitchProperties(
this, name, states, names, n);
500 ProcessAlignmentTextProperties(
this, name, texts, names, n);
511 if (m_IterativeGOTOPending)
513 char RAStr[32], DecStr[32];
514 fs_sexa(RAStr, m_SkyCurrentRADE.rightascension, 2, 3600);
515 fs_sexa(DecStr, m_SkyCurrentRADE.declination, 2, 3600);
521 if (TrackState != SCOPE_IDLE)
528 char RAStr[32], DecStr[32];
531 m_SkyTrackingTarget.rightascension =
ra;
532 m_SkyTrackingTarget.declination =
dec;
533 LOGF_INFO(
"Goto target RA %s DEC %s", RAStr, DecStr);
543 if (TransformCelestialToTelescope(
ra,
dec, 0.0, TDV))
546 AltitudeAzimuthFromTelescopeDirectionVector(TDV,
AltAz);
549 char RAStr[32], DecStr[32];
550 fs_sexa(RAStr, EquatorialCoordinates.rightascension, 2, 3600);
551 fs_sexa(DecStr, EquatorialCoordinates.declination, 2, 3600);
561 EquatorialCoordinates.declination =
dec;
563 TDV = TelescopeDirectionVectorFromAltitudeAzimuth(
AltAz);
564 switch (GetApproximateMountAlignment())
581 AltitudeAzimuthFromTelescopeDirectionVector(TDV,
AltAz);
585 "Sky -> Mount AZ %lf° (%ld) AL %lf° (%ld)",
587 DegreesToMicrosteps(AXIS1,
AltAz.azimuth),
589 DegreesToMicrosteps(AXIS2,
AltAz.altitude));
595 long AzimuthOffsetMicrosteps = DegreesToMicrosteps(AXIS1,
596 AltAz.azimuth) + ZeroPositionEncoders[AXIS1] - (CurrentEncoders[AXIS1] - AxisOffsetNP[AZSteps].getValue());
597 long AltitudeOffsetMicrosteps = DegreesToMicrosteps(AXIS2,
598 AltAz.altitude) + ZeroPositionEncoders[AXIS2] - (CurrentEncoders[AXIS2] - AxisOffsetNP[ALSteps].getValue());
600 if (AzimuthOffsetMicrosteps > MicrostepsPerRevolution[AXIS1] / 2)
603 AzimuthOffsetMicrosteps -= MicrostepsPerRevolution[AXIS1];
607 if (AltitudeOffsetMicrosteps > MicrostepsPerRevolution[AXIS2] / 2)
610 AltitudeOffsetMicrosteps -= MicrostepsPerRevolution[AXIS2];
614 CurrentEncoders[AXIS1], ZeroPositionEncoders[AXIS1], CurrentEncoders[AXIS2], ZeroPositionEncoders[AXIS2]);
616 AzimuthOffsetMicrosteps, AltitudeOffsetMicrosteps);
618 SilentSlewMode = (
IUFindSwitch(&SlewModesSP,
"SLEW_SILENT") !=
nullptr
621 SlewTo(AXIS1, AzimuthOffsetMicrosteps);
622 SlewTo(AXIS2, AltitudeOffsetMicrosteps);
624 TrackState = SCOPE_SLEWING;
639 UpdateDetailedMountInformation(
false);
644 defineProperty(&BasicMountInfoTP);
645 defineProperty(&AxisOneInfoNP);
646 defineProperty(&AxisOneStateSP);
647 defineProperty(&AxisTwoInfoNP);
648 defineProperty(&AxisTwoStateSP);
649 defineProperty(&AxisOneEncoderValuesNP);
650 defineProperty(&AxisTwoEncoderValuesNP);
651 defineProperty(&SlewModesSP);
652 defineProperty(&SoftPECModesSP);
653 defineProperty(&SoftPecNP);
654 defineProperty(&GuidingRatesNP);
655 defineProperty(&GuideNSNP);
656 defineProperty(&GuideWENP);
666 return *(
static_cast<double *
>(Switch->aux));
675 (dir ==
DIRECTION_NORTH) ? GetSlewRate() * LOW_SPEED_MARGIN / 2 : -GetSlewRate() * LOW_SPEED_MARGIN / 2;
683 Slew(AXIS2, speed,
true);
684 m_ManualMotionActive =
true;
702 (dir ==
DIRECTION_WEST) ? -GetSlewRate() * LOW_SPEED_MARGIN / 2 : GetSlewRate() * LOW_SPEED_MARGIN / 2;
710 Slew(AXIS1, speed,
true);
711 m_ManualMotionActive =
true;
729 long AltitudeOffsetMicrosteps = GetAxis2Park() - CurrentEncoders[AXIS2];
730 long AzimuthOffsetMicrosteps = GetAxis1Park() - CurrentEncoders[AXIS1];
732 "Parking: Altitude offset %ld microsteps Azimuth offset %ld microsteps", AltitudeOffsetMicrosteps,
733 AzimuthOffsetMicrosteps);
737 SilentSlewMode =
true;
741 SilentSlewMode =
false;
743 SlewTo(AXIS1, AzimuthOffsetMicrosteps);
744 SlewTo(AXIS2, AltitudeOffsetMicrosteps);
746 TrackState = SCOPE_PARKING;
766 TrackState = SCOPE_TRACKING;
768 m_SkyTrackingTarget.rightascension = EqN[
AXIS_RA].value;
769 m_SkyTrackingTarget.declination = EqN[
AXIS_DE].value;
773 TrackState = SCOPE_IDLE;
776 TrackState = SCOPE_IDLE;
780 GuideNSNP.s = GuideWENP.s =
IPS_IDLE;
781 GuideNSN[0].value = GuideNSN[1].value = 0.0;
782 GuideWEN[0].value = GuideWEN[1].value = 0.0;
796 if (!GetStatus(AXIS1))
799 if (!GetStatus(AXIS2))
803 if (!GetEncoder(AXIS1))
805 if (!GetEncoder(AXIS2))
808 UpdateDetailedMountInformation(
true);
810 bool resetTrackingTimers =
false;
815 CurrentEncoders[AXIS1] - AxisOffsetNP[AZSteps].getValue() - ZeroPositionEncoders[AXIS1]));
816 AltAz.altitude = MicrostepsToDegrees(AXIS2,
817 CurrentEncoders[AXIS2] - AxisOffsetNP[ALSteps].getValue() - ZeroPositionEncoders[AXIS2]);
819 CurrentEncoders[AXIS1], ZeroPositionEncoders[AXIS1],
AltAz.azimuth);
821 CurrentEncoders[AXIS2], ZeroPositionEncoders[AXIS2],
AltAz.altitude);
824 m_MountAltAz =
AltAz;
827 getCurrentRADE(
AltAz, m_SkyCurrentRADE);
828 char RAStr[32], DecStr[32];
829 fs_sexa(RAStr, m_SkyCurrentRADE.rightascension, 2, 3600);
830 fs_sexa(DecStr, m_SkyCurrentRADE.declination, 2, 3600);
833 if (TrackState == SCOPE_SLEWING)
835 if ((AxesStatus[AXIS1].FullStop) && (AxesStatus[AXIS2].FullStop))
838 if (m_IterativeGOTOPending)
839 m_IterativeGOTOPending =
false;
843 m_IterativeGOTOPending =
true;
844 return Goto(m_SkyTrackingTarget.rightascension, m_SkyTrackingTarget.declination);
850 TrackState = SCOPE_TRACKING;
851 resetTrackingTimers =
true;
856 TrackState = SCOPE_IDLE;
860 else if (TrackState == SCOPE_PARKING)
862 if (!IsInMotion(AXIS1) && !IsInMotion(AXIS2))
870 if (resetTrackingTimers)
873 NewRaDec(m_SkyCurrentRADE.rightascension, m_SkyCurrentRADE.declination);
883 if (GetEncoder(AXIS1) && GetEncoder(AXIS2))
886 CurrentEncoders[AXIS1] - AxisOffsetNP[AZSteps].getValue() - ZeroPositionEncoders[AXIS1]));
887 altaz.
altitude = MicrostepsToDegrees(AXIS2,
888 CurrentEncoders[AXIS2] - AxisOffsetNP[ALSteps].getValue() - ZeroPositionEncoders[AXIS2]);
903 double RightAscension, Declination;
904 if (!TransformTelescopeToCelestial(TDV, RightAscension, Declination))
907 switch (GetApproximateMountAlignment())
915 RotatedTDV.RotateAroundY(90.0 - m_Location.latitude);
916 AltitudeAzimuthFromTelescopeDirectionVector(RotatedTDV, altaz);
922 RotatedTDV.RotateAroundY(-90.0 - m_Location.latitude);
923 AltitudeAzimuthFromTelescopeDirectionVector(RotatedTDV, altaz);
942 SaveAlignmentConfigProperties(fp);
946 AxisDeadZoneNP.save(fp);
947 AxisClockNP.save(fp);
948 AxisOffsetNP.save(fp);
949 Axis1TrackRateNP.save(fp);
950 Axis2TrackRateNP.save(fp);
963 if (!GetEncoder(AXIS1))
965 if (!GetEncoder(AXIS2))
975 if (TransformCelestialToTelescope(
ra,
dec, 0.0, TDV))
977 AltitudeAzimuthFromTelescopeDirectionVector(TDV,
AltAz);
978 double OrigAlt =
AltAz.altitude;
979 ZeroPositionEncoders[AXIS1] = PolarisPositionEncoders[AXIS1] - DegreesToMicrosteps(AXIS1,
AltAz.azimuth);
980 ZeroPositionEncoders[AXIS2] = PolarisPositionEncoders[AXIS2] - DegreesToMicrosteps(AXIS2,
AltAz.altitude);
981 LOGF_INFO(
"Sync (Alt: %lf Az: %lf) in park position", OrigAlt,
AltAz.azimuth);
982 GetAlignmentDatabase().clear();
988 UpdateDetailedMountInformation(
true);
993 CurrentEncoders[AXIS1] - AxisOffsetNP[AZSteps].getValue() - ZeroPositionEncoders[AXIS1]));
994 AltAz.altitude = MicrostepsToDegrees(AXIS2,
995 CurrentEncoders[AXIS2] - AxisOffsetNP[ALSteps].getValue() - ZeroPositionEncoders[AXIS2]);
998 CurrentEncoders[AXIS1], ZeroPositionEncoders[AXIS1],
AltAz.azimuth);
1000 CurrentEncoders[AXIS2], ZeroPositionEncoders[AXIS2],
AltAz.altitude);
1013 m_IterativeGOTOPending =
false;
1015 if (!CheckForDuplicateSyncPoint(NewEntry))
1017 GetAlignmentDatabase().push_back(NewEntry);
1042 m_IterativeGOTOPending =
false;
1045 TrackState = SCOPE_IDLE;
1049 GuideNSNP.s = GuideWENP.s =
IPS_IDLE;
1050 GuideNSN[0].value = GuideNSN[1].value = 0.0;
1051 GuideWEN[0].value = GuideWEN[1].value = 0.0;
1077 GuidingPulses.clear();
1080 case SCOPE_TRACKING:
1083 if (m_ManualMotionActive && !IsInMotion(AXIS1) && !IsInMotion(AXIS2))
1085 m_ManualMotionActive =
false;
1089 if (m_ManualMotionActive)
1104 if (TrackModeS[TRACK_LUNAR].s ==
ISS_ON)
1109 m_SkyTrackingTarget.rightascension += (dRA / 3600.0) * 15.0;
1110 m_TrackingRateTimer.restart();
1112 else if (TrackModeS[TRACK_SOLAR].s ==
ISS_ON)
1115 m_SkyTrackingTarget.rightascension += (dRA / 3600.0) * 15.0;
1116 m_TrackingRateTimer.restart();
1119 auto ra = m_SkyTrackingTarget.rightascension + AxisOffsetNP[RAOffset].getValue() / 15.0;
1120 auto de = m_SkyTrackingTarget.declination + AxisOffsetNP[DEOffset].getValue();
1121 auto JDOffset = AxisOffsetNP[JulianOffset].getValue() / 86400.0;
1123 if (TransformCelestialToTelescope(
ra, de, JDOffset, TDV))
1126 AltitudeAzimuthFromTelescopeDirectionVector(TDV,
AltAz);
1135 "New Tracking Target AZ %lf° (%ld microsteps) AL %lf° (%ld microsteps) ",
1137 DegreesToMicrosteps(AXIS1,
AltAz.azimuth),
1139 DegreesToMicrosteps(AXIS2,
AltAz.altitude));
1142 double DeltaAlt = 0;
1145 for (
auto Iter = GuidingPulses.begin(); Iter != GuidingPulses.end(); )
1148 if (Iter->OriginalDuration == 1000)
1150 DeltaAlt += Iter->DeltaAlt;
1151 DeltaAz += Iter->DeltaAz;
1155 DeltaAlt += Iter->DeltaAlt / 2;
1156 DeltaAz += Iter->DeltaAz / 2;
1158 Iter->Duration -= getCurrentPollingPeriod();
1160 if (Iter->Duration <
static_cast<int>(getCurrentPollingPeriod()))
1162 Iter = GuidingPulses.erase(Iter);
1163 if (Iter == GuidingPulses.end())
1172 GuideDeltaAlt += DeltaAlt;
1173 GuideDeltaAz += DeltaAz;
1175 long SetPoint[2] = {0, 0}, Measurement[2] = {0, 0}, Error[2] = {0, 0};
1176 double TrackingRate[2] = {0, 0};
1178 SetPoint[AXIS1] = DegreesToMicrosteps(AXIS1,
AltAz.azimuth + GuideDeltaAz);
1179 Measurement[AXIS1] = CurrentEncoders[AXIS1] - AxisOffsetNP[AZSteps].getValue() - ZeroPositionEncoders[AXIS1];
1181 SetPoint[AXIS2] = DegreesToMicrosteps(AXIS2,
AltAz.altitude + GuideDeltaAlt);
1182 Measurement[AXIS2] = CurrentEncoders[AXIS2] - AxisOffsetNP[ALSteps].getValue() - ZeroPositionEncoders[AXIS2];
1185 while (SetPoint[AXIS1] > MicrostepsPerRevolution[AXIS1] / 2)
1186 SetPoint[AXIS1] -= MicrostepsPerRevolution[AXIS1];
1188 while (SetPoint[AXIS2] > MicrostepsPerRevolution[AXIS2] / 2)
1189 SetPoint[AXIS2] -= MicrostepsPerRevolution[AXIS2];
1191 Error[AXIS1] = SetPoint[AXIS1] - Measurement[AXIS1];
1192 Error[AXIS2] = SetPoint[AXIS2] - Measurement[AXIS2];
1194 auto Axis1CustomClockRate = Axis1TrackRateNP[TrackClockRate].getValue();
1197 if (!AxesStatus[AXIS1].FullStop && (
1198 (Axis1CustomClockRate == 0 && ((AxesStatus[AXIS1].SlewingForward && (Error[AXIS1] < -AxisDeadZoneNP[AXIS1].getValue())) ||
1199 (!AxesStatus[AXIS1].SlewingForward && (Error[AXIS1] > AxisDeadZoneNP[AXIS1].getValue())))) ||
1200 (Axis1CustomClockRate > 0 && Axis1TrackRateNP[TrackDirection].getValue() != m_LastCustomDirection[AXIS1])))
1202 m_LastCustomDirection[AXIS1] = Axis1TrackRateNP[TrackDirection].getValue();
1205 LOG_DEBUG(
"Tracking -> AXIS1 direction change.");
1206 LOGF_DEBUG(
"AXIS1 Setpoint %d Measurement %d Error %d Rate %f",
1210 TrackingRate[AXIS1]);
1215 TrackingRate[AXIS1] = m_Controllers[AXIS1]->calculate(SetPoint[AXIS1], Measurement[AXIS1]);
1216 char Direction = TrackingRate[AXIS1] > 0 ?
'0' :
'1';
1217 TrackingRate[AXIS1] = std::fabs(TrackingRate[AXIS1]);
1218 if (TrackingRate[AXIS1] != 0)
1220 auto clockRate = (StepperClockFrequency[AXIS1] / TrackingRate[AXIS1]) * (AxisClockNP[AXIS1].getValue() / 100.0);
1222 if (Axis1CustomClockRate > 0)
1224 clockRate = Axis1CustomClockRate;
1225 Direction = Axis1TrackRateNP[TrackDirection].getValue() == 0 ?
'0' :
'1';
1228 LOGF_DEBUG(
"AXIS1 Setpoint %d Measurement %d Error %d Rate %f Freq %f Dir %s",
1232 TrackingRate[AXIS1],
1234 Direction ==
'0' ?
"Forward" :
"Backward");
1237 m_Controllers[AXIS1]->propotionalTerm(),
1238 m_Controllers[AXIS1]->integralTerm(),
1239 m_Controllers[AXIS1]->derivativeTerm());
1242 SetClockTicksPerMicrostep(AXIS1, clockRate);
1243 if (AxesStatus[AXIS1].FullStop)
1245 LOG_DEBUG(
"Tracking -> AXIS1 restart.");
1246 SetAxisMotionMode(AXIS1,
'1',
Direction);
1247 StartAxisMotion(AXIS1);
1253 auto Axis2CustomClockRate = Axis2TrackRateNP[TrackClockRate].getValue();
1255 if (!AxesStatus[AXIS2].FullStop && (
1256 (Axis2CustomClockRate == 0 && ((AxesStatus[AXIS2].SlewingForward && (Error[AXIS2] < -AxisDeadZoneNP[AXIS2].getValue())) ||
1257 (!AxesStatus[AXIS2].SlewingForward && (Error[AXIS2] > AxisDeadZoneNP[AXIS2].getValue())))) ||
1258 (Axis2CustomClockRate > 0 && Axis2TrackRateNP[TrackDirection].getValue() != m_LastCustomDirection[AXIS2])))
1260 m_LastCustomDirection[AXIS2] = Axis2TrackRateNP[TrackDirection].getValue();
1262 LOG_DEBUG(
"Tracking -> AXIS2 direction change.");
1263 LOGF_DEBUG(
"AXIS2 Setpoint %d Measurement %d Error %d Rate %f",
1267 TrackingRate[AXIS2]);
1272 TrackingRate[AXIS2] = m_Controllers[AXIS2]->calculate(SetPoint[AXIS2], Measurement[AXIS2]);
1273 char Direction = TrackingRate[AXIS2] > 0 ?
'0' :
'1';
1274 TrackingRate[AXIS2] = std::fabs(TrackingRate[AXIS2]);
1275 if (TrackingRate[AXIS2] != 0)
1277 auto clockRate = StepperClockFrequency[AXIS2] / TrackingRate[AXIS2] * (AxisClockNP[AXIS2].getValue() / 100.0);
1279 if (Axis2CustomClockRate > 0)
1281 clockRate = Axis2CustomClockRate;
1282 Direction = Axis2TrackRateNP[TrackDirection].getValue() == 0 ?
'0' :
'1';
1285 LOGF_DEBUG(
"AXIS2 Setpoint %d Measurement %d Error %d Rate %f Freq %f Dir %s",
1289 TrackingRate[AXIS2],
1291 Error[AXIS2] > 0 ?
"Forward" :
"Backward");
1294 m_Controllers[AXIS2]->propotionalTerm(),
1295 m_Controllers[AXIS2]->integralTerm(),
1296 m_Controllers[AXIS2]->derivativeTerm());
1299 SetClockTicksPerMicrostep(AXIS2, clockRate);
1300 if (AxesStatus[AXIS2].FullStop)
1302 LOG_DEBUG(
"Tracking -> AXIS2 restart.");
1303 SetAxisMotionMode(AXIS2,
'1',
Direction);
1304 StartAxisMotion(AXIS2);
1317 GuidingPulses.clear();
1328 UpdateLocation(latitude, longitude, elevation);
1342 if (m_Location.longitude > 0)
1343 UpdateLocation(m_Location.latitude, m_Location.longitude, m_Location.elevation);
1346 UpdateDetailedMountInformation(
false);
1353 defineProperty(&BasicMountInfoTP);
1354 defineProperty(&AxisOneInfoNP);
1355 defineProperty(&AxisOneStateSP);
1356 defineProperty(&AxisTwoInfoNP);
1357 defineProperty(&AxisTwoStateSP);
1358 defineProperty(&AxisOneEncoderValuesNP);
1359 defineProperty(&AxisTwoEncoderValuesNP);
1360 defineProperty(&SlewModesSP);
1361 defineProperty(&SoftPECModesSP);
1362 defineProperty(&SoftPecNP);
1363 defineProperty(&GuidingRatesNP);
1364 defineProperty(&GuideNSNP);
1365 defineProperty(&GuideWENP);
1366 defineProperty(Axis1PIDNP);
1367 defineProperty(Axis2PIDNP);
1368 defineProperty(AxisDeadZoneNP);
1369 defineProperty(AxisClockNP);
1370 defineProperty(AxisOffsetNP);
1371 defineProperty(Axis1TrackRateNP);
1372 defineProperty(Axis2TrackRateNP);
1374 if (HasAuxEncoders())
1376 LOG_INFO(
"AUX encoders detected. Turning on...");
1377 TurnRAEncoder(
true);
1378 TurnDEEncoder(
true);
1379 defineProperty(AUXEncoderSP);
1385 SetAxis1ParkDefault(GetAxis1Park());
1386 SetAxis2ParkDefault(GetAxis2Park());
1391 SetAxis1Park(0x800000);
1392 SetAxis2Park(0x800000);
1393 SetAxis1ParkDefault(0x800000);
1394 SetAxis2ParkDefault(0x800000);
1399 SetEncoder(AXIS1, GetAxis1Park());
1400 SetEncoder(AXIS2, GetAxis2Park());
1409 deleteProperty(BasicMountInfoTP.name);
1410 deleteProperty(AxisOneInfoNP.name);
1411 deleteProperty(AxisOneStateSP.name);
1412 deleteProperty(AxisTwoInfoNP.name);
1413 deleteProperty(AxisTwoStateSP.name);
1414 deleteProperty(AxisOneEncoderValuesNP.name);
1415 deleteProperty(AxisTwoEncoderValuesNP.name);
1416 deleteProperty(SlewModesSP.name);
1417 deleteProperty(SoftPECModesSP.name);
1418 deleteProperty(SoftPecNP.name);
1419 deleteProperty(GuidingRatesNP.name);
1420 deleteProperty(GuideNSNP.name);
1421 deleteProperty(GuideWENP.name);
1422 deleteProperty(Axis1PIDNP);
1423 deleteProperty(Axis2PIDNP);
1424 deleteProperty(AxisDeadZoneNP);
1425 deleteProperty(AxisClockNP);
1426 deleteProperty(AxisOffsetNP);
1427 deleteProperty(Axis1TrackRateNP);
1428 deleteProperty(Axis2TrackRateNP);
1430 if (HasAuxEncoders())
1431 deleteProperty(AUXEncoderSP.getName());
1444 CalculateGuidePulses();
1445 Pulse.
DeltaAz = NorthPulse.DeltaAz;
1446 Pulse.
DeltaAlt = NorthPulse.DeltaAlt;
1449 GuidingPulses.push_back(Pulse);
1460 CalculateGuidePulses();
1461 Pulse.
DeltaAz = -NorthPulse.DeltaAz;
1462 Pulse.
DeltaAlt = -NorthPulse.DeltaAlt;
1465 GuidingPulses.push_back(Pulse);
1476 CalculateGuidePulses();
1477 Pulse.
DeltaAz = WestPulse.DeltaAz;
1478 Pulse.
DeltaAlt = WestPulse.DeltaAlt;
1481 GuidingPulses.push_back(Pulse);
1492 CalculateGuidePulses();
1493 Pulse.
DeltaAz = -WestPulse.DeltaAz;
1494 Pulse.
DeltaAlt = -WestPulse.DeltaAlt;
1497 GuidingPulses.push_back(Pulse);
1504 void SkywatcherAPIMount::CalculateGuidePulses()
1506 if (NorthPulse.Duration != 0 || WestPulse.Duration != 0)
1511 const double WestRate =
IUFindNumber(&GuidingRatesNP,
"GUIDERA_RATE")->value / 10 * -1.0 / 60 / 60 * 3.75 / 100;
1513 ConvertGuideCorrection(WestRate, 0, WestPulse.DeltaAlt, WestPulse.DeltaAz);
1514 WestPulse.Duration = 1;
1518 const double NorthRate =
IUFindNumber(&GuidingRatesNP,
"GUIDEDEC_RATE")->value / 10 * 1.0 / 60 / 60 * 100 / 100;
1520 ConvertGuideCorrection(0, NorthRate, NorthPulse.DeltaAlt, NorthPulse.DeltaAz);
1521 NorthPulse.Duration = 1;
1527 void SkywatcherAPIMount::ResetGuidePulses()
1529 NorthPulse.Duration = 0;
1530 WestPulse.Duration = 0;
1536 void SkywatcherAPIMount::ConvertGuideCorrection(
double delta_ra,
double delta_dec,
double &delta_alt,
double &delta_az)
1543 TransformCelestialToTelescope(m_SkyTrackingTarget.rightascension, m_SkyTrackingTarget.declination, 0.0, OldTDV);
1544 AltitudeAzimuthFromTelescopeDirectionVector(OldTDV, OldAltAz);
1545 TransformCelestialToTelescope(m_SkyTrackingTarget.rightascension + delta_ra,
1546 m_SkyTrackingTarget.declination + delta_dec, 0.0, NewTDV);
1547 AltitudeAzimuthFromTelescopeDirectionVector(NewTDV, NewAltAz);
1548 delta_alt = NewAltAz.altitude - OldAltAz.altitude;
1549 delta_az = NewAltAz.azimuth - OldAltAz.azimuth;
1555 void SkywatcherAPIMount::SkywatcherMicrostepsFromTelescopeDirectionVector(
1559 double Axis1Angle = 0;
1560 double Axis2Angle = 0;
1563 FROM_AZIMUTHAL_PLANE);
1565 Axis1Microsteps = RadiansToMicrosteps(AXIS1, Axis1Angle);
1566 Axis2Microsteps = RadiansToMicrosteps(AXIS2, Axis2Angle);
1573 SkywatcherAPIMount::TelescopeDirectionVectorFromSkywatcherMicrosteps(
long Axis1Microsteps,
long Axis2Microsteps)
1576 double Axis1Angle = MicrostepsToRadians(AXIS1, Axis1Microsteps);
1577 double Axis2Angle = MicrostepsToRadians(AXIS2, Axis2Microsteps);
1578 return TelescopeDirectionVectorFromSphericalCoordinate(
1585 void SkywatcherAPIMount::UpdateDetailedMountInformation(
bool InformClient)
1587 bool BasicMountInfoHasChanged =
false;
1589 if (std::string(BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION].text) !=
std::to_string(MCVersion))
1592 BasicMountInfoHasChanged =
true;
1594 if (std::string(BasicMountInfoT[MOUNT_CODE].text) !=
std::to_string(MountCode))
1597 SetApproximateMountAlignmentFromMountType(ALTAZ);
1598 BasicMountInfoHasChanged =
true;
1600 if (std::string(BasicMountInfoT[IS_DC_MOTOR].text) !=
std::to_string(IsDCMotor))
1603 BasicMountInfoHasChanged =
true;
1605 if (BasicMountInfoHasChanged && InformClient)
1608 IUSaveText(&BasicMountInfoT[MOUNT_NAME], mountTypeToString(MountCode));
1610 bool AxisOneInfoHasChanged =
false;
1612 if (AxisOneInfoN[MICROSTEPS_PER_REVOLUTION].value != MicrostepsPerRevolution[0])
1614 AxisOneInfoN[MICROSTEPS_PER_REVOLUTION].value = MicrostepsPerRevolution[0];
1615 AxisOneInfoHasChanged =
true;
1617 if (AxisOneInfoN[STEPPER_CLOCK_FREQUENCY].value != StepperClockFrequency[0])
1619 AxisOneInfoN[STEPPER_CLOCK_FREQUENCY].value = StepperClockFrequency[0];
1620 AxisOneInfoHasChanged =
true;
1622 if (AxisOneInfoN[HIGH_SPEED_RATIO].value != HighSpeedRatio[0])
1624 AxisOneInfoN[HIGH_SPEED_RATIO].value = HighSpeedRatio[0];
1625 AxisOneInfoHasChanged =
true;
1627 if (AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value != MicrostepsPerWormRevolution[0])
1629 AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value = MicrostepsPerWormRevolution[0];
1630 AxisOneInfoHasChanged =
true;
1632 if (AxisOneInfoHasChanged && InformClient)
1635 bool AxisOneStateHasChanged =
false;
1636 if (AxisOneStateS[FULL_STOP].s != (AxesStatus[0].FullStop ?
ISS_ON :
ISS_OFF))
1638 AxisOneStateS[FULL_STOP].s = AxesStatus[0].FullStop ?
ISS_ON :
ISS_OFF;
1639 AxisOneStateHasChanged =
true;
1641 if (AxisOneStateS[SLEWING].s != (AxesStatus[0].Slewing ?
ISS_ON :
ISS_OFF))
1643 AxisOneStateS[SLEWING].s = AxesStatus[0].Slewing ?
ISS_ON :
ISS_OFF;
1644 AxisOneStateHasChanged =
true;
1646 if (AxisOneStateS[SLEWING_TO].s != (AxesStatus[0].SlewingTo ?
ISS_ON :
ISS_OFF))
1648 AxisOneStateS[SLEWING_TO].s = AxesStatus[0].SlewingTo ?
ISS_ON :
ISS_OFF;
1649 AxisOneStateHasChanged =
true;
1651 if (AxisOneStateS[SLEWING_FORWARD].s != (AxesStatus[0].SlewingForward ?
ISS_ON :
ISS_OFF))
1653 AxisOneStateS[SLEWING_FORWARD].s = AxesStatus[0].SlewingForward ?
ISS_ON :
ISS_OFF;
1654 AxisOneStateHasChanged =
true;
1656 if (AxisOneStateS[HIGH_SPEED].s != (AxesStatus[0].HighSpeed ?
ISS_ON :
ISS_OFF))
1658 AxisOneStateS[HIGH_SPEED].s = AxesStatus[0].HighSpeed ?
ISS_ON :
ISS_OFF;
1659 AxisOneStateHasChanged =
true;
1661 if (AxisOneStateS[NOT_INITIALISED].s != (AxesStatus[0].NotInitialized ?
ISS_ON :
ISS_OFF))
1663 AxisOneStateS[NOT_INITIALISED].s = AxesStatus[0].NotInitialized ?
ISS_ON :
ISS_OFF;
1664 AxisOneStateHasChanged =
true;
1666 if (AxisOneStateHasChanged && InformClient)
1669 bool AxisTwoInfoHasChanged =
false;
1670 if (AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION].value != MicrostepsPerRevolution[1])
1672 AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION].value = MicrostepsPerRevolution[1];
1673 AxisTwoInfoHasChanged =
true;
1675 if (AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY].value != StepperClockFrequency[1])
1677 AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY].value = StepperClockFrequency[1];
1678 AxisTwoInfoHasChanged =
true;
1680 if (AxisTwoInfoN[HIGH_SPEED_RATIO].value != HighSpeedRatio[1])
1682 AxisTwoInfoN[HIGH_SPEED_RATIO].value = HighSpeedRatio[1];
1683 AxisTwoInfoHasChanged =
true;
1685 if (AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value != MicrostepsPerWormRevolution[1])
1687 AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION].value = MicrostepsPerWormRevolution[1];
1688 AxisTwoInfoHasChanged =
true;
1690 if (AxisTwoInfoHasChanged && InformClient)
1693 bool AxisTwoStateHasChanged =
false;
1694 if (AxisTwoStateS[FULL_STOP].s != (AxesStatus[1].FullStop ?
ISS_ON :
ISS_OFF))
1696 AxisTwoStateS[FULL_STOP].s = AxesStatus[1].FullStop ?
ISS_ON :
ISS_OFF;
1697 AxisTwoStateHasChanged =
true;
1699 if (AxisTwoStateS[SLEWING].s != (AxesStatus[1].Slewing ?
ISS_ON :
ISS_OFF))
1701 AxisTwoStateS[SLEWING].s = AxesStatus[1].Slewing ?
ISS_ON :
ISS_OFF;
1702 AxisTwoStateHasChanged =
true;
1704 if (AxisTwoStateS[SLEWING_TO].s != (AxesStatus[1].SlewingTo ?
ISS_ON :
ISS_OFF))
1706 AxisTwoStateS[SLEWING_TO].s = AxesStatus[1].SlewingTo ?
ISS_ON :
ISS_OFF;
1707 AxisTwoStateHasChanged =
true;
1709 if (AxisTwoStateS[SLEWING_FORWARD].s != (AxesStatus[1].SlewingForward ?
ISS_ON :
ISS_OFF))
1711 AxisTwoStateS[SLEWING_FORWARD].s = AxesStatus[1].SlewingForward ?
ISS_ON :
ISS_OFF;
1712 AxisTwoStateHasChanged =
true;
1714 if (AxisTwoStateS[HIGH_SPEED].s != (AxesStatus[1].HighSpeed ?
ISS_ON :
ISS_OFF))
1716 AxisTwoStateS[HIGH_SPEED].s = AxesStatus[1].HighSpeed ?
ISS_ON :
ISS_OFF;
1717 AxisTwoStateHasChanged =
true;
1719 if (AxisTwoStateS[NOT_INITIALISED].s != (AxesStatus[1].NotInitialized ?
ISS_ON :
ISS_OFF))
1721 AxisTwoStateS[NOT_INITIALISED].s = AxesStatus[1].NotInitialized ?
ISS_ON :
ISS_OFF;
1722 AxisTwoStateHasChanged =
true;
1724 if (AxisTwoStateHasChanged && InformClient)
1727 bool AxisOneEncoderValuesHasChanged =
false;
1728 if ((AxisOneEncoderValuesN[RAW_MICROSTEPS].value != CurrentEncoders[AXIS1]) ||
1729 (AxisOneEncoderValuesN[OFFSET_FROM_INITIAL].value != CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1]))
1731 AxisOneEncoderValuesN[RAW_MICROSTEPS].value = CurrentEncoders[AXIS1];
1732 AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC].value = MicrostepsPerDegree[AXIS1] / 3600.0;
1733 AxisOneEncoderValuesN[OFFSET_FROM_INITIAL].value = CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1];
1734 AxisOneEncoderValuesN[DEGREES_FROM_INITIAL].value =
1735 MicrostepsToDegrees(AXIS1, CurrentEncoders[AXIS1] - ZeroPositionEncoders[AXIS1]);
1736 AxisOneEncoderValuesHasChanged =
true;
1738 if (AxisOneEncoderValuesHasChanged && InformClient)
1741 bool AxisTwoEncoderValuesHasChanged =
false;
1742 if ((AxisTwoEncoderValuesN[RAW_MICROSTEPS].value != CurrentEncoders[AXIS2]) ||
1743 (AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL].value != CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2]))
1745 AxisTwoEncoderValuesN[RAW_MICROSTEPS].value = CurrentEncoders[AXIS2];
1746 AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC].value = MicrostepsPerDegree[AXIS2] / 3600.0;
1747 AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL].value = CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2];
1748 AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL].value =
1749 MicrostepsToDegrees(AXIS2, CurrentEncoders[AXIS2] - ZeroPositionEncoders[AXIS2]);
1750 AxisTwoEncoderValuesHasChanged =
true;
1752 if (AxisTwoEncoderValuesHasChanged && InformClient)
1761 SetAxis1Park(CurrentEncoders[AXIS1]);
1762 SetAxis2Park(CurrentEncoders[AXIS2]);
1772 SetAxis1Park(ZeroPositionEncoders[AXIS1]);
1773 SetAxis2Park(ZeroPositionEncoders[AXIS2]);
1780 void SkywatcherAPIMount::resetTracking()
1782 m_TrackingRateTimer.restart();
1785 m_Controllers[
AXIS_AZ].reset(
new PID(
std::max(0.001, getPollingPeriod() / 1000.), 50, -50,
1786 Axis1PIDNP[Propotional].getValue(),
1787 Axis1PIDNP[Derivative].getValue(),
1788 Axis1PIDNP[Integral].getValue()));
1789 m_Controllers[
AXIS_AZ]->setIntegratorLimits(-100, 100);
1790 m_Controllers[
AXIS_ALT].reset(
new PID(
std::max(0.001, getPollingPeriod() / 1000.), 50, -50,
1791 Axis2PIDNP[Propotional].getValue(),
1792 Axis2PIDNP[Derivative].getValue(),
1793 Axis2PIDNP[Integral].getValue()));
1794 m_Controllers[
AXIS_ALT]->setIntegratorLimits(-100, 100);
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void TimerHit() override
Called when setTimer() time is up.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
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.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual void TimerHit() override
Called when setTimer() time is up.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual const char * getDefaultName() override
Misc.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool Handshake() override
Communication.
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool Park() override
Parking.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Motion.
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 IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual IPState GuideNorth(uint32_t ms) override
Guiding.
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
const char * GUIDE_TAB
GUIDE_TAB Where all the properties for guiding are located.
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.
void tty_set_generic_udp_format(int enabled)
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
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.
#define TRACKRATE_SIDEREAL
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.
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
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.
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
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.
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
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 DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
#define LOGF_DEBUG(fmt,...)
#define DEBUGF(priority, msg,...)
int Sync(int fd, char *matchedObject)
Namespace to encapsulate the INDI Alignment Subsystem classes. For more information see "INDI Alignme...
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
const char * getDeviceName()
bool isParked(const int fd)
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
double SlewSpeeds[SLEWMODES]
Entry in the in memory alignment database.
double RightAscension
Right ascension in decimal hours. N.B. libnova works in decimal degrees so conversion is always neede...
double ObservationJulianDate
TelescopeDirectionVector TelescopeDirection
Normalised vector giving telescope pointing direction. This is referred to elsewhere as the "apparent...
double Declination
Declination in decimal degrees.
int PrivateDataSize
This size in bytes of any private data.
Holds a nomalised direction vector (direction cosines)
void RotateAroundY(double Angle)
Rotate the reference frame around the Y axis. This has the affect of rotating the vector itself in th...