Instrument Neutral Distributed Interface INDI  1.9.2
pmc8driver.cpp
Go to the documentation of this file.
1 /*
2  INDI Explore Scientific PMC8 driver
3 
4  Copyright (C) 2017 Michael Fulbright
5  Additional contributors:
6  Thomas Olson, Copyright (C) 2019
7  Karl Rees, Copyright (C) 2019-2021
8 
9  Based on IEQPro driver.
10 
11  This library is free software; you can redistribute it and/or
12  modify it under the terms of the GNU Lesser General Public
13  License as published by the Free Software Foundation; either
14  version 2.1 of the License, or (at your option) any later version.
15 
16  This library is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  Lesser General Public License for more details.
20 
21  You should have received a copy of the GNU Lesser General Public
22  License along with this library; if not, write to the Free Software
23  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25 
26 #include "pmc8driver.h"
27 
28 #include "indicom.h"
29 #include "indilogger.h"
30 #include "inditelescope.h"
31 
32 #include <libnova/julian_day.h>
33 #include <libnova/sidereal_time.h>
34 
35 #include <math.h>
36 #include <string.h>
37 #include <termios.h>
38 #include <unistd.h>
39 
40 #include <sys/ioctl.h>
41 
42 // only used for test timing of pulse guiding
43 #include <sys/time.h>
44 
45 #define PMC8_TIMEOUT 5 /* FD timeout in seconds */
46 
47 #define PMC8_SIMUL_VERSION_RESP "ESGvES06B9T9"
48 
49 // MOUNT_G11
50 #define PMC8_G11_AXIS0_SCALE 4608000.0
51 #define PMC8_G11_AXIS1_SCALE 4608000.0
52 // MOUNT_EXOS2
53 #define PMC8_EXOS2_AXIS0_SCALE 4147200.0
54 #define PMC8_EXOS2_AXIS1_SCALE 4147200.0
55 // MOUNT_iEXOS100
56 #define PMC8_iEXOS100_AXIS0_SCALE 4147200.0
57 #define PMC8_iEXOS100_AXIS1_SCALE 4147200.0
58 
59 // Need to initialize to some value, or certain clients (e.g., KStars Lite) freak out
62 
63 #define ARCSEC_IN_CIRCLE 1296000.0
64 
65 // Reference says 2621.44 counts, which then needs to be multiplied by 25 (so actually 16^4-1)
66 // However, on Exos2 62500 (F424) is reported when slewing
67 #define PMC8_MAX_PRECISE_MOTOR_RATE 62500
68 
69 // any guide pulses less than this are ignored as it will not result in any actual motor motion
70 #define PMC8_PULSE_GUIDE_MIN_MS 20
71 
72 // guide pulses longer than this require using a timer
73 #define PMC8_PULSE_GUIDE_MAX_NOTIMER 250
74 
75 #define PMC8_MAX_RETRIES 3 /*number of times to retry reading a response */
76 #define PMC8_RETRY_DELAY 30000 /* how long to wait before retrying i/o */
77 #define PMC8_MAX_IO_ERROR_THRESHOLD 2 /* how many consecutive read timeouts before trying to reset the connection */
78 
79 #define PMC8_RATE_SIDEREAL 15.0
80 #define PMC8_RATE_LUNAR 14.685
81 #define PMC8_RATE_SOLAR 15.041
82 #define PMC8_RATE_KING 15.0369
83 
85 bool pmc8_debug = false;
86 bool pmc8_simulation = false;
87 bool pmc8_isRev2Compliant = false;
88 bool pmc8_reconnect_flag = false;
89 bool pmc8_goto_resume = true;
91 char pmc8_device[MAXINDIDEVICE] = "PMC8";
92 double pmc8_latitude = 0; // must be kept updated by pmc8.cpp when it is changed!
93 double pmc8_longitude = 0; // must be kept updated by pmc8.cpp when it is changed!
96 int pmc8_east_dir = 1; // 1 is for northern hemisphere, switch to 0 for southern
98 
99 // state variable for driver based pulse guiding
100 typedef struct
101 {
102  bool pulseguideactive = false;
103  bool fakepulse = false;
104  int ms;
105  long long pulse_start_us;
106  double cur_rate;
107  int cur_dir;
108  double new_rate;
109  int new_dir;
111 
112 // need one for NS and EW pulses which may be simultaneous
114 
115 struct
116 {
117  double ra;
118  double dec;
121  double trackRate;
122  double moveRate;
123  double guide_rate;
124 } simPMC8Data;
125 
126 // convert mount count to 6 character two complement hex string
127 void convert_motor_counts_to_hex(int val, char *hex)
128 {
129  unsigned tmp;
130  char h[16];
131 
132  if (val < 0)
133  {
134  tmp=abs(val);
135  tmp=~tmp;
136  tmp++;
137  }
138  else
139  {
140  tmp=val;
141  }
142 
143  sprintf(h, "%08X", tmp);
144 
145  strcpy(hex, h+2);
146 
147  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_motor_counts_to_hex val=%d, h=%s, hex=%s", val, h, hex);
148 }
149 
150 // convert rate in arcsec/sidereal_second to internal PMC8 precise motor rate for RA axis tracking ONLY
151 bool convert_precise_rate_to_motor(double rate, int *mrate)
152 {
153 
154  *mrate = round(25*rate*(PMC8_AXIS0_SCALE/ARCSEC_IN_CIRCLE));
155 
156  if (*mrate > PMC8_MAX_PRECISE_MOTOR_RATE)
157  {
158  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_WARNING, "requested tracking motor rate %d exceeds maximum, using %d", *mrate,PMC8_MAX_PRECISE_MOTOR_RATE);
160  }
161  else if (*mrate < -PMC8_MAX_PRECISE_MOTOR_RATE)
162  {
163  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_WARNING, "requested tracking motor rate %d exceeds maximum, using %d", *mrate,-PMC8_MAX_PRECISE_MOTOR_RATE);
164  *mrate = -PMC8_MAX_PRECISE_MOTOR_RATE;
165  }
166 
167  return true;
168 }
169 
170 // convert rate in arcsec/sidereal_second to internal PMC8 precise motor rate for RA axis tracking ONLY
171 bool convert_precise_motor_to_rate(int mrate, double *rate)
172 {
173  *rate = ((double)mrate) * (ARCSEC_IN_CIRCLE/PMC8_AXIS0_SCALE)/25;
174 
175  return true;
176 }
177 
178 // convert rate in arcsec/sidereal_second to internal PMC8 motor rate for move action (not slewing)
179 bool convert_move_rate_to_motor(float rate, int *mrate)
180 {
181 
182  float capped_move_rate = rate;
183  if (rate > PMC8_MAX_MOVE_RATE)
184  capped_move_rate = PMC8_MAX_MOVE_RATE;
185  else if (rate < -PMC8_MAX_MOVE_RATE)
186  capped_move_rate = -PMC8_MAX_MOVE_RATE;
187 
188  *mrate = (int)(capped_move_rate*(PMC8_AXIS0_SCALE/ARCSEC_IN_CIRCLE));
189 
190  return true;
191 }
192 
193 // convert rate internal PMC8 motor rate to arcsec/sec for move action (not slewing)
194 bool convert_motor_rate_to_move_rate(int mrate, double *rate)
195 {
196  *rate = ((double)mrate)*ARCSEC_IN_CIRCLE/PMC8_AXIS0_SCALE;
197 
198  return true;
199 }
200 
202 {
203  switch(index)
204  {
205  case 0: // LosMandy G11
208  break;
209  case 1: // EXOS2
212  break;
213  case 2: // iEXOS100
216  break;
217  default:
218  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Need To Select a Mount");
219  break;
220  }
221 }
222 
223 void set_pmc8_debug(bool enable)
224 {
225  pmc8_debug = enable;
226 }
227 
228 void set_pmc8_simulation(bool enable)
229 {
230  pmc8_simulation = enable;
231  if (enable)
232  simPMC8Data.guide_rate = 0.5;
233 }
234 
235 void set_pmc8_device(const char *name)
236 {
237  strncpy(pmc8_device, name, MAXINDIDEVICE);
238 }
239 
240 void set_pmc8_location(double latitude, double longitude)
241 {
242  pmc8_latitude = latitude;
243  pmc8_longitude = longitude;
244 
245  pmc8_east_dir = (latitude < 0)?0:1;
246 
247  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "Set PMC8 'lowlevel' lat:%f long:%f",pmc8_latitude, pmc8_longitude);
248 }
249 
251 {
252  simPMC8Info.systemStatus = value;
253 
254  if (value == ST_PARKED)
255  {
256  double lst;
257  double ra;
258 
260 
261  ra = lst + 6;
262  if (ra > 24)
263  ra -= 24;
264 
266  set_pmc8_sim_dec(90.0);
267  }
268 }
269 
271 {
272  simPMC8Data.trackRate = value;
273 }
274 
275 void set_pmc8_sim_move_rate(int value)
276 {
277  simPMC8Data.moveRate = value;
278 }
279 
280 void set_pmc8_sim_ra(double ra)
281 {
282  simPMC8Data.ra = ra;
283 }
284 
285 void set_pmc8_sim_dec(double dec)
286 {
287  simPMC8Data.dec = dec;
288 }
289 
291 {
292  pmc8_connection = connection;
293 
294  if (connection == PMC8_ETHERNET)
295  {
296  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Connecting to PMC8 via Ethernet.");
297  }
298  else
299  {
300  if (connection == PMC8_SERIAL_STANDARD) DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Connecting to PMC8 via standard Serial cable. Please wait 15 seconds for mount to reset.");
301  else if (connection == PMC8_SERIAL_AUTO) DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Connecting to PMC8 via Serial. Autodecting cable type. This could take up to 30 seconds.");
302  else DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Connecting to PMC8 via inverted Serial.");
303  }
304 
305  if (connection != PMC8_SERIAL_STANDARD)
306  {
307  for (int i = 0; i < 2; i++)
308  {
309  if (i) DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Retrying...");
310 
311  if (detect_pmc8(fd)) return true;
312  usleep(PMC8_RETRY_DELAY);
313  }
314  }
315 
316  if ((connection == PMC8_SERIAL_STANDARD) || (connection == PMC8_SERIAL_AUTO))
317  {
318  // If they're not using a custom-configured cable, we need to clear DTR for serial to start working
319  // But this resets the PMC8, so only do it after we've already checked for connection
320  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "Attempting to clear DTR for standard cable.");
321  int serial = TIOCM_DTR;
322  ioctl(fd,TIOCMBIC,&serial);
323 
324  // when we clear DTR, the PMC8 will respond with initialization screen, so may need read several times
325  for (int i = 0; i < 2; i++)
326  {
327  if (i) DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_SESSION, "Retrying...");
328 
329  if (detect_pmc8(fd))
330  {
331  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_WARNING, "Connected to PMC8 using a standard-configured FTDI cable."
332  "Your mount will reset and lose its position anytime you disconnect and reconnect."
333  "See http://indilib.org/devices/telescopes/explore-scientific-g11-pmc-eight/ ");
334  return true;
335  }
336  usleep(PMC8_RETRY_DELAY);
337  }
338  }
339 
340  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "check_pmc8_connection(): Error connecting. Check power and connection settings.");
341 
342  return false;
343 }
344 
345 bool detect_pmc8(int fd)
346 {
347  char initCMD[] = "ESGv!";
348  int errcode = 0;
349  char errmsg[MAXRBUF];
350  char response[64];
351  int nbytes_read = 0;
352  int nbytes_written = 0;
353 
354  if (pmc8_simulation)
355  {
356  strcpy(response, PMC8_SIMUL_VERSION_RESP);
357  nbytes_read = strlen(response);
358  }
359  else
360  {
361  if ((errcode = send_pmc8_command(fd, initCMD, strlen(initCMD), &nbytes_written)) != TTY_OK)
362  {
363  tty_error_msg(errcode, errmsg, MAXRBUF);
364  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error connecting on write: %s", errmsg);
365  return false;
366  }
367 
368  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, "ESGv")))
369  {
370  tty_error_msg(errcode, errmsg, MAXRBUF);
371  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "Error connecting on read: %s", errmsg);
372  return false;
373  }
374  }
375 
376  // return true if valid firmware response
377  return (!strncmp(response, "ESGvES", 6));
378 }
379 
381 {
382  // Only one model for now
383  info->Model.assign("PMC-Eight");
384 
385  // Set the mount type from firmware if we can (instead of relying on interface)
386  // older firmware has type in firmware string
388  {
389  INDI_UNUSED(fd);
390 
391  if (strstr(info->MainBoardFirmware.c_str(),"G11"))
392  {
393  info->MountType = MOUNT_G11;
394  }
395  else if (strstr(info->MainBoardFirmware.c_str(),"EXOS2"))
396  {
397  info->MountType = MOUNT_EXOS2;
398  }
399  else if (strstr(info->MainBoardFirmware.c_str(),"ES1A"))
400  {
401  info->MountType = MOUNT_iEXOS100;
402  }
403  }
404  else
405  {
406  //for newer firmware, need to use ESGi to get mount type
407  char cmd[] = "ESGi!";
408  int errcode = 0;
409  char errmsg[MAXRBUF];
410  char response[64];
411  int nbytes_read = 0;
412  int nbytes_written = 0;
413 
414  if (pmc8_simulation)
415  {
416  strcpy(response, PMC8_SIMUL_VERSION_RESP);
417  nbytes_read = strlen(response);
418  }
419  else
420  {
421  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
422  {
423  tty_error_msg(errcode, errmsg, MAXRBUF);
425  return false;
426  }
427 
428  if ((errcode = get_pmc8_response(fd, response, &nbytes_read,"ESGi")))
429  {
430  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "get_pmc8_main_firmware(): Error reading response.");
431  return false;
432  }
433 
434  //ESGi response should be 31 characters
435  if (nbytes_read >= 31)
436  {
437  //locate P9 code in response
438  char num_str[3] = {0};
439  strncat(num_str, response+20, 2);
440  int p9 = (int)strtol(num_str, nullptr, 10);
441 
442  // Set mount type based on P9 code
443  if (p9 <= 1) info->MountType = MOUNT_iEXOS100;
444  // these codes are reserved. I'm assuming for something like iExos100, so let's go with that
445  else if (p9 <= 3)
446  {
447  info->MountType = MOUNT_iEXOS100;
448  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Unrecognized device code #%d. Treating as iEXOS100.", p9);
449  }
450  else if (p9 <= 7) info->MountType = MOUNT_G11;
451  else if (p9 <= 11) info->MountType = MOUNT_EXOS2;
452  // unrecognized code. Just going to guess and treat as iExos100.
453  else
454  {
455  info->MountType = MOUNT_iEXOS100;
456  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Unrecognized device code #%d. Treating as iEXOS100.", p9);
457  }
458 
459  }
460  else
461  {
462  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Could not detect device type. Only received #%d bytes, expected at least 31.", nbytes_read);
463  return false;
464  }
465 
466  tcflush(fd, TCIFLUSH);
467  }
468  }
469  // update mount parameters
471  return true;
472 }
473 
475 {
476  char cmd[] = "ESGv!";
477  char board[64];
478  int errcode = 0;
479  char errmsg[MAXRBUF];
480  char response[64];
481  int nbytes_read = 0;
482  int nbytes_written = 0;
483 
484  if (pmc8_simulation)
485  {
486  strcpy(response, PMC8_SIMUL_VERSION_RESP);
487  nbytes_read = strlen(response);
488  }
489  else
490  {
491  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
492  {
493  tty_error_msg(errcode, errmsg, MAXRBUF);
495  return false;
496  }
497 
498  if ((errcode = get_pmc8_response(fd, response, &nbytes_read,"ESGv")))
499  {
500  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "get_pmc8_main_firmware(): Error reading response.");
501  return false;
502  }
503  }
504 
505  // prior to v2, minimum size firmware string is 12 (for iExos100), 14 for others, but can be up to 20
506  // post v2, can be 50+
507  if (nbytes_read >= 12)
508  {
509 
510  // strip ESGvES from string when getting firmware version
511  strncpy(board, response+6, nbytes_read-7);
512  info->MainBoardFirmware.assign(board, nbytes_read-7);
513 
514  // Assuming version strings longer than 24 must be version 2.0 and up
515  if (nbytes_read > 24) info->IsRev2Compliant = pmc8_isRev2Compliant = true;
516 
517  tcflush(fd, TCIFLUSH);
518 
519  return true;
520  }
521 
522  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Could not read firmware. Only received #%d bytes, expected at least 12.", nbytes_read);
523  return false;
524 }
525 
527 {
528  bool rc = false;
529 
530  rc = get_pmc8_main_firmware(fd, info);
531 
532  if (rc == false)
533  return rc;
534 
535  rc = get_pmc8_model(fd, info);
536 
537  return rc;
538 }
539 
540 // return move rate in arcsec / sec
541 bool get_pmc8_move_rate_axis(int fd, PMC8_AXIS axis, double &rate)
542 {
543  char cmd[32];
544  int errcode = 0;
545  char errmsg[MAXRBUF];
546  char response[16];
547  int nbytes_read = 0;
548  int nbytes_written = 0;
549 
550  snprintf(cmd, sizeof(cmd), "ESGr%d!", axis);
551 
552  if (pmc8_simulation)
553  {
554  if (axis == PMC8_AXIS_RA)
555  rate = simPMC8Data.trackRate;
556  else if (axis == PMC8_AXIS_DEC)
557  rate = 0; // DEC tracking not supported yet
558  else
559  return false;
560 
561  return true;
562  }
563 
564  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
565  {
566  tty_error_msg(errcode, errmsg, MAXRBUF);
568  return false;
569  }
570 
571  cmd[5] = '\0';
572 
573  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, cmd)))
574  {
575  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error getting Move Rate");
576  return false;
577  }
578 
579  if (nbytes_read != 10)
580  {
581  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis get move rate cmd response incorrect");
582  return false;
583  }
584 
585  char num_str[16]= {0};
586 
587  strcpy(num_str, "0X");
588  strncat(num_str, response+5, 6);
589 
590  int mrate = (int)strtol(num_str, nullptr, 0);
591 
592  convert_motor_rate_to_move_rate(mrate,&rate);
593 
594  return true;
595 }
596 
597 bool get_pmc8_direction_axis(int fd, PMC8_AXIS axis, int &dir)
598 {
599  char cmd[32];
600  int errcode = 0;
601  char errmsg[MAXRBUF];
602  char response[16];
603  int nbytes_read = 0;
604  int nbytes_written = 0;
605 
606  snprintf(cmd, sizeof(cmd), "ESGd%d!", axis);
607 
608  if (pmc8_simulation)
609  {
610  if (axis == PMC8_AXIS_RA)
611  dir = simPMC8Data.raDirection;
612  else if (axis == PMC8_AXIS_DEC)
613  dir = simPMC8Data.decDirection;
614  else
615  return false;
616 
617  return true;
618  }
619 
620  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
621  {
622  tty_error_msg(errcode, errmsg, MAXRBUF);
624  return false;
625  }
626 
627  cmd[5] = '\0';
628 
629  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, cmd)))
630  {
631  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error getting direction axis");
632  return false;
633  }
634 
635  if (nbytes_read != 7)
636  {
637  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis get dir cmd response incorrect");
638  return false;
639  }
640 
641  char num_str[16]= {0};
642 
643  strncat(num_str, response+5, 2);
644 
645  dir = (int)strtol(num_str, nullptr, 0);
646 
647  return true;
648 }
649 
650 // if fast is true dont wait on response! Used for psuedo-pulse guide
651 // NOTE that this will possibly mean the response will be read by a following command if it is called before
652 // response comes from controller, since next command will flush before data is in buffer!
653 bool set_pmc8_direction_axis(int fd, PMC8_AXIS axis, int dir, bool fast)
654 {
655 
656  char cmd[32], expresp[32];
657  int errcode = 0;
658  char errmsg[MAXRBUF];
659  char response[16];
660  int nbytes_read = 0;
661  int nbytes_written = 0;
662 
663  snprintf(cmd, sizeof(cmd), "ESSd%d%d!", axis, dir);
664 
665  if (pmc8_simulation)
666  {
667  if (axis == PMC8_AXIS_RA)
668  simPMC8Data.raDirection = (PMC8_DIRECTION) dir;
669  else if (axis == PMC8_AXIS_DEC)
670  simPMC8Data.decDirection = (PMC8_DIRECTION) dir;
671  else
672  return false;
673 
674  return true;
675  }
676 
677  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
678  {
679  tty_error_msg(errcode, errmsg, MAXRBUF);
681  return false;
682  }
683 
684  if (fast)
685  {
686  return true;
687  }
688 
689  snprintf(expresp, sizeof(expresp), "ESGd%d%d!", axis, dir);
690 
691  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, expresp)))
692  {
693  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis get dir cmd response incorrect: expected=%s", expresp);
694  return false;
695  }
696 
697  return true;
698 }
699 
700 bool get_pmc8_is_scope_slewing(int fd, bool &isslew)
701 {
702  double rarate;
703  double decrate;
704  bool rc;
705 
707  if (!rc)
708  {
709  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "get_pmc8_is_scope_slewing(): Error reading RA move rate");
710  return false;
711  }
712 
714  if (!rc)
715  {
716  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "get_pmc8_is_scope_slewing(): Error reading DEC move rate");
717  return false;
718  }
719 
720  if (pmc8_simulation)
721  {
722  isslew = (simPMC8Info.systemStatus == ST_SLEWING);
723  }
724  else
725  {
726  isslew = ((rarate > PMC8_MAX_TRACK_RATE) || (decrate >= PMC8_MAX_TRACK_RATE));
727  }
728 
729  return true;
730 }
731 
732 // set move speed in terms of how many times sidereal
733 bool set_pmc8_move_rate_axis(int fd, PMC8_DIRECTION dir, int reqrate)
734 {
735  int rate = reqrate;
736 
737  if (rate > PMC8_MAX_MOVE_RATE)
738  rate = PMC8_MAX_MOVE_RATE;
739  else if (rate < -PMC8_MAX_MOVE_RATE)
740  rate = -PMC8_MAX_MOVE_RATE;
741 
742  switch (dir)
743  {
744  case PMC8_S:
745  rate = -rate;
746  [[fallthrough]];
747  case PMC8_N:
748  return set_pmc8_custom_dec_move_rate(fd, rate);
749  case PMC8_E:
750  rate = -rate;
751  [[fallthrough]];
752  case PMC8_W:
753  return set_pmc8_custom_ra_move_rate(fd, rate);
754  }
755 
756  return false;
757 }
758 
760 {
761  bool rc;
762 
763  // stop tracking
765  if (!rc)
766  {
767  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error stopping RA axis!");
768  return false;
769  }
770 
771  return true;
772 }
773 
774 // get current (precise) tracking rate in arcsec/sec
775 bool get_pmc8_track_rate(int fd, double &rate)
776 {
777  char cmd[32];
778  int errcode = 0;
779  char errmsg[MAXRBUF];
780  char response[16];
781  int nbytes_read = 0;
782  int nbytes_written = 0;
783 
784  snprintf(cmd, sizeof(cmd), "ESGx!");
785 
786  if (pmc8_simulation)
787  {
788  rate = simPMC8Data.trackRate;
789  return true;
790  }
791 
792  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
793  {
794  tty_error_msg(errcode, errmsg, MAXRBUF);
796  return false;
797  }
798 
799  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, "ESGx")))
800  {
801  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error getting Tracking Rate");
802  return false;
803  }
804 
805  if (nbytes_read != 9)
806  {
807  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Get track rate cmd response incorrect");
808  return false;
809  }
810 
811  char num_str[16]= {0};
812 
813  strcpy(num_str, "0X");
814  strncat(num_str, response+4, 4);
815 
816  int mrate = (int)strtol(num_str, nullptr, 0);
817  convert_precise_motor_to_rate(mrate, &rate);
818 
819  return true;
820 }
821 
822 bool get_pmc8_tracking_data(int fd, double &rate, uint8_t &mode)
823 {
824 
825  if (!get_pmc8_track_rate(fd,rate)) return false;
826 
827  int refmotor, tmotor;
828 
829  //get our current precise motor rate
830  bool rc = convert_precise_rate_to_motor(rate,&tmotor);
831 
832  //now check what sidereal would be
834  if (tmotor == refmotor) mode = PMC8_TRACK_SIDEREAL;
835  else
836  {
837 
838  //now check lunar
840  if (tmotor == refmotor) mode = PMC8_TRACK_LUNAR;
841  else
842  {
843 
844  //now check solar
846  if (tmotor == refmotor) mode = PMC8_TRACK_SOLAR;
847  else
848  {
849 
850  //now check king
852  if (tmotor == refmotor) mode = PMC8_TRACK_KING;
853  // must be custom
854  else mode = PMC8_TRACK_CUSTOM;
855  }
856  }
857  }
858 
859  return true;
860 }
861 
862 
863 // set speed for move action (MoveNS/MoveWE) NOT slews! This version DOESNT handle direction and expects a motor rate!
864 // if fast is true dont wait on response! Used for psuedo-pulse guide
865 // NOTE that this will possibly mean the response will be read by a following command if it is called before
866 // response comes from controller, since next command will flush before data is in buffer!
867 bool set_pmc8_axis_motor_rate(int fd, PMC8_AXIS axis, int mrate, bool fast)
868 {
869  char cmd[24];
870  int errcode = 0;
871  char errmsg[MAXRBUF];
872  char response[24];
873  int nbytes_read = 0;
874  int nbytes_written = 0;
875 
876  snprintf(cmd, sizeof(cmd), "ESSr%d%04X!", axis, mrate);
877 
878  if (pmc8_simulation)
879  {
880  return true;
881  }
882 
883  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
884  {
885  tty_error_msg(errcode, errmsg, MAXRBUF);
887  return false;
888  }
889 
890  if (fast)
891  {
892  return true;
893  }
894 
895  snprintf(cmd, sizeof(cmd), "ESGr%d", axis);
896 
897  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, cmd)))
898  {
899  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error setting axis motor rate");
900  return false;
901  }
902 
903  if (nbytes_read == 10)
904  {
905  tcflush(fd, TCIFLUSH);
906  return true;
907  }
908 
909  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Only received #%d bytes, expected 10.", nbytes_read);
910  return false;
911 }
912 
913 // set speed for move action (MoveNS/MoveWE) NOT slews! This version accepts arcsec/sec as rate.
914 // also handles direction
915 bool set_pmc8_axis_move_rate(int fd, PMC8_AXIS axis, float rate)
916 {
917  bool rc;
918  int motor_rate;
919 
920  // set direction
921  if (rate < 0)
922  rc=set_pmc8_direction_axis(fd, axis, 0, false);
923  else
924  rc=set_pmc8_direction_axis(fd, axis, 1, false);
925 
926  if (!rc)
927  return rc;
928 
929  if (!convert_move_rate_to_motor(fabs(rate), &motor_rate))
930  {
931  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error converting rate %f", rate);
932  return false;
933  }
934 
935  rc = set_pmc8_axis_motor_rate(fd, axis, motor_rate, false);
936 
937  if (pmc8_simulation)
938  {
939  simPMC8Data.moveRate = rate;
940  return true;
941  }
942 
943  return rc;
944 }
945 
946 #if 0
947 bool set_pmc8_track_enabled(int fd, bool enabled)
948 {
949  char cmd[32];
950  int errcode = 0;
951  char errmsg[MAXRBUF];
952  char response[8];
953  int nbytes_read = 0;
954  int nbytes_written = 0;
955 
956  snprintf(cmd, 32, ":ST%d#", enabled ? 1 : 0);
957 
959 
960  if (pmc8_simulation)
961  {
962  // FIXME - need to implement pmc8 track enabled sim
963 // simPMC8Info.systemStatus = enabled ? ST_TRACKING_PEC_ON : ST_STOPPED;
964 // strcpy(response, "1");
965 // nbytes_read = strlen(response);
966 
967  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Need to implement pmc8 track enabled sim");
968  return false;
969  }
970  else
971  {
972  // determine current tracking mode
973 
974 // return SetTrackMode(enabled ? IUFindOnSwitchIndex(&TrackModeSP) : AP_TRACKING_OFF);
975  }
976 }
977 #endif
978 
979 bool set_pmc8_track_mode(int fd, uint8_t mode)
980 {
981  float ratereal=0;
982 
983  switch (mode)
984  {
985  case PMC8_TRACK_SIDEREAL:
986  ratereal = PMC8_RATE_SIDEREAL;
987  break;
988  case PMC8_TRACK_LUNAR:
989  ratereal = PMC8_RATE_LUNAR;
990  break;
991  case PMC8_TRACK_SOLAR:
992  ratereal = PMC8_RATE_SOLAR;
993  break;
994  case PMC8_TRACK_KING:
995  ratereal = PMC8_RATE_KING;
996  break;
997  default:
998  return false;
999  break;
1000  }
1001 
1002  if (!set_pmc8_direction_axis(fd, PMC8_AXIS_RA, pmc8_east_dir, false)) return false;
1003  return set_pmc8_custom_ra_track_rate(fd, ratereal);
1004 }
1005 
1006 // start tracking at a precision track rate
1007 bool set_pmc8_ra_tracking(int fd, double rate)
1008 {
1009  //set right direction
1010  int direction = pmc8_east_dir;
1011  if (rate<0) direction = !direction;
1012  if (!set_pmc8_direction_axis(fd, PMC8_AXIS_RA, direction, false)) return false;
1013 
1014  //then set rate
1015  return (set_pmc8_custom_ra_track_rate(fd, fabs(rate)));
1016 }
1017 
1018 // just set the precision track rate - for when we've already set tracking direction
1019 bool set_pmc8_custom_ra_track_rate(int fd, double rate)
1020 {
1021  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_custom_ra_track_rate() called rate=%f ", rate);
1022 
1023  char cmd[24];
1024  int errcode = 0;
1025  char errmsg[MAXRBUF];
1026  char response[24];
1027  int nbytes_read = 0;
1028  int nbytes_written = 0;
1029  int rateval;
1030 
1031  if (!convert_precise_rate_to_motor(rate, &rateval))
1032  {
1033  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error converting rate %f", rate);
1034  return false;
1035  }
1036 
1037  snprintf(cmd, sizeof(cmd), "ESTr%04X!", rateval);
1038 
1039  if (pmc8_simulation)
1040  {
1041  simPMC8Data.trackRate = rate;
1042  return true;
1043  }
1044  else
1045  {
1046  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1047  {
1048  tty_error_msg(errcode, errmsg, MAXRBUF);
1050  return false;
1051  }
1052 
1053  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, "ESGx")))
1054  {
1055  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error setting custom RA track rate");
1056  return false;
1057  }
1058  }
1059 
1060  if (nbytes_read != 9)
1061  {
1062  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Only received #%d bytes, expected 9.", nbytes_read);
1063  return false;
1064  }
1065 
1066  tcflush(fd, TCIFLUSH);
1067 
1068  // set direction to 1
1069  // return set_pmc8_direction_axis(fd, PMC8_AXIS_RA, 1, false);
1070  return true;
1071 }
1072 
1073 #if 0
1074 bool set_pmc8_custom_dec_track_rate(int fd, double rate)
1075 {
1076  bool rc;
1077 
1078  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_custom_dec_track_rate() called rate=%f ", rate);
1079 
1080  if (pmc8_simulation)
1081  {
1082  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_custom_dec_track_rate simulation not implemented");
1083 
1084  rc=false;
1085  }
1086  else
1087  {
1088  rc=set_pmc8_axis_rate(fd, PMC8_AXIS_DEC, rate);
1089  }
1090 
1091  return rc;
1092 }
1093 #else
1094 bool set_pmc8_custom_dec_track_rate(int fd, double rate)
1095 {
1096  INDI_UNUSED(fd);
1097  INDI_UNUSED(rate);
1098 
1099  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_custom_dec_track_rate not implemented!");
1100  return false;
1101 }
1102 #endif
1103 
1104 bool set_pmc8_custom_ra_move_rate(int fd, double rate)
1105 {
1106  bool rc;
1107 
1108  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_custom_ra move_rate() called rate=%f ", rate);
1109 
1110  // safe guard for now - only all use to STOP slewing or MOVE commands with this
1111  if (fabs(rate) > PMC8_MAX_MOVE_RATE)
1112  {
1113  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_custom_ra_move rate only supports low rates currently");
1114 
1115  return false;
1116  }
1117 
1119 
1120  return rc;
1121 }
1122 
1123 bool set_pmc8_custom_dec_move_rate(int fd, double rate)
1124 {
1125  bool rc;
1126 
1127  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_custom_dec_move_rate() called rate=%f ", rate);
1128 
1129  // safe guard for now - only all use to STOP slewing with this
1130  if (fabs(rate) > PMC8_MAX_MOVE_RATE)
1131  {
1132  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_custom_dec_move_rate only supports low rates currently");
1133  return false;
1134  }
1135 
1137 
1138  return rc;
1139 }
1140 
1141 // rate is fraction of sidereal
1142 bool set_pmc8_guide_rate(int fd, PMC8_AXIS axis, double rate)
1143 {
1144  if (pmc8_simulation)
1145  {
1146  simPMC8Data.guide_rate = rate;
1147  return true;
1148  }
1149 
1150  // set driver values
1151  if (axis == PMC8_AXIS_RA)
1152  {
1154  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_guide_rate: ra guide rate set to %f", rate);
1155  }
1156  if ((axis == PMC8_AXIS_DEC) || !pmc8_isRev2Compliant)
1157  {
1159  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "set_pmc8_guide_rate: dec guide rate set to %f", rate);
1160  }
1161 
1162  if (pmc8_isRev2Compliant)
1163  {
1164  // now write to mount to sync ST4 rates
1165  char cmd[32], expresp[32];
1166  int errcode = 0;
1167  char errmsg[MAXRBUF];
1168  char response[16];
1169  int nbytes_read = 0;
1170  int nbytes_written = 0;
1171 
1172  snprintf(cmd, sizeof(cmd), "ESSf%d%02X!", axis, int(rate*100));
1173 
1174  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1175  {
1176  tty_error_msg(errcode, errmsg, MAXRBUF);
1178  return false;
1179  }
1180 
1181  snprintf(expresp, sizeof(expresp), "ESGf%d%02X!", axis, int(rate*100));
1182 
1183  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, expresp)))
1184  {
1185  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "SRF set cmd response incorrect: expected=%s", expresp);
1186  return false;
1187  }
1188  }
1189 
1190  return true;
1191 }
1192 
1193 // get SRF value for axis
1194 bool get_pmc8_guide_rate(int fd, PMC8_AXIS axis, double &rate)
1195 {
1196  if (pmc8_simulation)
1197  {
1198  rate = simPMC8Data.guide_rate;
1199  return true;
1200  }
1201 
1202  // read from mount
1203  char cmd[32];
1204  int errcode = 0;
1205  char errmsg[MAXRBUF];
1206  char response[16];
1207  int nbytes_read = 0;
1208  int nbytes_written = 0;
1209 
1210  snprintf(cmd, sizeof(cmd), "ESGf%d!", axis);
1211 
1212  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1213  {
1214  tty_error_msg(errcode, errmsg, MAXRBUF);
1216  return false;
1217  }
1218 
1219  cmd[5] = '\0';
1220 
1221  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, cmd)))
1222  {
1223  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error getting SRF rate");
1224  return false;
1225  }
1226 
1227  if (nbytes_read != 8)
1228  {
1229  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "SRF Get rate cmd response incorrect");
1230  return false;
1231  }
1232 
1233  char num_str[16]= {0};
1234 
1235  strcpy(num_str, "0X");
1236  strncat(num_str, response+5, 2);
1237  int tint = strtol(num_str, nullptr, 0);
1238 
1239  rate = ((double)tint)/100;
1240 
1241  // set driver values
1242  if (axis == PMC8_AXIS_RA)
1243  {
1245  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "get_pmc8_guide_rate: ra guide rate set to %f", rate);
1246  }
1247  else
1248  {
1250  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "get_pmc8_guide_rate: dec guide rate set to %f", rate);
1251  }
1252 
1253  return true;
1254 }
1255 
1257 {
1258 
1259  switch (gdir)
1260  {
1261  case PMC8_N:
1262  case PMC8_S:
1263  *pstate = &NS_PulseGuideState;
1264  break;
1265 
1266  case PMC8_W:
1267  case PMC8_E:
1268  *pstate = &EW_PulseGuideState;
1269  break;
1270 
1271  default:
1272  return false;
1273  break;
1274  }
1275  return true;
1276 }
1277 
1278 // if return value is true then timetaken will return how much pulse time has already occurred
1279 bool start_pmc8_guide(int fd, PMC8_DIRECTION gdir, int ms, long &timetaken_us, double ratehint)
1280 {
1281  bool rc;
1282  double cur_rate = 0;
1283  int cur_dir = -1;
1284 
1285  // used to test timing
1286  struct timeval tp;
1287  long long pulse_start_us;
1288  long long pulse_sofar_us;
1289 
1290  PulseGuideState *pstate;
1291 
1292  if (!get_pmc8_guide_state(gdir, &pstate))
1293  {
1294  return false;
1295  }
1296 
1297  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): pulse dir=%d dur=%d ms", gdir, ms);
1298 
1299  if (pstate->pulseguideactive)
1300  {
1301  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_start_guide(): already executing a pulse guide!");
1302  return false;
1303  }
1304 
1305  // ignore short pulses - they do nothing
1306  if (ms < PMC8_PULSE_GUIDE_MIN_MS)
1307  {
1308  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): ignore short pulse ms=%d ms", ms);
1309  timetaken_us = ms*1000;
1310  pstate->pulseguideactive = true;
1311  pstate->fakepulse = true;
1312  return true;
1313  }
1314 
1315  // get precise tracking rate if in RA
1316  if ((gdir == PMC8_E) || (gdir == PMC8_W))
1317  {
1318 
1319  // use rate provided by interface if valid rather than querying for it
1320  if (ratehint <= 0)
1321  {
1322  rc = get_pmc8_track_rate(fd, cur_rate);
1323  if (!rc)
1324  {
1325  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_start_guide(): error reading current RA rate!");
1326  return rc;
1327  }
1328  }
1329  else
1330  cur_rate = ratehint;
1331  }
1332  // we could get slew rate if in DEC, but driver doesn't currently support DEC tracking
1333  // and we shouldn't get here if we're slewing, so for now we assume interface is always correct and avoid delay from unnecessary calls to mount
1334  else
1335  {
1336  cur_rate = ratehint;
1337  }
1338 
1339  // if slewing abort
1340  // shouldn't get here if slewing, but doesn't hurt to check
1341  if (cur_rate > PMC8_MAX_TRACK_RATE)
1342  {
1343  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_start_guide(): Cannot send guide correction while slewing! rate=%d dir=%d",
1344  cur_rate, gdir);
1345  return rc;
1346  }
1347 
1348  double new_rate = cur_rate;
1349  int new_dir = 0;
1350 
1351  // RA guiding routine just changes the precision tracking call
1352  if ((gdir == PMC8_E) || (gdir == PMC8_W))
1353  {
1355 
1356  if (gdir == PMC8_E) new_rate -= guide_rate;
1357  else new_rate += guide_rate;
1358 
1359  if (new_rate < 0)
1360  {
1361  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): with current tracking rate of %f, requested guide rate of %f would flip RA motor in opposite direction, so pausing motor instead.", cur_rate, new_rate);
1362  new_rate = 0;
1363  }
1364 
1365  // measure time when we start pulse
1366  gettimeofday(&tp, nullptr);
1367  pulse_start_us = tp.tv_sec*1000000+tp.tv_usec;
1368 
1369  if (!set_pmc8_custom_ra_track_rate(fd, new_rate))
1370  {
1371  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_start_guide(): error settings new_rate to %f",new_rate);
1372  return false;
1373  }
1374 
1375  }
1376  // DEC guiding routine needs to set a DEC move rate and possibly a new direction
1377  else if ((gdir == PMC8_N) || (gdir == PMC8_S))
1378  {
1380 
1381  if (gdir == PMC8_S) new_rate -= guide_rate;
1382  else new_rate += guide_rate;
1383 
1384  if (new_rate < 0) new_dir = 1;
1385 
1386  int mrate;
1387  if (!convert_move_rate_to_motor(fabs(new_rate), &mrate))
1388  {
1389  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error converting rate %f", new_rate);
1390  return false;
1391  }
1392 
1393  // we should flip direction first so that we decrease the distance we could be going in the wrong direction
1394  // this is of course obvious with dec assumed to be 0, but just a reminder in case we ever support dec tracking
1395 
1396  // ideally, we would set direction only if needed
1397  // but based on our current assumptions, that could cost us an extra call to find out the current direction
1398  // so for now we'll always end up setting the direction
1399  if (cur_dir != new_dir)
1400  if (!set_pmc8_direction_axis(fd, PMC8_AXIS_DEC, new_dir, false))
1401  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): error setting new_dec_dir");
1402 
1403  // measure time when we start pulse
1404  gettimeofday(&tp, nullptr);
1405  pulse_start_us = tp.tv_sec*1000000+tp.tv_usec;
1406 
1407  if (!set_pmc8_axis_motor_rate(fd, PMC8_AXIS_DEC, mrate, false))
1408  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): error setting new_dec_rate");
1409 
1410  }
1411  // if for some reason the gdir is non-sensical ...
1412  else
1413  {
1414  return false;
1415  }
1416 
1417  // store state
1418  pstate->pulseguideactive = true;
1419  pstate->fakepulse = false;
1420  pstate->ms = ms;
1421  pstate->pulse_start_us = pulse_start_us;
1422  pstate->cur_rate = cur_rate;
1423  pstate->cur_dir = cur_dir;
1424  pstate->new_rate = new_rate;
1425  pstate->new_dir = new_dir;
1426 
1427  // see how long we've waited
1428  gettimeofday(&tp, nullptr);
1429  pulse_sofar_us = (tp.tv_sec*1000000+tp.tv_usec) - pulse_start_us;
1430 
1431  timetaken_us = pulse_sofar_us;
1432 
1433  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_start_guide(): timetaken_us=%d us", timetaken_us);
1434 
1435  return true;
1436 }
1437 
1439 {
1440  struct timeval tp;
1441  long long pulse_end_us;
1442 
1443  PulseGuideState *pstate;
1444 
1445  if (!get_pmc8_guide_state(gdir, &pstate))
1446  {
1447  return false;
1448  }
1449 
1450  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_stop_guide(): pulse dir=%d dur=%d ms", gdir, pstate->ms);
1451 
1452  if (!pstate->pulseguideactive)
1453  {
1454  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_stop_guide(): pulse guide not active!!");
1455  return false;
1456  }
1457 
1458  // flush any responses to commands we ignored above!
1459  tcflush(fd, TCIFLUSH);
1460 
1461  // "fake pulse" - it was so short we would have overshot its length AND the motors wouldn't have moved anyways
1462  if (pstate->fakepulse)
1463  {
1464 
1465  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_stop_guide(): fake pulse done");
1466  pstate->pulseguideactive = false;
1467  return true;
1468  }
1469 
1470  gettimeofday(&tp, nullptr);
1471  pulse_end_us = tp.tv_sec*1000000+tp.tv_usec;
1472 
1473  if ((gdir == PMC8_E) || (gdir == PMC8_W))
1474  {
1475  if (!set_pmc8_custom_ra_track_rate(fd, pstate->cur_rate))
1476  {
1477  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "pmc8_stop_guide(): error restoring tracking_rate to %f",pstate->cur_rate);
1478  return false;
1479  }
1480  }
1481  else if ((gdir == PMC8_N) || (gdir == PMC8_S))
1482  {
1483  int mrate;
1484 
1485  if (!convert_move_rate_to_motor(fabs(pstate->cur_rate), &mrate))
1486  {
1487  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error converting rate %f", pstate->cur_rate);
1488  return false;
1489  }
1490 
1491  // under assumption of no dec tracking, all we need to do is stop motion
1492  // but if dec tracking is ever supported, need to fix direction, and it may be better to do that first if cur_rate > new_rate
1493  if (!set_pmc8_axis_motor_rate(fd, PMC8_AXIS_DEC, mrate, false))
1494  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_stop_guide(): error returning to old move rate");
1495  // only change direction if needed
1496  if ((pstate->cur_rate != 0) && (pstate->cur_dir != pstate->new_dir))
1497  if (!set_pmc8_direction_axis(fd, PMC8_AXIS_DEC, pstate->cur_dir, false))
1498  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_stop_guide(): error returning to old direction");
1499  }
1500  else
1501  {
1502  return false;
1503  }
1504 
1505  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "pmc8_stop_guide(): requested = %d ms, actual = %f ms",
1506  pstate->ms, (pulse_end_us-pstate->pulse_start_us)/1000.0);
1507 
1508  // flush any responses to commands we ignored above!
1509  tcflush(fd, TCIFLUSH);
1510 
1511  // mark pulse done
1512  pstate->pulseguideactive = false;
1513 
1514  return true;
1515 }
1516 
1517 // convert from axis position returned by controller to motor counts used in conversion to RA/DEC
1519 {
1520  int r;
1521 
1522  if (axispos > 8388608)
1523  r = 0 - (16777216 - axispos);
1524  else
1525  r = axispos;
1526 
1527  return r;
1528 }
1529 
1531 {
1532  double motor_angle;
1533  double hour_angle;
1534  double lst;
1535 
1536 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_ra_to_motor - ra=%f sop=%d", ra, sop);
1537 
1539 
1540  hour_angle = lst- ra;
1541 
1542  // limit values to +/- 12 hours
1543  if (hour_angle > 12)
1544  hour_angle = hour_angle - 24;
1545  else if (hour_angle <= -12)
1546  hour_angle = hour_angle + 24;
1547 
1548  if (sop == INDI::Telescope::PIER_EAST)
1549  motor_angle = hour_angle - 6;
1550  else if (sop == INDI::Telescope::PIER_WEST)
1551  motor_angle = hour_angle + 6;
1552  else
1553  return false;
1554 
1555 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_ra_to_motor - lst = %f hour_angle=%f", lst, hour_angle);
1556 
1557  *mcounts = motor_angle * PMC8_AXIS0_SCALE / 24;
1558 
1559 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_ra_to_motor - motor_angle=%f *mcounts=%d", motor_angle, *mcounts);
1560 
1561 
1562  return true;
1563 }
1564 
1565 bool convert_motor_to_radec(int racounts, int deccounts, double &ra_value, double &dec_value)
1566 {
1567  double motor_angle;
1568  double hour_angle;
1569 
1570  double lst;
1571 
1573 
1574 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "lst = %f", lst);
1575 
1576  motor_angle = (24.0 * racounts) / PMC8_AXIS0_SCALE;
1577 
1578 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "racounts = %d motor_angle = %f", racounts, motor_angle);
1579 
1580  if (deccounts < 0)
1581  hour_angle = motor_angle + 6;
1582  else
1583  hour_angle = motor_angle - 6;
1584 
1585 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "hour_angle = %f", hour_angle);
1586 
1587  ra_value = lst - hour_angle;
1588 
1589 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "ra_value = %f", ra_value);
1590 
1591  if (ra_value >= 24.0)
1592  ra_value = ra_value - 24.0;
1593  else if (ra_value < 0.0)
1594  ra_value = ra_value + 24.0;
1595 
1596 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "ra_value (final) = %f", ra_value);
1597 
1598  motor_angle = (360.0 * deccounts) / PMC8_AXIS1_SCALE;
1599 
1600  if (motor_angle >= 0)
1601  dec_value = 90 - motor_angle;
1602  else
1603  dec_value = 90 + motor_angle;
1604 
1605  return true;
1606 }
1607 
1609 {
1610  double motor_angle;
1611 
1612  if (sop == INDI::Telescope::PIER_EAST)
1613  motor_angle = (dec - 90.0);
1614  else if (sop == INDI::Telescope::PIER_WEST)
1615  motor_angle = -(dec - 90.0);
1616  else
1617  return false;
1618 
1619  *mcounts = (motor_angle / 360.0) * PMC8_AXIS1_SCALE;
1620 
1621 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_dec_to_motor dec = %f, sop = %d", dec, sop);
1622 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "convert_dec_to_motor motor_angle = %f, motor_counts= %d", motor_angle, *mcounts);
1623 
1624  return true;
1625 }
1626 
1627 bool set_pmc8_target_position_axis(int fd, PMC8_AXIS axis, int point)
1628 {
1629 
1630  char cmd[32];
1631  char expresp[32];
1632  char hexpt[16];
1633  int errcode = 0;
1634  char errmsg[MAXRBUF];
1635  char response[16];
1636  int nbytes_read = 0;
1637  int nbytes_written = 0;
1638 
1639  convert_motor_counts_to_hex(point, hexpt);
1640 
1641  // for v2+ firmware, use axis 2 if we don't want to track after the slew
1642  int naxis = axis;
1643  if (pmc8_isRev2Compliant && !axis && !pmc8_goto_resume) naxis = 2;
1644  snprintf(cmd, sizeof(cmd), "ESPt%d%s!", naxis, hexpt);
1645 
1646  if (!pmc8_simulation)
1647  {
1648 
1649  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1650  {
1651  tty_error_msg(errcode, errmsg, MAXRBUF);
1653  return false;
1654  }
1655 
1656  snprintf(expresp, sizeof(expresp), "ESGt%d%s!", naxis, hexpt);
1657 
1658  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, expresp)))
1659  {
1660  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis Set Point cmd response incorrect: %s - expected %s", response, expresp);
1661  return false;
1662  }
1663  }
1664 
1665  return true;
1666 }
1667 
1668 bool set_pmc8_target_position(int fd, int rapoint, int decpoint)
1669 {
1670  bool rc;
1671 
1673 
1674  if (!rc)
1675  return rc;
1676 
1678 
1679  return rc;
1680 }
1681 
1682 
1683 bool set_pmc8_position_axis(int fd, PMC8_AXIS axis, int point)
1684 {
1685 
1686  char cmd[32];
1687  char expresp[32];
1688  char hexpt[16];
1689  int errcode = 0;
1690  char errmsg[MAXRBUF];
1691  char response[16];
1692  int nbytes_read = 0;
1693  int nbytes_written = 0;
1694 
1695  if (pmc8_simulation)
1696  {
1697  // FIXME - need to implement simulation code for setting point position
1698  return true;
1699  }
1700 
1701  convert_motor_counts_to_hex(point, hexpt);
1702  snprintf(cmd, sizeof(cmd), "ESSp%d%s!", axis, hexpt);
1703 
1704  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1705  {
1706  tty_error_msg(errcode, errmsg, MAXRBUF);
1708  return false;
1709  }
1710 
1711  snprintf(expresp, sizeof(expresp), "ESGp%d%s!", axis, hexpt);
1712 
1713  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, expresp)))
1714  {
1715  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis Set Point cmd response incorrect: %s - expected %s", response, expresp);
1716  return false;
1717  }
1718 
1719  return true;
1720 }
1721 
1722 
1723 bool set_pmc8_position(int fd, int rapoint, int decpoint)
1724 {
1725  bool rc;
1726 
1727  rc = set_pmc8_position_axis(fd, PMC8_AXIS_RA, rapoint);
1728 
1729  if (!rc)
1730  return rc;
1731 
1732  rc = set_pmc8_position_axis(fd, PMC8_AXIS_DEC, decpoint);
1733 
1734  return rc;
1735 }
1736 
1737 
1738 bool get_pmc8_position_axis(int fd, PMC8_AXIS axis, int &point)
1739 {
1740 
1741  char cmd[32];
1742  int errcode = 0;
1743  char errmsg[MAXRBUF];
1744  char response[16];
1745  int nbytes_read = 0;
1746  int nbytes_written = 0;
1747 
1748  if (pmc8_simulation)
1749  {
1750  // FIXME - need to implement simulation code for setting point position
1751  return true;
1752  }
1753 
1754  snprintf(cmd, sizeof(cmd), "ESGp%d!", axis);
1755 
1756  if ((errcode = send_pmc8_command(fd, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
1757  {
1758  tty_error_msg(errcode, errmsg, MAXRBUF);
1760  return false;
1761  }
1762 
1763  cmd[5] = '\0';
1764 
1765  if ((errcode = get_pmc8_response(fd, response, &nbytes_read, cmd)))
1766  {
1767  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error getting position axis");
1768  return false;
1769  }
1770 
1771  if (nbytes_read != 12)
1772  {
1773  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Axis Get Point cmd response incorrect");
1774  return false;
1775  }
1776 
1777  char num_str[16]= {0};
1778 
1779  strcpy(num_str, "0X");
1780  strncat(num_str, response+5, 6);
1781 
1782  point = (int)strtol(num_str, nullptr, 0);
1783 
1784  return true;
1785 }
1786 
1787 
1788 bool get_pmc8_position(int fd, int &rapoint, int &decpoint)
1789 {
1790  bool rc;
1791  int axis_ra_pos, axis_dec_pos;
1792 
1793  rc = get_pmc8_position_axis(fd, PMC8_AXIS_RA, axis_ra_pos);
1794 
1795  if (!rc)
1796  return rc;
1797 
1798  rc = get_pmc8_position_axis(fd, PMC8_AXIS_DEC, axis_dec_pos);
1799 
1800  if (!rc)
1801  return rc;
1802 
1803  // convert from axis position to motor counts
1804  rapoint = convert_axispos_to_motor(axis_ra_pos);
1805  decpoint = convert_axispos_to_motor(axis_dec_pos);
1806 
1807 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "ra axis pos = 0x%x motor_counts=%d", axis_ra_pos, rapoint);
1808 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "dec axis pos = 0x%x motor_counts=%d", axis_dec_pos, decpoint);
1809 
1810  return rc;
1811 }
1812 
1813 
1814 bool park_pmc8(int fd)
1815 {
1816 
1817  bool rc;
1818 
1819  rc = set_pmc8_target_position(fd, 0, 0);
1820 
1821  // FIXME - Need to add code to handle simulation and also setting any scope state values
1822 
1823  return rc;
1824 }
1825 
1826 
1827 bool unpark_pmc8(int fd)
1828 {
1829  INDI_UNUSED(fd);
1830 
1831  // nothing really to do for PMC8 there is no unpark command
1832 
1833  if (pmc8_simulation)
1834  {
1836  return true;
1837  }
1838 
1839 
1840  // FIXME - probably need to set a state variable to show we're unparked
1841  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "PMC8 unparked");
1842 
1843  return true;
1844 }
1845 
1846 bool abort_pmc8(int fd)
1847 {
1848  bool rc;
1849 
1850 
1851  if (pmc8_simulation)
1852  {
1853  // FIXME - need to do something to represent mount has stopped slewing
1854  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "PMC8 slew stopped in simulation - need to add more code?");
1855  return true;
1856  }
1857 
1858  // stop move/slew rates
1860  if (!rc)
1861  {
1862  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error stopping RA axis!");
1863  return false;
1864  }
1865 
1867  if (!rc)
1868  {
1869  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error stopping DEC axis!");
1870  return false;
1871  }
1872 
1873  return true;
1874 }
1875 
1876 // "slew" on PMC8 is instantaneous once you set the target ra/dec
1877 // no concept of setting target and then starting a slew operation as two steps
1878 bool slew_pmc8(int fd, double ra, double dec)
1879 {
1880  bool rc;
1881  int racounts, deccounts;
1883 
1884  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "slew_pmc8: ra=%f dec=%f", ra, dec);
1885 
1886  sop = destSideOfPier(ra, dec);
1887 
1888  rc = convert_ra_to_motor(ra, sop, &racounts);
1889  if (!rc)
1890  {
1891  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "slew_pmc8: error converting RA to motor counts");
1892  return false;
1893  }
1894 
1895  rc = convert_dec_to_motor(dec, sop, &deccounts);
1896  if (!rc)
1897  {
1898  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "slew_pmc8: error converting DEC to motor counts");
1899  return false;
1900  }
1901 
1902  rc = set_pmc8_target_position(fd, racounts, deccounts);
1903 
1904  if (!rc)
1905  {
1906  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error slewing PMC8");
1907  return false;
1908  }
1909 
1910  if (pmc8_simulation)
1911  {
1913  }
1914 
1915  return true;
1916 }
1917 
1919 {
1920  double hour_angle;
1921  double lst;
1922 
1923  INDI_UNUSED(dec);
1924 
1926 
1927  hour_angle = lst - ra;
1928 
1929  // limit values to +/- 12 hours
1930  if (hour_angle > 12)
1931  hour_angle = hour_angle - 24;
1932  else if (hour_angle <= -12)
1933  hour_angle = hour_angle + 24;
1934 
1935  if (hour_angle < 0.0)
1937  else
1939 }
1940 
1941 bool sync_pmc8(int fd, double ra, double dec)
1942 {
1943  bool rc;
1944  int racounts, deccounts;
1946 
1947  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "sync_pmc8: ra=%f dec=%f", ra, dec);
1948 
1949  sop = destSideOfPier(ra, dec);
1950 
1951  rc = convert_ra_to_motor(ra, sop, &racounts);
1952  if (!rc)
1953  {
1954  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "sync_pmc8: error converting RA to motor counts");
1955  return false;
1956  }
1957 
1958  rc = convert_dec_to_motor(dec, sop, &deccounts);
1959  if (!rc)
1960  {
1961  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "sync_pmc8: error converting DEC to motor counts");
1962  return false;
1963  }
1964 
1965  if (pmc8_simulation)
1966  {
1967  // FIXME - need to implement pmc8 sync sim
1968 // strcpy(response, "1");
1969 // nbytes_read = strlen(response);
1970  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Need to implement PMC8 sync simulation");
1971  return false;
1972  }
1973  else
1974  {
1975  rc = set_pmc8_position(fd, racounts, deccounts);
1976  }
1977 
1978  if (!rc)
1979  {
1980  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error setting pmc8 position");
1981  return false;
1982  }
1983 
1984  return true;
1985 }
1986 
1987 bool set_pmc8_radec(int fd, double ra, double dec)
1988 {
1989  bool rc;
1990  int racounts, deccounts;
1992 
1993 
1994  sop = destSideOfPier(ra, dec);
1995 
1996  rc = convert_ra_to_motor(ra, sop, &racounts);
1997  if (!rc)
1998  {
1999  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_radec: error converting RA to motor counts");
2000  return false;
2001  }
2002 
2003  rc = convert_dec_to_motor(ra, sop, &deccounts);
2004  if (!rc)
2005  {
2006  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "set_pmc8_radec: error converting DEC to motor counts");
2007  return false;
2008  }
2009 
2010  if (pmc8_simulation)
2011  {
2012  // FIXME - need to implement pmc8 sync sim
2013 // strcpy(response, "1");
2014 // nbytes_read = strlen(response);
2015  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Need to implement PMC8 sync simulation");
2016  return false;
2017  }
2018  else
2019  {
2020 
2021  rc = set_pmc8_target_position(fd, racounts, deccounts);
2022  }
2023 
2024  if (!rc)
2025  {
2026  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Error setting target position");
2027  return false;
2028  }
2029 
2030  return true;
2031 }
2032 
2033 bool get_pmc8_coords(int fd, double &ra, double &dec)
2034 {
2035  int racounts, deccounts;
2036  bool rc;
2037 
2038  if (pmc8_simulation)
2039  {
2040  // sortof silly but convert simulated RA/DEC to counts so we can then convert
2041  // back to RA/DEC to test that conversion code
2043 
2044  sop = destSideOfPier(simPMC8Data.ra, simPMC8Data.dec);
2045 
2046  rc = convert_ra_to_motor(simPMC8Data.ra, sop, &racounts);
2047 
2048  if (!rc)
2049  return rc;
2050 
2051  rc = convert_dec_to_motor(simPMC8Data.dec, sop, &deccounts);
2052 
2053  if (!rc)
2054  return rc;
2055  }
2056  else
2057  {
2058  rc = get_pmc8_position(fd, racounts, deccounts);
2059  }
2060 
2061  if (!rc)
2062  {
2063  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "Error getting PMC8 motor position");
2064  return false;
2065  }
2066 
2067  // convert motor counts to ra/dec
2068  convert_motor_to_radec(racounts, deccounts, ra, dec);
2069 
2070 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "ra motor_counts=%d RA = %f", racounts, ra);
2071 // DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "dec motor_counts=%d DEC = %f", deccounts, dec);
2072 
2073  return rc;
2074 }
2075 
2076 // wrap read commands to PMC8
2077 bool get_pmc8_response(int fd, char* buf, int *nbytes_read, const char* expected = NULL )
2078 {
2079  int err_code = 1;
2080  int cnt = 0;
2081 
2082  //repeat a few times, after that, let's assume we're not getting a response
2083  while ((err_code) && (cnt++ < PMC8_MAX_RETRIES))
2084  {
2085  //Read until exclamation point to get response
2086  if ((err_code = tty_read_section(fd, buf, '!', PMC8_TIMEOUT, nbytes_read)))
2087  {
2088 
2089  char errmsg[MAXRBUF];
2090  tty_error_msg(err_code, errmsg, MAXRBUF);
2091 
2092  // if we see connection timed out, exit out of here and try to reconnect
2093  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG,"Read error: %s",errmsg);
2094  if (strstr(errmsg,"Connection timed out") || strstr(errmsg,"Bad"))
2095  {
2097  return err_code;
2098  }
2099  }
2100  if (*nbytes_read > 0)
2101  {
2102  buf[*nbytes_read] = '\0';
2103  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "RES %d bytes (%s)", *nbytes_read, buf);
2104 
2105  //PMC8 connection is not entirely reliable when using Ethernet instead of Serial connection.
2106  //So, try to compensate for common problems
2108  {
2109  //One problem is we get the string *HELLO* when we connect or disconnect, so discard that
2110  if (buf[0]=='*')
2111  {
2112  strcpy(buf,buf+7);
2113  *nbytes_read = *nbytes_read-7;
2114  }
2115  //Another problem is we sometimes get the string AT when we reconnect, so discard that
2116  if (strncmp(buf, "AT", 2)==0)
2117  {
2118  strcpy(buf,buf+2);
2119  *nbytes_read = *nbytes_read-2;
2120  }
2121  //Another problem is random extraneous ESGp! reponses during slew, so when we see those, drop them and try again
2122  if (strncmp(buf, "ESGp!",5)==0)
2123  {
2124  err_code = 1;
2125  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_DEBUG, "Invalid response ESGp!");
2126  }
2127  }
2128  //If a particular response was expected, make sure we got it
2129  if (expected)
2130  {
2131  if (strncmp(buf, expected,strlen(expected))!=0)
2132  {
2133  err_code = 1;
2134  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_EXTRA_1, "No Match for %s", expected);
2135  }
2136  else
2137  {
2138  DEBUGFDEVICE(pmc8_device, INDI::Logger::DBG_EXTRA_1, "Matches %s", expected);
2139  // On rare occasions, there may have been a read error even though it's the response we want, so set err_code explicitly
2140  err_code = 0;
2141  }
2142  }
2143  }
2144  else
2145  {
2147  err_code = 1;
2148  }
2149  }
2150  // if this is our nth consecutive read error, try to reconnect
2151  if (err_code)
2152  {
2154  }
2155  else
2156  {
2157  pmc8_io_error_ctr = 0;
2158  }
2159  return err_code;
2160 }
2161 
2162 //wrap write commands to pmc8
2163 bool send_pmc8_command(int fd, const char *buf, int nbytes, int *nbytes_written)
2164 {
2165 
2167 
2168  tcflush(fd, TCIFLUSH);
2169 
2170  int err_code = 1;
2171  //try to reconnect if we see broken pipe error
2172  if ((err_code = tty_write(fd, buf, nbytes, nbytes_written)))
2173  {
2174  char errmsg[MAXRBUF];
2175  tty_error_msg(err_code, errmsg, MAXRBUF);
2176  if (strstr(errmsg,"Broken pipe") || strstr(errmsg,"Bad"))
2177  {
2179  return err_code;
2180  }
2181  }
2182  return err_code;
2183 }
2184 
2186 {
2187  DEBUGDEVICE(pmc8_device, INDI::Logger::DBG_ERROR, "Bad connection. Trying to reconnect.");
2188  pmc8_reconnect_flag = true;
2189 }
2190 
2192 {
2193  if (pmc8_reconnect_flag)
2194  {
2195  pmc8_reconnect_flag = false;
2196  return true;
2197  }
2198  return false;
2199 }
2200 
2201 void set_pmc8_goto_resume(bool resume)
2202 {
2203  pmc8_goto_resume = resume;
2204 }
MAXINDIDEVICE
#define MAXINDIDEVICE
Definition: indiapi.h:192
PMC8_AXIS
PMC8_AXIS
Definition: pmc8driver.h:57
get_pmc8_is_scope_slewing
bool get_pmc8_is_scope_slewing(int fd, bool &isslew)
Definition: pmc8driver.cpp:700
convert_axispos_to_motor
int convert_axispos_to_motor(int axispos)
Definition: pmc8driver.cpp:1518
PMC8_AXIS0_SCALE
double PMC8_AXIS0_SCALE
Definition: pmc8driver.cpp:60
set_pmc8_target_position_axis
bool set_pmc8_target_position_axis(int fd, PMC8_AXIS axis, int point)
Definition: pmc8driver.cpp:1627
get_pmc8_direction_axis
bool get_pmc8_direction_axis(int fd, PMC8_AXIS axis, int &dir)
Definition: pmc8driver.cpp:597
PMC8_RATE_SOLAR
#define PMC8_RATE_SOLAR
Definition: pmc8driver.cpp:81
PMC8_RATE_SIDEREAL
#define PMC8_RATE_SIDEREAL
Definition: pmc8driver.cpp:79
simPMC8Data
struct @139 simPMC8Data
NS_PulseGuideState
PulseGuideState NS_PulseGuideState
Definition: pmc8driver.cpp:113
pmc8_sidereal_rate_fraction_ra
double pmc8_sidereal_rate_fraction_ra
Definition: pmc8driver.cpp:94
set_pmc8_device
void set_pmc8_device(const char *name)
Definition: pmc8driver.cpp:235
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
PMC8_TIMEOUT
#define PMC8_TIMEOUT
Definition: pmc8driver.cpp:45
set_pmc8_custom_dec_track_rate
bool set_pmc8_custom_dec_track_rate(int fd, double rate)
Definition: pmc8driver.cpp:1094
abort_pmc8
bool abort_pmc8(int fd)
Definition: pmc8driver.cpp:1846
PMC8_SERIAL_STANDARD
@ PMC8_SERIAL_STANDARD
Definition: pmc8driver.h:62
convert_motor_counts_to_hex
void convert_motor_counts_to_hex(int val, char *hex)
Definition: pmc8driver.cpp:127
dec
double dec
Definition: pmc8driver.cpp:118
INDI::Telescope::PIER_WEST
@ PIER_WEST
Definition: inditelescope.h:124
set_pmc8_direction_axis
bool set_pmc8_direction_axis(int fd, PMC8_AXIS axis, int dir, bool fast)
Definition: pmc8driver.cpp:653
pmc8_east_dir
int pmc8_east_dir
Definition: pmc8driver.cpp:96
PMC8Info
Definition: pmc8driver.h:64
pmc8_device
char pmc8_device[MAXINDIDEVICE]
Definition: pmc8driver.cpp:91
set_pmc8_custom_ra_track_rate
bool set_pmc8_custom_ra_track_rate(int fd, double rate)
Definition: pmc8driver.cpp:1019
PMC8_TRACK_KING
@ PMC8_TRACK_KING
Definition: pmc8driver.h:53
PMC8_G11_AXIS1_SCALE
#define PMC8_G11_AXIS1_SCALE
Definition: pmc8driver.cpp:51
PMC8_DIRECTION
PMC8_DIRECTION
Definition: pmc8driver.h:58
indicom.h
Implementations for common driver routines.
stop_pmc8_tracking_motion
bool stop_pmc8_tracking_motion(int fd)
Definition: pmc8driver.cpp:759
convert_dec_to_motor
bool convert_dec_to_motor(double dec, INDI::Telescope::TelescopePierSide sop, int *mcounts)
Definition: pmc8driver.cpp:1608
INDI::Logger::DBG_WARNING
@ DBG_WARNING
Definition: indilogger.h:193
convert_precise_rate_to_motor
bool convert_precise_rate_to_motor(double rate, int *mrate)
Definition: pmc8driver.cpp:151
FirmwareInfo::IsRev2Compliant
bool IsRev2Compliant
Definition: pmc8driver.h:76
PMC8_N
@ PMC8_N
Definition: pmc8driver.h:58
PulseGuideState::cur_rate
double cur_rate
Definition: pmc8driver.cpp:106
set_pmc8_sim_system_status
void set_pmc8_sim_system_status(PMC8_SYSTEM_STATUS value)
Definition: pmc8driver.cpp:250
PulseGuideState::cur_dir
int cur_dir
Definition: pmc8driver.cpp:107
PMC8_SIMUL_VERSION_RESP
#define PMC8_SIMUL_VERSION_RESP
Definition: pmc8driver.cpp:47
PMC8_S
@ PMC8_S
Definition: pmc8driver.h:58
pmc8_latitude
double pmc8_latitude
Definition: pmc8driver.cpp:92
PulseGuideState::ms
int ms
Definition: pmc8driver.cpp:104
pmc8_connection
PMC8_CONNECTION_TYPE pmc8_connection
Definition: pmc8driver.cpp:84
INDI::Logger::DBG_EXTRA_1
@ DBG_EXTRA_1
Definition: indilogger.h:196
PMC8_TRACK_CUSTOM
@ PMC8_TRACK_CUSTOM
Definition: pmc8driver.h:53
INDI::Logger::DBG_ERROR
@ DBG_ERROR
Definition: indilogger.h:192
set_pmc8_custom_ra_move_rate
bool set_pmc8_custom_ra_move_rate(int fd, double rate)
Definition: pmc8driver.cpp:1104
round
double round(double value, int decimal_places)
Definition: test_alignment.cpp:32
PMC8_MAX_RETRIES
#define PMC8_MAX_RETRIES
Definition: pmc8driver.cpp:75
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
convert_motor_rate_to_move_rate
bool convert_motor_rate_to_move_rate(int mrate, double *rate)
Definition: pmc8driver.cpp:194
destSideOfPier
INDI::Telescope::TelescopePierSide destSideOfPier(double ra, double dec)
Definition: pmc8driver.cpp:1918
get_pmc8_tracking_data
bool get_pmc8_tracking_data(int fd, double &rate, uint8_t &mode)
Definition: pmc8driver.cpp:822
set_pmc8_goto_resume
void set_pmc8_goto_resume(bool resume)
Definition: pmc8driver.cpp:2201
get_pmc8_move_rate_axis
bool get_pmc8_move_rate_axis(int fd, PMC8_AXIS axis, double &rate)
Definition: pmc8driver.cpp:541
stop_pmc8_guide
bool stop_pmc8_guide(int fd, PMC8_DIRECTION gdir)
Definition: pmc8driver.cpp:1438
FirmwareInfo::MainBoardFirmware
std::string MainBoardFirmware
Definition: ieqprolegacydriver.h:59
PulseGuideState::fakepulse
bool fakepulse
Definition: pmc8driver.cpp:103
set_pmc8_radec
bool set_pmc8_radec(int fd, double ra, double dec)
Definition: pmc8driver.cpp:1987
set_pmc8_mountParameters
void set_pmc8_mountParameters(int index)
Definition: pmc8driver.cpp:201
set_pmc8_track_mode
bool set_pmc8_track_mode(int fd, uint8_t mode)
Definition: pmc8driver.cpp:979
PulseGuideState::pulse_start_us
long long pulse_start_us
Definition: pmc8driver.cpp:105
moveRate
double moveRate
Definition: pmc8driver.cpp:122
INDI::Logger::DBG_SESSION
@ DBG_SESSION
Definition: indilogger.h:194
PMC8_RATE_KING
#define PMC8_RATE_KING
Definition: pmc8driver.cpp:82
set_pmc8_guide_rate
bool set_pmc8_guide_rate(int fd, PMC8_AXIS axis, double rate)
Definition: pmc8driver.cpp:1142
tty_read_section
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:541
get_pmc8_position
bool get_pmc8_position(int fd, int &rapoint, int &decpoint)
Definition: pmc8driver.cpp:1788
PulseGuideState::pulseguideactive
bool pulseguideactive
Definition: pmc8driver.cpp:102
get_pmc8_response
bool get_pmc8_response(int fd, char *buf, int *nbytes_read, const char *expected=NULL)
Definition: pmc8driver.cpp:2077
DEBUGDEVICE
#define DEBUGDEVICE(device, priority, msg)
Definition: indilogger.h:60
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
unpark_pmc8
bool unpark_pmc8(int fd)
Definition: pmc8driver.cpp:1827
raDirection
int raDirection
Definition: pmc8driver.cpp:119
convert_motor_to_radec
bool convert_motor_to_radec(int racounts, int deccounts, double &ra_value, double &dec_value)
Definition: pmc8driver.cpp:1565
pmc8_simulation
bool pmc8_simulation
Definition: pmc8driver.cpp:86
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:1135
simPMC8Info
PMC8Info simPMC8Info
Definition: pmc8driver.cpp:97
INDI::Logger::DBG_DEBUG
@ DBG_DEBUG
Definition: indilogger.h:195
PMC8_TRACK_LUNAR
@ PMC8_TRACK_LUNAR
Definition: pmc8driver.h:53
PMC8_E
@ PMC8_E
Definition: pmc8driver.h:58
indilogger.h
PMC8_TRACK_RATE
PMC8_TRACK_RATE
Definition: pmc8driver.h:53
PulseGuideState
Definition: pmc8driver.cpp:100
PMC8_CONNECTION_TYPE
PMC8_CONNECTION_TYPE
Definition: pmc8driver.h:62
trackRate
double trackRate
Definition: pmc8driver.cpp:121
set_pmc8_move_rate_axis
bool set_pmc8_move_rate_axis(int fd, PMC8_DIRECTION dir, int reqrate)
Definition: pmc8driver.cpp:733
start_pmc8_guide
bool start_pmc8_guide(int fd, PMC8_DIRECTION gdir, int ms, long &timetaken_us, double ratehint)
Definition: pmc8driver.cpp:1279
get_pmc8_position_axis
bool get_pmc8_position_axis(int fd, PMC8_AXIS axis, int &point)
Definition: pmc8driver.cpp:1738
get_pmc8_guide_state
bool get_pmc8_guide_state(PMC8_DIRECTION gdir, PulseGuideState **pstate)
Definition: pmc8driver.cpp:1256
MOUNT_EXOS2
@ MOUNT_EXOS2
Definition: pmc8driver.h:60
pmc8driver.h
iEQ::ST_SLEWING
@ ST_SLEWING
Definition: ieqdriverbase.h:34
PMC8_TRACK_SOLAR
@ PMC8_TRACK_SOLAR
Definition: pmc8driver.h:53
pmc8_io_error_ctr
int pmc8_io_error_ctr
Definition: pmc8driver.cpp:90
set_pmc8_reconnect_flag
void set_pmc8_reconnect_flag()
Definition: pmc8driver.cpp:2185
park_pmc8
bool park_pmc8(int fd)
Definition: pmc8driver.cpp:1814
EW_PulseGuideState
PulseGuideState EW_PulseGuideState
Definition: pmc8driver.cpp:113
detect_pmc8
bool detect_pmc8(int fd)
Definition: pmc8driver.cpp:345
get_pmc8_guide_rate
bool get_pmc8_guide_rate(int fd, PMC8_AXIS axis, double &rate)
Definition: pmc8driver.cpp:1194
PMC8_MAX_MOVE_RATE
#define PMC8_MAX_MOVE_RATE
Definition: pmc8driver.h:39
INDI::Telescope::TelescopePierSide
TelescopePierSide
Definition: inditelescope.h:121
set_pmc8_debug
void set_pmc8_debug(bool enable)
Definition: pmc8driver.cpp:223
get_pmc8_track_rate
bool get_pmc8_track_rate(int fd, double &rate)
Definition: pmc8driver.cpp:775
set_pmc8_target_position
bool set_pmc8_target_position(int fd, int rapoint, int decpoint)
Definition: pmc8driver.cpp:1668
tty_write
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:404
convert_ra_to_motor
bool convert_ra_to_motor(double ra, INDI::Telescope::TelescopePierSide sop, int *mcounts)
Definition: pmc8driver.cpp:1530
PMC8_AXIS_RA
@ PMC8_AXIS_RA
Definition: pmc8driver.h:57
set_pmc8_sim_track_rate
void set_pmc8_sim_track_rate(PMC8_TRACK_RATE value)
Definition: pmc8driver.cpp:270
get_pmc8_coords
bool get_pmc8_coords(int fd, double &ra, double &dec)
Definition: pmc8driver.cpp:2033
PMC8_EXOS2_AXIS1_SCALE
#define PMC8_EXOS2_AXIS1_SCALE
Definition: pmc8driver.cpp:54
inditelescope.h
send_pmc8_command
bool send_pmc8_command(int fd, const char *buf, int nbytes, int *nbytes_written)
Definition: pmc8driver.cpp:2163
PMC8_RETRY_DELAY
#define PMC8_RETRY_DELAY
Definition: pmc8driver.cpp:76
FirmwareInfo::MountType
PMC8_MOUNT_TYPES MountType
Definition: pmc8driver.h:75
PMC8Info::systemStatus
PMC8_SYSTEM_STATUS systemStatus
Definition: pmc8driver.h:66
set_pmc8_simulation
void set_pmc8_simulation(bool enable)
Definition: pmc8driver.cpp:228
get_pmc8_firmware
bool get_pmc8_firmware(int fd, FirmwareInfo *info)
Definition: pmc8driver.cpp:526
PMC8_iEXOS100_AXIS0_SCALE
#define PMC8_iEXOS100_AXIS0_SCALE
Definition: pmc8driver.cpp:56
guide_rate
double guide_rate
Definition: pmc8driver.cpp:123
MOUNT_iEXOS100
@ MOUNT_iEXOS100
Definition: pmc8driver.h:60
pmc8_reconnect_flag
bool pmc8_reconnect_flag
Definition: pmc8driver.cpp:88
PMC8_SERIAL_AUTO
@ PMC8_SERIAL_AUTO
Definition: pmc8driver.h:62
PulseGuideState::new_rate
double new_rate
Definition: pmc8driver.cpp:108
get_pmc8_reconnect_flag
bool get_pmc8_reconnect_flag()
Definition: pmc8driver.cpp:2191
PMC8_SYSTEM_STATUS
PMC8_SYSTEM_STATUS
Definition: pmc8driver.h:41
PMC8_MAX_IO_ERROR_THRESHOLD
#define PMC8_MAX_IO_ERROR_THRESHOLD
Definition: pmc8driver.cpp:77
PulseGuideState::new_dir
int new_dir
Definition: pmc8driver.cpp:109
get_local_sidereal_time
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
set_pmc8_position
bool set_pmc8_position(int fd, int rapoint, int decpoint)
Definition: pmc8driver.cpp:1723
set_pmc8_sim_dec
void set_pmc8_sim_dec(double dec)
Definition: pmc8driver.cpp:285
fd
int fd
Definition: intelliscope.c:43
PMC8_MAX_PRECISE_MOTOR_RATE
#define PMC8_MAX_PRECISE_MOTOR_RATE
Definition: pmc8driver.cpp:67
set_pmc8_ra_tracking
bool set_pmc8_ra_tracking(int fd, double rate)
Definition: pmc8driver.cpp:1007
check_pmc8_connection
bool check_pmc8_connection(int fd, PMC8_CONNECTION_TYPE connection)
Definition: pmc8driver.cpp:290
PMC8_AXIS_DEC
@ PMC8_AXIS_DEC
Definition: pmc8driver.h:57
convert_move_rate_to_motor
bool convert_move_rate_to_motor(float rate, int *mrate)
Definition: pmc8driver.cpp:179
PMC8_W
@ PMC8_W
Definition: pmc8driver.h:58
pmc8_goto_resume
bool pmc8_goto_resume
Definition: pmc8driver.cpp:89
DEBUGFDEVICE
#define DEBUGFDEVICE(device, priority, msg,...)
Definition: indilogger.h:61
PMC8_MAX_TRACK_RATE
#define PMC8_MAX_TRACK_RATE
Definition: pmc8driver.h:36
iEQ::ST_PARKED
@ ST_PARKED
Definition: ieqdriverbase.h:38
PMC8_iEXOS100_AXIS1_SCALE
#define PMC8_iEXOS100_AXIS1_SCALE
Definition: pmc8driver.cpp:57
slew_pmc8
bool slew_pmc8(int fd, double ra, double dec)
Definition: pmc8driver.cpp:1878
get_pmc8_model
bool get_pmc8_model(int fd, FirmwareInfo *info)
Definition: pmc8driver.cpp:380
set_pmc8_location
void set_pmc8_location(double latitude, double longitude)
Definition: pmc8driver.cpp:240
ARCSEC_IN_CIRCLE
#define ARCSEC_IN_CIRCLE
Definition: pmc8driver.cpp:63
PMC8_G11_AXIS0_SCALE
#define PMC8_G11_AXIS0_SCALE
Definition: pmc8driver.cpp:50
PMC8_PULSE_GUIDE_MIN_MS
#define PMC8_PULSE_GUIDE_MIN_MS
Definition: pmc8driver.cpp:70
PMC8_RATE_LUNAR
#define PMC8_RATE_LUNAR
Definition: pmc8driver.cpp:80
set_pmc8_sim_move_rate
void set_pmc8_sim_move_rate(int value)
Definition: pmc8driver.cpp:275
FirmwareInfo
Definition: celestrondriver.h:156
pmc8_isRev2Compliant
bool pmc8_isRev2Compliant
Definition: pmc8driver.cpp:87
PMC8_AXIS1_SCALE
double PMC8_AXIS1_SCALE
Definition: pmc8driver.cpp:61
pmc8_debug
bool pmc8_debug
Definition: pmc8driver.cpp:85
set_pmc8_sim_ra
void set_pmc8_sim_ra(double ra)
Definition: pmc8driver.cpp:280
TTY_OK
@ TTY_OK
Definition: indicom.h:94
convert_precise_motor_to_rate
bool convert_precise_motor_to_rate(int mrate, double *rate)
Definition: pmc8driver.cpp:171
PMC8_TRACK_SIDEREAL
@ PMC8_TRACK_SIDEREAL
Definition: pmc8driver.h:53
set_pmc8_custom_dec_move_rate
bool set_pmc8_custom_dec_move_rate(int fd, double rate)
Definition: pmc8driver.cpp:1123
PMC8_EXOS2_AXIS0_SCALE
#define PMC8_EXOS2_AXIS0_SCALE
Definition: pmc8driver.cpp:53
INDI::Telescope::PIER_EAST
@ PIER_EAST
Definition: inditelescope.h:125
FirmwareInfo::Model
std::string Model
Definition: celestrondriver.h:158
MOUNT_G11
@ MOUNT_G11
Definition: pmc8driver.h:60
pmc8_longitude
double pmc8_longitude
Definition: pmc8driver.cpp:93
sync_pmc8
bool sync_pmc8(int fd, double ra, double dec)
Definition: pmc8driver.cpp:1941
ra
double ra
Definition: pmc8driver.cpp:117
pmc8_sidereal_rate_fraction_de
double pmc8_sidereal_rate_fraction_de
Definition: pmc8driver.cpp:95
set_pmc8_position_axis
bool set_pmc8_position_axis(int fd, PMC8_AXIS axis, int point)
Definition: pmc8driver.cpp:1683
decDirection
int decDirection
Definition: pmc8driver.cpp:120
PMC8_ETHERNET
@ PMC8_ETHERNET
Definition: pmc8driver.h:62
set_pmc8_axis_motor_rate
bool set_pmc8_axis_motor_rate(int fd, PMC8_AXIS axis, int mrate, bool fast)
Definition: pmc8driver.cpp:867
iEQ::ST_STOPPED
@ ST_STOPPED
Definition: ieqdriverbase.h:32
get_pmc8_main_firmware
bool get_pmc8_main_firmware(int fd, FirmwareInfo *info)
Definition: pmc8driver.cpp:474
set_pmc8_axis_move_rate
bool set_pmc8_axis_move_rate(int fd, PMC8_AXIS axis, float rate)
Definition: pmc8driver.cpp:915