Instrument Neutral Distributed Interface INDI 2.2.5
nfocus.cpp
Go to the documentation of this file.
1/*
2 NFocus DC Relative Focuser
3
4 Copyright (C) 2019 Jasem Mutlaq ([email protected])
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
30static std::unique_ptr<NFocus> nFocus(new NFocus());
31
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 FocusRelPosNP[0].setMin(0);
55 FocusRelPosNP[0].setMax(50000);
56 FocusRelPosNP[0].setStep(1000);
57 FocusRelPosNP[0].setValue(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
106bool 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
165void 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
174bool 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
195bool 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
210bool 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
229bool 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
296bool 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.getState() == IPS_BUSY && isMoving() == false)
327 {
328 // Are we done moving?
329 if (m_TargetPosition == 0)
330 {
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 = FocusMotionSP.findOnSwitchIndex();
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.");
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
365bool 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}
double temperature
bool isConnected() const
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.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertySwitch FocusMotionSP
virtual bool saveConfigItems(FILE *fp) override
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...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
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
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
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
Performs a device-specific handshake after a connection is established. This method should be overrid...
Definition nfocus.cpp:92
bool saveConfigItems(FILE *fp) override
Definition nfocus.cpp:377
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
Definition nfocus.cpp:38
const char * getDefaultName() override
Definition nfocus.cpp:87
NFocus()
Definition nfocus.cpp:32
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
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:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
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:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
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:490
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition indidevapi.c:275
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
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:398
#define INDI_UNUSED(x)
Definition indidevapi.h:132
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#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 getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
One number descriptor.
char name[MAXINDINAME]
Definition indiapi.h:321