Instrument Neutral Distributed Interface INDI  2.0.2
deepskydad_fr1.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2020 Jasem Mutlaq. All rights reserved.
3 
4  Deep Sky Dad Field Rotator 1
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the Free
8  Software Foundation; either version 2 of the License, or (at your option)
9  any later version.
10 
11  This program is distributed in the hope that it will be useful, but WITHOUT
12  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 
21  The full GNU General Public License is included in this distribution in the
22  file called LICENSE.
23 *******************************************************************************/
24 
25 #include "deepskydad_fr1.h"
26 #include "indicom.h"
28 
29 #include <cerrno>
30 #include <cstring>
31 #include <memory>
32 #include <termios.h>
33 #include <unistd.h>
34 #include <inttypes.h>
35 #include <sys/ioctl.h>
36 #include <cmath>
37 
38 #define DSD_CMD 40
39 #define DSD_RES 40
40 #define DSD_TIMEOUT 3
41 
42 // We declare an auto pointer to DeepSkyDadFR1.
43 static std::unique_ptr<DeepSkyDadFR1> dsdFR1(new DeepSkyDadFR1());
44 
46 {
47  setVersion(1, 0);
48 }
49 
51 {
53 
57 
59 
60  // Speed mode
61  IUFillSwitch(&SpeedModeS[Slow], "SLOW", "Slow", ISS_OFF);
62  IUFillSwitch(&SpeedModeS[Fast], "FAST", "Fast", ISS_OFF);
63  IUFillSwitchVector(&SpeedModeSP, SpeedModeS, 2, getDeviceName(), "Speed mode", "Speed mode", MAIN_CONTROL_TAB, IP_RW,
64  ISR_1OFMANY, 0, IPS_IDLE);
65 
66  // Step mode
67  IUFillSwitch(&StepSizeS[One], "1", "1", ISS_OFF);
68  IUFillSwitch(&StepSizeS[Two], "2", "1/2", ISS_OFF);
69  IUFillSwitch(&StepSizeS[Four], "4", "1/4", ISS_OFF);
70  IUFillSwitch(&StepSizeS[Eight], "8", "1/8", ISS_OFF);
71  IUFillSwitchVector(&StepSizeSP, StepSizeS, 4, getDeviceName(), "Step mode", "Step mode", MAIN_CONTROL_TAB, IP_RW,
72  ISR_1OFMANY, 0, IPS_IDLE);
73 
74  // Firmware version
75  IUFillText(&FirmwareT[0], "Version", "Version", nullptr);
76  IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
77 
78  serialConnection->setDefaultPort("/dev/ttyACM0");
80  {
81  return Handshake();
82  });
84  return true;
85 }
86 
88 {
90 
91  if (isConnected())
92  {
93  defineProperty(&SpeedModeSP);
94  defineProperty(&StepSizeSP);
95  defineProperty(&FirmwareTP);
96  }
97  else
98  {
99  deleteProperty(SpeedModeSP.name);
100  deleteProperty(StepSizeSP.name);
101  deleteProperty(FirmwareTP.name);
102  }
103 
104  return true;
105 }
106 
108 {
109  return "Deep Sky Dad FR1";
110 }
111 
112 bool DeepSkyDadFR1::Handshake()
113 {
115  return getInitialStatusData();
116 }
117 
118 bool DeepSkyDadFR1::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
119 {
120  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
121  {
122  if (strcmp(SpeedModeSP.name, name) == 0)
123  {
124  int current_mode = IUFindOnSwitchIndex(&SpeedModeSP);
125 
126  IUUpdateSwitch(&SpeedModeSP, states, names, n);
127 
128  int target_mode = IUFindOnSwitchIndex(&SpeedModeSP);
129 
130  if (current_mode == target_mode)
131  {
132  SpeedModeSP.s = IPS_OK;
133  IDSetSwitch(&SpeedModeSP, nullptr);
134  return true;
135  }
136 
137  char cmd[DSD_RES] = {0};
138  char response[DSD_RES] = {0};
139 
140  snprintf(cmd, DSD_RES, "[SSPD%d]", target_mode);
141  bool rc = sendCommand(cmd, response);
142  if (!rc)
143  {
144  IUResetSwitch(&SpeedModeSP);
145  SpeedModeS[current_mode].s = ISS_ON;
146  SpeedModeSP.s = IPS_ALERT;
147  IDSetSwitch(&SpeedModeSP, nullptr);
148  return false;
149  }
150 
151  SpeedModeSP.s = IPS_OK;
152  IDSetSwitch(&SpeedModeSP, nullptr);
153  return true;
154  }
155  else if (strcmp(StepSizeSP.name, name) == 0)
156  {
157  int current_mode = IUFindOnSwitchIndex(&StepSizeSP);
158 
159  IUUpdateSwitch(&StepSizeSP, states, names, n);
160 
161  int target_mode = IUFindOnSwitchIndex(&StepSizeSP);
162 
163  if (current_mode == target_mode)
164  {
165  StepSizeSP.s = IPS_OK;
166  IDSetSwitch(&StepSizeSP, nullptr);
167  return true;
168  }
169 
170  char cmd[DSD_RES] = {0};
171  char response[DSD_RES] = {0};
172 
173  snprintf(cmd, DSD_RES, "[SSTP%d]", target_mode);
174  bool rc = sendCommand(cmd, response);
175  if (!rc)
176  {
177  IUResetSwitch(&StepSizeSP);
178  StepSizeS[current_mode].s = ISS_ON;
179  StepSizeSP.s = IPS_ALERT;
180  IDSetSwitch(&StepSizeSP, nullptr);
181  return false;
182  }
183 
184  StepSizeSP.s = IPS_OK;
185  IDSetSwitch(&StepSizeSP, nullptr);
186  return true;
187  }
188  }
189 
190  return Rotator::ISNewSwitch(dev, name, states, names, n);
191 }
192 
194 {
195  char response[DSD_RES];
196  char cmd[DSD_CMD];
197  int angleInt = (int)(angle * 100);
198  snprintf(cmd, DSD_CMD, "[STRG%d]", angleInt);
199  if (!sendCommand(cmd, response) || !sendCommand("[SMOV]", response))
200  return IPS_ALERT;
201 
202  if (strcmp(response, "(OK)") == 0)
203  {
204  return IPS_BUSY;
205  }
206  else
207  return IPS_ALERT;
208 }
209 
211 {
212  char response[DSD_RES];
213  if (!sendCommand("[STOP]", response))
214  return false;
215 
216  if (strcmp(response, "(OK)") == 0)
217  {
218  return true;
219  }
220  else
221  return false;
222 }
223 
225 {
226  char response[DSD_RES];
227  char cmd[DSD_CMD];
228  snprintf(cmd, DSD_CMD, "[SREV%d]", enabled ? 1 : 0);
229  if (!sendCommand(cmd, response))
230  return false;
231 
232  if (strcmp(response, "(OK)") == 0)
233  {
234  return true;
235  }
236  else
237  return false;
238 }
239 
240 bool DeepSkyDadFR1::SyncRotator(double angle)
241 {
242  char response[DSD_RES];
243  char cmd[DSD_CMD];
244  int angleInt = (int)(angle * 100);
245  snprintf(cmd, DSD_CMD, "[SPOS%d]", angleInt);
246  if (!sendCommand(cmd, response))
247  return false;
248 
249  if (strcmp(response, "(OK)") == 0)
250  {
251  return true;
252  }
253  else
254  return false;
255 }
256 
258 {
259  if (!isConnected())
260  return;
261  getStatusData();
263 }
264 
265 bool DeepSkyDadFR1::getStatusData()
266 {
267  char response[DSD_RES];
268 
269  int motorStatus;
270  int motorPosition;
271 
272  if (!sendCommand("[GMOV]", response))
273  return false;
274  else
275  sscanf(response, "(%d)", &motorStatus);
276 
277  if (!sendCommand("[GPOS]", response))
278  return false;
279  else
280  sscanf(response, "(%d)", &motorPosition);
281 
282 
283 
284  const IPState motionState = motorStatus == 1 ? IPS_BUSY : IPS_OK;
285 
286  double motorPositionDouble = (double)motorPosition / (double)100;
287  if (std::abs(motorPositionDouble - GotoRotatorN[0].value) > 0.01 || GotoRotatorNP.s != motionState)
288  {
289  GotoRotatorN[0].value = motorPositionDouble;
290  GotoRotatorNP.s = motionState;
291  IDSetNumber(&GotoRotatorNP, nullptr);
292  }
293 
294  return true;
295 }
296 
297 bool DeepSkyDadFR1::getInitialStatusData()
298 {
299  char response[DSD_RES] = {0};
300  if (!sendCommand("[GFRM]", response))
301  return false;
302 
303  char versionString[6] = { 0 };
304  snprintf(versionString, 6, "%s", response + 31);
305  IUSaveText(&FirmwareT[0], response);
306  IDSetText(&FirmwareTP, nullptr);
307 
308  int motorReversed;
309 
310  if (!sendCommand("[GREV]", response))
311  return false;
312  else
313  sscanf(response, "(%d)", &motorReversed);
314 
315  const bool wasReversed = ReverseRotatorS[INDI_ENABLED].s == ISS_ON;
316  if (motorReversed != wasReversed)
317  {
318  ReverseRotatorS[INDI_ENABLED].s = motorReversed ? ISS_ON : ISS_OFF;
319  ReverseRotatorS[INDI_DISABLED].s = motorReversed ? ISS_OFF : ISS_ON;
320  IDSetSwitch(&ReverseRotatorSP, nullptr);
321  }
322 
323  if (!sendCommand("[GSPD]", response))
324  return false;
325 
326  if(strcmp(response, "(2)") == 0)
327  SpeedModeS[Slow].s = ISS_ON;
328  else if(strcmp(response, "(3)") == 0)
329  SpeedModeS[Fast].s = ISS_ON;
330 
331  if (!sendCommand("[GSTP]", response))
332  return false;
333 
334  if(strcmp(response, "(1)") == 0)
335  StepSizeS[One].s = ISS_ON;
336  else if(strcmp(response, "(2)") == 0)
337  StepSizeS[Two].s = ISS_ON;
338  else if(strcmp(response, "(4)") == 0)
339  StepSizeS[Four].s = ISS_ON;
340  else if(strcmp(response, "(8)") == 0)
341  StepSizeS[Eight].s = ISS_ON;
342 
343  return true;
344 }
345 
349 bool DeepSkyDadFR1::sendCommand(const char *cmd, char *res)
350 {
351  int nbytes_written = 0, nbytes_read = 0, rc = -1;
352 
353  tcflush(PortFD, TCIOFLUSH);
354 
355  LOGF_DEBUG("CMD <%s>", cmd);
356 
357  if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
358  {
359  char errstr[MAXRBUF] = {0};
360  tty_error_msg(rc, errstr, MAXRBUF);
361  LOGF_ERROR("Serial write error: %s.", errstr);
362  return false;
363  }
364 
365  if (res == nullptr)
366  return true;
367 
368  if ((rc = tty_nread_section(PortFD, res, DSD_RES, ')', DSD_TIMEOUT, &nbytes_read)) != TTY_OK)
369  {
370  char errstr[MAXRBUF] = {0};
371  tty_error_msg(rc, errstr, MAXRBUF);
372  LOGF_ERROR("Serial read error: %s.", errstr);
373  return false;
374  }
375 
376  LOGF_DEBUG("RES <%s>", res);
377 
378  tcflush(PortFD, TCIOFLUSH);
379 
380  return true;
381 }
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
virtual bool AbortRotator() override
AbortRotator Abort all motion.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
const char * getDefaultName() override
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INumberVectorProperty GotoRotatorNP
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
ISwitchVectorProperty ReverseRotatorSP
Connection::Serial * serialConnection
Definition: indirotator.h:95
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indirotator.cpp:37
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indirotator.cpp:94
#define DSD_CMD
#define DSD_RES
#define DSD_TIMEOUT
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ 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
@ ISR_1OFMANY
Definition: indiapi.h:173
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
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
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: indidevapi.c:158
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
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: indidevapi.c:235
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define MAXRBUF
Definition: indiserver.cpp:102
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250