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 MaxPositionNP[FOCUSER].
fill(
"FOCUSER",
"Focuser",
"%.f",
95 MaxPositionNP[ROTATOR].
fill(
"ROTATOR",
"Rotator",
"%.f",
105 SensorNP[SENSOR_TEMPERATURE].
fill(
"TEMPERATURE",
"Temperature (C)",
"%.2f", -100, 100., 1., 0.);
118 FocusAbsPosNP[0].setStep(MaxPositionNP[ROTATOR].getValue() / 50.0);
129 RotatorAbsPosNP[0].
fill(
"ROTATOR_ABSOLUTE_POSITION",
"Ticks",
"%.f", 0., 61802., 1., 0.);
132 RotatorAbsPosNP[0].setMin(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.");
199void 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];
223bool 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.");
252bool Integra::getFocuserType()
255 int rotator_max = int(RotatorAbsPosNP[0].getMax());
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 = RotatorAbsPosNP[0].getMax() / 360.0;
275 rotatorDegreesPerTick = 360.0 / RotatorAbsPosNP[0].getMax();
281bool 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 > MaxPositionNP[
MOTOR_FOCUS].getValue())
297 int newRelativePosition = (int32_t)floor(MaxPositionNP[
MOTOR_FOCUS].getValue()) - lastFocuserPosition;
298 LOGF_INFO(
"Focus position change %d clipped to %d to stay at MAX %d",
299 relativePosition, newRelativePosition, MaxPositionNP[
MOTOR_FOCUS].getValue());
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 > MaxPositionNP[
MOTOR_ROTATOR].getValue())
320 int newRelativePosition = (int32_t)floor(MaxPositionNP[
MOTOR_ROTATOR].getValue()) - lastRotatorPosition;
321 LOGF_INFO(
"Rotator position change %d clipped to %d to stay at MAX %d",
322 relativePosition, newRelativePosition, MaxPositionNP[
MOTOR_ROTATOR].getValue());
323 relativePosition = newRelativePosition;
328 if (lastRotatorPosition + relativePosition < - MaxPositionNP[
MOTOR_ROTATOR].getValue())
330 int newRelativePosition = - (int32_t)floor(MaxPositionNP[
MOTOR_ROTATOR].getValue()) - lastRotatorPosition;
331 LOGF_INFO(
"Rotator position change %d clipped to %d to stay at MIN %d",
332 relativePosition, newRelativePosition, - MaxPositionNP[
MOTOR_ROTATOR].getValue());
333 relativePosition = newRelativePosition;
338 return integraMotorSetCommand( __FUNCTION__, motorMoveCommand,
type, abs(relativePosition),
nullptr);
341bool 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);
359bool 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 (RotatorAbsPosNP[0].getValue() != position)
391 auto position_from = (int) RotatorAbsPosNP[0].getValue();
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 RotatorAbsPosNP[0].setValue(position);
408 LOGF_ERROR(
"%s error: motor type %d is unknown", __FUNCTION__,
type);
424 FindHomeSP.
update(states, names, n);
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 LOG_INFO(
"Unknown homing index %d");
471 else if (strstr(name,
"ROTATOR"))
488 RotatorAbsPosNP.
apply();
490 LOGF_DEBUG(
"Rotator moving from %d to %.f ticks...", lastRotatorPosition, values[0]);
493 else if (strstr(name,
"ROTATOR"))
505 targetPosition = targetTicks;
506 LOGF_DEBUG(
"Focuser will move absolute from %d to %d ...", lastFocuserPosition, targetTicks);
520 double newPosition = 0;
522 LOGF_DEBUG(
"Focuser will move in direction %d relative %d ticks...", dir, ticks);
548 bool savePositionsToEEPROM =
false;
554 LOGF_WARN(
"Warning: Focus motor max position %d should be %d and Rotator motor max position %d should be %d",
562 if (isHomingComplete())
570 haveReadFocusPositionAtLeastOnce =
false;
571 haveReadRotatorPositionAtLeastOnce =
false;
585 && timeToReadTemperature <= 0)
587 rc = getTemperature();
589 rc = getTemperature();
595 lastTemperature = SensorNP[SENSOR_TEMPERATURE].getValue();
602 timeToReadTemperature--;
624 if (haveReadFocusPositionAtLeastOnce)
626 LOGF_INFO(
"Focuser reached requested position %d", lastFocuserPosition);
630 LOGF_INFO(
"Focuser position is %d", lastFocuserPosition);
631 haveReadFocusPositionAtLeastOnce =
true;
633 savePositionsToEEPROM =
true;
644 if (!haveReadRotatorPositionAtLeastOnce || RotatorAbsPosNP.
getState() ==
IPS_BUSY)
654 if (RotatorAbsPosNP[0].getValue() != lastRotatorPosition)
656 lastRotatorPosition = RotatorAbsPosNP[0].getValue();
658 lastRotatorPosition));
659 RotatorAbsPosNP.
apply( );
661 if (haveReadRotatorPositionAtLeastOnce)
662 LOGF_INFO(
"Rotator reached requested angle %.2f, position %d",
663 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
666 LOGF_INFO(
"Rotator is at angle %.2f, position %d",
667 rotatorTicksToDegrees(lastRotatorPosition), lastRotatorPosition);
668 haveReadRotatorPositionAtLeastOnce =
true;
670 savePositionsToEEPROM =
true;
682 if (savePositionsToEEPROM)
694bool Integra::stopMotor(MotorType
type)
697 if (integraMotorGetCommand(__FUNCTION__,
stop_motor,
type,
nullptr))
701 haveReadFocusPositionAtLeastOnce =
false;
705 haveReadRotatorPositionAtLeastOnce =
false;
713bool Integra::isMotorMoving(MotorType
type)
716 if ( ! integraGetCommand( __FUNCTION__,
is_moving, res))
744 LOG_DEBUG(
"Rotator motor is not running");
751bool Integra::getMaxPosition(MotorType
type)
758 int position = atoi(res);
759 if (MaxPositionNP[
type].getValue() == position)
765 LOGF_WARN(
"Updated %s motor max position from %d to %d",
767 MaxPositionNP[
type].setValue(position);
774 RotatorAbsPosNP[0].setMax(MaxPositionNP[
type].getValue());
784bool Integra::saveToEEPROM()
786 return integraGetCommand(__FUNCTION__,
EEPROMwrite,
nullptr);
789bool Integra::getTemperature()
794 SensorNP[SENSOR_TEMPERATURE].setValue(strtod(res,
nullptr));
800bool Integra::findHome()
805bool Integra::abortHome()
810bool Integra::isHomingComplete()
815 return (res[0] ==
'1');
822 Focuser::saveConfigItems(fp);
832 uint32_t p1 = lastRotatorPosition;
833 uint32_t p2 = rotatorDegreesToTicks(angle);
835 LOGF_INFO(
"MoveRotator from %.2f to %.2f degrees, from position %d to %d ...",
836 rotatorTicksToDegrees(lastRotatorPosition), angle, p1, p2);
841 RotatorAbsPosNP.
apply();
854 RotatorAbsPosNP.
apply();
860uint32_t Integra::rotatorDegreesToTicks(
double angle)
862 uint32_t position = 61802 / 2;
863 if (angle >= 0.0 && angle <= 180.0)
865 position = (uint32_t) lround(61802.0 - (180.0 - angle) / rotatorDegreesPerTick);
867 else if (angle > 180 && angle <= 360)
869 position = (uint32_t) lround(61802.0 - (540.0 - angle) / rotatorDegreesPerTick);
873 LOGF_ERROR(
"%s error: %.2f is out of range", __FUNCTION__, angle);
878double Integra::rotatorTicksToDegrees(uint32_t ticks)
886 uint32_t position = rotatorDegreesToTicks(angle);
889 haveReadRotatorPositionAtLeastOnce =
false;
900bool Integra::integraGetCommand(
const char *name,
int commandIdx,
char *returnValueString )
903 snprintf(
cmd, 16,
"%s", IntegraProtocol[commandIdx].
cmd);
904 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
907bool Integra::integraMotorGetCommand(
const char *name,
int commandIdx, MotorType motor,
char *returnValueString )
910 snprintf(
cmd, 16, IntegraProtocol[commandIdx].
cmd, motor + 1);
911 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
914bool Integra::integraMotorSetCommand(
const char *name,
int commandIdx, MotorType motor,
int value,
915 char *returnValueString )
918 snprintf(
cmd, 16, IntegraProtocol[commandIdx].
cmd, motor + 1, value);
919 return genericIntegraCommand(name,
cmd, IntegraProtocol[commandIdx].ret[this->firmwareVersion], returnValueString);
922bool Integra::genericIntegraCommand(
const char *name,
const char *
cmd,
const char *expectStart,
char *returnValueString)
924 char cmdnocrlf[16] = {0};
925 int nbytes_written = 0, nbytes_read = 0, rc = -1;
927 char *correctRes =
nullptr;
930 cleanPrint(
cmd, cmdnocrlf);
933 tcflush(
PortFD, TCIOFLUSH);
951 if (expectStart !=
nullptr)
953 correctRes = strstr(res, expectStart);
954 if (correctRes ==
nullptr)
956 LOGF_ERROR(
"%s error: invalid response (%s)", name, res);
961 if (res[nbytes_read - 1] !=
'#')
963 LOGF_ERROR(
"%s error: invalid response 2 (%s)", name, res);
966 res[nbytes_read - 1] =
'\0';
968 if (returnValueString !=
nullptr && expectStart !=
nullptr)
970 size_t expectStrlen = strlen(expectStart);
971 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...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
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 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
void setSupportedConnections(const uint8_t &value)
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
int findOnSwitchIndex() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Rotator switch properties.
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.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Rotator number properties.
INDI::PropertyNumber GotoRotatorNP
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
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
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
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
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
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
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
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.
#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,...)
#define INTEGRA_TEMPERATURE_TRESHOLD_IN_C
std::unique_ptr< Integra > integra(new Integra())
#define INTEGRA_TEMPERATURE_LOOP_SKIPS
#define INTEGRA_TIMEOUT_IN_S
#define INTEGRA_ROUNDING_FUDGE