30 #define INTEGRA_TIMEOUT_IN_S 5
31 #define INTEGRA_TEMPERATURE_LOOP_SKIPS 60
32 #define INTEGRA_TEMPERATURE_TRESHOLD_IN_C 0.1
33 #define INTEGRA_ROUNDING_FUDGE 0.001
35 #define ROTATOR_TAB "Rotator"
36 #define SETTINGS_TAB "Settings"
48 {
"@SW%d,0\r\n", {
"S",
"SW"}},
49 {
"@CS%d,0\r\n", {
"C",
"CS"}},
50 {
"@CE%d,0\r\n", {
"CE",
"CE"}},
51 {
"@CR%d,0\r\n", {
"CR",
"CR"}},
52 {
"@TR\r\n", {
"T",
"TR"}},
53 {
"@PW%d,0\r\n", {
"P",
"PW"}},
54 {
"@PR%d,0\r\n", {
"P",
"PR"}},
55 {
"@MI%d,%d\r\n", {
"M",
"MI"}},
56 {
"@MO%d,%d\r\n", {
"M",
"MO"}},
57 {
"@RR%d,0\r\n", {
"R",
"RR"}},
58 {
"X\r\n", {
"",
"X"}},
59 {
"@IW%d,0\r\n", {
"I",
"IW"}},
60 {
"@ZW\r\n", {
"",
"ZW"}}
93 IUFillNumber(&MaxPositionN[0],
"FOCUSER",
"Focuser",
"%.f",
95 IUFillNumber(&MaxPositionN[1],
"ROTATOR",
"Rotator",
"%.f",
105 IUFillNumber(&SensorN[SENSOR_TEMPERATURE],
"TEMPERATURE",
"Temperature (C)",
"%.2f", -100, 100., 1., 0.);
129 IUFillNumber(&RotatorAbsPosN[0],
"ROTATOR_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0., 61802., 1., 0.);
132 RotatorAbsPosN[0].min = 0;
181 bool rcFirmware = getFirmware();
182 bool rcMaxPositionMotorFocus = getMaxPosition(
MOTOR_FOCUS);
183 bool rcMaxPositionMotorRotator = getMaxPosition(
MOTOR_ROTATOR);
184 bool rcType = getFocuserType();
185 if (rcFirmware && rcMaxPositionMotorFocus && rcMaxPositionMotorRotator && rcType)
190 LOG_ERROR(
"Error retrieving data from Integra, please ensure Integra controller is powered, port choice is correct and baud rate is 115200.");
199 void Integra::cleanPrint(
const char *
cmd,
char *cleancmd)
201 size_t len = strlen(
cmd);
203 for (
size_t i = 0; i <= len; i++)
207 cleancmd[j++] =
'\\';
210 else if (
cmd[i] == 0xD)
212 cleancmd[j++] =
'\\';
217 cleancmd[j++] =
cmd[i];
223 bool Integra::getFirmware()
232 if ( genericIntegraCommand(__FUNCTION__,
"@RR1,0\r\n",
"RR",
nullptr))
234 LOGF_INFO(
"Firmware version is %s",
"2017-12-20");
237 else if ( genericIntegraCommand(__FUNCTION__,
"@RR1,0\r\n",
"R",
nullptr))
239 LOGF_INFO(
"Firmware version is %s, note: there is a firmware upgrade available.",
"2017-01-25");
244 LOG_ERROR(
"Cannot determine firmware version, there may be a firmware upgrade available.");
252 bool Integra::getFocuserType()
255 int rotator_max = int(RotatorAbsPosN[0].
max);
258 LOGF_ERROR(
"This is no Integra85 because focus max position %d != %d, trying to continue still", focus_max,
264 LOGF_ERROR(
"This is no Integra85 because rotator max position %d != %d, trying to continue still", rotator_max,
269 char resp[64] =
"Integra85";
271 if (strcmp(resp,
"Integra85") == 0)
274 rotatorTicksPerDegree = RotatorAbsPosN[0].max / 360.0;
275 rotatorDegreesPerTick = 360.0 / RotatorAbsPosN[0].max;
281 bool Integra::relativeGotoMotor(MotorType
type, int32_t relativePosition)
283 int motorMoveCommand;
285 LOGF_DEBUG(
"Start relativeGotoMotor to %d ...", relativePosition);
286 if (relativePosition > 0)
293 if (relativePosition > 0)
295 if (lastFocuserPosition + relativePosition > MaxPositionN[
MOTOR_FOCUS].value)
297 int newRelativePosition = (int32_t)floor(MaxPositionN[
MOTOR_FOCUS].value) - lastFocuserPosition;
298 LOGF_INFO(
"Focus position change %d clipped to %d to stay at MAX %d",
299 relativePosition, newRelativePosition, MaxPositionN[
MOTOR_FOCUS].value);
300 relativePosition = newRelativePosition;
305 if ((int32_t )lastFocuserPosition + relativePosition < 0)
307 int newRelativePosition = -lastFocuserPosition;
308 LOGF_INFO(
"Focus position change %d clipped to %d to stay at MIN 0",
309 relativePosition, newRelativePosition);
310 relativePosition = newRelativePosition;
316 if (relativePosition > 0)
318 if (lastRotatorPosition + relativePosition > MaxPositionN[
MOTOR_ROTATOR].value)
320 int newRelativePosition = (int32_t)floor(MaxPositionN[
MOTOR_ROTATOR].value) - lastRotatorPosition;
321 LOGF_INFO(
"Rotator position change %d clipped to %d to stay at MAX %d",
322 relativePosition, newRelativePosition, MaxPositionN[
MOTOR_ROTATOR].value);
323 relativePosition = newRelativePosition;
328 if (lastRotatorPosition + relativePosition < - MaxPositionN[
MOTOR_ROTATOR].value)
330 int newRelativePosition = - (int32_t)floor(MaxPositionN[
MOTOR_ROTATOR].value) - lastRotatorPosition;
331 LOGF_INFO(
"Rotator position change %d clipped to %d to stay at MIN %d",
332 relativePosition, newRelativePosition, - MaxPositionN[
MOTOR_ROTATOR].value);
333 relativePosition = newRelativePosition;
338 return integraMotorSetCommand( __FUNCTION__, motorMoveCommand,
type, abs(relativePosition),
nullptr);
341 bool Integra::gotoMotor(MotorType
type, int32_t position)
343 LOGF_DEBUG(
"Start gotoMotor to %d", position);
346 return relativeGotoMotor(
type, position - lastFocuserPosition);
350 return relativeGotoMotor(
type, position - lastRotatorPosition);
354 LOGF_ERROR(
"%s error: MotorType %d is unknown.", __FUNCTION__,
type);
359 bool Integra::getPosition(MotorType
type)
367 position = atoi(res);
368 if (position != -1e6)
375 int position_to = position;
376 if (haveReadFocusPositionAtLeastOnce)
378 LOGF_DEBUG(
"Focus position changed from %d to %d", position_from, position_to);
382 LOGF_DEBUG(
"Focus position is %d", position_to);
389 if (RotatorAbsPosN[0].value != position)
391 auto position_from = (int) RotatorAbsPosN[0].value;
392 int position_to = position;
393 if (haveReadRotatorPositionAtLeastOnce)
395 LOGF_DEBUG(
"Rotator changed angle from %.2f to %.2f, position from %d to %d",
396 rotatorTicksToDegrees(position_from), rotatorTicksToDegrees(position_to), position_from, position_to);
400 LOGF_DEBUG(
"Rotator angle is %.2f, position is %d",
401 rotatorTicksToDegrees(position_to), position_to);
403 RotatorAbsPosN[0].value = position;
408 LOGF_ERROR(
"%s error: motor type %d is unknown", __FUNCTION__,
type);
422 if (strcmp(name, FindHomeSP.
name) == 0)
439 "Homing process can take up to 2 minutes. You cannot control the unit until the process is fully complete.");
445 LOG_ERROR(
"Failed to start homing process.");
459 LOG_ERROR(
"Failed to abort homing process.");
464 IDSetSwitch(&FindHomeSP,
"Unknown homing index %d", index);
470 else if (strstr(name,
"ROTATOR"))
484 if (strcmp(name, RotatorAbsPosNP.
name) == 0)
489 LOGF_DEBUG(
"Rotator moving from %d to %.f ticks...", lastRotatorPosition, values[0]);
492 else if (strstr(name,
"ROTATOR"))
504 targetPosition = targetTicks;
505 LOGF_DEBUG(
"Focuser will move absolute from %d to %d ...", lastFocuserPosition, targetTicks);
519 double newPosition = 0;
521 LOGF_DEBUG(
"Focuser will move in direction %d relative %d ticks...", dir, ticks);
547 bool savePositionsToEEPROM =
false;
552 LOGF_WARN(
"Warning: Focus motor max position %d should be %d and Rotator motor max position %d should be %d",
560 if (isHomingComplete())
568 haveReadFocusPositionAtLeastOnce =
false;
569 haveReadRotatorPositionAtLeastOnce =
false;
583 && timeToReadTemperature <= 0)
585 rc = getTemperature();
587 rc = getTemperature();
593 lastTemperature = SensorN[SENSOR_TEMPERATURE].value;
600 timeToReadTemperature--;
622 if (haveReadFocusPositionAtLeastOnce)
624 LOGF_INFO(
"Focuser reached requested position %d", lastFocuserPosition);
628 LOGF_INFO(
"Focuser position is %d", lastFocuserPosition);
629 haveReadFocusPositionAtLeastOnce =
true;
631 savePositionsToEEPROM =
true;
642 if (!haveReadRotatorPositionAtLeastOnce || RotatorAbsPosNP.
s ==
IPS_BUSY)
652 if (RotatorAbsPosN[0].value != lastRotatorPosition)
654 lastRotatorPosition = RotatorAbsPosN[0].value;
656 lastRotatorPosition);
659 if (haveReadRotatorPositionAtLeastOnce)
660 LOGF_INFO(
"Rotator reached requested angle %.2f, position %d",
661 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
664 LOGF_INFO(
"Rotator is at angle %.2f, position %d",
665 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
666 haveReadRotatorPositionAtLeastOnce =
true;
668 savePositionsToEEPROM =
true;
680 if (savePositionsToEEPROM)
692 bool Integra::stopMotor(MotorType
type)
695 if (integraMotorGetCommand(__FUNCTION__,
stop_motor,
type,
nullptr))
699 haveReadFocusPositionAtLeastOnce =
false;
703 haveReadRotatorPositionAtLeastOnce =
false;
711 bool Integra::isMotorMoving(MotorType
type)
714 if ( ! integraGetCommand( __FUNCTION__,
is_moving, res))
742 LOG_DEBUG(
"Rotator motor is not running");
749 bool Integra::getMaxPosition(MotorType
type)
756 int position = atoi(res);
757 if (MaxPositionN[
type].value == position)
763 LOGF_WARN(
"Updated %s motor max position from %d to %d",
765 MaxPositionN[
type].value = position;
772 RotatorAbsPosN[0].max = MaxPositionN[
type].value;
782 bool Integra::saveToEEPROM()
784 return integraGetCommand(__FUNCTION__,
EEPROMwrite,
nullptr);
787 bool Integra::getTemperature()
792 SensorN[SENSOR_TEMPERATURE].value = strtod(res,
nullptr);
798 bool Integra::findHome()
803 bool Integra::abortHome()
808 bool Integra::isHomingComplete()
813 return (res[0] ==
'1');
820 Focuser::saveConfigItems(fp);
830 uint32_t p1 = lastRotatorPosition;
831 uint32_t p2 = rotatorDegreesToTicks(angle);
833 LOGF_INFO(
"MoveRotator from %.2f to %.2f degrees, from position %d to %d ...",
834 rotatorTicksToDegrees(lastRotatorPosition), angle, p1, p2);
849 if (rc && RotatorAbsPosNP.
s !=
IPS_OK)
858 uint32_t Integra::rotatorDegreesToTicks(
double angle)
860 uint32_t position = 61802 / 2;
861 if (angle >= 0.0 && angle <= 180.0)
863 position = (uint32_t) lround(61802.0 - (180.0 - angle) / rotatorDegreesPerTick);
865 else if (angle > 180 && angle <= 360)
867 position = (uint32_t) lround(61802.0 - (540.0 - angle) / rotatorDegreesPerTick);
871 LOGF_ERROR(
"%s error: %.2f is out of range", __FUNCTION__, angle);
876 double Integra::rotatorTicksToDegrees(uint32_t ticks)
884 uint32_t position = rotatorDegreesToTicks(angle);
887 haveReadRotatorPositionAtLeastOnce =
false;
898 bool Integra::integraGetCommand(
const char *name,
int commandIdx,
char *returnValueString )
901 snprintf(
cmd, 16,
"%s", IntegraProtocol[commandIdx].
cmd);
902 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
905 bool Integra::integraMotorGetCommand(
const char *name,
int commandIdx, MotorType motor,
char *returnValueString )
908 snprintf(
cmd, 16, IntegraProtocol[commandIdx].
cmd, motor + 1);
909 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
912 bool Integra::integraMotorSetCommand(
const char *name,
int commandIdx, MotorType motor,
int value,
913 char *returnValueString )
916 snprintf(
cmd, 16, IntegraProtocol[commandIdx].
cmd, motor + 1, value);
917 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
920 bool Integra::genericIntegraCommand(
const char *name,
const char *
cmd,
const char *expectStart,
char *returnValueString)
922 char cmdnocrlf[16] = {0};
923 int nbytes_written = 0, nbytes_read = 0, rc = -1;
925 char *correctRes =
nullptr;
928 cleanPrint(
cmd, cmdnocrlf);
931 tcflush(
PortFD, TCIOFLUSH);
949 if (expectStart !=
nullptr)
951 correctRes = strstr(res, expectStart);
952 if (correctRes ==
nullptr)
954 LOGF_ERROR(
"%s error: invalid response (%s)", name, res);
959 if (res[nbytes_read - 1] !=
'#')
961 LOGF_ERROR(
"%s error: invalid response 2 (%s)", name, res);
964 res[nbytes_read - 1] =
'\0';
966 if (returnValueString !=
nullptr && expectStart !=
nullptr)
968 size_t expectStrlen = strlen(expectStart);
969 strcpy(returnValueString, correctRes + expectStrlen);
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
const char * getDeviceName() 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)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
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
void addDebugControl()
Add Debug control to the driver.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
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 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
void setSupportedConnections(const uint8_t &value)
setConnection Set Focuser connection mode. Child class should call this in the constructor before Foc...
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
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.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
const char * getDefaultName() override
virtual bool AbortRotator() override
AbortRotator Abort all motion.
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
const int wellKnownIntegra85RotateMax
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
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 bool Handshake() override
perform handshake with device to check communication
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
const int wellKnownIntegra85FocusMax
Provides interface to implement Rotator functionality.
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
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
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.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
void 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,...)
#define LOGF_INFO(fmt,...)
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
#define LOGF_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,...)
struct COMMANDDESC COMMANDDESC
#define INTEGRA_TEMPERATURE_TRESHOLD_IN_C
#define INTEGRA_TEMPERATURE_LOOP_SKIPS
std::unique_ptr< Integra > integra(new Integra())
#define INTEGRA_TIMEOUT_IN_S
#define INTEGRA_ROUNDING_FUDGE