31 #define STEELDRIVE_MAX_RETRIES 1
32 #define STEELDRIVE_TIMEOUT 1
33 #define STEELDRIVE_MAXBUF 16
34 #define STEELDRIVE_CMD 9
35 #define STEELDRIVE_CMD_LONG 11
36 #define STEELDRIVE_TEMPERATURE_FREQ 20
37 #define STEELDIVE_POSITION_THRESHOLD 5
39 #define FOCUS_SETTINGS_TAB "Settings"
59 IUFillNumber(&TemperatureN[0],
"TEMPERATURE",
"Celsius",
"%6.2f", -50, 70., 0., 0.);
91 IUFillSwitchVector(&ModelSP, ModelS, 5,
getDeviceName(),
"Model",
"",
FOCUS_SETTINGS_TAB,
IP_RW,
ISR_1OFMANY, 0,
101 IUFillNumber(&AccelerationN[0],
"Ramp",
"",
"%3.0f", 1500., 3000., 100., 2000.);
106 IUFillNumber(&SyncN[0],
"Position (steps)",
"",
"%3.0f", 0., 200000., 100., 0.);
110 IUFillText(&VersionT[0],
"HW Rev.",
"",
nullptr);
111 IUFillText(&VersionT[1],
"FW Rev.",
"",
nullptr);
119 updateFocusMaxRange(fSettings[4].maxTrip, fSettings[4].gearRatio);
149 LOG_INFO(
"SteelDrive parameters updated, focuser ready for use.");
175 LOG_INFO(
"SteelDrive is online. Getting focus parameters...");
176 temperatureUpdateCounter = 0;
180 LOG_INFO(
"Error retrieving data from SteelDrive, please ensure SteelDrive controller is "
181 "powered and the port is correct.");
187 return "Baader SteelDrive";
193 bool SteelDrive::Ack()
195 int nbytes_written = 0, nbytes_read = 0, rc = -1;
200 tcflush(
PortFD, TCIOFLUSH);
205 LOGF_ERROR(
":FVERSIO# getHWVersion error: %s.", errstr);
219 LOGF_ERROR(
"getHWVersion error: %s.", errstr);
223 resp[nbytes_read] =
'\0';
227 rc = sscanf(resp,
":FV%s#", hwVer);
235 bool SteelDrive::updateVersion()
237 int nbytes_written = 0, nbytes_read = 0, rc = -1;
248 memset(hwdate, 0,
sizeof(hwdate));
249 memset(hwrev, 0,
sizeof(hwrev));
250 memset(fwdate, 0,
sizeof(fwdate));
251 memset(fwrev, 0,
sizeof(fwrev));
253 tcflush(
PortFD, TCIOFLUSH);
258 LOGF_ERROR(
":FVERSIO# getHWVersion write error: %s.", errstr);
272 LOGF_ERROR(
"FVERSIO# getHWVersion read error: %s.", errstr);
276 resp[nbytes_read] =
'\0';
280 rc = sscanf(resp,
":FV%s#", hardware_string);
284 strncpy(hwrev, hardware_string, 3);
285 strncpy(hwdate, hardware_string + 3, 4);
286 char mon[3], year[3];
287 memset(mon, 0,
sizeof(mon));
288 memset(year, 0,
sizeof(year));
289 strncpy(mon, hwdate, 2);
290 strncpy(year, hwdate + 2, 2);
291 snprintf(hardware_string,
MAXRBUF,
"Version: %s Date: %s.%s", hwrev, mon, year);
296 LOGF_ERROR(
"Unknown error: getHWVersion value (%s)", resp);
300 tcflush(
PortFD, TCIOFLUSH);
305 LOGF_ERROR(
":FNFIRMW# getSWVersion write error: %s.", errstr);
319 LOGF_ERROR(
"FNFIRMW# getSWVersion read error: %s.", errstr);
323 resp[nbytes_read] =
'\0';
327 rc = sscanf(resp,
":FN%s#", firmware_string);
331 strncpy(fwrev, firmware_string, 3);
332 strncpy(fwdate, firmware_string + 3, 4);
333 char mon[3], year[3];
334 memset(mon, 0,
sizeof(mon));
335 memset(year, 0,
sizeof(year));
336 strncpy(mon, fwdate, 2);
337 strncpy(year, fwdate + 2, 2);
338 snprintf(firmware_string,
MAXRBUF,
"Version: %s Date: %s.%s", fwrev, mon, year);
343 LOGF_ERROR(
"Unknown error: getSWVersion value (%s)", resp);
353 bool SteelDrive::updateTemperature()
355 int nbytes_written = 0, nbytes_read = 0, rc = -1;
360 tcflush(
PortFD, TCIOFLUSH);
365 LOGF_ERROR(
":F5ASKT0# updateTemperature write error: %s.", errstr);
379 LOGF_ERROR(
":F5ASKT0# updateTemperature read error: %s.", errstr);
383 resp[nbytes_read] =
'\0';
387 rc = sscanf(resp,
":F5%d#", &temperature);
391 TemperatureN[0].value = ((double)temperature) / 100.0;
397 rc = sscanf(resp,
":F5%s#", junk);
400 TemperatureN[0].value = 0;
401 LOG_DEBUG(
"Temperature probe is not connected.");
404 LOGF_ERROR(
"Unknown error: focuser temperature value (%s)", resp);
416 bool SteelDrive::updatePosition()
418 int nbytes_written = 0, nbytes_read = 0, rc = -1;
421 unsigned short pos = 0;
426 tcflush(
PortFD, TCIOFLUSH);
431 LOGF_ERROR(
":F8ASKS0# updatePosition write error: %s.", errstr);
446 resp[nbytes_read] =
'\0';
447 LOGF_DEBUG(
":F8ASKS0# updatePosition read error: %s. Retry: %d. Bytes: %d. Buffer (%s)", errstr, retries,
456 LOG_ERROR(
"UpdatePosition: failed to read.");
460 resp[nbytes_read] =
'\0';
464 rc = sscanf(resp,
":F8%hu#", &pos);
472 LOGF_ERROR(
"Unknown error: focuser position value (%s)", resp);
482 bool SteelDrive::updateSpeed()
484 int nbytes_written = 0, nbytes_read = 0, rc = -1;
487 unsigned short speed;
489 tcflush(
PortFD, TCIOFLUSH);
494 LOGF_ERROR(
":FGSPMAX# updateSpeed write error: %s.", errstr);
508 LOGF_ERROR(
":FGSPMAX# updateSpeed read error: %s.", errstr);
512 resp[nbytes_read] =
'\0';
516 rc = sscanf(resp,
":FG%hu#", &speed);
524 LOGF_ERROR(
"Unknown error: focuser speed value (%s)", resp);
534 bool SteelDrive::updateAcceleration()
536 int nbytes_written = 0, nbytes_read = 0, rc = -1;
539 unsigned short accel;
541 tcflush(
PortFD, TCIOFLUSH);
546 LOGF_ERROR(
":FHSPMIN# updateAcceleration write error: %s.", errstr);
560 LOGF_ERROR(
":FHSPMIN# updateAcceleration read error: %s.", errstr);
564 resp[nbytes_read] =
'\0';
568 rc = sscanf(resp,
":FH%hu#", &accel);
572 AccelerationN[0].value = accel;
576 LOGF_ERROR(
"Unknown error: updateAcceleration value (%s)", resp);
585 bool SteelDrive::updateTemperatureSettings()
587 int nbytes_written = 0, nbytes_read = 0, rc = -1;
593 tcflush(
PortFD, TCIOFLUSH);
598 LOGF_ERROR(
":F7ASKC0# updateTemperatureSettings write error: %s.", errstr);
612 LOGF_ERROR(
":F7ASKC0# updateTemperatureSettings read error: %s.", errstr);
616 resp[nbytes_read] =
'\0';
620 rc = sscanf(resp,
":F7%s#", tResp);
624 strncpy(coeff, tResp, 3);
625 strncpy(enabled, tResp + 3, 1);
626 strncpy(selectedFocuser, tResp + 4, 1);
628 TemperatureSettingN[
FOCUS_T_COEFF].value = atof(coeff) / 1000.0;
631 if (enabled[0] ==
'0')
632 TemperatureCompensateS[1].s =
ISS_ON;
634 TemperatureCompensateS[0].s =
ISS_ON;
638 LOGF_ERROR(
"Unknown error: updateTemperatureSettings value (%s)", resp);
648 bool SteelDrive::updateCustomSettings()
650 int nbytes_written = 0, nbytes_read = 0, rc = -1;
658 tcflush(
PortFD, TCIOFLUSH);
664 LOGF_ERROR(
":FEASKGR# updateCustomSettings write error: %s.", errstr);
678 LOGF_ERROR(
":FEASKGR# updateCustomSettings read error: %s.", errstr);
682 resp[nbytes_read] =
'\0';
686 rc = sscanf(resp,
":FE%d#", &gearR);
690 gearRatio = ((double)gearR) / 100000.0;
694 LOGF_ERROR(
"Unknown error: updateCustomSettings value (%s)", resp);
698 tcflush(
PortFD, TCIOFLUSH);
704 LOGF_ERROR(
":F8ASKS1# updateCustomSettings write error: %s.", errstr);
718 LOGF_ERROR(
":F8ASKS1# updateCustomSettings read error: %s.", errstr);
722 resp[nbytes_read] =
'\0';
726 rc = sscanf(resp,
":F%s#", tResp);
730 strncpy(selectedFocuser, tResp, 1);
731 strncpy(maxTrip, tResp + 1, 7);
733 int sFocuser = atoi(selectedFocuser);
736 ModelS[sFocuser].s =
ISS_ON;
738 fSettings[sFocuser].
maxTrip = (atof(maxTrip) * gearRatio) / 100.0;
739 fSettings[sFocuser].
gearRatio = gearRatio;
744 LOGF_DEBUG(
"Updated max trip: %g gear ratio: %g", fSettings[sFocuser].maxTrip,
745 fSettings[sFocuser].gearRatio);
749 LOGF_ERROR(
"Unknown error: updateCustomSettings value (%s)", resp);
759 bool SteelDrive::setTemperatureSamples(
unsigned int targetSamples,
unsigned int *finalSample)
761 int nbytes_written = 0, rc = -1;
768 for (
int i = maxSample; i > 0;)
770 if (targetSamples & maxSample)
782 else if (sample == 32)
784 else if (sample == 64)
791 tcflush(
PortFD, TCIOFLUSH);
798 LOGF_ERROR(
"%s setTemperatureSamples write error: %s.",
cmd, errstr);
803 *finalSample = sample;
811 bool SteelDrive::setTemperatureCompensation()
813 int nbytes_written = 0, rc = -1;
818 bool enable = TemperatureCompensateS[0].s ==
ISS_ON;
821 snprintf(
cmd,
STEELDRIVE_CMD + 1,
":F%02d%03d%d#", selectedFocus, (
int)(coeff * 1000), enable ? 2 : 0);
823 tcflush(
PortFD, TCIOFLUSH);
830 LOGF_ERROR(
"setTemperatureCoefficient error: %s.", errstr);
840 bool SteelDrive::setCustomSettings(
double maxTrip,
double gearRatio)
842 int nbytes_written = 0, rc = -1;
846 unsigned short mmTrip = (
unsigned short int)(maxTrip / gearRatio * 100.0);
850 tcflush(
PortFD, TCIOFLUSH);
857 LOGF_ERROR(
"setCustomSettings error: %s.", errstr);
863 tcflush(
PortFD, TCIOFLUSH);
870 LOGF_ERROR(
"setCustomSettings error: %s.", errstr);
880 bool SteelDrive::Sync(
unsigned int position)
882 int nbytes_written = 0, rc = -1;
888 tcflush(
PortFD, TCIOFLUSH);
899 simPosition = position;
907 bool SteelDrive::moveFocuser(
unsigned int position)
909 int nbytes_written = 0, rc = -1;
915 LOGF_ERROR(
"Requested position value out of bound: %d", position);
924 tcflush(
PortFD, TCIOFLUSH);
936 targetPos = position;
944 bool SteelDrive::startMotion(FocusDirection dir)
946 int nbytes_written = 0, rc = -1;
954 tcflush(
PortFD, TCIOFLUSH);
971 bool SteelDrive::setSpeed(
unsigned short speed)
973 int nbytes_written = 0, rc = -1;
979 tcflush(
PortFD, TCIOFLUSH);
990 currentSpeed = speed;
998 bool SteelDrive::setAcceleration(
unsigned short accel)
1000 int nbytes_written = 0, rc = -1;
1006 tcflush(
PortFD, TCIOFLUSH);
1013 LOGF_ERROR(
"setAcceleration error: %s.", errstr);
1027 if (strcmp(TemperatureCompensateSP.
name, name) == 0)
1032 bool rc = setTemperatureCompensation();
1038 TemperatureCompensateS[last_index].s =
ISS_ON;
1043 TemperatureCompensateSP.
s =
IPS_OK;
1048 if (strcmp(ModelSP.
name, name) == 0)
1054 double focusMaxPos = floor(fSettings[i].maxTrip / fSettings[i].gearRatio) * 100;
1081 if (strcmp(name, AccelerationNP.
name) == 0)
1083 if (setAcceleration((
int)values[0]))
1099 if (strcmp(name, TemperatureSettingNP.
name) == 0)
1102 unsigned int targetSamples;
1105 targetSamples = (int)values[0];
1107 targetSamples = (int)values[1];
1109 unsigned int finalSample = targetSamples;
1111 if (setTemperatureSamples(targetSamples, &finalSample))
1119 TemperatureSettingNP.
s =
IPS_OK;
1132 if (strcmp(name, CustomSettingNP.
name) == 0)
1140 LOG_WARN(
"You can not set custom values for a non-custom focuser.");
1145 double maxTrip, gearRatio;
1149 maxTrip = values[0];
1150 gearRatio = values[1];
1154 maxTrip = values[1];
1155 gearRatio = values[0];
1158 if (setCustomSettings(maxTrip, gearRatio))
1164 updateFocusMaxRange(maxTrip, gearRatio);
1176 if (strcmp(name, SyncNP.
name) == 0)
1178 if (Sync((
unsigned int)values[0]))
1184 if (updatePosition())
1205 void SteelDrive::GetFocusParams()
1207 if (updateVersion())
1210 if (updateTemperature())
1213 if (updateTemperatureSettings())
1216 if (updatePosition())
1222 if (updateAcceleration())
1225 if (updateCustomSettings())
1236 rc = setSpeed(speed);
1241 currentSpeed = speed;
1251 if (speed != (
int)currentSpeed)
1253 bool rc = setSpeed(speed);
1259 gettimeofday(&focusMoveStart,
nullptr);
1260 focusMoveRequest = duration / 1000.0;
1276 targetPos = targetTicks;
1280 rc = moveFocuser(targetPos);
1292 double newPosition = 0;
1300 rc = moveFocuser(newPosition);
1317 bool rc = updatePosition();
1329 temperatureUpdateCounter = 0;
1330 rc = updateTemperature();
1333 if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
1334 lastTemperature = TemperatureN[0].value;
1342 float remaining = CalcTimeLeft(focusMoveStart, focusMoveRequest);
1375 else if (remaining <= 0)
1396 if (fabs(simPosition - targetPos) < 100)
1411 LOG_INFO(
"Focuser reached requested position.");
1423 int nbytes_written = 0, rc = -1;
1426 tcflush(
PortFD, TCIOFLUSH);
1433 LOGF_ERROR(
":F3STOP0# Stop error: %s.", errstr);
1454 float SteelDrive::CalcTimeLeft(timeval start,
float req)
1462 gettimeofday(&now,
nullptr);
1465 (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
1466 timesince = timesince / 1000;
1467 timeleft = req - timesince;
1485 return saveFocuserConfig();
1491 bool SteelDrive::saveFocuserConfig()
1493 int nbytes_written = 0, rc = -1;
1496 tcflush(
PortFD, TCIOFLUSH);
1503 LOGF_ERROR(
":FFPOWER# saveFocuserConfig error: %s.", errstr);
1521 void SteelDrive::updateFocusMaxRange(
double maxTrip,
double gearRatio)
1523 double maxSteps = floor(maxTrip / gearRatio * 100.0);
const char * getDeviceName() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty FocusSpeedNP
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
INumberVectorProperty FocusTimerNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
@ FOCUSER_HAS_VARIABLE_SPEED
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.
void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
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 ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
const char * getDefaultName() override
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual IPState MoveRelFocuser(FocusDirection dir, unsigned int ticks) override
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
virtual bool Handshake() override
perform handshake with device to check communication
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
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 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 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 IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void 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_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,...)
@ value
the parser finished reading a JSON value
#define STEELDRIVE_TIMEOUT
std::unique_ptr< SteelDrive > steelDrive(new SteelDrive())
#define STEELDRIVE_MAX_RETRIES
#define STEELDRIVE_MAXBUF
#define FOCUS_SETTINGS_TAB
#define STEELDIVE_POSITION_THRESHOLD
#define STEELDRIVE_TEMPERATURE_FREQ
#define STEELDRIVE_CMD_LONG