Instrument Neutral Distributed Interface INDI  2.0.2
nfocus.cpp
Go to the documentation of this file.
1 /*
2  NFocus DC Relative Focuser
3 
4  Copyright (C) 2019 Jasem Mutlaq (mutlaqja@ikarustech.com)
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Lesser General Public
8  License as published by the Free Software Foundation; either
9  version 2.1 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public
17  License along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20 */
21 
22 #include "nfocus.h"
23 
24 #include "indicom.h"
25 
26 #include <cstring>
27 #include <memory>
28 #include <termios.h>
29 
30 static std::unique_ptr<NFocus> nFocus(new NFocus());
31 
33 {
34  setVersion(1, 1);
36 }
37 
39 {
41 
42  // Focuser temperature
43  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -100, 100, 0, 0);
44  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
46 
47  // Settings of the Nfocus
48  IUFillNumber(&SettingsN[SETTING_ON_TIME], "ON time", "ON waiting time", "%6.0f", 10., 250., 0., 73.);
49  IUFillNumber(&SettingsN[SETTING_OFF_TIME], "OFF time", "OFF waiting time", "%6.0f", 1., 250., 0., 15.);
50  IUFillNumber(&SettingsN[SETTING_MODE_DELAY], "Fast Mode Delay", "Fast Mode Delay", "%6.0f", 0., 255., 0., 9.);
51  IUFillNumberVector(&SettingsNP, SettingsN, 3, getDeviceName(), "FOCUS_SETTINGS", "Settings", SETTINGS_TAB, IP_RW, 0,
52  IPS_IDLE);
53 
54  FocusRelPosN[0].min = 0;
55  FocusRelPosN[0].max = 50000;
56  FocusRelPosN[0].step = 1000;
57  FocusRelPosN[0].value = 0;
58 
59  // Set polling to 500ms
61 
62  return true;
63 }
64 
66 {
68 
69  if (isConnected())
70  {
71  if (readTemperature())
72  defineProperty(&TemperatureNP);
73  defineProperty(&SettingsNP);
74 
75  if (getStartupValues())
76  LOG_INFO("NFocus is ready.");
77  }
78  else
79  {
80  deleteProperty(TemperatureNP.name);
81  deleteProperty(SettingsNP.name);
82  }
83 
84  return true;
85 }
86 
88 {
89  return "NFocus";
90 }
91 
93 {
94  char cmd[NFOCUS_LEN] = {0}, res[NFOCUS_LEN] = {0};
95 
96  // Ack
97  cmd[0] = 0x6;
98 
99  bool rc = sendCommand(cmd, res, 1, 1);
100  if (rc == false)
101  return false;
102 
103  return res[0] == 'n';
104 }
105 
106 bool NFocus::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
107 {
108  int nbytes_written = 0, nbytes_read = 0, rc = -1;
109 
110  tcflush(PortFD, TCIOFLUSH);
111 
112  if (cmd_len > 0)
113  {
114  char hex_cmd[NFOCUS_LEN * 3] = {0};
115  hexDump(hex_cmd, cmd, cmd_len);
116  LOGF_DEBUG("CMD <%s>", hex_cmd);
117  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
118  }
119  else
120  {
121  LOGF_DEBUG("CMD <%s>", cmd);
122  rc = tty_write_string(PortFD, cmd, &nbytes_written);
123  }
124 
125  if (rc != TTY_OK)
126  {
127  char errstr[MAXRBUF] = {0};
128  tty_error_msg(rc, errstr, MAXRBUF);
129  LOGF_ERROR("Serial write error: %s.", errstr);
130  return false;
131  }
132 
133  if (res == nullptr)
134  return true;
135 
136  if (res_len > 0)
137  rc = tty_read(PortFD, res, res_len, NFOCUS_TIMEOUT, &nbytes_read);
138  else
139  rc = tty_nread_section(PortFD, res, NFOCUS_LEN, NFOCUS_STOP_CHAR, NFOCUS_TIMEOUT, &nbytes_read);
140 
141  if (rc != TTY_OK)
142  {
143  char errstr[MAXRBUF] = {0};
144  tty_error_msg(rc, errstr, MAXRBUF);
145  LOGF_ERROR("Serial read error: %s.", errstr);
146  return false;
147  }
148 
149  if (res_len > 0)
150  {
151  char hex_res[NFOCUS_LEN * 3] = {0};
152  hexDump(hex_res, res, res_len);
153  LOGF_DEBUG("RES <%s>", hex_res);
154  }
155  else
156  {
157  LOGF_DEBUG("RES <%s>", res);
158  }
159 
160  tcflush(PortFD, TCIOFLUSH);
161 
162  return true;
163 }
164 
165 void NFocus::hexDump(char * buf, const char * data, int size)
166 {
167  for (int i = 0; i < size; i++)
168  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
169 
170  if (size > 0)
171  buf[3 * size - 1] = '\0';
172 }
173 
174 bool NFocus::readTemperature()
175 {
176  char res[NFOCUS_LEN] = {0};
177 
178  if (sendCommand(":RT", res, 3, 4) == false)
179  return false;
180 
181  float temperature = -1000;
182  sscanf(res, "%f", &temperature);
183 
184  temperature /= 10.0;
185 
186  if (temperature <= -80)
187  return false;
188 
189  TemperatureN[0].value = temperature;
190  TemperatureNP.s = IPS_OK;
191 
192  return true;
193 }
194 
195 bool NFocus::setMotorSettings(double onTime, double offTime, double fastDelay)
196 {
197  char on_cmd[NFOCUS_LEN] = {0}, off_cmd[NFOCUS_LEN] = {0}, fast_cmd[NFOCUS_LEN] = {0};
198 
199  snprintf(on_cmd, NFOCUS_LEN, ":CO%03d#", static_cast<int>(onTime));
200  snprintf(off_cmd, NFOCUS_LEN, ":CF%03d#", static_cast<int>(offTime));
201  snprintf(fast_cmd, NFOCUS_LEN, ":CS%03d#", static_cast<int>(fastDelay));
202 
203  bool on_rc = sendCommand(on_cmd);
204  bool off_rc = sendCommand(off_cmd);
205  bool fast_rc = sendCommand(fast_cmd);
206 
207  return (on_rc && off_rc && fast_rc);
208 }
209 
210 bool NFocus::readMotorSettings()
211 {
212  char on_res[NFOCUS_LEN] = {0}, off_res[NFOCUS_LEN] = {0}, fast_res[NFOCUS_LEN] = {0};
213 
214  bool on_rc = sendCommand(":RO", on_res, 3, 3);
215  bool off_rc = sendCommand(":RF", off_res, 3, 3);
216  bool fast_rc = sendCommand(":RS", fast_res, 3, 3);
217 
218  if (on_rc && off_rc && fast_rc)
219  {
220  SettingsN[SETTING_ON_TIME].value = std::stoi(on_res);
221  SettingsN[SETTING_OFF_TIME].value = std::stoi(off_res);
222  SettingsN[SETTING_MODE_DELAY].value = std::stoi(fast_res);
223  return true;
224  }
225 
226  return false;
227 }
228 
229 bool NFocus::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
230 {
231  int nset = 0, i = 0;
232 
233  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
234  {
235  // Settings
236  if (strcmp(name, SettingsNP.name) == 0)
237  {
238  // New Settings
239  double new_onTime = 0;
240  double new_offTime = 0;
241  double new_fastDelay = 0;
242 
243  for (nset = i = 0; i < n; i++)
244  {
245  /* Find numbers with the passed names in the SettingsNP property */
246  INumber *eqp = IUFindNumber(&SettingsNP, names[i]);
247 
248  /* If the number found is (SettingsN[0]) then process it */
249  if (eqp == &SettingsN[SETTING_ON_TIME])
250  {
251  new_onTime = (values[i]);
252  nset += static_cast<int>(new_onTime >= 10 && new_onTime <= 250);
253  }
254  else if (eqp == &SettingsN[SETTING_OFF_TIME])
255  {
256  new_offTime = (values[i]);
257  nset += static_cast<int>(new_offTime >= 1 && new_offTime <= 250);
258  }
259  else if (eqp == &SettingsN[SETTING_MODE_DELAY])
260  {
261  new_fastDelay = (values[i]);
262  nset += static_cast<int>(new_fastDelay >= 1 && new_fastDelay <= 9);
263  }
264  }
265 
266  /* Did we process the three numbers? */
267  if (nset == 3)
268  {
269  if (setMotorSettings(new_onTime, new_offTime, new_fastDelay) == false)
270  {
271  LOG_ERROR("Changing to new settings failed");
272  SettingsNP.s = IPS_ALERT;
273  IDSetNumber(&SettingsNP, nullptr);
274  return false;
275  }
276 
277  IUUpdateNumber(&SettingsNP, values, names, n);
278  SettingsNP.s = IPS_OK;
279  IDSetNumber(&SettingsNP, nullptr);
280  return true;
281  }
282  else
283  {
284  SettingsNP.s = IPS_IDLE;
285  LOG_WARN("Settings invalid.");
286  IDSetNumber(&SettingsNP, nullptr);
287  return false;
288  }
289  }
290 
291  }
292 
293  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
294 }
295 
296 bool NFocus::getStartupValues()
297 {
298  if (readMotorSettings())
299  {
300  SettingsNP.s = IPS_OK;
301  IDSetNumber(&SettingsNP, nullptr);
302  }
303 
304  return true;
305 }
306 
308 {
309  INDI_UNUSED(dir);
310  m_TargetPosition = ticks;
311  return IPS_BUSY;
312 }
313 
315 {
316  return sendCommand("F00000#");
317 }
318 
320 {
321  if (isConnected() == false)
322  return;
323 
324  // Check if we have a pending motion
325  // and if we STOPPED, then let's take the next action
326  if (FocusRelPosNP.s == IPS_BUSY && isMoving() == false)
327  {
328  // Are we done moving?
329  if (m_TargetPosition == 0)
330  {
332  IDSetNumber(&FocusRelPosNP, nullptr);
333  }
334  else
335  {
336  // 999 is the max we can go in one command
337  // so we need to go 999 or LESS
338  // therefore for larger movements, we break it down.
339  int nextMotion = (m_TargetPosition > 999) ? 999 : m_TargetPosition;
340  int direction = IUFindOnSwitchIndex(&FocusMotionSP);
341  char cmd[NFOCUS_LEN] = {0};
342  snprintf(cmd, NFOCUS_LEN, ":F%d0%03d#", direction, nextMotion);
343  if (sendCommand(cmd) == false)
344  {
346  LOG_ERROR("Failed to issue motion command.");
347  IDSetNumber(&FocusRelPosNP, nullptr);
348  }
349  else
350  m_TargetPosition -= nextMotion;
351  }
352  }
353 
354  // Read temperature
355  if (TemperatureNP.s == IPS_OK && m_TemperatureCounter++ == NFOCUS_TEMPERATURE_FREQ)
356  {
357  m_TemperatureCounter = 0;
358  if (readTemperature())
359  IDSetNumber(&TemperatureNP, nullptr);
360  }
361 
363 }
364 
365 bool NFocus::isMoving()
366 {
367  char res[NFOCUS_LEN] = {0};
368 
369  bool rc = sendCommand("S", res, 1, 1);
370 
371  if (rc && res[0] == '1')
372  return true;
373 
374  return false;
375 }
376 
378 {
380  IUSaveConfigNumber(fp, &SettingsNP);
381  return true;
382 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
ISwitchVectorProperty FocusMotionSP
INumberVectorProperty FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
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.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
The NFocus class Handles communication and control with nFocus DC focuser.
Definition: nfocus.h:47
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: nfocus.cpp:229
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: nfocus.cpp:307
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: nfocus.cpp:314
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: nfocus.cpp:319
virtual bool Handshake() override
perform handshake with device to check communication
Definition: nfocus.cpp:92
bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: nfocus.cpp:377
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: nfocus.cpp:38
const char * getDefaultName() override
Definition: nfocus.cpp:87
NFocus()
Definition: nfocus.cpp:32
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: nfocus.cpp:65
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
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:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
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: indidevapi.c:272
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indidevapi.c:66
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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: indidevapi.c:180
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#define LOG_WARN(txt)
Definition: indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
One number descriptor.
char name[MAXINDINAME]
Definition: indiapi.h:323