Instrument Neutral Distributed Interface INDI  2.0.2
pyxis.cpp
Go to the documentation of this file.
1 /*
2  Optec Pyrix Rotator
3  Copyright (C) 2017 Jasem Mutlaq (mutlaqja@ikarustech.com)
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "pyxis.h"
22 
23 #include "indicom.h"
25 
26 #include <cmath>
27 #include <cstring>
28 #include <memory>
29 #include <termios.h>
30 
31 #define PYXIS_TIMEOUT 3
32 #define PYRIX_BUF 7
33 #define PYRIX_CMD 6
34 #define SETTINGS_TAB "Settings"
35 
36 // Recommended default rates for 3 inch and 2 inch rotators
37 #define PYXIS_3INCH_RATE 6
38 #define PYXIS_2INCH_RATE 8
39 
40 // Number of steps per degree for rotators
41 #define PYXIS_3INCH_PER_DEG (128)
42 #define PYXIS_2INCH_PER_DEG 14
43 
44 // 100ms poll rate while rotating
45 #define POLL_100MS 100
46 
47 std::unique_ptr<Pyxis> pyxis(new Pyxis());
48 
50 {
51  // We do not have absolute ticks
53 
55 }
56 
58 {
60 
61  // Rotation Rate
62  IUFillNumber(&RotationRateN[0], "RATE", "Rate", "%.f", 0, 99, 10, 8);
63  IUFillNumberVector(&RotationRateNP, RotationRateN, 1, getDeviceName(), "ROTATION_RATE", "Rotation", SETTINGS_TAB, IP_RW, 0,
64  IPS_IDLE);
65 
66  // Stepping
67  IUFillSwitch(&SteppingS[FULL_STEP], "FULL_STEP", "Full", ISS_OFF);
68  IUFillSwitch(&SteppingS[HALF_STEP], "HALF_STEP", "Half", ISS_OFF);
69  IUFillSwitchVector(&SteppingSP, SteppingS, 2, getDeviceName(), "STEPPING_RATE", "Stepping", SETTINGS_TAB, IP_RW,
70  ISR_ATMOST1, 0, IPS_IDLE);
71 
72  // Power
73  IUFillSwitch(&PowerS[POWER_SLEEP], "POWER_SLEEP", "Sleep", ISS_OFF);
74  IUFillSwitch(&PowerS[POWER_WAKEUP], "POWER_WAKEUP", "Wake Up", ISS_OFF);
75  IUFillSwitchVector(&PowerSP, PowerS, 2, getDeviceName(), "POWER_STATE", "Power", SETTINGS_TAB, IP_RW, ISR_ATMOST1, 0,
76  IPS_IDLE);
77 
78  // Firmware version
79  IUFillText(&FirmwareT[0], "FIRMWARE_VERSION", "Version", "Unknown");
80  IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "FIRMWARE_VERSION", "Firmware", INFO_TAB, IP_RO, 0, IPS_IDLE);
81 
82  // Firmware version
83  IUFillText(&ModelT[0], "HARDWARE_MODEL", "Model", "Unknown");
84  IUFillTextVector(&ModelTP, ModelT, 1, getDeviceName(), "HARDWARE_MODEL", "Model", INFO_TAB, IP_RO, 0, IPS_IDLE);
85 
86 
88 
89  return true ;
90 }
91 
93 {
94  if (Ack())
95  return true;
96 
97  LOG_INFO("Error retrieving data from Pyxis, please ensure Pyxis controller is powered and the port is correct.");
98  return false;
99 }
100 
101 const char * Pyxis::getDefaultName()
102 {
103  return "Pyxis";
104 }
105 
107 {
109 
110  if (isConnected())
111  {
112  defineProperty(&RotationRateNP) ;
113  defineProperty(&SteppingSP);
114  defineProperty(&PowerSP);
115  defineProperty(&FirmwareTP) ;
116  defineProperty(&ModelTP) ;
117 
118  queryParams();
119  }
120  else
121  {
122  deleteProperty(RotationRateNP.name);
123  deleteProperty(SteppingSP.name);
124  deleteProperty(PowerSP.name);
125  deleteProperty(FirmwareTP.name) ;
126  deleteProperty(ModelTP.name) ;
127  }
128 
129  return true;
130 }
131 
132 void Pyxis::queryParams()
133 {
135  // Reverse Parameter
137  int dir = getReverseStatus();
138 
141  if (dir == 0)
143  else if (dir == 1)
145  else
147 
148  IDSetSwitch(&ReverseRotatorSP, nullptr);
149 
150  // Firmware version parameter
151  std::string sversion = getVersion() ;
152  IUSaveText(&FirmwareT[0], sversion.c_str()) ;
153  FirmwareTP.s = IPS_OK;
154  IDSetText(&FirmwareTP, nullptr) ;
155 
156  LOGF_DEBUG("queryParms firmware = %s", sversion.c_str()) ;
157 
158  // Firmware tells us device type, 3 inch or 2 inch, which defines the correct default rotation rate
159  if (atof(sversion.c_str()) >= 3)
160  {
161  uint16_t rate = (atof(sversion.c_str()) >= 3 ? PYXIS_3INCH_RATE : PYXIS_2INCH_RATE) ;
162  bool rc = setRotationRate(rate) ;
163  LOGF_DEBUG("queryParms rate = %d, firmware = %s", rate, sversion.c_str()) ;
164  if (rc)
165  {
166  RotationRateNP.s = IPS_OK ;
167  RotationRateN[0].value = rate ;
168  IDSetNumber(&RotationRateNP, nullptr) ;
169 
170  IUSaveText(&ModelT[0], "Pyxis 3 Inch") ;
171  ModelTP.s = IPS_OK;
172  IDSetText(&ModelTP, nullptr) ;
173  }
174  }
175  else
176  {
177  IUSaveText(&ModelT[0], "Pyxis 2 Inch") ;
178  ModelTP.s = IPS_OK;
179  IDSetText(&ModelTP, nullptr) ;
180  }
181 
182 }
183 
184 bool Pyxis::Ack()
185 {
186  const char *cmd = "CCLINK";
187  char res[1] = {0};
188 
189  int nbytes_written = 0, nbytes_read = 0, rc = -1;
190  char errstr[MAXRBUF];
191 
192  LOGF_DEBUG("CMD <%s>", cmd);
193 
194  tcflush(PortFD, TCIOFLUSH);
195 
196  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
197  {
198  tty_error_msg(rc, errstr, MAXRBUF);
199  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
200  return false;
201  }
202 
203  if ( (rc = tty_read(PortFD, res, 1, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
204  {
205  tty_error_msg(rc, errstr, MAXRBUF);
206  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
207  return false;
208  }
209 
210  LOGF_DEBUG("RES <%c>", res[0]);
211 
212  tcflush(PortFD, TCIOFLUSH);
213 
214  if (res[0] != '!')
215  {
216  LOG_ERROR("Cannot establish communication. Check power is on and homing is complete.");
217  return false;
218  }
219 
220  return true;
221 }
222 
223 bool Pyxis::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
224 {
225  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
226  {
227  if (!strcmp(name, RotationRateNP.name))
228  {
229  bool rc = setRotationRate(static_cast<uint8_t>(values[0]));
230  if (rc)
231  {
232  RotationRateNP.s = IPS_OK;
233  RotationRateN[0].value = values[0];
234  }
235  else
236  RotationRateNP.s = IPS_ALERT;
237 
238  IDSetNumber(&RotationRateNP, nullptr);
239  return true;
240  }
241  }
242 
243  return Rotator::ISNewNumber(dev, name, values, names, n);
244 }
245 
246 bool Pyxis::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
247 {
248  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
249  {
251  // Stepping
253  if (!strcmp(name, SteppingSP.name))
254  {
255  bool rc = false;
256  if (!strcmp(IUFindOnSwitchName(states, names, n), SteppingS[FULL_STEP].name))
257  rc = setSteppingMode(FULL_STEP);
258  else
259  rc = setSteppingMode(HALF_STEP);
260 
261 
262  if (rc)
263  {
264  IUUpdateSwitch(&SteppingSP, states, names, n);
265  SteppingSP.s = IPS_OK;
266  }
267  else
268  SteppingSP.s = IPS_ALERT;
269 
270  IDSetSwitch(&SteppingSP, nullptr);
271  return true;
272  }
273 
275  // Power
277  if (!strcmp(name, PowerSP.name))
278  {
279  bool rc = false;
280  if (!strcmp(IUFindOnSwitchName(states, names, n), PowerS[POWER_WAKEUP].name))
281  {
282  // If not sleeping
283  if (PowerS[POWER_SLEEP].s == ISS_OFF)
284  {
285  PowerSP.s = IPS_OK;
286  LOG_WARN("Controller is not in sleep mode.");
287  IDSetSwitch(&PowerSP, nullptr);
288  return true;
289  }
290 
291  rc = wakeupController();
292 
293  if (rc)
294  {
295  IUResetSwitch(&PowerSP);
296  PowerSP.s = IPS_OK;
297  LOG_INFO("Controller is awake.");
298  }
299  else
300  PowerSP.s = IPS_ALERT;
301 
302  IDSetSwitch(&PowerSP, nullptr);
303  return true;
304  }
305  else
306  {
307  bool rc = sleepController();
308  IUResetSwitch(&PowerSP);
309  if (rc)
310  {
311  PowerSP.s = IPS_OK;
312  PowerS[POWER_SLEEP].s = ISS_ON;
313  LOG_INFO("Controller in sleep mode. No functions can be used until controller is waken up.");
314  }
315  else
316  PowerSP.s = IPS_ALERT;
317 
318  IDSetSwitch(&PowerSP, nullptr);
319  return true;
320  }
321  }
322  }
323 
324  return Rotator::ISNewSwitch(dev, name, states, names, n);
325 }
326 
327 bool Pyxis::setSteppingMode(uint8_t mode)
328 {
329  char cmd[PYRIX_BUF] = {0};
330 
331  int nbytes_written = 0, rc = -1;
332  char errstr[MAXRBUF];
333 
334  snprintf(cmd, PYRIX_BUF, "CZ%dxxx", mode);
335 
336  LOGF_DEBUG("CMD <%s>", cmd);
337 
338  tcflush(PortFD, TCIOFLUSH);
339 
340  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
341  {
342  tty_error_msg(rc, errstr, MAXRBUF);
343  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
344  return false;
345  }
346 
347  return true;
348 }
349 
350 bool Pyxis::setRotationRate(uint8_t rate)
351 {
352  char cmd[PYRIX_BUF] = {0};
353  int nbytes_written = 0, rc = -1;
354  char errstr[MAXRBUF];
355 
356  char res[1] = { 0 } ;
357  int nbytes_read = 0 ;
358 
359  snprintf(cmd, PYRIX_BUF, "CTxx%02d", rate);
360 
361  LOGF_DEBUG("CMD <%s>", cmd);
362 
363  tcflush(PortFD, TCIOFLUSH);
364 
365  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
366  {
367  tty_error_msg(rc, errstr, MAXRBUF);
368  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
369  return false;
370  }
371 
372  if ( (rc = tty_read(PortFD, res, 1, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
373  {
374  tty_error_msg(rc, errstr, MAXRBUF);
375  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
376  return false;
377  }
378 
379  tcflush(PortFD, TCIOFLUSH);
380 
381  LOGF_DEBUG("RES <%c>", res[0]);
382 
383  return (res[0] == '!');
384 }
385 
386 bool Pyxis::sleepController()
387 {
388  const char *cmd = "CSLEEP";
389 
390  int nbytes_written = 0, rc = -1;
391  char errstr[MAXRBUF];
392 
393  LOGF_DEBUG("CMD <%s>", cmd);
394 
395  tcflush(PortFD, TCIOFLUSH);
396 
397  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
398  {
399  tty_error_msg(rc, errstr, MAXRBUF);
400  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
401  return false;
402  }
403 
404  return true;
405 }
406 
407 bool Pyxis::wakeupController()
408 {
409  const char *cmd = "CWAKEUP";
410  char res[1] = { 0 };
411 
412  int nbytes_written = 0, nbytes_read = 0, rc = -1;
413  char errstr[MAXRBUF];
414 
415  LOGF_DEBUG("CMD <%s>", cmd);
416 
417  tcflush(PortFD, TCIOFLUSH);
418 
419  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
420  {
421  tty_error_msg(rc, errstr, MAXRBUF);
422  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
423  return false;
424  }
425 
426  if ( (rc = tty_read(PortFD, res, 1, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
427  {
428  tty_error_msg(rc, errstr, MAXRBUF);
429  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
430  return false;
431  }
432 
433  tcflush(PortFD, TCIOFLUSH);
434 
435  LOGF_DEBUG("RES <%c>", res[0]);
436 
437  return (res[0] == '!');
438 }
439 
441 {
442  const char *cmd = "CHOMES";
443 
444  int nbytes_written = 0, rc = -1;
445  char errstr[MAXRBUF];
446 
447  LOGF_DEBUG("CMD <%s>", cmd);
448 
449  tcflush(PortFD, TCIOFLUSH);
450 
451  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
452  {
453  tty_error_msg(rc, errstr, MAXRBUF);
454  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
455  return IPS_ALERT;
456  }
457 
458  return IPS_BUSY;
459 }
460 
462 {
463  char cmd[PYRIX_BUF] = {0};
464 
465  int nbytes_written = 0, rc = -1;
466  char errstr[MAXRBUF];
467 
468  uint16_t current = static_cast<uint16_t>(GotoRotatorN[0].value) ;
469 
470  targetPA = static_cast<uint16_t>(round(angle));
471 
472  if (targetPA > 359)
473  targetPA = 0;
474 
475  // Rotator will only rotation +-180 degress from home (0 degrees) so it make take
476  // the long way to avoid cable wrap
477  if (current <= 180 && targetPA < 180)
478  direction = (targetPA >= current ? 1 : -1) ;
479  else if (current <= 180 && targetPA > 180)
480  direction = -1 ;
481  else if (current > 180 && targetPA >= 180)
482  direction = (targetPA >= current ? 1 : -1) ;
483  else if (current > 180 && targetPA < 180)
484  direction = 1 ;
485 
486  snprintf(cmd, PYRIX_BUF, "CPA%03d", targetPA);
487 
488  LOGF_DEBUG("CMD <%s>", cmd);
489 
490  tcflush(PortFD, TCIOFLUSH);
491 
492  if ( (rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
493  {
494  tty_error_msg(rc, errstr, MAXRBUF);
495  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
496  return IPS_ALERT;
497  }
498 
499  return IPS_BUSY;
500 }
501 
502 bool Pyxis::ReverseRotator(bool enabled)
503 {
504  char cmd[PYRIX_BUF] = {0};
505 
506  int nbytes_written = 0, rc = -1;
507  char errstr[MAXRBUF];
508 
509  snprintf(cmd, PYRIX_BUF, "CD%dxxx", enabled ? 1 : 0);
510 
511  LOGF_DEBUG("CMD <%s>", cmd);
512 
513  tcflush(PortFD, TCIOFLUSH);
514 
515  if ( (rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
516  {
517  tty_error_msg(rc, errstr, MAXRBUF);
518  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
519  return false;
520  }
521 
522  return true;
523 }
524 
526 {
527  if (!isConnected() || PowerS[POWER_SLEEP].s == ISS_ON)
528  {
530  return;
531  }
532 
533  if (HomeRotatorSP.s == IPS_BUSY)
534  {
535  if (isMotionComplete())
536  {
538  HomeRotatorS[0].s = ISS_OFF;
539  IDSetSwitch(&HomeRotatorSP, nullptr);
540  LOG_INFO("Homing is complete.");
541  }
542  else
543  {
544  // Fast timer
546  return;
547  }
548  }
549  else if (GotoRotatorNP.s == IPS_BUSY)
550  {
551  if (!isMotionComplete())
552  {
553  LOGF_DEBUG("Motion in %s", "progress") ;
555  return ;
556  }
558  }
559 
560  uint16_t PA = 0;
561  if (getPA(PA) && (PA != static_cast<uint16_t>(GotoRotatorN[0].value)))
562  {
563  GotoRotatorN[0].value = PA;
564  IDSetNumber(&GotoRotatorNP, nullptr);
565  }
566 
568 }
569 
570 bool Pyxis::isMotionComplete()
571 {
572  int nbytes_read = 0, rc = -1;
573  char errstr[MAXRBUF];
574  char res[PYXIS_3INCH_PER_DEG + 1] = { 0 };
575 
576  bool pyxis3inch = atoi(FirmwareT[0].text) >= 3 ;
577 
578  if ( (rc = tty_nread_section(PortFD, res, (pyxis3inch ? PYXIS_3INCH_PER_DEG : PYXIS_2INCH_PER_DEG), 'F', 1,
579  &nbytes_read)) != TTY_OK)
580  {
581  // '!' motion is not complete yet
582  if (rc == TTY_TIME_OUT)
583  return false;
584  else if (rc == TTY_OVERFLOW)
585  {
586  LOGF_DEBUG("RES <%s>", res);
587 
588  int current = static_cast<uint16_t>(GotoRotatorN[0].value) ;
589 
590  current = current + direction ;
591  if (current < 0) current = 359 ;
592  if (current > 360) current = 1 ;
593 
594  GotoRotatorN[0].value = current ;
595  IDSetNumber(&GotoRotatorNP, nullptr);
596 
597  LOGF_DEBUG("ANGLE = %d", current) ;
598  LOGF_DEBUG("TTY_OVERFLOW, nbytes_read = %d", nbytes_read) ;
599  return false ;
600  }
601 
602  tty_error_msg(rc, errstr, MAXRBUF);
603  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
604 
605  if (HomeRotatorSP.s == IPS_BUSY)
606  {
607  HomeRotatorS[0].s = ISS_OFF;
609  LOG_ERROR("Homing failed. Check possible jam.");
610  tcflush(PortFD, TCIOFLUSH);
611  }
612 
613  return false;
614  }
615 
616  LOGF_DEBUG("RES <%s>", res);
617 
618  return true;
619 }
620 
621 #if 0
622 bool Pyxis::isMotionComplete()
623 {
624  int nbytes_read = 0, rc = -1;
625  char errstr[MAXRBUF];
626  char res[1] = { 0 };
627 
628  if ( (rc = tty_read(PortFD, res, 1, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
629  {
630  tty_error_msg(rc, errstr, MAXRBUF);
631  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
632  return false;
633  }
634 
635  LOGF_DEBUG("RES <%c>", res[0]);
636 
637  // Homing still in progress
638  if (res[0] == '!')
639  return false;
640  // Homing is complete
641  else if (res[0] == 'F')
642  return true;
643  // Error
644  else if (HomeRotatorSP.s == IPS_BUSY)
645  {
646  HomeRotatorS[0].s = ISS_OFF;
648  LOG_ERROR("Homing failed. Check possible jam.");
649  tcflush(PortFD, TCIOFLUSH);
650  }
651 
652  return false;
653 }
654 #endif
655 
656 std::string Pyxis::getVersion()
657 {
658  const char *cmd = "CVxxxx";
659  char res[4] = {0};
660 
661  int nbytes_written = 0, nbytes_read = 0, rc = -1;
662  char errstr[MAXRBUF];
663 
664  LOGF_DEBUG("CMD <%s>", cmd);
665 
666  tcflush(PortFD, TCIOFLUSH);
667 
668  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
669  {
670  tty_error_msg(rc, errstr, MAXRBUF);
671  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
672  return std::string("");;
673  }
674 
675  if ( (rc = tty_read(PortFD, res, 3, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
676  {
677  tty_error_msg(rc, errstr, MAXRBUF);
678  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
679  return std::string("") ;
680  }
681 
682  tcflush(PortFD, TCIOFLUSH);
683 
684  LOGF_DEBUG("RES <%s>", res);
685 
686  if (res[0] == '!')
687  return std::string("");
688 
689  return std::string(res) ;;
690 }
691 
692 
693 bool Pyxis::getPA(uint16_t &PA)
694 {
695  const char *cmd = "CGETPA";
696  char res[4] = {0};
697 
698  int nbytes_written = 0, nbytes_read = 0, rc = -1;
699  char errstr[MAXRBUF];
700 
701  LOGF_DEBUG("CMD <%s>", cmd);
702 
703  tcflush(PortFD, TCIOFLUSH);
704 
705  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
706  {
707  tty_error_msg(rc, errstr, MAXRBUF);
708  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
709  return false;
710  }
711 
712  if ( (rc = tty_read(PortFD, res, 3, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
713  {
714  tty_error_msg(rc, errstr, MAXRBUF);
715  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
716  return false;
717  }
718 
719  tcflush(PortFD, TCIOFLUSH);
720 
721  LOGF_DEBUG("RES <%s>", res);
722 
723  if (res[0] == '!')
724  return false;
725 
726  PA = atoi(res);
727 
728  return true;
729 }
730 
731 int Pyxis::getReverseStatus()
732 {
733  const char *cmd = "CMREAD";
734  char res[1] = {0};
735 
736  int nbytes_written = 0, nbytes_read = 0, rc = -1;
737  char errstr[MAXRBUF];
738 
739  LOGF_DEBUG("CMD <%s>", cmd);
740 
741  tcflush(PortFD, TCIOFLUSH);
742 
743  if ( (rc = tty_write(PortFD, cmd, PYRIX_CMD, &nbytes_written)) != TTY_OK)
744  {
745  tty_error_msg(rc, errstr, MAXRBUF);
746  LOGF_ERROR("%s: %s.", __FUNCTION__, errstr);
747  return -1;
748  }
749 
750  if ( (rc = tty_read(PortFD, res, 1, PYXIS_TIMEOUT, &nbytes_read)) != TTY_OK)
751  {
752  tty_error_msg(rc, errstr, MAXRBUF);
753  LOGF_ERROR("%s error: %s.", __FUNCTION__, errstr);
754  return -1;
755  }
756 
757  tcflush(PortFD, TCIOFLUSH);
758 
759  LOGF_DEBUG("RES <%c>", res[0]);
760 
761  return (res[0] == '1' ? 1 : 0);
762 }
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
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.
INumberVectorProperty GotoRotatorNP
void SetCapability(uint32_t cap)
SetRotatorCapability sets the Rotator capabilities. All capabilities must be initialized.
ISwitchVectorProperty HomeRotatorSP
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
void setRotatorConnection(const uint8_t &value)
setRotatorConnection Set Rotator connection mode. Child class should call this in the constructor bef...
Definition: pyxis.h:27
virtual IPState HomeRotator() override
HomeRotator Go to home position.
Definition: pyxis.cpp:440
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: pyxis.cpp:106
Pyxis()
Definition: pyxis.cpp:49
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: pyxis.cpp:57
const char * getDefaultName() override
Definition: pyxis.cpp:101
virtual IPState MoveRotator(double angle) override
MoveRotator Go to specific angle.
Definition: pyxis.cpp:461
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: pyxis.cpp:223
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: pyxis.cpp:246
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: pyxis.cpp:525
virtual bool ReverseRotator(bool enabled) override
ReverseRotator Reverse the direction of the rotator. CW is usually the normal direction,...
Definition: pyxis.cpp:502
virtual bool Handshake() override
perform handshake with device to check communication
Definition: pyxis.cpp:92
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
const char * INFO_TAB
INFO_TAB Where all the properties for general information are located.
void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
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_ATMOST1
Definition: indiapi.h:174
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
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
@ TTY_TIME_OUT
Definition: indicom.h:154
@ TTY_OVERFLOW
Definition: indicom.h:158
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
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
Definition: indidevapi.c:137
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 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 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 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
__u8 cmd[4]
Definition: pwc-ioctl.h:2
#define PYRIX_CMD
Definition: pyxis.cpp:33
std::unique_ptr< Pyxis > pyxis(new Pyxis())
#define PYXIS_2INCH_RATE
Definition: pyxis.cpp:38
#define PYXIS_3INCH_RATE
Definition: pyxis.cpp:37
#define POLL_100MS
Definition: pyxis.cpp:45
#define PYXIS_3INCH_PER_DEG
Definition: pyxis.cpp:41
#define PYXIS_2INCH_PER_DEG
Definition: pyxis.cpp:42
#define PYXIS_TIMEOUT
Definition: pyxis.cpp:31
#define PYRIX_BUF
Definition: pyxis.cpp:32
#define SETTINGS_TAB
Definition: pyxis.cpp:34
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250
double round(double value, int decimal_places)