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