Instrument Neutral Distributed Interface INDI  2.0.2
pegasus_prodigyMF.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3 
4  Pegasus ProdigyMF
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 #include "pegasus_prodigyMF.h"
25 
26 #include "indicom.h"
28 
29 #include <cmath>
30 #include <cstring>
31 #include <memory>
32 
33 #include <termios.h>
34 #include <unistd.h>
35 
36 #define DMFC_TIMEOUT 3
37 #define FOCUS_SETTINGS_TAB "Settings"
38 #define TEMPERATURE_THRESHOLD 0.1
39 
40 static std::unique_ptr<PegasusProdigyMF> focusCube(new PegasusProdigyMF());
41 
42 
44 {
45  // Can move in Absolute & Relative motions, can AbortFocuser motion.
51 }
52 
54 {
56 
57  // Focuser temperature
58  IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -50, 70., 0., 0.);
59  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
61 
62  // Max Speed
63  IUFillNumber(&MaxSpeedN[0], "Value", "", "%6.2f", 100, 1000., 100., 400.);
64  IUFillNumberVector(&MaxSpeedNP, MaxSpeedN, 1, getDeviceName(), "MaxSpeed", "", FOCUS_SETTINGS_TAB, IP_RW, 0, IPS_IDLE);
65 
66 
67 
68  // Firmware Version
69  IUFillText(&FirmwareVersionT[0], "Version", "Version", "");
70  IUFillTextVector(&FirmwareVersionTP, FirmwareVersionT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO,
71  0, IPS_IDLE);
72 
73  // Relative and absolute movement
74  FocusRelPosN[0].min = 0.;
75  FocusRelPosN[0].max = 20000.;
76  FocusRelPosN[0].value = 0;
77  FocusRelPosN[0].step = 1000;
78 
79  FocusAbsPosN[0].max = 20000.;
80  FocusAbsPosN[0].min = 0.;
81  FocusAbsPosN[0].value = 0;
82  FocusAbsPosN[0].step = 1000;
83 
84  FocusMaxPosN[0].max = 2000;
85  FocusMaxPosN[0].value = 2000;
86  FocusAbsPosN[0].min = 0;
87 
88 
92  return true;
93 }
94 
96 {
98 
99  if (isConnected())
100  {
101  defineProperty(&MaxSpeedNP);
102  defineProperty(&TemperatureNP);
103  defineProperty(&FirmwareVersionTP);
104  }
105  else
106  {
107  deleteProperty(MaxSpeedNP.name);
108  deleteProperty(TemperatureNP.name);
109  deleteProperty(FirmwareVersionTP.name);
110  }
111 
112  return true;
113 }
114 
116 {
117  if (ack())
118  {
119  LOGF_INFO("%s is online. Getting focus parameters...", this->getDeviceName());
120  return true;
121  }
122 
123  LOGF_INFO(
124  "Error retrieving data from %s, please ensure device is powered and the port is correct.", this->getDeviceName());
125  return false;
126 }
127 
129 {
130  return "Pegasus ProdigyMF";
131 }
132 
133 bool PegasusProdigyMF::ack()
134 {
135  int nbytes_written = 0, nbytes_read = 0, rc = -1;
136  char errstr[MAXRBUF];
137  char cmd[2] = {0};
138  char res[16] = {0};
139 
140  cmd[0] = '#';
141  cmd[1] = 0xA;
142 
143  LOGF_DEBUG("CMD <%#02X>", cmd[0]);
144 
145  tcflush(PortFD, TCIOFLUSH);
146 
147  if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
148  {
149  tty_error_msg(rc, errstr, MAXRBUF);
150  LOGF_ERROR("Ack error: %s.", errstr);
151  return false;
152  }
153 
154  if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
155  {
156  tty_error_msg(rc, errstr, MAXRBUF);
157  LOGF_ERROR("Ack error: %s.", errstr);
158  return false;
159  }
160 
161  // Get rid of 0xA
162  res[nbytes_read - 1] = 0;
163 
164 
165  if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
166 
167  LOGF_DEBUG("RES <%s>", res);
168 
169  tcflush(PortFD, TCIOFLUSH);
170 
171  if(strstr(res, "OK_PRDG") != nullptr)
172  return true;
173 
174  return false;
175 }
176 
177 
178 bool PegasusProdigyMF::SyncFocuser(uint32_t ticks)
179 {
180  int nbytes_written = 0, rc = -1;
181  char cmd[16] = {0};
182 
183  snprintf(cmd, 16, "W:%ud", ticks);
184  cmd[strlen(cmd)] = 0xA;
185 
186  LOGF_DEBUG("CMD <%s>", cmd);
187 
188  // Set Position
189  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
190  {
191  char errstr[MAXRBUF];
192  tty_error_msg(rc, errstr, MAXRBUF);
193  LOGF_ERROR("sync error: %s.", errstr);
194  return false;
195  }
196 
197  this->ignoreResponse();
198 
199  return true;
200 }
201 
202 bool PegasusProdigyMF::move(uint32_t newPosition)
203 {
204  int nbytes_written = 0, rc = -1;
205  char cmd[16] = {0};
206 
207  snprintf(cmd, 16, "M:%ud", newPosition);
208  cmd[strlen(cmd)] = 0xA;
209 
210  LOGF_DEBUG("CMD <%s>", cmd);
211 
212  // Set Position
213  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
214  {
215  char errstr[MAXRBUF];
216  tty_error_msg(rc, errstr, MAXRBUF);
217  LOGF_ERROR("move error: %s.", errstr);
218  return false;
219  }
220 
221  this->ignoreResponse();
222 
223  return true;
224 }
225 
226 bool PegasusProdigyMF::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
227 {
228  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
229  {
230 
231  }
232  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
233 }
234 
235 bool PegasusProdigyMF::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
236 {
237  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
238  {
239  // MaxSpeed
240  if (strcmp(name, MaxSpeedNP.name) == 0)
241  {
242  IUUpdateNumber(&MaxSpeedNP, values, names, n);
243  bool rc = setMaxSpeed(MaxSpeedN[0].value);
244  MaxSpeedNP.s = rc ? IPS_OK : IPS_ALERT;
245  IDSetNumber(&MaxSpeedNP, nullptr);
246  return true;
247  }
248 
249  }
250 
251  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
252 }
253 
254 void PegasusProdigyMF::ignoreResponse()
255 {
256  int nbytes_read = 0;
257  char res[64];
258  tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read);
259 }
260 
261 bool PegasusProdigyMF::updateFocusParams()
262 {
263  int nbytes_written = 0, nbytes_read = 0, rc = -1;
264  char errstr[MAXRBUF];
265  char cmd[3] = {0};
266  char res[64];
267  cmd[0] = 'A';
268  cmd[1] = 0xA;
269 
270  LOGF_DEBUG("CMD <%#02X>", cmd[0]);
271 
272  tcflush(PortFD, TCIOFLUSH);
273 
274  if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
275  {
276  tty_error_msg(rc, errstr, MAXRBUF);
277  LOGF_ERROR("GetFocusParams error: %s.", errstr);
278  return false;
279  }
280 
281 
282  if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
283  {
284  tty_error_msg(rc, errstr, MAXRBUF);
285  LOGF_ERROR("GetFocusParams error: %s.", errstr);
286  return false;
287  }
288 
289  res[nbytes_read - 1] = 0;
290 
291  // Check for '\r' at end of string and replace with nullptr (DMFC firmware version 2.8)
292  if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
293 
294  LOGF_DEBUG("RES <%s>", res);
295 
296  tcflush(PortFD, TCIOFLUSH);
297 
298  char *token = std::strtok(res, ":");
299 
300  // #1 Status
301  if (token == nullptr || (strstr(token, "OK_PRDG") == nullptr))
302  {
303  LOGF_ERROR("Invalid status response. %s", res);
304  return false;
305  }
306 
307  // #2 Version
308  token = std::strtok(nullptr, ":");
309 
310  if (token == nullptr)
311  {
312  LOG_ERROR("Invalid version response.");
313  return false;
314  }
315 
316  if (FirmwareVersionT[0].text == nullptr || strcmp(FirmwareVersionT[0].text, token))
317  {
318  IUSaveText(&FirmwareVersionT[0], token);
319  FirmwareVersionTP.s = IPS_OK;
320  IDSetText(&FirmwareVersionTP, nullptr);
321  }
322 
323  // #3 Motor Type
324  token = std::strtok(nullptr, ":");
325 
326  // #4 Temperature
327  token = std::strtok(nullptr, ":");
328 
329  if (token == nullptr)
330  {
331  LOG_ERROR("Invalid temperature response.");
332  return false;
333  }
334 
335  double temperature = atof(token);
336  if (temperature == -127)
337  {
338  TemperatureNP.s = IPS_ALERT;
339  IDSetNumber(&TemperatureNP, nullptr);
340  }
341  else
342  {
343  if (fabs(temperature - TemperatureN[0].value) > TEMPERATURE_THRESHOLD)
344  {
345  TemperatureN[0].value = temperature;
346  TemperatureNP.s = IPS_OK;
347  IDSetNumber(&TemperatureNP, nullptr);
348  }
349  }
350 
351  // #5 Position
352  token = std::strtok(nullptr, ":");
353 
354  if (token == nullptr)
355  {
356  LOG_ERROR("Invalid position response.");
357  return false;
358  }
359 
360  currentPosition = atoi(token);
361  if (currentPosition != FocusAbsPosN[0].value)
362  {
363  FocusAbsPosN[0].value = currentPosition;
364  IDSetNumber(&FocusAbsPosNP, nullptr);
365  }
366 
367  // #6 Moving Status
368  token = std::strtok(nullptr, ":");
369 
370  if (token == nullptr)
371  {
372  LOG_ERROR("Invalid moving status response.");
373  return false;
374  }
375 
376  isMoving = (token[0] == '1');
377 
378  // #7 LED Status fake read
379  token = std::strtok(nullptr, ":");
380 
381 
382 
383  // #8 Reverse Status
384  token = std::strtok(nullptr, ":");
385 
386  if (token == nullptr)
387  {
388  LOG_ERROR("Invalid reverse response.");
389  return false;
390  }
391 
392  int reverseStatus = atoi(token);
393  if (reverseStatus >= 0 && reverseStatus <= 1)
394  {
396  FocusReverseS[INDI_ENABLED].s = (reverseStatus == 1) ? ISS_ON : ISS_OFF;
397  FocusReverseS[INDI_DISABLED].s = (reverseStatus == 0) ? ISS_ON : ISS_OFF;
399  IDSetSwitch(&FocusReverseSP, nullptr);
400  }
401 
402  // #9 Encoder status fake read
403  token = std::strtok(nullptr, ":");
404 
405 
406  // #10 Backlash fake read
407  token = std::strtok(nullptr, ":");
408 
409 
410  return true;
411 }
412 
413 bool PegasusProdigyMF::setMaxSpeed(uint16_t speed)
414 {
415  int nbytes_written = 0, rc = -1;
416  char cmd[16] = {0};
417 
418  snprintf(cmd, 16, "S:%d", speed);
419  cmd[strlen(cmd)] = 0xA;
420 
421  LOGF_DEBUG("CMD <%s>", cmd);
422 
423  tcflush(PortFD, TCIOFLUSH);
424 
425  // Set Speed
426  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
427  {
428  char errstr[MAXRBUF];
429  tty_error_msg(rc, errstr, MAXRBUF);
430  LOGF_ERROR("setMaxSpeed error: %s.", errstr);
431  return false;
432  }
433 
434  this->ignoreResponse();
435  return true;
436 }
437 
439 {
440  int nbytes_written = 0, rc = -1;
441  char cmd[16] = {0};
442 
443  snprintf(cmd, 16, "N:%d", enabled ? 1 : 0);
444  cmd[strlen(cmd)] = 0xA;
445 
446  LOGF_DEBUG("CMD <%s>", cmd);
447 
448  tcflush(PortFD, TCIOFLUSH);
449 
450  // Reverse
451  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
452  {
453  char errstr[MAXRBUF];
454  tty_error_msg(rc, errstr, MAXRBUF);
455  LOGF_ERROR("Reverse error: %s.", errstr);
456  return false;
457  }
458 
459  this->ignoreResponse();
460 
461  return true;
462 }
463 
464 
466 {
467  targetPosition = targetTicks;
468 
469  bool rc = move(targetPosition);
470 
471  if (!rc)
472  return IPS_ALERT;
473 
475 
476  return IPS_BUSY;
477 }
478 
480 {
481  double newPosition = 0;
482  bool rc = false;
483 
484  if (dir == FOCUS_INWARD)
485  newPosition = FocusAbsPosN[0].value - ticks;
486  else
487  newPosition = FocusAbsPosN[0].value + ticks;
488 
489  rc = move(newPosition);
490 
491  if (!rc)
492  return IPS_ALERT;
493 
494  FocusRelPosN[0].value = ticks;
496 
497  return IPS_BUSY;
498 }
499 
501 {
502  if (!isConnected())
503  {
505  return;
506  }
507 
508  bool rc = updateFocusParams();
509 
510  if (rc)
511  {
513  {
514  if (isMoving == false)
515  {
518  IDSetNumber(&FocusAbsPosNP, nullptr);
519  IDSetNumber(&FocusRelPosNP, nullptr);
520  LOG_INFO("Focuser reached requested position.");
521  }
522  }
523  }
524 
526 }
527 
529 {
530  int nbytes_written;
531  char cmd[2] = { 'H', 0xA };
532 
533  if (tty_write(PortFD, cmd, 2, &nbytes_written) == TTY_OK)
534  {
537  IDSetNumber(&FocusAbsPosNP, nullptr);
538  IDSetNumber(&FocusRelPosNP, nullptr);
539  this->ignoreResponse();
540  return true;
541  }
542  else
543  return false;
544 }
545 
547 {
549  IUSaveConfigNumber(fp, &MaxSpeedNP);
550 
551  return true;
552 }
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
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.
void addDebugControl()
Add Debug control to the driver.
INumberVectorProperty FocusAbsPosNP
INumberVectorProperty FocusRelPosNP
ISwitchVectorProperty FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:42
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Connection::Serial * serialConnection
Definition: indifocuser.h:116
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * getDefaultName() override
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
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
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
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:272
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 IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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 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:180
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#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 MAXRBUF
Definition: indiserver.cpp:102
#define DMFC_TIMEOUT
#define TEMPERATURE_THRESHOLD
#define FOCUS_SETTINGS_TAB
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:250