source: BAORadio/libindi/libindi/examples/tutorial_two.c @ 700

Last change on this file since 700 was 504, checked in by frichard, 13 years ago

-Version 0.8 de libini
-Formule de Marc
-Nouvelles fonctionnalités (goto nom-de l'objet etc...)

File size: 11.4 KB
RevLine 
[490]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.*/
[501]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}};
[504]104static INumberVectorProperty eqNPR = {  mydev, "EQUATORIAL_EOD_COORD_REQUEST", "Equatorial Request",  MAIN_GROUP , IP_WO, 0, IPS_IDLE,  eqNR, NARRAY(eqNR), "", 0};
[490]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
[504]124static ISwitch OnCoordSetS[] = {{"TRACK", "Track", ISS_ON, 0, 0}};
125static ISwitchVectorProperty OnCoordSetSP = {mydev, "ON_COORD_SET", "On Set", MAIN_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_OK, OnCoordSetS, NARRAY(OnCoordSetS), "", 0};
126
127
[490]128/* Initlization routine */
129static void mountInit()
130{
131        static int inited;              /* set once mountInit is called */
132
133        if (inited)
134            return;
135       
136        /* start timer to simulate mount motion
137           The timer will call function mountSim after POLLMS milliseconds */
138        IEAddTimer (POLLMS, mountSim, NULL);
139
140        inited = 1;
141       
142}
143
144/* send client definitions of all properties */
145void ISGetProperties (const char *dev)
146{
147        if (dev && strcmp (mydev, dev))
148            return;
149
150        IDDefSwitch (&connectSP, NULL);
151        IDDefNumber (&eqNP, NULL);
152        IDDefNumber (&eqNPR, NULL);
153        IDDefSwitch (&MovementNSSP, NULL);
154        IDDefSwitch (&MovementWESP, NULL);
[504]155        IDDefSwitch(&OnCoordSetSP, NULL);
[490]156       
157}
158
159void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
160{
161        return;
162}
163
164/* client is sending us a new value for a Numeric vector property */
165void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
166{
167        /* Make sure to initalize */
168        mountInit();
169       
170        /* ignore if not ours */
171        if (strcmp (dev, mydev))
172            return;
173
174        if (!strcmp (name, eqNPR.name)) {
175            /* new equatorial target coords */
176            double newra = 0, newdec = 0;
177            int i, nset;
178           
179            /* Check connectSP, if it is idle, then return */
180            if (connectSP.s == IPS_IDLE)
181            {
182                eqNPR.s = IPS_IDLE;
183                IDSetNumber(&eqNP, "Telescope is offline.");
184                return;
185            }
186           
187            for (nset = i = 0; i < n; i++) 
188            {
189                /* Find numbers with the passed names in the eqNP property */
190                INumber *eqp = IUFindNumber (&eqNPR, names[i]);
191               
192                /* If the number found is Right ascension (eqN[0]) then process it */
193                if (eqp == &eqNR[0])
194                {
195                    newra = (values[i]);
196                    nset += newra >= 0 && newra <= 24;
197                }
198                /* Otherwise, if the number found is Declination (eqN[1]) then process it */ 
199                else if (eqp == &eqNR[1]) {
200                    newdec = (values[i]);
201                    nset += newdec >= -90 && newdec <= 90;
202                }
203            } /* end for */
204           
205            /* Did we process the two numbers? */
206            if (nset == 2)
207            {
208                char r[32], d[32];
209               
210                /* Set the mount state to BUSY */
211                eqNP.s = IPS_BUSY;
212                eqNPR.s = IPS_BUSY;
213               
214                /* Set the new target coordinates */
215                targetRA = newra;
216                targetDEC = newdec;
217               
218                /* Convert the numeric coordinates to a sexagesmal string (H:M:S) */
219                fs_sexa (r, targetRA, 2, 3600);
220                fs_sexa (d, targetDEC, 3, 3600);
221               
222                IDSetNumber(&eqNP, NULL);
223                IDSetNumber(&eqNPR, "Moving to RA Dec %s %s", r, d);
224            }
225            /* We didn't process the two number correctly, report an error */
226            else 
227            {
228                /* Set property state to ALERT */
229                eqNPR.s = IPS_ALERT;
230               
231                IDSetNumber(&eqNP, "RA or Dec absent or bogus.");
232            }
233           
234            return;
235        }
236}
237
238/* client is sending us a new value for a Switch property */
239void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
240{
241        ISwitch *sp;
242
243        mountInit();
244       
245        /* ignore if not ours */
246        if (strcmp (dev, mydev))
247            return;
248
249       
250        if (!strcmp(name, connectSP.name))
251        {
252                /* We update switches. This is different from the way we used to update switches in tutorial 1. This is
253                * to illustrate that there are several ways to update the switches. Here, we try to find the switch with names[0],
254                * and if found, we update its state to states[0] and call connectTelescope(). We must call IUResetSwitches to erase any previous history */
255         
256                sp = IUFindSwitch (&connectSP, names[0]);
257       
258                if (sp)
259                {
260                        IUResetSwitch(&connectSP);
261                        sp->s = states[0];
262                        connectTelescope();
263                }
264        }
265        else if (! strcmp(name, MovementNSSP.name)) {
266                sp = IUFindSwitch (&MovementNSSP, names[0]);
267                if (sp) {
268                        IUResetSwitch(&MovementNSSP);
269                        sp->s = states[0];
270                        show_runtime(sp->s);
271                        IDSetSwitch (&MovementNSSP, "Toggle North/South.");
272                }
273        }
274        else if (! strcmp(name, MovementWESP.name)) {
275                sp = IUFindSwitch (&MovementWESP, names[0]);
276                if (sp) {
277                        IUResetSwitch(&MovementWESP);
278                        sp->s = states[0];
279                        show_runtime(sp->s);
280                        IDSetSwitch (&MovementWESP, "Toggle West/East.");
281                }
282        }
[504]283        else if (! strcmp(name, OnCoordSetSP.name))
284        {
285            OnCoordSetSP.s = IPS_OK;
286            IDSetSwitch(&OnCoordSetSP, NULL);
287        }
[490]288       
289       
290}
291
292/* update the "mount" over time */
293void mountSim (void *p)
294{
295        static struct timeval ltv;
296        struct timeval tv;
297        double dt, da, dx;
298        int nlocked;
299
300        /* If telescope is not on, do not simulate */
301        if (connectSP.s == IPS_IDLE)
302        {
303                IEAddTimer (POLLMS, mountSim, NULL);
304                return;
305        }
306
307        /* update elapsed time since last poll, don't presume exactly POLLMS */
308        gettimeofday (&tv, NULL);
309       
310        if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
311            ltv = tv;
312           
313        dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec)/1e6;
314        ltv = tv;
315        da = SLEWRATE*dt;
316
317        /* Process per current state. We check the state of EQUATORIAL_EOD_COORDS_REQUEST and act acoordingly */
318        switch (eqNPR.s)
319        {
320       
321        /* #1 State is idle, update telesocpe at sidereal rate */
322        case IPS_IDLE:
323            /* RA moves at sidereal, Dec stands still */
324            currentRA += (SIDRATE*dt/15.);
325            IDSetNumber(&eqNP, NULL);
326            break;
327
328        case IPS_BUSY:
329            /* slewing - nail it when both within one pulse @ SLEWRATE */
330            nlocked = 0;
331
332            dx = targetRA - currentRA;
333           
334            if (fabs(dx) <= da)
335            {
336                currentRA = targetRA;
337                nlocked++;
338            }
339            else if (dx > 0)
340                currentRA += da/15.;
341            else 
342                currentRA -= da/15.;
343           
344
345            dx = targetDEC - currentDec;
346            if (fabs(dx) <= da)
347            {
348                currentDec = targetDEC;
349                nlocked++;
350            }
351            else if (dx > 0)
352              currentDec += da;
353            else
354              currentDec -= da;
355
356            if (nlocked == 2)
357            {
358                eqNP.s = IPS_OK;
359                eqNPR.s = IPS_OK;
360                IDSetNumber(&eqNP, NULL);
361                IDSetNumber(&eqNPR, "Now tracking");
362            } else
363                IDSetNumber(&eqNP, NULL);
364
365            break;
366
367        case IPS_OK:
368            /* tracking */
369           IDSetNumber(&eqNP, NULL);
370            break;
371
372        case IPS_ALERT:
373            break;
374        }
375
376        /* again */
377        IEAddTimer (POLLMS, mountSim, NULL);
378}
379
380/* Note that we must define ISNewBLOB and ISSnoopDevice even if we don't use them, otherwise, the driver will NOT compile */
381void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) {}
382void ISSnoopDevice (XMLEle *root) {}
383
384
385static void connectTelescope ()
386{
387        if (connectS[0].s == ISS_ON)
388        {
389            connectSP.s   = IPS_OK;
390            IDSetSwitch (&connectSP, "Telescope is connected.");
391        } 
392        else
393        {
394            connectSP.s   = IPS_IDLE;
395            IDSetSwitch (&connectSP, "Telescope is disconnected.");
396        }
397}
Note: See TracBrowser for help on using the repository browser.