source: BAORadio/libindi/v1.0.1/drivers/telescope/orionatlas.cpp @ 614

Last change on this file since 614 was 490, checked in by campagne, 14 years ago

import libindi (JEC)

File size: 25.3 KB
Line 
1/*
2    OrionAtlas (EQ-G/EQ-6 with SkyScan/SynScan controller)
3    Copyright (C) 2005 Bruce Bockius (bruceb@WhiteAntelopeSoftware.com)
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
19*/
20
21#include "orionatlas.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdarg.h>
27#include <math.h>
28#include <unistd.h>
29#include <time.h>
30#include <sys/ioctl.h>
31#include <fcntl.h>
32#include <termios.h>
33
34#define mydev       "Orion Atlas"
35#define ATLAS_DEBUG 1
36#define POLLMS 5000
37
38#define lat geo[0].value
39#define lon geo[1].value
40
41OrionAtlas *telescope = NULL;
42
43
44/* There is _one_ binary for all LX200 drivers, but each binary is renamed
45** to its device name (i.e. lx200gps, lx200_16..etc). The main function will
46** fetch from std args the binary name and ISInit will create the apporpiate
47** device afterwards. If the binary name does not match any known devices,
48** we simply create a generic device
49*/
50extern char* me;
51
52#define COMM_GROUP  "Communication"
53#define BASIC_GROUP "Main Control"
54#define SETUP_GROUP "Setup"
55
56#define ATLAS_MIN_RA 0.0
57#define ATLAS_MAX_RA 24.0
58#define ATLAS_MIN_DEC -90.0
59#define ATLAS_MAX_DEC 90.0
60
61#define ATLAS_MIN_AZ 0.0
62#define ATLAS_MAX_AZ 360.0
63#define ATLAS_MIN_ALT -90.0
64#define ATLAS_MAX_ALT 90.0
65
66static void ISPoll(void *);
67
68//static ISwitch abortSlewS[]      = {{"ABORT", "Abort", ISS_OFF, 0, 0}};
69
70/* Fundamental group */
71static ISwitch PowerS[] = {{"CONNECT","Connect",ISS_OFF,0,0},{"DISCONNECT","Disconnect",ISS_ON,0,0},{"RECONNECT","Reconnect",ISS_OFF,0,0}};
72static ISwitchVectorProperty PowerSw = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, PowerS, NARRAY(PowerS), "", 0};
73static IText PortT[] = {{"PORT", "Port", 0, 0, 0, 0}};
74static ITextVectorProperty Port = { mydev, "DEVICE_PORT", "Ports", COMM_GROUP, IP_RW, 0, IPS_OK, PortT, NARRAY(PortT), "", 0};
75
76/* Movement group */
77//static ISwitchVectorProperty abortSlewSw     = { mydev, "ABORT_MOTION", "Abort Slew/Track", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, abortSlewS, NARRAY(abortSlewS), "", 0};
78static INumber eq[] = {
79    {"RA",  "RA (hh:mm.m)", "%010.5m",  ATLAS_MIN_RA, ATLAS_MAX_RA, 0., 0., 0, 0, 0},
80    {"DEC", "Dec (dd:mm.m)", "%010.5m", ATLAS_MIN_DEC, ATLAS_MAX_DEC, 0., 0., 0, 0, 0}};
81// Azimuth/Altitude
82static INumber aa[] = {
83    {"XAZ",  "Az (ddd:mm.m)", "%010.5m", ATLAS_MIN_AZ, ATLAS_MAX_AZ, 0.0, 0.0, 0, 0, 0},
84    {"XALT", "Alt (dd:mm.m)", "%010.5m", ATLAS_MIN_ALT, ATLAS_MAX_ALT, 0.0, 0.0, 0, 0, 0}};
85static INumberVectorProperty eqNum = {
86    mydev, "EQUATORIAL_EOD_COORD", "Eq. Coordinates", BASIC_GROUP, IP_RW, 0, IPS_OK, eq, NARRAY(eq), "", 0};
87
88static INumberVectorProperty aaNum = {
89    mydev, "XHORIZONTAL_COORD", "Horz. Coordinates", BASIC_GROUP, IP_RW, 0, IPS_OK, aa, NARRAY(aa), "", 0};
90
91static ISwitch OnCoordSetS[] = {{"TRACK", "Track", ISS_ON, 0, 0}}; // switch does nothing, but some clients won't let you track w/o it.
92static ISwitchVectorProperty OnCoordSetSw = {
93    mydev, "ON_COORD_SET", "On Set", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_OK, OnCoordSetS, NARRAY(OnCoordSetS), "", 0};
94static ISwitch UpdateS[] = {{"UPDATE1", "On", ISS_ON, 0, 0},{"UPDATE0","Off", ISS_OFF,0,0}};
95static ISwitch MovementRADecS[] = {{"XRAPLUS", "RA+", ISS_OFF, 0, 0}, {"XRAMINUS", "RA-", ISS_OFF, 0, 0}, {"XDECPLUS", "Dec+", ISS_OFF, 0, 0},            {"XDECMINUS", "Dec-", ISS_OFF, 0, 0}};
96static ISwitch MovementAzAltS[] = {{"XAZPLUS", "Az+", ISS_OFF, 0, 0}, {"XAZMINUS", "Az-", ISS_OFF, 0, 0}, {"XALTPLUS", "Alt+", ISS_OFF, 0, 0},            {"XALTMINUS", "Alt-", ISS_OFF, 0, 0}};
97static ISwitchVectorProperty MovementRADecSw = { mydev, "XRADECMOVEMENT", "Nudge", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_OK, MovementRADecS,             NARRAY(MovementRADecS), "", 0};
98static ISwitchVectorProperty MovementAzAltSw = { mydev, "XAZALTMOVEMENT", "Nudge", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_OK, MovementAzAltS,             NARRAY(MovementAzAltS), "", 0};
99static ISwitchVectorProperty UpdateSw = { mydev, "XUPDATE", "Update Coords", BASIC_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_OK, UpdateS, NARRAY(UpdateS), "", 0};
100
101/* Telescope Parameters group */
102// Setup group
103static INumber steps[] = {
104    {"XRASTEP", "RA Step", "%010.6m", 0.0, 5.0, 0.0, 0.0, 0, 0, 0},
105    {"XDECSTEP", "Dec Step", "%010.6m", 0.0, 5.0, 0.0, 0.0, 0, 0, 0},
106    {"XAZSTEP", "Az Step", "%010.6m", 0.0, 5.0, 0.0, 0.0, 0, 0, 0},
107    {"XALTSTEP", "Alt Step", "%010.6m", 0.0, 5.0, 0.0, 0.0, 0, 0, 0}};
108// geographic position
109static INumber geo[] = {
110    {"LAT", "Lat (dd:mm.m)", "%010.5m", -90.0, 90.0, 0.0, 0.0, 0, 0, 0},
111    {"LONG", "Lon (ddd:mm.m)", "%010.5m", -180.0, 360.0, 0.0, 0.0, 0, 0, 0}};
112static INumberVectorProperty geoNum = {
113    mydev, "GEOGRAPHIC_COORD", "Scope Location", SETUP_GROUP, IP_RW, 0, IPS_OK, geo, NARRAY(geo), "", 0};
114static INumberVectorProperty stepNum = {
115    mydev, "XSTEPS", "Nudge Steps", SETUP_GROUP, IP_RW, 0, IPS_OK, steps, NARRAY(steps), "", 0};
116
117/* send client definitions of all properties */
118void ISInit()
119{
120    static int isInit=0;
121
122    if (isInit) return;
123
124    isInit = 1;
125
126    PortT[0].text = strcpy(new char[32], "/dev/ttyUSB0");
127    steps[0].value=1.0/60.0;
128    steps[1].value=1.0/60.0;
129    steps[2].value=1.0/60.0;
130    steps[3].value=1.0/60.0;
131
132    telescope = new OrionAtlas();
133
134    IEAddTimer (POLLMS, ISPoll, NULL);
135}
136
137void ISGetProperties (const char *dev)
138{ ISInit(); telescope->ISGetProperties(dev);}
139void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
140{ ISInit(); telescope->ISNewSwitch(dev, name, states, names, n);}
141void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
142{ ISInit(); telescope->ISNewText(dev, name, texts, names, n);}
143void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
144{ ISInit(); telescope->ISNewNumber(dev, name, values, names, n);}
145void ISPoll (void *p) { telescope->ISPoll();    IEAddTimer (POLLMS, ISPoll, NULL); p=p;}
146void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) 
147{
148  INDI_UNUSED(dev);
149  INDI_UNUSED(name);
150  INDI_UNUSED(sizes);
151  INDI_UNUSED(blobsizes);
152  INDI_UNUSED(blobs);
153  INDI_UNUSED(formats);
154  INDI_UNUSED(names);
155  INDI_UNUSED(n);
156}
157void ISSnoopDevice (XMLEle *root) 
158{
159  INDI_UNUSED(root);
160}
161
162OrionAtlas::OrionAtlas()
163{
164    geo[0].value=0;
165    geo[1].value=0;
166    lat=lon=-1000;
167
168    TelConnectFlag=0;
169    Updating=true;
170    // Children call parent routines, this is the default
171    IDLog("Initialized Orion Atlas EQ-G device, driver ver 0.101\n");
172    if (ATLAS_DEBUG) IDLog("Driver in DEBUG mode.");
173}
174
175void OrionAtlas::ISGetProperties(const char *dev)
176{
177
178    if (dev && strcmp (mydev, dev))
179        return;
180
181    // COMM_GROUP
182    IDDefSwitch (&PowerSw, NULL);
183    IDDefText   (&Port, NULL);
184
185    // BASIC_GROUP
186    IDDefNumber (&eqNum, NULL);
187    IDDefNumber (&aaNum, NULL);
188    IDDefSwitch (&UpdateSw, NULL);
189//    IDDefSwitch (&abortSlewSw, NULL);
190    IDDefSwitch (&OnCoordSetSw,NULL);
191    IDDefSwitch (&MovementRADecSw, NULL);
192    IDDefSwitch (&MovementAzAltSw, NULL);
193
194  // SETUP_GROUP
195    IDDefNumber (&geoNum, NULL);
196    IDDefNumber (&stepNum, NULL);
197
198  /* Send the basic data to the new client if the previous client(s) are already connected. */
199    if (PowerSw.s == IPS_OK) {
200        if (ATLAS_DEBUG) log("Initial call to getBasicData()\n");
201        getBasicData();
202    }
203}
204
205void OrionAtlas::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
206{
207    IText *tp;
208
209    // suppress warning
210    n=n;
211    // ignore if not ours //
212    if (strcmp (dev, mydev))
213        return;
214
215    if (!strcmp(name, Port.name) ) {
216        Port.s = IPS_OK;
217
218        tp = IUFindText( &Port, names[0] );
219        if (!tp)
220            return;
221
222        tp->text = new char[strlen(texts[0])+1];
223        strcpy(tp->text, texts[0]);
224        IDSetText (&Port, NULL);
225        return;
226    }
227    else {
228        if (ATLAS_DEBUG) log("ISNewText('%s')\n",name);
229    }
230}
231
232void OrionAtlas::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
233{
234    double newRA=0, newDEC=0, newAlt=0, newAz=0;
235
236        // ignore if not ours //
237    if (strcmp (dev, mydev))
238        return;
239
240    if (!strcmp (name, eqNum.name)) {
241        int i=0, nset=0;
242        if (checkPower(&eqNum)) return;
243
244        for (nset = i = 0; i < n; i++) {
245            INumber *eqp = IUFindNumber (&eqNum, names[i]);
246            if (eqp == &eq[0]) {
247                newRA = values[i];
248                nset += newRA >= 0 && newRA <= 24.0;
249            } else if (eqp == &eq[1]) {
250                newDEC = values[i];
251                nset += newDEC >= -90.0 && newDEC <= 90.0;
252            }
253        }
254        if (nset==2) { // both coords were valid.  Slew.
255            MoveScope(RADEC,newRA,newDEC);
256        }
257    }
258    else if (!strcmp(name, aaNum.name)) {
259        int i=0, nset=0;
260        if (checkPower(&eqNum)) return;
261
262        for (nset = i = 0; i < n; i++) {
263            INumber *aap = IUFindNumber (&aaNum, names[i]);
264            if (aap == &aa[1]) {
265                newAlt = values[i];
266                nset += newAlt >= -90.0 && newAlt <= 90.0;
267            } else if (aap == &aa[0]) {
268                newAz = values[i];
269                nset += newAz >= 0.0 && newAz <=360.0;
270            }
271        }
272        if (nset==2) { // both coords were valid.  Slew.
273            MoveScope(AZALT,newAz,newAlt);
274        }
275    }
276    else if (!strcmp(name, geoNum.name)) {
277        if (ATLAS_DEBUG) log("NewNumber(geoName)\n");
278        int i=0;
279        for (i=0;i<n;i++) {
280            INumber *geop = IUFindNumber(&geoNum, names[i]);
281            geop->value=values[i];
282        }
283        IDSetNumber(&geoNum,NULL);
284    }
285    else if (!strcmp(name, stepNum.name)) {
286        if (ATLAS_DEBUG) log("NewNumber(stepNum)\n");
287        for (int i=0;i<n;i++) {
288            INumber *stepp = IUFindNumber(&stepNum, names[i]);
289            stepp->value=values[i];
290        }
291        IDSetNumber(&stepNum,NULL);
292    }
293    else {
294        if (ATLAS_DEBUG) log("ISNewNumber('%s')\n",name);
295    }
296}
297
298void OrionAtlas::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
299{
300
301    INDI_UNUSED(names);
302
303    // ignore if not ours //
304    if (strcmp(dev,mydev))
305        return;
306
307    // FIRST Switch ALWAYS for power
308    if (!strcmp(name,PowerSw.name)) 
309    {
310        if (IUUpdateSwitch(&PowerSw,states,names,n) < 0)
311                return;
312
313        if (PowerS[1].== ISS_ON)
314                DisconnectTel();
315        else
316                ConnectTel();
317        return;
318        }
319    else if (!strcmp(name,UpdateSw.name)) {
320        IUResetSwitch(&UpdateSw);
321        IUUpdateSwitch(&UpdateSw,states,names,n);
322        IDSetSwitch(&UpdateSw,NULL);
323        Updating=!Updating;
324    }
325    else if (!strcmp(name,MovementRADecSw.name)) {
326        if (!GetCoords(RADEC)) {
327            IDMessage(mydev,"Invalid coordinates from scope - aborted nudge.");
328        }
329        log("before RA=%f  Dec=%f\n",returnRA,returnDec);
330        for (int i=0; i<n; i++) {
331            states[i]=ISS_OFF;
332            ISwitch *mvp = IUFindSwitch(&MovementRADecSw, names[i]);
333            if (mvp == &MovementRADecS[0]) {
334                // RA+
335                returnRA+=steps[0].value;
336            } else if (mvp == &MovementRADecS[1]) {
337                // RA-
338                returnRA-=steps[0].value;
339            } else if (mvp == &MovementRADecS[2]) {
340                // Dec+
341                returnDec+=steps[1].value;
342            } else if (mvp == &MovementRADecS[3]) {
343                // Dec-
344                returnDec-=steps[1].value;
345            }
346        }
347        log("after  RA=%f  Dec=%f\n",returnRA,returnDec);
348        IUResetSwitch(&MovementRADecSw);
349        IUUpdateSwitch(&MovementRADecSw,states,names,n);
350        IDSetSwitch(&MovementRADecSw,NULL);
351        MoveScope(RADEC,returnRA,returnDec);
352        UpdateCoords();
353    }
354    else if (!strcmp(name,MovementAzAltSw.name)) {
355        if (!GetCoords(AZALT)) {
356            IDMessage(mydev,"Invalid coordinates from scope - aborted nudge.");
357        }
358        log("before     Az=%f  Alt=%f\n",returnAz,returnAlt);
359        for (int i=0; i<n; i++) {
360            states[i]=ISS_OFF;
361            ISwitch *mvp = IUFindSwitch(&MovementAzAltSw, names[i]);
362            if (mvp == &MovementAzAltS[0]) {
363                // Az+
364                returnAz+=steps[2].value;
365            } else if (mvp == &MovementAzAltS[1]) {
366                // Az-
367                returnAz-=steps[2].value;
368            } else if (mvp == &MovementAzAltS[2]) {
369                // Alt+
370                returnAlt+=steps[3].value;
371            } else if (mvp == &MovementAzAltS[3]) {
372                // Alt-
373                returnAlt-=steps[3].value;
374            }
375        }
376        log("commanded  Az=%f  Alt=%f\n",returnAz,returnAlt);
377        IUResetSwitch(&MovementAzAltSw);
378        IUUpdateSwitch(&MovementAzAltSw,states,names,n);
379        IDSetSwitch(&MovementAzAltSw,NULL);
380        MoveScope(AZALT,returnAz,returnAlt);
381        UpdateCoords();
382        log("after      Az=%f  Alt=%f\n",returnAz,returnAlt);
383    }
384    else {
385        if (ATLAS_DEBUG) log("ISNewSwitch('%s')\n",name);
386    }
387}
388
389int OrionAtlas::checkPower(ISwitchVectorProperty *sp)
390{
391    if (PowerSw.s != IPS_OK) {
392        if (!strcmp(sp->label, ""))
393            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", sp->name);
394        else
395            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", sp->label);
396        sp->s = IPS_IDLE;
397        IDSetSwitch(sp, NULL);
398        return -1;
399    }
400    return 0;
401}
402
403int OrionAtlas::checkPower(INumberVectorProperty *np)
404{
405    if (PowerSw.s != IPS_OK) {
406        if (!strcmp(np->label, ""))
407            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", np->name);
408        else
409            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", np->label);
410        np->s = IPS_IDLE;
411        IDSetNumber(np, NULL);
412        return -1;
413    }
414    return 0;
415}
416
417int OrionAtlas::checkPower(ITextVectorProperty *tp)
418{
419    if (PowerSw.s != IPS_OK) {
420        if (!strcmp(tp->label, ""))
421            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", tp->name);
422        else
423            IDMessage (mydev, "Cannot change property %s while the telescope is offline.", tp->label);
424        tp->s = IPS_IDLE;
425        IDSetText(tp, NULL);
426        return -1;
427    }
428    return 0;
429}
430
431void OrionAtlas::getBasicData()
432{
433    if (ATLAS_DEBUG) log("getBasicData\n");
434    UpdateCoords();
435}
436
437void OrionAtlas::ConnectTel()
438{
439    if (PowerSw.sp[0].s==ISS_ON||PowerSw.sp[2].s==ISS_ON) { // CONNECT or RECONNECT
440        if (ConnectTel(Port.tp[0].text) < 0) 
441        {
442            PowerS[0].s = PowerS[2].s = ISS_OFF;
443            PowerS[1].s = ISS_ON;
444            IDSetSwitch (&PowerSw, "Error connecting to port %s", Port.tp[0].text);
445            return;
446        }
447        PowerSw.s = IPS_OK;
448        PowerS[0].s = ISS_ON;
449        PowerS[1].s = PowerS[2].s= ISS_OFF;
450        IDSetSwitch (&PowerSw, "Telescope is online. Updating coordinates.");
451        if (ATLAS_DEBUG) log("Powered on scope, calling getBasicData()\n");
452        getBasicData();
453    }
454    else if (PowerSw.sp[0].s==ISS_OFF) {
455        IDSetSwitch (&PowerSw, "Telescope is offline.");
456        DisconnectTel();
457    }
458}
459
460int OrionAtlas::CheckConnectTel(void)
461{
462    return TelConnectFlag;
463}
464
465int OrionAtlas::ConnectTel(char *port)
466{
467    struct termios tty;
468    unsigned char returnStr[128];
469    int numRead;
470
471    if (ATLAS_DEBUG) log( "Connecting to port: %s\n",port);
472
473    if(TelConnectFlag != 0)
474        return 0;
475
476    /* Make the connection */
477
478    TelPortFD = open(port,O_RDWR);
479    if(TelPortFD == -1) {
480        IDMessage(mydev,"Could not open the supplied port!");
481        return -1;
482    }
483
484    tcgetattr(TelPortFD,&tty);
485    cfsetospeed(&tty, (speed_t) B9600);
486    cfsetispeed(&tty, (speed_t) B9600);
487    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
488    tty.c_iflag =  IGNBRK;
489    tty.c_lflag = 0;
490    tty.c_oflag = 0;
491    tty.c_cflag |= CLOCAL | CREAD;
492    tty.c_cc[VMIN] = 1;
493    tty.c_cc[VTIME] = 5;
494    tty.c_iflag &= ~(IXON|IXOFF|IXANY);
495    tty.c_cflag &= ~(PARENB | PARODD);
496    tcsetattr(TelPortFD, TCSANOW, &tty);
497
498    /* Flush the input (read) buffer */
499
500    tcflush(TelPortFD,TCIOFLUSH);
501
502    /* Test connection */
503
504    writen(TelPortFD,(unsigned char*)"?",1);
505    numRead=readn(TelPortFD,returnStr,1,2);
506    returnStr[numRead] = '\0';
507
508    if (numRead == 1 && returnStr[0]=='#') {
509        TelConnectFlag = 1;
510        IDMessage(mydev,"Successfully connected.");
511        return (0);
512    }
513    else if (numRead>0) {
514        IDMessage(mydev,"Connect failure: Did not detect an Orion Atlas EQ-G on this port!");
515        return -2;
516    }
517    else {
518        IDMessage(mydev,"Connect failure: Did not detect any device on this port!");
519        return -3;
520    }
521}
522
523void OrionAtlas::DisconnectTel(void)
524{
525    if(TelConnectFlag == 1)
526        close(TelPortFD);
527    TelConnectFlag = 0;
528    IDMessage(mydev,"Telescope is offline.");
529}
530
531// If System=RADEC, c1=RA, c2=Dec
532//          =AZALT, c1=Az, c2=Alt
533int OrionAtlas::MoveScope(int System, double c1, double c2)
534{
535    MovementRADecSw.s=MovementAzAltSw.s=eqNum.s=aaNum.s=OnCoordSetSw.s=IPS_BUSY;
536    IDSetSwitch(&MovementRADecSw,NULL);
537    IDSetSwitch(&MovementAzAltSw,NULL);
538    IDSetSwitch(&OnCoordSetSw,NULL);
539    IDSetNumber(&eqNum,NULL);
540    IDSetNumber(&aaNum,NULL);
541    union {
542        signed short int ints[2];
543        unsigned short int uints[2];
544        unsigned char bytes[4];
545    };
546
547    char Command=' ';
548    while (1) {
549        if (System==RADEC) Command='R';
550        else if (System==AZALT) Command='A';
551        else {
552            log("Invalid command '%c' to MoveScope!",Command);
553            break;
554        }
555        unsigned char sendStr[5];
556        writen(TelPortFD,(unsigned char*)"?",1);
557        int numRead=readn(TelPortFD,sendStr,1,3);
558        if (numRead!=1 || sendStr[0]!='#') {
559            IDMessage(mydev,"Failure: Scope not ready for movement command!");
560            break;
561        }
562
563        if (Command=='R') {
564            IDMessage(mydev,"Beginning slew to RA=%f Dec=%f\n",c1,c2);
565            // RA:
566            uints[0]= (unsigned short int) (c1*65536.0/24.0);
567            ints[1]=  (signed short int) (c2*65536.0/360.0);
568            sendStr[2]=bytes[0];    sendStr[1]=bytes[1];
569            sendStr[4]=bytes[2];    sendStr[3]=bytes[3];
570        }
571        else {
572            //c2=c2-lat+90.0; // decorrect
573            IDMessage(mydev,"Beginning slew to Az=%f Alt=%f\n",c1,c2);
574            // Az
575            uints[0]= (unsigned short int) (c1*65536.0/360.0);
576            ints[1]= (signed short int) (c2*65536.0/360.0);
577            sendStr[2]=bytes[0];    sendStr[1]=bytes[1];
578            sendStr[4]=bytes[2];    sendStr[3]=bytes[3];
579        }
580        sendStr[0]=Command;
581        if (ATLAS_DEBUG) log("Sending '%c' %X %X %X %X\n",Command,sendStr[1],sendStr[2],sendStr[3],sendStr[4]);
582        writen(TelPortFD,sendStr,5);
583        // it should send us an '@' when done slewing
584        numRead=readn(TelPortFD,sendStr,1,60);
585        if (numRead!=1||sendStr[0]!='@') {
586            IDMessage(mydev,"Timeout waiting for scope to complete slewing.");
587            break;
588        }
589        IDMessage(mydev,"Slewing complete.");
590
591        MovementRADecSw.s=MovementAzAltSw.s=eqNum.s=aaNum.s=OnCoordSetSw.s=IPS_OK;
592        IDSetSwitch(&MovementRADecSw,NULL);
593        IDSetSwitch(&MovementAzAltSw,NULL);
594        IDSetSwitch(&OnCoordSetSw,NULL);
595        IDSetNumber(&eqNum,NULL);
596        IDSetNumber(&aaNum,NULL);
597        return 1;
598    }
599    // only here if break = error
600    MovementRADecSw.s=MovementAzAltSw.s=eqNum.s=aaNum.s=OnCoordSetSw.s=IPS_ALERT;
601    IDSetSwitch(&MovementRADecSw,NULL);
602    IDSetSwitch(&MovementAzAltSw,NULL);
603    IDSetSwitch(&OnCoordSetSw,NULL);
604    IDSetNumber(&eqNum,NULL);
605    IDSetNumber(&aaNum,NULL);
606    return 0;
607}
608
609/* Read the telescope coordinates */
610int OrionAtlas::GetCoords(int System)
611{
612    UpdateSw.s=IPS_BUSY;
613    IDSetSwitch(&UpdateSw,NULL);
614    unsigned char returnStr[4];
615    union {
616        signed short int ints[2];
617        unsigned short int uints[2];
618        unsigned char bytes[4];
619    };
620
621    while (1) {
622        if (System&AZALT) {
623            returnAz=returnAlt=-1000;
624            if (System&RADEC) {returnRA=returnDec=-1000;}
625            // is scope ready?
626            writen(TelPortFD,(unsigned char*)"?",1);
627            int numRead=readn(TelPortFD,returnStr,1,3);
628            if (numRead!=1 || returnStr[0]!='#') {
629                IDMessage(mydev,"Failure: Scope not ready for Z command");
630                break;
631            }
632            // Send request for current Az/Alt coords
633            writen(TelPortFD,(unsigned char*)"Z",1);
634            numRead=readn(TelPortFD,returnStr,4,1);
635            if (numRead!=4) break;
636            // bytes as expected
637            if (ATLAS_DEBUG) log("Received '%c' %02x %02x %02x %02x\n",'Z',returnStr[0],returnStr[1],returnStr[2],returnStr[3]);
638
639            bytes[0]=returnStr[1];  bytes[1]=returnStr[0];
640            bytes[2]=returnStr[3];  bytes[3]=returnStr[2];
641            returnAz=uints[0]/65536.0*360.0;
642            returnAlt=ints[1]/65536.0*360.0;
643    //        returnAlt=returnAlt+lat-90.0;
644        }
645
646        if (System&RADEC) {
647            returnRA=returnDec=-1000;
648            // Check scope is ready
649            writen(TelPortFD,(unsigned char*)"?",1);
650            int numRead=readn(TelPortFD,returnStr,1,3);
651            if (numRead!=1 || returnStr[0]!='#') {
652                IDMessage(mydev,"Failure: Scope not ready for E command");
653                break;
654            }
655            // Send get RA/Dec
656            writen(TelPortFD,(unsigned char*)"E",1);
657            numRead=readn(TelPortFD,returnStr,4,1);
658            if (ATLAS_DEBUG) log("Received '%c' %02x %02x %02x %02x\n",'E',returnStr[0],returnStr[1],returnStr[2],returnStr[3]);
659            if (numRead!=4) break;
660            // bytes read is as expected
661
662            bytes[0]=returnStr[1];  bytes[1]=returnStr[0];
663            bytes[2]=returnStr[3];  bytes[3]=returnStr[2];
664            returnRA=uints[0]/65536.0*24.0;
665            returnDec=ints[1]/65536.0*360.0;
666        }
667        // Yep, data is valid.
668        UpdateSw.s=IPS_OK;
669        IDSetSwitch(&UpdateSw,NULL);
670        return(1);
671    }
672    // only here if break -> error!
673    UpdateSw.s=IPS_ALERT;
674    IDSetSwitch(&UpdateSw,NULL);
675    return 0;
676}
677
678// Get coords from scope and update INumbers
679void OrionAtlas::UpdateCoords(int System)
680{
681    if (GetCoords(System)) {
682        if (System&RADEC) {
683            eqNum.np[0].value = returnRA;
684            eqNum.np[1].value = returnDec;
685            IDSetNumber(&eqNum, NULL);
686        }
687        if (System&AZALT) {
688            aaNum.np[1].value = returnAlt;
689            aaNum.np[0].value = returnAz;
690            IDSetNumber(&aaNum, NULL);
691        }
692    }
693}
694
695int OrionAtlas::writen(int fd, unsigned char* ptr, int nbytes)
696{
697    int nleft, nwritten;
698    nleft = nbytes;
699    while (nleft > 0) {
700        nwritten = write (fd, ptr, nleft);
701        if (nwritten <=0 ) break;
702        nleft -= nwritten;
703        ptr += nwritten;
704    }
705    return (nbytes - nleft);
706}
707
708int OrionAtlas::readn(int fd, unsigned char* ptr, int nbytes, int sec)
709{
710    int status;
711    int nleft, nread;
712    nleft = nbytes;
713    while (nleft > 0) {
714        status = telstat(fd,sec,0);
715        if (status <=  0 ) break;
716        nread  = read (fd, ptr, nleft);
717
718/*  Diagnostic */
719
720/*    printf("readn: %d read\n", nread);  */
721
722        if (nread <= 0)  break;
723        nleft -= nread;
724        ptr += nread;
725    }
726    return (nbytes - nleft);
727}
728
729/*
730 * Examines the read status of a file descriptor.
731 * The timeout (sec, usec) specifies a maximum interval to
732 * wait for data to be available in the descriptor.
733 * To effect a poll, the timeout (sec, usec) should be 0.
734 * Returns non-negative value on data available.
735 * 0 indicates that the time limit referred by timeout expired.
736 * On failure, it returns -1 and errno is set to indicate the
737 * error.
738 */
739
740int OrionAtlas::telstat(int fd,int sec,int usec)
741{
742    int ret;
743    int width;
744    struct timeval timeout;
745    telfds readfds;
746
747    memset((char *)&readfds,0,sizeof(readfds));
748    FD_SET(fd, &readfds);
749    width = fd+1;
750    timeout.tv_sec = sec;
751    timeout.tv_usec = usec;
752    ret = select(width,&readfds,(telfds *)0,(telfds *)0,&timeout);
753    return(ret);
754}
755
756void OrionAtlas::ISPoll()
757{
758    // Called every 2 seconds.
759    if (!TelConnectFlag) return;
760    if (!Updating) return;
761    // Check status of
762    if (eqNum.s==IPS_IDLE||eqNum.s==IPS_OK) {
763        GetCoords();
764        eqNum.np[0].value=returnRA;
765        eqNum.np[1].value=returnDec;
766        aaNum.np[0].value=returnAz;
767        aaNum.np[1].value=returnAlt;
768        IDSetNumber(&eqNum,NULL);
769        IDSetNumber(&aaNum,NULL);
770    }
771    else { // Slewing.  Don't get coords yet.
772        if (ATLAS_DEBUG) log("   (still slewing)\n");
773    }
774}
775
776void OrionAtlas::log(const char *fmt,...)
777{
778    if (fmt) {
779        va_list ap;
780        va_start (ap, fmt);
781        fprintf(stderr, "%i: ", ((int) time(NULL)));
782        vfprintf(stderr, fmt, ap);
783        va_end(ap);
784    }
785}
Note: See TracBrowser for help on using the repository browser.