Instrument Neutral Distributed Interface INDI  1.9.5
pegasus_falcon.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2020 Jasem Mutlaq. All rights reserved.
3 
4  Pegasus Falcon Rotator
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 "pegasus_falcon.h"
26 #include "indicom.h"
27 
28 #include <cmath>
29 #include <memory>
30 #include <regex>
31 #include <termios.h>
32 #include <cstring>
33 #include <sys/ioctl.h>
34 #include <chrono>
35 #include <math.h>
36 #include <iomanip>
37 
38 // We declare an auto pointer to PegasusFalcon.
39 static std::unique_ptr<PegasusFalcon> falcon(new PegasusFalcon());
40 
42 {
43  setVersion(1, 0);
44  lastStatusData.reserve(7);
45 }
46 
48 {
50 
54 
56 
60  // Reload Firmware
61  IUFillSwitch(&ReloadFirmwareS[0], "RELOAD", "Reload", ISS_OFF);
62  IUFillSwitchVector(&ReloadFirmwareSP, ReloadFirmwareS, 1, getDeviceName(), "RELOAD_FIRMWARE", "Firmware", MAIN_CONTROL_TAB,
64  60, IPS_IDLE);
65 
66  // Derotate
67  IUFillNumber(&DerotateN[0], "INTERVAL", "Interval (ms)", "%.f", 0, 10000, 1000, 0);
68  IUFillNumberVector(&DerotateNP, DerotateN, 1, getDeviceName(), "ROTATOR_DEROTATE", "Derotation", MAIN_CONTROL_TAB, IP_RW,
69  60, IPS_IDLE);
70 
71  // Firmware
72  IUFillText(&FirmwareT[0], "VERSION", "Version", "NA");
73  IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "FIRMWARE_INFO", "Firmware", MAIN_CONTROL_TAB, IP_RO, 60,
74  IPS_IDLE);
75 
76  return true;
77 }
78 
80 {
82 
83  if (isConnected())
84  {
85  // Main Control
86  defineProperty(&DerotateNP);
87  defineProperty(&FirmwareTP);
88  defineProperty(&ReloadFirmwareSP);
89 
90  }
91  else
92  {
93  // Main Control
94  deleteProperty(DerotateNP.name);
95  deleteProperty(FirmwareTP.name);
96  deleteProperty(ReloadFirmwareSP.name);
97  }
98 
99  return true;
100 }
101 
103 {
104  return "Pegasus Falcon";
105 }
106 
110 bool PegasusFalcon::Handshake()
111 {
112  return getFirmware();
113 }
114 
118 bool PegasusFalcon::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
119 {
120  if (dev && !strcmp(dev, getDeviceName()))
121  {
122  // De-rotation
123  if (!strcmp(name, DerotateNP.name))
124  {
125  const uint32_t ms = static_cast<uint32_t>(values[0]);
126  if (setDerotation(ms))
127  {
128  DerotateN[0].value = values[0];
129  if (values[0] > 0)
130  LOGF_INFO("De-rotation is enabled and set to 1 step per %u milliseconds.", ms);
131  else
132  LOG_INFO("De-rotaiton is disabled.");
133  }
134  else
135  DerotateNP.s = IPS_ALERT;
136  IDSetNumber(&DerotateNP, nullptr);
137  return true;
138  }
139  }
140 
141  return Rotator::ISNewNumber(dev, name, values, names, n);
142 }
143 
147 bool PegasusFalcon::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
148 {
149  if (dev && !strcmp(dev, getDeviceName()))
150  {
151  // ReloadFirmware
152  if (!strcmp(name, ReloadFirmwareSP.name))
153  {
154  ReloadFirmwareSP.s = reloadFirmware() ? IPS_OK : IPS_ALERT;
155  IDSetSwitch(&ReloadFirmwareSP, nullptr);
156  LOG_INFO("Reloading firmware...");
157  return true;
158  }
159  }
160 
161  return Rotator::ISNewSwitch(dev, name, states, names, n);
162 }
163 
168 {
169  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
170  snprintf(cmd, DRIVER_LEN, "MD:%.2f", angle);
171  if (sendCommand(cmd, res))
172  {
173  return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
174  }
175 
176  return IPS_ALERT;
177 }
178 
183 {
184  char res[DRIVER_LEN] = {0};
185  if (sendCommand("FH", res))
186  {
187  return (!strcmp(res, "FH:1"));
188  }
189 
190  return false;
191 }
192 
197 {
198  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
199  snprintf(cmd, DRIVER_LEN, "FR:%d", enabled ? 1 : 0);
200  if (sendCommand(cmd, res))
201  {
202  return (!strcmp(res, cmd));
203  }
204 
205  return false;
206 }
207 
211 bool PegasusFalcon::SyncRotator(double angle)
212 {
213  char cmd[DRIVER_LEN] = {0};
214  snprintf(cmd, DRIVER_LEN, "SD:%.2f", angle);
215  return sendCommand(cmd, nullptr);
216 }
217 
221 bool PegasusFalcon::reloadFirmware()
222 {
223  return sendCommand("FF");
224 }
225 
229 bool PegasusFalcon::setDerotation(uint32_t ms)
230 {
231  char cmd[DRIVER_LEN] = {0};
232  snprintf(cmd, DRIVER_LEN, "DR:%d", ms);
233  return sendCommand(cmd, nullptr);
234 }
235 
240 {
242  IUSaveConfigNumber(fp, &DerotateNP);
243  return true;
244 }
245 
250 {
251  if (!isConnected())
252  return;
253  getStatusData();
255 }
256 
260 bool PegasusFalcon::getFirmware()
261 {
262  char res[DRIVER_LEN] = {0};
263  if (sendCommand("FV", res))
264  {
265  IUSaveText(&FirmwareT[0], res + 3);
266  return true;
267  }
268 
269  return false;
270 }
271 
275 bool PegasusFalcon::getStatusData()
276 {
277  char res[DRIVER_LEN] = {0};
278  if (sendCommand("FA", res))
279  {
280  std::vector<std::string> result = split(res, ":");
281  if (result.size() != 7)
282  {
283  LOG_WARN("Received wrong number of detailed sensor data. Retrying...");
284  return false;
285  }
286 
287  if (result == lastStatusData)
288  return true;
289 
290  // Position
291  const double position = std::stod(result[2]);
292  // Is running?
293  const IPState motionState = std::stoi(result[3]) == 1 ? IPS_BUSY : IPS_OK;
294 
295  // Update Absolute Position property if either position changes, or status changes.
296  if (std::abs(position - GotoRotatorN[0].value) > 0.01 || GotoRotatorNP.s != motionState)
297  {
298  GotoRotatorN[0].value = position;
299  GotoRotatorNP.s = motionState;
300  IDSetNumber(&GotoRotatorNP, nullptr);
301  }
302 
303  // TODO add this later to properties (Light?)
304  //const bool limit = std::stoi(result[4]) == 1;
305 
306  const bool derotation = std::stoi(result[5]) == 1;
307  const bool wasDerotated = DerotateN[0].value > 0;
308  // TODO check if we get value from firmware
309  if (derotation != wasDerotated)
310  {
311  DerotateNP.s = derotation ? IPS_BUSY : IPS_IDLE;;
312  IDSetNumber(&DerotateNP, nullptr);
313  }
314 
315  const bool reversed = std::stoi(result[6]) == 1;
316  const bool wasReversed = ReverseRotatorS[INDI_ENABLED].s == ISS_ON;
317  if (reversed != wasReversed)
318  {
319  ReverseRotatorS[INDI_ENABLED].s = reversed ? ISS_ON : ISS_OFF;
320  ReverseRotatorS[INDI_DISABLED].s = reversed ? ISS_OFF : ISS_ON;
321  IDSetSwitch(&ReverseRotatorSP, nullptr);
322  }
323 
324  lastStatusData = result;
325  return true;
326  }
327 
328  return false;
329 }
330 
334 bool PegasusFalcon::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
335 {
336  int nbytes_written = 0, nbytes_read = 0, rc = -1;
337 
338  tcflush(PortFD, TCIOFLUSH);
339 
340  if (cmd_len > 0)
341  {
342  char hex_cmd[DRIVER_LEN * 3] = {0};
343  hexDump(hex_cmd, cmd, cmd_len);
344  LOGF_DEBUG("CMD <%s>", hex_cmd);
345  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
346  }
347  else
348  {
349  LOGF_DEBUG("CMD <%s>", cmd);
350 
351  char formatted_command[DRIVER_LEN] = {0};
352  snprintf(formatted_command, DRIVER_LEN, "%s\n", cmd);
353  rc = tty_write_string(PortFD, formatted_command, &nbytes_written);
354  }
355 
356  if (rc != TTY_OK)
357  {
358  char errstr[MAXRBUF] = {0};
359  tty_error_msg(rc, errstr, MAXRBUF);
360  LOGF_ERROR("Serial write error: %s.", errstr);
361  return false;
362  }
363 
364  if (res == nullptr)
365  return true;
366 
367  if (res_len > 0)
368  rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
369  else
370  rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read);
371 
372  if (rc != TTY_OK)
373  {
374  char errstr[MAXRBUF] = {0};
375  tty_error_msg(rc, errstr, MAXRBUF);
376  LOGF_ERROR("Serial read error: %s.", errstr);
377  return false;
378  }
379 
380  if (res_len > 0)
381  {
382  char hex_res[DRIVER_LEN * 3] = {0};
383  hexDump(hex_res, res, res_len);
384  LOGF_DEBUG("RES <%s>", hex_res);
385  }
386  else
387  {
388  // Remove extra \r
389  res[nbytes_read - 1] = 0;
390  LOGF_DEBUG("RES <%s>", res);
391  }
392 
393  tcflush(PortFD, TCIOFLUSH);
394 
395  return true;
396 }
397 
401 void PegasusFalcon::hexDump(char * buf, const char * data, uint32_t size)
402 {
403  for (uint32_t i = 0; i < size; i++)
404  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
405 
406  if (size > 0)
407  buf[3 * size - 1] = '\0';
408 }
409 
413 std::vector<std::string> PegasusFalcon::split(const std::string &input, const std::string &regex)
414 {
415  // passing -1 as the submatch index parameter performs splitting
416  std::regex re(regex);
417  std::sregex_token_iterator
418  first{input.begin(), input.end(), re, -1},
419  last;
420  return {first, last};
421 }
422 
426 void PegasusFalcon::cleanupResponse(char *response)
427 {
428  std::string s(response);
429  s.erase(std::remove_if(s.begin(), s.end(),
430  [](unsigned char x)
431  {
432  return std::isspace(x);
433  }), s.end());
434  strncpy(response, s.c_str(), DRIVER_LEN);
435 }
IP_RO
@ IP_RO
Definition: indiapi.h:183
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
INDI::DefaultDevice::addAuxControls
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Definition: defaultdevice.cpp:665
PegasusFalcon::getDefaultName
const char * getDefaultName() override
Definition: pegasus_falcon.cpp:102
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
PegasusFalcon::MoveRotator
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition: pegasus_falcon.cpp:167
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
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
indicom.h
Implementations for common driver routines.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
INDI::Rotator::PortFD
int PortFD
Definition: indirotator.h:98
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
PegasusFalcon
Definition: pegasus_falcon.h:30
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
PegasusFalcon::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: pegasus_falcon.cpp:249
INDI::RotatorInterface::ROTATOR_CAN_ABORT
@ ROTATOR_CAN_ABORT
Definition: indirotatorinterface.h:55
IUFillTextVector
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: indidriver.c:477
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::RotatorInterface::GotoRotatorN
INumber GotoRotatorN[1]
Definition: indirotatorinterface.h:197
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1455
IUFillText
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: indidriver.c:369
PegasusFalcon::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the reverse direction property in the configuration file
Definition: pegasus_falcon.cpp:239
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
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
PegasusFalcon::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: pegasus_falcon.cpp:147
PegasusFalcon::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: pegasus_falcon.cpp:118
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
ISNewNumber
void ISNewNumber(const char *dev, const char *name, double *values, char *names[], int n)
Update the value of an existing number vector property.
tty_read
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:473
INDI::Rotator::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indirotator.cpp:53
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
PegasusFalcon::ReverseRotator
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition: pegasus_falcon.cpp:196
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
tty_write
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:415
IUFillSwitchVector
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: indidriver.c:412
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
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
PegasusFalcon::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: pegasus_falcon.cpp:47
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
ISR_ATMOST1
@ ISR_ATMOST1
Definition: indiapi.h:173
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
INDI::RotatorInterface::ROTATOR_CAN_SYNC
@ ROTATOR_CAN_SYNC
Definition: indirotatorinterface.h:57
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
ISNewSwitch
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
Definition: defaultdevice.cpp:60
INDI::RotatorInterface::GotoRotatorNP
INumberVectorProperty GotoRotatorNP
Definition: indirotatorinterface.h:198
PegasusFalcon::SyncRotator
virtual bool SyncRotator(double angle) override
SyncRotator Set current angle as the supplied angle without moving the rotator.
Definition: pegasus_falcon.cpp:211
PegasusFalcon::PegasusFalcon
PegasusFalcon()
Definition: pegasus_falcon.cpp:41
INDI::Rotator::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the reverse direction property in the configuration file
Definition: indirotator.cpp:196
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
INDI::RotatorInterface::ROTATOR_CAN_REVERSE
@ ROTATOR_CAN_REVERSE
Definition: indirotatorinterface.h:58
INDI::Rotator::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indirotator.cpp:110
PegasusFalcon::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: pegasus_falcon.cpp:79
INDI::RotatorInterface::SetCapability
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
Definition: indirotatorinterface.h:74
IUSaveText
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1449
name
const char * name
Definition: indiserver.c:116
INDI::RotatorInterface::ReverseRotatorSP
ISwitchVectorProperty ReverseRotatorSP
Definition: indirotatorinterface.h:210
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
pegasus_falcon.h
PegasusFalcon::AbortRotator
virtual bool AbortRotator() override
AbortRotator Abort all motion.
Definition: pegasus_falcon.cpp:182
ISState
ISState
Switch state.
Definition: indiapi.h:148
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::RotatorInterface::ReverseRotatorS
ISwitch ReverseRotatorS[2]
Definition: indirotatorinterface.h:209
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.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
IUFillSwitch
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: indidriver.c:320
INDI::BaseDevice::INDI_DISABLED
@ INDI_DISABLED
Definition: basedevice.h:65
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151