Instrument Neutral Distributed Interface INDI  2.0.2
pegasus_scopsoag.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2021 Chrysikos Efstathios. All rights reserved.
3 
4  Pegasus ScopsOAG
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_scopsoag.h"
26 
27 #include "indicom.h"
29 
30 #include <cmath>
31 #include <cstring>
32 #include <memory>
33 
34 #include <termios.h>
35 #include <unistd.h>
36 
37 #define DMFC_TIMEOUT 3
38 #define FOCUS_SETTINGS_TAB "Settings"
39 #define TEMPERATURE_THRESHOLD 0.1
40 
41 static std::unique_ptr<PegasusScopsOAG> scopsOAG(new PegasusScopsOAG());
42 
44 {
45  // Can move in Absolute & Relative motions, can AbortFocuser motion.
51 }
52 
54 {
56 
57  // LED
58  IUFillSwitch(&LEDS[LED_OFF], "Off", "", ISS_ON);
59  IUFillSwitch(&LEDS[LED_ON], "On", "", ISS_OFF);
60  IUFillSwitchVector(&LEDSP, LEDS, 2, getDeviceName(), "LED", "", FOCUS_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
61 
62  // Firmware Version
63  IUFillText(&FirmwareVersionT[0], "Version", "Version", "");
64  IUFillTextVector(&FirmwareVersionTP, FirmwareVersionT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO,
65  0, IPS_IDLE);
66 
67  // Relative and absolute movement
68  FocusRelPosN[0].min = 0.;
69  FocusRelPosN[0].max = 50000.;
70  FocusRelPosN[0].value = 0;
71  FocusRelPosN[0].step = 1000;
72 
73  FocusAbsPosN[0].min = 0.;
74  FocusAbsPosN[0].max = 100000;
75  FocusAbsPosN[0].value = 0;
76  FocusAbsPosN[0].step = 1000;
77 
78  // Backlash compensation
79  FocusBacklashN[0].min = 1; // 0 is off.
80  FocusBacklashN[0].max = 1000;
81  FocusBacklashN[0].value = 1;
82  FocusBacklashN[0].step = 1;
83 
84  //LED Default ON
85  LEDS[LED_ON].s = ISS_ON;
86  LEDS[LED_OFF].s = ISS_OFF;
87 
91 
92  return true;
93 }
94 
96 {
98 
99  if (isConnected())
100  {
101 
105  defineProperty(&LEDSP);
106  defineProperty(&FirmwareVersionTP);
107  }
108  else
109  {
110  deleteProperty(LEDSP.name);
111  deleteProperty(FirmwareVersionTP.name);
112  }
113 
114  return true;
115 }
116 
118 {
119  if (ack())
120  {
121  LOGF_INFO("%s is online. Getting focus parameters...", this->getDeviceName());
122  return true;
123  }
124 
125  LOGF_INFO(
126  "Error retrieving data from %s, please ensure device is powered and the port is correct.", this->getDeviceName());
127  return false;
128 }
129 
130 
132 {
133  return "Pegasus ScopsOAG";
134 }
135 
136 bool PegasusScopsOAG::ack()
137 {
138  int nbytes_written = 0, nbytes_read = 0, rc = -1;
139  char errstr[MAXRBUF];
140  char cmd[2] = {0};
141  char res[16] = {0};
142 
143  cmd[0] = '#';
144  cmd[1] = 0xA;
145 
146  LOGF_DEBUG("CMD <%#02X>", cmd[0]);
147 
148  tcflush(PortFD, TCIOFLUSH);
149 
150  if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
151  {
152  tty_error_msg(rc, errstr, MAXRBUF);
153  LOGF_ERROR("Ack error: %s.", errstr);
154  return false;
155  }
156 
157  if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
158  {
159  tty_error_msg(rc, errstr, MAXRBUF);
160  LOGF_ERROR("Ack error: %s.", errstr);
161  return false;
162  }
163 
164  // Get rid of 0xA
165  res[nbytes_read - 1] = 0;
166 
167 
168  if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
169 
170  LOGF_DEBUG("RES <%s>", res);
171 
172  tcflush(PortFD, TCIOFLUSH);
173 
174 
175 
176  if((strstr(res, "OK_SCOPS") != nullptr))
177  return true;
178 
179  return false;
180 }
181 
182 
183 bool PegasusScopsOAG::SyncFocuser(uint32_t ticks)
184 {
185  int nbytes_written = 0, rc = -1;
186  char cmd[16] = {0};
187 
188  snprintf(cmd, 16, "W:%ud", ticks);
189  cmd[strlen(cmd)] = 0xA;
190 
191  LOGF_DEBUG("CMD <%s>", cmd);
192 
193  // Set Position
194  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
195  {
196  char errstr[MAXRBUF];
197  tty_error_msg(rc, errstr, MAXRBUF);
198  LOGF_ERROR("sync error: %s.", errstr);
199  return false;
200  }
201 
202  this->ignoreResponse();
203 
204  return true;
205 }
206 
207 bool PegasusScopsOAG::move(uint32_t newPosition)
208 {
209  int nbytes_written = 0, rc = -1;
210  char cmd[16] = {0};
211 
212  snprintf(cmd, 16, "M:%ud", newPosition);
213  cmd[strlen(cmd)] = 0xA;
214 
215  LOGF_DEBUG("CMD <%s>", cmd);
216 
217  // Set Position
218  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
219  {
220  char errstr[MAXRBUF];
221  tty_error_msg(rc, errstr, MAXRBUF);
222  LOGF_ERROR("move error: %s.", errstr);
223  return false;
224  }
225 
226  this->ignoreResponse();
227 
228  return true;
229 }
230 
231 bool PegasusScopsOAG::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
232 {
233  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
234  {
235  // LED
236  if (!strcmp(name, LEDSP.name))
237  {
238  IUUpdateSwitch(&LEDSP, states, names, n);
239  bool rc = setLedEnabled(LEDS[LED_ON].s == ISS_ON);
240  LEDSP.s = rc ? IPS_OK : IPS_ALERT;
241  IDSetSwitch(&LEDSP, nullptr);
242  return true;
243  }
244  }
245  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
246 }
247 
248 void PegasusScopsOAG::ignoreResponse()
249 {
250  int nbytes_read = 0;
251  char res[64];
252  tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read);
253 }
254 
255 bool PegasusScopsOAG::updateFocusParams()
256 {
257  int nbytes_written = 0, nbytes_read = 0, rc = -1;
258  char errstr[MAXRBUF];
259  char cmd[3] = {0};
260  char res[64];
261  cmd[0] = 'A';
262  cmd[1] = 0xA;
263 
264  LOGF_DEBUG("CMD <%#02X>", cmd[0]);
265 
266  tcflush(PortFD, TCIOFLUSH);
267 
268  if ((rc = tty_write(PortFD, cmd, 2, &nbytes_written)) != TTY_OK)
269  {
270  tty_error_msg(rc, errstr, MAXRBUF);
271  LOGF_ERROR("GetFocusParams error: %s.", errstr);
272  return false;
273  }
274 
275 
276  if ((rc = tty_read_section(PortFD, res, 0xA, DMFC_TIMEOUT, &nbytes_read)) != TTY_OK)
277  {
278  tty_error_msg(rc, errstr, MAXRBUF);
279  LOGF_ERROR("GetFocusParams error: %s.", errstr);
280  return false;
281  }
282 
283  res[nbytes_read - 1] = 0;
284 
285  // Check for '\r' at end of string and replace with nullptr (DMFC firmware version 2.8)
286  if( res[nbytes_read - 2] == '\r') res[nbytes_read - 2] = 0;
287 
288  LOGF_DEBUG("RES <%s>", res);
289 
290  tcflush(PortFD, TCIOFLUSH);
291 
292  char *token = std::strtok(res, ":");
293 
294 
295  // #1 Status
296  if (token == nullptr || ((strstr(token, "OK_SCOPS") == nullptr)))
297  {
298  LOGF_ERROR("Invalid status response. %s", res);
299  return false;
300  }
301 
302  // #2 Version
303  token = std::strtok(nullptr, ":");
304 
305  if (token == nullptr)
306  {
307  LOG_ERROR("Invalid version response.");
308  return false;
309  }
310 
311  if (FirmwareVersionT[0].text == nullptr || strcmp(FirmwareVersionT[0].text, token))
312  {
313  IUSaveText(&FirmwareVersionT[0], token);
314  FirmwareVersionTP.s = IPS_OK;
315  IDSetText(&FirmwareVersionTP, nullptr);
316  }
317 
318  // #3 Motor Type
319  token = std::strtok(nullptr, ":");
320 
321  // #4 Temperature
322  token = std::strtok(nullptr, ":");
323 
324  // #5 Position
325  token = std::strtok(nullptr, ":");
326 
327  if (token == nullptr)
328  {
329  LOG_ERROR("Invalid position response.");
330  return false;
331  }
332 
333  currentPosition = atoi(token);
334  if (currentPosition != FocusAbsPosN[0].value)
335  {
336  FocusAbsPosN[0].value = currentPosition;
337  IDSetNumber(&FocusAbsPosNP, nullptr);
338  }
339 
340  // #6 Moving Status
341  token = std::strtok(nullptr, ":");
342 
343  if (token == nullptr)
344  {
345  LOG_ERROR("Invalid moving status response.");
346  return false;
347  }
348 
349  isMoving = (token[0] == '1');
350 
351  // #7 LED Status
352  token = std::strtok(nullptr, ":");
353 
354  if (token == nullptr)
355  {
356  LOG_ERROR("Invalid LED response.");
357  return false;
358  }
359 
360  int ledStatus = atoi(token);
361  if (ledStatus >= 0 && ledStatus <= 1)
362  {
363  IUResetSwitch(&LEDSP);
364  LEDS[ledStatus].s = ISS_ON;
365  LEDSP.s = IPS_OK;
366  IDSetSwitch(&LEDSP, nullptr);
367  }
368 
369  // #8 Reverse Status
370  token = std::strtok(nullptr, ":");
371 
372  // #9 Encoder status
373  token = std::strtok(nullptr, ":");
374 
375  // #10 Backlash
376  token = std::strtok(nullptr, ":");
377 
378  if (token == nullptr)
379  {
380  LOG_ERROR("Invalid encoder response.");
381  return false;
382  }
383 
384  int backlash = atoi(token);
385  // If backlash is zero then compensation is disabled
386  if (backlash == 0 && FocusBacklashS[INDI_ENABLED].s == ISS_ON)
387  {
388  LOG_WARN("Backlash value is zero, disabling backlash switch...");
389 
393  IDSetSwitch(&FocusBacklashSP, nullptr);
394  }
395  else if (backlash > 0 && (FocusBacklashS[INDI_DISABLED].s == ISS_ON || backlash != FocusBacklashN[0].value))
396  {
397  if (backlash != FocusBacklashN[0].value)
398  {
399  FocusBacklashN[0].value = backlash;
401  IDSetNumber(&FocusBacklashNP, nullptr);
402  }
403 
405  {
409  IDSetSwitch(&FocusBacklashSP, nullptr);
410  }
411  }
412 
413  return true;
414 }
415 
416 
417 bool PegasusScopsOAG::setLedEnabled(bool enable)
418 {
419  int nbytes_written = 0, rc = -1;
420  char cmd[16] = {0};
421 
422  snprintf(cmd, 16, "L:%d", enable ? 2 : 1);
423  cmd[strlen(cmd)] = 0xA;
424 
425  LOGF_DEBUG("CMD <%s>", cmd);
426 
427  tcflush(PortFD, TCIOFLUSH);
428 
429  // Led
430  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
431  {
432  char errstr[MAXRBUF];
433  tty_error_msg(rc, errstr, MAXRBUF);
434  LOGF_ERROR("Led error: %s.", errstr);
435  return false;
436  }
437 
438  this->ignoreResponse();
439  return true;
440 }
441 
443 {
444  targetPosition = targetTicks;
445 
446  bool rc = move(targetPosition);
447 
448  if (!rc)
449  return IPS_ALERT;
450 
452 
453  return IPS_BUSY;
454 }
455 
457 {
458  double newPosition = 0;
459  bool rc = false;
460 
461  if (dir == FOCUS_INWARD)
462  newPosition = FocusAbsPosN[0].value - ticks;
463  else
464  newPosition = FocusAbsPosN[0].value + ticks;
465 
466  rc = move(newPosition);
467 
468  if (!rc)
469  return IPS_ALERT;
470 
471  FocusRelPosN[0].value = ticks;
473 
474  return IPS_BUSY;
475 }
476 
478 {
479  if (!isConnected())
480  {
482  return;
483  }
484 
485  bool rc = updateFocusParams();
486 
487  if (rc)
488  {
490  {
491  if (isMoving == false)
492  {
495  IDSetNumber(&FocusAbsPosNP, nullptr);
496  IDSetNumber(&FocusRelPosNP, nullptr);
497  LOG_INFO("Focuser reached requested position.");
498  }
499  }
500  }
501 
503 }
504 
506 {
507  int nbytes_written;
508  char cmd[2] = { 'H', 0xA };
509 
510  if (tty_write(PortFD, cmd, 2, &nbytes_written) == TTY_OK)
511  {
514  IDSetNumber(&FocusAbsPosNP, nullptr);
515  IDSetNumber(&FocusRelPosNP, nullptr);
516  this->ignoreResponse();
517  return true;
518  }
519  else
520  return false;
521 }
522 
524 {
526  IUSaveConfigSwitch(fp, &LEDSP);
527 
528  return true;
529 }
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 FocusBacklashNP
ISwitchVectorProperty FocusBacklashSP
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
Connection::Serial * serialConnection
Definition: indifocuser.h:116
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
const char * getDefaultName() override
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.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
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
@ ISR_1OFMANY
Definition: indiapi.h:173
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 IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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_INFO(fmt,...)
Definition: indilogger.h:82
#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 MAXRBUF
Definition: indiserver.cpp:102
#define DMFC_TIMEOUT
#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:371
char name[MAXINDINAME]
Definition: indiapi.h:250