source: BAORadio/libindi/v1.0.1/examples/tutorial_two.c @ 614

Last change on this file since 614 was 501, checked in by frichard, 14 years ago

-BAOControl : petite interface permettant de contrôler les antennes via le pilote indi_BAO
-Le pilote indi_BAO utilise désormais libindi v 0.7

File size: 11.0 KB
Line 
1/*
2   INDI Developers Manual
3   Tutorial #2
4   
5   "Simple Telescope Simulator"
6   
7   In this tutorial, we create a simple simulator. We will use a few handy utility functions provided by INDI
8   like timers, string <---> number conversion, and more.
9   
10   Refer to README, which contains instruction on how to build this driver, and use it
11   with an INDI-compatible client.
12
13*/
14
15/** \file tutorial_two.c
16    \brief Implement a simple telescope simulator using more complex INDI concepts.
17    \author Jasem Mutlaq
18*/
19
20/* Standard headers */
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <math.h>
25#include <unistd.h>
26#include <sys/time.h>
27
28#include <sys/time.h>
29#include <time.h>
30
31void show_runtime(int state) {
32        static struct timeval tv;
33        struct timeval tv1;
34        double x, y;
35
36        if (state) {
37                gettimeofday(&tv, NULL);
38        } else {
39                gettimeofday(&tv1, NULL);
40                fprintf(stderr, "Ran for: %fmsec\n", (double)(tv1.tv_sec * 1000.0 + tv1.tv_usec / 1000.0) - (double)(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0));
41        }
42}
43
44/* INDI Core headers */
45
46/* indidevapi.h contains API declerations */
47#include "indidevapi.h"
48
49/* INDI Eventloop mechanism */
50#include "eventloop.h"
51
52/* INDI Common Routines */
53#include "indicom.h"
54
55/* Definitions */
56
57#define mydev           "Telescope Simulator"           /* Device name */
58#define MAIN_GROUP      "Main Control"                  /* Group name */
59#define SLEWRATE        1                               /* slew rate, degrees/s */
60#define POLLMS          250                             /* poll period, ms */
61#define SIDRATE         0.004178                        /* sidereal rate, degrees/s */
62
63/* Function protptypes */
64static void connectTelescope (void);
65static void mountSim (void *);
66
67/* operational info */
68static double targetRA;
69static double targetDEC;
70
71/* main connection switch
72  Note that the switch will appear to the user as On and Off (versus Connect and Disconnect in tutorial one)
73  Nevertheless, the members _names_ are still CONNECT and DISCONNECT and therefore this is a perfectly legal standard property decleration
74   */
75static ISwitch connectS[] = {
76    {"CONNECT",  "On",  ISS_OFF, 0, 0}, {"DISCONNECT", "Off", ISS_ON, 0, 0}};
77
78static ISwitchVectorProperty connectSP = { mydev, "CONNECTION", "Connection",  MAIN_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE,  connectS, NARRAY(connectS), "", 0 };
79
80/* Equatorial position. EQUATORIAL_EOD_COORD is one of INDI's reserved Standard  Properties */
81static INumber eqN[] = {
82                                /* 1st member is Right ascension */
83                                {"RA"                           /* 1st Number name */
84                                ,"RA  H:M:S"                    /* Number label */
85                                , "%10.6m"                      /* Format. Refer to the indiapi.h for details on number formats */
86                                ,0.                                     /* Minimum value */
87                                , 24.                                   /* Maximum value */
88                                , 0.                                    /* Steps */
89                                , 0.                                    /* Initial value */
90                                , 0                                     /* Pointer to parent, we don't use it, so set it to 0 */
91                                , 0                                     /* Auxiluary member, set it to 0 */
92                                , 0},                                   /* Autxiluar member, set it to 0 */
93                               
94                                /* 2nd member is Declination */
95                                {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0}
96};
97
98static INumberVectorProperty eqNP = {  mydev, "EQUATORIAL_EOD_COORD", "Equatorial JNow",  MAIN_GROUP , IP_RO, 0, IPS_IDLE,  eqN, NARRAY(eqN), "", 0};
99
100
101/* Equatorial EOD Coord Request. This property is for requesting changes to target equatorial coordinates. However, the CURRENT coordinates are reported in EQUATORIAL_EOD_COORDS above.*/
102static INumber eqNR[] = {{"RA" ,"RA  H:M:S" , "%10.6m" ,0. , 24., 0., 0., 0, 0, 0},
103                         {"DEC", "Dec D:M:S", "%10.6m", -90., 90., 0., 0., 0, 0, 0}};
104static INumberVectorProperty eqNPR = {  mydev, "EQUATORIAL_EOD_COORD_REQUEST", "Equatorial Request",  MAIN_GROUP , IP_RW, 0, IPS_IDLE,  eqNR, NARRAY(eqNR), "", 0};
105
106/* Property naming convention. All property names are lower case with a postfix to indicate their type. connectS is a switch,
107 * connectSP is a switch vector. eqN is a number, eqNP is a number property, and so on. While this is not strictly required, it makes the code easier to read. */
108
109#define currentRA       eqN[0].value            /* scope's current simulated RA, rads. Handy macro to right ascension from eqN[] */
110#define currentDec      eqN[1].value            /* scope's current simulated Dec, rads. Handy macro to declination from eqN[] */
111
112/********************************************
113 Property: Movement (Arrow keys on handset). North/South
114*********************************************/
115static ISwitch MovementNSS[]       = {{"MOTION_NORTH", "North", ISS_OFF, 0, 0}, {"MOTION_SOUTH", "South", ISS_OFF, 0, 0}};
116ISwitchVectorProperty MovementNSSP      = { mydev, "TELESCOPE_MOTION_NS", "North/South", MAIN_GROUP, IP_RW, ISR_ATMOST1, 0, IPS_IDLE, MovementNSS, NARRAY(MovementNSS), "", 0};
117
118/********************************************
119 Property: Movement (Arrow keys on handset). West/East
120*********************************************/
121static ISwitch MovementWES[]       = {{"MOTION_WEST", "West", ISS_OFF, 0, 0}, {"MOTION_EAST", "East", ISS_OFF, 0, 0}};
122ISwitchVectorProperty MovementWESP      = { mydev, "TELESCOPE_MOTION_WE", "West/East", MAIN_GROUP, IP_RW, ISR_ATMOST1, 0, IPS_IDLE, MovementWES, NARRAY(MovementWES), "", 0};
123
124/* Initlization routine */
125static void mountInit()
126{
127        static int inited;              /* set once mountInit is called */
128
129        if (inited)
130            return;
131       
132        /* start timer to simulate mount motion
133           The timer will call function mountSim after POLLMS milliseconds */
134        IEAddTimer (POLLMS, mountSim, NULL);
135
136        inited = 1;
137       
138}
139
140/* send client definitions of all properties */
141void ISGetProperties (const char *dev)
142{
143        if (dev && strcmp (mydev, dev))
144            return;
145
146        IDDefSwitch (&connectSP, NULL);
147        IDDefNumber (&eqNP, NULL);
148        IDDefNumber (&eqNPR, NULL);
149        IDDefSwitch (&MovementNSSP, NULL);
150        IDDefSwitch (&MovementWESP, NULL);
151       
152}
153
154void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
155{
156        return;
157}
158
159/* client is sending us a new value for a Numeric vector property */
160void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
161{
162        /* Make sure to initalize */
163        mountInit();
164       
165        /* ignore if not ours */
166        if (strcmp (dev, mydev))
167            return;
168
169        if (!strcmp (name, eqNPR.name)) {
170            /* new equatorial target coords */
171            double newra = 0, newdec = 0;
172            int i, nset;
173           
174            /* Check connectSP, if it is idle, then return */
175            if (connectSP.s == IPS_IDLE)
176            {
177                eqNPR.s = IPS_IDLE;
178                IDSetNumber(&eqNP, "Telescope is offline.");
179                return;
180            }
181           
182            for (nset = i = 0; i < n; i++) 
183            {
184                /* Find numbers with the passed names in the eqNP property */
185                INumber *eqp = IUFindNumber (&eqNPR, names[i]);
186               
187                /* If the number found is Right ascension (eqN[0]) then process it */
188                if (eqp == &eqNR[0])
189                {
190                    newra = (values[i]);
191                    nset += newra >= 0 && newra <= 24;
192                }
193                /* Otherwise, if the number found is Declination (eqN[1]) then process it */ 
194                else if (eqp == &eqNR[1]) {
195                    newdec = (values[i]);
196                    nset += newdec >= -90 && newdec <= 90;
197                }
198            } /* end for */
199           
200            /* Did we process the two numbers? */
201            if (nset == 2)
202            {
203                char r[32], d[32];
204               
205                /* Set the mount state to BUSY */
206                eqNP.s = IPS_BUSY;
207                eqNPR.s = IPS_BUSY;
208               
209                /* Set the new target coordinates */
210                targetRA = newra;
211                targetDEC = newdec;
212               
213                /* Convert the numeric coordinates to a sexagesmal string (H:M:S) */
214                fs_sexa (r, targetRA, 2, 3600);
215                fs_sexa (d, targetDEC, 3, 3600);
216               
217                IDSetNumber(&eqNP, NULL);
218                IDSetNumber(&eqNPR, "Moving to RA Dec %s %s", r, d);
219            }
220            /* We didn't process the two number correctly, report an error */
221            else 
222            {
223                /* Set property state to ALERT */
224                eqNPR.s = IPS_ALERT;
225               
226                IDSetNumber(&eqNP, "RA or Dec absent or bogus.");
227            }
228           
229            return;
230        }
231}
232
233/* client is sending us a new value for a Switch property */
234void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
235{
236        ISwitch *sp;
237
238        mountInit();
239       
240        /* ignore if not ours */
241        if (strcmp (dev, mydev))
242            return;
243
244       
245        if (!strcmp(name, connectSP.name))
246        {
247                /* We update switches. This is different from the way we used to update switches in tutorial 1. This is
248                * to illustrate that there are several ways to update the switches. Here, we try to find the switch with names[0],
249                * and if found, we update its state to states[0] and call connectTelescope(). We must call IUResetSwitches to erase any previous history */
250         
251                sp = IUFindSwitch (&connectSP, names[0]);
252       
253                if (sp)
254                {
255                        IUResetSwitch(&connectSP);
256                        sp->s = states[0];
257                        connectTelescope();
258                }
259        }
260        else if (! strcmp(name, MovementNSSP.name)) {
261                sp = IUFindSwitch (&MovementNSSP, names[0]);
262                if (sp) {
263                        IUResetSwitch(&MovementNSSP);
264                        sp->s = states[0];
265                        show_runtime(sp->s);
266                        IDSetSwitch (&MovementNSSP, "Toggle North/South.");
267                }
268        }
269        else if (! strcmp(name, MovementWESP.name)) {
270                sp = IUFindSwitch (&MovementWESP, names[0]);
271                if (sp) {
272                        IUResetSwitch(&MovementWESP);
273                        sp->s = states[0];
274                        show_runtime(sp->s);
275                        IDSetSwitch (&MovementWESP, "Toggle West/East.");
276                }
277        }
278       
279       
280}
281
282/* update the "mount" over time */
283void mountSim (void *p)
284{
285        static struct timeval ltv;
286        struct timeval tv;
287        double dt, da, dx;
288        int nlocked;
289
290        /* If telescope is not on, do not simulate */
291        if (connectSP.s == IPS_IDLE)
292        {
293                IEAddTimer (POLLMS, mountSim, NULL);
294                return;
295        }
296
297        /* update elapsed time since last poll, don't presume exactly POLLMS */
298        gettimeofday (&tv, NULL);
299       
300        if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
301            ltv = tv;
302           
303        dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec)/1e6;
304        ltv = tv;
305        da = SLEWRATE*dt;
306
307        /* Process per current state. We check the state of EQUATORIAL_EOD_COORDS_REQUEST and act acoordingly */
308        switch (eqNPR.s)
309        {
310       
311        /* #1 State is idle, update telesocpe at sidereal rate */
312        case IPS_IDLE:
313            /* RA moves at sidereal, Dec stands still */
314            currentRA += (SIDRATE*dt/15.);
315            IDSetNumber(&eqNP, NULL);
316            break;
317
318        case IPS_BUSY:
319            /* slewing - nail it when both within one pulse @ SLEWRATE */
320            nlocked = 0;
321
322            dx = targetRA - currentRA;
323           
324            if (fabs(dx) <= da)
325            {
326                currentRA = targetRA;
327                nlocked++;
328            }
329            else if (dx > 0)
330                currentRA += da/15.;
331            else 
332                currentRA -= da/15.;
333           
334
335            dx = targetDEC - currentDec;
336            if (fabs(dx) <= da)
337            {
338                currentDec = targetDEC;
339                nlocked++;
340            }
341            else if (dx > 0)
342              currentDec += da;
343            else
344              currentDec -= da;
345
346            if (nlocked == 2)
347            {
348                eqNP.s = IPS_OK;
349                eqNPR.s = IPS_OK;
350                IDSetNumber(&eqNP, NULL);
351                IDSetNumber(&eqNPR, "Now tracking");
352            } else
353                IDSetNumber(&eqNP, NULL);
354
355            break;
356
357        case IPS_OK:
358            /* tracking */
359           IDSetNumber(&eqNP, NULL);
360            break;
361
362        case IPS_ALERT:
363            break;
364        }
365
366        /* again */
367        IEAddTimer (POLLMS, mountSim, NULL);
368}
369
370/* Note that we must define ISNewBLOB and ISSnoopDevice even if we don't use them, otherwise, the driver will NOT compile */
371void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) {}
372void ISSnoopDevice (XMLEle *root) {}
373
374
375static void connectTelescope ()
376{
377        if (connectS[0].s == ISS_ON)
378        {
379            connectSP.s   = IPS_OK;
380            IDSetSwitch (&connectSP, "Telescope is connected.");
381        } 
382        else
383        {
384            connectSP.s   = IPS_IDLE;
385            IDSetSwitch (&connectSP, "Telescope is disconnected.");
386        }
387}
Note: See TracBrowser for help on using the repository browser.