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