Instrument Neutral Distributed Interface INDI  1.9.2
aaf2.cpp
Go to the documentation of this file.
1 /*
2  Arduino ASCOM Focuser 2 (AAF2) INDI 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 "aaf2.h"
23 
24 #include "indicom.h"
25 
26 #include <cmath>
27 #include <cstring>
28 #include <memory>
29 
30 #include <termios.h>
31 #include <unistd.h>
32 
33 static std::unique_ptr<AAF2> aaf2(new AAF2());
34 
36 {
37  // Absolute, Abort, and Sync
39 
40  setVersion(1, 0);
41 }
42 
44 {
46 
47  // Focuser temperature
48  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
49  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
51 
52  // Relative and absolute movement
53  FocusRelPosN[0].min = 0.;
54  FocusRelPosN[0].max = 50000.;
55  FocusRelPosN[0].value = 0;
56  FocusRelPosN[0].step = 1000;
57 
58  FocusAbsPosN[0].min = 0.;
59  FocusAbsPosN[0].max = 100000.;
60  FocusAbsPosN[0].value = 0;
61  FocusAbsPosN[0].step = 1000;
62 
64 
65  return true;
66 }
67 
69 {
71 
72  if (isConnected())
73  {
74  defineProperty(&TemperatureNP);
75 
76  LOG_INFO("Focuser ready.");
77  }
78  else
79  {
80  deleteProperty(TemperatureNP.name);
81  }
82 
83  return true;
84 }
85 
87 {
88  if (Ack())
89  {
90  LOG_INFO("AAF2 is online.");
91 
92  readVersion();
93 
94  return true;
95  }
96 
97  LOG_INFO("Error retrieving data from AAF2, please ensure AAF2 controller is powered and the port is correct.");
98  return false;
99 }
100 
101 const char * AAF2::getDefaultName()
102 {
103  return "AAF2";
104 }
105 
106 bool AAF2::Ack()
107 {
108  int nbytes_written = 0, nbytes_read = 0, rc = -1;
109  char errstr[MAXRBUF];
110  char resp[5] = {0};
111 
112  tcflush(PortFD, TCIOFLUSH);
113 
114  int numChecks = 0;
115  bool success = false;
116  while (numChecks < 3 && !success)
117  {
118  numChecks++;
119  //wait 1 second between each test.
120  sleep(1);
121 
122  bool transmissionSuccess = (rc = tty_write(PortFD, "#", 1, &nbytes_written)) == TTY_OK;
123  if(!transmissionSuccess)
124  {
125  tty_error_msg(rc, errstr, MAXRBUF);
126  LOGF_ERROR("Handshake Attempt %i, tty transmission error: %s.", numChecks, errstr);
127  }
128 
129  bool responseSuccess = (rc = tty_read(PortFD, resp, 4, DRIVER_TIMEOUT, &nbytes_read)) == TTY_OK;
130  if(!responseSuccess)
131  {
132  tty_error_msg(rc, errstr, MAXRBUF);
133  LOGF_ERROR("Handshake Attempt %i, updatePosition response error: %s.", numChecks, errstr);
134  }
135 
136  success = transmissionSuccess && responseSuccess;
137  }
138 
139  if(!success)
140  {
141  LOG_INFO("Handshake failed after 3 attempts");
142  return false;
143  }
144 
145  tcflush(PortFD, TCIOFLUSH);
146 
147  return !strcmp(resp, "OK!#");
148 }
149 
150 
151 bool AAF2::readTemperature()
152 {
153  char res[DRIVER_RES] = {0};
154 
155  if (sendCommand("C#", res) == false)
156  return false;
157 
158  int32_t temp = 0;
159  int rc = sscanf(res, "C%d:OK", &temp);
160  if (rc > 0)
161  // Hundredth of a degree
162  TemperatureN[0].value = temp / 100.0;
163  else
164  {
165  LOGF_ERROR("Unknown error: focuser temperature value (%s)", res);
166  return false;
167  }
168 
169  return true;
170 }
171 
172 bool AAF2::readVersion()
173 {
174  char res[DRIVER_RES] = {0};
175 
176  if (sendCommand("V#", res) == false)
177  return false;
178 
179  LOGF_INFO("Detected %s", res);
180 
181  return true;
182 }
183 
184 bool AAF2::readPosition()
185 {
186  char res[DRIVER_RES] = {0};
187 
188  if (sendCommand("P#", res) == false)
189  return false;
190 
191  int32_t pos;
192  int rc = sscanf(res, "P%d:OK", &pos);
193 
194  if (rc > 0)
195  FocusAbsPosN[0].value = pos;
196  else
197  {
198  LOGF_ERROR("Unknown error: focuser position value (%s)", res);
199  return false;
200  }
201 
202  return true;
203 }
204 
205 bool AAF2::isMoving()
206 {
207  char res[DRIVER_RES] = {0};
208 
209  if (sendCommand("M#", res) == false)
210  return false;
211 
212  if (strcmp(res, "M1:OK") == 0)
213  return true;
214  else if (strcmp(res, "M0:OK") == 0)
215  return false;
216 
217  LOGF_ERROR("Unknown error: isMoving value (%s)", res);
218  return false;
219 }
220 
221 
222 bool AAF2::SyncFocuser(uint32_t ticks)
223 {
224  char cmd[DRIVER_RES] = {0};
225  snprintf(cmd, DRIVER_RES, "I%d#", ticks);
226  return sendCommand(cmd);
227 }
228 
229 IPState AAF2::MoveAbsFocuser(uint32_t targetTicks)
230 {
231  char cmd[DRIVER_RES] = {0}, res[DRIVER_RES] = {0}, expected[DRIVER_RES] = {0};
232  snprintf(cmd, DRIVER_RES, "T%d#", targetTicks);
233  snprintf(expected, DRIVER_RES, "T%d:OK", targetTicks);
234  if (sendCommand(cmd, res) == false)
235  return IPS_ALERT;
236 
237  targetPos = targetTicks;
238 
239  if (!strcmp(res, expected))
240  return IPS_BUSY;
241  else
242  return IPS_ALERT;
243 }
244 
246 {
247  int32_t newPosition = 0;
248 
249  if (dir == FOCUS_INWARD)
250  newPosition = FocusAbsPosN[0].value - ticks;
251  else
252  newPosition = FocusAbsPosN[0].value + ticks;
253 
254  // Clamp
255  newPosition = std::max(0, std::min(static_cast<int32_t>(FocusAbsPosN[0].max), newPosition));
256  if (MoveAbsFocuser(newPosition) != IPS_BUSY)
257  return IPS_ALERT;
258 
259  FocusRelPosN[0].value = ticks;
261 
262  return IPS_BUSY;
263 }
264 
266 {
267  if (!isConnected())
268  {
270  return;
271  }
272 
273  bool rc = readPosition();
274  if (rc)
275  {
276  if (fabs(lastPos - FocusAbsPosN[0].value) > 5)
277  {
278  IDSetNumber(&FocusAbsPosNP, nullptr);
279  lastPos = FocusAbsPosN[0].value;
280  }
281  }
282 
283  rc = readTemperature();
284  if (rc)
285  {
286  if (fabs(lastTemperature - TemperatureN[0].value) >= 0.5)
287  {
288  IDSetNumber(&TemperatureNP, nullptr);
289  lastTemperature = TemperatureN[0].value;
290  }
291  }
292 
294  {
295  if (!isMoving())
296  {
299  IDSetNumber(&FocusAbsPosNP, nullptr);
300  IDSetNumber(&FocusRelPosNP, nullptr);
301  lastPos = FocusAbsPosN[0].value;
302  LOG_INFO("Focuser reached requested position.");
303  }
304  }
305 
307 }
308 
310 {
311  return sendCommand("H#");
312 }
313 
314 
315 bool AAF2::sendCommand(const char * cmd, char * res)
316 {
317  int nbytes_written = 0, nbytes_read = 0, rc = -1;
318 
319  tcflush(PortFD, TCIOFLUSH);
320 
321  LOGF_DEBUG("CMD <%s>", cmd);
322 
323  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
324  {
325  char errstr[MAXRBUF] = {0};
326  tty_error_msg(rc, errstr, MAXRBUF);
327  LOGF_ERROR("Serial write error: %s.", errstr);
328  return false;
329  }
330 
331  if (res == nullptr)
332  return true;
333 
334  if ((rc = tty_nread_section(PortFD, res, DRIVER_RES, DRIVER_DEL, DRIVER_TIMEOUT, &nbytes_read)) != TTY_OK)
335  {
336  char errstr[MAXRBUF] = {0};
337  tty_error_msg(rc, errstr, MAXRBUF);
338  LOGF_ERROR("Serial read error: %s.", errstr);
339  return false;
340  }
341 
342  // Remove the #
343  res[nbytes_read - 1] = 0;
344 
345  LOGF_DEBUG("RES <%s>", res);
346 
347  tcflush(PortFD, TCIOFLUSH);
348 
349  return true;
350 }
AAF2::SyncFocuser
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set the supplied position as the current focuser position.
Definition: aaf2.cpp:222
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
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
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
min
double min(void)
indicom.h
Implementations for common driver routines.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
AAF2::AAF2
AAF2()
Definition: aaf2.cpp:35
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
aaf2.h
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
INDI::FocuserInterface::FOCUSER_CAN_SYNC
@ FOCUSER_CAN_SYNC
Definition: indifocuserinterface.h:78
AAF2::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveRelFocuser Move focuser for a relative amount of ticks in a specific direction.
Definition: aaf2.cpp:245
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
AAF2::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveAbsFocuser Move to an absolute target position.
Definition: aaf2.cpp:229
AAF2::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: aaf2.cpp:309
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
max
double max(void)
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
tty_read
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:462
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
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
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
AAF2::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: aaf2.cpp:68
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
AAF2::Handshake
virtual bool Handshake() override
Handshake Try to communicate with Focuser and see if there is a valid response.
Definition: aaf2.cpp:86
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
AAF2::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: aaf2.cpp:265
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
AAF2
Definition: aaf2.h:26
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
AAF2::getDefaultName
const char * getDefaultName() override
Definition: aaf2.cpp:101
AAF2::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: aaf2.cpp:43
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_write_string
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:454
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
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.