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"
42 static std::unique_ptr<Gemini> geminiFR(
new Gemini());
50 focuserSimPosition = 0;
58 isFocuserAbsolute =
true;
59 isFocuserHoming =
false;
91 IUFillNumber(&TemperatureN[0],
"TEMPERATURE",
"Celsius",
"%6.2f", -50, 70., 0., 0.);
108 IUFillNumber(&TemperatureCoeffN[0],
"A",
"",
"%.f", -9999, 9999, 100., 0.);
109 IUFillNumber(&TemperatureCoeffN[1],
"B",
"",
"%.f", -9999, 9999, 100., 0.);
110 IUFillNumber(&TemperatureCoeffN[2],
"C",
"",
"%.f", -9999, 9999, 100., 0.);
111 IUFillNumber(&TemperatureCoeffN[3],
"D",
"",
"%.f", -9999, 9999, 100., 0.);
112 IUFillNumber(&TemperatureCoeffN[4],
"E",
"",
"%.f", -9999, 9999, 100., 0.);
184 IUFillNumber(&RotatorAbsPosN[0],
"ROTATOR_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0., 0., 0., 0.);
191 IUFillNumber(&RotatorAbsAngleN[0],
"ANGLE",
"Degrees",
"%.2f", 0, 360., 10., 0.);
219 IUFillNumber(&LedN[0],
"Intensity",
"",
"%.f", 0, 100, 5., 0.);
224 IUFillSwitchVector(&ResetSP, ResetS, 1,
getDeviceName(),
"Reset",
"",
HUB_TAB,
IP_RW,
ISR_ATMOST1, 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";
353 if (strcmp(TemperatureCompensateSP.
name, name) == 0)
357 if (setTemperatureCompensation(TemperatureCompensateS[0].s ==
ISS_ON))
359 TemperatureCompensateSP.
s =
IPS_OK;
365 TemperatureCompensateS[prevIndex].s =
ISS_ON;
373 if (!strcmp(TemperatureCompensateOnStartSP.
name, name))
376 IUUpdateSwitch(&TemperatureCompensateOnStartSP, states, names, n);
377 if (setTemperatureCompensationOnStart(TemperatureCompensateOnStartS[0].s ==
ISS_ON))
379 TemperatureCompensateOnStartSP.
s =
IPS_OK;
385 TemperatureCompensateOnStartS[prevIndex].s =
ISS_ON;
388 IDSetSwitch(&TemperatureCompensateOnStartSP,
nullptr);
393 if (!strcmp(TemperatureCompensateModeSP.
name, name))
398 if (setTemperatureCompensationMode(mode))
400 TemperatureCompensateModeSP.
s =
IPS_OK;
406 TemperatureCompensateModeS[prevIndex].s =
ISS_ON;
409 IDSetSwitch(&TemperatureCompensateModeSP,
nullptr);
414 if (!strcmp(FocuserHomeOnStartSP.
name, name))
420 FocuserHomeOnStartSP.
s =
IPS_OK;
426 FocuserHomeOnStartS[prevIndex].s =
ISS_ON;
434 if (!strcmp(RotatorHomeOnStartSP.
name, name))
440 RotatorHomeOnStartSP.
s =
IPS_OK;
446 RotatorHomeOnStartS[prevIndex].s =
ISS_ON;
474 if (strcmp(ResetSP.
name, name) == 0)
487 if (!strcmp(FocuserGotoSP.
name, name))
498 isFocuserHoming =
true;
499 LOG_INFO(
"Focuser moving to home position...");
509 LOG_INFO(
"Focuser moving to center position...");
522 if (strstr(name,
"ROTATOR"))
530 if (!strcmp(RotatorGotoSP.name, name))
541 isRotatorHoming =
true;
542 LOG_INFO(
"Rotator moving to home position...");
552 LOG_INFO(
"Rotator moving to center position...");
583 RotatorAbsPosNP.
s = RotatorAbsAngleNP.s = RotatorGotoSP.s =
IPS_IDLE;
611 if (!strcmp(name, HFocusNameTP.
name))
634 if (!strcmp(TemperatureCoeffNP.
name, name))
637 for (
int i = 0; i < n; i++)
639 if (setTemperatureCompensationCoeff(
'A' + i, TemperatureCoeffN[i].value) ==
false)
641 LOG_ERROR(
"Failed to set temperature coefficients.");
676 LOG_ERROR(
"Failed to set rotator backlash value.");
688 if (!strcmp(LedNP.
name, name))
691 if (setLedLevel(LedN[0].value))
695 LOGF_INFO(
"Focuser LED level intensity : %f", LedN[0].value);
701 if (!strcmp(RotatorAbsPosNP.
name, name))
704 RotatorAbsPosNP.
s = MoveAbsRotatorTicks(
static_cast<uint32_t
>(RotatorAbsPosN[0].value));
709 if (strstr(name,
"ROTATOR"))
717 if (!strcmp(RotatorAbsPosNP.
name, name))
720 RotatorAbsPosNP.
s = MoveAbsRotatorTicks(
static_cast<uint32_t
>(RotatorAbsPosN[0].value));
726 if (!strcmp(RotatorAbsAngleNP.name, name))
729 RotatorAbsAngleNP.s = MoveAbsRotatorAngle(RotatorAbsAngleN[0].value);
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);
802 bool 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);
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", TemperatureCompensateS[0].s ==
ISS_ON ? 1 : 0);
942 nbytes_read = strlen(response);
950 response[nbytes_read - 1] =
'\0';
954 rc = sscanf(response,
"%15[^=]=%d", key, &TCompOn);
961 TemperatureCompensateSP.
s =
IPS_OK;
964 memset(response, 0,
sizeof(response));
969 snprintf(response,
sizeof(response),
"TempCo A = %d\n", (
int)TemperatureCoeffN[
FOCUS_A_COEFF].value);
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)TemperatureCoeffN[
FOCUS_B_COEFF].value);
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)TemperatureCoeffN[
FOCUS_C_COEFF].value);
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)TemperatureCoeffN[
FOCUS_D_COEFF].value);
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)TemperatureCoeffN[
FOCUS_E_COEFF].value);
1066 nbytes_read = strlen(response);
1074 response[nbytes_read - 1] =
'\0';
1078 rc = sscanf(response,
"%15[^=]=%d", key, &TCoeffE);
1084 TemperatureCoeffNP.
s =
IPS_OK;
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);
1110 int index = compensateMode -
'A';
1111 if (index >= 0 && index <= 5)
1113 TemperatureCompensateModeS[index].s =
ISS_ON;
1114 TemperatureCompensateModeSP.
s =
IPS_OK;
1118 LOGF_ERROR(
"Invalid index %d for compensation mode.", index);
1122 IDSetSwitch(&TemperatureCompensateModeSP,
nullptr);
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", TemperatureCompensateOnStartS[0].s ==
ISS_ON ? 1 : 0);
1181 nbytes_read = strlen(response);
1189 response[nbytes_read - 1] =
'\0';
1193 rc = sscanf(response,
"%15[^=]=%d", key, &TCOnStart);
1198 TemperatureCompensateOnStartS[0].s = TCOnStart ?
ISS_ON :
ISS_OFF;
1199 TemperatureCompensateOnStartS[1].s = TCOnStart ?
ISS_OFF :
ISS_ON;
1200 TemperatureCompensateOnStartSP.
s =
IPS_OK;
1201 IDSetSwitch(&TemperatureCompensateOnStartSP,
nullptr);
1204 memset(response, 0,
sizeof(response));
1209 snprintf(response,
sizeof(response),
"HOnStart = %d\n", FocuserHomeOnStartS[0].s ==
ISS_ON ? 1 : 0);
1210 nbytes_read = strlen(response);
1218 response[nbytes_read - 1] =
'\0';
1222 rc = sscanf(response,
"%15[^=]=%d", key, &StartOnHome);
1229 FocuserHomeOnStartSP.
s =
IPS_OK;
1234 memset(response, 0,
sizeof(response));
1237 strncpy(response,
"END\n", 16);
1238 nbytes_read = strlen(response);
1247 if (nbytes_read > 0)
1249 response[nbytes_read - 1] =
'\0';
1254 if (strcmp(response,
"END"))
1259 tcflush(
PortFD, TCIFLUSH);
1269 bool Gemini::getRotatorStatus()
1271 const char *
cmd =
"<R100GETSTA>";
1275 int nbytes_read = 0;
1276 int nbytes_written = 0;
1279 memset(response, 0,
sizeof(response));
1285 strncpy(response,
"!00", 16);
1286 nbytes_read = strlen(response) + 1;
1290 tcflush(
PortFD, TCIFLUSH);
1313 memset(response, 0,
sizeof(response));
1316 snprintf(response, 32,
"CurrStep = %06d\n", rotatorSimPosition);
1317 nbytes_read = strlen(response);
1325 response[nbytes_read - 1] =
'\0';
1326 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1329 int rc = sscanf(response,
"%15[^=]=%d", key, &currPos);
1333 if (RotatorAbsPosN[0].value != currPos)
1335 RotatorAbsPosN[0].value = currPos;
1345 memset(response, 0,
sizeof(response));
1348 snprintf(response, 32,
"TargStep = %06d\n", targetFocuserPosition);
1349 nbytes_read = strlen(response);
1357 response[nbytes_read - 1] =
'\0';
1358 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1363 memset(response, 0,
sizeof(response));
1366 snprintf(response, 32,
"CurenPA = %06d\n", rotatorSimPA);
1367 nbytes_read = strlen(response);
1375 response[nbytes_read - 1] =
'\0';
1376 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1379 rc = sscanf(response,
"%15[^=]=%d", key, &currPA);
1383 double diffPA = fabs(
GotoRotatorN[0].value - currPA / 1000.0);
1396 memset(response, 0,
sizeof(response));
1399 snprintf(response, 32,
"TargetPA = %06d\n", targetFocuserPosition);
1400 nbytes_read = strlen(response);
1408 response[nbytes_read - 1] =
'\0';
1409 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1416 memset(response, 0,
sizeof(response));
1419 snprintf(response, 32,
"IsMoving = %d\n", (rotatorSimStatus[
STATUS_MOVING] ==
ISS_ON) ? 1 : 0);
1420 nbytes_read = strlen(response);
1428 response[nbytes_read - 1] =
'\0';
1429 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1432 rc = sscanf(response,
"%15[^=]=%d", key, &isMoving);
1441 memset(response, 0,
sizeof(response));
1444 snprintf(response, 32,
"IsHoming = %d\n", (rotatorSimStatus[
STATUS_HOMING] ==
ISS_ON) ? 1 : 0);
1445 nbytes_read = strlen(response);
1453 response[nbytes_read - 1] =
'\0';
1454 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1457 rc = sscanf(response,
"%15[^=]=%d", key, &_isHoming);
1465 isRotatorHoming =
true;
1470 memset(response, 0,
sizeof(response));
1473 snprintf(response, 32,
"IsHomed = %d\n", (rotatorSimStatus[
STATUS_HOMED] ==
ISS_ON) ? 1 : 0);
1474 nbytes_read = strlen(response);
1482 response[nbytes_read - 1] =
'\0';
1483 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1486 rc = sscanf(response,
"%15[^=]=%d", key, &isHomed);
1495 memset(response, 0,
sizeof(response));
1498 strncpy(response,
"END\n", 16);
1499 nbytes_read = strlen(response);
1508 if (nbytes_read > 0)
1510 response[nbytes_read - 1] =
'\0';
1515 if (strcmp(response,
"END"))
1523 tcflush(
PortFD, TCIFLUSH);
1532 bool Gemini::getRotatorConfig()
1534 const char *
cmd =
"<R100GETCFG>";
1538 int nbytes_read = 0;
1539 int nbytes_written = 0;
1542 memset(response, 0,
sizeof(response));
1548 strncpy(response,
"!00",
sizeof(response));
1549 nbytes_read = strlen(response) + 1;
1553 tcflush(
PortFD, TCIFLUSH);
1582 memset(response, 0,
sizeof(response));
1588 strncpy(response,
"NickName=Juli\n",
sizeof(response));
1589 nbytes_read = strlen(response);
1597 response[nbytes_read - 1] =
'\0';
1601 int rc = sscanf(response,
"%15[^=]=%15[^\n]s", key, nickname);
1610 memset(response, 0,
sizeof(response));
1617 snprintf(response,
sizeof(response),
"MaxSteps = %06d\n", 100000);
1618 nbytes_read = strlen(response);
1626 response[nbytes_read - 1] =
'\0';
1629 uint32_t maxPos = 0;
1630 rc = sscanf(response,
"%15[^=]=%d", key, &maxPos);
1633 RotatorAbsPosN[0].min = 0;
1634 RotatorAbsPosN[0].max = maxPos;
1635 RotatorAbsPosN[0].step = maxPos / 50.0;
1641 memset(response, 0,
sizeof(response));
1648 strncpy(response,
"Dev Type = B\n",
sizeof(response));
1649 nbytes_read = strlen(response);
1657 response[nbytes_read - 1] =
'\0';
1661 memset(response, 0,
sizeof(response));
1666 memset(response, 0,
sizeof(response));
1670 nbytes_read = strlen(response);
1678 response[nbytes_read - 1] =
'\0';
1682 rc = sscanf(response,
"%15[^=]=%d", key, &BLCCompensate);
1695 memset(response, 0,
sizeof(response));
1698 snprintf(response,
sizeof(response),
"BLCSteps = %d\n", 50);
1699 nbytes_read = strlen(response);
1707 response[nbytes_read - 1] =
'\0';
1711 rc = sscanf(response,
"%15[^=]=%d", key, &BLCValue);
1722 memset(response, 0,
sizeof(response));
1725 snprintf(response,
sizeof(response),
"HOnStart = %d\n", RotatorHomeOnStartS[0].s ==
ISS_ON ? 1 : 0);
1726 nbytes_read = strlen(response);
1734 response[nbytes_read - 1] =
'\0';
1738 rc = sscanf(response,
"%15[^=]=%d", key, &StartOnHome);
1745 RotatorHomeOnStartSP.
s =
IPS_OK;
1751 memset(response, 0,
sizeof(response));
1754 snprintf(response, 32,
"Reverse = %d\n", (rotatorSimStatus[
STATUS_REVERSE] ==
ISS_ON) ? 1 : 0);
1755 nbytes_read = strlen(response);
1763 response[nbytes_read - 1] =
'\0';
1764 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1767 rc = sscanf(response,
"%15[^=]=%d", key, &reverse);
1789 memset(response, 0,
sizeof(response));
1792 snprintf(response, 32,
"MaxSpeed = %d\n", 800);
1793 nbytes_read = strlen(response);
1801 response[nbytes_read - 1] =
'\0';
1802 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1806 memset(response, 0,
sizeof(response));
1809 strncpy(response,
"END\n", 16);
1810 nbytes_read = strlen(response);
1819 if (nbytes_read > 0)
1821 response[nbytes_read - 1] =
'\0';
1826 if (strcmp(response,
"END"))
1831 tcflush(
PortFD, TCIFLUSH);
1841 bool Gemini::getFocusStatus()
1843 const char *
cmd =
"<F100GETSTA>";
1847 int nbytes_read = 0;
1848 int nbytes_written = 0;
1851 memset(response, 0,
sizeof(response));
1857 strncpy(response,
"!00", 16);
1858 nbytes_read = strlen(response) + 1;
1862 tcflush(
PortFD, TCIFLUSH);
1883 memset(response, 0,
sizeof(response));
1887 strcpy(response,
"CurrTemp = +21.7\n");
1888 nbytes_read = strlen(response);
1897 if (nbytes_read > 0)
1898 response[nbytes_read - 1] =
'\0';
1900 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1902 float temperature = 0;
1903 int rc = sscanf(response,
"%15[^=]=%f", key, &temperature);
1906 TemperatureN[0].value = temperature;
1912 int rc = sscanf(response,
"%15[^=]= %s", key, np);
1914 if (rc != 2 || strcmp(np,
"NP"))
1928 memset(response, 0,
sizeof(response));
1931 snprintf(response, 32,
"CurrStep = %06d\n", focuserSimPosition);
1932 nbytes_read = strlen(response);
1940 response[nbytes_read - 1] =
'\0';
1941 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1943 uint32_t currPos = 0;
1944 rc = sscanf(response,
"%15[^=]=%d", key, &currPos);
1956 memset(response, 0,
sizeof(response));
1959 snprintf(response, 32,
"TargStep = %06d\n", targetFocuserPosition);
1960 nbytes_read = strlen(response);
1968 response[nbytes_read - 1] =
'\0';
1969 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1976 memset(response, 0,
sizeof(response));
1979 snprintf(response, 32,
"IsMoving = %d\n", (focuserSimStatus[
STATUS_MOVING] ==
ISS_ON) ? 1 : 0);
1980 nbytes_read = strlen(response);
1988 response[nbytes_read - 1] =
'\0';
1989 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
1992 rc = sscanf(response,
"%15[^=]=%d", key, &isMoving);
2001 memset(response, 0,
sizeof(response));
2004 snprintf(response, 32,
"IsHoming = %d\n", (focuserSimStatus[
STATUS_HOMING] ==
ISS_ON) ? 1 : 0);
2005 nbytes_read = strlen(response);
2013 response[nbytes_read - 1] =
'\0';
2014 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2017 rc = sscanf(response,
"%15[^=]=%d", key, &_isHoming);
2023 if (isFocuserAbsolute ==
false)
2028 isFocuserHoming =
true;
2033 memset(response, 0,
sizeof(response));
2036 snprintf(response, 32,
"IsHomed = %d\n", (focuserSimStatus[
STATUS_HOMED] ==
ISS_ON) ? 1 : 0);
2037 nbytes_read = strlen(response);
2045 response[nbytes_read - 1] =
'\0';
2046 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2049 rc = sscanf(response,
"%15[^=]=%d", key, &isHomed);
2055 if (isFocuserAbsolute ==
false)
2061 memset(response, 0,
sizeof(response));
2065 nbytes_read = strlen(response);
2073 response[nbytes_read - 1] =
'\0';
2074 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2077 rc = sscanf(response,
"%15[^=]=%d", key, &TmpProbe);
2086 memset(response, 0,
sizeof(response));
2090 nbytes_read = strlen(response);
2098 response[nbytes_read - 1] =
'\0';
2099 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2102 rc = sscanf(response,
"%15[^=]=%d", key, &RemoteIO);
2111 memset(response, 0,
sizeof(response));
2114 snprintf(response, 32,
"HCStatus = %d\n", (focuserSimStatus[
STATUS_HNDCTRL] ==
ISS_ON) ? 1 : 0);
2115 nbytes_read = strlen(response);
2123 response[nbytes_read - 1] =
'\0';
2124 DEBUGF(DBG_FOCUS,
"RES (%s)", response);
2127 rc = sscanf(response,
"%15[^=]=%d", key, &HndCtlr);
2138 memset(response, 0,
sizeof(response));
2141 strncpy(response,
"END\n", 16);
2142 nbytes_read = strlen(response);
2151 if (nbytes_read > 0)
2153 response[nbytes_read - 1] =
'\0';
2158 if (strcmp(response,
"END"))
2166 tcflush(
PortFD, TCIFLUSH);
2175 bool Gemini::setLedLevel(
int level)
2183 int nbytes_read = 0;
2184 int nbytes_written = 0;
2186 memset(response, 0,
sizeof(response));
2188 snprintf(
cmd, 16,
"<H100SETLED%d>", level);
2194 strncpy(response,
"SET", 16);
2195 nbytes_read = strlen(response) + 1;
2199 tcflush(
PortFD, TCIFLUSH);
2219 if (nbytes_read > 0)
2221 response[nbytes_read - 1] =
'\0';
2223 tcflush(
PortFD, TCIFLUSH);
2225 if (!strcmp(response,
"SET"))
2237 bool Gemini::setNickname(DeviceType
type,
const char *nickname)
2244 int nbytes_read = 0;
2245 int nbytes_written = 0;
2247 memset(response, 0,
sizeof(response));
2255 tcflush(
PortFD, TCIFLUSH);
2271 tcflush(
PortFD, TCIFLUSH);
2278 bool Gemini::halt(DeviceType
type)
2284 int nbytes_read = 0;
2285 int nbytes_written = 0;
2287 memset(response, 0,
sizeof(response));
2299 isRotatorHoming =
false;
2304 tcflush(
PortFD, TCIFLUSH);
2320 isRotatorHoming =
false;
2322 tcflush(
PortFD, TCIFLUSH);
2330 bool Gemini::home(DeviceType
type)
2336 int nbytes_read = 0;
2337 int nbytes_written = 0;
2339 memset(response, 0,
sizeof(response));
2349 targetFocuserPosition = 0;
2354 targetRotatorPosition = 0;
2359 tcflush(
PortFD, TCIFLUSH);
2375 tcflush(
PortFD, TCIFLUSH);
2383 bool Gemini::homeOnStart(DeviceType
type,
bool enable)
2389 int nbytes_read = 0;
2390 int nbytes_written = 0;
2392 memset(response, 0,
sizeof(response));
2399 tcflush(
PortFD, TCIFLUSH);
2415 tcflush(
PortFD, TCIFLUSH);
2423 bool Gemini::center(DeviceType
type)
2426 return MoveAbsRotatorTicks(RotatorAbsPosN[0].
max / 2);
2428 const char *
cmd =
"<F100CENTER>";
2432 int nbytes_read = 0;
2433 int nbytes_written = 0;
2435 memset(response, 0,
sizeof(response));
2449 targetRotatorPosition = RotatorAbsPosN[0].max / 2;
2455 tcflush(
PortFD, TCIFLUSH);
2471 tcflush(
PortFD, TCIFLUSH);
2479 bool Gemini::setTemperatureCompensation(
bool enable)
2485 int nbytes_read = 0;
2486 int nbytes_written = 0;
2488 memset(response, 0,
sizeof(response));
2490 snprintf(
cmd, 16,
"<F100SETTCE%d>", enable ? 1 : 0);
2496 tcflush(
PortFD, TCIFLUSH);
2518 bool Gemini::setTemperatureCompensationMode(
char mode)
2524 int nbytes_read = 0;
2525 int nbytes_written = 0;
2527 memset(response, 0,
sizeof(response));
2529 snprintf(
cmd, 16,
"<F100SETTCM%c>", mode);
2535 tcflush(
PortFD, TCIFLUSH);
2557 bool Gemini::setTemperatureCompensationCoeff(
char mode, int16_t coeff)
2563 int nbytes_read = 0;
2564 int nbytes_written = 0;
2566 memset(response, 0,
sizeof(response));
2568 snprintf(
cmd, 32,
"<F100SETTCC%c%c%04d>", mode, coeff >= 0 ?
'+' :
'-', (
int)std::abs(coeff));
2574 tcflush(
PortFD, TCIFLUSH);
2596 bool Gemini::setTemperatureCompensationOnStart(
bool enable)
2602 int nbytes_read = 0;
2603 int nbytes_written = 0;
2605 memset(response, 0,
sizeof(response));
2607 snprintf(
cmd, 16,
"<F100SETTCS%d>", enable ? 1 : 0);
2611 tcflush(
PortFD, TCIFLUSH);
2651 bool Gemini::setBacklashCompensation(DeviceType
type,
bool enable)
2657 int nbytes_read = 0;
2658 int nbytes_written = 0;
2660 memset(response, 0,
sizeof(response));
2688 bool Gemini::setBacklashCompensationSteps(DeviceType
type, uint16_t steps)
2694 int nbytes_read = 0;
2695 int nbytes_written = 0;
2697 memset(response, 0,
sizeof(response));
2725 bool Gemini::reverseRotator(
bool enable)
2731 int nbytes_read = 0;
2732 int nbytes_written = 0;
2734 memset(response, 0,
sizeof(response));
2736 snprintf(
cmd, 16,
"<R100SETREV%d>", enable ? 1 : 0);
2762 bool Gemini::resetFactory()
2764 const char *
cmd =
"<H100RESETH>";
2768 int nbytes_read = 0;
2769 int nbytes_written = 0;
2771 memset(response, 0,
sizeof(response));
2777 strncpy(response,
"SET", 16);
2778 nbytes_read = strlen(response) + 1;
2782 tcflush(
PortFD, TCIFLUSH);
2802 if (nbytes_read > 0)
2804 response[nbytes_read - 1] =
'\0';
2806 tcflush(
PortFD, TCIFLUSH);
2808 if (!strcmp(response,
"SET"))
2829 int nbytes_read = 0;
2831 memset(response, 0,
sizeof(response));
2835 strcpy(response,
"!00");
2836 nbytes_read = strlen(response) + 1;
2848 if (nbytes_read > 0)
2850 response[nbytes_read - 1] =
'\0';
2853 if (!strcmp(response,
"!00"))
2857 memset(response, 0,
sizeof(response));
2858 while (strstr(response,
"END") ==
nullptr)
2866 response[nbytes_read - 1] =
'\0';
2867 LOGF_ERROR(
"Controller error: %s", response);
2885 int nbytes_written = 0;
2889 memset(response, 0,
sizeof(response));
2891 snprintf(
cmd, 16,
"<F100DOMOVE%c>", (dir ==
FOCUS_INWARD) ?
'0' :
'1');
2897 tcflush(
PortFD, TCIFLUSH);
2909 gettimeofday(&focusMoveStart,
nullptr);
2910 focusMoveRequest = duration / 1000.0;
2920 tcflush(
PortFD, TCIFLUSH);
2934 int nbytes_written = 0;
2936 targetFocuserPosition = targetTicks;
2938 memset(response, 0,
sizeof(response));
2940 snprintf(
cmd, 32,
"<F100MOVABS%06d>", targetTicks);
2946 tcflush(
PortFD, TCIFLUSH);
2961 tcflush(
PortFD, TCIFLUSH);
2971 uint32_t newPosition = 0;
2996 bool statusrc =
false;
2997 for (
int i = 0; i < 2; i++)
2999 statusrc = getFocusStatus();
3004 if (statusrc ==
false)
3006 LOG_WARN(
"Unable to read focuser status....");
3016 focuserSimPosition += 100;
3018 focuserSimPosition -= 100;
3022 if (std::abs((int64_t)focuserSimPosition - (int64_t)targetFocuserPosition) < 100)
3038 isFocuserHoming =
false;
3045 LOG_INFO(
"Focuser reached home position.");
3059 LOG_INFO(
"Focuser reached requested position.");
3070 for (
int i = 0; i < 2; i++)
3072 statusrc = getRotatorStatus();
3077 if (statusrc ==
false)
3079 LOG_WARN(
"Unable to read rotator status....");
3111 isRotatorHoming =
false;
3119 LOG_INFO(
"Rotator reached home position.");
3133 LOG_INFO(
"Rotator reached requested position.");
3150 const char *
cmd =
"<F100DOHALT>";
3154 int nbytes_written = 0;
3156 memset(response, 0,
sizeof(response));
3162 strncpy(response,
"!00", 16);
3168 tcflush(
PortFD, TCIFLUSH);
3195 tcflush(
PortFD, TCIFLUSH);
3203 float Gemini::calcTimeLeft(timeval start,
float req)
3211 gettimeofday(&now,
nullptr);
3214 (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
3215 timesince = timesince / 1000;
3216 timeleft = req - timesince;
3223 IPState Gemini::MoveAbsRotatorTicks(uint32_t targetTicks)
3229 int nbytes_written = 0;
3231 targetRotatorPosition = targetTicks;
3233 memset(response, 0,
sizeof(response));
3235 snprintf(
cmd, 32,
"<R100MOVABS%06d>", targetTicks);
3241 tcflush(
PortFD, TCIFLUSH);
3256 tcflush(
PortFD, TCIFLUSH);
3264 IPState Gemini::MoveAbsRotatorAngle(
double angle)
3268 int nbytes_written = 0;
3270 targetRotatorAngle = angle * 1000.0;
3272 memset(response, 0,
sizeof(response));
3274 snprintf(
cmd, 32,
"<R100MOVEPA%06ud>", targetRotatorAngle);
3282 tcflush(
PortFD, TCIFLUSH);
3297 tcflush(
PortFD, TCIFLUSH);
3327 IPState state = MoveAbsRotatorAngle(angle);
3328 RotatorAbsPosNP.
s = state;
3347 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
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
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
Process the client newSwitch command.
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
Process the client newNumber command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
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
Process the client newSwitch command.
virtual bool SetRotatorBacklash(int32_t steps) override
SetRotatorBacklash Set the Rotatorer backlash compensation value.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual const char * getDefaultName() override
const char * getDeviceName() const
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.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
uint16_t getDriverInterface() const
INumberVectorProperty FocusBacklashNP
ISwitchVectorProperty FocusBacklashSP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INumber FocusBacklashN[1]
ISwitch FocusBacklashS[2]
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Connection::Serial * serialConnection
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
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
INumberVectorProperty RotatorBacklashNP
INumberVectorProperty GotoRotatorNP
void initProperties(const char *groupName)
Initilize Rotator properties. It is recommended to call this function within initProperties() of your...
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.
INumber RotatorBacklashN[1]
ISwitchVectorProperty HomeRotatorSP
ISwitch RotatorBacklashS[2]
ISwitchVectorProperty ReverseRotatorSP
ISwitch ReverseRotatorS[2]
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
ISwitchVectorProperty RotatorBacklashSP
ISwitchVectorProperty AbortRotatorSP
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 IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light 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.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetLight(const ILightVectorProperty *lvp, const char *fmt,...)
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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,...)
@ key
the parser read a key of a value in an object
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.