41static std::unique_ptr<SkywatcherAPIMount> SkywatcherAPIMountPtr(
new SkywatcherAPIMount());
45static double SlewSpeeds[
SLEWMODES] = { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 600.0, 800.0 };
64 m_LastCustomDirection[
AXIS1] = m_LastCustomDirection[
AXIS2] = 0;
83 DEBUGF(
DBG_SCOPE,
"SkywatcherAPIMount::Handshake - Result: %d", Result);
92 return "Skywatcher Alt-Az";
125 IUFillText(&BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION],
"MOTOR_CONTROL_FIRMWARE_VERSION",
126 "Motor control firmware version",
"-");
127 IUFillText(&BasicMountInfoT[MOUNT_CODE],
"MOUNT_CODE",
"Mount code",
"-");
128 IUFillText(&BasicMountInfoT[MOUNT_NAME],
"MOUNT_NAME",
"Mount name",
"-");
129 IUFillText(&BasicMountInfoT[IS_DC_MOTOR],
"IS_DC_MOTOR",
"Is DC motor",
"-");
131 "Basic mount information", MountInfoTab,
IP_RO, 60,
IPS_IDLE);
133 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_REVOLUTION],
"MICROSTEPS_PER_REVOLUTION",
"Microsteps per revolution",
134 "%.0f", 0, 0xFFFFFF, 1, 0);
135 IUFillNumber(&AxisOneInfoN[STEPPER_CLOCK_FREQUENCY],
"STEPPER_CLOCK_FREQUENCY",
"Stepper clock frequency",
"%.0f", 0,
137 IUFillNumber(&AxisOneInfoN[HIGH_SPEED_RATIO],
"HIGH_SPEED_RATIO",
"High speed ratio",
"%.0f", 0, 0xFFFFFF, 1, 0);
138 IUFillNumber(&AxisOneInfoN[MICROSTEPS_PER_WORM_REVOLUTION],
"MICROSTEPS_PER_WORM_REVOLUTION",
139 "Microsteps per worm revolution",
"%.0f", 0, 0xFFFFFF, 1, 0);
147 IUFillSwitch(&AxisOneStateS[SLEWING_FORWARD],
"SLEWING_FORWARD",
"SLEWING_FORWARD",
ISS_OFF);
149 IUFillSwitch(&AxisOneStateS[NOT_INITIALISED],
"NOT_INITIALISED",
"NOT_INITIALISED",
ISS_ON);
153 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_REVOLUTION],
"MICROSTEPS_PER_REVOLUTION",
"Microsteps per revolution",
154 "%.0f", 0, 0xFFFFFF, 1, 0);
155 IUFillNumber(&AxisTwoInfoN[STEPPER_CLOCK_FREQUENCY],
"STEPPER_CLOCK_FREQUENCY",
"Step timer frequency",
"%.0f", 0,
157 IUFillNumber(&AxisTwoInfoN[HIGH_SPEED_RATIO],
"HIGH_SPEED_RATIO",
"High speed ratio",
"%.0f", 0, 0xFFFFFF, 1, 0);
158 IUFillNumber(&AxisTwoInfoN[MICROSTEPS_PER_WORM_REVOLUTION],
"MICROSTEPS_PER_WORM_REVOLUTION",
159 "Microsteps per worm revolution",
"%.0f", 0, 0xFFFFFF, 1, 0);
167 IUFillSwitch(&AxisTwoStateS[SLEWING_FORWARD],
"SLEWING_FORWARD",
"SLEWING_FORWARD",
ISS_OFF);
169 IUFillSwitch(&AxisTwoStateS[NOT_INITIALISED],
"NOT_INITIALISED",
"NOT_INITIALISED",
ISS_ON);
173 IUFillNumber(&AxisOneEncoderValuesN[RAW_MICROSTEPS],
"RAW_MICROSTEPS",
"Raw Microsteps",
"%.0f", 0, 0xFFFFFF, 1, 0);
174 IUFillNumber(&AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC],
"MICROSTEPS_PER_ARCSEC",
"Microsteps/arcsecond",
175 "%.4f", 0, 0xFFFFFF, 1, 0);
176 IUFillNumber(&AxisOneEncoderValuesN[OFFSET_FROM_INITIAL],
"OFFSET_FROM_INITIAL",
"Offset from initial",
"%.0f", 0,
178 IUFillNumber(&AxisOneEncoderValuesN[DEGREES_FROM_INITIAL],
"DEGREES_FROM_INITIAL",
"Degrees from initial",
"%.2f",
179 -1000.0, 1000.0, 1, 0);
184 IUFillNumber(&AxisTwoEncoderValuesN[RAW_MICROSTEPS],
"RAW_MICROSTEPS",
"Raw Microsteps",
"%.0f", 0, 0xFFFFFF, 1, 0);
185 IUFillNumber(&AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC],
"MICROSTEPS_PER_ARCSEC",
"Microsteps/arcsecond",
186 "%.4f", 0, 0xFFFFFF, 1, 0);
187 IUFillNumber(&AxisTwoEncoderValuesN[OFFSET_FROM_INITIAL],
"OFFSET_FROM_INITIAL",
"Offset from initial",
"%.0f", 0,
189 IUFillNumber(&AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL],
"DEGREES_FROM_INITIAL",
"Degrees from initial",
"%.2f",
190 -1000.0, 1000.0, 1, 0);
203 IUFillSwitch(&SoftPECModesS[SOFTPEC_ENABLED],
"SOFTPEC_ENABLED",
"Enable for tracking",
ISS_OFF);
204 IUFillSwitch(&SoftPECModesS[SOFTPEC_DISABLED],
"SOFTPEC_DISABLED",
"Disabled",
ISS_ON);
209 IUFillNumber(&SoftPecN,
"SOFTPEC_VALUE",
"degree/minute (Alt)",
"%1.3f", 0.001, 1.0, 0.001, 0.009);
214 IUFillNumber(&GuidingRatesN[0],
"GUIDERA_RATE",
"arcsec/seconds (RA)",
"%1.3f", 1.0, 6000.0, 1.0, 120.0);
215 IUFillNumber(&GuidingRatesN[1],
"GUIDEDEC_RATE",
"arcsec/seconds (Dec)",
"%1.3f", 1.0, 6000.0, 1.0, 120.0);
231 Axis1PIDNP[Propotional].
fill(
"Propotional",
"Propotional",
"%.2f", 0.1, 100, 1, 0.1);
232 Axis1PIDNP[Derivative].
fill(
"Derivative",
"Derivative",
"%.2f", 0, 500, 10, 0.05);
233 Axis1PIDNP[Integral].
fill(
"Integral",
"Integral",
"%.2f", 0, 500, 10, 0.05);
236 Axis2PIDNP[Propotional].
fill(
"Propotional",
"Propotional",
"%.2f", 0.1, 100, 1, 0.2);
237 Axis2PIDNP[Derivative].
fill(
"Derivative",
"Derivative",
"%.2f", 0, 100, 10, 0.1);
238 Axis2PIDNP[Integral].
fill(
"Integral",
"Integral",
"%.2f", 0, 100, 10, 0.1);
242 AxisDeadZoneNP[
AXIS1].
fill(
"AXIS1",
"AZ (steps)",
"%.f", 0, 100, 10, 10);
243 AxisDeadZoneNP[
AXIS2].
fill(
"AXIS2",
"AL (steps)",
"%.f", 0, 100, 10, 10);
247 AxisClockNP[
AXIS1].
fill(
"AXIS1",
"AZ %",
"%.f", 1, 200, 10, 100);
248 AxisClockNP[
AXIS2].
fill(
"AXIS2",
"AL %",
"%.f", 1, 200, 10, 100);
252 AxisOffsetNP[RAOffset].
fill(
"RAOffset",
"RA (deg)",
"%.2f", -1, 1, 0.05, 0);
253 AxisOffsetNP[DEOffset].
fill(
"DEOffset",
"DE (deg)",
"%.2f", -1, 1, 0.05, 0);
254 AxisOffsetNP[AZSteps].
fill(
"AZEncoder",
"AZ (steps)",
"%.f", -10000, 10000, 1000, 0);
255 AxisOffsetNP[ALSteps].
fill(
"ALEncoder",
"AL (steps)",
"%.f", -10000, 10000, 1000, -100.0);
256 AxisOffsetNP[JulianOffset].
fill(
"JulianOffset",
"JD (s)",
"%.f", -5, 5, 0.1, 0);
260 Axis1TrackRateNP[TrackDirection].
fill(
"TrackDirection",
"West/East",
"%.f", 0, 1, 1, 0);
261 Axis1TrackRateNP[TrackClockRate].
fill(
"TrackClockRate",
"Freq/Step (Hz/s)",
"%.f", 0, 16000000, 500000, 0);
264 Axis2TrackRateNP[TrackDirection].
fill(
"TrackDirection",
"North/South",
"%.f", 0, 1, 1, 0);
265 Axis2TrackRateNP[TrackClockRate].
fill(
"TrackClockRate",
"Freq/Stel (Hz/s)",
"%.f", 0, 16000000, 500000, 0);
299 char *formats[],
char *names[],
int n)
323 if (strcmp(name,
"SOFTPEC") == 0)
331 if (strcmp(name,
"GUIDE_RATES") == 0)
343 AxisDeadZoneNP.
update(values, names, n);
345 AxisDeadZoneNP.
apply();
353 AxisClockNP.
update(values, names, n);
363 AxisOffsetNP.
update(values, names, n);
365 AxisOffsetNP.
apply();
373 Axis1TrackRateNP.
update(values, names, n);
375 Axis1TrackRateNP.
apply();
383 Axis2TrackRateNP.
update(values, names, n);
385 Axis2TrackRateNP.
apply();
393 Axis1PIDNP.
update(values, names, n);
399 Axis1PIDNP[Propotional].getValue(),
400 Axis1PIDNP[Derivative].getValue(),
401 Axis1PIDNP[Integral].getValue()));
408 Axis2PIDNP.
update(values, names, n);
414 Axis2PIDNP[Propotional].getValue(),
415 Axis2PIDNP[Derivative].getValue(),
416 Axis2PIDNP[Integral].getValue()));
422 if (strcmp(name,
"EQUATORIAL_EOD_COORD") == 0)
427 for (
int x = 0; x < n; x++)
434 if ((
ra >= 0) && (
ra <= 24) && (
dec >= -90) && (
dec <= 90))
457 AUXEncoderSP.
update(states, names, n);
459 AUXEncoderSP.
apply();
470 SnapPortSP.
update(states, names, n);
474 LOG_INFO(
"Toggling snap port on...");
476 LOG_INFO(
"Toggling snap port off...");
506 if (m_IterativeGOTOPending)
508 char RAStr[32], DecStr[32];
522 if (onSwitch && (onSwitch->isNameMatch(
"TRACK") || onSwitch->isNameMatch(
"SLEW")))
524 char RAStr[32], DecStr[32];
529 LOGF_INFO(
"Goto target RA %s DEC %s", RAStr, DecStr);
545 char RAStr[32], DecStr[32];
546 fs_sexa(RAStr, EquatorialCoordinates.rightascension, 2, 3600);
547 fs_sexa(DecStr, EquatorialCoordinates.declination, 2, 3600);
557 EquatorialCoordinates.declination =
dec;
581 "Sky -> Mount AZ %lf° (%ld) AL %lf° (%ld)",
612 AzimuthOffsetMicrosteps, AltitudeOffsetMicrosteps);
621 AzimuthOffsetMicrosteps -= deltaAz;
622 AltitudeOffsetMicrosteps -= deltaAlt;
642 UpdateDetailedMountInformation(
false);
669 return *(
static_cast<double *
>(Switch->aux));
687 m_ManualMotionActive[
AXIS2] =
true;
714 m_ManualMotionActive[
AXIS1] =
true;
735 "Parking: Altitude offset %ld microsteps Azimuth offset %ld microsteps", AltitudeOffsetMicrosteps,
736 AzimuthOffsetMicrosteps);
780 m_ManualMotionActive[
AXIS1] = m_ManualMotionActive[
AXIS2] =
false;
815 UpdateDetailedMountInformation(
true);
817 bool resetTrackingTimers =
false;
831 m_MountAltAz = AltAz;
834 getCurrentRADE(AltAz, m_SkyCurrentRADE);
835 char RAStr[32], DecStr[32];
845 if (m_IterativeGOTOPending)
846 m_IterativeGOTOPending =
false;
850 m_IterativeGOTOPending =
true;
855 if (onSwitch && (onSwitch->isNameMatch(
"TRACK")))
859 resetTrackingTimers =
true;
878 if (resetTrackingTimers)
911 double RightAscension, Declination;
954 AxisDeadZoneNP.
save(fp);
955 AxisClockNP.
save(fp);
956 AxisOffsetNP.
save(fp);
957 Axis1TrackRateNP.
save(fp);
958 Axis2TrackRateNP.
save(fp);
959 AUXEncoderSP.
save(fp);
987 double OrigAlt = AltAz.altitude;
990 LOGF_INFO(
"Sync (Alt: %lf Az: %lf) in park position", OrigAlt, AltAz.azimuth);
997 UpdateDetailedMountInformation(
true);
1022 m_IterativeGOTOPending =
false;
1051 m_IterativeGOTOPending =
false;
1055 m_ManualMotionActive[
AXIS1] = m_ManualMotionActive[
AXIS2] =
false;
1090 GuidingPulses.clear();
1096 bool manualMotionStopped =
false;
1102 if (m_ManualMotionActive[axis] && !
IsInMotion(axis) && movementSP.getState() !=
IPS_BUSY)
1104 m_ManualMotionActive[axis] =
false;
1105 manualMotionStopped =
true;
1109 if (manualMotionStopped)
1116 if (m_ManualMotionActive[
AXIS1] || m_ManualMotionActive[
AXIS2])
1124 trackUsingPredictiveRates();
1134 GuidingPulses.clear();
1163 UpdateDetailedMountInformation(
false);
1194 LOGF_INFO(
"AUX encoders detected. Turning %s...", enabled ?
"on" :
"off");
1262 CalculateGuidePulses();
1267 GuidingPulses.push_back(Pulse);
1278 CalculateGuidePulses();
1283 GuidingPulses.push_back(Pulse);
1294 CalculateGuidePulses();
1299 GuidingPulses.push_back(Pulse);
1310 CalculateGuidePulses();
1315 GuidingPulses.push_back(Pulse);
1322void SkywatcherAPIMount::CalculateGuidePulses()
1329 const double WestRate =
IUFindNumber(&GuidingRatesNP,
"GUIDERA_RATE")->value / 10 * -1.0 / 60 / 60 * 3.75 / 100;
1331 ConvertGuideCorrection(WestRate, 0, WestPulse.
DeltaAlt, WestPulse.
DeltaAz);
1336 const double NorthRate =
IUFindNumber(&GuidingRatesNP,
"GUIDEDEC_RATE")->value / 10 * 1.0 / 60 / 60 * 100 / 100;
1338 ConvertGuideCorrection(0, NorthRate, NorthPulse.
DeltaAlt, NorthPulse.
DeltaAz);
1345void SkywatcherAPIMount::ResetGuidePulses()
1354void SkywatcherAPIMount::ConvertGuideCorrection(
double delta_ra,
double delta_dec,
double &delta_alt,
double &delta_az)
1362 ln_get_julian_from_sys(), OldTDV);
1365 m_SkyTrackingTarget.
declination + delta_dec, ln_get_julian_from_sys(), NewTDV);
1367 delta_alt = NewAltAz.altitude - OldAltAz.altitude;
1368 delta_az = NewAltAz.azimuth - OldAltAz.azimuth;
1374void SkywatcherAPIMount::SkywatcherMicrostepsFromTelescopeDirectionVector(
1378 double Axis1Angle = 0;
1379 double Axis2Angle = 0;
1392SkywatcherAPIMount::TelescopeDirectionVectorFromSkywatcherMicrosteps(
long Axis1Microsteps,
long Axis2Microsteps)
1404void SkywatcherAPIMount::UpdateDetailedMountInformation(
bool InformClient)
1406 bool BasicMountInfoHasChanged =
false;
1408 if (std::string(BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION].text) != std::to_string(
MCVersion))
1410 IUSaveText(&BasicMountInfoT[MOTOR_CONTROL_FIRMWARE_VERSION], std::to_string(
MCVersion).c_str());
1411 BasicMountInfoHasChanged =
true;
1413 if (std::string(BasicMountInfoT[MOUNT_CODE].text) != std::to_string(
MountCode))
1417 BasicMountInfoHasChanged =
true;
1419 if (std::string(BasicMountInfoT[IS_DC_MOTOR].text) != std::to_string(
IsDCMotor))
1422 BasicMountInfoHasChanged =
true;
1424 if (BasicMountInfoHasChanged && InformClient)
1429 bool AxisOneInfoHasChanged =
false;
1434 AxisOneInfoHasChanged =
true;
1439 AxisOneInfoHasChanged =
true;
1444 AxisOneInfoHasChanged =
true;
1449 AxisOneInfoHasChanged =
true;
1451 if (AxisOneInfoHasChanged && InformClient)
1454 bool AxisOneStateHasChanged =
false;
1458 AxisOneStateHasChanged =
true;
1463 AxisOneStateHasChanged =
true;
1468 AxisOneStateHasChanged =
true;
1473 AxisOneStateHasChanged =
true;
1478 AxisOneStateHasChanged =
true;
1483 AxisOneStateHasChanged =
true;
1485 if (AxisOneStateHasChanged && InformClient)
1488 bool AxisTwoInfoHasChanged =
false;
1492 AxisTwoInfoHasChanged =
true;
1497 AxisTwoInfoHasChanged =
true;
1502 AxisTwoInfoHasChanged =
true;
1507 AxisTwoInfoHasChanged =
true;
1509 if (AxisTwoInfoHasChanged && InformClient)
1512 bool AxisTwoStateHasChanged =
false;
1516 AxisTwoStateHasChanged =
true;
1521 AxisTwoStateHasChanged =
true;
1526 AxisTwoStateHasChanged =
true;
1531 AxisTwoStateHasChanged =
true;
1536 AxisTwoStateHasChanged =
true;
1541 AxisTwoStateHasChanged =
true;
1543 if (AxisTwoStateHasChanged && InformClient)
1546 bool AxisOneEncoderValuesHasChanged =
false;
1553 AxisOneEncoderValuesN[DEGREES_FROM_INITIAL].value =
1555 AxisOneEncoderValuesHasChanged =
true;
1557 if (AxisOneEncoderValuesHasChanged && InformClient)
1560 bool AxisTwoEncoderValuesHasChanged =
false;
1567 AxisTwoEncoderValuesN[DEGREES_FROM_INITIAL].value =
1569 AxisTwoEncoderValuesHasChanged =
true;
1571 if (AxisTwoEncoderValuesHasChanged && InformClient)
1599void SkywatcherAPIMount::resetTracking()
1601 m_TrackingRateTimer.
restart();
1605 Axis1PIDNP[Propotional].getValue(),
1606 Axis1PIDNP[Derivative].getValue(),
1607 Axis1PIDNP[Integral].getValue()));
1608 m_Controllers[
AXIS_AZ]->setIntegratorLimits(-1000, 1000);
1610 Axis2PIDNP[Propotional].getValue(),
1611 Axis2PIDNP[Derivative].getValue(),
1612 Axis2PIDNP[Integral].getValue()));
1613 m_Controllers[
AXIS_ALT]->setIntegratorLimits(-1000, 1000);
1620bool SkywatcherAPIMount::trackByRate(AXISID axis,
double rate)
1622 if (std::abs(rate) > 0 && rate == m_LastTrackRate[axis])
1625 m_LastTrackRate[axis] = rate;
1631 else if (!
AxesStatus[axis].FullStop && (rate == 0 || (
AxesStatus[axis].SlewingForward && rate < 0)
1632 || (!
AxesStatus[axis].SlewingForward && rate > 0)))
1635 LOGF_DEBUG(
"Tracking -> %s direction change.", (axis ==
AXIS1 ?
"Axis 1" :
"Axis 2"));
1640 auto stepsPerSecond =
static_cast<uint32_t
>(std::abs(rate * (axis ==
AXIS1 ?
1641 AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC].value : AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC].value)));
1646 LOGF_DEBUG(
"Tracking -> %s restart.", (axis ==
AXIS1 ?
"Axis 1" :
"Axis 2"));
1662bool SkywatcherAPIMount::trackUsingPID()
1681 m_TrackingRateTimer.
restart();
1688 m_TrackingRateTimer.
restart();
1691 auto ra = m_SkyTrackingTarget.
rightascension + AxisOffsetNP[RAOffset].getValue() / 15.0;
1692 auto de = m_SkyTrackingTarget.
declination + AxisOffsetNP[DEOffset].getValue();
1693 auto JDOffset = AxisOffsetNP[JulianOffset].getValue() / 86400.0;
1707 "New Tracking Target AZ %lf° (%ld microsteps) AL %lf° (%ld microsteps) ",
1714 double DeltaAlt = 0;
1717 getGuidePulses(DeltaAz, DeltaAlt);
1719 long SetPoint[2] = {0, 0}, Measurement[2] = {0, 0},
Error[2] = {0, 0};
1720 double TrackingRate[2] = {0, 0};
1738 auto Axis1CustomClockRate = Axis1TrackRateNP[TrackClockRate].getValue();
1741 (Axis1CustomClockRate == 0 && ((
AxesStatus[
AXIS1].SlewingForward && (Error[
AXIS1] < -AxisDeadZoneNP[
AXIS1].getValue())) ||
1743 (Axis1CustomClockRate > 0 && Axis1TrackRateNP[TrackDirection].getValue() != m_LastCustomDirection[
AXIS1])))
1745 m_LastCustomDirection[
AXIS1] = Axis1TrackRateNP[TrackDirection].getValue();
1748 LOG_DEBUG(
"Tracking -> AXIS1 direction change.");
1749 LOGF_DEBUG(
"AXIS1 Setpoint %d Measurement %d Error %d Rate %f",
1753 TrackingRate[
AXIS1]);
1760 TrackingRate[
AXIS1] = std::fabs(TrackingRate[
AXIS1]);
1761 if (TrackingRate[
AXIS1] != 0)
1765 if (Axis1CustomClockRate > 0)
1767 clockRate = Axis1CustomClockRate;
1768 Direction = Axis1TrackRateNP[TrackDirection].getValue() == 0 ?
'0' :
'1';
1771 LOGF_DEBUG(
"AXIS1 Setpoint %d Measurement %d Error %d Rate %f Freq %f Dir %s",
1775 TrackingRate[
AXIS1],
1777 Direction ==
'0' ?
"Forward" :
"Backward");
1780 m_Controllers[
AXIS1]->proportionalTerm(),
1781 m_Controllers[
AXIS1]->integralTerm(),
1782 m_Controllers[
AXIS1]->derivativeTerm());
1788 LOG_DEBUG(
"Tracking -> AXIS1 restart.");
1796 auto Axis2CustomClockRate = Axis2TrackRateNP[TrackClockRate].getValue();
1799 (Axis2CustomClockRate == 0 && ((
AxesStatus[
AXIS2].SlewingForward && (Error[
AXIS2] < -AxisDeadZoneNP[
AXIS2].getValue())) ||
1801 (Axis2CustomClockRate > 0 && Axis2TrackRateNP[TrackDirection].getValue() != m_LastCustomDirection[
AXIS2])))
1803 m_LastCustomDirection[
AXIS2] = Axis2TrackRateNP[TrackDirection].getValue();
1805 LOG_DEBUG(
"Tracking -> AXIS2 direction change.");
1806 LOGF_DEBUG(
"AXIS2 Setpoint %d Measurement %d Error %d Rate %f",
1810 TrackingRate[
AXIS2]);
1817 TrackingRate[
AXIS2] = std::fabs(TrackingRate[
AXIS2]);
1818 if (TrackingRate[
AXIS2] != 0)
1822 if (Axis2CustomClockRate > 0)
1824 clockRate = Axis2CustomClockRate;
1825 Direction = Axis2TrackRateNP[TrackDirection].getValue() == 0 ?
'0' :
'1';
1828 LOGF_DEBUG(
"AXIS2 Setpoint %d Measurement %d Error %d Rate %f Freq %f Dir %s",
1832 TrackingRate[
AXIS2],
1834 Error[
AXIS2] > 0 ?
"Forward" :
"Backward");
1837 m_Controllers[
AXIS2]->proportionalTerm(),
1838 m_Controllers[
AXIS2]->integralTerm(),
1839 m_Controllers[
AXIS2]->derivativeTerm());
1845 LOG_DEBUG(
"Tracking -> AXIS2 restart.");
1858bool SkywatcherAPIMount::trackUsingPredictiveRates()
1867 double timeStep { 5.0 };
1869 double JDoffset { timeStep / (60 * 60 * 24) } ;
1881 m_TrackingRateTimer.
restart();
1888 m_TrackingRateTimer.
restart();
1892 double JDnow {ln_get_julian_from_sys()};
1899 JDnow + JDoffset, futureTDV);
1902 JDnow - JDoffset, pastTDV);
1911 EquatorialCoordinates.declination = m_SkyTrackingTarget.
declination;
1917 double azGuideOffset = 0, altGuideOffset = 0;
1918 getGuidePulses(azGuideOffset, altGuideOffset);
1921 targetMountAxisCoordinates.azimuth += azGuideOffset;
1922 pastMountAxisCoordinates.azimuth += azGuideOffset;
1923 futureMountAxisCoordinates.azimuth += azGuideOffset;
1925 targetMountAxisCoordinates.altitude += altGuideOffset;
1926 pastMountAxisCoordinates.altitude += altGuideOffset;
1927 futureMountAxisCoordinates.altitude += altGuideOffset;
1930 double predRate[2] = {0, 0};
1933 predRate[
AXIS_AZ] =
range180(AzimuthToDegrees(futureMountAxisCoordinates.azimuth - pastMountAxisCoordinates.azimuth)) /
1935 predRate[
AXIS_ALT] = (futureMountAxisCoordinates.altitude - pastMountAxisCoordinates.altitude) / timeStep / 2;
1941 LOGF_DEBUG(
"Predicted positions (AZ): %9.4f %9.4f (now, future, degs)",
1942 AzimuthToDegrees(targetMountAxisCoordinates.azimuth),
1943 AzimuthToDegrees(futureMountAxisCoordinates.azimuth)) ;
1944 LOGF_DEBUG(
"Predicted positions (AL): %9.4f %9.4f (now, future, degs)", targetMountAxisCoordinates.altitude,
1945 futureMountAxisCoordinates.altitude);
1965 double offsetAngle[2] = {0, 0};
1966 offsetAngle[
AXIS_AZ] =
range180(targetMountAxisCoordinates.azimuth - currentAltAz.azimuth) * 3600;
1967 offsetAngle[
AXIS_ALT] = (targetMountAxisCoordinates.altitude - currentAltAz.altitude) * 3600;
1969 int32_t targetSteps[2] = {0, 0};
1970 int32_t offsetSteps[2] = {0, 0};
1971 double trackRates[2] = {0, 0};
1974 offsetSteps[
AXIS_AZ] = offsetAngle[
AXIS_AZ] * AxisOneEncoderValuesN[MICROSTEPS_PER_ARCSEC].value;
1975 offsetSteps[
AXIS_ALT] = offsetAngle[
AXIS_ALT] * AxisTwoEncoderValuesN[MICROSTEPS_PER_ARCSEC].value;
1979 m_OffsetSwitchSettle[
AXIS_AZ] = 0;
1988 double minTrackRate = predRate[
AXIS_AZ] * MIN_TRACK_RATE_FACTOR;
1989 if(trackRates[
AXIS_AZ] * predRate[
AXIS_AZ] < 0 || std::abs(trackRates[
AXIS_AZ]) < std::abs(minTrackRate))
1990 trackRates[
AXIS_AZ] = minTrackRate;
1992 LOGF_DEBUG(
"Tracking AZ Now: %8.f Target: %8d Offset: %8d Rate: %8.2f", Axis1Steps, targetSteps[
AXIS_AZ],
1995 LOGF_DEBUG(
"Tracking AZ P: %8.1f I: %8.1f D: %8.1f O: %8.1f",
1996 m_Controllers[
AXIS_AZ]->proportionalTerm(),
1997 m_Controllers[
AXIS_AZ]->integralTerm(),
1998 m_Controllers[
AXIS_AZ]->derivativeTerm(),
2008 m_OffsetSwitchSettle[
AXIS_ALT] = 0;
2018 double minTrackRate = predRate[
AXIS_ALT] * MIN_TRACK_RATE_FACTOR;
2020 trackRates[
AXIS_ALT] = minTrackRate;
2022 LOGF_DEBUG(
"Tracking AL Now: %8.f Target: %8d Offset: %8d Rate: %8.2f", Axis2Steps,
2026 LOGF_DEBUG(
"Tracking AL P: %8.1f I: %8.1f D: %8.1f O: %8.1f",
2027 m_Controllers[
AXIS_ALT]->proportionalTerm(),
2028 m_Controllers[
AXIS_ALT]->integralTerm(),
2029 m_Controllers[
AXIS_ALT]->derivativeTerm(),
2041double SkywatcherAPIMount::AzimuthToDegrees(
double degree)
2043 if (isNorthHemisphere())
2052double SkywatcherAPIMount::DegreesToAzimuth(
double degree)
2054 if (isNorthHemisphere())
2060void SkywatcherAPIMount::getGuidePulses(
double &az,
double &alt)
2062 double DeltaAz = 0, DeltaAlt = 0;
2064 for (
auto Iter = GuidingPulses.begin(); Iter != GuidingPulses.end(); )
2067 if (Iter->OriginalDuration == 1000)
2069 DeltaAlt += Iter->DeltaAlt;
2070 DeltaAz += Iter->DeltaAz;
2074 DeltaAlt += Iter->DeltaAlt / 2;
2075 DeltaAz += Iter->DeltaAz / 2;
2081 Iter = GuidingPulses.erase(Iter);
2082 if (Iter == GuidingPulses.end())
void setDefaultHost(const char *addressHost)
void setLANSearchEnabled(bool enabled)
void setConnectionType(int type)
TODO should be renamed to setDefaultConnectionType.
void setDefaultPort(uint32_t addressPort)
void ProcessAlignmentNumberProperties(Telescope *pTelescope, const char *name, double values[], char *names[], int n)
Call this function from within the ISNewNumber processing path. The function will handle any alignmen...
void ProcessAlignmentSwitchProperties(Telescope *pTelescope, const char *name, ISState *states, char *names[], int n)
Call this function from within the ISNewSwitch processing path. The function will handle any alignmen...
void InitAlignmentProperties(Telescope *pTelescope)
Initialize alignment subsystem properties. It is recommended to call this function within initPropert...
void ProcessAlignmentBLOBProperties(Telescope *pTelescope, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Call this function from within the ISNewBlob processing path. The function will handle any alignment ...
void SaveAlignmentConfigProperties(FILE *fp)
Call this function to save persistent alignment related properties. This function should be called fr...
void ProcessAlignmentTextProperties(Telescope *pTelescope, const char *name, char *texts[], char *names[], int n)
Call this function from within the ISNewText processing path. The function will handle any alignment ...
bool CheckForDuplicateSyncPoint(const AlignmentDatabaseEntry &CandidateEntry, double Tolerance=0.1) const
Check if a entry already exists in the database.
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
void UpdateLocation(double latitude, double longitude, double elevation)
Call this function from within the updateLocation processing path.
void UpdateSize()
Call this function when the number of entries in the database changes.
bool TransformTelescopeToCelestialJD(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianDate)
TransformTelescopeToCelestialJD Transforms Mount Coords to Celestial (Sky) Coordinates.
bool TransformCelestialToTelescopeJD(double RightAscension, double Declination, double JulianDate, TelescopeDirectionVector &ApparentTelescopeDirectionVector)
TransformCelestialToTelescopeJD Transforms Celestial (Sky) Coords to Mount Coordinates.
bool Initialise(InMemoryDatabase *pInMemoryDatabase)
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
MountAlignment_t GetApproximateMountAlignment()
Get the approximate alignment of the mount.
void SetApproximateMountAlignmentFromMountType(MountType_t Type)
Call this function to set the ApproximateMountAlignment property of the current Math Plugin....
const TelescopeDirectionVector TelescopeDirectionVectorFromSphericalCoordinate(const double AzimuthAngle, AzimuthAngleDirection_t AzimuthAngleDirection, const double PolarAngle, PolarAngleDirection_t PolarAngleDirection)
Calculates a telescope direction vector from the supplied spherical coordinate information.
void SphericalCoordinateFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, double &AzimuthAngle, AzimuthAngleDirection_t AzimuthAngleDirection, double &PolarAngle, PolarAngleDirection_t PolarAngleDirection)
Calculates a spherical coordinate from the supplied telescope direction vector.
const TelescopeDirectionVector TelescopeDirectionVectorFromAltitudeAzimuth(INDI::IHorizontalCoordinates HorizontalCoordinates)
Calculates a normalised direction vector from the supplied altitude and azimuth.
void AltitudeAzimuthFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IHorizontalCoordinates &HorizontalCoordinates)
Calculates an altitude and azimuth from the supplied normalised direction vector and declination.
INDI::PropertySwitch getSwitch(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void addConfigurationControl()
Add Configuration control to the driver.
void setActiveConnection(Connection::Interface *existingConnection)
setActiveConnection Switch the active connection to the passed connection plugin
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.
uint32_t getPollingPeriod() const
getPollingPeriod Return the polling period.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
uint32_t getDriverInterface() const
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.
Connection::Interface * getActiveConnection()
void addDebugControl()
Add Debug control to the driver.
int64_t elapsed() const
Returns the number of milliseconds since this ElapsedTimer was last started.
int64_t restart()
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
INDI::PropertyNumber GuideNSNP
virtual void GuideComplete(INDI_EQ_AXIS axis)
Call GuideComplete once the guiding pulse is complete.
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber GuideWENP
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 setName(const char *name)
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
void setLabel(const char *label)
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool isSwitchOn(const std::string &name) const
bool update(const ISState states[], const char *const names[], int n)
INDI::WidgetViewSwitch * findOnSwitch() const
int findOnSwitchIndex() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
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.
double GetAxis1Park() const
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...
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....
Connection::TCP * tcpConnection
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
@ TELESCOPE_HAS_TRACK_MODE
@ TELESCOPE_CAN_CONTROL_TRACK
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
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.
double MicrostepsToRadians(AXISID Axis, long Microsteps)
Convert microsteps to angle in radians.
void TurnRAEncoder(bool on)
void SetSerialPort(int port)
Set the serail port to be usb for mount communication.
static constexpr double SIDEREALRATE
double MicrostepsToDegrees(AXISID Axis, long Microsteps)
Convert microsteps to angle in degrees.
void SlewTo(AXISID Axis, long OffsetInMicrosteps, bool verbose=true)
Slew to the given offset and stop.
long ZeroPositionEncoders[2]
Zero position encoder values (microsteps).
long CurrentEncoders[2]
Current encoder values (microsteps).
bool GetStatus(AXISID Axis)
long RadiansToMicrosteps(AXISID Axis, double AngleInRadians)
Convert angle in radians to microsteps.
bool SetEncoder(AXISID Axis, long Microsteps)
Set axis encoder to the specified value.
void TurnDEEncoder(bool on)
long PolarisPositionEncoders[2]
Polaris position (initial) encoder values (microsteps).
bool GetEncoder(AXISID Axis)
Set the CurrentEncoders status variable to the current encoder value in microsteps for the specified ...
long MicrostepsPerWormRevolution[2]
double MicrostepsPerDegree[2]
long DegreesToMicrosteps(AXISID Axis, double AngleInDegrees)
Convert angle in degrees to microsteps.
bool SlowStop(AXISID Axis)
Bring the axis to slow stop in the distance specified by SetSlewModeDeccelerationRampLength.
bool StartAxisMotion(AXISID Axis)
Start the axis slewing in the previously selected mode.
bool InitMount()
Initialize the communication to the mount.
void Slew(AXISID Axis, double SpeedInRadiansPerSecond, bool IgnoreSilentMode=true)
Start the axis slewing at the given rate.
bool SetAxisMotionMode(AXISID Axis, char Func, char Direction)
Set the motion mode per the specified axis.
long MicrostepsPerRevolution[2]
bool IsInMotion(AXISID Axis)
Check if an axis is moving.
long StepperClockFrequency[2]
bool toggleSnapPort(bool enabled)
Toggle the snap port on or off.
bool SetClockTicksPerMicrostep(AXISID Axis, long ClockTicksPerMicrostep)
Set the PIC internal divider variable which determines how many clock interrupts have to occur betwee...
INDI::Telescope * pChildTelescope
static const char * mountTypeToString(uint8_t type)
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.
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertySwitch MountTypeSP
INDI::PropertySwitch MovementWESP
INDI::PropertySwitch CoordSP
void tty_set_generic_udp_format(int enabled)
void tty_set_auto_reset_udp_session(int flag)
automatically reset UDP session on write
double range180(double r)
range180 Limits an angle to be between -180 to +180 degrees
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.
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.
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.
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
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.
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,...)
#define TRACKRATE_SIDEREAL
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.
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 normalised 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...