1 /*
2  * Telescope Control Protocol for Celestron NexStar GPS telescopes
3  *
4  * Copyright 2003 John Kielkopf
5  * John Kielkopf (kielkopf@louisville.edu)
6  *
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2.1 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  * 15 May  2003  -- Version 2.00
22  *
23  *
24  *
25  */
26 
27 #include "celestronprotocol.h"
28 
29 #ifndef _WIN32
30 #include <termios.h>
31 #endif
32 
33 #define NULL_PTR(x) (x *)0
34 
35 /* There are two classes of routines defined here:                    */
36 
37 /*   XmTel commands to allow easy NexStar access.  These              */
38 /*     include routines that mimic the extensive LX200 command        */
39 /*     language and, for the most part, trap calls and                */
40 /*     respond with an error message to the console.                  */
41 
42 /*   NexStar specific commands and data.                              */
43 
44 /*   The NexStar command set as documented by Celestron               */
45 /*     is very limited.  This version of xmtel uses ta few            */
46 /*     auxilliary commands which permit direct access to the motor    */
47 /*     controllers.                                                   */
48 
49 /* XmTel compatibility commands */
50 
51 int ConnectTel(char *port);
52 void DisconnectTel(void);
53 int CheckConnectTel(void);
54 
55 void SetRate(int newRate);
56 void SetLimits(double limitLower, double limitHigher);
57 double GetRA(void);
58 double GetDec(void);
59 int SlewToCoords(double newRA, double newDec);
60 int SyncToCoords(double newRA, double newDec);
61 int CheckCoords(double desRA, double desDec, double tolRA, double tolDEC);
62 
63 void StopNSEW(void);
64 int SetSlewRate(void);
65 
66 int SyncLST(double newTime);
67 int SyncLocalTime(void);
68 
69 void Reticle(int reticle);
70 void Focus(int focus);
71 void Derotator(int rotate);
72 void Fan(int fan);
73 
74 static int TelPortFD;
75 static int TelConnectFlag = 0;
76 
77 /* NexStar local data */
78 
79 static double returnRA;  /* Last update of RA */
80 static double returnDec; /* Last update of Dec */
81 static int updateRA;     /* Set if no RA  inquiry since last update */
82 static int updateDec;    /* Set if no Dec inquiry since last update */
83 static int slewRate;     /* Rate for slew request in StartSlew */
84 
85 /* Coordinate reported by NexStar  = true coordinate + offset. */
86 
87 static double offsetRA  = 0; /* Correction to RA from NexStar */
88 static double offsetDec = 0; /* Correction to Dec from NexStar */
89 
90 /* NexStar local commands */
91 
92 void GetRAandDec(void); /* Update RA and Dec from NexStar */
93 
94 /* Serial communication utilities */
95 
96 typedef fd_set telfds;
97 
98 static int readn(int fd, char *ptr, int nbytes, int sec);
99 static int writen(int fd, char *ptr, int nbytes);
100 static int telstat(int fd, int sec, int usec);
101 
CheckConnectTel(void)102 int CheckConnectTel(void)
103 {
104     int numRead;
105     char returnStr[128];
106 
107     //return TelConnectFlag;
108     tcflush(TelPortFD, TCIOFLUSH);
109 
110     /* Test connection */
111 
112     writen(TelPortFD, "Kx", 2);
113     numRead            = readn(TelPortFD, returnStr, 3, 2);
114     returnStr[numRead] = '\0';
115 
116     if (numRead == 2)
117     {
118         TelConnectFlag = 1;
119         return (0);
120     }
121     else
122         return -1;
123 }
124 
ConnectTel(char * port)125 int ConnectTel(char *port)
126 {
127 #ifdef _WIN32
128     return -1;
129 #else
130     struct termios tty;
131     char returnStr[128];
132     int numRead;
133 
134     fprintf(stderr, "Connecting to port: %s\n", port);
135 
136     if (TelConnectFlag != 0)
137         return 0;
138 
139     /* Make the connection */
140 
141     TelPortFD = open(port, O_RDWR);
142     if (TelPortFD == -1)
143         return -1;
144 
145     tcgetattr(TelPortFD, &tty);
146     cfsetospeed(&tty, (speed_t)B9600);
147     cfsetispeed(&tty, (speed_t)B9600);
148     tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
149     tty.c_iflag = IGNBRK;
150     tty.c_lflag = 0;
151     tty.c_oflag = 0;
152     tty.c_cflag |= CLOCAL | CREAD;
153     tty.c_cc[VMIN]  = 1;
154     tty.c_cc[VTIME] = 5;
155     tty.c_iflag &= ~(IXON | IXOFF | IXANY);
156     tty.c_cflag &= ~(PARENB | PARODD);
157     tcsetattr(TelPortFD, TCSANOW, &tty);
158 
159     /* Flush the input (read) buffer */
160 
161     tcflush(TelPortFD, TCIOFLUSH);
162 
163     /* Test connection */
164 
165     writen(TelPortFD, "Kx", 2);
166     numRead            = readn(TelPortFD, returnStr, 3, 2);
167     returnStr[numRead] = '\0';
168 
169     /* Diagnostic tests */
170     fprintf(stderr, "ConnectTel read %d characters: %s\n", numRead, returnStr);
171 
172     if (numRead == 2)
173     {
174         TelConnectFlag = 1;
175         return (0);
176     }
177     else
178         return -1;
179 
180 #endif
181 }
182 
183 /* Assign and save slewRate for use in StartSlew */
184 
SetRate(int newRate)185 void SetRate(int newRate)
186 {
187     if (newRate == SLEW)
188     {
189         slewRate = 9;
190     }
191     else if (newRate == FIND)
192     {
193         slewRate = 6;
194     }
195     else if (newRate == CENTER)
196     {
197         slewRate = 3;
198     }
199     else if (newRate == GUIDE)
200     {
201         slewRate = 1;
202     }
203 }
204 
205 /* Start a slew in chosen direction at slewRate */
206 /* Use auxilliary NexStar command set through the hand control computer */
207 
StartSlew(int direction)208 int StartSlew(int direction)
209 {
210     char slewCmd[] = { 0x50, 0x02, 0x11, 0x24, 0x09, 0x00, 0x00, 0x00 };
211     char inputStr[2048];
212 
213     if (direction == NORTH)
214     {
215         slewCmd[2] = 0x11;
216         slewCmd[3] = 0x24;
217         slewCmd[4] = slewRate;
218     }
219     else if (direction == EAST)
220     {
221         slewCmd[2] = 0x10;
222         slewCmd[3] = 0x25;
223         slewCmd[4] = slewRate;
224     }
225     else if (direction == SOUTH)
226     {
227         slewCmd[2] = 0x11;
228         slewCmd[3] = 0x25;
229         slewCmd[4] = slewRate;
230     }
231     else if (direction == WEST)
232     {
233         slewCmd[2] = 0x10;
234         slewCmd[3] = 0x24;
235         slewCmd[4] = slewRate;
236     }
237 
238     writen(TelPortFD, slewCmd, 8);
239 
240     /* Look for '#' acknowledgment of request*/
241 
242     for (;;)
243     {
244         if (readn(TelPortFD, inputStr, 1, 1))
245         {
246             if (inputStr[0] == '#')
247                 return 0;
248         }
249         else
250         {
251             fprintf(stderr, "No acknowledgment from telescope in StartSlew.\n");
252             return -1;
253         }
254     }
255 }
256 
257 /* Stop the slew in chosen direction */
258 
StopSlew(int direction)259 int StopSlew(int direction)
260 {
261     char slewCmd[] = { 0x50, 0x02, 0x11, 0x24, 0x00, 0x00, 0x00, 0x00 };
262     char inputStr[2048];
263 
264     if (direction == NORTH)
265     {
266         slewCmd[2] = 0x11;
267         slewCmd[3] = 0x24;
268     }
269     else if (direction == EAST)
270     {
271         slewCmd[2] = 0x10;
272         slewCmd[3] = 0x25;
273     }
274     else if (direction == SOUTH)
275     {
276         slewCmd[2] = 0x11;
277         slewCmd[3] = 0x25;
278     }
279     else if (direction == WEST)
280     {
281         slewCmd[2] = 0x10;
282         slewCmd[3] = 0x24;
283     }
284 
285     writen(TelPortFD, slewCmd, 8);
286 
287     /* Look for '#' acknowledgment of request*/
288 
289     for (;;)
290     {
291         if (readn(TelPortFD, inputStr, 1, 1))
292         {
293             if (inputStr[0] == '#')
294                 return 0;
295         }
296         else
297         {
298             fprintf(stderr, "No acknowledgment from telescope in StartSlew.\n");
299             return -1;
300         }
301     }
302 }
303 
DisconnectTel(void)304 void DisconnectTel(void)
305 {
306     /* printf("DisconnectTel\n"); */
307     if (TelConnectFlag == 1)
308         close(TelPortFD);
309     TelConnectFlag = 0;
310 }
311 
312 /* Test update status and return the telescope right ascension */
313 /* Set updateRA flag false */
314 /* Last telescope readout will be returned if no RA inquiry since then */
315 /* Otherwise force a new readout */
316 /* Two successive calls to GetRA will always force a new readout */
317 
GetRA(void)318 double GetRA(void)
319 {
320     if (updateRA != 1)
321         GetRAandDec();
322     updateRA = 0;
323     return returnRA;
324 }
325 
326 /* Test update status and return the telescope declination */
327 /* Set updateDec flag false */
328 /* Last telescope readout will returned if no Dec inquiry since then */
329 /* Otherwise force a new readout */
330 /* Two successive calls to GetDec will always force a new readout */
331 
GetDec(void)332 double GetDec(void)
333 {
334     if (updateDec != 1)
335         GetRAandDec();
336     updateDec = 0;
337     return returnDec;
338 }
339 
340 /* Read the telescope right ascension and declination and set update status */
341 
isScopeSlewing()342 int isScopeSlewing()
343 {
344     int numRead;
345     char returnStr[128];
346 
347     writen(TelPortFD, "L", 1);
348     numRead            = readn(TelPortFD, returnStr, 2, 2);
349     returnStr[numRead] = '\0';
350 
351     // 0 Slew complete
352     // 1 Slew in progress
353     return (returnStr[0] == '0' ? 0 : 1);
354 }
355 
GetRAandDec(void)356 void GetRAandDec(void)
357 {
358     char returnStr[12];
359     int countRA, countDec;
360     int numRead;
361 
362     writen(TelPortFD, "E", 1);
363     numRead      = readn(TelPortFD, returnStr, 10, 1);
364     returnStr[4] = returnStr[9] = '\0';
365 
366     /* Diagnostic
367  *
368  * printf("GetRAandDec: %d read %x\n",numRead,returnStr);
369  *
370  */
371 
372     sscanf(returnStr, "%x", &countRA);
373     sscanf(returnStr + 5, "%x:", &countDec);
374     returnRA  = (double)countRA;
375     returnRA  = returnRA / (3. * 15. * 60. * 65536. / 64800.);
376     returnDec = (double)countDec;
377     returnDec = returnDec / (3. * 60. * 65536. / 64800.);
378 
379     /* Account for the quadrant in declination */
380 
381     /* 90 to 180 */
382 
383     if ((returnDec > 90.) && (returnDec <= 180.))
384     {
385         returnDec = 180. - returnDec;
386     }
387 
388     /* 180 to 270 */
389 
390     if ((returnDec > 180.) && (returnDec <= 270.))
391     {
392         returnDec = returnDec - 270.;
393     }
394 
395     /* 270 to 360 */
396 
397     if ((returnDec > 270.) && (returnDec <= 360.))
398     {
399         returnDec = returnDec - 360.;
400     }
401 
402     /* Set update flags */
403 
404     updateRA  = 1;
405     updateDec = 1;
406 
407     /* Correct for offsets and return true coordinate */
408     /* Coordinate reported by NexStar  = true coordinate + offset. */
409 
410     returnRA  = returnRA - offsetRA;
411     returnDec = returnDec - offsetDec;
412 }
413 
414 /* Reset telescope coordinates to new coordinates by adjusting offsets*/
415 /* Coordinate reported by NexStar  = true coordinate + offset. */
416 
SyncToCoords(double newRA,double newDec)417 int SyncToCoords(double newRA, double newDec)
418 {
419     /*  offsetRA = 0.;
420    offsetDec = 0.;
421    GetRAandDec();
422    offsetRA = returnRA - newRA;
423    offsetDec = returnDec - newDec;
424 
425    return (0);*/
426 
427     /* 2013-10-19 JM: Trying to support sync in Nextstar command set v4.10+ */
428     char str[20];
429     int n1, n2;
430 
431     //  so, lets format up a sync command
432     n1 = newRA * 0x1000000 / 24;
433     n2 = newDec * 0x1000000 / 360;
434     n1 = n1 << 8;
435     n2 = n2 << 8;
436     sprintf((char *)str, "s%08X,%08X", n1, n2);
437     writen(TelPortFD, str, 18);
438 
439     /* Look for '#' in response */
440     for (;;)
441     {
442         if (readn(TelPortFD, str, 1, 2))
443         {
444             if (str[0] == '#')
445                 break;
446         }
447         else
448             fprintf(stderr, "No acknowledgment from telescope after SyncToCoords.\n");
449         return 4;
450     }
451     return 0;
452 }
453 
454 /* 2013-11-06 JM: support location update in Nextstar command set */
updateLocation(double lng,double lat)455 int updateLocation(double lng, double lat)
456 {
457     int lat_d, lat_m, lat_s;
458     int long_d, long_m, long_s;
459 
460     char str[10];
461     char cmd[8];
462 
463     // Convert from INDI standard to regular east/west -180 to 180
464     if (lng > 180)
465         lng -= 360;
466 
467     getSexComponents(lat, &lat_d, &lat_m, &lat_s);
468     getSexComponents(lng, &long_d, &long_m, &long_s);
469 
470     cmd[0] = abs(lat_d);
471     cmd[1] = lat_m;
472     cmd[2] = lat_s;
473     cmd[3] = lat_d > 0 ? 0 : 1;
474     cmd[4] = abs(long_d);
475     cmd[5] = long_m;
476     cmd[6] = long_s;
477     cmd[7] = long_d > 0 ? 0 : 1;
478 
479     sprintf((char *)str, "W%c%c%c%c%c%c%c%c", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]);
480     writen(TelPortFD, str, 9);
481 
482     /* Look for '#' in response */
483     for (;;)
484     {
485         if (readn(TelPortFD, str, 1, 2))
486         {
487             if (str[0] == '#')
488                 break;
489         }
490         else
491             fprintf(stderr, "No acknowledgment from telescope after updateLocation.\n");
492         return 4;
493     }
494     return 0;
495 }
496 
updateTime(struct ln_date * utc,double utc_offset)497 int updateTime(struct ln_date *utc, double utc_offset)
498 {
499     char str[10];
500     char cmd[8];
501     struct ln_zonedate local_date;
502 
503     // Celestron takes local time
504     ln_date_to_zonedate(utc, &local_date, utc_offset * 3600);
505 
506     cmd[0] = local_date.hours;
507     cmd[1] = local_date.minutes;
508     cmd[2] = local_date.seconds;
509     cmd[3] = local_date.months;
510     cmd[4] = local_date.days;
511     cmd[5] = local_date.years - 2000;
512 
513     if (utc_offset < 0)
514         cmd[6] = 256 - ((unsigned int)fabs(utc_offset));
515     else
516         cmd[6] = ((unsigned int)fabs(utc_offset));
517 
518     // Always assume standard time
519     cmd[7] = 0;
520 
521     sprintf((char *)str, "H%c%c%c%c%c%c%c%c", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7]);
522     writen(TelPortFD, str, 9);
523 
524     /* Look for '#' in response */
525     for (;;)
526     {
527         if (readn(TelPortFD, str, 1, 2))
528         {
529             if (str[0] == '#')
530                 break;
531         }
532         else
533             fprintf(stderr, "No acknowledgment from telescope after updateTime.\n");
534         return 4;
535     }
536     return 0;
537 }
538 
539 /* Slew to new coordinates */
540 /* Coordinate sent to NexStar  = true coordinate + offset. */
541 
SlewToCoords(double newRA,double newDec)542 int SlewToCoords(double newRA, double newDec)
543 {
544     int countRA, countDec;
545     char r0, r1, r2, r3, d0, d1, d2, d3;
546     double degs, hrs;
547     char outputStr[32], inputStr[2048];
548 
549     /* Add offsets */
550 
551     hrs  = newRA + offsetRA;
552     degs = newDec + offsetDec;
553 
554     /* Convert float RA to integer count */
555 
556     hrs     = hrs * (3. * 15. * 60. * 65536. / 64800.);
557     countRA = (int)hrs;
558 
559     /* Account for the quadrant in declination */
560 
561     if ((newDec >= 0.0) && (newDec <= 90.0))
562     {
563         degs = degs * (3. * 60. * 65536. / 64800.);
564     }
565     else if ((newDec < 0.0) && (newDec >= -90.0))
566     {
567         degs = (360. + degs) * (3. * 60. * 65536. / 64800.);
568     }
569     else
570     {
571         fprintf(stderr, "Invalid newDec in SlewToCoords.\n");
572         return 1;
573     }
574 
575     /* Convert float Declination to integer count */
576 
577     countDec = (int)degs;
578 
579     /* Convert each integer count to four HEX characters */
580     /* Inline coding just to be fast */
581 
582     if (countRA < 65536)
583     {
584         r0 = countRA % 16;
585         if (r0 < 10)
586         {
587             r0 = r0 + 48;
588         }
589         else
590         {
591             r0 = r0 + 55;
592         }
593         countRA = countRA / 16;
594         r1      = countRA % 16;
595         if (r1 < 10)
596         {
597             r1 = r1 + 48;
598         }
599         else
600         {
601             r1 = r1 + 55;
602         }
603         countRA = countRA / 16;
604         r2      = countRA % 16;
605         if (r2 < 10)
606         {
607             r2 = r2 + 48;
608         }
609         else
610         {
611             r2 = r2 + 55;
612         }
613         r3 = countRA / 16;
614         if (r3 < 10)
615         {
616             r3 = r3 + 48;
617         }
618         else
619         {
620             r3 = r3 + 55;
621         }
622     }
623     else
624     {
625         printf("RA count overflow in SlewToCoords.\n");
626         return 2;
627     }
628     if (countDec < 65536)
629     {
630         d0 = countDec % 16;
631         if (d0 < 10)
632         {
633             d0 = d0 + 48;
634         }
635         else
636         {
637             d0 = d0 + 55;
638         }
639         countDec = countDec / 16;
640         d1       = countDec % 16;
641         if (d1 < 10)
642         {
643             d1 = d1 + 48;
644         }
645         else
646         {
647             d1 = d1 + 55;
648         }
649         countDec = countDec / 16;
650         d2       = countDec % 16;
651         if (d2 < 10)
652         {
653             d2 = d2 + 48;
654         }
655         else
656         {
657             d2 = d2 + 55;
658         }
659         d3 = countDec / 16;
660         if (d3 < 10)
661         {
662             d3 = d3 + 48;
663         }
664         else
665         {
666             d3 = d3 + 55;
667         }
668     }
669     else
670     {
671         fprintf(stderr, "Dec count overflow in SlewToCoords.\n");
672         return 3;
673     }
674 
675     /* Send the command and characters to the NexStar */
676 
677     sprintf(outputStr, "R%c%c%c%c,%c%c%c%c", r3, r2, r1, r0, d3, d2, d1, d0);
678     writen(TelPortFD, outputStr, 10);
679 
680     /* Look for '#' in response */
681 
682     for (;;)
683     {
684         if (readn(TelPortFD, inputStr, 1, 2))
685         {
686             if (inputStr[0] == '#')
687                 break;
688         }
689         else
690             fprintf(stderr, "No acknowledgment from telescope after SlewToCoords.\n");
691         return 4;
692     }
693     return 0;
694 }
695 
696 /* Test whether the destination has been reached */
697 /* With the NexStar we use the goto in progress query */
698 /* Return value is  */
699 /*   0 -- goto in progress */
700 /*   1 -- goto complete within tolerance */
701 /*   2 -- goto complete but outside tolerance */
702 
CheckCoords(double desRA,double desDec,double tolRA,double tolDEC)703 int CheckCoords(double desRA, double desDec, double tolRA, double tolDEC)
704 {
705     double errorRA, errorDec, nowRA, nowDec;
706     char inputStr[2048];
707 
708     writen(TelPortFD, "L", 1);
709 
710     /* Look for '0#' in response indicating goto is not in progress */
711 
712     for (;;)
713     {
714         if (readn(TelPortFD, inputStr, 2, 2))
715         {
716             if ((inputStr[0] == '0') && (inputStr[1] == '#'))
717                 break;
718         }
719         else
720             return 0;
721     }
722 
723     nowRA    = GetRA();
724     errorRA  = nowRA - desRA;
725     nowDec   = GetDec();
726     errorDec = nowDec - desDec;
727 
728     /* For 6 minute of arc precision; change as needed.  */
729 
730     if (fabs(errorRA) > tolRA || fabs(errorDec) > tolDEC)
731         return 1;
732     else
733         return 2;
734 }
735 
736 /* Set lower and upper limits to protect hardware */
737 
SetLimits(double limitLower,double limitHigher)738 void SetLimits(double limitLower, double limitHigher)
739 {
740     limitLower = limitHigher;
741     fprintf(stderr, "NexStar does not support software limits.\n");
742 }
743 
744 /* Set slew speed limited by MAXSLEWRATE */
745 
SetSlewRate(void)746 int SetSlewRate(void)
747 {
748     fprintf(stderr, "NexStar does not support remote setting of slew rate.\n");
749     return 0;
750 }
751 
752 /* Stop all slew motion */
753 
StopNSEW(void)754 void StopNSEW(void)
755 {
756     char inputStr[2048];
757 
758     writen(TelPortFD, "M", 1);
759 
760     /* Look for '#' */
761 
762     for (;;)
763     {
764         if (readn(TelPortFD, inputStr, 1, 1))
765         {
766             if (inputStr[0] == '#')
767                 break;
768         }
769         else
770         {
771             fprintf(stderr, "No acknowledgment from telescope in StopNSEW.\n");
772         }
773     }
774 }
775 
776 /* Control the reticle function using predefined values */
777 
Reticle(int reticle)778 void Reticle(int reticle)
779 {
780     reticle = reticle;
781     fprintf(stderr, "NexStar does not support remote setting of reticle.\n");
782 }
783 
784 /* Control the focus using predefined values */
785 
Focus(int focus)786 void Focus(int focus)
787 {
788     focus = focus;
789     fprintf(stderr, "NexStar does not support remote setting of focus.\n");
790 }
791 
792 /* Control the derotator using predefined values */
793 
Derotator(int rotate)794 void Derotator(int rotate)
795 
796 {
797     rotate = rotate;
798     fprintf(stderr, "NexStar does not support an image derotator.\n");
799 }
800 
801 /* Control the fan using predefined values */
802 
Fan(int fan)803 void Fan(int fan)
804 
805 {
806     fan = fan;
807     fprintf(stderr, "NexStar does not have a fan.\n");
808 }
809 
810 /* Time synchronization utilities */
811 
812 /* Reset the telescope sidereal time */
813 
SyncLST(double newTime)814 int SyncLST(double newTime)
815 {
816     newTime = newTime;
817     fprintf(stderr, "NexStar does not support remote setting of sidereal time.\n");
818     return -1;
819 }
820 
821 /*  Reset the telescope local time */
822 
SyncLocalTime()823 int SyncLocalTime()
824 {
825     fprintf(stderr, "NexStar does not support remote setting of local time.\n");
826     return -1;
827 }
828 
829 /* Serial port utilities */
830 
writen(fd,ptr,nbytes)831 static int writen(fd, ptr, nbytes) int fd;
832 char *ptr;
833 int nbytes;
834 {
835     int nleft, nwritten;
836     nleft = nbytes;
837     while (nleft > 0)
838     {
839         nwritten = write(fd, ptr, nleft);
840         if (nwritten <= 0)
841             break;
842         nleft -= nwritten;
843         ptr += nwritten;
844     }
845     return (nbytes - nleft);
846 }
847 
readn(fd,ptr,nbytes,sec)848 static int readn(fd, ptr, nbytes, sec) int fd;
849 char *ptr;
850 int nbytes;
851 int sec;
852 {
853     int status;
854     int nleft, nread;
855     nleft = nbytes;
856     while (nleft > 0)
857     {
858         status = telstat(fd, sec, 0);
859         if (status <= 0)
860             break;
861         nread = read(fd, ptr, nleft);
862 
863         /*  Diagnostic */
864 
865         /*    printf("readn: %d read\n", nread);  */
866 
867         if (nread <= 0)
868             break;
869         nleft -= nread;
870         ptr += nread;
871     }
872     return (nbytes - nleft);
873 }
874 
875 /*
876  * Examines the read status of a file descriptor.
877  * The timeout (sec, usec) specifies a maximum interval to
878  * wait for data to be available in the descriptor.
879  * To effect a poll, the timeout (sec, usec) should be 0.
880  * Returns non-negative value on data available.
881  * 0 indicates that the time limit referred by timeout expired.
882  * On failure, it returns -1 and errno is set to indicate the
883  * error.
884  */
telstat(fd,sec,usec)885 static int telstat(fd, sec, usec) register int fd, sec, usec;
886 {
887     int ret;
888     int width;
889     struct timeval timeout;
890     telfds readfds;
891 
892     memset((char *)&readfds, 0, sizeof(readfds));
893     FD_SET(fd, &readfds);
894     width           = fd + 1;
895     timeout.tv_sec  = sec;
896     timeout.tv_usec = usec;
897     ret             = select(width, &readfds, NULL_PTR(telfds), NULL_PTR(telfds), &timeout);
898     return (ret);
899 }
900