Instrument Neutral Distributed Interface INDI  1.9.2
rainbowRSF.cpp
Go to the documentation of this file.
1 /*
2  Rainbow Astro Focuser
3  Copyright (C) 2020 Abdulaziz Bouland (boulandab@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 /*
22 This is the Rainbow API. A pdf is in the driver docs folder.
23 
24 Command Send Receive Comment
25 
26 Get Position :Fp# :FPsDD.DDD# s is + or -,
27  sDD.DDD is a float number
28  range: -08.000 to +08.000
29  unit millimeters
30 
31 Is Focus Moving :Fs# :FS0# not moving
32  :FS1# moving
33 
34 Temperature :Ft1# :FT1sDD.D# s is + or -, DD.D temp in celcius
35 
36 Move Absolute :FmsDDDD# :FM# s is + or -, DDDD from -8000 to 8000
37 
38 Move Relative :FnsDDDD# :FM# s is + or -, DDDD from -8000 to 8000
39  neg numbers move away from main mirror
40 
41 Move Home :Fh# :FH#
42 */
43 
44 #include "rainbowRSF.h"
45 #include "indicom.h"
46 
47 #include <cmath>
48 #include <cstring>
49 #include <termios.h>
50 
51 static std::unique_ptr<RainbowRSF> rainbowRSF(new RainbowRSF());
52 
54 {
55  setVersion(1, 0);
56 
58 }
59 
64 {
65  return "Rainbow Astro RSF";
66 }
67 
72 {
74 
75  // Go home switch
76  IUFillSwitch(&GoHomeS[0], "GO_HOME", "Go Home", ISS_OFF);
77  IUFillSwitchVector(&GoHomeSP, GoHomeS, 1, getDeviceName(), "FOCUS_GO_HOME", "Home", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,
78  60, IPS_IDLE);
79 
80  // Focuser temperature
81  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%.2f", -50, 70., 0., 0.);
82  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", MAIN_CONTROL_TAB,
83  IP_RO, 0, IPS_IDLE);
84 
85  // Focuser Limits
86  FocusAbsPosN[0].min = 0;
87  FocusAbsPosN[0].max = 16000;
88  FocusAbsPosN[0].step = 1000;
89 
90  FocusMaxPosN[0].min = 0 ;
91  FocusMaxPosN[0].max = 16000;
92  FocusMaxPosN[0].step = 1000;
93  FocusMaxPosN[0].value = 16000;
95 
96  FocusRelPosN[0].min = 0;
97  FocusRelPosN[0].max = 8000;
98  FocusRelPosN[0].step = 1000;
99 
101  addDebugControl();
102 
103  return true;
104 }
105 
110 {
112 
113  if (isConnected())
114  {
115  defineProperty(&TemperatureNP);
116  defineProperty(&GoHomeSP);
117  }
118  else
119  {
120  deleteProperty(TemperatureNP.name);
121  deleteProperty(GoHomeSP.name);
122  }
123  return true;
124 }
125 
129 bool RainbowRSF::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
130 {
131  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
132  {
133  // Find Home
134  if (!strcmp(name, GoHomeSP.name))
135  {
136  GoHomeSP.s = findHome() ? IPS_BUSY : IPS_ALERT;
137 
138  if (GoHomeSP.s == IPS_BUSY)
139  LOG_INFO("Moving to home position...");
140  else
141  LOG_ERROR("Failed to move to home position.");
142 
143  IDSetSwitch(&GoHomeSP, nullptr);
144  return true;
145  }
146  }
147  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
148 }
149 
154 {
155  if (updateTemperature())
156  {
157  LOG_INFO("Rainbow Astro is online. Getting focus parameters...");
158  return true;
159  }
160 
161  LOG_INFO(
162  "Error retrieving data from Rainbow Astro, please ensure Rainbow Astro controller is powered and the port is correct.");
163  return false;
164 }
165 
169 
170 namespace {
171 bool parsePosition(char *result, int *pos)
172 {
173  const int length = strlen(result);
174  if (length < 6) return false;
175  // Check for a decimal/period
176  char *period = strchr(result+3, '.');
177  if (period == nullptr) return false;
178 
179  float position;
180  if (sscanf(result, ":FP%f#", &position) == 1)
181  {
182  // position is a float number between -8 and +8 that needs to be multiplied by 1000.
183  *pos = position * 1000;
184  return true;
185  }
186  return false;
187 }
188 } // namespace
189 
190 
192 {
193  char res[DRIVER_LEN] = {0};
194 
198  if (isSimulation())
199  {
200  // Move the focuser
201  if (FocusAbsPosN[0].value > m_TargetPosition)
202  {
203  FocusAbsPosN[0].value -= 500;
204  if (FocusAbsPosN[0].value < m_TargetPosition)
205  FocusAbsPosN[0].value = m_TargetPosition;
206  }
207  else if (FocusAbsPosN[0].value < m_TargetPosition)
208  {
209  FocusAbsPosN[0].value += 500;
210  if (FocusAbsPosN[0].value > m_TargetPosition)
211  FocusAbsPosN[0].value = m_TargetPosition;
212  }
213 
214  // update the states
215  if (FocusAbsPosN[0].value == m_TargetPosition)
216  {
217  if (GoHomeSP.s == IPS_BUSY)
218  {
219  GoHomeSP.s = IPS_OK;
222  IDSetSwitch(&GoHomeSP, nullptr);
223  IDSetNumber(&FocusAbsPosNP, nullptr);
224  IDSetNumber(&FocusRelPosNP, nullptr);
225  LOG_INFO("Focuser reached home position.");
226  }
227 
228  else if (FocusAbsPosNP.s == IPS_BUSY)
229  {
232  IDSetNumber(&FocusAbsPosNP, nullptr);
233  IDSetNumber(&FocusRelPosNP, nullptr);
234  LOG_INFO("Focuser reached target position.");
235  }
236  }
237 
238  return true;
239  }
240 
244  else if (sendCommand(":Fp#", res, DRIVER_LEN) == false)
245  return false;
246 
247  int newPosition { 0 };
248  bool ok = parsePosition(res, &newPosition);
249  if (ok) {
250  FocusAbsPosN[0].value = newPosition + 8000;
251 
252  const int offset = FocusAbsPosN[0].value - m_TargetPosition;
253  constexpr int TOLERANCE = 1; // Off-by-one position is ok given the resolution of the response.
254  if (std::abs(offset) <= TOLERANCE)
255  {
256  if (GoHomeSP.s == IPS_BUSY)
257  {
258  GoHomeSP.s = IPS_OK;
259  GoHomeS[0].s = ISS_OFF;
260  IDSetSwitch(&GoHomeSP, nullptr);
261 
263  IDSetNumber(&FocusAbsPosNP, nullptr);
264 
266  IDSetNumber(&FocusRelPosNP, nullptr);
267 
268  LOG_INFO("Focuser reached home position.");
269  }
270 
271  else if (FocusAbsPosNP.s == IPS_BUSY)
272  {
274  IDSetNumber(&FocusAbsPosNP, nullptr);
275 
277 
278  IDSetNumber(&FocusRelPosNP, nullptr);
279  LOG_INFO("Focuser reached target position.");
280  }
281  }
282  return true;
283  }
284  else
285  {
287  return false;
288  }
289 
290 }
291 
296 {
297  char res[DRIVER_LEN] = {0};
298  float temperature = 0;
299 
300  if (isSimulation())
301  strncpy(res, ":FT1+23.5#", DRIVER_LEN);
302 
303  else if (sendCommand(":Ft1#", res, DRIVER_LEN) == false)
304  return false;
305 
306  if (sscanf(res, ":FT1%g", &temperature) == 1)
307  {
308  TemperatureN[0].value = temperature;
309  TemperatureNP.s = IPS_OK;
310  return true;
311  }
312  else
313  {
314  TemperatureNP.s = IPS_ALERT;
315  return false;
316  }
317 }
318 
322 IPState RainbowRSF::MoveAbsFocuser(uint32_t targetTicks)
323 {
324  m_TargetPosition = targetTicks;
325 
326  char cmd[DRIVER_LEN] = {0};
327  char res[DRIVER_LEN] = {0};
328  int steps = targetTicks - 8000;
329 
330  snprintf(cmd, 16, ":Fm%c%04d#", steps >= 0 ? '+' : '-', std::abs(steps));
331 
332  if (isSimulation() == false)
333  {
334  if (sendCommand(cmd, res, DRIVER_LEN) == false)
335  return IPS_ALERT;
336  }
337  return IPS_BUSY;
338 }
339 
341 {
342  int reversed = (IUFindOnSwitchIndex(&FocusReverseSP) == INDI_ENABLED) ? -1 : 1;
343  int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
344  double newPosition = FocusAbsPosN[0].value + relativeTicks;
345 
346  bool rc = MoveAbsFocuser(newPosition);
347 
348  return (rc ? IPS_BUSY : IPS_ALERT);
349 }
350 
355 {
356  if (isSimulation())
357  {
358  MoveAbsFocuser(homePosition);
360  return true;
361  }
362  else
363  {
364  m_TargetPosition = homePosition;
366  char res[DRIVER_LEN] = {0};
367  return sendCommand(":Fh#", res, DRIVER_LEN);
368  }
369 }
370 
375 {
376  // position update
377  bool rc = updatePosition();
378  if (rc)
379  {
380  if (abs(m_LastPosition - FocusAbsPosN[0].value) > 0)
381  {
382  IDSetNumber(&FocusAbsPosNP, nullptr);
383  m_LastPosition = FocusAbsPosN[0].value;
384 
385  if (GoHomeSP.s == IPS_BUSY && FocusAbsPosN[0].value == homePosition)
386  {
387  GoHomeSP.s = IPS_OK;
388  LOG_INFO("Focuser arrived at home position.");
389  IDSetSwitch(&GoHomeSP, nullptr);
390  }
391  }
392  }
393 
394  // temperature update
395  if (m_TemperatureCounter++ == DRIVER_TEMPERATURE_FREQ)
396  {
397  rc = updateTemperature();
398  if (rc)
399  {
400  if (abs(m_LastTemperature - TemperatureN[0].value) >= 0.05)
401  {
402  IDSetNumber(&TemperatureNP, nullptr);
403  m_LastTemperature = TemperatureN[0].value;
404  }
405  }
406  // Reset the counter
407  m_TemperatureCounter = 0;
408  }
409 
411 }
412 
416 bool RainbowRSF::sendCommand(const char * cmd, char * res, int res_len)
417 {
418  if (cmd == nullptr || res == nullptr || res_len <= 0)
419  return false;
420  const int cmd_len = strlen(cmd);
421  if (cmd_len <= 0)
422  return false;
423 
424  tcflush(PortFD, TCIOFLUSH);
425 
426  LOGF_DEBUG("CMD <%s>", cmd);
427 
428  int nbytes_written = 0;
429  int rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
430  if (rc != TTY_OK)
431  {
432  char errstr[MAXRBUF] = {0};
433  tty_error_msg(rc, errstr, MAXRBUF);
434  LOGF_ERROR("Serial write error: %s.", errstr);
435  return false;
436  }
437 
438  int nbytes_read = 0;
439  rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR,
440  DRIVER_TIMEOUT, &nbytes_read);
441  if (nbytes_read == DRIVER_LEN)
442  return false;
443  res[nbytes_read] = 0;
444  if (rc != TTY_OK)
445  {
446  char errstr[MAXRBUF] = {0};
447  tty_error_msg(rc, errstr, MAXRBUF);
448  LOGF_ERROR("Serial read error: %s.", errstr);
449  return false;
450  }
451 
452  LOGF_DEBUG("RES <%s>", res);
453 
454  tcflush(PortFD, TCIOFLUSH);
455 
456  return true;
457 }
RainbowRSF::updateTemperature
bool updateTemperature()
Definition: rainbowRSF.cpp:295
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
RainbowRSF::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, unsigned int ticks) override
Definition: rainbowRSF.cpp:340
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
INDI::FocuserInterface::FocusMaxPosNP
INumberVectorProperty FocusMaxPosNP
Definition: indifocuserinterface.h:290
IPState
IPState
Property state.
Definition: indiapi.h:158
tty_nread_section
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
Definition: indicom.c:636
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
RainbowRSF::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: rainbowRSF.cpp:109
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
indicom.h
Implementations for common driver routines.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
IUFillNumber
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.
Definition: indidriver.c:348
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
MAIN_CONTROL_TAB
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
Definition: defaultdevice.cpp:34
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
INDI::DefaultDevice::setVersion
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
Definition: defaultdevice.cpp:1219
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
RainbowRSF::Handshake
virtual bool Handshake() override
perform handshake with device to check communication
Definition: rainbowRSF.cpp:153
INDI::FocuserInterface::FocusReverseSP
ISwitchVectorProperty FocusReverseSP
Definition: indifocuserinterface.h:302
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
tty_error_msg
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1135
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
INDI::Focuser::PortFD
int PortFD
Definition: indifocuser.h:116
tty_write
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:404
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
IUFillSwitchVector
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.
Definition: indidriver.c:412
rainbowRSF.h
IUFillNumberVector
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.
Definition: indidriver.c:455
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
ISR_ATMOST1
@ ISR_ATMOST1
Definition: indiapi.h:173
INDI::Focuser::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indifocuser.cpp:120
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
RainbowRSF
Definition: rainbowRSF.h:26
RainbowRSF::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: rainbowRSF.cpp:129
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
RainbowRSF::updatePosition
bool updatePosition()
Definition: rainbowRSF.cpp:191
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
RainbowRSF::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
Focuser Command Functions.
Definition: rainbowRSF.cpp:322
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
INDI::FocuserInterface::FocusMaxPosN
INumber FocusMaxPosN[1]
Definition: indifocuserinterface.h:291
_INumberVectorProperty::p
IPerm p
Definition: indiapi.h:328
RainbowRSF::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: rainbowRSF.cpp:71
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
RainbowRSF::sendCommand
bool sendCommand(const char *cmd, char *res, int res_len)
Communication Functions.
Definition: rainbowRSF.cpp:416
ISState
ISState
Switch state.
Definition: indiapi.h:148
RainbowRSF::findHome
bool findHome()
Definition: rainbowRSF.cpp:354
RainbowRSF::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: rainbowRSF.cpp:374
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1403
RainbowRSF::RainbowRSF
RainbowRSF()
Definition: rainbowRSF.cpp:53
INDI::DefaultDevice::addSimulationControl
void addSimulationControl()
Add Simulation control to the driver.
Definition: defaultdevice.cpp:646
INDI::DefaultDevice::addDebugControl
void addDebugControl()
Add Debug control to the driver.
Definition: defaultdevice.cpp:639
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
TTY_OK
@ TTY_OK
Definition: indicom.h:94
RainbowRSF::getDefaultName
const char * getDefaultName() override
Definition: rainbowRSF.cpp:63
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
INDI::Focuser::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:168
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
IUFillSwitch
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.
Definition: indidriver.c:320
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370