34 static std::unique_ptr<nFrameRotator> rotator(
new nFrameRotator());
59 IUFillNumber(&SteppingPhaseN[0],
"PHASES",
"Wiring",
"%.f", 0, 2, 1, 0);
69 IUFillNumber(&MaxSpeedN[0],
"RATE",
"Rate",
"%.f", 1, 254, 10, 0);
74 IUFillSwitch(&CoilStatusS[COIL_ENERGIZED_OFF],
"COIL_ENERGIZED_OFF",
"De-energized",
ISS_OFF);
79 IUFillNumber(&SettingN[PARAM_STEPS_DEGREE],
"PARAM_STEPS_DEGREE",
"Steps/Degree",
"%.2f", 1., 10000., 500., 1000.);
83 IUFillNumber(&RotatorAbsPosN[0],
"ROTATOR_ABSOLUTE_POSITION",
"Value",
"%.f", 0., 1000000., 0., 0.);
104 return "nFrameRotator";
126 bool rc = getStartupValues();
135 LOG_INFO(
"nFrameRotator is ready.");
137 LOG_WARN(
"Failed to query startup values.");
155 char cmd[NFRAME_LEN] = {0}, res[NFRAME_LEN] = {0};
160 bool rc = sendCommand(
cmd, res, 1, 1);
164 return res[0] ==
'S';
167 bool nFrameRotator::sendCommand(
const char *
cmd,
char * res,
int cmd_len,
int res_len)
169 int nbytes_written = 0, nbytes_read = 0, rc = -1;
171 tcflush(
PortFD, TCIOFLUSH);
175 char hex_cmd[NFRAME_LEN * 3] = {0};
176 hexDump(hex_cmd,
cmd, cmd_len);
190 LOGF_ERROR(
"Serial write error: %s.", errstr);
198 rc =
tty_read(
PortFD, res, res_len, NFRAME_TIMEOUT, &nbytes_read);
212 char hex_res[NFRAME_LEN * 3] = {0};
213 hexDump(hex_res, res, res_len);
221 tcflush(
PortFD, TCIOFLUSH);
226 void nFrameRotator::hexDump(
char * buf,
const char * data,
int size)
228 for (
int i = 0; i < size; i++)
229 sprintf(buf + 3 * i,
"%02X ",
static_cast<uint8_t
>(data[i]));
232 buf[3 * size - 1] =
'\0';
241 if (!strcmp(name, SteppingPhaseNP.
name))
243 if (setSteppingPhase(
static_cast<uint8_t
>(values[0])))
274 if (!strcmp(name, MaxSpeedNP.
name))
276 if (setMaxSpeed(
static_cast<uint8_t
>(values[0])))
293 if (!strcmp(name, SettingNP.
name))
296 std::vector<double> prevValue(SettingNP.
nnp);
297 for (uint8_t i = 0; i < SettingNP.
nnp; i++)
298 prevValue[i] = SettingN[i].value;
302 if (SettingN[PARAM_STEPS_DEGREE].value != prevValue[PARAM_STEPS_DEGREE])
304 GotoRotatorN[0].value = calculateAngle(RotatorAbsPosN[0].value);
310 for (uint8_t i = 0; i < SettingNP.
nnp; i++)
311 SettingN[i].value = prevValue[i];
330 if (!strcmp(name, SteppingModeSP.
name))
339 if (!strcmp(name, CoilStatusSP.
name))
345 if (setCoilStatus(state))
348 if (state == COIL_ENERGIZED_ON)
349 LOG_WARN(
"Coil shall be kept energized after motion is complete. Watch for motor heating!");
351 LOG_INFO(
"Coil shall be de-energized after motion is complete.");
356 CoilStatusS[prevIndex].s =
ISS_ON;
358 LOG_ERROR(
"Failed to update coil energization status.");
369 bool nFrameRotator::getStartupValues()
371 bool rc1 = readCoilStatus();
372 bool rc2 = readSteppingInfo();
374 return (rc1 && rc2 );
379 requestedAngle = angle ;
381 LOGF_DEBUG(
"Angle = <%f> Step/Deg=<%d>", angle, (
int)SettingN[PARAM_STEPS_DEGREE].value);
383 double r = (angle > 180) ? 360 - angle : angle;
384 int sign = (angle >= 0 && angle <= 180) ? 1 : -1;
391 double newTarget = r * SettingN[PARAM_STEPS_DEGREE].value + m_ZeroPosition;
392 m_TargetDiff = (int)newTarget - (
int)RotatorAbsPosN[0].value;
419 LOGF_DEBUG(
"wantAbort = %d, diff = %d", wantAbort, m_TargetDiff);
421 if (m_TargetDiff == 0)
425 LOGF_DEBUG(
"HIT reqAngle=%f diff=%d", requestedAngle, m_TargetDiff);
434 int nextMotion = (std::abs(m_TargetDiff) > 999) ? 999 : std::abs(m_TargetDiff);
437 char cmd[NFRAME_LEN] = {0};
438 snprintf(
cmd, NFRAME_LEN,
":F%d%d%03d#", direction, mode, nextMotion);
439 if (sendCommand(
cmd) ==
false)
441 LOG_ERROR(
"Failed to issue motion command.");
458 m_TargetDiff = m_TargetDiff + (nextMotion * ((direction ==
ROTATE_INWARD) ? 1 : -1));
463 if (m_TargetDiff == 0)
473 bool nFrameRotator::isMoving()
475 char res[NFRAME_LEN] = {0};
477 bool rc = sendCommand(
"S", res, 1, 1);
479 if (rc && res[0] ==
'1')
486 bool nFrameRotator::readPosition()
488 char res[NFRAME_LEN] = {0};
490 if (sendCommand(
":RP", res, 3, 7) ==
false)
494 sscanf(res,
"%d", &pos);
500 RotatorAbsPosN[0].value = pos;
502 GotoRotatorN[0].value = calculateAngle(RotatorAbsPosN[0].value );
510 bool nFrameRotator::readSpeedInfo()
512 char res[NFRAME_LEN] = {0};
513 int32_t max_step = 1e6, current_step = 1e6;
516 if (sendCommand(
":RS", res, 3, 3) ==
false)
518 sscanf(res,
"%d", &max_step);
523 if (sendCommand(
":RO", res, 3, 3) ==
false)
525 sscanf(res,
"%d", ¤t_step);
526 if (current_step == 1e6)
529 MaxSpeedN[0].value = 254 - max_step + 1;
543 bool nFrameRotator::readSteppingInfo()
545 char res[NFRAME_LEN] = {0};
547 if (sendCommand(
":RW", res, 3, 1) ==
false)
551 sscanf(res,
"%d", &phase);
556 SteppingPhaseN[0].value = phase;
562 bool nFrameRotator::readCoilStatus()
564 char res[NFRAME_LEN] = {0};
566 if (sendCommand(
":RC", res, 3, 1) ==
false)
572 CoilStatusS[COIL_ENERGIZED_OFF].s = (res[0] ==
'0') ?
ISS_OFF :
ISS_ON;
573 CoilStatusS[COIL_ENERGIZED_ON].s = (res[0] ==
'0') ?
ISS_ON :
ISS_OFF;
587 double r = (angle > 180) ? 360 - angle : angle;
588 int sign = (angle >= 0 && angle <= 180) ? 1 : -1;
592 double newTarget = r * SettingN[PARAM_STEPS_DEGREE].value + m_ZeroPosition;
594 char cmd[NFRAME_LEN] = {0};
595 snprintf(
cmd, NFRAME_LEN,
"#:CP+%06d#", (
int)newTarget);
596 return sendCommand(
cmd);
603 char cmd[NFRAME_LEN] = {0};
604 snprintf(
cmd, NFRAME_LEN,
"#:CO%03d#", 254 - speed + 1);
605 return sendCommand(
cmd);
608 bool nFrameRotator::setMaxSpeed(uint8_t maxSpeed)
613 char cmd[NFRAME_LEN] = {0};
614 snprintf(
cmd, NFRAME_LEN,
":CS%03d#", 254 - maxSpeed + 1);
615 return sendCommand(
cmd);
620 bool nFrameRotator::setSteppingPhase(uint8_t phase)
622 char cmd[NFRAME_LEN] = {0};
623 snprintf(
cmd, NFRAME_LEN,
"#:CW%01d#", phase);
624 return sendCommand(
cmd);
627 bool nFrameRotator::setCoilStatus(uint8_t status)
629 char cmd[NFRAME_LEN] = {0};
630 snprintf(
cmd, NFRAME_LEN,
"#:CC%01d#", status == COIL_ENERGIZED_OFF ? 1 : 0);
631 LOGF_DEBUG(
"setCoil = %d hex=%x", status, status);
632 LOGF_DEBUG(
"setCoilS = %s CEOFF=%d CEON = %d",
cmd, COIL_ENERGIZED_OFF, COIL_ENERGIZED_ON);
633 return sendCommand(
cmd);
646 double nFrameRotator::calculateAngle(uint32_t steps)
648 int diff = (
static_cast<int32_t
>(steps) - m_ZeroPosition) *
651 return range360((
float(diff) + 0.5) / SettingN[PARAM_STEPS_DEGREE].value);
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 loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INumberVectorProperty GotoRotatorNP
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
ISwitchVectorProperty ReverseRotatorSP
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
INumberVectorProperty PresetNP
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 void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the reverse direction property in the configuration file
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the reverse direction property in the configuration file
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Handshake() override
perform handshake with device to check communication
INumberVectorProperty RotatorSpeedNP
bool SetRotatorSpeed(uint8_t speed)
const char * getDefaultName() override
virtual bool AbortRotator() override
AbortRotator Abort all motion.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
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 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 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.
#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,...)