Instrument Neutral Distributed Interface INDI  1.9.5
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, IPS_IDLE);
52 
53  FocusRelPosN[0].min = 0;
54  FocusRelPosN[0].max = 50000;
55  FocusRelPosN[0].step = 1000;
56  FocusRelPosN[0].value = 0;
57 
58  // Set polling to 500ms
60 
61  return true;
62 }
63 
65 {
67 
68  if (isConnected())
69  {
70  if (readTemperature())
71  defineProperty(&TemperatureNP);
72  defineProperty(&SettingsNP);
73 
74  if (getStartupValues())
75  LOG_INFO("NFocus is ready.");
76  }
77  else
78  {
79  deleteProperty(TemperatureNP.name);
80  deleteProperty(SettingsNP.name);
81  }
82 
83  return true;
84 }
85 
87 {
88  return "NFocus";
89 }
90 
92 {
93  char cmd[NFOCUS_LEN] = {0}, res[NFOCUS_LEN] = {0};
94 
95  // Ack
96  cmd[0] = 0x6;
97 
98  bool rc = sendCommand(cmd, res, 1, 1);
99  if (rc == false)
100  return false;
101 
102  return res[0] == 'n';
103 }
104 
105 bool NFocus::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
106 {
107  int nbytes_written = 0, nbytes_read = 0, rc = -1;
108 
109  tcflush(PortFD, TCIOFLUSH);
110 
111  if (cmd_len > 0)
112  {
113  char hex_cmd[NFOCUS_LEN * 3] = {0};
114  hexDump(hex_cmd, cmd, cmd_len);
115  LOGF_DEBUG("CMD <%s>", hex_cmd);
116  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
117  }
118  else
119  {
120  LOGF_DEBUG("CMD <%s>", cmd);
121  rc = tty_write_string(PortFD, cmd, &nbytes_written);
122  }
123 
124  if (rc != TTY_OK)
125  {
126  char errstr[MAXRBUF] = {0};
127  tty_error_msg(rc, errstr, MAXRBUF);
128  LOGF_ERROR("Serial write error: %s.", errstr);
129  return false;
130  }
131 
132  if (res == nullptr)
133  return true;
134 
135  if (res_len > 0)
136  rc = tty_read(PortFD, res, res_len, NFOCUS_TIMEOUT, &nbytes_read);
137  else
138  rc = tty_nread_section(PortFD, res, NFOCUS_LEN, NFOCUS_STOP_CHAR, NFOCUS_TIMEOUT, &nbytes_read);
139 
140  if (rc != TTY_OK)
141  {
142  char errstr[MAXRBUF] = {0};
143  tty_error_msg(rc, errstr, MAXRBUF);
144  LOGF_ERROR("Serial read error: %s.", errstr);
145  return false;
146  }
147 
148  if (res_len > 0)
149  {
150  char hex_res[NFOCUS_LEN * 3] = {0};
151  hexDump(hex_res, res, res_len);
152  LOGF_DEBUG("RES <%s>", hex_res);
153  }
154  else
155  {
156  LOGF_DEBUG("RES <%s>", res);
157  }
158 
159  tcflush(PortFD, TCIOFLUSH);
160 
161  return true;
162 }
163 
164 void NFocus::hexDump(char * buf, const char * data, int size)
165 {
166  for (int i = 0; i < size; i++)
167  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
168 
169  if (size > 0)
170  buf[3 * size - 1] = '\0';
171 }
172 
173 bool NFocus::readTemperature()
174 {
175  char res[NFOCUS_LEN] = {0};
176 
177  if (sendCommand(":RT", res, 3, 4) == false)
178  return false;
179 
180  float temperature = -1000;
181  sscanf(res, "%f", &temperature);
182 
183  temperature /= 10.0;
184 
185  if (temperature <= -80)
186  return false;
187 
188  TemperatureN[0].value = temperature;
189  TemperatureNP.s = IPS_OK;
190 
191  return true;
192 }
193 
194 bool NFocus::setMotorSettings(double onTime, double offTime, double fastDelay)
195 {
196  char on_cmd[NFOCUS_LEN] = {0}, off_cmd[NFOCUS_LEN] = {0}, fast_cmd[NFOCUS_LEN] = {0};
197 
198  snprintf(on_cmd, NFOCUS_LEN, ":CO%03d#", static_cast<int>(onTime));
199  snprintf(off_cmd, NFOCUS_LEN, ":CF%03d#", static_cast<int>(offTime));
200  snprintf(fast_cmd, NFOCUS_LEN, ":CS%03d#", static_cast<int>(fastDelay));
201 
202  bool on_rc = sendCommand(on_cmd);
203  bool off_rc = sendCommand(off_cmd);
204  bool fast_rc = sendCommand(fast_cmd);
205 
206  return (on_rc && off_rc && fast_rc);
207 }
208 
209 bool NFocus::readMotorSettings()
210 {
211  char on_res[NFOCUS_LEN] = {0}, off_res[NFOCUS_LEN] = {0}, fast_res[NFOCUS_LEN] = {0};
212 
213  bool on_rc = sendCommand(":RO", on_res, 3, 3);
214  bool off_rc = sendCommand(":RF", off_res, 3, 3);
215  bool fast_rc = sendCommand(":RS", fast_res, 3, 3);
216 
217  if (on_rc && off_rc && fast_rc)
218  {
219  SettingsN[SETTING_ON_TIME].value = std::stoi(on_res);
220  SettingsN[SETTING_OFF_TIME].value = std::stoi(off_res);
221  SettingsN[SETTING_MODE_DELAY].value = std::stoi(fast_res);
222  return true;
223  }
224 
225  return false;
226 }
227 
228 bool NFocus::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
229 {
230  int nset = 0, i = 0;
231 
232  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
233  {
234  // Settings
235  if (strcmp(name, SettingsNP.name) == 0)
236  {
237  // New Settings
238  double new_onTime = 0;
239  double new_offTime = 0;
240  double new_fastDelay = 0;
241 
242  for (nset = i = 0; i < n; i++)
243  {
244  /* Find numbers with the passed names in the SettingsNP property */
245  INumber *eqp = IUFindNumber(&SettingsNP, names[i]);
246 
247  /* If the number found is (SettingsN[0]) then process it */
248  if (eqp == &SettingsN[SETTING_ON_TIME])
249  {
250  new_onTime = (values[i]);
251  nset += static_cast<int>(new_onTime >= 10 && new_onTime <= 250);
252  }
253  else if (eqp == &SettingsN[SETTING_OFF_TIME])
254  {
255  new_offTime = (values[i]);
256  nset += static_cast<int>(new_offTime >= 1 && new_offTime <= 250);
257  }
258  else if (eqp == &SettingsN[SETTING_MODE_DELAY])
259  {
260  new_fastDelay = (values[i]);
261  nset += static_cast<int>(new_fastDelay >= 1 && new_fastDelay <= 9);
262  }
263  }
264 
265  /* Did we process the three numbers? */
266  if (nset == 3)
267  {
268  if (setMotorSettings(new_onTime, new_offTime, new_fastDelay) == false)
269  {
270  LOG_ERROR("Changing to new settings failed");
271  SettingsNP.s = IPS_ALERT;
272  IDSetNumber(&SettingsNP, nullptr);
273  return false;
274  }
275 
276  IUUpdateNumber(&SettingsNP, values, names, n);
277  SettingsNP.s = IPS_OK;
278  IDSetNumber(&SettingsNP, nullptr);
279  return true;
280  }
281  else
282  {
283  SettingsNP.s = IPS_IDLE;
284  LOG_WARN("Settings invalid.");
285  IDSetNumber(&SettingsNP, nullptr);
286  return false;
287  }
288  }
289 
290  }
291 
292  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
293 }
294 
295 bool NFocus::getStartupValues()
296 {
297  if (readMotorSettings())
298  {
299  SettingsNP.s = IPS_OK;
300  IDSetNumber(&SettingsNP, nullptr);
301  }
302 
303  return true;
304 }
305 
307 {
308  INDI_UNUSED(dir);
309  m_TargetPosition = ticks;
310  return IPS_BUSY;
311 }
312 
314 {
315  return sendCommand("F00000#");
316 }
317 
319 {
320  if (isConnected() == false)
321  return;
322 
323  // Check if we have a pending motion
324  // and if we STOPPED, then let's take the next action
325  if (FocusRelPosNP.s == IPS_BUSY && isMoving() == false)
326  {
327  // Are we done moving?
328  if (m_TargetPosition == 0)
329  {
331  IDSetNumber(&FocusRelPosNP, nullptr);
332  }
333  else
334  {
335  // 999 is the max we can go in one command
336  // so we need to go 999 or LESS
337  // therefore for larger movements, we break it down.
338  int nextMotion = (m_TargetPosition > 999) ? 999 : m_TargetPosition;
339  int direction = IUFindOnSwitchIndex(&FocusMotionSP);
340  char cmd[NFOCUS_LEN] = {0};
341  snprintf(cmd, NFOCUS_LEN, ":F%d0%03d#", direction, nextMotion);
342  if (sendCommand(cmd) == false)
343  {
345  LOG_ERROR("Failed to issue motion command.");
346  IDSetNumber(&FocusRelPosNP, nullptr);
347  }
348  else
349  m_TargetPosition -= nextMotion;
350  }
351  }
352 
353  // Read temperature
354  if (TemperatureNP.s == IPS_OK && m_TemperatureCounter++ == NFOCUS_TEMPERATURE_FREQ)
355  {
356  m_TemperatureCounter = 0;
357  if (readTemperature())
358  IDSetNumber(&TemperatureNP, nullptr);
359  }
360 
362 }
363 
364 bool NFocus::isMoving()
365 {
366  char res[NFOCUS_LEN] = {0};
367 
368  bool rc = sendCommand("S", res, 1, 1);
369 
370  if (rc && res[0] == '1')
371  return true;
372 
373  return false;
374 }
375 
377 {
379  IUSaveConfigNumber(fp, &SettingsNP);
380  return true;
381 }
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
NFocus::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: nfocus.cpp:38
NFocus::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: nfocus.cpp:228
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:657
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
NFocus::saveConfigItems
bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: nfocus.cpp:376
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
NFocus::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: nfocus.cpp:318
indicom.h
Implementations for common driver routines.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INumber
One number descriptor.
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::DefaultDevice::setDefaultPollingPeriod
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
Definition: defaultdevice.cpp:1157
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
INDI::Focuser::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: indifocuser.cpp:241
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
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
nfocus.h
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1455
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
NFocus::NFocus
NFocus()
Definition: nfocus.cpp:32
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:1156
NFocus::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: nfocus.cpp:306
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
tty_read
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:473
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
NFocus::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: nfocus.cpp:313
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:415
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::FocusMotionSP
ISwitchVectorProperty FocusMotionSP
Definition: indifocuserinterface.h:274
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
NFocus
The NFocus class Handles communication and control with nFocus DC focuser.
Definition: nfocus.h:46
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
NFocus::Handshake
virtual bool Handshake() override
perform handshake with device to check communication
Definition: nfocus.cpp:91
NFocus::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: nfocus.cpp:64
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
IUFindNumber
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indicom.c:1372
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
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
name
const char * name
Definition: indiserver.c:116
SETTINGS_TAB
#define SETTINGS_TAB
Definition: robofocus.cpp:50
IUUpdateNumber
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:225
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1424
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_write_string
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:465
TTY_OK
@ TTY_OK
Definition: indicom.h:94
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
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
NFocus::getDefaultName
const char * getDefaultName() override
Definition: nfocus.cpp:86
INDI::Focuser::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indifocuser.cpp:145