38#define PROXISKY_SETTINGS_TAB "Proxisky Settings"
39#define PROXISKY_LIMITS_TAB "Proxisky RA/Dec Limits"
40#define PROXISKY_ACS_TAB "Proxisky ACS"
41#define PROXISKY_ADVANCED_TAB "Proxisky Advanced"
48#define PROXISKY_ACS_POLL_DIVIDER 5
56#define PROXISKY_MODEL_PROBE_SECONDS 3
65#define PROXISKY_MODEL_PROBE_MAX_ATTEMPTS 64
71#define PROXISKY_PROBE_RETRY_NSEC 50000000L
76#define PROXISKY_PID_RESTART_SECONDS 1
88#define PROXISKY_PROBE_BUDGET_SECONDS 10
102static double monotonicSeconds()
104 struct timespec now = {0, 0};
110 if (clock_gettime(CLOCK_MONOTONIC, &now) != 0)
111 return std::numeric_limits<double>::infinity();
113 return now.tv_sec + now.tv_nsec / 1e9;
120static void sleepFor(
const struct timespec &duration)
122 struct timespec remaining = duration;
124 while (nanosleep(&remaining, &remaining) == -1 &&
errno == EINTR)
150template <
typename PropertyT>
151static bool reportUnavailable(PropertyT &property,
const char *message)
154 property.apply(
"%s", message);
170 return "Proxisky UMi";
179 ModelTP[0].
fill(
"MODEL",
"Model/Firmware",
"Unknown");
182 RALimitEnableSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
183 RALimitEnableSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
189 RALimitNP[0].
fill(
"LEFT",
"Left (deg)",
"%.0f", 1, 180, 1, 90);
190 RALimitNP[1].
fill(
"RIGHT",
"Right (deg)",
"%.0f", 1, 180, 1, 90);
193 DecLimitEnableSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
194 DecLimitEnableSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
198 DecLimitNP[0].
fill(
"CCW",
"CCW (deg)",
"%.0f", 90, 200, 1, 90);
199 DecLimitNP[1].
fill(
"CW",
"CW (deg)",
"%.0f", 90, 200, 1, 90);
206 AcsEnableSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
207 AcsEnableSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
217 AcsThresholdNP[0].
fill(
"RA",
"RA Threshold",
"%.0f", 10, 59999, 1, 1000);
218 AcsThresholdNP[1].
fill(
"DEC",
"Dec Threshold",
"%.0f", 100, 59999, 1, 1000);
221 AcsCollisionNP[0].
fill(
"RA",
"RA Collisions",
"%.0f", 0, 1e9, 1, 0);
222 AcsCollisionNP[1].
fill(
"DEC",
"Dec Collisions",
"%.0f", 0, 1e9, 1, 0);
226 AcsClearSP[0].
fill(
"CLEAR",
"Clear Counters",
ISS_OFF);
230 PowerLossSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
231 PowerLossSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
240 SuperHomeSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
241 SuperHomeSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
245 DecSecHomeSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
246 DecSecHomeSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
250 AutoTrackSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
251 AutoTrackSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
255 SuperGotoSP[0].
fill(
"DISABLED",
"Disabled",
ISS_OFF);
256 SuperGotoSP[1].
fill(
"ENABLED",
"Enabled",
ISS_OFF);
260 SuperGotoToleranceNP[0].
fill(
"TOLERANCE",
"Tolerance (deg)",
"%.0f", 5, 30, 1, 10);
261 SuperGotoToleranceNP.
fill(
getDeviceName(),
"PROXISKY_SUPER_GOTO_TOLERANCE",
"Supervised GOTO Tolerance",
267 RaPidNP[0].
fill(
"ANGLE_KP",
"Angle Kp",
"%.0f", 11, 1999, 1, 100);
268 RaPidNP[1].
fill(
"ANGLE_KI",
"Angle Ki",
"%.0f", 11, 1999, 1, 100);
269 RaPidNP[2].
fill(
"SPEED_KP",
"Speed Kp",
"%.0f", 11, 1999, 1, 100);
270 RaPidNP[3].
fill(
"SPEED_KI",
"Speed Ki",
"%.0f", 11, 80, 1, 20);
273 DecPidNP[0].
fill(
"ANGLE_KP",
"Angle Kp",
"%.0f", 11, 1999, 1, 100);
274 DecPidNP[1].
fill(
"ANGLE_KI",
"Angle Ki",
"%.0f", 11, 1999, 1, 100);
275 DecPidNP[2].
fill(
"SPEED_KP",
"Speed Kp",
"%.0f", 11, 1999, 1, 100);
276 DecPidNP[3].
fill(
"SPEED_KI",
"Speed Ki",
"%.0f", 11, 80, 1, 20);
342 m_HasRaPid =
probePidAxis(RaPidNP,
":Pnag0#",
"RA/Azm");
345 m_HasDecPid =
probePidAxis(DecPidNP,
":Pnag1#",
"Dec/Alt");
355 if (m_ProbeDeadline <= 0 || monotonicSeconds() < m_ProbeDeadline)
358 if (!m_ProbeBudgetWarned)
360 m_ProbeBudgetWarned =
true;
361 LOGF_WARN(
"Vendor probing exceeded its %ds budget - the mount identified itself but "
362 "stopped answering. Skipping the remaining feature probes; reconnect to retry.",
374 m_PropertiesDefined =
true;
420 if (m_HasSuperGotoTolerance)
457 m_ProbesDone =
false;
458 m_PropertiesDefined =
false;
460 m_ProbeBudgetWarned =
false;
461 m_HasRALimits =
false;
462 m_HasDecLimits =
false;
465 m_HasPowerLoss =
false;
467 m_HasSuperHome =
false;
468 m_HasDecSecHome =
false;
469 m_HasAutoTrack =
false;
470 m_HasSuperGoto =
false;
471 m_HasSuperGotoTolerance =
false;
474 m_AcsEnabled =
false;
475 m_AcsCountersGood =
true;
476 m_AcsPollDivider = 0;
488 if (rcOut !=
nullptr)
519 sleepFor(retryPause);
521 LOGF_DEBUG(
"Retrying %s once before treating the feature as unsupported.",
cmd);
532 if (rcOut !=
nullptr)
537 snprintf(out, outLen,
"%s", response);
542 if (rc < 1 && response[0] ==
'\0')
550 if (requireTerminated)
552 LOGF_DEBUG(
"Discarding unterminated response to %s: <%s> (rc=%d)",
cmd, response, rc);
556 LOGF_DEBUG(
"Unterminated response to %s: <%s> (rc=%d)",
cmd, response, rc);
569 if (response[0] !=
'0' && response[0] !=
'1')
571 LOGF_DEBUG(
"%s: unexpected boolean reply <%s>",
cmd, response);
575 value = (response[0] ==
'1');
597 if (response[0] !=
'1')
599 LOGF_DEBUG(
"%s: refused, mount replied <%s>",
cmd, response);
617 if (sscanf(response,
"%lf|%lf", &first, &second) != 2)
619 LOGF_DEBUG(
"%s: not a pair reply <%s>, treating feature as unsupported",
cmd, response);
634 if (sscanf(response,
"%lf|%lf|%lf|%lf", &out[0], &out[1], &out[2], &out[3]) != 4)
636 LOGF_DEBUG(
"%s: fewer than four numeric fields in <%s>, treating feature as unsupported",
cmd, response);
659 LOGF_WARN(
"%s did not answer (%s), even after a retry; its control is hidden. "
660 "Reconnect to probe again.", label, readCmd);
667 LOGF_INFO(
"%s: %s", label, value ?
"enabled" :
"disabled");
672 const char *readCmd,
const char *label,
673 ISState *states,
char *names[],
int n,
bool *mirror)
679 if (!sp.
update(states, names, n))
681 restoreAfterFailedUpdate(sp, previous);
689 LOGF_DEBUG(
"===CMD==> %s = %s", label, wanted ?
"enabled" :
"disabled");
699 const bool restored =
getVendorBool(readCmd, actual) ? actual : previous;
705 if (mirror !=
nullptr)
708 sp.
apply(
"The mount refused the %s change; it is still %s.", label,
709 restored ?
"enabled" :
"disabled");
710 LOGF_ERROR(
"The mount refused the %s change.", label);
720 if (mirror !=
nullptr)
723 if (actual == wanted)
726 sp.
apply(
"%s %s.", label, actual ?
"enabled" :
"disabled");
727 LOGF_INFO(
"%s %s.", label, actual ?
"enabled" :
"disabled");
732 sp.
apply(
"Mount still reports %s %s after requesting %s.", label,
733 actual ?
"enabled" :
"disabled", wanted ?
"enabled" :
"disabled");
734 LOGF_WARN(
"Mount did not accept the %s change (wanted %s, reads %s).", label,
735 wanted ?
"enabled" :
"disabled", actual ?
"enabled" :
"disabled");
747 if (mirror !=
nullptr)
750 sp.
apply(
"Set %s but could not read the state back (%s).", label, readCmd);
751 LOGF_ERROR(
"Could not read back %s state (%s).", label, readCmd);
756 const char *readCmd,
const char *label,
757 ISState *states,
char *names[],
int n)
763 if (!sp.
update(states, names, n))
765 restoreAfterFailedUpdate(sp, previous);
773 LOGF_DEBUG(
"===CMD==> %s = %s (applies at next power up)", label, wanted ?
"enabled" :
"disabled");
785 const bool restored =
getVendorBool(readCmd, actual) ? actual : previous;
790 sp.
apply(
"The mount refused the %s change; it still has %s stored.", label,
791 restored ?
"enabled" :
"disabled");
792 LOGF_ERROR(
"The mount refused the %s change.", label);
802 sp.
apply(
"Sent the %s change but could not confirm the mount stored it (%s).", label, readCmd);
803 LOGF_WARN(
"Could not read back %s state (%s) after the write.", label, readCmd);
812 sp.
apply(
"%s will be %s after the mount is power cycled.", label, wanted ?
"enabled" :
"disabled");
814 if (actual == wanted)
815 LOGF_INFO(
"Mount stored %s = %s; power cycle to apply it.", label, wanted ?
"enabled" :
"disabled");
817 LOGF_INFO(
"Mount still reports %s = %s; the new setting applies at next power up.", label,
818 actual ?
"enabled" :
"disabled");
833 const struct timespec settle = {0, 250000000L};
836 for (
int attempt = 1; ; attempt++)
847 if (response[0] !=
'\0' && !(response[0] ==
'0' && response[1] ==
'\0'))
853 LOGF_WARN(
"Proxisky :Pbvg# replied without a terminator (rc=%d), accepting '%s'.", rc, response);
856 ModelTP[0].setText(response);
858 LOGF_INFO(
"Proxisky model/firmware: %s", response);
869 LOGF_WARN(
"Proxisky vendor extensions not reported by :Pbvg# after %d attempts over %ds "
870 "(rc=%d, response='%s'), vendor properties disabled.",
875 LOGF_DEBUG(
"Proxisky :Pbvg# not ready yet (attempt %d, rc=%d, response='%s'), retrying.",
876 attempt, rc, response);
886 double left = 0, right = 0;
890 LOG_INFO(
"RA travel limits not supported by this mount.");
894 m_HasRALimits =
true;
895 const double raLimits[2] = {left, right};
898 bool enabled =
false;
899 RALimitEnableSP.
reset();
905 LOGF_INFO(
"RA travel limits: left %.0f deg, right %.0f deg, %s", left, right,
906 enabled ?
"enabled" :
"disabled");
914 LOG_WARN(
"Could not read RA limit enable state (:PsSrg#), showing it as disabled.");
924 double ccw = 0, cw = 0;
928 LOGF_INFO(
"Dec travel limits not supported by this mount (model '%s').", ModelTP[0].
getText());
932 m_HasDecLimits =
true;
933 const double decLimits[2] = {ccw, cw};
936 bool enabled =
false;
937 DecLimitEnableSP.
reset();
943 LOGF_INFO(
"Dec travel limits: CCW %.0f deg, CW %.0f deg, %s", ccw, cw,
944 enabled ?
"enabled" :
"disabled");
950 LOG_WARN(
"Could not read Dec limit enable state (:PsSdg#), showing it as disabled.");
962 bool supported =
false;
967 LOG_WARN(
"Status LED capability could not be read (:PsLga#); its control is hidden.");
973 LOG_INFO(
"Status LED not fitted to this mount.");
989 LOGF_INFO(
"Status LED: %s", lit ?
"on" :
"off");
995 LOG_WARN(
"Mount reports a status LED (:PsLga#) but its state could not be read (:PsLgb#); "
996 "showing it as off.");
1007 bool supported =
false;
1011 LOG_WARN(
"Anti-collision capability could not be read (:PbC#); its controls are hidden.");
1017 LOG_INFO(
"Anti-collision system not fitted to this mount.");
1021 double raThreshold = 0, decThreshold = 0;
1025 LOG_WARN(
"Mount reports an anti-collision system (:PbC#) but its thresholds could not be read (:Pncg#).");
1030 const double thresholds[2] = {raThreshold, decThreshold};
1033 AcsEnableSP.
reset();
1044 LOG_WARN(
"Could not read anti-collision enable state (:PsCg#), showing it as disabled.");
1047 LOGF_INFO(
"Anti-collision: RA threshold %.0f, Dec threshold %.0f, %s", raThreshold, decThreshold,
1048 m_AcsEnabled ?
"enabled" :
"disabled");
1060 bool fitted =
false;
1063 LOG_WARN(
"Power loss memory capability could not be read (:Pbc#); its control is hidden.");
1065 m_HasPowerLoss =
probeBoolSwitch(PowerLossSP,
":Pscg#",
"Power loss memory");
1067 LOG_INFO(
"Power loss memory not fitted to this mount.");
1077 m_HasSuperHome =
probeBoolSwitch(SuperHomeSP,
":PsShg#",
"Supervised Home");
1082 m_HasDecSecHome =
probeBoolSwitch(DecSecHomeSP,
":PsDgb#",
"Dec second home");
1087 m_HasAutoTrack =
probeBoolSwitch(AutoTrackSP,
":Psag#",
"Auto tracking");
1102 m_HasSuperGoto =
true;
1111 LOG_WARN(
"Mount reports Super GOTO (:Pndgo#) but its tolerance could not be read (:Pndgv#); "
1112 "the enable switch is still available, the tolerance field is not.");
1116 double tolerance = 0;
1120 if (sscanf(response,
"%lf", &tolerance) != 1)
1122 LOGF_WARN(
"Supervised GOTO tolerance reply was not numeric <%s>; the tolerance field is hidden.", response);
1130 if (tolerance < SuperGotoToleranceNP[0].getMin() || tolerance > SuperGotoToleranceNP[0].getMax())
1132 LOGF_INFO(
"Supervised GOTO tolerance not supported by this mount (reply '%s'); "
1133 "its enable switch is still available.", response);
1137 m_HasSuperGotoTolerance =
true;
1138 SuperGotoToleranceNP[0].setValue(tolerance);
1140 LOGF_INFO(
"Supervised GOTO tolerance: %.0f degrees", tolerance);
1150 SuperGotoToleranceNP.
apply();
1151 LOG_ERROR(
"Could not read back Super GOTO tolerance (:Pndgv#).");
1157 if (sscanf(response,
"%lf", &actual) != 1)
1160 SuperGotoToleranceNP.
apply();
1161 LOGF_ERROR(
"Supervised GOTO tolerance reply was not numeric <%s>.", response);
1168 if (actual < SuperGotoToleranceNP[0].getMin() || actual > SuperGotoToleranceNP[0].getMax())
1171 SuperGotoToleranceNP.
apply();
1172 LOGF_ERROR(
"Mount reported an out-of-range Super GOTO tolerance <%s>.", response);
1176 SuperGotoToleranceNP[0].setValue(actual);
1178 const bool matched = (std::lround(actual) == std::lround(expected));
1183 SuperGotoToleranceNP.
apply(
"Supervised GOTO tolerance set to %.0f degrees.", actual);
1184 LOGF_INFO(
"Supervised GOTO tolerance set to %.0f degrees.", actual);
1188 SuperGotoToleranceNP.
apply(
"Mount reports Supervised GOTO tolerance %.0f after requesting %.0f.",
1190 LOGF_WARN(
"Mount reports Super GOTO tolerance %.0f after requesting %.0f.", actual, expected);
1199 auto &widget = np[index];
1203 if (std::isfinite(value) && value >= widget.getMin() && value <= widget.getMax())
1208 LOGF_ERROR(
"%s must be between %.0f and %.0f%s.", what, widget.getMin(), widget.getMax(), unit);
1214 bool allInRange =
true;
1216 for (
int i = 0; i < count; i++)
1218 np[i].setValue(values[i]);
1220 if (!std::isfinite(values[i]) || values[i] < np[i].getMin() || values[i] > np[i].getMax())
1223 LOGF_WARN(
"Mount reports %s = %.0f, outside this driver's accepted range %.0f..%.0f. "
1224 "It was most likely set with the vendor tool; writing a new value will correct it.",
1225 np[i].getLabel(), values[i], np[i].getMin(), np[i].getMax());
1234 double gains[4] = {0, 0, 0, 0};
1238 LOGF_INFO(
"%s PID gains not supported by this mount.", label);
1243 LOGF_INFO(
"%s PID: angle Kp %.0f, angle Ki %.0f, speed Kp %.0f, speed Ki %.0f", label, gains[0], gains[1], gains[2],
1249 const char *label,
double values[],
char *names[],
int n)
1263 np.
apply(
"PID gains can only be changed while the mount is stopped - the commit restarts the axis.");
1264 LOGF_WARN(
"Refusing to write %s PID gains while the mount is not idle.", label);
1272 for (
int i = 0; i < 4; i++)
1273 wanted[i] = np[i].getValue();
1275 for (
int i = 0; i < n; i++)
1277 for (
int j = 0; j < 4; j++)
1279 if (np[j].isNameMatch(names[i]))
1281 wanted[j] = values[i];
1282 LOGF_DEBUG(
"===CMD==> %s %s= %f", label, np[j].getLabel(), wanted[j]);
1290 for (
int i = 0; i < 4; i++)
1292 char field[64] = {0};
1293 snprintf(field,
sizeof(field),
"%s %s", label, np[i].getLabel());
1300 static const char *suffix[4] = {
"Ap",
"Ai",
"Sp",
"Si"};
1303 for (
int i = 0; i < 4; i++)
1305 snprintf(
cmd,
sizeof(
cmd),
"%s%s%d#", axisPrefix, suffix[i],
static_cast<int>(std::lround(wanted[i])));
1313 np.
apply(
"The mount did not accept the %s PID gains.", label);
1318 snprintf(
cmd,
sizeof(
cmd),
"%sr#", axisPrefix);
1323 np.
apply(
"The mount did not accept the %s PID gain commit.", label);
1335 double actual[4] = {0, 0, 0, 0};
1336 const struct timespec axisSettle = {0, 20000000L};
1338 bool readOk =
false;
1346 const bool restarting = readOk && actual[0] == 0 && actual[1] == 0 && actual[2] == 0 && actual[3] == 0;
1348 if ((readOk && !restarting) || monotonicSeconds() >= restartDeadline)
1351 sleepFor(axisSettle);
1358 LOGF_ERROR(
"Could not read back %s PID gains (%s) after the write.", label, readCmd);
1366 bool matched =
true;
1368 for (
int i = 0; i < 4; i++)
1370 np[i].setValue(wanted[i]);
1372 if (std::lround(actual[i]) != std::lround(wanted[i]))
1379 np.
apply(
"%s PID gains stored. Power cycle the mount for them to take effect.", label);
1382 LOGF_INFO(
"Mount stored %s PID gains; power cycle to apply them.", label);
1384 LOGF_INFO(
"Mount still reports the previous %s PID gains (%.0f|%.0f|%.0f|%.0f); "
1385 "the new ones apply at next power up.", label, actual[0], actual[1], actual[2], actual[3]);
1392 double raCount = 0, decCount = 0;
1402 if (m_PropertiesDefined)
1403 AcsCollisionNP.
apply();
1405 if (stickyOnFailure)
1407 m_AcsCountersGood =
false;
1408 LOG_WARN(
"Collision counters did not respond (:Pnccg#), disabling further checks this session.");
1412 LOG_WARN(
"Collision counters did not respond (:Pnccg#) during startup.");
1418 AcsCollisionNP[0].setValue(raCount);
1419 AcsCollisionNP[1].setValue(decCount);
1424 if (m_PropertiesDefined)
1425 AcsCollisionNP.
apply();
1446 if (m_HasAcs && m_AcsEnabled && m_AcsCountersGood && settled)
1450 m_AcsPollDivider = 0;
1464 double left = 0, right = 0;
1470 LOG_ERROR(
"Could not read back RA travel limits (:Pnbg#).");
1474 RALimitNP[0].setValue(left);
1475 RALimitNP[1].setValue(right);
1477 const bool matched = (std::lround(left) == std::lround(expectedLeft) &&
1478 std::lround(right) == std::lround(expectedRight));
1484 RALimitNP.
apply(
"RA travel limits set to %.0f / %.0f degrees.", left, right);
1485 LOGF_INFO(
"RA travel limits set to left %.0f, right %.0f degrees.", left, right);
1489 RALimitNP.
apply(
"Mount reports RA travel limits %.0f / %.0f after requesting %.0f / %.0f.",
1490 left, right, expectedLeft, expectedRight);
1491 LOGF_WARN(
"Mount reports RA travel limits %.0f|%.0f after requesting %.0f|%.0f.", left, right,
1492 expectedLeft, expectedRight);
1500 double ccw = 0, cw = 0;
1506 LOG_ERROR(
"Could not read back Dec travel limits (:PsSdG#).");
1510 DecLimitNP[0].setValue(ccw);
1511 DecLimitNP[1].setValue(cw);
1513 const bool matched = (std::lround(ccw) == std::lround(expectedCCW) &&
1514 std::lround(cw) == std::lround(expectedCW));
1520 DecLimitNP.
apply(
"Dec travel limits set to %.0f / %.0f degrees.", ccw, cw);
1521 LOGF_INFO(
"Dec travel limits set to CCW %.0f, CW %.0f degrees.", ccw, cw);
1525 DecLimitNP.
apply(
"Mount reports Dec travel limits %.0f / %.0f after requesting %.0f / %.0f.",
1526 ccw, cw, expectedCCW, expectedCW);
1527 LOGF_WARN(
"Mount reports Dec travel limits %.0f|%.0f after requesting %.0f|%.0f.", ccw, cw,
1528 expectedCCW, expectedCW);
1536 double raThreshold = 0, decThreshold = 0;
1541 AcsThresholdNP.
apply();
1542 LOG_ERROR(
"Could not read back anti-collision thresholds (:Pncg#).");
1546 AcsThresholdNP[0].setValue(raThreshold);
1547 AcsThresholdNP[1].setValue(decThreshold);
1549 const bool matched = (std::lround(raThreshold) == std::lround(expectedRA) &&
1550 std::lround(decThreshold) == std::lround(expectedDec));
1556 AcsThresholdNP.
apply(
"Anti-collision thresholds set to RA %.0f / Dec %.0f.", raThreshold, decThreshold);
1557 LOGF_INFO(
"Anti-collision thresholds set to RA %.0f, Dec %.0f.", raThreshold, decThreshold);
1561 AcsThresholdNP.
apply(
"Mount reports anti-collision thresholds %.0f / %.0f after requesting %.0f / %.0f.",
1562 raThreshold, decThreshold, expectedRA, expectedDec);
1563 LOGF_WARN(
"Mount reports anti-collision thresholds %.0f|%.0f after requesting %.0f|%.0f.", raThreshold,
1564 decThreshold, expectedRA, expectedDec);
1581 return reportUnavailable(RALimitNP,
"RA limits are not available on this mount.");
1587 double left = RALimitNP[0].getValue();
1588 double right = RALimitNP[1].getValue();
1590 for (
int i = 0; i < n; i++)
1592 if (RALimitNP[0].isNameMatch(names[i]))
1595 LOGF_DEBUG(
"===CMD==> RA limit left= %f", left);
1597 else if (RALimitNP[1].isNameMatch(names[i]))
1600 LOGF_DEBUG(
"===CMD==> RA limit right= %f", right);
1606 if (!
rangeCheck(RALimitNP, 0, left,
"RA left limit",
" degrees") ||
1607 !
rangeCheck(RALimitNP, 1, right,
"RA right limit",
" degrees"))
1611 snprintf(
cmd,
sizeof(
cmd),
":Pnbsl%d#",
static_cast<int>(std::lround(left)));
1613 snprintf(
cmd,
sizeof(
cmd),
":Pnbsr%d#",
static_cast<int>(std::lround(right)));
1619 if (!sentLeft || !sentRight)
1620 LOGF_ERROR(
"The mount refused the RA %s travel limit.",
1621 !sentLeft ? (!sentRight ?
"left and right" :
"left") :
"right");
1630 return reportUnavailable(RaPidNP,
"RA/Azm PID gains are not available on this mount.");
1632 return handlePidWrite(RaPidNP,
":Pnas0",
":Pnag0#",
"RA/Azm", values, names, n);
1638 return reportUnavailable(DecPidNP,
"Dec/Alt PID gains are not available on this mount.");
1640 return handlePidWrite(DecPidNP,
":Pnas1",
":Pnag1#",
"Dec/Alt", values, names, n);
1645 if (!m_HasSuperGotoTolerance)
1646 return reportUnavailable(SuperGotoToleranceNP,
1647 "The Supervised GOTO tolerance is not available on this mount.");
1649 double tolerance = SuperGotoToleranceNP[0].getValue();
1651 for (
int i = 0; i < n; i++)
1653 if (SuperGotoToleranceNP[0].isNameMatch(names[i]))
1655 tolerance = values[i];
1656 LOGF_DEBUG(
"===CMD==> Supervised GOTO tolerance= %f", tolerance);
1660 if (!
rangeCheck(SuperGotoToleranceNP, 0, tolerance,
"Supervised GOTO tolerance",
" degrees"))
1664 snprintf(
cmd,
sizeof(
cmd),
":Pndsv%d#",
static_cast<int>(std::lround(tolerance)));
1669 LOG_ERROR(
"The mount refused the Supervised GOTO tolerance.");
1678 return reportUnavailable(AcsThresholdNP,
"The anti-collision system is not available on this mount.");
1680 double raThreshold = AcsThresholdNP[0].getValue();
1681 double decThreshold = AcsThresholdNP[1].getValue();
1683 for (
int i = 0; i < n; i++)
1685 if (AcsThresholdNP[0].isNameMatch(names[i]))
1687 raThreshold = values[i];
1688 LOGF_DEBUG(
"===CMD==> ACS RA threshold= %f", raThreshold);
1690 else if (AcsThresholdNP[1].isNameMatch(names[i]))
1692 decThreshold = values[i];
1693 LOGF_DEBUG(
"===CMD==> ACS Dec threshold= %f", decThreshold);
1700 if (!
rangeCheck(AcsThresholdNP, 0, raThreshold,
"RA collision threshold") ||
1701 !
rangeCheck(AcsThresholdNP, 1, decThreshold,
"Dec collision threshold"))
1705 snprintf(
cmd,
sizeof(
cmd),
":Pncsr%d#",
static_cast<int>(std::lround(raThreshold)));
1707 snprintf(
cmd,
sizeof(
cmd),
":Pncsd%d#",
static_cast<int>(std::lround(decThreshold)));
1712 if (!sentRa || !sentDec)
1713 LOGF_ERROR(
"The mount refused the %s anti-collision threshold.",
1714 !sentRa ? (!sentDec ?
"RA and Dec" :
"RA") :
"Dec");
1722 if (!m_HasDecLimits)
1723 return reportUnavailable(DecLimitNP,
"Dec limits are not available on this mount.");
1725 double ccw = DecLimitNP[0].getValue();
1726 double cw = DecLimitNP[1].getValue();
1728 for (
int i = 0; i < n; i++)
1730 if (DecLimitNP[0].isNameMatch(names[i]))
1733 LOGF_DEBUG(
"===CMD==> Dec limit CCW= %f", ccw);
1735 else if (DecLimitNP[1].isNameMatch(names[i]))
1738 LOGF_DEBUG(
"===CMD==> Dec limit CW= %f", cw);
1743 if (!
rangeCheck(DecLimitNP, 0, ccw,
"Dec CCW limit",
" degrees") ||
1744 !
rangeCheck(DecLimitNP, 1, cw,
"Dec CW limit",
" degrees"))
1748 snprintf(
cmd,
sizeof(
cmd),
":PsSdl%d#",
static_cast<int>(std::lround(ccw)));
1750 snprintf(
cmd,
sizeof(
cmd),
":PsSdr%d#",
static_cast<int>(std::lround(cw)));
1755 if (!sentCCW || !sentCW)
1756 LOGF_ERROR(
"The mount refused the Dec %s travel limit.",
1757 !sentCCW ? (!sentCW ?
"counter-clockwise and clockwise" :
"counter-clockwise") :
"clockwise");
1774 if (!m_HasPowerLoss)
1775 return reportUnavailable(PowerLossSP,
"Power-loss memory is not available on this mount.");
1777 return handleBoolSwitch(PowerLossSP,
":Pscs1#",
":Pscs0#",
":Pscg#",
"power loss memory", states, names, n);
1783 return reportUnavailable(AsiairSP,
"ASIAIR homing is not available on this mount.");
1785 return handleBoolSwitch(AsiairSP,
":PsAs1#",
":PsAs0#",
":PsAg#",
"ASIAIR homing", states, names, n);
1790 if (!m_HasSuperHome)
1791 return reportUnavailable(SuperHomeSP,
"Supervised Home is not available on this mount.");
1793 return handleBoolSwitch(SuperHomeSP,
":PsShs1#",
":PsShs0#",
":PsShg#",
"Supervised Home", states, names, n);
1799 if (!m_HasDecSecHome)
1800 return reportUnavailable(DecSecHomeSP,
"Dec second home is not available on this mount.");
1802 return handleBoolSwitch(DecSecHomeSP,
":PsDs1#",
":PsDs0#",
":PsDgb#",
"Dec second home", states, names, n);
1808 if (!m_HasSuperGoto)
1809 return reportUnavailable(SuperGotoSP,
"Supervised GOTO is not available on this mount.");
1811 return handleBoolSwitch(SuperGotoSP,
":Pndss1#",
":Pndss0#",
":Pndgo#",
"Supervised GOTO", states, names, n);
1817 return reportUnavailable(RALimitEnableSP,
"RA limits are not available on this mount.");
1819 return handleBoolSwitch(RALimitEnableSP,
":PsSrs1#",
":PsSrs0#",
":PsSrg#",
"RA limits", states, names, n);
1824 if (!m_HasDecLimits)
1825 return reportUnavailable(DecLimitEnableSP,
"Dec limits are not available on this mount.");
1827 return handleBoolSwitch(DecLimitEnableSP,
":PsSds1#",
":PsSds0#",
":PsSdg#",
"Dec limits", states, names, n);
1834 return reportUnavailable(LedSP,
"This mount has no status LED.");
1836 return handlePendingSwitch(LedSP,
":PsLs1#",
":PsLs0#",
":PsLgb#",
"Status LED", states, names, n);
1841 if (!m_HasAutoTrack)
1842 return reportUnavailable(AutoTrackSP,
"Auto tracking is not available on this mount.");
1844 return handlePendingSwitch(AutoTrackSP,
":Psas1#",
":Psas0#",
":Psag#",
"Auto tracking", states, names, n);
1850 return reportUnavailable(AcsEnableSP,
"The anti-collision system is not available on this mount.");
1854 handleBoolSwitch(AcsEnableSP,
":PsCs1#",
":PsCs0#",
":PsCg#",
"anti-collision", states, names, n,
1861 m_AcsCountersGood =
true;
1871 return reportUnavailable(AcsClearSP,
"The anti-collision system is not available on this mount.");
1875 if (!AcsClearSP.
update(states, names, n))
1881 if (AcsClearSP[0].getState() !=
ISS_ON)
1896 AcsClearSP.
apply(
"The mount did not accept the counter clear.");
1904 m_AcsCountersGood =
true;
1911 LOG_INFO(
"Collision counters cleared.");
INDI::PropertyText getText(const char *name) const
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
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)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
TelescopeStatus TrackState
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
int getCommandSingleCharErrorOrLongResponse(int fd, char *data, const char *cmd)
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool ReadScopeStatus() override
Read telescope status.
bool getVendorQuad(const char *cmd, double *out)
bool refreshRALimits(double expectedLeft, double expectedRight)
bool handlePidWrite(INDI::PropertyNumber &np, const char *axisPrefix, const char *readCmd, const char *label, double values[], char *names[], int n)
bool getVendorBool(const char *cmd, bool &value)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
void deleteVendorProperties()
bool readVendorStringOnce(const char *cmd, char *out, size_t outLen, bool requireTerminated, int *rcOut)
bool getVendorString(const char *cmd, char *out, size_t outLen, bool requireTerminated=false, int *rcOut=nullptr)
bool refreshDecLimits(double expectedCCW, double expectedCW)
bool probeBudgetExpired()
bool refreshSuperGotoTolerance(double expected)
bool probePidAxis(INDI::PropertyNumber &np, const char *readCmd, const char *label)
virtual bool ReadScopeStatus() override
Read telescope status.
bool probeBoolSwitch(INDI::PropertySwitch &sp, const char *readCmd, const char *label)
bool rangeCheck(INDI::PropertyNumber &np, int index, double value, const char *what, const char *unit="")
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void defineVendorProperties()
virtual bool initProperties() override
Called to initialize basic properties required all the time.
bool sendVendorSetter(const char *cmd)
bool handleBoolSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff, const char *readCmd, const char *label, ISState *states, char *names[], int n, bool *mirror=nullptr)
virtual const char * getDefaultName() override
bool setReportedValues(INDI::PropertyNumber &np, const double *values, int count)
bool getVendorPair(const char *cmd, double &first, double &second)
bool refreshAcsCounters(bool stickyOnFailure=true)
bool handlePendingSwitch(INDI::PropertySwitch &sp, const char *setOn, const char *setOff, const char *readCmd, const char *label, ISState *states, char *names[], int n)
bool refreshAcsThresholds(double expectedRA, double expectedDec)
INDI::PropertySwitch MovementNSSP
INDI::PropertySwitch MovementWESP
#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,...)
#define PROXISKY_MODEL_PROBE_SECONDS
#define PROXISKY_LIMITS_TAB
#define PROXISKY_PROBE_BUDGET_SECONDS
#define PROXISKY_PROBE_RETRY_NSEC
#define PROXISKY_PID_RESTART_SECONDS
#define PROXISKY_ADVANCED_TAB
#define PROXISKY_MODEL_PROBE_MAX_ATTEMPTS
#define PROXISKY_SETTINGS_TAB
#define PROXISKY_ACS_POLL_DIVIDER