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