Instrument Neutral Distributed Interface INDI  1.4.1
indicom.c
1 /*
2  INDI LIB
3  Common routines used by all drivers
4  Copyright (C) 2003 by Jason Harris (jharris@30doradus.org)
5  Elwood C. Downey
6 
7  This is the C version of the astronomical library in KStars
8  modified by Jasem Mutlaq (mutlaqja@ikarustech.com)
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Lesser General Public
12  License as published by the Free Software Foundation; either
13  version 2.1 of the License, or (at your option) any later version.
14 
15  This library is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public
21  License along with this library; if not, write to the Free Software
22  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 
24 */
25 
26 #define _GNU_SOURCE 1
27 
28 #include <stdlib.h>
29 #include <math.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <stdint.h>
36 #include <locale.h>
37 
38 #ifdef __APPLE__
39 #include <sys/param.h>
40 #endif
41 
42 #if defined(BSD) && !defined(__GNU__)
43 #include <IOKit/serial/ioss.h>
44 #include <sys/ioctl.h>
45 #endif
46 
47 #include <config.h>
48 #include <libnova.h>
49 
50 #include "indicom.h"
51 #include "indidevapi.h"
52 
53 #ifdef _WIN32
54 #undef CX
55 #undef CY
56 #endif
57 
58 #ifndef _WIN32
59 #include <unistd.h>
60 #include <termios.h>
61 #include <sys/param.h>
62 #define PARITY_NONE 0
63 #define PARITY_EVEN 1
64 #define PARITY_ODD 2
65 #endif
66 
67 #if defined( _MSC_VER )
68 #define snprintf _snprintf
69 #pragma warning(push)
70 #pragma warning(disable: 4996)
72 #endif
73 
74 #define MAXRBUF 2048
75 
76 int tty_debug = 0;
77 
78 #ifndef _WIN32
79 int extractISOTime(const char *timestr, struct ln_date *iso_date)
80 {
81  struct tm utm;
82 
83  if (strptime(timestr, "%Y/%m/%dT%H:%M:%S", &utm))
84  {
85  ln_get_date_from_tm(&utm, iso_date);
86  return (0);
87  }
88 
89  if (strptime(timestr, "%Y-%m-%dT%H:%M:%S", &utm))
90  {
91  ln_get_date_from_tm(&utm, iso_date);
92  return (0);
93  }
94 
95  return (-1);
96 }
97 #endif
98 
99 
100 /* sprint the variable a in sexagesimal format into out[].
101  * w is the number of spaces for the whole part.
102  * fracbase is the number of pieces a whole is to broken into; valid options:
103  * 360000: <w>:mm:ss.ss
104  * 36000: <w>:mm:ss.s
105  * 3600: <w>:mm:ss
106  * 600: <w>:mm.m
107  * 60: <w>:mm
108  * return number of characters written to out, not counting final '\0'.
109  */
110 int
111 fs_sexa (char *out, double a, int w, int fracbase)
112 {
113  char *out0 = out;
114  unsigned long n;
115  int d;
116  int f;
117  int m;
118  int s;
119  int isneg;
120 
121  /* save whether it's negative but do all the rest with a positive */
122  isneg = (a < 0);
123  if (isneg)
124  a = -a;
125 
126  /* convert to an integral number of whole portions */
127  n = (unsigned long)(a * fracbase + 0.5);
128  d = n/fracbase;
129  f = n%fracbase;
130 
131  /* form the whole part; "negative 0" is a special case */
132  if (isneg && d == 0)
133  out += snprintf (out, MAXINDIFORMAT, "%*s-0", w-2, "");
134  else
135  out += snprintf (out, MAXINDIFORMAT, "%*d", w, isneg ? -d : d);
136 
137  /* do the rest */
138  switch (fracbase) {
139  case 60: /* dd:mm */
140  m = f/(fracbase/60);
141  out += snprintf (out, MAXINDIFORMAT, ":%02d", m);
142  break;
143  case 600: /* dd:mm.m */
144  out += snprintf (out, MAXINDIFORMAT, ":%02d.%1d", f/10, f%10);
145  break;
146  case 3600: /* dd:mm:ss */
147  m = f/(fracbase/60);
148  s = f%(fracbase/60);
149  out += snprintf (out, MAXINDIFORMAT, ":%02d:%02d", m, s);
150  break;
151  case 36000: /* dd:mm:ss.s*/
152  m = f/(fracbase/60);
153  s = f%(fracbase/60);
154  out += snprintf (out, MAXINDIFORMAT, ":%02d:%02d.%1d", m, s/10, s%10);
155  break;
156  case 360000: /* dd:mm:ss.ss */
157  m = f/(fracbase/60);
158  s = f%(fracbase/60);
159  out += snprintf (out, MAXINDIFORMAT, ":%02d:%02d.%02d", m, s/100, s%100);
160  break;
161  default:
162  printf ("fs_sexa: unknown fracbase: %d\n", fracbase);
163  return -1;
164  }
165 
166  return (out - out0);
167 }
168 
169 /* convert sexagesimal string str AxBxC to double.
170  * x can be anything non-numeric. Any missing A, B or C will be assumed 0.
171  * optional - and + can be anywhere.
172  * return 0 if ok, -1 if can't find a thing.
173  */
174 int
176 const char *str0, /* input string */
177 double *dp) /* cracked value, if return 0 */
178 {
179  char *orig = setlocale(LC_NUMERIC,"C");
180 
181  double a = 0, b = 0, c = 0;
182  char str[128];
183  char *neg;
184  int r;
185 
186  /* copy str0 so we can play with it */
187  strncpy (str, str0, sizeof(str)-1);
188  str[sizeof(str)-1] = '\0';
189 
190  neg = strchr(str, '-');
191  if (neg)
192  *neg = ' ';
193 
194  r = sscanf (str, "%lf%*[^0-9]%lf%*[^0-9]%lf", &a, &b, &c);
195 
196  setlocale(LC_NUMERIC,orig);
197 
198  if (r < 1)
199  return (-1);
200  *dp = a + b/60 + c/3600;
201  if (neg)
202  *dp *= -1;
203  return (0);
204 }
205 
206 void getSexComponents(double value, int *d, int *m, int *s)
207 {
208 
209  *d = (int32_t) fabs(value);
210  *m = (int32_t) ((fabs(value) - *d) * 60.0);
211  *s = (int32_t) rint(((fabs(value) - *d) * 60.0 - *m) *60.0);
212 
213  if (value < 0)
214  *d *= -1;
215 }
216 
217 /* fill buf with properly formatted INumber string. return length */
218 int
219 numberFormat (char *buf, const char *format, double value)
220 {
221  int w, f, s;
222  char m;
223 
224  if (sscanf (format, "%%%d.%d%c", &w, &f, &m) == 3 && m == 'm') {
225  /* INDI sexi format */
226  switch (f) {
227  case 9: s = 360000; break;
228  case 8: s = 36000; break;
229  case 6: s = 3600; break;
230  case 5: s = 600; break;
231  default: s = 60; break;
232  }
233  return (fs_sexa (buf, value, w-f, s));
234  } else {
235  /* normal printf format */
236  return (snprintf (buf, MAXINDIFORMAT, format, value));
237  }
238 }
239 
240 /* log message locally.
241  * this has nothing to do with XML or any Clients.
242  */
243 void
244 IDLog (const char *fmt, ...)
245 {
246  va_list ap;
247  /* JM: Since all INDI's stderr are timestampped now, we don't need to time stamp ID Log */
248  /*fprintf (stderr, "%s ", timestamp());*/
249  va_start (ap, fmt);
250  vfprintf (stderr, fmt, ap);
251  va_end (ap);
252 }
253 
254 /* return current system time in message format */
255 const char *
257 {
258  static char ts[32];
259  struct tm *tp;
260  time_t t;
261 
262  time (&t);
263  tp = gmtime (&t);
264  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
265  return (ts);
266 }
267 
268 void tty_set_debug(int debug)
269 {
270  tty_debug = debug;
271 }
272 
273 int tty_timeout(int fd, int timeout)
274 {
275  #if defined(_WIN32) || defined(ANDROID)
276  return TTY_ERRNO;
277  #else
278 
279  if (fd == -1)
280  return TTY_ERRNO;
281 
282  struct timeval tv;
283  fd_set readout;
284  int retval;
285 
286  FD_ZERO(&readout);
287  FD_SET(fd, &readout);
288 
289  /* wait for 'timeout' seconds */
290  tv.tv_sec = timeout;
291  tv.tv_usec = 0;
292 
293  /* Wait till we have a change in the fd status */
294  retval = select (fd+1, &readout, NULL, NULL, &tv);
295 
296  /* Return 0 on successful fd change */
297  if (retval > 0)
298  return TTY_OK;
299  /* Return -1 due to an error */
300  else if (retval == -1)
301  return TTY_SELECT_ERROR;
302  /* Return -2 if time expires before anything interesting happens */
303  else
304  return TTY_TIME_OUT;
305 
306 #endif
307 }
308 
309 int tty_write(int fd, const char * buf, int nbytes, int *nbytes_written)
310 {
311  #ifdef _WIN32
312  return TTY_ERRNO;
313  #else
314 
315  if (fd == -1)
316  return TTY_ERRNO;
317 
318  int bytes_w = 0;
319  *nbytes_written = 0;
320 
321  if (tty_debug)
322  {
323  int i=0;
324  for (i=0; i < nbytes; i++)
325  IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char) buf[i], buf[i]);
326  }
327 
328  while (nbytes > 0)
329  {
330 
331  bytes_w = write(fd, buf+(*nbytes_written), nbytes);
332 
333  if (bytes_w < 0)
334  return TTY_WRITE_ERROR;
335 
336  *nbytes_written += bytes_w;
337  //buf += bytes_w;
338  nbytes -= bytes_w;
339  }
340 
341  return TTY_OK;
342 
343  #endif
344 }
345 
346 int tty_write_string(int fd, const char * buf, int *nbytes_written)
347 {
348  #ifdef _WIN32
349  return TTY_ERRNO;
350  #else
351 
352  if (fd == -1)
353  return TTY_ERRNO;
354 
355  unsigned int nbytes;
356  int bytes_w = 0;
357  *nbytes_written = 0;
358 
359  nbytes = strlen(buf);
360 
361  if (tty_debug)
362  {
363  int i=0;
364  for (i=0; i < nbytes; i++)
365  IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char) buf[i], buf[i]);
366  }
367 
368  while (nbytes > 0)
369  {
370 
371  bytes_w = write(fd, buf+(*nbytes_written), nbytes);
372 
373  if (bytes_w < 0)
374  return TTY_WRITE_ERROR;
375 
376  *nbytes_written += bytes_w;
377  //buf += bytes_w;
378  nbytes -= bytes_w;
379  }
380 
381  return TTY_OK;
382 
383  #endif
384 }
385 
386 int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
387 {
388  #ifdef _WIN32
389  return TTY_ERRNO;
390  #else
391 
392  if (fd == -1)
393  return TTY_ERRNO;
394 
395  int bytesRead = 0;
396  int err = 0;
397  *nbytes_read =0;
398 
399  if (nbytes <=0)
400  return TTY_PARAM_ERROR;
401 
402  if (tty_debug)
403  IDLog("%s: Request to read %d bytes with %d timeout for fd %d\n", __FUNCTION__, nbytes, timeout, fd);
404 
405  while (nbytes > 0)
406  {
407  if ( (err = tty_timeout(fd, timeout)) )
408  return err;
409 
410  bytesRead = read(fd, buf+(*nbytes_read), ((uint32_t) nbytes));
411 
412  if (bytesRead < 0 )
413  return TTY_READ_ERROR;
414 
415  if (tty_debug)
416  {
417  IDLog("%d bytes read and %d bytes remaining...\n", bytesRead, nbytes-bytesRead);
418  int i=0;
419  for (i=*nbytes_read; i < (*nbytes_read+bytesRead); i++)
420  IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char) buf[i], buf[i]);
421  }
422 
423  *nbytes_read += bytesRead;
424  nbytes -= bytesRead;
425 
426  }
427 
428  return TTY_OK;
429 
430  #endif
431 }
432 
433 int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
434 {
435  #ifdef _WIN32
436  return TTY_ERRNO;
437  #else
438 
439  if (fd == -1)
440  return TTY_ERRNO;
441 
442  int bytesRead = 0;
443  int err = TTY_OK;
444  *nbytes_read = 0;
445 
446  uint8_t *read_char = 0;
447 
448  if (tty_debug)
449  IDLog("%s: Request to read until stop char '%c' with %d timeout for fd %d\n", __FUNCTION__, stop_char, timeout, fd);
450 
451  for (;;)
452  {
453  if ( (err = tty_timeout(fd, timeout)) )
454  return err;
455 
456  read_char = buf+*nbytes_read;
457  bytesRead = read(fd, read_char, 1);
458 
459  if (bytesRead < 0 )
460  return TTY_READ_ERROR;
461 
462  if (tty_debug)
463  IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, (*nbytes_read), *read_char, *read_char);
464 
465  (*nbytes_read)++;
466 
467  if (*read_char == stop_char)
468  return TTY_OK;
469  }
470 
471  return TTY_TIME_OUT;
472 
473  #endif
474 }
475 
476 #if defined(BSD) && !defined(__GNU__)
477 // BSD - OSX version
478 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
479 {
480  int t_fd = -1;
481  int bps;
482  char msg[80];
483  int handshake;
484  struct termios tty_setting;
485 
486  // Open the serial port read/write, with no controlling terminal, and don't wait for a connection.
487  // The O_NONBLOCK flag also causes subsequent I/O on the device to be non-blocking.
488  // See open(2) ("man 2 open") for details.
489 
490  t_fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
491  if (t_fd == -1)
492  {
493  printf("Error opening serial port %s - %s(%d).\n",
494  device, strerror(errno), errno);
495  goto error;
496  }
497 
498  // Note that open() follows POSIX semantics: multiple open() calls to the same file will succeed
499  // unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned
500  // processes.
501  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
502 
503  if (ioctl(t_fd, TIOCEXCL) == -1)
504  {
505  printf("Error setting TIOCEXCL on %s - %s(%d).\n",
506  device, strerror(errno), errno);
507  goto error;
508  }
509 
510  // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
511  // See fcntl(2) ("man 2 fcntl") for details.
512 
513  if (fcntl(t_fd, F_SETFL, 0) == -1)
514  {
515  printf("Error clearing O_NONBLOCK %s - %s(%d).\n",
516  device, strerror(errno), errno);
517  goto error;
518  }
519 
520  // Get the current options and save them so we can restore the default settings later.
521  if (tcgetattr(t_fd, &tty_setting) == -1)
522  {
523  printf("Error getting tty attributes %s - %s(%d).\n",
524  device, strerror(errno), errno);
525  goto error;
526  }
527 
528  // Set raw input (non-canonical) mode, with reads blocking until either a single character
529  // has been received or a one second timeout expires.
530  // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") for details.
531 
532  cfmakeraw(&tty_setting);
533  tty_setting.c_cc[VMIN] = 1;
534  tty_setting.c_cc[VTIME] = 10;
535 
536  // The baud rate, word length, and handshake options can be set as follows:
537  switch (bit_rate) {
538  case 0:
539  bps = B0;
540  break;
541  case 50:
542  bps = B50;
543  break;
544  case 75:
545  bps = B75;
546  break;
547  case 110:
548  bps = B110;
549  break;
550  case 134:
551  bps = B134;
552  break;
553  case 150:
554  bps = B150;
555  break;
556  case 200:
557  bps = B200;
558  break;
559  case 300:
560  bps = B300;
561  break;
562  case 600:
563  bps = B600;
564  break;
565  case 1200:
566  bps = B1200;
567  break;
568  case 1800:
569  bps = B1800;
570  break;
571  case 2400:
572  bps = B2400;
573  break;
574  case 4800:
575  bps = B4800;
576  break;
577  case 9600:
578  bps = B9600;
579  break;
580  case 19200:
581  bps = B19200;
582  break;
583  case 38400:
584  bps = B38400;
585  break;
586  case 57600:
587  bps = B57600;
588  break;
589  case 115200:
590  bps = B115200;
591  break;
592  case 230400:
593  bps = B230400;
594  break;
595  default:
596  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
597  perror(NULL);
598  else
599  perror(msg);
600  return TTY_PARAM_ERROR;
601  }
602 
603  cfsetspeed(&tty_setting, bps); // Set baud rate
604  /* word size */
605  switch (word_size) {
606  case 5:
607  tty_setting.c_cflag |= CS5;
608  break;
609  case 6:
610  tty_setting.c_cflag |= CS6;
611  break;
612  case 7:
613  tty_setting.c_cflag |= CS7;
614  break;
615  case 8:
616  tty_setting.c_cflag |= CS8;
617  break;
618  default:
619 
620  fprintf( stderr, "Default\n") ;
621  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
622  perror(NULL);
623  else
624  perror(msg);
625 
626  return TTY_PARAM_ERROR;
627  }
628 
629  /* parity */
630  switch (parity) {
631  case PARITY_NONE:
632  break;
633  case PARITY_EVEN:
634  tty_setting.c_cflag |= PARENB;
635  break;
636  case PARITY_ODD:
637  tty_setting.c_cflag |= PARENB | PARODD;
638  break;
639  default:
640 
641  fprintf( stderr, "Default1\n") ;
642  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
643  perror(NULL);
644  else
645  perror(msg);
646 
647  return TTY_PARAM_ERROR;
648  }
649 
650  /* stop_bits */
651  switch (stop_bits) {
652  case 1:
653  break;
654  case 2:
655  tty_setting.c_cflag |= CSTOPB;
656  break;
657  default:
658  fprintf( stderr, "Default2\n") ;
659  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
660  perror(NULL);
661  else
662  perror(msg);
663 
664  return TTY_PARAM_ERROR;
665  }
666 
667 #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
668  // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
669  // other than those specified by POSIX. The driver for the underlying serial hardware
670  // ultimately determines which baud rates can be used. This ioctl sets both the input
671  // and output speed.
672 
673  speed_t speed = 14400; // Set 14400 baud
674  if (ioctl(t_fd, IOSSIOSPEED, &speed) == -1)
675  {
676  printf("Error calling ioctl(..., IOSSIOSPEED, ...) - %s(%d).\n",
677  strerror(errno), errno);
678  }
679 #endif
680 
681  // Cause the new options to take effect immediately.
682  if (tcsetattr(t_fd, TCSANOW, &tty_setting) == -1)
683  {
684  printf("Error setting tty attributes %s - %s(%d).\n",
685  device, strerror(errno), errno);
686  goto error;
687  }
688 
689  // To set the modem handshake lines, use the following ioctls.
690  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
691 
692  if (ioctl(t_fd, TIOCSDTR) == -1) // Assert Data Terminal Ready (DTR)
693  {
694  printf("Error asserting DTR %s - %s(%d).\n",
695  device, strerror(errno), errno);
696  }
697 
698  if (ioctl(t_fd, TIOCCDTR) == -1) // Clear Data Terminal Ready (DTR)
699  {
700  printf("Error clearing DTR %s - %s(%d).\n",
701  device, strerror(errno), errno);
702  }
703 
704  handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
705  if (ioctl(t_fd, TIOCMSET, &handshake) == -1)
706  // Set the modem lines depending on the bits set in handshake
707  {
708  printf("Error setting handshake lines %s - %s(%d).\n",
709  device, strerror(errno), errno);
710  }
711 
712  // To read the state of the modem lines, use the following ioctl.
713  // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
714 
715  if (ioctl(t_fd, TIOCMGET, &handshake) == -1)
716  // Store the state of the modem lines in handshake
717  {
718  printf("Error getting handshake lines %s - %s(%d).\n",
719  device, strerror(errno), errno);
720  }
721 
722  printf("Handshake lines currently set to %d\n", handshake);
723 
724 #if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
725  unsigned long mics = 1UL;
726 
727  // Set the receive latency in microseconds. Serial drivers use this value to determine how often to
728  // dequeue characters received by the hardware. Most applications don't need to set this value: if an
729  // app reads lines of characters, the app can't do anything until the line termination character has been
730  // received anyway. The most common applications which are sensitive to read latency are MIDI and IrDA
731  // applications.
732 
733  if (ioctl(t_fd, IOSSDATALAT, &mics) == -1)
734  {
735  // set latency to 1 microsecond
736  printf("Error setting read latency %s - %s(%d).\n",
737  device, strerror(errno), errno);
738  goto error;
739  }
740 #endif
741 
742  *fd = t_fd;
743  /* return success */
744  return TTY_OK;
745 
746  // Failure path
747 error:
748  if (t_fd != -1)
749  {
750  close(t_fd);
751  *fd = -1;
752  }
753 
754  return TTY_PORT_FAILURE;
755 }
756 #else
757 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
758 {
759 #ifdef _WIN32
760  return TTY_PORT_FAILURE;
761 
762 #else
763  int t_fd=-1;
764  char msg[80];
765  int bps;
766  struct termios tty_setting;
767 
768  if ( (t_fd = open(device, O_RDWR | O_NOCTTY )) == -1)
769  {
770  *fd = -1;
771 
772  return TTY_PORT_FAILURE;
773  }
774 
775  /* Control Modes
776  Set bps rate */
777  switch (bit_rate) {
778  case 0:
779  bps = B0;
780  break;
781  case 50:
782  bps = B50;
783  break;
784  case 75:
785  bps = B75;
786  break;
787  case 110:
788  bps = B110;
789  break;
790  case 134:
791  bps = B134;
792  break;
793  case 150:
794  bps = B150;
795  break;
796  case 200:
797  bps = B200;
798  break;
799  case 300:
800  bps = B300;
801  break;
802  case 600:
803  bps = B600;
804  break;
805  case 1200:
806  bps = B1200;
807  break;
808  case 1800:
809  bps = B1800;
810  break;
811  case 2400:
812  bps = B2400;
813  break;
814  case 4800:
815  bps = B4800;
816  break;
817  case 9600:
818  bps = B9600;
819  break;
820  case 19200:
821  bps = B19200;
822  break;
823  case 38400:
824  bps = B38400;
825  break;
826  case 57600:
827  bps = B57600;
828  break;
829  case 115200:
830  bps = B115200;
831  break;
832  case 230400:
833  bps = B230400;
834  break;
835  default:
836  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
837  perror(NULL);
838  else
839  perror(msg);
840  return TTY_PARAM_ERROR;
841  }
842  if ((cfsetispeed(&tty_setting, bps) < 0) ||
843  (cfsetospeed(&tty_setting, bps) < 0))
844  {
845  perror("tty_connect: failed setting bit rate.");
846  return TTY_PORT_FAILURE;
847  }
848 
849  /* Control Modes
850  set no flow control word size, parity and stop bits.
851  Also don't hangup automatically and ignore modem status.
852  Finally enable receiving characters. */
853  tty_setting.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | HUPCL | CRTSCTS);
854  tty_setting.c_cflag |= (CLOCAL | CREAD);
855 
856  /* word size */
857  switch (word_size) {
858  case 5:
859  tty_setting.c_cflag |= CS5;
860  break;
861  case 6:
862  tty_setting.c_cflag |= CS6;
863  break;
864  case 7:
865  tty_setting.c_cflag |= CS7;
866  break;
867  case 8:
868  tty_setting.c_cflag |= CS8;
869  break;
870  default:
871 
872  fprintf( stderr, "Default\n") ;
873  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
874  perror(NULL);
875  else
876  perror(msg);
877 
878  return TTY_PARAM_ERROR;
879  }
880 
881  /* parity */
882  switch (parity) {
883  case PARITY_NONE:
884  break;
885  case PARITY_EVEN:
886  tty_setting.c_cflag |= PARENB;
887  break;
888  case PARITY_ODD:
889  tty_setting.c_cflag |= PARENB | PARODD;
890  break;
891  default:
892 
893  fprintf( stderr, "Default1\n") ;
894  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
895  perror(NULL);
896  else
897  perror(msg);
898 
899  return TTY_PARAM_ERROR;
900  }
901 
902  /* stop_bits */
903  switch (stop_bits) {
904  case 1:
905  break;
906  case 2:
907  tty_setting.c_cflag |= CSTOPB;
908  break;
909  default:
910  fprintf( stderr, "Default2\n") ;
911  if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
912  perror(NULL);
913  else
914  perror(msg);
915 
916  return TTY_PARAM_ERROR;
917  }
918  /* Control Modes complete */
919 
920  /* Ignore bytes with parity errors and make terminal raw and dumb.*/
921  tty_setting.c_iflag &= ~(PARMRK | ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON | IXANY);
922  tty_setting.c_iflag |= INPCK | IGNPAR | IGNBRK;
923 
924  /* Raw output.*/
925  tty_setting.c_oflag &= ~(OPOST | ONLCR);
926 
927  /* Local Modes
928  Don't echo characters. Don't generate signals.
929  Don't process any characters.*/
930  tty_setting.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN | NOFLSH | TOSTOP);
931  tty_setting.c_lflag |= NOFLSH;
932 
933  /* blocking read until 1 char arrives */
934  tty_setting.c_cc[VMIN] = 1;
935  tty_setting.c_cc[VTIME] = 0;
936 
937  /* now clear input and output buffers and activate the new terminal settings */
938  tcflush(t_fd, TCIOFLUSH);
939  if (tcsetattr(t_fd, TCSANOW, &tty_setting))
940  {
941  perror("tty_connect: failed setting attributes on serial port.");
942  tty_disconnect(t_fd);
943  return TTY_PORT_FAILURE;
944  }
945 
946  *fd = t_fd;
947  /* return success */
948  return TTY_OK;
949 #endif
950 }
951 // Unix - Linux version
952 
953 #endif
954 
955 
956 int tty_disconnect(int fd)
957 {
958  if (fd == -1)
959  return TTY_ERRNO;
960 
961 #ifdef _WIN32
962  return TTY_ERRNO;
963 #else
964  int err;
965  tcflush(fd, TCIOFLUSH);
966  err = close(fd);
967 
968  if (err != 0)
969  return TTY_ERRNO;
970 
971  return TTY_OK;
972 #endif
973 }
974 
975 void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
976 {
977  char error_string[512];
978 
979  switch (err_code)
980  {
981  case TTY_OK:
982  strncpy(err_msg, "No Error", err_msg_len);
983  break;
984 
985  case TTY_READ_ERROR:
986  snprintf(error_string, 512, "Read Error: %s", strerror(errno));
987  strncpy(err_msg, error_string, err_msg_len);
988  break;
989 
990  case TTY_WRITE_ERROR:
991  snprintf(error_string, 512, "Write Error: %s", strerror(errno));
992  strncpy(err_msg, error_string, err_msg_len);
993  break;
994 
995  case TTY_SELECT_ERROR:
996  snprintf(error_string, 512, "Select Error: %s", strerror(errno));
997  strncpy(err_msg, error_string, err_msg_len);
998  break;
999 
1000  case TTY_TIME_OUT:
1001  strncpy(err_msg, "Timeout error", err_msg_len);
1002  break;
1003 
1004  case TTY_PORT_FAILURE:
1005  if (errno == EACCES)
1006  snprintf(error_string, 512, "Port failure Error: %s. Try adding your user to the dialout group and restart (sudo adduser $USER dialout)", strerror(errno));
1007  else
1008  snprintf(error_string, 512, "Port failure Error: %s. Check if device is connected to this port.", strerror(errno));
1009 
1010  strncpy(err_msg, error_string, err_msg_len);
1011  break;
1012 
1013  case TTY_PARAM_ERROR:
1014  strncpy(err_msg, "Parameter error", err_msg_len);
1015  break;
1016 
1017  case TTY_ERRNO:
1018  snprintf(error_string, 512, "%s", strerror(errno));
1019  strncpy(err_msg, error_string, err_msg_len);
1020  break;
1021 
1022  default:
1023  strncpy(err_msg, "Error: unrecognized error code", err_msg_len);
1024  break;
1025 
1026 
1027  }
1028 }
1029 
1030 /* return static string corresponding to the given property or light state */
1031 const char *
1033 {
1034  switch (s) {
1035  case IPS_IDLE: return ("Idle");
1036  case IPS_OK: return ("Ok");
1037  case IPS_BUSY: return ("Busy");
1038  case IPS_ALERT: return ("Alert");
1039  default:
1040  fprintf (stderr, "Impossible IPState %d\n", s);
1041  return NULL;
1042  }
1043 }
1044 
1045 /* crack string into IPState.
1046  * return 0 if ok, else -1
1047  */
1048 int
1049 crackIPState (const char *str, IPState *ip)
1050 {
1051  if (!strcmp (str, "Idle")) *ip = IPS_IDLE;
1052  else if (!strcmp (str, "Ok")) *ip = IPS_OK;
1053  else if (!strcmp (str, "Busy")) *ip = IPS_BUSY;
1054  else if (!strcmp (str, "Alert")) *ip = IPS_ALERT;
1055  else return (-1);
1056  return (0);
1057 }
1058 
1059 /* crack string into ISState.
1060  * return 0 if ok, else -1
1061  */
1062 int
1063 crackISState (const char *str, ISState *ip)
1064 {
1065  if (!strcmp (str, "On")) *ip = ISS_ON;
1066  else if (!strcmp (str, "Off")) *ip = ISS_OFF;
1067  else return (-1);
1068  return (0);
1069 }
1070 
1071 int
1072 crackIPerm (const char *str, IPerm *ip)
1073 {
1074  if (!strcmp (str, "rw")) *ip = IP_RW;
1075  else if (!strcmp (str, "ro")) *ip = IP_RO;
1076  else if (!strcmp (str, "wo")) *ip = IP_WO;
1077  else return (-1);
1078  return (0);
1079 }
1080 
1081 int crackISRule (const char *str, ISRule *ip)
1082 {
1083  if (!strcmp (str, "OneOfMany")) *ip = ISR_1OFMANY;
1084  else if (!strcmp (str, "AtMostOne")) *ip = ISR_ATMOST1;
1085  else if (!strcmp (str, "AnyOfMany")) *ip = ISR_NOFMANY;
1086  else return (-1);
1087  return (0);
1088 }
1089 
1090 /* return static string corresponding to the given switch state */
1091 const char *
1093 {
1094  switch (s) {
1095  case ISS_ON: return ("On");
1096  case ISS_OFF: return ("Off");
1097  default:
1098  fprintf (stderr, "Impossible ISState %d\n", s);
1099  return NULL;
1100  }
1101 }
1102 
1103 /* return static string corresponding to the given Rule */
1104 const char *
1106 {
1107  switch (r) {
1108  case ISR_1OFMANY: return ("OneOfMany");
1109  case ISR_ATMOST1: return ("AtMostOne");
1110  case ISR_NOFMANY: return ("AnyOfMany");
1111  default:
1112  fprintf (stderr, "Impossible ISRule %d\n", r);
1113  return NULL;
1114  }
1115 }
1116 
1117 /* return static string corresponding to the given IPerm */
1118 const char *
1120 {
1121  switch (p) {
1122  case IP_RO: return ("ro");
1123  case IP_WO: return ("wo");
1124  case IP_RW: return ("rw");
1125  default:
1126  fprintf (stderr, "Impossible IPerm %d\n", p);
1127  return NULL;
1128  }
1129 }
1130 
1131 /* print the boilerplate comment introducing xml */
1132 void
1133 xmlv1()
1134 {
1135  printf ("<?xml version='1.0'?>\n");
1136 }
1137 
1138 /* pull out device and name attributes from root.
1139  * return 0 if ok else -1 with reason in msg[].
1140  */
1141 int
1142 crackDN (XMLEle *root, char **dev, char **name, char msg[])
1143 {
1144  XMLAtt *ap;
1145 
1146  ap = findXMLAtt (root, "device");
1147  if (!ap) {
1148  sprintf (msg, "%s requires 'device' attribute", tagXMLEle(root));
1149  return (-1);
1150  }
1151  *dev = valuXMLAtt(ap);
1152 
1153  ap = findXMLAtt (root, "name");
1154  if (!ap) {
1155  sprintf (msg, "%s requires 'name' attribute", tagXMLEle(root));
1156  return (-1);
1157  }
1158  *name = valuXMLAtt(ap);
1159 
1160  return (0);
1161 }
1162 
1163 /* find a member of an IText vector, else NULL */
1164 IText *
1165 IUFindText (const ITextVectorProperty *tvp, const char *name)
1166 {
1167  int i;
1168 
1169  for (i = 0; i < tvp->ntp; i++)
1170  if (strcmp (tvp->tp[i].name, name) == 0)
1171  return (&tvp->tp[i]);
1172  fprintf (stderr, "No IText '%s' in %s.%s\n",name,tvp->device,tvp->name);
1173  return (NULL);
1174 }
1175 
1176 /* find a member of an INumber vector, else NULL */
1177 INumber *
1178 IUFindNumber(const INumberVectorProperty *nvp, const char *name)
1179 {
1180  int i;
1181 
1182  for (i = 0; i < nvp->nnp; i++)
1183  if (strcmp (nvp->np[i].name, name) == 0)
1184  return (&nvp->np[i]);
1185  fprintf(stderr,"No INumber '%s' in %s.%s\n",name,nvp->device,nvp->name);
1186  return (NULL);
1187 }
1188 
1189 /* find a member of an ISwitch vector, else NULL */
1190 ISwitch *
1191 IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
1192 {
1193  int i;
1194 
1195  for (i = 0; i < svp->nsp; i++)
1196  if (strcmp (svp->sp[i].name, name) == 0)
1197  return (&svp->sp[i]);
1198  fprintf(stderr,"No ISwitch '%s' in %s.%s\n",name,svp->device,svp->name);
1199  return (NULL);
1200 }
1201 
1202 /* find a member of an ILight vector, else NULL */
1203 ILight *
1204 IUFindLight(const ILightVectorProperty *lvp, const char *name)
1205 {
1206  int i;
1207 
1208  for (i = 0; i < lvp->nlp; i++)
1209  if (strcmp (lvp->lp[i].name, name) == 0)
1210  return (&lvp->lp[i]);
1211  fprintf(stderr,"No ILight '%s' in %s.%s\n",name,lvp->device,lvp->name);
1212  return (NULL);
1213 }
1214 
1215 /* find a member of an IBLOB vector, else NULL */
1216 IBLOB *
1217 IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
1218 {
1219  int i;
1220 
1221  for (i = 0; i < bvp->nbp; i++)
1222  if (strcmp (bvp->bp[i].name, name) == 0)
1223  return (&bvp->bp[i]);
1224  fprintf(stderr,"No IBLOB '%s' in %s.%s\n",name,bvp->device,bvp->name);
1225  return (NULL);
1226 }
1227 
1228 /* find an ON member of an ISwitch vector, else NULL.
1229  * N.B. user must make sense of result with ISRule in mind.
1230  */
1231 ISwitch *
1233 {
1234  int i;
1235 
1236  for (i = 0; i < svp->nsp; i++)
1237  if (svp->sp[i].s == ISS_ON)
1238  return (&svp->sp[i]);
1239  /*fprintf(stderr, "No ISwitch On in %s.%s\n", svp->device, svp->name);*/
1240  return (NULL);
1241 }
1242 
1243 /* Find index of the ON member of an ISwitchVectorProperty */
1245 {
1246  int i;
1247 
1248  for (i = 0; i < svp->nsp; i++)
1249  if (svp->sp[i].s == ISS_ON)
1250  return i;
1251  return -1;
1252 }
1253 
1254 /* Set all switches to off */
1255 void
1257 {
1258  int i;
1259 
1260  for (i = 0; i < svp->nsp; i++)
1261  svp->sp[i].s = ISS_OFF;
1262 }
1263 
1264 /* save malloced copy of newtext in tp->text, reusing if not first time */
1265 void
1266 IUSaveText (IText *tp, const char *newtext)
1267 {
1268  /* seed for realloc */
1269  if (tp->text == NULL)
1270  tp->text = malloc (1);
1271 
1272  /* copy in fresh string */
1273  tp->text = strcpy (realloc (tp->text, strlen(newtext)+1), newtext);
1274 }
1275 
1276 double rangeHA(double r)
1277 {
1278  double res = r;
1279  while (res< -12.0) res+=24.0;
1280  while (res>= 12.0) res-=24.0;
1281  return res;
1282 }
1283 
1284 
1285 double range24(double r)
1286 {
1287  double res = r;
1288  while (res<0.0) res+=24.0;
1289  while (res>24.0) res-=24.0;
1290  return res;
1291 }
1292 
1293 double range360(double r)
1294 {
1295  double res = r;
1296  while (res<0.0) res+=360.0;
1297  while (res>360.0) res-=360.0;
1298  return res;
1299 }
1300 
1301 double rangeDec(double decdegrees)
1302 {
1303  if ((decdegrees >= 270.0) && (decdegrees <= 360.0))
1304  return (decdegrees - 360.0);
1305  if ((decdegrees >= 180.0) && (decdegrees < 270.0))
1306  return (180.0 - decdegrees);
1307  if ((decdegrees >= 90.0) && (decdegrees < 180.0))
1308  return (180.0 - decdegrees);
1309  return decdegrees;
1310 }
1311 
1312 double get_local_sideral_time(double longitude)
1313 {
1314  double SD = ln_get_apparent_sidereal_time(ln_get_julian_from_sys()) - (360.0 - longitude)/15.0;
1315 
1316  return range24(SD);
1317 }
1318 
1319 double get_local_hour_angle(double sideral_time, double ra)
1320 {
1321 
1322  double HA = sideral_time - ra;
1323 
1324  if (HA > 12)
1325  HA -= 24;
1326  else if (HA < -12)
1327  HA += 24;
1328 
1329  return HA;
1330 }
1331 
1332 #if defined( _MSC_VER )
1333 #undef snprintf
1334 #pragma warning(pop)
1335 #endif
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1266
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1256
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double.
Definition: indicom.c:175
const char * pstateStr(IPState s)
Definition: indicom.c:1032
Light vector property descriptor.
Definition: indiapi.h:345
char device[MAXINDIDEVICE]
Definition: indiapi.h:211
char * text
Definition: indiapi.h:197
int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
Establishes a tty connection to a terminal device.
Definition: indicom.c:757
ISState s
Definition: indiapi.h:296
ILight * IUFindLight(const ILightVectorProperty *lvp, const char *name)
Find an ILight member in a vector Light property.
Definition: indicom.c:1204
int extractISOTime(const char *timestr, struct ln_date *iso_date)
Extract ISO 8601 time and store it in a tm struct.
Definition: indicom.c:79
int crackISRule(const char *str, ISRule *ip)
Extract switch rule (OneOfMany, OnlyOne..etc) from the supplied string.
Definition: indicom.c:1081
double get_local_sideral_time(double longitude)
get_local_sideral_time Returns local sideral time given longitude and system clock.
Definition: indicom.c:1312
IPState
Property state.
Definition: indiapi.h:128
One light descriptor.
Definition: indiapi.h:334
char device[MAXINDIDEVICE]
Definition: indiapi.h:394
One number descriptor.
Definition: indiapi.h:237
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition: indicom.c:268
One Blob (Binary Large Object) descriptor.
Definition: indiapi.h:369
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition: indicom.c:1285
char device[MAXINDIDEVICE]
Definition: indiapi.h:347
int crackIPerm(const char *str, IPerm *ip)
Extract property permission state (RW, RO, WO) from the supplied string.
Definition: indicom.c:1072
Definition: indiapi.h:152
double rangeHA(double r)
rangeHA Limits the hour angle value to be between -12 —> 12
Definition: indicom.c:1276
Definition: indiapi.h:151
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.c:560
Definition: indiapi.h:153
int tty_disconnect(int fd)
Closes a tty connection and flushes the bus.
Definition: indicom.c:956
Switch vector property descriptor.
Definition: indiapi.h:304
char name[MAXINDINAME]
Definition: indiapi.h:269
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:346
int numberFormat(char *buf, const char *format, double value)
Fill buffer with properly formatted INumber string.
Definition: indicom.c:219
const char * permStr(IPerm p)
Definition: indicom.c:1119
One switch descriptor.
Definition: indiapi.h:293
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition: indicom.c:1165
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string.
Definition: indicom.c:111
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition: indicom.c:1319
char name[MAXINDINAME]
Definition: indiapi.h:349
BLOB (Binary Large Object) vector property descriptor.
Definition: indiapi.h:392
const char * timestamp()
Create an ISO 8601 formatted time stamp. The format is YYYY-MM-DDTHH:MM:SS.
Definition: indicom.c:256
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:433
IBLOB * IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
Find an IBLOB member in a vector BLOB property.
Definition: indicom.c:1217
int crackDN(XMLEle *root, char **dev, char **name, char msg[])
Extract dev and name attributes from an XML element.
Definition: indicom.c:1142
const char * ruleStr(ISRule r)
Definition: indicom.c:1105
int crackISState(const char *str, ISState *ip)
Extract switch state (On or Off) from the supplied string.
Definition: indicom.c:1063
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indicom.c:1178
char name[MAXINDINAME]
Definition: indiapi.h:371
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1244
Number vector property descriptor.
Definition: indiapi.h:265
int crackIPState(const char *str, IPState *ip)
Extract property state (Idle, OK, Busy, Alert) from the supplied string.
Definition: indicom.c:1049
One text descriptor.
Definition: indiapi.h:191
char name[MAXINDINAME]
Definition: indiapi.h:308
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:309
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.c:451
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:386
char name[MAXINDINAME]
Definition: indiapi.h:213
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:975
Interface to the reference INDI C API device implementation on the Device Driver side.
void IDLog(const char *fmt,...)
Function Drivers call to log a message locally.
Definition: indicom.c:244
double rangeDec(double decdegrees)
rangeDec Limits declination value to be in -90 to 90 range.
Definition: indicom.c:1301
IPerm
Permission hint, with respect to client.
Definition: indiapi.h:149
ISState
Switch state.
Definition: indiapi.h:119
char name[MAXINDINAME]
Definition: indiapi.h:193
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition: indicom.c:1293
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition: indicom.c:1191
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition: indicom.c:1232
const char * sstateStr(ISState s)
Definition: indicom.c:1092
Text vector property descriptor.
Definition: indiapi.h:209
char name[MAXINDINAME]
Definition: indiapi.h:396
char device[MAXINDIDEVICE]
Definition: indiapi.h:306
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.c:532
ISRule
Switch vector rule hint.
Definition: indiapi.h:139
char device[MAXINDIDEVICE]
Definition: indiapi.h:267
Implementations for common driver routines.