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

Last change on this file since 654 was 501, checked in by frichard, 15 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.