31#define GEMINI_MAX_RETRIES 1
32#define GEMINI_TIMEOUT 3
33#define GEMINI_MAXBUF 16
34#define GEMINI_TEMPERATURE_FREQ 20
35#define GEMINI_POSITION_THRESHOLD 5
37#define FOCUS_SETTINGS_TAB "Settings"
38#define STATUS_TAB "Status"
39#define ROTATOR_TAB "Rotator"
42static std::unique_ptr<Gemini> geminiFR(
new Gemini());
50 focuserSimPosition = 0;
58 isFocuserAbsolute =
true;
59 isFocuserHoming =
false;
91 TemperatureNP[0].
fill(
"TEMPERATURE",
"Celsius",
"%6.2f", -50, 70., 0., 0.);
108 TemperatureCoeffNP[A].
fill(
"A",
"",
"%.f", -9999, 9999, 100., 0.);
109 TemperatureCoeffNP[B].
fill(
"B",
"",
"%.f", -9999, 9999, 100., 0.);
110 TemperatureCoeffNP[C].
fill(
"C",
"",
"%.f", -9999, 9999, 100., 0.);
111 TemperatureCoeffNP[D].
fill(
"D",
"",
"%.f", -9999, 9999, 100., 0.);
112 TemperatureCoeffNP[E].
fill(
"E",
"",
"%.f", -9999, 9999, 100., 0.);
119 FocuserHomeOnStartSP.
fill(
getDeviceName(),
"FOCUSER_HOME_ON_START",
"Home on Start",
123 TemperatureCompensateModeSP[A].
fill(
"A",
"",
ISS_OFF);
124 TemperatureCompensateModeSP[B].
fill(
"B",
"",
ISS_OFF);
125 TemperatureCompensateModeSP[C].
fill(
"C",
"",
ISS_OFF);
126 TemperatureCompensateModeSP[D].
fill(
"D",
"",
ISS_OFF);
127 TemperatureCompensateModeSP[E].
fill(
"E",
"",
ISS_OFF);
167 RotatorHomeOnStartSP.
fill(
getDeviceName(),
"ROTATOR_HOME_ON_START",
"Home on Start",
184 RotatorAbsPosNP[0].
fill(
"ROTATOR_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0., 0., 0., 0.);
191 IUFillNumber(&RotatorAbsAngleN[0],
"ANGLE",
"Degrees",
"%.2f", 0, 360., 10., 0.);
219 LedNP[0].
fill(
"Intensity",
"",
"%.f", 0, 100, 5., 0.);
275 if (getFocusConfig() && getRotatorConfig())
276 LOG_INFO(
"Gemini parameters updated, rotating focuser ready for use.");
279 LOG_ERROR(
"Failed to retrieve rotating focuser configuration settings...");
327 LOG_INFO(
"Gemini is online. Getting focus parameters...");
331 LOG_INFO(
"Error retrieving data from Gemini, please ensure Gemini controller is "
332 "powered and the port is correct.");
342 return "Gemini Focusing Rotator";
356 TemperatureCompensateSP.
update(states, names, n);
357 if (setTemperatureCompensation(TemperatureCompensateSP[
INDI_ENABLED].getState() ==
ISS_ON))
363 TemperatureCompensateSP.
reset();
368 TemperatureCompensateSP.
apply();
373 if (TemperatureCompensateOnStartSP.
isNameMatch(name))
376 TemperatureCompensateOnStartSP.
update(states, names, n);
377 if (setTemperatureCompensationOnStart(TemperatureCompensateOnStartSP[
INDI_ENABLED].getState() ==
ISS_ON))
383 TemperatureCompensateOnStartSP.
reset();
388 TemperatureCompensateOnStartSP.
apply();
396 TemperatureCompensateModeSP.
update(states, names, n);
398 if (setTemperatureCompensationMode(mode))
404 TemperatureCompensateModeSP.
reset();
409 TemperatureCompensateModeSP.
apply();
417 FocuserHomeOnStartSP.
update(states, names, n);
424 FocuserHomeOnStartSP.
reset();
429 FocuserHomeOnStartSP.
apply();
437 RotatorHomeOnStartSP.
update(states, names, n);
444 RotatorHomeOnStartSP.
reset();
449 RotatorHomeOnStartSP.
apply();
489 FocuserGotoSP.
update(states, names, n);
498 isFocuserHoming =
true;
499 LOG_INFO(
"Focuser moving to home position...");
509 LOG_INFO(
"Focuser moving to center position...");
517 FocuserGotoSP.
apply();
522 if (strstr(name,
"ROTATOR"))
530 if (!strcmp(RotatorGotoSP.name, name))
540 RotatorAbsPosNP.
apply();
541 isRotatorHoming =
true;
542 LOG_INFO(
"Rotator moving to home position...");
552 LOG_INFO(
"Rotator moving to center position...");
554 RotatorAbsPosNP.
apply();
569 if (reverseRotator(ReverseRotatorS[0].s ==
ISS_ON))
584 RotatorAbsPosNP.
apply();
585 RotatorAbsAngleNP.apply();
613 HFocusNameTP.
update(texts, names, n);
619 HFocusNameTP.
apply();
636 TemperatureCoeffNP.
update(values, names, n);
637 for (
int i = 0; i < n; i++)
639 if (setTemperatureCompensationCoeff(
'A' + i, TemperatureCoeffNP[i].getValue()) ==
false)
641 LOG_ERROR(
"Failed to set temperature coefficients.");
643 TemperatureCoeffNP.
apply();
649 TemperatureCoeffNP.
apply();
676 LOG_ERROR(
"Failed to set rotator backlash value.");
690 LedNP.
update(values, names, n);
691 if (setLedLevel(LedNP[0].getValue()))
695 LOGF_INFO(
"Focuser LED level intensity : %f", LedNP[0].getValue());
703 RotatorAbsPosNP.
update(values, names, n);
704 RotatorAbsPosNP.
setState(MoveAbsRotatorTicks(
static_cast<uint32_t
>(RotatorAbsPosNP[0].value)));
705 RotatorAbsPosNP.
apply();
709 if (strstr(name,
"ROTATOR"))
717 if (!strcmp(RotatorAbsPosNP.name, name))
720 RotatorAbsPosNP.s = MoveAbsRotatorTicks(
static_cast<uint32_t
>(RotatorAbsPosN[0].value));
721 RotatorAbsPosNP.
apply();
726 if (!strcmp(RotatorAbsAngleNP.name, name))
729 RotatorAbsAngleNP.s = MoveAbsRotatorAngle(RotatorAbsAngleN[0].value);
730 RotatorAbsAngleNP.apply();
745 const char *
cmd =
"<F100GETDNN>";
750 int nbytes_written = 0;
752 memset(response, 0,
sizeof(response));
758 strncpy(response,
"Castor", 16);
759 nbytes_read = strlen(response) + 1;
783 response[nbytes_read - 1] =
'\0';
790 tcflush(
PortFD, TCIFLUSH);
795 tcflush(
PortFD, TCIFLUSH);
802bool Gemini::getFocusConfig()
804 const char *
cmd =
"<F100GETCFG>";
809 int nbytes_written = 0;
812 memset(response, 0,
sizeof(response));
818 strncpy(response,
"!00",
sizeof(response));
819 nbytes_read = strlen(response) + 1;
823 tcflush(
PortFD, TCIFLUSH);
852 memset(response, 0,
sizeof(response));
857 strncpy(response,
"NickName=Tommy\n",
sizeof(response));
858 nbytes_read = strlen(response);
866 response[nbytes_read - 1] =
'\0';
870 int rc = sscanf(response,
"%15[^=]=%15[^\n]s", key, nickname);
875 HFocusNameTP.
setName(nickname);
876 HFocusNameTP.
apply();
879 HFocusNameTP.
apply();
881 memset(response, 0,
sizeof(response));
886 snprintf(response,
sizeof(response),
"Max Pos = %06d\n", 100000);
887 nbytes_read = strlen(response);
895 response[nbytes_read - 1] =
'\0';
899 rc = sscanf(response,
"%15[^=]=%d", key, &maxPos);
913 maxControllerTicks = maxPos;
918 memset(response, 0,
sizeof(response));
923 strncpy(response,
"Dev Typ = A\n",
sizeof(response));
924 nbytes_read = strlen(response);
932 response[nbytes_read - 1] =
'\0';
936 memset(response, 0,
sizeof(response));
941 snprintf(response,
sizeof(response),
"TComp ON = %d\n", TemperatureCompensateSP[
INDI_ENABLED].getState() ==
ISS_ON ? 1 : 0);
942 nbytes_read = strlen(response);
950 response[nbytes_read - 1] =
'\0';
954 rc = sscanf(response,
"%15[^=]=%d", key, &TCompOn);
958 TemperatureCompensateSP.
reset();
962 TemperatureCompensateSP.
apply();
964 memset(response, 0,
sizeof(response));
969 snprintf(response,
sizeof(response),
"TempCo A = %d\n", (
int)TemperatureCoeffNP[
FOCUS_A_COEFF].getValue());
970 nbytes_read = strlen(response);
978 response[nbytes_read - 1] =
'\0';
982 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffA);
988 memset(response, 0,
sizeof(response));
993 snprintf(response,
sizeof(response),
"TempCo B = %d\n", (
int)TemperatureCoeffNP[
FOCUS_B_COEFF].getValue());
994 nbytes_read = strlen(response);
1002 response[nbytes_read - 1] =
'\0';
1006 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffB);
1012 memset(response, 0,
sizeof(response));
1017 snprintf(response,
sizeof(response),
"TempCo C = %d\n", (
int)TemperatureCoeffNP[
FOCUS_C_COEFF].getValue());
1018 nbytes_read = strlen(response);
1026 response[nbytes_read - 1] =
'\0';
1030 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffC);
1036 memset(response, 0,
sizeof(response));
1041 snprintf(response,
sizeof(response),
"TempCo D = %d\n", (
int)TemperatureCoeffNP[
FOCUS_D_COEFF].getValue());
1042 nbytes_read = strlen(response);
1050 response[nbytes_read - 1] =
'\0';
1054 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffD);
1060 memset(response, 0,
sizeof(response));
1065 snprintf(response,
sizeof(response),
"TempCo E = %d\n", (
int)TemperatureCoeffNP[
FOCUS_E_COEFF].getValue());
1066 nbytes_read = strlen(response);
1074 response[nbytes_read - 1] =
'\0';
1078 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffE);
1085 TemperatureCoeffNP.
apply();
1087 memset(response, 0,
sizeof(response));
1092 snprintf(response,
sizeof(response),
"TC Mode = %c\n",
'C');
1093 nbytes_read = strlen(response);
1101 response[nbytes_read - 1] =
'\0';
1104 char compensateMode;
1105 rc = sscanf(response,
"%15[^=]= %c", key, &compensateMode);
1109 TemperatureCompensateModeSP.
reset();
1110 int index = compensateMode -
'A';
1111 if (index >= 0 && index <= 5)
1118 LOGF_ERROR(
"Invalid index %d for compensation mode.", index);
1122 TemperatureCompensateModeSP.
apply();
1125 memset(response, 0,
sizeof(response));
1129 nbytes_read = strlen(response);
1137 response[nbytes_read - 1] =
'\0';
1141 rc = sscanf(response,
"%15[^=]=%d", key, &BLCCompensate);
1152 memset(response, 0,
sizeof(response));
1155 snprintf(response,
sizeof(response),
"BLC Stps = %d\n", 50);
1156 nbytes_read = strlen(response);
1164 response[nbytes_read - 1] =
'\0';
1168 rc = sscanf(response,
"%15[^=]=%d", key, &BLCValue);
1177 memset(response, 0,
sizeof(response));
1180 snprintf(response,
sizeof(response),
"TC Start = %d\n",
1182 nbytes_read = strlen(response);
1190 response[nbytes_read - 1] =
'\0';
1194 rc = sscanf(response,
"%15[^=]=%d", key, &TCOnStart);
1198 TemperatureCompensateOnStartSP.
reset();
1202 TemperatureCompensateOnStartSP.
apply();
1205 memset(response, 0,
sizeof(response));
1210 snprintf(response,
sizeof(response),
"HOnStart = %d\n", FocuserHomeOnStartSP[
INDI_ENABLED].getState() ==
ISS_ON ? 1 : 0);
1211 nbytes_read = strlen(response);
1219 response[nbytes_read - 1] =
'\0';
1223 rc = sscanf(response,
"%15[^=]=%d", key, &StartOnHome);
1227 FocuserHomeOnStartSP.
reset();
1231 FocuserHomeOnStartSP.
apply();
1235 memset(response, 0,
sizeof(response));
1238 strncpy(response,
"END\n", 16);
1239 nbytes_read = strlen(response);
1248 if (nbytes_read > 0)
1250 response[nbytes_read - 1] =
'\0';
1255 if (strcmp(response,
"END"))
1260 tcflush(
PortFD, TCIFLUSH);
1270bool Gemini::getRotatorStatus()
1272 const char *
cmd =
"<R100GETSTA>";
1276 int nbytes_read = 0;
1277 int nbytes_written = 0;
1280 memset(response, 0,
sizeof(response));
1286 strncpy(response,
"!00", 16);
1287 nbytes_read = strlen(response) + 1;
1291 tcflush(
PortFD, TCIFLUSH);
1314 memset(response, 0,
sizeof(response));
1317 snprintf(response, 32,
"CurrStep = %06d\n", rotatorSimPosition);
1318 nbytes_read = strlen(response);
1326 response[nbytes_read - 1] =
'\0';
1327 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1330 int rc = sscanf(response,
"%15[^=]=%d", key, &currPos);
1334 if (RotatorAbsPosNP[0].getValue() != currPos)
1336 RotatorAbsPosNP[0].setValue(currPos);
1337 RotatorAbsPosNP.
apply();
1346 memset(response, 0,
sizeof(response));
1349 snprintf(response, 32,
"TargStep = %06d\n", targetFocuserPosition);
1350 nbytes_read = strlen(response);
1358 response[nbytes_read - 1] =
'\0';
1359 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1364 memset(response, 0,
sizeof(response));
1367 snprintf(response, 32,
"CurenPA = %06d\n", rotatorSimPA);
1368 nbytes_read = strlen(response);
1376 response[nbytes_read - 1] =
'\0';
1377 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1380 rc = sscanf(response,
"%15[^=]=%d", key, &currPA);
1384 double diffPA = fabs(
GotoRotatorNP[0].getValue() - currPA / 1000.0);
1397 memset(response, 0,
sizeof(response));
1400 snprintf(response, 32,
"TargetPA = %06d\n", targetFocuserPosition);
1401 nbytes_read = strlen(response);
1409 response[nbytes_read - 1] =
'\0';
1410 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1417 memset(response, 0,
sizeof(response));
1420 snprintf(response, 32,
"IsMoving = %d\n", (rotatorSimStatus[
STATUS_MOVING] ==
ISS_ON) ? 1 : 0);
1421 nbytes_read = strlen(response);
1429 response[nbytes_read - 1] =
'\0';
1430 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1433 rc = sscanf(response,
"%15[^=]=%d", key, &isMoving);
1442 memset(response, 0,
sizeof(response));
1445 snprintf(response, 32,
"IsHoming = %d\n", (rotatorSimStatus[
STATUS_HOMING] ==
ISS_ON) ? 1 : 0);
1446 nbytes_read = strlen(response);
1454 response[nbytes_read - 1] =
'\0';
1455 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1458 rc = sscanf(response,
"%15[^=]=%d", key, &_isHoming);
1466 isRotatorHoming =
true;
1471 memset(response, 0,
sizeof(response));
1474 snprintf(response, 32,
"IsHomed = %d\n", (rotatorSimStatus[
STATUS_HOMED] ==
ISS_ON) ? 1 : 0);
1475 nbytes_read = strlen(response);
1483 response[nbytes_read - 1] =
'\0';
1484 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1487 rc = sscanf(response,
"%15[^=]=%d", key, &isHomed);
1492 RotatorStatusLP.
apply();
1496 memset(response, 0,
sizeof(response));
1499 strncpy(response,
"END\n", 16);
1500 nbytes_read = strlen(response);
1509 if (nbytes_read > 0)
1511 response[nbytes_read - 1] =
'\0';
1516 if (strcmp(response,
"END"))
1524 tcflush(
PortFD, TCIFLUSH);
1533bool Gemini::getRotatorConfig()
1535 const char *
cmd =
"<R100GETCFG>";
1539 int nbytes_read = 0;
1540 int nbytes_written = 0;
1543 memset(response, 0,
sizeof(response));
1549 strncpy(response,
"!00",
sizeof(response));
1550 nbytes_read = strlen(response) + 1;
1554 tcflush(
PortFD, TCIFLUSH);
1583 memset(response, 0,
sizeof(response));
1589 strncpy(response,
"NickName=Juli\n",
sizeof(response));
1590 nbytes_read = strlen(response);
1598 response[nbytes_read - 1] =
'\0';
1602 int rc = sscanf(response,
"%15[^=]=%15[^\n]s", key, nickname);
1609 HFocusNameTP.
apply();
1611 memset(response, 0,
sizeof(response));
1618 snprintf(response,
sizeof(response),
"MaxSteps = %06d\n", 100000);
1619 nbytes_read = strlen(response);
1627 response[nbytes_read - 1] =
'\0';
1630 uint32_t maxPos = 0;
1631 rc = sscanf(response,
"%15[^=]=%d", key, &maxPos);
1634 RotatorAbsPosNP[0].setMin(0);
1635 RotatorAbsPosNP[0].setMax(maxPos);
1636 RotatorAbsPosNP[0].setStep(maxPos / 50.0);
1642 memset(response, 0,
sizeof(response));
1649 strncpy(response,
"Dev Type = B\n",
sizeof(response));
1650 nbytes_read = strlen(response);
1658 response[nbytes_read - 1] =
'\0';
1662 memset(response, 0,
sizeof(response));
1667 memset(response, 0,
sizeof(response));
1671 nbytes_read = strlen(response);
1679 response[nbytes_read - 1] =
'\0';
1683 rc = sscanf(response,
"%15[^=]=%d", key, &BLCCompensate);
1696 memset(response, 0,
sizeof(response));
1699 snprintf(response,
sizeof(response),
"BLCSteps = %d\n", 50);
1700 nbytes_read = strlen(response);
1708 response[nbytes_read - 1] =
'\0';
1712 rc = sscanf(response,
"%15[^=]=%d", key, &BLCValue);
1723 memset(response, 0,
sizeof(response));
1726 snprintf(response,
sizeof(response),
"HOnStart = %d\n", RotatorHomeOnStartSP[
INDI_ENABLED].getState() ==
ISS_ON ? 1 : 0);
1727 nbytes_read = strlen(response);
1735 response[nbytes_read - 1] =
'\0';
1739 rc = sscanf(response,
"%15[^=]=%d", key, &StartOnHome);
1743 RotatorHomeOnStartSP.
reset();
1747 RotatorHomeOnStartSP.
apply();
1752 memset(response, 0,
sizeof(response));
1755 snprintf(response, 32,
"Reverse = %d\n", (rotatorSimStatus[
STATUS_REVERSE] ==
ISS_ON) ? 1 : 0);
1756 nbytes_read = strlen(response);
1764 response[nbytes_read - 1] =
'\0';
1765 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1768 rc = sscanf(response,
"%15[^=]=%d", key, &reverse);
1785 RotatorStatusLP.
apply();
1790 memset(response, 0,
sizeof(response));
1793 snprintf(response, 32,
"MaxSpeed = %d\n", 800);
1794 nbytes_read = strlen(response);
1802 response[nbytes_read - 1] =
'\0';
1803 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1807 memset(response, 0,
sizeof(response));
1810 strncpy(response,
"END\n", 16);
1811 nbytes_read = strlen(response);
1820 if (nbytes_read > 0)
1822 response[nbytes_read - 1] =
'\0';
1827 if (strcmp(response,
"END"))
1832 tcflush(
PortFD, TCIFLUSH);
1842bool Gemini::getFocusStatus()
1844 const char *
cmd =
"<F100GETSTA>";
1848 int nbytes_read = 0;
1849 int nbytes_written = 0;
1852 memset(response, 0,
sizeof(response));
1858 strncpy(response,
"!00", 16);
1859 nbytes_read = strlen(response) + 1;
1863 tcflush(
PortFD, TCIFLUSH);
1884 memset(response, 0,
sizeof(response));
1888 strcpy(response,
"CurrTemp = +21.7\n");
1889 nbytes_read = strlen(response);
1898 if (nbytes_read > 0)
1899 response[nbytes_read - 1] =
'\0';
1901 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1904 int rc = sscanf(response,
"%15[^=]=%f", key, &
temperature);
1908 TemperatureNP.
apply();
1913 int rc = sscanf(response,
"%15[^=]= %s", key, np);
1915 if (rc != 2 || strcmp(np,
"NP"))
1920 TemperatureNP.
apply();
1929 memset(response, 0,
sizeof(response));
1932 snprintf(response, 32,
"CurrStep = %06d\n", focuserSimPosition);
1933 nbytes_read = strlen(response);
1941 response[nbytes_read - 1] =
'\0';
1942 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1944 uint32_t currPos = 0;
1945 rc = sscanf(response,
"%15[^=]=%d", key, &currPos);
1957 memset(response, 0,
sizeof(response));
1960 snprintf(response, 32,
"TargStep = %06d\n", targetFocuserPosition);
1961 nbytes_read = strlen(response);
1969 response[nbytes_read - 1] =
'\0';
1970 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1977 memset(response, 0,
sizeof(response));
1980 snprintf(response, 32,
"IsMoving = %d\n", (focuserSimStatus[
STATUS_MOVING] ==
ISS_ON) ? 1 : 0);
1981 nbytes_read = strlen(response);
1989 response[nbytes_read - 1] =
'\0';
1990 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1993 rc = sscanf(response,
"%15[^=]=%d", key, &isMoving);
2002 memset(response, 0,
sizeof(response));
2005 snprintf(response, 32,
"IsHoming = %d\n", (focuserSimStatus[
STATUS_HOMING] ==
ISS_ON) ? 1 : 0);
2006 nbytes_read = strlen(response);
2014 response[nbytes_read - 1] =
'\0';
2015 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2018 rc = sscanf(response,
"%15[^=]=%d", key, &_isHoming);
2024 if (isFocuserAbsolute ==
false)
2029 isFocuserHoming =
true;
2034 memset(response, 0,
sizeof(response));
2037 snprintf(response, 32,
"IsHomed = %d\n", (focuserSimStatus[
STATUS_HOMED] ==
ISS_ON) ? 1 : 0);
2038 nbytes_read = strlen(response);
2046 response[nbytes_read - 1] =
'\0';
2047 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2050 rc = sscanf(response,
"%15[^=]=%d", key, &isHomed);
2056 if (isFocuserAbsolute ==
false)
2062 memset(response, 0,
sizeof(response));
2066 nbytes_read = strlen(response);
2074 response[nbytes_read - 1] =
'\0';
2075 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2078 rc = sscanf(response,
"%15[^=]=%d", key, &TmpProbe);
2087 memset(response, 0,
sizeof(response));
2091 nbytes_read = strlen(response);
2099 response[nbytes_read - 1] =
'\0';
2100 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2103 rc = sscanf(response,
"%15[^=]=%d", key, &RemoteIO);
2112 memset(response, 0,
sizeof(response));
2115 snprintf(response, 32,
"HCStatus = %d\n", (focuserSimStatus[
STATUS_HNDCTRL] ==
ISS_ON) ? 1 : 0);
2116 nbytes_read = strlen(response);
2124 response[nbytes_read - 1] =
'\0';
2125 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2128 rc = sscanf(response,
"%15[^=]=%d", key, &HndCtlr);
2135 FocuserStatusLP.
apply();
2139 memset(response, 0,
sizeof(response));
2142 strncpy(response,
"END\n", 16);
2143 nbytes_read = strlen(response);
2152 if (nbytes_read > 0)
2154 response[nbytes_read - 1] =
'\0';
2159 if (strcmp(response,
"END"))
2167 tcflush(
PortFD, TCIFLUSH);
2176bool Gemini::setLedLevel(
int level)
2184 int nbytes_read = 0;
2185 int nbytes_written = 0;
2187 memset(response, 0,
sizeof(response));
2189 snprintf(
cmd, 16,
"<H100SETLED%d>", level);
2195 strncpy(response,
"SET", 16);
2196 nbytes_read = strlen(response) + 1;
2200 tcflush(
PortFD, TCIFLUSH);
2220 if (nbytes_read > 0)
2222 response[nbytes_read - 1] =
'\0';
2224 tcflush(
PortFD, TCIFLUSH);
2226 if (!strcmp(response,
"SET"))
2238bool Gemini::setNickname(DeviceType
type,
const char *nickname)
2245 int nbytes_read = 0;
2246 int nbytes_written = 0;
2248 memset(response, 0,
sizeof(response));
2256 tcflush(
PortFD, TCIFLUSH);
2272 tcflush(
PortFD, TCIFLUSH);
2279bool Gemini::halt(DeviceType
type)
2285 int nbytes_read = 0;
2286 int nbytes_written = 0;
2288 memset(response, 0,
sizeof(response));
2300 isRotatorHoming =
false;
2305 tcflush(
PortFD, TCIFLUSH);
2321 isRotatorHoming =
false;
2323 tcflush(
PortFD, TCIFLUSH);
2331bool Gemini::home(DeviceType
type)
2337 int nbytes_read = 0;
2338 int nbytes_written = 0;
2340 memset(response, 0,
sizeof(response));
2350 targetFocuserPosition = 0;
2355 targetRotatorPosition = 0;
2360 tcflush(
PortFD, TCIFLUSH);
2376 tcflush(
PortFD, TCIFLUSH);
2384bool Gemini::homeOnStart(DeviceType
type,
bool enable)
2390 int nbytes_read = 0;
2391 int nbytes_written = 0;
2393 memset(response, 0,
sizeof(response));
2400 tcflush(
PortFD, TCIFLUSH);
2416 tcflush(
PortFD, TCIFLUSH);
2424bool Gemini::center(DeviceType
type)
2427 return MoveAbsRotatorTicks(RotatorAbsPosNP[0].getMax() / 2);
2429 const char *
cmd =
"<F100CENTER>";
2433 int nbytes_read = 0;
2434 int nbytes_written = 0;
2436 memset(response, 0,
sizeof(response));
2450 targetRotatorPosition = RotatorAbsPosNP[0].getMax() / 2;
2456 tcflush(
PortFD, TCIFLUSH);
2472 tcflush(
PortFD, TCIFLUSH);
2480bool Gemini::setTemperatureCompensation(
bool enable)
2486 int nbytes_read = 0;
2487 int nbytes_written = 0;
2489 memset(response, 0,
sizeof(response));
2491 snprintf(
cmd, 16,
"<F100SETTCE%d>", enable ? 1 : 0);
2497 tcflush(
PortFD, TCIFLUSH);
2519bool Gemini::setTemperatureCompensationMode(
char mode)
2525 int nbytes_read = 0;
2526 int nbytes_written = 0;
2528 memset(response, 0,
sizeof(response));
2530 snprintf(
cmd, 16,
"<F100SETTCM%c>", mode);
2536 tcflush(
PortFD, TCIFLUSH);
2558bool Gemini::setTemperatureCompensationCoeff(
char mode, int16_t coeff)
2564 int nbytes_read = 0;
2565 int nbytes_written = 0;
2567 memset(response, 0,
sizeof(response));
2569 snprintf(
cmd, 32,
"<F100SETTCC%c%c%04d>", mode, coeff >= 0 ?
'+' :
'-', (int)
std::abs(coeff));
2575 tcflush(
PortFD, TCIFLUSH);
2597bool Gemini::setTemperatureCompensationOnStart(
bool enable)
2603 int nbytes_read = 0;
2604 int nbytes_written = 0;
2606 memset(response, 0,
sizeof(response));
2608 snprintf(
cmd, 16,
"<F100SETTCS%d>", enable ? 1 : 0);
2612 tcflush(
PortFD, TCIFLUSH);
2652bool Gemini::setBacklashCompensation(DeviceType
type,
bool enable)
2658 int nbytes_read = 0;
2659 int nbytes_written = 0;
2661 memset(response, 0,
sizeof(response));
2689bool Gemini::setBacklashCompensationSteps(DeviceType
type, uint16_t steps)
2695 int nbytes_read = 0;
2696 int nbytes_written = 0;
2698 memset(response, 0,
sizeof(response));
2726bool Gemini::reverseRotator(
bool enable)
2732 int nbytes_read = 0;
2733 int nbytes_written = 0;
2735 memset(response, 0,
sizeof(response));
2737 snprintf(
cmd, 16,
"<R100SETREV%d>", enable ? 1 : 0);
2763bool Gemini::resetFactory()
2765 const char *
cmd =
"<H100RESETH>";
2769 int nbytes_read = 0;
2770 int nbytes_written = 0;
2772 memset(response, 0,
sizeof(response));
2778 strncpy(response,
"SET", 16);
2779 nbytes_read = strlen(response) + 1;
2783 tcflush(
PortFD, TCIFLUSH);
2803 if (nbytes_read > 0)
2805 response[nbytes_read - 1] =
'\0';
2807 tcflush(
PortFD, TCIFLUSH);
2809 if (!strcmp(response,
"SET"))
2830 int nbytes_read = 0;
2832 memset(response, 0,
sizeof(response));
2836 strcpy(response,
"!00");
2837 nbytes_read = strlen(response) + 1;
2849 if (nbytes_read > 0)
2851 response[nbytes_read - 1] =
'\0';
2854 if (!strcmp(response,
"!00"))
2858 memset(response, 0,
sizeof(response));
2859 while (strstr(response,
"END") ==
nullptr)
2867 response[nbytes_read - 1] =
'\0';
2868 LOGF_ERROR(
"Controller error: %s", response);
2886 int nbytes_written = 0;
2890 memset(response, 0,
sizeof(response));
2892 snprintf(
cmd, 16,
"<F100DOMOVE%c>", (dir ==
FOCUS_INWARD) ?
'0' :
'1');
2898 tcflush(
PortFD, TCIFLUSH);
2910 gettimeofday(&focusMoveStart,
nullptr);
2911 focusMoveRequest = duration / 1000.0;
2921 tcflush(
PortFD, TCIFLUSH);
2935 int nbytes_written = 0;
2937 targetFocuserPosition = targetTicks;
2939 memset(response, 0,
sizeof(response));
2941 snprintf(
cmd, 32,
"<F100MOVABS%06d>", targetTicks);
2947 tcflush(
PortFD, TCIFLUSH);
2962 tcflush(
PortFD, TCIFLUSH);
2972 uint32_t newPosition = 0;
2997 bool statusrc =
false;
2998 for (
int i = 0; i < 2; i++)
3000 statusrc = getFocusStatus();
3005 if (statusrc ==
false)
3007 LOG_WARN(
"Unable to read focuser status....");
3017 focuserSimPosition += 100;
3019 focuserSimPosition -= 100;
3023 if (std::abs((int64_t)focuserSimPosition - (int64_t)targetFocuserPosition) < 100)
3039 isFocuserHoming =
false;
3041 FocuserGotoSP.
reset();
3043 FocuserGotoSP.
apply();
3046 LOG_INFO(
"Focuser reached home position.");
3056 FocuserGotoSP.
reset();
3058 FocuserGotoSP.
apply();
3060 LOG_INFO(
"Focuser reached requested position.");
3066 FocuserGotoSP.
apply();
3071 for (
int i = 0; i < 2; i++)
3073 statusrc = getRotatorStatus();
3078 if (statusrc ==
false)
3080 LOG_WARN(
"Unable to read rotator status....");
3112 isRotatorHoming =
false;
3117 RotatorAbsPosNP.
apply();
3120 LOG_INFO(
"Rotator reached home position.");
3125 RotatorAbsPosNP.
apply();
3134 LOG_INFO(
"Rotator reached requested position.");
3151 const char *
cmd =
"<F100DOHALT>";
3155 int nbytes_written = 0;
3157 memset(response, 0,
sizeof(response));
3163 strncpy(response,
"!00", 16);
3169 tcflush(
PortFD, TCIFLUSH);
3192 FocuserGotoSP.
reset();
3194 FocuserGotoSP.
apply();
3196 tcflush(
PortFD, TCIFLUSH);
3204float Gemini::calcTimeLeft(timeval start,
float req)
3212 gettimeofday(&now,
nullptr);
3215 (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
3216 timesince = timesince / 1000;
3217 timeleft = req - timesince;
3224IPState Gemini::MoveAbsRotatorTicks(uint32_t targetTicks)
3230 int nbytes_written = 0;
3232 targetRotatorPosition = targetTicks;
3234 memset(response, 0,
sizeof(response));
3236 snprintf(
cmd, 32,
"<R100MOVABS%06d>", targetTicks);
3242 tcflush(
PortFD, TCIFLUSH);
3257 tcflush(
PortFD, TCIFLUSH);
3265IPState Gemini::MoveAbsRotatorAngle(
double angle)
3269 int nbytes_written = 0;
3271 targetRotatorAngle = angle * 1000.0;
3273 memset(response, 0,
sizeof(response));
3275 snprintf(
cmd, 32,
"<R100MOVEPA%06ud>", targetRotatorAngle);
3283 tcflush(
PortFD, TCIFLUSH);
3298 tcflush(
PortFD, TCIFLUSH);
3313 TemperatureCompensateSP.
save(fp);
3314 TemperatureCompensateOnStartSP.
save(fp);
3315 TemperatureCoeffNP.
save(fp);
3316 TemperatureCompensateModeSP.
save(fp);
3317 FocuserHomeOnStartSP.
apply();
3318 RotatorHomeOnStartSP.
save(fp);
3328 IPState state = MoveAbsRotatorAngle(angle);
3330 RotatorAbsPosNP.
apply();
3348 return reverseRotator(enabled);
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool saveConfigItems(FILE *fp) override
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual IPState HomeRotator() override
HomeRotator Go to home position.
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Processes incoming new text values from the client for this device. Delegates processing to the contr...
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
bool focuserConfigurationComplete
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
bool rotatorConfigurationComplete
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool SetRotatorBacklashEnabled(bool enabled) override
SetRotatorBacklashEnabled Enables or disables the Rotator backlash compensation.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
virtual IPState MoveAbsFocuser(uint32_t targetPosition) override
MoveFocuser the focuser to an absolute position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool SetRotatorBacklash(int32_t steps) override
SetRotatorBacklash Set the Rotatorer backlash compensation value.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual const char * getDefaultName() override
INDI::PropertyText getText(const char *name) const
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusTimerNP
INDI::PropertySwitch FocusBacklashSP
INDI::PropertyNumber FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Connection::Serial * serialConnection
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Processes incoming new text values from the client for this device. Delegates processing to the contr...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setName(const char *name)
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPState state)
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 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)
bool update(const char *const texts[], 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)
INDI::PropertySwitch AbortRotatorSP
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
INDI::PropertySwitch RotatorBacklashSP
void initProperties(const char *groupName)
Initialize Rotator properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
INDI::PropertyNumber RotatorBacklashNP
INDI::PropertySwitch HomeRotatorSP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertySwitch ReverseRotatorSP
INDI::PropertyNumber GotoRotatorNP
Provides interface to implement Rotator functionality.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
#define FOCUS_SETTINGS_TAB
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
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 IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
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 IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void 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.
#define LOGF_INFO(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 DEBUGF(priority, msg,...)
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.