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 "indicom.h"
29 
30 #include "indidevapi.h"
31 #include "locale_compat.h"
32 
33 #include "config.h"
34 
35 #if defined(HAVE_LIBNOVA)
36 #include <libnova/julian_day.h>
37 #include <libnova/sidereal_time.h>
38 #endif // HAVE_LIBNOVA
39 
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <math.h>
43 #include <stdarg.h>
44 #include <stdint.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <time.h>
49 
50 #ifdef __linux__
51 #include <sys/ioctl.h>
52 #endif
53 
54 #ifdef __APPLE__
55 #include <sys/param.h>
56 #endif
57 
58 #if defined(BSD) && !defined(__GNU__)
59 #include <IOKit/serial/ioss.h>
60 #include <sys/ioctl.h>
61 #endif
62 
63 #ifdef _WIN32
64 #undef CX
65 #undef CY
66 #endif
67 
68 #ifndef _WIN32
69 #include <unistd.h>
70 #include <termios.h>
71 #include <sys/param.h>
72 #define PARITY_NONE 0
73 #define PARITY_EVEN 1
74 #define PARITY_ODD  2
75 #endif
76 
77 #if defined(_MSC_VER)
78 #define snprintf _snprintf
79 #pragma warning(push)
80 ///@todo Introduce plattform indipendent safe functions as macros to fix this
81 #pragma warning(disable : 4996)
82 #endif
83 
84 #define MAXRBUF 2048
85 
86 int tty_debug = 0;
87 int tty_gemini_udp_format = 0;
88 int tty_generic_udp_format = 0;
89 int tty_sequence_number = 1;
90 int tty_clear_trailing_lf = 0;
91 
92 #if defined(HAVE_LIBNOVA)
extractISOTime(const char * timestr,struct ln_date * iso_date)93 int extractISOTime(const char *timestr, struct ln_date *iso_date)
94 {
95     struct tm utm;
96 
97     if (strptime(timestr, "%Y/%m/%dT%H:%M:%S", &utm))
98     {
99         ln_get_date_from_tm(&utm, iso_date);
100         return (0);
101     }
102 
103     if (strptime(timestr, "%Y-%m-%dT%H:%M:%S", &utm))
104     {
105         ln_get_date_from_tm(&utm, iso_date);
106         return (0);
107     }
108 
109     return (-1);
110 }
111 #endif
112 
113 /* sprint the variable a in sexagesimal format into out[].
114  * w is the number of spaces for the whole part.
115  * fracbase is the number of pieces a whole is to broken into; valid options:
116  *	360000:	<w>:mm:ss.ss
117  *	36000:	<w>:mm:ss.s
118  *	3600:	<w>:mm:ss
119  *	600:	<w>:mm.m
120  *	60:	<w>:mm
121  * return number of characters written to out, not counting final '\0'.
122  */
fs_sexa(char * out,double a,int w,int fracbase)123 int fs_sexa(char *out, double a, int w, int fracbase)
124 {
125     char *out0 = out;
126     unsigned long n;
127     int d;
128     int f;
129     int m;
130     int s;
131     int isneg;
132 
133     /* save whether it's negative but do all the rest with a positive */
134     isneg = (a < 0);
135     if (isneg)
136         a = -a;
137 
138     /* convert to an integral number of whole portions */
139     n = (unsigned long)(a * fracbase + 0.5);
140     d = n / fracbase;
141     f = n % fracbase;
142 
143     /* form the whole part; "negative 0" is a special case */
144     if (isneg && d == 0)
145         out += snprintf(out, MAXINDIFORMAT, "%*s-0", w - 2, "");
146     else
147         out += snprintf(out, MAXINDIFORMAT, "%*d", w, isneg ? -d : d);
148 
149     /* do the rest */
150     switch (fracbase)
151     {
152     case 60: /* dd:mm */
153         m = f / (fracbase / 60);
154         out += snprintf(out, MAXINDIFORMAT, ":%02d", m);
155         break;
156     case 600: /* dd:mm.m */
157         out += snprintf(out, MAXINDIFORMAT, ":%02d.%1d", f / 10, f % 10);
158         break;
159     case 3600: /* dd:mm:ss */
160         m = f / (fracbase / 60);
161         s = f % (fracbase / 60);
162         out += snprintf(out, MAXINDIFORMAT, ":%02d:%02d", m, s);
163         break;
164     case 36000: /* dd:mm:ss.s*/
165         m = f / (fracbase / 60);
166         s = f % (fracbase / 60);
167         out += snprintf(out, MAXINDIFORMAT, ":%02d:%02d.%1d", m, s / 10, s % 10);
168         break;
169     case 360000: /* dd:mm:ss.ss */
170         m = f / (fracbase / 60);
171         s = f % (fracbase / 60);
172         out += snprintf(out, MAXINDIFORMAT, ":%02d:%02d.%02d", m, s / 100, s % 100);
173         break;
174     default:
175         printf("fs_sexa: unknown fracbase: %d\n", fracbase);
176         return -1;
177     }
178 
179     return (out - out0);
180 }
181 
182 /* convert sexagesimal string str AxBxC to double.
183  *   x can be anything non-numeric. Any missing A, B or C will be assumed 0.
184  *   optional - and + can be anywhere.
185  * return 0 if ok, -1 if can't find a thing.
186  */
f_scansexa(const char * str0,double * dp)187 int f_scansexa(const char *str0, /* input string */
188                double *dp)       /* cracked value, if return 0 */
189 {
190     locale_char_t *orig = indi_locale_C_numeric_push();
191 
192     double a = 0, b = 0, c = 0;
193     char str[128];
194     //char *neg;
195     uint8_t isNegative=0;
196     int r= 0;
197 
198     /* copy str0 so we can play with it */
199     strncpy(str, str0, sizeof(str) - 1);
200     str[sizeof(str) - 1] = '\0';
201 
202     /* remove any spaces */
203     char* i = str;
204     char* j = str;
205     while(*j != 0)
206     {
207         *i = *j++;
208         if(*i != ' ')
209             i++;
210     }
211     *i = 0;
212 
213     // This has problem process numbers in scientific notations e.g. 1e-06
214     /*neg = strchr(str, '-');
215     if (neg)
216         *neg = ' ';
217     */
218     if (str[0] == '-')
219     {
220         isNegative = 1;
221         str[0] = ' ';
222     }
223 
224     r = sscanf(str, "%lf%*[^0-9]%lf%*[^0-9]%lf", &a, &b, &c);
225 
226     indi_locale_C_numeric_pop(orig);
227 
228     if (r < 1)
229         return (-1);
230     *dp = a + b / 60 + c / 3600;
231     if (isNegative)
232         *dp *= -1;
233     return (0);
234 }
235 
getSexComponents(double value,int * d,int * m,int * s)236 void getSexComponents(double value, int *d, int *m, int *s)
237 {
238     *d = (int32_t)fabs(value);
239     *m = (int32_t)((fabs(value) - *d) * 60.0);
240     *s = (int32_t)rint(((fabs(value) - *d) * 60.0 - *m) * 60.0);
241 
242     // Special case if seconds are >= 59.5 so it will be rounded by rint above
243     // to 60
244     if (*s == 60)
245     {
246         *s  = 0;
247         *m += 1;
248     }
249     if (*m == 60)
250     {
251         *m  = 0;
252         *d += 1;
253     }
254 
255     if (value < 0)
256         *d *= -1;
257 }
258 
getSexComponentsIID(double value,int * d,int * m,double * s)259 void getSexComponentsIID(double value, int *d, int *m, double *s)
260 {
261     *d = (int32_t)fabs(value);
262     *m = (int32_t)((fabs(value) - *d) * 60.0);
263     *s = (double)(((fabs(value) - *d) * 60.0 - *m) * 60.0);
264 
265     if (value < 0)
266         *d *= -1;
267 }
268 
269 /* fill buf with properly formatted INumber string. return length */
numberFormat(char * buf,const char * format,double value)270 int numberFormat(char *buf, const char *format, double value)
271 {
272     int w, f, s;
273     char m;
274 
275     if (sscanf(format, "%%%d.%d%c", &w, &f, &m) == 3 && m == 'm')
276     {
277         /* INDI sexi format */
278         switch (f)
279         {
280         case 9:
281             s = 360000;
282             break;
283         case 8:
284             s = 36000;
285             break;
286         case 6:
287             s = 3600;
288             break;
289         case 5:
290             s = 600;
291             break;
292         default:
293             s = 60;
294             break;
295         }
296         return (fs_sexa(buf, value, w - f, s));
297     }
298     else
299     {
300         /* normal printf format */
301         return (snprintf(buf, MAXINDIFORMAT, format, value));
302     }
303 }
304 
305 /* log message locally.
306  * this has nothing to do with XML or any Clients.
307  */
IDLog(const char * fmt,...)308 void IDLog(const char *fmt, ...)
309 {
310     va_list ap;
311     /* JM: Since all INDI's stderr are timestampped now, we don't need to time stamp ID Log */
312     /*fprintf (stderr, "%s ", timestamp());*/
313     va_start(ap, fmt);
314     vfprintf(stderr, fmt, ap);
315     va_end(ap);
316 }
317 
318 /* return current system time in message format */
timestamp()319 const char *timestamp()
320 {
321     static char ts[32];
322     struct tm *tp;
323     time_t t;
324 
325     time(&t);
326     tp = gmtime(&t);
327     strftime(ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
328     return (ts);
329 }
330 
tty_set_debug(int debug)331 void tty_set_debug(int debug)
332 {
333     tty_debug = debug;
334 }
335 
tty_set_gemini_udp_format(int enabled)336 void tty_set_gemini_udp_format(int enabled)
337 {
338     tty_gemini_udp_format = enabled;
339 }
340 
tty_set_generic_udp_format(int enabled)341 void tty_set_generic_udp_format(int enabled)
342 {
343     tty_generic_udp_format = enabled;
344 }
345 
tty_clr_trailing_read_lf(int enabled)346 void tty_clr_trailing_read_lf(int enabled)
347 {
348     tty_clear_trailing_lf = enabled;
349 }
350 
tty_timeout(int fd,int timeout)351 int tty_timeout(int fd, int timeout)
352 {
353 #if defined(_WIN32) || defined(ANDROID)
354     INDI_UNUSED(fd);
355     INDI_UNUSED(timeout);
356     return TTY_ERRNO;
357 #else
358 
359     if (fd == -1)
360         return TTY_ERRNO;
361 
362     struct timeval tv;
363     fd_set readout;
364     int retval;
365 
366     FD_ZERO(&readout);
367     FD_SET(fd, &readout);
368 
369     /* wait for 'timeout' seconds */
370     tv.tv_sec  = timeout;
371     tv.tv_usec = 0;
372 
373     /* Wait till we have a change in the fd status */
374     retval = select(fd + 1, &readout, NULL, NULL, &tv);
375 
376     /* Return 0 on successful fd change */
377     if (retval > 0)
378         return TTY_OK;
379     /* Return -1 due to an error */
380     else if (retval == -1)
381         return TTY_SELECT_ERROR;
382     /* Return -2 if time expires before anything interesting happens */
383     else
384         return TTY_TIME_OUT;
385 
386 #endif
387 }
388 
tty_write(int fd,const char * buf,int nbytes,int * nbytes_written)389 int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
390 {
391 #ifdef _WIN32
392     return TTY_ERRNO;
393 #else
394     int geminiBuffer[66]={0};
395     char *buffer = (char *)buf;
396 
397     if (tty_gemini_udp_format)
398     {
399         buffer = (char*)geminiBuffer;
400         geminiBuffer[0] = ++tty_sequence_number;
401         geminiBuffer[1] = 0;
402         memcpy((char *)&geminiBuffer[2], buf, nbytes);
403         // Add on the 8 bytes for the header and 1 byte for the null terminator
404         nbytes += 9;
405     }
406 
407     if (fd == -1)
408         return TTY_ERRNO;
409 
410     int bytes_w     = 0;
411     *nbytes_written = 0;
412 
413     if (tty_debug)
414     {
415         int i = 0;
416         for (i = 0; i < nbytes; i++)
417             IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char)buf[i], buf[i]);
418     }
419 
420     while (nbytes > 0)
421     {
422         bytes_w = write(fd, buffer + (*nbytes_written), nbytes);
423 
424         if (bytes_w < 0)
425             return TTY_WRITE_ERROR;
426 
427         *nbytes_written += bytes_w;
428         nbytes -= bytes_w;
429     }
430 
431     if (tty_gemini_udp_format)
432         *nbytes_written -= 9;
433 
434     return TTY_OK;
435 
436 #endif
437 }
438 
tty_write_string(int fd,const char * buf,int * nbytes_written)439 int tty_write_string(int fd, const char *buf, int *nbytes_written)
440 {
441     unsigned int nbytes;
442     nbytes = strlen(buf);
443 
444     return tty_write(fd, buf, nbytes, nbytes_written);
445 }
446 
tty_read(int fd,char * buf,int nbytes,int timeout,int * nbytes_read)447 int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
448 {
449 #ifdef _WIN32
450     return TTY_ERRNO;
451 #else
452 
453     if (fd == -1)
454         return TTY_ERRNO;
455 
456     int numBytesToRead =  nbytes;
457     int bytesRead = 0;
458     int err       = 0;
459     *nbytes_read  = 0;
460 
461     if (nbytes <= 0)
462         return TTY_PARAM_ERROR;
463 
464     if (tty_debug)
465         IDLog("%s: Request to read %d bytes with %d timeout for fd %d\n", __FUNCTION__, nbytes, timeout, fd);
466 
467     char geminiBuffer[257]={0};
468     char* buffer = buf;
469 
470     if (tty_gemini_udp_format)
471     {
472         numBytesToRead = nbytes + 8;
473         buffer = geminiBuffer;
474     }
475 
476     while (numBytesToRead > 0)
477     {
478         if ((err = tty_timeout(fd, timeout)))
479             return err;
480 
481         bytesRead = read(fd, buffer + (*nbytes_read), ((uint32_t)numBytesToRead));
482 
483         if (bytesRead < 0)
484             return TTY_READ_ERROR;
485 
486         if (tty_debug)
487         {
488             IDLog("%d bytes read and %d bytes remaining...\n", bytesRead, numBytesToRead - bytesRead);
489             int i = 0;
490             for (i = *nbytes_read; i < (*nbytes_read + bytesRead); i++)
491                 IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, i, (unsigned char)buf[i], buf[i]);
492         }
493 
494         if (*nbytes_read == 0 && tty_clear_trailing_lf && *buffer == 0x0A)
495         {
496             if (tty_debug)
497                 IDLog("%s: Cleared LF char left in buf\n", __FUNCTION__);
498 
499             memcpy(buffer, buffer+1,bytesRead);
500             --bytesRead;
501         }
502 
503         *nbytes_read += bytesRead;
504         numBytesToRead -= bytesRead;
505     }
506 
507 
508     if (tty_gemini_udp_format)
509     {
510         int *intSizedBuffer = (int *)geminiBuffer;
511         if (intSizedBuffer[0] != tty_sequence_number)
512         {
513             // Not the right reply just do the read again.
514             return tty_read(fd, buf, nbytes, timeout, nbytes_read);
515         }
516 
517         *nbytes_read -= 8;
518         memcpy(buf, geminiBuffer+8, *nbytes_read);
519     }
520 
521     return TTY_OK;
522 
523 #endif
524 }
525 
tty_read_section(int fd,char * buf,char stop_char,int timeout,int * nbytes_read)526 int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
527 {
528 #ifdef _WIN32
529     return TTY_ERRNO;
530 #else
531 
532     char readBuffer[257]={0};
533 
534     if (fd == -1)
535         return TTY_ERRNO;
536 
537     int bytesRead = 0;
538     int err       = TTY_OK;
539     *nbytes_read  = 0;
540 
541     uint8_t *read_char = 0;
542 
543     if (tty_debug)
544         IDLog("%s: Request to read until stop char '%#02X' with %d timeout for fd %d\n", __FUNCTION__, stop_char, timeout, fd);
545 
546     if (tty_gemini_udp_format)
547     {
548         bytesRead = read(fd, readBuffer, 255);
549 
550         if (bytesRead < 0)
551             return TTY_READ_ERROR;
552 
553         int *intSizedBuffer = (int *)readBuffer;
554         if (intSizedBuffer[0] != tty_sequence_number)
555         {
556             // Not the right reply just do the read again.
557             return tty_read_section(fd, buf, stop_char, timeout, nbytes_read);
558         }
559 
560         for (int index = 8; index < bytesRead; index++)
561         {
562             (*nbytes_read)++;
563 
564             if (*(readBuffer+index) == stop_char)
565             {
566                 strncpy(buf, readBuffer+8, *nbytes_read);
567                 return TTY_OK;
568             }
569         }
570     }
571     else if (tty_generic_udp_format)
572     {
573         bytesRead = read(fd, readBuffer, 255);
574         if (bytesRead < 0)
575             return TTY_READ_ERROR;
576         for (int index = 0; index < bytesRead; index++)
577         {
578             (*nbytes_read)++;
579 
580             if (*(readBuffer+index) == stop_char)
581             {
582                 strncpy(buf, readBuffer, *nbytes_read);
583                 return TTY_OK;
584             }
585         }
586     }
587     else
588     {
589         for (;;)
590         {
591             if ((err = tty_timeout(fd, timeout)))
592                 return err;
593 
594             read_char = (uint8_t*)(buf + *nbytes_read);
595             bytesRead = read(fd, read_char, 1);
596 
597             if (bytesRead < 0)
598                 return TTY_READ_ERROR;
599 
600             if (tty_debug)
601                 IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, (*nbytes_read), *read_char, *read_char);
602 
603             if (!(tty_clear_trailing_lf && *read_char == 0X0A && *nbytes_read == 0))
604                 (*nbytes_read)++;
605             else {
606                 if (tty_debug)
607                     IDLog("%s: Cleared LF char left in buf\n", __FUNCTION__);
608             }
609 
610             if (*read_char == stop_char) {
611                 return TTY_OK;
612             }
613         }
614     }
615 
616     return TTY_TIME_OUT;
617 
618 #endif
619 }
620 
tty_nread_section(int fd,char * buf,int nsize,char stop_char,int timeout,int * nbytes_read)621 int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
622 {
623 #ifdef _WIN32
624     return TTY_ERRNO;
625 #else
626 
627     if (fd == -1)
628         return TTY_ERRNO;
629 
630     // For Gemini
631     if (tty_gemini_udp_format || tty_generic_udp_format)
632         return tty_read_section(fd, buf, stop_char, timeout, nbytes_read);
633 
634     int bytesRead = 0;
635     int err       = TTY_OK;
636     *nbytes_read  = 0;
637     uint8_t *read_char = 0;
638     memset(buf, 0, nsize);
639 
640     if (tty_debug)
641         IDLog("%s: Request to read until stop char '%#02X' with %d timeout for fd %d\n", __FUNCTION__, stop_char, timeout, fd);
642 
643     for (;;)
644     {
645         if ((err = tty_timeout(fd, timeout)))
646             return err;
647 
648         read_char = (uint8_t*)(buf + *nbytes_read);
649         bytesRead = read(fd, read_char, 1);
650 
651         if (bytesRead < 0)
652             return TTY_READ_ERROR;
653 
654         if (tty_debug)
655             IDLog("%s: buffer[%d]=%#X (%c)\n", __FUNCTION__, (*nbytes_read), *read_char, *read_char);
656 
657         if (!(tty_clear_trailing_lf && *read_char == 0X0A && *nbytes_read == 0))
658             (*nbytes_read)++;
659         else {
660             if (tty_debug)
661                 IDLog("%s: Cleared LF char left in buf\n", __FUNCTION__);
662         }
663 
664         if (*read_char == stop_char)
665             return TTY_OK;
666         else if (*nbytes_read >= nsize)
667             return TTY_OVERFLOW;
668     }
669 
670 #endif
671 }
672 
673 #if defined(BSD) && !defined(__GNU__)
674 // BSD - OSX version
tty_connect(const char * device,int bit_rate,int word_size,int parity,int stop_bits,int * fd)675 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
676 {
677     int t_fd = -1;
678     int bps;
679     char msg[80];
680     int handshake;
681     struct termios tty_setting;
682 
683     // Open the serial port read/write, with no controlling terminal, and don't wait for a connection.
684     // The O_NONBLOCK flag also causes subsequent I/O on the device to be non-blocking.
685     // See open(2) ("man 2 open") for details.
686 
687     t_fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
688     if (t_fd == -1)
689     {
690         IDLog("Error opening serial port (%s) - %s(%d).\n", device, strerror(errno), errno);
691         goto error;
692     }
693 
694     // Note that open() follows POSIX semantics: multiple open() calls to the same file will succeed
695     // unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned
696     // processes.
697     // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
698 
699     if (ioctl(t_fd, TIOCEXCL) == -1)
700     {
701         IDLog("Error setting TIOCEXCL on %s - %s(%d).\n", device, strerror(errno), errno);
702         goto error;
703     }
704 
705     // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block.
706     // See fcntl(2) ("man 2 fcntl") for details.
707 
708     if (fcntl(t_fd, F_SETFL, 0) == -1)
709     {
710         IDLog("Error clearing O_NONBLOCK %s - %s(%d).\n", device, strerror(errno), errno);
711         goto error;
712     }
713 
714     // Get the current options and save them so we can restore the default settings later.
715     if (tcgetattr(t_fd, &tty_setting) == -1)
716     {
717         IDLog("Error getting tty attributes %s - %s(%d).\n", device, strerror(errno), errno);
718         goto error;
719     }
720 
721     // Set raw input (non-canonical) mode, with reads blocking until either a single character
722     // has been received or a one second timeout expires.
723     // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") for details.
724 
725     cfmakeraw(&tty_setting);
726     tty_setting.c_cc[VMIN]  = 1;
727     tty_setting.c_cc[VTIME] = 10;
728 
729     // The baud rate, word length, and handshake options can be set as follows:
730     switch (bit_rate)
731     {
732     case 0:
733         bps = B0;
734         break;
735     case 50:
736         bps = B50;
737         break;
738     case 75:
739         bps = B75;
740         break;
741     case 110:
742         bps = B110;
743         break;
744     case 134:
745         bps = B134;
746         break;
747     case 150:
748         bps = B150;
749         break;
750     case 200:
751         bps = B200;
752         break;
753     case 300:
754         bps = B300;
755         break;
756     case 600:
757         bps = B600;
758         break;
759     case 1200:
760         bps = B1200;
761         break;
762     case 1800:
763         bps = B1800;
764         break;
765     case 2400:
766         bps = B2400;
767         break;
768     case 4800:
769         bps = B4800;
770         break;
771     case 9600:
772         bps = B9600;
773         break;
774     case 19200:
775         bps = B19200;
776         break;
777     case 38400:
778         bps = B38400;
779         break;
780     case 57600:
781         bps = B57600;
782         break;
783     case 115200:
784         bps = B115200;
785         break;
786     case 230400:
787         bps = B230400;
788         break;
789     default:
790         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
791             perror(NULL);
792         else
793             perror(msg);
794         return TTY_PARAM_ERROR;
795     }
796 
797     cfsetspeed(&tty_setting, bps); // Set baud rate
798     /* word size */
799     switch (word_size)
800     {
801     case 5:
802         tty_setting.c_cflag |= CS5;
803         break;
804     case 6:
805         tty_setting.c_cflag |= CS6;
806         break;
807     case 7:
808         tty_setting.c_cflag |= CS7;
809         break;
810     case 8:
811         tty_setting.c_cflag |= CS8;
812         break;
813     default:
814         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
815             perror(NULL);
816         else
817             perror(msg);
818 
819         return TTY_PARAM_ERROR;
820     }
821 
822     /* parity */
823     switch (parity)
824     {
825     case PARITY_NONE:
826         break;
827     case PARITY_EVEN:
828         tty_setting.c_cflag |= PARENB;
829         break;
830     case PARITY_ODD:
831         tty_setting.c_cflag |= PARENB | PARODD;
832         break;
833     default:
834         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
835             perror(NULL);
836         else
837             perror(msg);
838 
839         return TTY_PARAM_ERROR;
840     }
841 
842     /* stop_bits */
843     switch (stop_bits)
844     {
845     case 1:
846         break;
847     case 2:
848         tty_setting.c_cflag |= CSTOPB;
849         break;
850     default:
851         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
852             perror(NULL);
853         else
854             perror(msg);
855 
856         return TTY_PARAM_ERROR;
857     }
858 
859 #if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
860     // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
861     // other than those specified by POSIX. The driver for the underlying serial hardware
862     // ultimately determines which baud rates can be used. This ioctl sets both the input
863     // and output speed.
864 
865     speed_t speed = 14400; // Set 14400 baud
866     if (ioctl(t_fd, IOSSIOSPEED, &speed) == -1)
867     {
868         IDLog("Error calling ioctl(..., IOSSIOSPEED, ...) - %s(%d).\n", strerror(errno), errno);
869     }
870 #endif
871 
872     // Cause the new options to take effect immediately.
873     if (tcsetattr(t_fd, TCSANOW, &tty_setting) == -1)
874     {
875         IDLog("Error setting tty attributes %s - %s(%d).\n", device, strerror(errno), errno);
876         goto error;
877     }
878 
879     // To set the modem handshake lines, use the following ioctls.
880     // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
881 
882     if (ioctl(t_fd, TIOCSDTR) == -1) // Assert Data Terminal Ready (DTR)
883     {
884         IDLog("Error asserting DTR %s - %s(%d).\n", device, strerror(errno), errno);
885     }
886 
887     if (ioctl(t_fd, TIOCCDTR) == -1) // Clear Data Terminal Ready (DTR)
888     {
889         IDLog("Error clearing DTR %s - %s(%d).\n", device, strerror(errno), errno);
890     }
891 
892     handshake = TIOCM_DTR | TIOCM_RTS | TIOCM_CTS | TIOCM_DSR;
893     if (ioctl(t_fd, TIOCMSET, &handshake) == -1)
894         // Set the modem lines depending on the bits set in handshake
895     {
896         IDLog("Error setting handshake lines %s - %s(%d).\n", device, strerror(errno), errno);
897     }
898 
899     // To read the state of the modem lines, use the following ioctl.
900     // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
901 
902     if (ioctl(t_fd, TIOCMGET, &handshake) == -1)
903         // Store the state of the modem lines in handshake
904     {
905         IDLog("Error getting handshake lines %s - %s(%d).\n", device, strerror(errno), errno);
906     }
907 
908     IDLog("Handshake lines currently set to %d\n", handshake);
909 
910 #if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
911     unsigned long mics = 1UL;
912 
913     // Set the receive latency in microseconds. Serial drivers use this value to determine how often to
914     // dequeue characters received by the hardware. Most applications don't need to set this value: if an
915     // app reads lines of characters, the app can't do anything until the line termination character has been
916     // received anyway. The most common applications which are sensitive to read latency are MIDI and IrDA
917     // applications.
918 
919     if (ioctl(t_fd, IOSSDATALAT, &mics) == -1)
920     {
921         // set latency to 1 microsecond
922         IDLog("Error setting read latency %s - %s(%d).\n", device, strerror(errno), errno);
923         goto error;
924     }
925 #endif
926 
927     *fd = t_fd;
928     /* return success */
929     return TTY_OK;
930 
931     // Failure path
932 error:
933     if (t_fd != -1)
934     {
935         close(t_fd);
936         *fd = -1;
937     }
938 
939     return TTY_PORT_FAILURE;
940 }
941 #else
tty_connect(const char * device,int bit_rate,int word_size,int parity,int stop_bits,int * fd)942 int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
943 {
944 #ifdef _WIN32
945     return TTY_PORT_FAILURE;
946 
947 #else
948     int t_fd = -1;
949     int i = 0;
950     char msg[128]={0};
951     int bps;
952     struct termios tty_setting;
953     // Check for bluetooth
954     int bt = strstr(device, "rfcomm") || strstr(device, "Bluetooth");
955 
956     // Open as Read/Write, no fnctl, and close on exclusive
957     for (i = 0 ; i < 3 ; i++)
958     {
959         // Do not use O_CLOEXEC on bluetooth
960         t_fd = open(device, O_RDWR | O_NOCTTY | (bt ? 0 : O_CLOEXEC));
961         if (t_fd > 0)
962             break;
963         else
964         {
965             *fd = -1;
966             if (errno == EBUSY)
967             {
968                 usleep(1e6);
969                 continue;
970             }
971             else
972                 return TTY_PORT_FAILURE;
973         }
974     }
975 
976     if (t_fd == -1)
977         return TTY_PORT_BUSY;
978 
979     // Set port in exclusive mode to prevent other non-root processes from opening it.
980     // JM 2019-08-12: Do not set it for bluetooth
981     if (bt == 0 && ioctl(t_fd, TIOCEXCL) == -1)
982     {
983         perror("tty_connect: Error setting TIOCEXC.");
984         close(t_fd);
985         return TTY_PORT_FAILURE;
986     }
987 
988     // Get the current options and save them so we can restore the default settings later.
989     if (tcgetattr(t_fd, &tty_setting) == -1)
990     {
991         perror("tty_connect: failed getting tty attributes.");
992         close(t_fd);
993         return TTY_PORT_FAILURE;
994     }
995 
996     /* Control Modes
997     Set bps rate */
998     switch (bit_rate)
999     {
1000     case 0:
1001         bps = B0;
1002         break;
1003     case 50:
1004         bps = B50;
1005         break;
1006     case 75:
1007         bps = B75;
1008         break;
1009     case 110:
1010         bps = B110;
1011         break;
1012     case 134:
1013         bps = B134;
1014         break;
1015     case 150:
1016         bps = B150;
1017         break;
1018     case 200:
1019         bps = B200;
1020         break;
1021     case 300:
1022         bps = B300;
1023         break;
1024     case 600:
1025         bps = B600;
1026         break;
1027     case 1200:
1028         bps = B1200;
1029         break;
1030     case 1800:
1031         bps = B1800;
1032         break;
1033     case 2400:
1034         bps = B2400;
1035         break;
1036     case 4800:
1037         bps = B4800;
1038         break;
1039     case 9600:
1040         bps = B9600;
1041         break;
1042     case 19200:
1043         bps = B19200;
1044         break;
1045     case 38400:
1046         bps = B38400;
1047         break;
1048     case 57600:
1049         bps = B57600;
1050         break;
1051     case 115200:
1052         bps = B115200;
1053         break;
1054     case 230400:
1055         bps = B230400;
1056         break;
1057     default:
1058         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid bit rate.", bit_rate) < 0)
1059             perror(NULL);
1060         else
1061             perror(msg);
1062         close(t_fd);
1063         return TTY_PARAM_ERROR;
1064     }
1065     if ((cfsetispeed(&tty_setting, bps) < 0) || (cfsetospeed(&tty_setting, bps) < 0))
1066     {
1067         perror("tty_connect: failed setting bit rate.");
1068         close(t_fd);
1069         return TTY_PORT_FAILURE;
1070     }
1071 
1072     /* Control Modes
1073     set no flow control word size, parity and stop bits.
1074     Also don't hangup automatically and ignore modem status.
1075     Finally enable receiving characters. */
1076     tty_setting.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | HUPCL | CRTSCTS);
1077     tty_setting.c_cflag |= (CLOCAL | CREAD);
1078 
1079     /* word size */
1080     switch (word_size)
1081     {
1082     case 5:
1083         tty_setting.c_cflag |= CS5;
1084         break;
1085     case 6:
1086         tty_setting.c_cflag |= CS6;
1087         break;
1088     case 7:
1089         tty_setting.c_cflag |= CS7;
1090         break;
1091     case 8:
1092         tty_setting.c_cflag |= CS8;
1093         break;
1094     default:
1095 
1096         fprintf(stderr, "Default\n");
1097         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid data bit count.", word_size) < 0)
1098             perror(NULL);
1099         else
1100             perror(msg);
1101 
1102         close(t_fd);
1103         return TTY_PARAM_ERROR;
1104     }
1105 
1106     /* parity */
1107     switch (parity)
1108     {
1109     case PARITY_NONE:
1110         break;
1111     case PARITY_EVEN:
1112         tty_setting.c_cflag |= PARENB;
1113         break;
1114     case PARITY_ODD:
1115         tty_setting.c_cflag |= PARENB | PARODD;
1116         break;
1117     default:
1118 
1119         fprintf(stderr, "Default1\n");
1120         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid parity selection value.", parity) < 0)
1121             perror(NULL);
1122         else
1123             perror(msg);
1124 
1125         close(t_fd);
1126         return TTY_PARAM_ERROR;
1127     }
1128 
1129     /* stop_bits */
1130     switch (stop_bits)
1131     {
1132     case 1:
1133         break;
1134     case 2:
1135         tty_setting.c_cflag |= CSTOPB;
1136         break;
1137     default:
1138         fprintf(stderr, "Default2\n");
1139         if (snprintf(msg, sizeof(msg), "tty_connect: %d is not a valid stop bit count.", stop_bits) < 0)
1140             perror(NULL);
1141         else
1142             perror(msg);
1143 
1144         close(t_fd);
1145         return TTY_PARAM_ERROR;
1146     }
1147     /* Control Modes complete */
1148 
1149     /* Ignore bytes with parity errors and make terminal raw and dumb.*/
1150     tty_setting.c_iflag &= ~(PARMRK | ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON | IXANY);
1151     tty_setting.c_iflag |= INPCK | IGNPAR | IGNBRK;
1152 
1153     /* Raw output.*/
1154     tty_setting.c_oflag &= ~(OPOST | ONLCR);
1155 
1156     /* Local Modes
1157     Don't echo characters. Don't generate signals.
1158     Don't process any characters.*/
1159     tty_setting.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | IEXTEN | NOFLSH | TOSTOP);
1160     tty_setting.c_lflag |= NOFLSH;
1161 
1162     /* blocking read until 1 char arrives */
1163     tty_setting.c_cc[VMIN]  = 1;
1164     tty_setting.c_cc[VTIME] = 0;
1165 
1166     /* now clear input and output buffers and activate the new terminal settings */
1167     tcflush(t_fd, TCIOFLUSH);
1168     if (tcsetattr(t_fd, TCSANOW, &tty_setting))
1169     {
1170         perror("tty_connect: failed setting attributes on serial port.");
1171         tty_disconnect(t_fd);
1172         return TTY_PORT_FAILURE;
1173     }
1174 
1175     *fd = t_fd;
1176     /* return success */
1177     return TTY_OK;
1178 #endif
1179 }
1180 // Unix - Linux version
1181 
1182 #endif
1183 
tty_disconnect(int fd)1184 int tty_disconnect(int fd)
1185 {
1186     if (fd == -1)
1187         return TTY_ERRNO;
1188 
1189 #ifdef _WIN32
1190     return TTY_ERRNO;
1191 #else
1192     int err;
1193     tcflush(fd, TCIOFLUSH);
1194     err = close(fd);
1195 
1196     if (err != 0)
1197         return TTY_ERRNO;
1198 
1199     return TTY_OK;
1200 #endif
1201 }
1202 
tty_error_msg(int err_code,char * err_msg,int err_msg_len)1203 void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
1204 {
1205     char error_string[512];
1206 
1207     switch (err_code)
1208     {
1209     case TTY_OK:
1210         strncpy(err_msg, "No Error", err_msg_len);
1211         break;
1212 
1213     case TTY_READ_ERROR:
1214         snprintf(error_string, 512, "Read Error: %s", strerror(errno));
1215         strncpy(err_msg, error_string, err_msg_len);
1216         break;
1217 
1218     case TTY_WRITE_ERROR:
1219         snprintf(error_string, 512, "Write Error: %s", strerror(errno));
1220         strncpy(err_msg, error_string, err_msg_len);
1221         break;
1222 
1223     case TTY_SELECT_ERROR:
1224         snprintf(error_string, 512, "Select Error: %s", strerror(errno));
1225         strncpy(err_msg, error_string, err_msg_len);
1226         break;
1227 
1228     case TTY_TIME_OUT:
1229         strncpy(err_msg, "Timeout error", err_msg_len);
1230         break;
1231 
1232     case TTY_PORT_FAILURE:
1233         if (errno == EACCES)
1234             snprintf(error_string, 512,
1235                      "Port failure Error: %s. Try adding your user to the dialout group and restart (sudo adduser "
1236                      "$USER dialout)",
1237                      strerror(errno));
1238         else
1239             snprintf(error_string, 512, "Port failure Error: %s. Check if device is connected to this port.",
1240                      strerror(errno));
1241 
1242         strncpy(err_msg, error_string, err_msg_len);
1243         break;
1244 
1245     case TTY_PARAM_ERROR:
1246         strncpy(err_msg, "Parameter error", err_msg_len);
1247         break;
1248 
1249     case TTY_ERRNO:
1250         snprintf(error_string, 512, "%s", strerror(errno));
1251         strncpy(err_msg, error_string, err_msg_len);
1252         break;
1253 
1254     case TTY_OVERFLOW:
1255         strncpy(err_msg, "Read overflow", err_msg_len);
1256         break;
1257 
1258     case TTY_PORT_BUSY:
1259         strncpy(err_msg, "Port is busy", err_msg_len);
1260         break;
1261 
1262     default:
1263         strncpy(err_msg, "Error: unrecognized error code", err_msg_len);
1264         break;
1265     }
1266 }
1267 
1268 /* return static string corresponding to the given property or light state */
pstateStr(IPState s)1269 const char *pstateStr(IPState s)
1270 {
1271     switch (s)
1272     {
1273     case IPS_IDLE:
1274         return ("Idle");
1275     case IPS_OK:
1276         return ("Ok");
1277     case IPS_BUSY:
1278         return ("Busy");
1279     case IPS_ALERT:
1280         return ("Alert");
1281     default:
1282         fprintf(stderr, "Impossible IPState %d\n", s);
1283         return NULL;
1284     }
1285 }
1286 
1287 /* crack string into IPState.
1288  * return 0 if ok, else -1
1289  */
crackIPState(const char * str,IPState * ip)1290 int crackIPState(const char *str, IPState *ip)
1291 {
1292     if (!strcmp(str, "Idle"))
1293         *ip = IPS_IDLE;
1294     else if (!strncmp(str, "Ok", 2))
1295         *ip = IPS_OK;
1296     else if (!strcmp(str, "Busy"))
1297         *ip = IPS_BUSY;
1298     else if (!strcmp(str, "Alert"))
1299         *ip = IPS_ALERT;
1300     else
1301         return (-1);
1302     return (0);
1303 }
1304 
1305 /* crack string into ISState.
1306  * return 0 if ok, else -1
1307  */
crackISState(const char * str,ISState * ip)1308 int crackISState(const char *str, ISState *ip)
1309 {
1310     if (!strncmp(str, "On", 2))
1311         *ip = ISS_ON;
1312     else if (!strcmp(str, "Off"))
1313         *ip = ISS_OFF;
1314     else
1315         return (-1);
1316     return (0);
1317 }
1318 
crackIPerm(const char * str,IPerm * ip)1319 int crackIPerm(const char *str, IPerm *ip)
1320 {
1321     if (!strncmp(str, "rw", 2))
1322         *ip = IP_RW;
1323     else if (!strncmp(str, "ro", 2))
1324         *ip = IP_RO;
1325     else if (!strncmp(str, "wo", 2))
1326         *ip = IP_WO;
1327     else
1328         return (-1);
1329     return (0);
1330 }
1331 
crackISRule(const char * str,ISRule * ip)1332 int crackISRule(const char *str, ISRule *ip)
1333 {
1334     if (!strcmp(str, "OneOfMany"))
1335         *ip = ISR_1OFMANY;
1336     else if (!strcmp(str, "AtMostOne"))
1337         *ip = ISR_ATMOST1;
1338     else if (!strcmp(str, "AnyOfMany"))
1339         *ip = ISR_NOFMANY;
1340     else
1341         return (-1);
1342     return (0);
1343 }
1344 
1345 /* return static string corresponding to the given switch state */
sstateStr(ISState s)1346 const char *sstateStr(ISState s)
1347 {
1348     switch (s)
1349     {
1350     case ISS_ON:
1351         return ("On");
1352     case ISS_OFF:
1353         return ("Off");
1354     default:
1355         fprintf(stderr, "Impossible ISState %d\n", s);
1356         return NULL;
1357     }
1358 }
1359 
1360 /* return static string corresponding to the given Rule */
ruleStr(ISRule r)1361 const char *ruleStr(ISRule r)
1362 {
1363     switch (r)
1364     {
1365     case ISR_1OFMANY:
1366         return ("OneOfMany");
1367     case ISR_ATMOST1:
1368         return ("AtMostOne");
1369     case ISR_NOFMANY:
1370         return ("AnyOfMany");
1371     default:
1372         fprintf(stderr, "Impossible ISRule %d\n", r);
1373         return NULL;
1374     }
1375 }
1376 
1377 /* return static string corresponding to the given IPerm */
permStr(IPerm p)1378 const char *permStr(IPerm p)
1379 {
1380     switch (p)
1381     {
1382     case IP_RO:
1383         return ("ro");
1384     case IP_WO:
1385         return ("wo");
1386     case IP_RW:
1387         return ("rw");
1388     default:
1389         fprintf(stderr, "Impossible IPerm %d\n", p);
1390         return NULL;
1391     }
1392 }
1393 
1394 /* print the boilerplate comment introducing xml */
xmlv1()1395 void xmlv1()
1396 {
1397     printf("<?xml version='1.0'?>\n");
1398 }
1399 
1400 /* pull out device and name attributes from root.
1401  * return 0 if ok else -1 with reason in msg[].
1402  */
crackDN(XMLEle * root,char ** dev,char ** name,char msg[])1403 int crackDN(XMLEle *root, char **dev, char **name, char msg[])
1404 {
1405     XMLAtt *ap;
1406 
1407     ap = findXMLAtt(root, "device");
1408     if (!ap)
1409     {
1410         sprintf(msg, "%s requires 'device' attribute", tagXMLEle(root));
1411         return (-1);
1412     }
1413     *dev = valuXMLAtt(ap);
1414 
1415     ap = findXMLAtt(root, "name");
1416     if (!ap)
1417     {
1418         sprintf(msg, "%s requires 'name' attribute", tagXMLEle(root));
1419         return (-1);
1420     }
1421     *name = valuXMLAtt(ap);
1422 
1423     return (0);
1424 }
1425 
1426 /* find a member of an IText vector, else NULL */
IUFindText(const ITextVectorProperty * tvp,const char * name)1427 IText *IUFindText(const ITextVectorProperty *tvp, const char *name)
1428 {
1429     int i;
1430 
1431     for (i = 0; i < tvp->ntp; i++)
1432         if (strcmp(tvp->tp[i].name, name) == 0)
1433             return (&tvp->tp[i]);
1434     fprintf(stderr, "No IText '%s' in %s.%s\n", name, tvp->device, tvp->name);
1435     return (NULL);
1436 }
1437 
1438 /* find a member of an INumber vector, else NULL */
IUFindNumber(const INumberVectorProperty * nvp,const char * name)1439 INumber *IUFindNumber(const INumberVectorProperty *nvp, const char *name)
1440 {
1441     int i;
1442 
1443     for (i = 0; i < nvp->nnp; i++)
1444         if (strcmp(nvp->np[i].name, name) == 0)
1445             return (&nvp->np[i]);
1446     fprintf(stderr, "No INumber '%s' in %s.%s\n", name, nvp->device, nvp->name);
1447     return (NULL);
1448 }
1449 
1450 /* find a member of an ISwitch vector, else NULL */
IUFindSwitch(const ISwitchVectorProperty * svp,const char * name)1451 ISwitch *IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
1452 {
1453     int i;
1454 
1455     for (i = 0; i < svp->nsp; i++)
1456         if (strcmp(svp->sp[i].name, name) == 0)
1457             return (&svp->sp[i]);
1458     fprintf(stderr, "No ISwitch '%s' in %s.%s\n", name, svp->device, svp->name);
1459     return (NULL);
1460 }
1461 
1462 /* find a member of an ILight vector, else NULL */
IUFindLight(const ILightVectorProperty * lvp,const char * name)1463 ILight *IUFindLight(const ILightVectorProperty *lvp, const char *name)
1464 {
1465     int i;
1466 
1467     for (i = 0; i < lvp->nlp; i++)
1468         if (strcmp(lvp->lp[i].name, name) == 0)
1469             return (&lvp->lp[i]);
1470     fprintf(stderr, "No ILight '%s' in %s.%s\n", name, lvp->device, lvp->name);
1471     return (NULL);
1472 }
1473 
1474 /* find a member of an IBLOB vector, else NULL */
IUFindBLOB(const IBLOBVectorProperty * bvp,const char * name)1475 IBLOB *IUFindBLOB(const IBLOBVectorProperty *bvp, const char *name)
1476 {
1477     int i;
1478 
1479     for (i = 0; i < bvp->nbp; i++)
1480         if (strcmp(bvp->bp[i].name, name) == 0)
1481             return (&bvp->bp[i]);
1482     fprintf(stderr, "No IBLOB '%s' in %s.%s\n", name, bvp->device, bvp->name);
1483     return (NULL);
1484 }
1485 
1486 /* find an ON member of an ISwitch vector, else NULL.
1487  * N.B. user must make sense of result with ISRule in mind.
1488  */
IUFindOnSwitch(const ISwitchVectorProperty * svp)1489 ISwitch *IUFindOnSwitch(const ISwitchVectorProperty *svp)
1490 {
1491     int i;
1492 
1493     for (i = 0; i < svp->nsp; i++)
1494         if (svp->sp[i].s == ISS_ON)
1495             return (&svp->sp[i]);
1496     /*fprintf(stderr, "No ISwitch On in %s.%s\n", svp->device, svp->name);*/
1497     return (NULL);
1498 }
1499 
1500 /* Find index of the ON member of an ISwitchVectorProperty */
IUFindOnSwitchIndex(const ISwitchVectorProperty * svp)1501 int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
1502 {
1503     int i;
1504 
1505     for (i = 0; i < svp->nsp; i++)
1506         if (svp->sp[i].s == ISS_ON)
1507             return i;
1508     return -1;
1509 }
1510 
1511 /* Find name the ON member in the given states and names */
IUFindOnSwitchName(ISState * states,char * names[],int n)1512 const char *IUFindOnSwitchName(ISState *states, char *names[], int n)
1513 {
1514     int i;
1515 
1516     for (i = 0; i < n; i++)
1517         if (states[i] == ISS_ON)
1518             return names[i];
1519     return NULL;
1520 }
1521 
1522 /* Set all switches to off */
IUResetSwitch(ISwitchVectorProperty * svp)1523 void IUResetSwitch(ISwitchVectorProperty *svp)
1524 {
1525     int i;
1526 
1527     for (i = 0; i < svp->nsp; i++)
1528         svp->sp[i].s = ISS_OFF;
1529 }
1530 
1531 /* save malloced copy of newtext in tp->text, reusing if not first time */
IUSaveText(IText * tp,const char * newtext)1532 void IUSaveText(IText *tp, const char *newtext)
1533 {
1534     /* seed for realloc */
1535     if (tp->text == NULL)
1536         tp->text = malloc(1);
1537 
1538     /* copy in fresh string */
1539     tp->text = strcpy(realloc(tp->text, strlen(newtext) + 1), newtext);
1540 }
1541 
rangeHA(double r)1542 double rangeHA(double r)
1543 {
1544     double res = r;
1545     while (res < -12.0)
1546         res += 24.0;
1547     while (res >= 12.0)
1548         res -= 24.0;
1549     return res;
1550 }
1551 
range24(double r)1552 double range24(double r)
1553 {
1554     double res = r;
1555     while (res < 0.0)
1556         res += 24.0;
1557     while (res > 24.0)
1558         res -= 24.0;
1559     return res;
1560 }
1561 
range360(double r)1562 double range360(double r)
1563 {
1564     double res = r;
1565     while (res < 0.0)
1566         res += 360.0;
1567     while (res > 360.0)
1568         res -= 360.0;
1569     return res;
1570 }
1571 
rangeDec(double decdegrees)1572 double rangeDec(double decdegrees)
1573 {
1574     if ((decdegrees >= 270.0) && (decdegrees <= 360.0))
1575         return (decdegrees - 360.0);
1576     if ((decdegrees >= 180.0) && (decdegrees < 270.0))
1577         return (180.0 - decdegrees);
1578     if ((decdegrees >= 90.0) && (decdegrees < 180.0))
1579         return (180.0 - decdegrees);
1580     return decdegrees;
1581 }
1582 
1583 #if defined(HAVE_LIBNOVA)
get_local_sidereal_time(double longitude)1584 double get_local_sidereal_time(double longitude)
1585 {
1586     double SD = ln_get_apparent_sidereal_time(ln_get_julian_from_sys()) - (360.0 - longitude) / 15.0;
1587 
1588     return range24(SD);
1589 }
1590 #endif // HAVE_LIBNOVA
1591 
get_local_hour_angle(double sideral_time,double ra)1592 double get_local_hour_angle(double sideral_time, double ra)
1593 {
1594     double HA = sideral_time - ra;
1595     return rangeHA(HA);
1596 }
1597 
get_alt_az_coordinates(double Ha,double Dec,double Lat,double * Alt,double * Az)1598 void get_alt_az_coordinates(double Ha, double Dec, double Lat, double* Alt, double *Az)
1599 {
1600     double alt, az;
1601     Ha *= M_PI / 180.0;
1602     Dec *= M_PI / 180.0;
1603     Lat *= M_PI / 180.0;
1604     alt = asin(sin(Dec) * sin(Lat) + cos(Dec) * cos(Lat) * cos(Ha));
1605     az = acos((sin(Dec) - sin(alt)*sin(Lat)) / (cos(alt) * cos(Lat)));
1606     alt *= 180.0 / M_PI;
1607     az *= 180.0 / M_PI;
1608     if (sin(Ha) >= 0.0)
1609         az = 360 - az;
1610     *Alt = alt;
1611     *Az = az;
1612 }
1613 
estimate_geocentric_elevation(double Lat,double El)1614 double estimate_geocentric_elevation(double Lat, double El)
1615 {
1616     Lat *= M_PI / 180.0;
1617     Lat = sin(Lat);
1618     El += Lat * (EARTHRADIUSPOLAR - EARTHRADIUSEQUATORIAL);
1619     return El;
1620 }
1621 
estimate_field_rotation_rate(double Alt,double Az,double Lat)1622 double estimate_field_rotation_rate(double Alt, double Az, double Lat)
1623 {
1624     Alt *= M_PI / 180.0;
1625     Az *= M_PI / 180.0;
1626     Lat *= M_PI / 180.0;
1627     double ret = cos(Lat) * cos(Az) / cos(Alt);
1628     ret *= 180.0 / M_PI;
1629     return ret;
1630 }
1631 
estimate_field_rotation(double HA,double rate)1632 double estimate_field_rotation(double HA, double rate)
1633 {
1634     HA *= rate;
1635     while(HA >= 360.0)
1636         HA -= 360.0;
1637     while(HA < 0)
1638         HA += 360.0;
1639     return HA;
1640 }
1641 
as2rad(double as)1642 double as2rad(double as)
1643 {
1644     return as * M_PI / (60.0*60.0*12.0);
1645 }
1646 
rad2as(double rad)1647 double rad2as(double rad)
1648 {
1649     return rad * (60.0*60.0*12.0) / M_PI;
1650 }
1651 
estimate_distance(double parsecs,double parallax_radius)1652 double estimate_distance(double parsecs, double parallax_radius)
1653 {
1654     double cat1 = parallax_radius * cos(as2rad(parsecs));
1655     double cat2 = parallax_radius * sin(as2rad(parsecs));
1656     return sqrt(pow(cat1, 2)+pow(cat2, 2));
1657 }
1658 
m2au(double m)1659 double m2au(double m)
1660 {
1661     return m / ASTRONOMICALUNIT;
1662 }
1663 
calc_delta_magnitude(double mag_ratio,double * spectrum,double * ref_spectrum,int spectrum_size)1664 double calc_delta_magnitude(double mag_ratio, double *spectrum, double *ref_spectrum, int spectrum_size)
1665 {
1666     double delta_mag = 0;
1667     for(int l = 0; l < spectrum_size; l++) {
1668         delta_mag += spectrum[l] * mag_ratio * ref_spectrum[l] / spectrum[l];
1669     }
1670     delta_mag /= spectrum_size;
1671     return delta_mag;
1672 }
1673 
calc_photon_flux(double rel_magnitude,double filter_bandwidth,double wavelength,double incident_surface)1674 double calc_photon_flux(double rel_magnitude, double filter_bandwidth, double wavelength, double incident_surface)
1675 {
1676     return LUMEN(wavelength)/(1.51E+7*(filter_bandwidth/wavelength)*incident_surface*pow(10, -0.4*rel_magnitude));
1677 }
1678 
calc_rel_magnitude(double photon_flux,double filter_bandwidth,double wavelength,double incident_surface)1679 double calc_rel_magnitude(double photon_flux, double filter_bandwidth, double wavelength, double incident_surface)
1680 {
1681     return (1.51E+7*(filter_bandwidth/wavelength)*incident_surface*log10(LUMEN(wavelength)/photon_flux))/-0.4;
1682 }
1683 
estimate_absolute_magnitude(double delta_dist,double delta_mag)1684 double estimate_absolute_magnitude(double delta_dist, double delta_mag)
1685 {
1686     return sqrt(delta_dist) * delta_mag;
1687 }
1688 
interferometry_uv_coords_vector(double baseline_m,double wavelength,double * target_vector)1689 double* interferometry_uv_coords_vector(double baseline_m, double wavelength, double *target_vector)
1690 {
1691     double* uv = (double*)malloc(sizeof(double) * 2);
1692     double* vector = (double*)malloc(sizeof(double) * 3);
1693     double hypo = sqrt(pow(target_vector[0], 2) * pow(target_vector[1], 2) * pow(target_vector[2], 2));
1694     vector[0] = target_vector[0] / hypo;
1695     vector[1] = target_vector[1] / hypo;
1696     vector[2] = target_vector[2] / hypo;
1697     uv[0] = baseline_m * target_vector[0] * target_vector[2];
1698     uv[1] = baseline_m * target_vector[1] * target_vector[2];
1699     uv[0] *= AIRY / wavelength;
1700     uv[1] *= AIRY / wavelength;
1701     return uv;
1702 }
1703 
interferometry_uv_coords_hadec(double ha,double dec,double * baseline,double wavelength)1704 double* interferometry_uv_coords_hadec(double ha, double dec, double *baseline, double wavelength)
1705 {
1706     double* uv = (double*)malloc(sizeof(double) * 2);
1707     ha *= M_PI / 12.0;
1708     dec *= M_PI / 180.0;
1709     uv[0] = (baseline[0] * sin(ha) + baseline[1] * cos(ha));
1710     uv[1] = (-baseline[0] * sin(dec) * cos(ha) + baseline[1] * sin(dec) * sin(ha) + baseline[2] * cos(dec));
1711     uv[0] *= AIRY / wavelength;
1712     uv[1] *= AIRY / wavelength;
1713     return uv;
1714 }
1715 
1716 #if defined(_MSC_VER)
1717 #undef snprintf
1718 #pragma warning(pop)
1719 #endif
1720