source: trunk/FACT++/src/drivectrl.cc@ 18358

Last change on this file since 18358 was 18358, checked in by tbretz, 10 years ago
Updates to the failed states.
File size: 118.8 KB
Line 
1#include <boost/regex.hpp>
2#include <boost/algorithm/string.hpp>
3
4#ifdef HAVE_SQL
5#include "Database.h"
6#endif
7
8#include "FACT.h"
9#include "Dim.h"
10#include "Event.h"
11#include "Shell.h"
12#include "StateMachineDim.h"
13#include "StateMachineAsio.h"
14#include "Connection.h"
15#include "LocalControl.h"
16#include "Configuration.h"
17#include "Timers.h"
18#include "Console.h"
19
20#include "HeadersDrive.h"
21
22#include "pal.h"
23#include "externals/nova.h"
24
25namespace ba = boost::asio;
26namespace bs = boost::system;
27
28using namespace std;
29using namespace Drive;
30
31// ------------------------------------------------------------------------
32
33// The Nova classes are in degree. This is to be used in rad
34struct RaDec
35{
36 double ra; // [rad]
37 double dec; // [rad]
38 RaDec() : ra(0), dec(0) { }
39 RaDec(double _ra, double _dec) : ra(_ra), dec(_dec) { }
40};
41
42struct RaDecHa : RaDec
43{
44 double ha; // [rad]
45 RaDecHa() : ha(0) { }
46 RaDecHa(double _ra, double _dec, double _ha) : RaDec(_ra, _dec), ha(_ha) { }
47};
48
49struct Local
50{
51 double zd;
52 double az;
53
54 Local(double _zd=0, double _az=0) : zd(_zd), az(_az) { }
55};
56
57struct Velocity : Local
58{
59 Velocity(double _zd=0, double _az=0) : Local(_zd, _az) { }
60 Velocity operator/(double f) const { return Velocity(zd/f, az/f); }
61 Velocity operator*(double f) const { return Velocity(zd*f, az*f); }
62};
63
64struct Encoder : Local // [units: revolutions]
65{
66 Encoder(double _zd=0, double _az=0) : Local(_zd, _az) { }
67
68 Encoder &operator*=(double f) { zd*=f; az*=f; return *this; }
69 Encoder &operator-=(const Encoder &enc) { zd-=enc.zd; az-=enc.az; return *this; }
70 Encoder operator*(double f) const { return Encoder(zd*f, az*f); }
71 Velocity operator/(double t) const { return Velocity(zd/t, az/t); }
72 Encoder Abs() const { return Encoder(fabs(zd), fabs(az)); }
73};
74
75struct ZdAz : Local // [units: rad]
76{
77 ZdAz(double _zd=0, double _az=0) : Local(_zd, _az) { }
78 ZdAz operator*(const double &f) const { return ZdAz(zd*f, az*f); }
79};
80
81struct Acceleration : Local
82{
83 Acceleration(double _zd=0, double _az=0) : Local(_zd, _az) { }
84 bool operator>(const Acceleration &a) const
85 {
86 return zd>a.zd || az>a.az;
87 }
88};
89
90Encoder operator-(const Encoder &a, const Encoder &b)
91{
92 return Encoder(a.zd-b.zd, a.az-b.az);
93}
94Velocity operator-(const Encoder &a, const Velocity &b)
95{
96 return Velocity(a.zd-b.zd, a.az-b.az);
97}
98Velocity operator-(const Velocity &a, const Velocity &b)
99{
100 return Velocity(a.zd-b.zd, a.az-b.az);
101}
102Encoder operator/(const Encoder &a, const Encoder &b)
103{
104 return Encoder(a.zd/b.zd, a.az/b.az);
105}
106
107struct Weather
108{
109 float hum;
110 float temp;
111 float press;
112 Time time;
113};
114
115struct Source
116{
117 Source() : ra(0), dec(0), mag(0), offset(0)
118 {
119 angles[0] = -90;
120 angles[1] = 90;
121 }
122
123 string name;
124 double ra; // [h]
125 double dec; // [deg]
126 double mag;
127
128 double offset;
129 array<double, 2> angles;
130};
131
132enum Planets_t
133{
134 kENone = -1,
135 kESun = 0,
136 kEMercury = 1,
137 kEVenus = 2,
138 kEMoon = 3, // earth moon barycentre
139 kEMars = 4,
140 kEJupiter = 5,
141 kESaturn = 6,
142 kEUranus = 7,
143 kENeptune = 8,
144 kEPluto = 9,
145};
146
147// ------------------------------------------------------------------------
148
149struct PointingSetup
150{
151 Source source; // Informations about source to track [h/deg]
152 Planets_t planet; // Id of the planet if tracking a planet
153 double start; // Starting time of wobble observation [mjd]
154 double orbit_period; // Time for one revolution (0:off) [day]
155 double wobble_offset; // Distance of wobble position [rad]
156 double wobble_angle; // Starting phi angle of wobble observation [rad]
157
158 PointingSetup(Planets_t p=kENone) : planet(p), start(Time::none), orbit_period(0) { }
159};
160
161struct PointingData
162{
163 // Pointing direction of the opticl axis of the telescope
164 RaDec source; // Informations about source to track [rad/rad]
165 RaDec pointing; // Catalog coordinates (J2000, FK5) [rad/rad] pointing position
166 RaDecHa apparent; // Apparent position on the sky [rad/rad]
167 ZdAz sky; // Apparent position on the sky [rad/rad]
168 Encoder mount; // Encoder position corresponding to 'sky' [deg/deg]
169 double mjd;
170};
171
172class PointingModel
173{
174private:
175 double fIe; // [rad] Index Error in Elevation
176 double fIa; // [rad] Index Error in Azimuth
177 double fFlop; // [rad] Vertical Sag
178 double fNpae; // [rad] Az-El Nonperpendicularity
179 double fCa; // [rad] Left-Right Collimation Error
180 double fAn; // [rad] Azimuth Axis Misalignment (N-S, 1st order)
181 double fAw; // [rad] Azimuth Axis Misalignment (E-W, 1st order)
182 double fAn2; // [rad] Azimuth Axis Misalignment (N-S, 2nd order)
183 double fAw2; // [rad] Azimuth Axis Misalignment (E-W, 2nd order)
184 double fTf; // [rad] Tube fluxture (sin)
185 double fTx; // [rad] Tube fluxture (tan)
186 double fNrx; // [rad] Nasmyth rotator displacement, horizontal
187 double fNry; // [rad] Nasmyth rotator displacement, vertical
188 double fCrx; // [rad] Alt/Az Coude Displacement (N-S)
189 double fCry; // [rad] Alt/Az Coude Displacement (E-W)
190 double fEces; // [rad] Elevation Centering Error (sin)
191 double fAces; // [rad] Azimuth Centering Error (sin)
192 double fEcec; // [rad] Elevation Centering Error (cos)
193 double fAcec; // [rad] Azimuth Centering Error (cos)
194
195public:
196 /*
197 Source source; // Informations about source to track
198 Planets_t planet; // Id of the planet if tracking a planet
199 double start; // Starting time of wobble observation [mjd]
200 double orbit_period; // Time for one revolution (0:off) [day]
201 double wobble_offset; // Distance of wobble position [rad]
202 double wobble_angle; // Starting phi angle of wobble observation [rad]
203
204 // Source to track with all constraints
205 //RaDec source; // Catalog coordinates (J2000, FK5) [rad/rad] source position
206
207 // Pointing direction of the opticl axis of the telescope
208 RaDec pointing; // Catalog coordinates (J2000, FK5) [rad/rad] pointing position
209 RaDecHa apparent; // Apparent position on the sky [rad/rad]
210 ZdAz sky; // Apparent position on the sky [rad/rad]
211 Encoder mount; // Encoder position corresponding to 'sky' [deg/deg]
212 double mjd;
213 */
214
215 void Load(const string &name)
216 {
217 /*
218 ! MMT 1987 July 8
219 ! T 36 7.3622 41.448 -0.0481
220 ! IA -37.5465 20.80602
221 ! IE -13.9180 1.25217
222 ! NPAE +7.0751 26.44763
223 ! CA -6.9149 32.05358
224 ! AN +0.5053 1.40956
225 ! AW -2.2016 1.37480
226 ! END
227 */
228
229 ifstream fin(name);
230 if (!fin)
231 throw runtime_error("Cannot open file "+name+": "+strerror(errno));
232
233 map<string,double> coeff;
234
235 string buf;
236 while (getline(fin, buf))
237 {
238 buf = Tools::Trim(buf);
239
240 vector<string> vec;
241 boost::split(vec, buf, boost::is_any_of(" "), boost::token_compress_on);
242 if (vec.size()<2)
243 continue;
244
245 coeff[vec[0]] = atof(vec[1].c_str()) * M_PI/180;
246 }
247
248 fIe = coeff["IE"]; // [rad] Index Error in Elevation
249 fIa = coeff["IA"]; // [rad] Index Error in Azimuth
250 fFlop = coeff["FLOP"]; // [rad] Vertical Sag
251 fNpae = coeff["NPAE"]; // [rad] Az-El Nonperpendicularity
252 fCa = coeff["CA"]; // [rad] Left-Right Collimation Error
253 fAn = coeff["AN"]; // [rad] Azimuth Axis Misalignment (N-S, 1st order)
254 fAw = coeff["AW"]; // [rad] Azimuth Axis Misalignment (E-W, 1st order)
255 fAn2 = coeff["AN2"]; // [rad] Azimuth Axis Misalignment (N-S, 2nd order)
256 fAw2 = coeff["AW2"]; // [rad] Azimuth Axis Misalignment (E-W, 2nd order)
257 fTf = coeff["TF"]; // [rad] Tube fluxture (sin)
258 fTx = coeff["TX"]; // [rad] Tube fluxture (tan)
259 fNrx = coeff["NRX"]; // [rad] Nasmyth rotator displacement, horizontal
260 fNry = coeff["NRY"]; // [rad] Nasmyth rotator displacement, vertical
261 fCrx = coeff["CRX"]; // [rad] Alt/Az Coude Displacement (N-S)
262 fCry = coeff["CRY"]; // [rad] Alt/Az Coude Displacement (E-W)
263 fEces = coeff["ECES"]; // [rad] Elevation Centering Error (sin)
264 fAces = coeff["ACES"]; // [rad] Azimuth Centering Error (sin)
265 fEcec = coeff["ECEC"]; // [rad] Elevation Centering Error (cos)
266 fAcec = coeff["ACEC"]; // [rad] Azimuth Centering Error (cos)
267 }
268
269 struct AltAz
270 {
271 double alt;
272 double az;
273
274 AltAz(double _alt, double _az) : alt(_alt), az(_az) { }
275 AltAz(const ZdAz &za) : alt(M_PI/2-za.zd), az(za.az) { }
276
277 AltAz &operator+=(const AltAz &aa) { alt += aa.alt; az+=aa.az; return *this; }
278 AltAz &operator-=(const AltAz &aa) { alt -= aa.alt; az-=aa.az; return *this; }
279 };
280
281 double Sign(double val, double alt) const
282 {
283 // Some pointing corrections are defined as Delta ZA, which
284 // is (P. Wallace) defined [0,90]deg while Alt is defined
285 // [0,180]deg
286 return (M_PI/2-alt < 0 ? -val : val);
287 }
288/*
289 AltAz CalcAnAw(const AltAz &p) const
290 {
291 // Corrections for AN and AW without approximations
292 // as done by Patrick Wallace. The approximation cannot
293 // be used for MAGIC because the correction angle
294 // AW (~1.5deg) is not small enough.
295
296 const double theta0 = M_PI/2-p.alt;
297 const double phi0 = p.az;
298
299 const double st = sin(theta0);
300 const double ct = cos(theta0);
301 const double sp = sin(phi0);
302 const double cp = cos(phi0);
303
304 double x = st * cp;
305 double y = st * sp;
306 double z = ct;
307
308 // RotateY
309 const double sy = sin(fAn);
310 const double cy = cos(fAn);
311 const double zz = z;
312 z = cy*zz - sy*x;
313 x = sy*zz + cy*x;
314
315 // RotateX
316 const double sx = sin(fAw);
317 const double cx = cos(fAw);
318 const double yy = y;
319 y = cx*yy - sx*z;
320 z = sx*yy + cx*z;
321
322 const double xy = hypot(x, y);
323 const double theta1 = atan2(xy, z);
324 const double phi1 = atan2(y, x);
325
326 double dalt = theta0 - theta1;
327 double daz = phi1 - phi0;
328
329 if (daz>M_PI)
330 daz -= 2*M_PI;
331 if (daz<-M_PI)
332 daz += 2*M_PI;
333
334 return AltAz(dalt, daz);
335 }
336*/
337 Encoder SkyToMount(AltAz p)
338 {
339 const AltAz CRX(-fCrx*sin(p.az-p.alt), fCrx*cos(p.az-p.alt)/cos(p.alt));
340 const AltAz CRY(-fCry*cos(p.az-p.alt), -fCry*sin(p.az-p.alt)/cos(p.alt));
341 p += CRX;
342 p += CRY;
343
344 const AltAz NRX(fNrx*sin(p.alt), -fNrx);
345 const AltAz NRY(fNry*cos(p.alt), -fNry*tan(p.alt));
346 p += NRX;
347 p += NRY;
348
349 const AltAz CES(-fEces*sin(p.alt), -fAces*sin(p.az));
350 const AltAz CEC(-fEcec*cos(p.alt), -fAcec*cos(p.az));
351 p += CES;
352 p += CEC;
353
354 const AltAz TX(Sign(fTx/tan(p.alt), p.alt), 0);
355 const AltAz TF(Sign(fTf*cos(p.alt), p.alt), 0);
356 //p += TX;
357 p += TF;
358
359 const AltAz CA(0, -fCa/cos(p.alt));
360 p += CA;
361
362 const AltAz NPAE(0, -fNpae*tan(p.alt));
363 p += NPAE;
364
365 const AltAz AW2( fAw2*sin(p.az*2), -fAw2*cos(p.az*2)*tan(p.alt));
366 const AltAz AN2(-fAn2*cos(p.az*2), -fAn2*sin(p.az*2)*tan(p.alt));
367 const AltAz AW1( fAw *sin(p.az), -fAw *cos(p.az) *tan(p.alt));
368 const AltAz AN1(-fAn *cos(p.az), -fAn *sin(p.az) *tan(p.alt));
369 p += AW2;
370 p += AN2;
371 p += AW1;
372 p += AN1;
373
374 const AltAz FLOP(Sign(fFlop, p.alt), 0);
375 p += FLOP;
376
377 const AltAz I(fIe, fIa);
378 p += I;
379
380 return Encoder(90 - p.alt*180/M_PI, p.az *180/M_PI);
381 }
382
383 ZdAz MountToSky(const Encoder &mnt) const
384 {
385 AltAz p(M_PI/2-mnt.zd*M_PI/180, mnt.az*M_PI/180);
386
387 const AltAz I(fIe, fIa);
388 p -= I;
389
390 const AltAz FLOP(Sign(fFlop, p.alt), 0);
391 p -= FLOP;
392
393 const AltAz AW1( fAw *sin(p.az), -fAw *cos(p.az) *tan(p.alt));
394 const AltAz AN1(-fAn *cos(p.az), -fAn *sin(p.az) *tan(p.alt));
395 const AltAz AW2( fAw2*sin(p.az*2), -fAw2*cos(p.az*2)*tan(p.alt));
396 const AltAz AN2(-fAn2*cos(p.az*2), -fAn2*sin(p.az*2)*tan(p.alt));
397 p -= AW1;
398 p -= AN1;
399 p -= AW2;
400 p -= AN2;
401
402 const AltAz NPAE(0, -fNpae*tan(p.alt));
403 p -= NPAE;
404
405 const AltAz CA(0, -fCa/cos(p.alt));
406 p -= CA;
407
408 const AltAz TF(Sign(fTf*cos(p.alt), p.alt), 0);
409 const AltAz TX(Sign(fTx/tan(p.alt), p.alt), 0);
410 p -= TF;
411 //p -= TX;
412
413 const AltAz CEC(-fEcec*cos(p.alt), -fAcec*cos(p.az));
414 const AltAz CES(-fEces*sin(p.alt), -fAces*sin(p.az));
415 p -= CEC;
416 p -= CES;
417
418 const AltAz NRY(fNry*cos(p.alt), -fNry*tan(p.alt));
419 const AltAz NRX(fNrx*sin(p.alt), -fNrx);
420 p -= NRY;
421 p -= NRX;
422
423 const AltAz CRY(-fCry*cos(p.az-p.alt), -fCry*sin(p.az-p.alt)/cos(p.alt));
424 const AltAz CRX(-fCrx*sin(p.az-p.alt), fCrx*cos(p.az-p.alt)/cos(p.alt));
425 p -= CRY;
426 p -= CRX;
427
428 return ZdAz(M_PI/2-p.alt, p.az);
429 }
430
431 PointingData CalcPointingPos(const PointingSetup &setup, double _mjd, const Weather &weather, uint16_t timeout, bool tpoint=false)
432 {
433 PointingData out;
434 out.mjd = _mjd;
435
436 const double elong = Nova::ORM().lng * M_PI/180;
437 const double lat = Nova::ORM().lat * M_PI/180;
438 const double height = 2200;
439
440 const bool valid = weather.time+boost::posix_time::seconds(timeout) > Time();
441
442 const double temp = valid ? weather.temp : 10;
443 const double hum = valid ? weather.hum : 0.25;
444 const double press = valid ? weather.press : 780;
445
446 const double dtt = palDtt(_mjd); // 32.184 + 35
447
448 const double tdb = _mjd + dtt/3600/24;
449 const double dut = 0;
450
451 // prepare calculation: Mean Place to geocentric apperent
452 // (UTC would also do, except for the moon?)
453 double fAmprms[21];
454 palMappa(2000.0, tdb, fAmprms); // Epoche, TDB
455
456 // prepare: Apperent to observed place
457 double fAoprms[14];
458 palAoppa(_mjd, dut, // mjd, Delta UT=UT1-UTC
459 elong, lat, height, // long, lat, height
460 0, 0, // polar motion x, y-coordinate (radians)
461 273.155+temp, press, hum, // temp, pressure, humidity
462 0.40, 0.0065, // wavelength, tropo lapse rate
463 fAoprms);
464
465 out.source.ra = setup.source.ra * M_PI/ 12;
466 out.source.dec = setup.source.dec * M_PI/180;
467
468 if (setup.planet!=kENone)
469 {
470 // coordinates of planet: topocentric, equatorial, J2000
471 // One can use TT instead of TDB for all planets (except the moon?)
472 double ra, dec, diam;
473 palRdplan(tdb, setup.planet, elong, lat, &ra, &dec, &diam);
474
475 // ---- apparent to mean ----
476 palAmpqk(ra, dec, fAmprms, &out.source.ra, &out.source.dec);
477 }
478
479 if (setup.wobble_offset<=0 || tpoint)
480 {
481 out.pointing.dec = out.source.dec;
482 out.pointing.ra = out.source.ra;
483 }
484 else
485 {
486 const double dphi =
487 setup.orbit_period==0 ? 0 : 2*M_PI*(_mjd-setup.start)/setup.orbit_period;
488
489 const double phi = setup.wobble_angle + dphi;
490
491 const double cosdir = cos(phi);
492 const double sindir = sin(phi);
493 const double cosoff = cos(setup.wobble_offset);
494 const double sinoff = sin(setup.wobble_offset);
495 const double cosdec = cos(out.source.dec);
496 const double sindec = sin(out.source.dec);
497
498 const double sintheta = sindec*cosoff + cosdec*sinoff*cosdir;
499
500 const double costheta = sintheta>1 ? 0 : sqrt(1 - sintheta*sintheta);
501
502 const double cosdeltara = (cosoff - sindec*sintheta)/(cosdec*costheta);
503 const double sindeltara = sindir*sinoff/costheta;
504
505 out.pointing.dec = asin(sintheta);
506 out.pointing.ra = atan2(sindeltara, cosdeltara) + out.source.ra;
507 }
508
509 // ---- Mean to apparent ----
510 double r=0, d=0;
511 palMapqkz(out.pointing.ra, out.pointing.dec, fAmprms, &r, &d);
512
513 //
514 // Doesn't work - don't know why
515 //
516 // slaMapqk (radec.Ra(), radec.Dec(), rdpm.Ra(), rdpm.Dec(),
517 // 0, 0, (double*)fAmprms, &r, &d);
518 //
519
520 // -- apparent to observed --
521 palAopqk(r, d, fAoprms,
522 &out.sky.az, // observed azimuth (radians: N=0,E=90) [-pi, pi]
523 &out.sky.zd, // observed zenith distance (radians) [-pi/2, pi/2]
524 &out.apparent.ha, // observed hour angle (radians)
525 &out.apparent.dec, // observed declination (radians)
526 &out.apparent.ra); // observed right ascension (radians)
527
528 // ----- fix ambiguity -----
529 if (out.sky.zd<0)
530 {
531 out.sky.zd = -out.sky.zd;
532 out.sky.az += out.sky.az<0 ? M_PI : -M_PI;
533 }
534
535 // Star culminating behind zenith and Az between ~90 and ~180deg
536 if (out.source.dec<lat && out.sky.az>0)
537 out.sky.az -= 2*M_PI;
538
539 out.mount = SkyToMount(out.sky);
540
541 return out;
542 }
543
544 //PointingModel(double ra, double dec) : planet(kENone), pointing(ra, dec) { }
545 //PointingModel(const Nova::ZdAzPosn &za) : planet(kENone), sky(za.zd* M_PI/180,za.az* M_PI/180) { SkyToMount(); }
546 //PointingModel(const Nova::EquPosn &rd) : planet(kENone), pointing(rd.ra* M_PI/180, rd.dec* M_PI/180) { }
547};
548
549// ------------------------------------------------------------------------
550
551/*
552#define kEMERGENCY 0x1
553#define kPDO1_TX 0x3
554#define kPDO2_TX 0x5
555#define kPDO3_TX 0x7
556#define kPDO4_TX 0x9
557#define kSDO_RX 0xb // this is used to set data of the shaft encoder
558#define kSDO_TX 0xc // this is used to request data from the shaft encoder
559#define kNodeguard 0xe
560
561// NMT: no answer to NMT command
562// cob-id=0, command (byte), id (byte)
563#define kNMT_START 0x01 // change to operational state (start)
564#define kNMT_STOP 0x02 // change to prepared state (stop)
565#define kNMT_PREOP 0x80 // enter pre operational state
566#define kNMT_RESET 0x81 // reset node (set parameter to power on values)
567#define kNMT_REINIT 0x82 // reset communication of node (set communication parameters to power on values)
568
569// command for SDOs
570#define kSDO_LEN4 0x3
571#define kSDO_LEN2 0xb
572#define kSDO_LEN1 0xf
573
574#define kSDO_RXm4 0x22 // this is used with SDO_TX to send a maximum of 4 bytes
575#define kSDO_RX4 0x20|kSDO_LEN4 // this is used with SDO_TX to send 4 bytes
576#define kSDO_RX2 0x20|kSDO_LEN2 // this is used with SDO_TX to send 2 bytes
577#define kSDO_RX1 0x20|kSDO_LEN1 // this is used with SDO_TX to send 1 byte
578#define kSDO_RX_DATA 0x40 // this is used to request parameters from the encoder
579#define kSDO_TX4 0x40|kSDO_LEN4 // answer to 0x40 with 4 bytes of data
580#define kSDO_TX3 0x40|kSDO_LEN2 // answer to 0x40 with 2 bytes of data
581#define kSDO_TX1 0x40|kSDO_LEN1 // answer to 0x40 with 1 byte of data
582#define kSDO_TX_OK 0x60 // answer to a SDO_TX message
583#define kSDO_TX_ERROR 0x80 // error message (instead of 0x60)
584*/
585
586
587
588class ConnectionDrive : public Connection
589{
590 uint16_t fVerbosity;
591
592public:
593 virtual void UpdatePointing(const Time &, const array<double, 2> &)
594 {
595 }
596
597 virtual void UpdateTracking(const Time &, const array<double, 12> &)
598 {
599 }
600
601 virtual void UpdateStatus(const Time &, const array<uint8_t, 3> &)
602 {
603 }
604
605 virtual void UpdateTPoint(const Time &, const DimTPoint &, const string &)
606 {
607 }
608
609 virtual void UpdateSource(const Time &, const string &, bool)
610 {
611 }
612 virtual void UpdateSource(const Time &,const array<double, 5> &, const string& = "")
613 {
614 }
615
616private:
617 enum NodeId_t
618 {
619 kNodeAz = 1,
620 kNodeZd = 3
621 };
622
623 enum
624 {
625 kRxNodeguard = 0xe,
626 kRxPdo1 = 3,
627 kRxPdo2 = 5,
628 kRxPdo3 = 7,
629 kRxPdo4 = 9,
630 kRxSdo = 0xb,
631 kRxSdo4 = 0x40|0x3,
632 kRxSdo2 = 0x40|0xb,
633 kRxSdo1 = 0x40|0xf,
634 kRxSdoOk = 0x60,
635 kRxSdoErr = 0x80,
636
637 kTxSdo = 0x40,
638 kTxSdo4 = 0x20|0x3,
639 kTxSdo2 = 0x20|0xb,
640 kTxSdo1 = 0x20|0xf,
641 };
642
643 //const Int_t id1 = env.GetValue("Az_Id", 1);
644 //const Int_t id2 = env.GetValue("Zd_Id", 3);
645
646 void SendCanFrame(uint16_t cobid,
647 uint8_t m0=0, uint8_t m1=0, uint8_t m2=0, uint8_t m3=0,
648 uint8_t m4=0, uint8_t m5=0, uint8_t m6=0, uint8_t m7=0)
649 {
650 const uint16_t desc = (cobid<<5) | 8;
651
652 vector<uint8_t> data(11);
653 data[0] = 10;
654 data[1] = desc>>8;
655 data[2] = desc&0xff;
656
657 const uint8_t msg[8] = { m0, m1, m2, m3, m4, m5, m6, m7 };
658 memcpy(data.data()+3, msg, 8);
659
660 PostMessage(data);
661 }
662
663 enum Index_t
664 {
665 kReqArmed = 0x1000,
666 kReqPDO = 0x1001,
667 kReqErrStat = 0x1003,
668 kReqSoftVer = 0x100a,
669 kReqKeepAlive = 0x100b,
670 kReqVel = 0x2002,
671 kReqVelRes = 0x6002,
672 kReqVelMax = 0x6003,
673 kReqPos = 0x6004,
674 kReqPosRes = 0x6501,
675
676 kSetArmed = 0x1000,
677 kSetPointVel = 0x2002,
678 kSetAcc = 0x2003,
679 kSetRpmMode = 0x3006,
680 kSetTrackVel = 0x3007,
681 kSetLedVoltage = 0x4000,
682 kSetPosition = 0x6004,
683 };
684
685 static uint32_t String(uint8_t b0=0, uint8_t b1=0, uint8_t b2=0, uint8_t b3=0)
686 {
687 return uint32_t(b0)<<24 | uint32_t(b1)<<16 | uint32_t(b2)<<8 | uint32_t(b3);
688 }
689
690 uint32_t fVelRes[2];
691 uint32_t fVelMax[2];
692 uint32_t fPosRes[2];
693
694 uint32_t fErrCode[2];
695
696 void HandleSdo(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
697 const uint32_t &val, const Time &tv)
698 {
699 if (fVerbosity>0)
700 {
701 ostringstream out;
702 out << hex;
703 out << "SDO[" << int(node) << "] " << idx << "/" << int(subidx) << ": " << val << dec;
704 Out() << out.str() << endl;
705 }
706
707 switch (idx)
708 {
709 case kReqArmed:
710 //fArmed = val==1;
711 return;
712
713 case kReqErrStat:
714 {
715 fErrCode[node/2] = (val>>8);
716 LogErrorCode(node);
717 }
718 return;
719
720 case kReqSoftVer:
721 //fSoftVersion = val;
722 return;
723
724 case kReqKeepAlive:
725 // Do not display, this is used for CheckConnection
726 fIsInitialized[node/2] = true;
727 return;
728
729 case kReqVel:
730 //fVel = val;
731 return;
732
733 case kReqPos:
734 switch (subidx)
735 {
736 case 0:
737 fPdoPos1[node/2] = val;
738 fPdoTime1[node/2] = tv;
739 fHasChangedPos1[node/2] = true;
740 return;
741 case 1:
742 fPdoPos2[node/2] = val;
743 fPdoTime2[node/2] = tv;
744 fHasChangedPos2[node/2] = true;
745 return;
746 }
747 break;
748
749 case kReqVelRes:
750 fVelRes[node/2] = val;
751 return;
752
753 case kReqVelMax:
754 fVelMax[node/2] = val;
755 return;
756
757 case kReqPosRes:
758 fPosRes[node/2] = val;
759 return;
760 }
761
762 ostringstream str;
763 str << "HandleSDO: Idx=0x"<< hex << idx << "/" << (int)subidx;
764 str << ", val=0x" << val;
765 Warn(str);
766 }
767
768 void HandleSdoOk(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
769 const Time &)
770 {
771 ostringstream out;
772 out << hex;
773 out << "SDO-OK[" << int(node) << "] " << idx << "/" << int(subidx) << dec << " ";
774
775 switch (idx)
776 {
777 case kSetArmed:
778 out << "(Armed state set)";
779 break;
780 /*
781 case 0x1001:
782 Out() << inf2 << "- " << GetNodeName() << ": PDOs requested." << endl;
783 return;
784 */
785 case kSetPointVel:
786 out << "(Pointing velocity set)";
787 break;
788
789 case kSetAcc:
790 out << "(Acceleration set)";
791 break;
792
793 case kSetRpmMode:
794 out << "(RPM mode set)";
795 break;
796
797 case kSetLedVoltage:
798 out << "(LED Voltage set)";
799 Info(out);
800 return;
801 /*
802 case 0x3007:
803 //Out() << inf2 << "- Velocity set (" << GetNodeName() << ")" << endl;
804 return;
805
806 case 0x4000:
807 HandleNodeguard(tv);
808 return;
809
810 case 0x6000:
811 Out() << inf2 << "- " << GetNodeName() << ": Rotation direction set." << endl;
812 return;
813
814 case 0x6002:
815 Out() << inf2 << "- " << GetNodeName() << ": Velocity resolution set." << endl;
816 return;
817 */
818 case kSetPosition:
819 out << "(Absolute positioning started)";
820 break;
821 /*
822 case 0x6005:
823 Out() << inf2 << "- " << GetNodeName() << ": Relative positioning started." << endl;
824 fPosActive = kTRUE; // Make sure that the status is set correctly already before the first PDO
825 return;*/
826 }
827 /*
828 Out() << warn << setfill('0') << "WARNING - Nodedrv::HandleSDOOK: ";
829 Out() << "Node #" << dec << (int)fId << ": Sdo=" << hex << idx << "/" << (int)subidx << " set.";
830 Out() << endl;
831 */
832
833 if (fVerbosity>1)
834 Out() << out.str() << endl;
835 }
836
837 void HandleSdoError(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx,
838 const Time &)
839 {
840 ostringstream out;
841 out << hex;
842 out << "SDO-ERR[" << int(node) << "] " << idx << "/" << int(subidx) << dec;
843 Out() << out.str() << endl;
844 }
845
846
847 int32_t fPdoPos1[2];
848 int32_t fPdoPos2[2];
849
850 Time fPdoTime1[2];
851public:
852 Time fPdoTime2[2];
853private:
854 bool fHasChangedPos1[2];
855 bool fHasChangedPos2[2];
856
857 void HandlePdo1(const uint8_t &node, const uint8_t *data, const Time &tv)
858 {
859 const uint32_t pos1 = (data[3]<<24) | (data[2]<<16) | (data[1]<<8) | data[0];
860 const uint32_t pos2 = (data[7]<<24) | (data[6]<<16) | (data[5]<<8) | data[4];
861
862 if (fVerbosity>2)
863 Out() << Time().GetAsStr("%M:%S.%f") << " PDO1[" << (int)node << "] " << 360.*int32_t(pos1)/fPosRes[node/2] << " " << 360.*int32_t(pos2)/fPosRes[node/2] << endl;
864
865 // Once every few milliseconds!
866
867 fPdoPos1[node/2] = pos1;
868 fPdoTime1[node/2] = tv;
869 fHasChangedPos1[node/2] = true;
870
871 fPdoPos2[node/2] = pos2;
872 fPdoTime2[node/2] = tv;
873 fHasChangedPos2[node/2] = true;
874 }
875
876 uint8_t fStatusAxis[2];
877 uint8_t fStatusSys;
878
879 enum {
880 kUpsAlarm = 0x01, // UPS Alarm (FACT only)
881 kUpsBattery = 0x02, // UPS on battery (FACT only)
882 kUpsCharging = 0x04, // UPS charging (FACT only)
883 kEmergencyOk = 0x10, // Emergency button released
884 kOvervoltOk = 0x20, // Overvoltage protection ok
885 kManualMode = 0x40, // Manual mode button pressed
886
887 kAxisBb = 0x01, // IndraDrive reports Bb (Regler betriebsbereit)
888 kAxisMoving = 0x02, // SPS reports
889 kAxisRpmMode = 0x04, // SPS reports
890 kAxisRf = 0x20, // IndraDrive reports Rf (Regler freigegeben)
891 kAxisHasPower = 0x80 // IndraDrive reports axis power on
892 };
893
894 //std::function<void(const Time &, const array<uint8_t, 3>&)> fUpdateStatus;
895
896 void HandlePdo3(const uint8_t &node, const uint8_t *data, const Time &tv)
897 {
898 /*
899 TX1M_STATUS.0 := 1;
900 TX1M_STATUS.1 := ((NOT X_in_Standstill OR NOT X_in_AntriebHalt) AND (NOT X_PC_VStart AND NOT X_in_Pos)) OR X_PC_AnnounceStartMovement;
901 TX1M_STATUS.2 := X_PC_VStart;
902 TX1M_STATUS.6 := NOT X_ist_freigegeben;
903
904 TX3M_STATUS.0 := X_ist_betriebsbereit;
905 TX3M_STATUS.1 := 1;
906 TX3M_STATUS.2 := Not_Aus_IO;
907 TX3M_STATUS.3 := UeberspannungsSchutz_OK;
908 TX3M_STATUS.4 := FB_soll_drehen_links OR FB_soll_drehen_rechts OR FB_soll_schwenk_auf OR FB_soll_schwenk_ab;
909 TX3M_STATUS.5 := X_ist_freigegeben;
910 TX3M_STATUS.6 := 1;
911 TX3M_STATUS.7 := LeistungEinAz;
912
913 TX3M_STATUS.8 := NOT UPS_ALARM;
914 TX3M_STATUS.9 := UPS_BattMode;
915 TX3M_STATUS.10 := UPS_Charging;
916 */
917
918 const uint8_t sys = ((data[0] & 0x1c)<<2) | (data[1]);
919 if (fStatusSys!=sys)
920 {
921 fStatusSys = sys;
922
923 const bool alarm = sys&kUpsAlarm; // 01 TX3M.8 100
924 const bool batt = sys&kUpsBattery; // 02 TX3M.9 200
925 const bool charge = sys&kUpsCharging; // 04 TX3M.10 400
926 const bool emcy = sys&kEmergencyOk; // 10 TX3M.2 04
927 const bool vltg = sys&kOvervoltOk; // 20 TX3M.3 08
928 const bool mode = sys&kManualMode; // 40 TX3M.4 10
929
930 ostringstream out;
931 if (alarm) out << " UPS-PowerLoss";
932 if (batt) out << " UPS-OnBattery";
933 if (charge) out << " UPS-Charging";
934 if (emcy) out << " EmcyOk";
935 if (vltg) out << " OvervoltOk";
936 if (mode) out << " ManualMove";
937
938 Info("New system status["+to_string(node)+"]:"+out.str());
939 if (fVerbosity>1)
940 Out() << "PDO3[" << (int)node << "] StatusSys=" << hex << (int)fStatusSys << dec << endl;
941 }
942
943 const uint8_t axis = (data[0]&0xa1) | (data[3]&0x06);
944 if (fStatusAxis[node/2]!=axis)
945 {
946 fStatusAxis[node/2] = axis;
947
948 const bool ready = axis&kAxisBb; // 01
949 const bool move = axis&kAxisMoving; // 02
950 const bool rpm = axis&kAxisRpmMode; // 04
951 const bool rf = axis&kAxisRf; // 20
952 const bool power = axis&kAxisHasPower; // 80
953
954 ostringstream out;
955 if (ready) out << " DKC-Ready";
956 if (move) out << " Moving";
957 if (rpm) out << " RpmMode";
958 if (rf) out << " RF";
959 if (power) out << " PowerOn";
960
961 Info("New axis status["+to_string(node)+"]:"+out.str());
962 if (fVerbosity>1)
963 Out() << "PDO3[" << (int)node << "] StatusAxis=" << hex << (int)fStatusAxis[node/2] << dec << endl;
964 }
965
966 array<uint8_t, 3> arr = {{ fStatusAxis[0], fStatusAxis[1], fStatusSys }};
967 UpdateStatus(tv, arr);
968 }
969
970 string ErrCodeToString(uint32_t code) const
971 {
972 switch (code)
973 {
974 case 0: return "offline";
975 case 0xa000: case 0xa0000:
976 case 0xa001: case 0xa0001:
977 case 0xa002: case 0xa0002:
978 case 0xa003: case 0xa0003: return "Communication phase "+to_string(code&0xf);
979 case 0xa010: case 0xa0010: return "Drive HALT";
980 case 0xa012: case 0xa0012: return "Control and power section ready for operation";
981 case 0xa013: case 0xa0013: return "Ready for power on";
982 case 0xa100: case 0xa0100: return "Drive in Torque mode";
983 case 0xa101: case 0xa0101: return "Drive in Velocity mode";
984 case 0xa102: case 0xa0102: return "Position control mode with encoder 1";
985 case 0xa103: case 0xa0103: return "Position control mode with encoder 2";
986 case 0xa104: case 0xa0104: return "Position control mode with encoder 1, lagless";
987 case 0xa105: case 0xa0105: return "Position control mode with encoder 2, lagless";
988 case 0xa106: case 0xa0106: return "Drive controlled interpolated positioning with encoder 1";
989 case 0xa107: case 0xa0107: return "Drive controlled interpolated positioning with encoder 2";
990 case 0xa108: case 0xa0108: return "Drive controlled interpolated positioning with encoder 1, lagless";
991 case 0xa109: case 0xa0109: return "Drive controlled interpolated positioning with encoder 2, lagless";
992 //case 0xa146: return "Drive controlled interpolated relative positioning with encoder 1";
993 //case 0xa147: return "Drive controlled interpolated relative positioning with encoder 2";
994 //case 0xa148: return "Drive controlled interpolated relative positioning lagless with encoder 1";
995 //case 0xa149: return "Drive controlled interpolated relative positioning lagless with encoder 2";
996 case 0xa150: case 0xa0150: return "Drive controlled positioning with encoder 1";
997 case 0xa151: case 0xa0151: return "Drive controlled positioning with encoder 1, lagless";
998 case 0xa152: case 0xa0152: return "Drive controlled positioning with encoder 2";
999 case 0xa153: case 0xa0153: return "Drive controlled positioning with encoder 2, lagless";
1000 case 0xa208: return "Jog mode positive";
1001 case 0xa218: return "Jog mode negative";
1002 case 0xa400: case 0xa4000: return "Automatic drive check and adjustment";
1003 case 0xa401: case 0xa4001: return "Drive decelerating to standstill";
1004 case 0xa800: case 0xa0800: return "Unknown operation mode";
1005 case 0xc217: return "Motor encoder reading error";
1006 case 0xc218: return "Shaft encoder reading error";
1007 case 0xc220: return "Motor encoder initialization error";
1008 case 0xc221: return "Shaft encoder initialization error";
1009 case 0xc300: return "Command: set absolute measure";
1010 case 0xc400: case 0xc0400: return "Switching to parameter mode";
1011 case 0xc401: case 0xc0401: return "Drive active, switching mode not allowed";
1012 case 0xc500: case 0xc0500: return "Error reset";
1013 case 0xc600: case 0xc0600: return "Drive controlled homing procedure";
1014 case 0xe225: return "Motor overload";
1015 case 0xe249: case 0xe2049: return "Positioning command velocity exceeds limit bipolar";
1016 case 0xe250: return "Drive overtemp warning";
1017 case 0xe251: return "Motor overtemp warning";
1018 case 0xe252: return "Bleeder overtemp warning";
1019 case 0xe257: return "Continous current limit active";
1020 case 0xe2819: return "Main power failure";
1021 case 0xe259: return "Command velocity limit active";
1022 case 0xe8260: return "Torque limit active";
1023 case 0xe264: return "Target position out of numerical range";
1024 case 0xe829: case 0xe8029: return "Positive position limit exceeded";
1025 case 0xe830: case 0xe8030: return "Negative position limit exceeded";
1026 case 0xe831: return "Position limit reached during jog";
1027 case 0xe834: return "Emergency-Stop";
1028 case 0xe842: return "Both end-switches activated";
1029 case 0xe843: return "Positive end-switch activated";
1030 case 0xe844: return "Negative end-switch activated";
1031 case 0xf218: case 0xf2018: return "Amplifier overtemp shutdown";
1032 case 0xf219: case 0xf2019: return "Motor overtemp shutdown";
1033 case 0xf220: return "Bleeder overload shutdown";
1034 case 0xf221: case 0xf2021: return "Motor temperature surveillance defective";
1035 case 0xf2022: return "Unit temperature surveillance defective";
1036 case 0xf224: return "Maximum breaking time exceeded";
1037 case 0xf2025: return "Drive not ready for power on";
1038 case 0xf228: case 0xf2028: return "Excessive control deviation";
1039 case 0xf250: return "Overflow of target position preset memory";
1040 case 0xf257: case 0xf2057: return "Command position out of range";
1041 case 0xf269: return "Error during release of the motor holding brake";
1042 case 0xf276: return "Absolute encoder out of allowed window";
1043 case 0xf2174: return "Lost reference of motor encoder";
1044 case 0xf409: case 0xf4009: return "Bus error on Profibus interface";
1045 case 0xf434: return "Emergency-Stop";
1046 case 0xf629: return "Positive position limit exceeded";
1047 case 0xf630: return "Negative position limit exceeded";
1048 case 0xf634: return "Emergency-Stop";
1049 case 0xf643: return "Positive end-switch activated";
1050 case 0xf644: return "Negative end-switch activated";
1051 case 0xf8069: return "15V DC error";
1052 case 0xf870: case 0xf8070: return "24V DC error";
1053 case 0xf878: case 0xf8078: return "Velocity loop error";
1054 case 0xf8079: return "Velocity limit exceeded";
1055 case 0xf2026: return "Undervoltage in power section";
1056 }
1057 return "unknown";
1058 }
1059
1060 void LogErrorCode(uint32_t node)
1061 {
1062 const uint8_t typ = fErrCode[node/2]>>16;
1063
1064 ostringstream out;
1065 out << "IndraDrive ";
1066 out << (node==1?"Az":"Zd");
1067 out << " [" << hex << fErrCode[node/2];
1068 out << "]: ";
1069 out << ErrCodeToString(fErrCode[node/2]);
1070 out << (typ==0xf || typ==0xe ? "!" : ".");
1071
1072 switch (typ)
1073 {
1074 case 0xf: Error(out); break;
1075 case 0xe: Warn(out); break;
1076 case 0xa: Info(out); break;
1077 case 0x0:
1078 case 0xc:
1079 case 0xd: Message(out); break;
1080 default: Fatal(out); break;
1081 }
1082 }
1083
1084 void HandlePdo2(const uint8_t &node, const uint8_t *data, const Time &)
1085 {
1086 fErrCode[node/2] = (data[4]<<24) | (data[5]<<16) | (data[6]<<8) | data[7];
1087
1088 if (fVerbosity>0)
1089 Out() << "PDO2[" << int(node) << "] err=" << hex << fErrCode[node/2] << endl;
1090
1091 LogErrorCode(node);
1092 }
1093
1094 struct SDO
1095 {
1096 uint8_t node;
1097 uint8_t req;
1098 uint16_t idx;
1099 uint8_t subidx;
1100 uint32_t val;
1101
1102 SDO(uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v=0)
1103 : node(n), req(r&0xf), idx(i), subidx(s), val(v) { }
1104
1105 bool operator==(const SDO &s) const
1106 {
1107 return node==s.node && idx==s.idx && subidx==s.subidx;
1108 }
1109 };
1110
1111 struct Timeout_t : SDO, ba::deadline_timer
1112 {
1113
1114 Timeout_t(ba::io_service& ioservice,
1115 uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v, uint16_t millisec) : SDO(n, r, i, s, v),
1116 ba::deadline_timer(ioservice)
1117 {
1118 expires_from_now(boost::posix_time::milliseconds(millisec));
1119 }
1120 // get_io_service()
1121 };
1122
1123 std::list<Timeout_t> fTimeouts;
1124
1125 vector<uint8_t> fData;
1126
1127 void HandleReceivedData(const boost::system::error_code& err, size_t bytes_received, int)
1128 {
1129 // Do not schedule a new read if the connection failed.
1130 if (bytes_received!=11 || fData[0]!=10 || err)
1131 {
1132 if (err==ba::error::eof)
1133 Warn("Connection closed by remote host (cosy).");
1134
1135 // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
1136 // 125: Operation canceled
1137 if (err && err!=ba::error::eof && // Connection closed by remote host
1138 err!=ba::error::basic_errors::not_connected && // Connection closed by remote host
1139 err!=ba::error::basic_errors::operation_aborted) // Connection closed by us
1140 {
1141 ostringstream str;
1142 str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
1143 Error(str);
1144 }
1145 PostClose(err!=ba::error::basic_errors::operation_aborted);
1146 return;
1147 }
1148
1149 Time now;
1150
1151 const uint16_t desc = fData[1]<<8 | fData[2];
1152 //const uint8_t rtr = (desc>>4)&1;
1153 //const uint8_t len = desc&0xf;
1154 const uint16_t cobid = desc>>5;
1155
1156 const uint8_t *data = fData.data()+3;
1157
1158 const uint16_t fcode = cobid >> 7;
1159 const uint8_t node = cobid & 0x1f;
1160
1161 switch (fcode)
1162 {
1163 case kRxNodeguard:
1164 Out() << "Received nodeguard" << endl;
1165 //HandleNodeguard(node, now);
1166 break;
1167
1168 case kRxSdo:
1169 {
1170 const uint8_t cmd = data[0];
1171 const uint16_t idx = data[1] | (data[2]<<8);
1172 const uint8_t subidx = data[3];
1173 const uint32_t dat = data[4] | (data[5]<<8) | (data[6]<<16) | (data[7]<<24);
1174
1175 const auto it = find(fTimeouts.begin(), fTimeouts.end(), SDO(node, cmd, idx, subidx));
1176 if (it!=fTimeouts.end())
1177 {
1178 // This will call the handler and in turn remove the object from the list
1179 it->cancel();
1180 }
1181
1182 switch (cmd)
1183 {
1184 case kRxSdo4: // answer to 0x40 with 4 bytes of data
1185 HandleSdo(node, idx, subidx, dat, now);
1186 break;
1187
1188 case kRxSdo2: // answer to 0x40 with 2 bytes of data
1189 HandleSdo(node, idx, subidx, dat&0xffff, now);
1190 break;
1191
1192 case kRxSdo1: // answer to 0x40 with 1 byte of data
1193 HandleSdo(node, idx, subidx, dat&0xff, now);
1194 break;
1195
1196 case kRxSdoOk: // answer to a SDO_TX message
1197 HandleSdoOk(node, idx, subidx, now);
1198 break;
1199
1200 case kRxSdoErr: // error message
1201 HandleSdoError(node, idx, subidx, now);
1202 break;
1203
1204 default:
1205 {
1206 ostringstream out;
1207 out << "Invalid SDO command code " << hex << cmd << " received.";
1208 Error(out);
1209 PostClose(false);
1210 return;
1211 }
1212 }
1213 }
1214 break;
1215
1216 case kRxPdo1:
1217 HandlePdo1(node, data, now);
1218 break;
1219
1220 case kRxPdo2:
1221 HandlePdo2(node, data, now);
1222 break;
1223
1224 case kRxPdo3:
1225 HandlePdo3(node, data, now);
1226 break;
1227
1228 default:
1229 {
1230 ostringstream out;
1231 out << "Invalid function code " << hex << fcode << " received.";
1232 Error(out);
1233 PostClose(false);
1234 return;
1235 }
1236 }
1237
1238 StartReadReport();
1239 }
1240
1241 void StartReadReport()
1242 {
1243 ba::async_read(*this, ba::buffer(fData),
1244 boost::bind(&ConnectionDrive::HandleReceivedData, this,
1245 ba::placeholders::error, ba::placeholders::bytes_transferred, 0));
1246
1247 //AsyncWait(fInTimeout, 35000, &Connection::HandleReadTimeout); // 30s
1248 }
1249
1250 bool fIsInitialized[2];
1251
1252 // This is called when a connection was established
1253 void ConnectionEstablished()
1254 {
1255 //Info("Connection to PLC established.");
1256
1257 fIsInitialized[0] = false;
1258 fIsInitialized[1] = false;
1259
1260 SendSdo(kNodeZd, kSetArmed, 1);
1261 SendSdo(kNodeAz, kSetArmed, 1);
1262
1263 RequestSdo(kNodeZd, kReqErrStat);
1264 RequestSdo(kNodeAz, kReqErrStat);
1265
1266 SetRpmMode(false);
1267
1268 RequestSdo(kNodeZd, kReqPosRes);
1269 RequestSdo(kNodeAz, kReqPosRes);
1270
1271 RequestSdo(kNodeZd, kReqVelRes);
1272 RequestSdo(kNodeAz, kReqVelRes);
1273
1274 RequestSdo(kNodeZd, kReqVelMax);
1275 RequestSdo(kNodeAz, kReqVelMax);
1276
1277 RequestSdo(kNodeZd, kReqPos, 0);
1278 RequestSdo(kNodeAz, kReqPos, 0);
1279 RequestSdo(kNodeZd, kReqPos, 1);
1280 RequestSdo(kNodeAz, kReqPos, 1);
1281
1282 RequestSdo(kNodeZd, kReqKeepAlive);
1283 RequestSdo(kNodeAz, kReqKeepAlive);
1284
1285 StartReadReport();
1286 }
1287
1288 void HandleTimeoutImp(const std::list<Timeout_t>::iterator &ref, const bs::error_code &error)
1289 {
1290 if (error==ba::error::basic_errors::operation_aborted)
1291 return;
1292
1293 if (error)
1294 {
1295 ostringstream str;
1296 str << "SDO timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
1297 Error(str);
1298
1299 //PostClose();
1300 return;
1301 }
1302
1303 if (!is_open())
1304 {
1305 // For example: Here we could schedule a new accept if we
1306 // would not want to allow two connections at the same time.
1307 return;
1308 }
1309
1310 // Check whether the deadline has passed. We compare the deadline
1311 // against the current time since a new asynchronous operation
1312 // may have moved the deadline before this actor had a chance
1313 // to run.
1314 if (ref->expires_at() > ba::deadline_timer::traits_type::now())
1315 return;
1316
1317 ostringstream str;
1318 str << hex;
1319 str << "SDO timeout (";
1320 str << uint32_t(ref->node) << ": ";
1321 str << (ref->req==kTxSdo?"RX ":"TX ");
1322 str << ref->idx << "/" << uint32_t(ref->subidx) << " [" << ref->val << "] ";
1323 str << to_simple_string(ref->expires_from_now());
1324 str << ")";
1325
1326 Warn(str);
1327
1328 //PostClose();
1329 }
1330
1331 void HandleTimeout(const std::list<Timeout_t>::iterator &ref, const bs::error_code &error)
1332 {
1333 HandleTimeoutImp(ref, error);
1334 fTimeouts.erase(ref);
1335 }
1336
1337 void SendSdoRequest(uint8_t node, uint8_t req,
1338 uint16_t idx, uint8_t subidx, uint32_t val=0)
1339 {
1340 if (fVerbosity>1)
1341 Out() << "SDO-" << (req==kTxSdo?"REQ":"SET") << "[" << int(node) << "] " << idx << "/" << int(subidx) << " = " << val << endl;
1342
1343
1344 SendCanFrame(0x600|(node&0x1f), req, idx&0xff, idx>>8, subidx,
1345 val&0xff, (val>>8)&0xff, (val>>16)&0xff, (val>>24)&0xff);
1346
1347 // - The boost::asio::basic_deadline_timer::expires_from_now()
1348 // function cancels any pending asynchronous waits, and returns
1349 // the number of asynchronous waits that were cancelled. If it
1350 // returns 0 then you were too late and the wait handler has
1351 // already been executed, or will soon be executed. If it
1352 // returns 1 then the wait handler was successfully cancelled.
1353 // - If a wait handler is cancelled, the bs::error_code passed to
1354 // it contains the value bs::error::operation_aborted.
1355
1356 const uint32_t milliseconds = 3000;
1357 fTimeouts.emplace_front(get_io_service(), node, req, idx, subidx, val, milliseconds);
1358
1359 const std::list<Timeout_t>::iterator &timeout = fTimeouts.begin();
1360
1361 timeout->async_wait(boost::bind(&ConnectionDrive::HandleTimeout, this, timeout, ba::placeholders::error));
1362 }
1363
1364public:
1365 ConnectionDrive(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
1366 fVerbosity(0), fData(11)
1367 {
1368 SetLogStream(&imp);
1369 }
1370
1371 void SetVerbosity(const uint16_t &v)
1372 {
1373 fVerbosity = v;
1374 }
1375
1376 uint16_t GetVerbosity() const
1377 {
1378 return fVerbosity;
1379 }
1380
1381 void RequestSdo(uint8_t node, uint16_t idx, uint8_t subidx=0)
1382 {
1383 SendSdoRequest(node, kTxSdo, idx, subidx);
1384 }
1385 void SendSdo(uint8_t node, uint16_t idx, uint8_t subidx, uint32_t val)
1386 {
1387 SendSdoRequest(node, kTxSdo4, idx, subidx, val);
1388 }
1389
1390 void SendSdo(uint8_t node, uint16_t idx, uint32_t val)
1391 {
1392 SendSdo(node, idx, 0, val);
1393 }
1394
1395 bool IsMoving() const
1396 {
1397 return (fStatusAxis[0]&kAxisMoving) || (fStatusAxis[1]&kAxisMoving)
1398 || (fStatusAxis[0]&kAxisRpmMode) || (fStatusAxis[1]&kAxisRpmMode);
1399 }
1400
1401 bool IsInitialized() const
1402 {
1403 // All important information has been successfully requested from the
1404 // SPS and the power control units are in RF (Regler freigegeben)
1405 return fIsInitialized[0] && fIsInitialized[1];
1406 }
1407
1408 bool HasError() const
1409 {
1410 const uint8_t typ0 = fErrCode[0]>>16;
1411 const uint8_t typ1 = fErrCode[1]>>16;
1412 return typ0==0xe || typ0==0xf || typ1==0xe || typ1==0xf;
1413 }
1414
1415 bool IsOnline() const
1416 {
1417 return fErrCode[0]!=0 && fErrCode[1]!=0;
1418 }
1419
1420 bool IsReady() const
1421 {
1422 return fStatusAxis[0]&kAxisRf && fStatusAxis[1]&kAxisRf;
1423 }
1424
1425 bool IsBlocked() const
1426 {
1427 return (fStatusSys&0x10)==0 || (fStatusSys&kManualMode);
1428 }
1429
1430 Encoder GetSePos() const // [rev]
1431 {
1432 return Encoder(double(fPdoPos2[1])/fPosRes[1], double(fPdoPos2[0])/fPosRes[0]);
1433 }
1434
1435 double GetSeTime() const // [rev]
1436 {
1437 // The maximum difference here should not be larger than 100ms.
1438 // So th error we make on both axes should not exceed 50ms;
1439 return (Time(fPdoTime2[0]).Mjd()+Time(fPdoTime2[1]).Mjd())/2;
1440 }
1441
1442 Encoder GetVelUnit() const
1443 {
1444 return Encoder(fVelMax[1], fVelMax[0]);
1445 }
1446
1447 void SetRpmMode(bool mode)
1448 {
1449 const uint32_t val = mode ? String('s','t','r','t') : String('s','t','o','p');
1450 SendSdo(kNodeAz, kSetRpmMode, val);
1451 SendSdo(kNodeZd, kSetRpmMode, val);
1452 }
1453
1454 void SetAcceleration(const Acceleration &acc)
1455 {
1456 SendSdo(kNodeAz, kSetAcc, lrint(acc.az*1000000000+0.5));
1457 SendSdo(kNodeZd, kSetAcc, lrint(acc.zd*1000000000+0.5));
1458 }
1459
1460 void SetPointingVelocity(const Velocity &vel, double scale=1)
1461 {
1462 SendSdo(kNodeAz, kSetPointVel, lrint(vel.az*fVelMax[0]*scale));
1463 SendSdo(kNodeZd, kSetPointVel, lrint(vel.zd*fVelMax[1]*scale));
1464 }
1465 void SetTrackingVelocity(const Velocity &vel)
1466 {
1467 SendSdo(kNodeAz, kSetTrackVel, lrint(vel.az*fVelRes[0]));
1468 SendSdo(kNodeZd, kSetTrackVel, lrint(vel.zd*fVelRes[1]));
1469 }
1470
1471 void StartAbsolutePositioning(const Encoder &enc, bool zd, bool az)
1472 {
1473 if (az) SendSdo(kNodeAz, kSetPosition, lrint(enc.az*fPosRes[0]));
1474 if (zd) SendSdo(kNodeZd, kSetPosition, lrint(enc.zd*fPosRes[1]));
1475
1476 // Make sure that the status is set correctly already before the first PDO
1477 if (az) fStatusAxis[0] |= 0x02;
1478 if (zd) fStatusAxis[1] |= 0x02;
1479
1480 // FIXME: UpdateDim?
1481 }
1482
1483 void SetLedVoltage(uint32_t v1, uint32_t v2)
1484 {
1485 SendSdo(kNodeAz, 0x4000, v1);
1486 SendSdo(kNodeZd, 0x4000, v2);
1487 }
1488};
1489
1490
1491// ------------------------------------------------------------------------
1492
1493#include "DimDescriptionService.h"
1494
1495class ConnectionDimDrive : public ConnectionDrive
1496{
1497private:
1498 DimDescribedService fDimPointing;
1499 DimDescribedService fDimTracking;
1500 DimDescribedService fDimSource;
1501 DimDescribedService fDimTPoint;
1502 DimDescribedService fDimStatus;
1503
1504 // Update dim from a different thread to ensure that these
1505 // updates cannot block the main eventloop which eventually
1506 // also checks the timeouts
1507 Queue<pair<Time,array<double, 2>>> fQueuePointing;
1508 Queue<pair<Time,array<double, 12>>> fQueueTracking;
1509 Queue<tuple<Time,vector<char>,bool>> fQueueSource;
1510 Queue<pair<Time,vector<char>>> fQueueTPoint;
1511 Queue<pair<Time,array<uint8_t, 3>>> fQueueStatus;
1512
1513 bool SendPointing(const pair<Time,array<double,2>> &p)
1514 {
1515 fDimPointing.setData(p.second);
1516 fDimPointing.Update(p.first);
1517 return true;
1518 }
1519
1520 bool SendTracking(const pair<Time,array<double, 12>> &p)
1521 {
1522 fDimTracking.setData(p.second);
1523 fDimTracking.Update(p.first);
1524 return true;
1525 }
1526
1527 bool SendSource(const tuple<Time,vector<char>,bool> &t)
1528 {
1529 const Time &time = get<0>(t);
1530 const vector<char> &data = get<1>(t);
1531 const bool &tracking = get<2>(t);
1532
1533 fDimSource.setQuality(tracking);
1534 fDimSource.setData(data);
1535 fDimSource.Update(time);
1536 return true;
1537 }
1538
1539 bool SendStatus(const pair<Time,array<uint8_t, 3>> &p)
1540 {
1541 fDimStatus.setData(p.second);
1542 fDimStatus.Update(p.first);
1543 return true;
1544 }
1545
1546 bool SendTPoint(const pair<Time,vector<char>> &p)
1547 {
1548 fDimTPoint.setData(p.second);
1549 fDimTPoint.Update(p.first);
1550 return true;
1551 }
1552
1553public:
1554 void UpdatePointing(const Time &t, const array<double, 2> &arr)
1555 {
1556 fQueuePointing.emplace(t, arr);
1557 }
1558
1559 void UpdateTracking(const Time &t,const array<double, 12> &arr)
1560 {
1561 fQueueTracking.emplace(t, arr);
1562 }
1563
1564 void UpdateStatus(const Time &t, const array<uint8_t, 3> &arr)
1565 {
1566 fQueueStatus.emplace(t, arr);
1567 }
1568
1569 void UpdateTPoint(const Time &t, const DimTPoint &data,
1570 const string &name)
1571 {
1572 vector<char> dim(sizeof(data)+name.length()+1);
1573 memcpy(dim.data(), &data, sizeof(data));
1574 memcpy(dim.data()+sizeof(data), name.c_str(), name.length()+1);
1575
1576 fQueueTPoint.emplace(t, dim);
1577 }
1578
1579 void UpdateSource(const Time &t, const string &name, bool tracking)
1580 {
1581 vector<char> dat(5*sizeof(double)+31, 0);
1582 strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1583
1584 fQueueSource.emplace(t, dat, tracking);
1585 }
1586
1587 void UpdateSource(const Time &t, const array<double, 5> &arr, const string &name="")
1588 {
1589 vector<char> dat(5*sizeof(double)+31, 0);
1590 memcpy(dat.data(), arr.data(), 5*sizeof(double));
1591 strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1592
1593 fQueueSource.emplace(t, dat, true);
1594 }
1595
1596public:
1597 ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) :
1598 ConnectionDrive(ioservice, imp),
1599 fDimPointing("DRIVE_CONTROL/POINTING_POSITION", "D:1;D:1",
1600 "|Zd[deg]:Zenith distance (derived from encoder readout)"
1601 "|Az[deg]:Azimuth angle (derived from encoder readout)"),
1602 fDimTracking("DRIVE_CONTROL/TRACKING_POSITION", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1",
1603 "|Ra[h]:Command right ascension pointing direction (J2000)"
1604 "|Dec[deg]:Command declination pointing direction (J2000)"
1605 "|Ha[h]:Hour angle pointing direction"
1606 "|SrcRa[h]:Right ascension source (J2000)"
1607 "|SrcDec[deg]:Declination source (J2000)"
1608 "|SrcHa[h]:Hour angle source"
1609 "|Zd[deg]:Nominal zenith distance"
1610 "|Az[deg]:Nominal azimuth angle"
1611 "|dZd[deg]:Control deviation Zd"
1612 "|dAz[deg]:Control deviation Az"
1613 "|dev[arcsec]:Absolute control deviation"
1614 "|avgdev[arcsec]:Average control deviation used to define OnTrack"),
1615 fDimSource("DRIVE_CONTROL/SOURCE_POSITION", "D:1;D:1;D:1;D:1;D:1;C:31",
1616 "|Ra_src[h]:Source right ascension"
1617 "|Dec_src[deg]:Source declination"
1618 "|Offset[deg]:Wobble offset"
1619 "|Angle[deg]:Wobble angle"
1620 "|Period[min]:Time for one orbit"
1621 "|Name[string]:Source name if available"),
1622 fDimTPoint("DRIVE_CONTROL/TPOINT_DATA", "D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;S:1;S:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;D:1;C",
1623 "|Ra[h]:Command right ascension"
1624 "|Dec[deg]:Command declination"
1625 "|Zd_nom[deg]:Nominal zenith distance"
1626 "|Az_nom[deg]:Nominal azimuth angle"
1627 "|Zd_cur[deg]:Current zenith distance (calculated from image)"
1628 "|Az_cur[deg]:Current azimuth angle (calculated from image)"
1629 "|Zd_enc[deg]:Feedback zenith axis (from encoder)"
1630 "|Az_enc[deg]:Feedback azimuth angle (from encoder)"
1631 "|N_leds[cnt]:Number of detected LEDs"
1632 "|N_rings[cnt]:Number of rings used to calculate the camera center"
1633 "|Xc[pix]:X position of center in CCD camera frame"
1634 "|Yc[pix]:Y position of center in CCD camera frame"
1635 "|Ic[au]:Average intensity (LED intensity weighted with their frequency of occurance in the calculation)"
1636 "|Xs[pix]:X position of start in CCD camera frame"
1637 "|Ys[pix]:Y position of star in CCD camera frame"
1638 "|Ms[mag]:Artifical magnitude of star (calculated from image)"
1639 "|Phi[deg]:Rotation angle of image derived from detected LEDs"
1640 "|Mc[mag]:Catalog magnitude of star"
1641 "|Dx[arcsec]:De-rotated dx"
1642 "|Dy[arcsec]:De-rotated dy"
1643 "|Name[string]:Name of star"),
1644 fDimStatus("DRIVE_CONTROL/STATUS", "C:2;C:1", ""),
1645 fQueuePointing(std::bind(&ConnectionDimDrive::SendPointing, this, placeholders::_1)),
1646 fQueueTracking(std::bind(&ConnectionDimDrive::SendTracking, this, placeholders::_1)),
1647 fQueueSource( std::bind(&ConnectionDimDrive::SendSource, this, placeholders::_1)),
1648 fQueueTPoint( std::bind(&ConnectionDimDrive::SendTPoint, this, placeholders::_1)),
1649 fQueueStatus( std::bind(&ConnectionDimDrive::SendStatus, this, placeholders::_1))
1650 {
1651 }
1652
1653 // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z
1654};
1655
1656// ------------------------------------------------------------------------
1657
1658template <class T, class S>
1659class StateMachineDrive : public StateMachineAsio<T>
1660{
1661private:
1662 S fDrive;
1663
1664 ba::deadline_timer fTrackingLoop;
1665
1666 string fDatabase;
1667
1668 typedef map<string, Source> sources;
1669 sources fSources;
1670
1671 Weather fWeather;
1672 uint16_t fWeatherTimeout;
1673
1674 ZdAz fParkingPos;
1675
1676 PointingModel fPointingModel;
1677 PointingSetup fPointingSetup;
1678 Encoder fMovementTarget;
1679
1680 Time fSunRise;
1681
1682 Encoder fPointingMin;
1683 Encoder fPointingMax;
1684
1685 uint16_t fDeviationLimit;
1686 uint16_t fDeviationCounter;
1687 uint16_t fDeviationMax;
1688
1689 vector<double> fDevBuffer;
1690 uint64_t fDevCount;
1691
1692 uint64_t fTrackingCounter;
1693
1694
1695 // --------------------- DIM Sending ------------------
1696
1697 bool CheckEventSize(size_t has, const char *name, size_t size)
1698 {
1699 if (has==size)
1700 return true;
1701
1702 ostringstream msg;
1703 msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1704 T::Fatal(msg);
1705 return false;
1706 }
1707
1708 // --------------------- DIM Receiving ------------------
1709
1710 int HandleWeatherData(const EventImp &evt)
1711 {
1712 if (!CheckEventSize(evt.GetSize(), "HandleWeatherData", 7*4+2))
1713 {
1714 fWeather.time = Time(Time::none);
1715 return T::GetCurrentState();
1716 }
1717
1718 const float *ptr = evt.Ptr<float>(2);
1719
1720 fWeather.temp = ptr[0];
1721 fWeather.hum = ptr[2];
1722 fWeather.press = ptr[3];
1723 fWeather.time = evt.GetTime();
1724
1725 return T::GetCurrentState();
1726 }
1727
1728 int HandleTPoint(const EventImp &evt)
1729 {
1730 // Skip disconnect events
1731 if (evt.GetSize()==0)
1732 return T::GetCurrentState();
1733
1734 // skip invalid events
1735 if (!CheckEventSize(evt.GetSize(), "HandleTPoint", 11*8))
1736 return T::GetCurrentState();
1737
1738 // skip event which are older than one minute
1739 if (Time().UnixTime()-evt.GetTime().UnixTime()>60)
1740 return T::GetCurrentState();
1741
1742 // Original code in slaTps2c:
1743 //
1744 // From the tangent plane coordinates of a star of known RA,Dec,
1745 // determine the RA,Dec of the tangent point.
1746
1747 const double *ptr = evt.Ptr<double>();
1748
1749 // Tangent plane rectangular coordinates
1750 const double dx = ptr[0] * M_PI/648000; // [arcsec -> rad]
1751 const double dy = ptr[1] * M_PI/648000; // [arcsec -> rad]
1752
1753 const PointingData data = fPointingModel.CalcPointingPos(fPointingSetup, evt.GetTime().Mjd(), fWeather, fWeatherTimeout, true);
1754
1755 const double x2 = dx*dx;
1756 const double y2 = 1 + dy*dy;
1757
1758 const double sd = cos(data.sky.zd);//sin(M_PI/2-sky.zd);
1759 const double cd = sin(data.sky.zd);//cos(M_PI/2-sky.zd);
1760 const double sdf = sd*sqrt(x2+y2);
1761 const double r2 = cd*cd*y2 - sd*sd*x2;
1762
1763 // Case of no solution ("at the pole") or
1764 // two solutions ("over the pole solution")
1765 if (r2<0 || fabs(sdf)>=1)
1766 {
1767 T::Warn("Could not determine pointing direction from TPoint.");
1768 return T::GetCurrentState();
1769 }
1770
1771 const double r = sqrt(r2);
1772 const double s = sdf - dy * r;
1773 const double c = sdf * dy + r;
1774 const double phi = atan2(dx, r);
1775
1776 // Spherical coordinates of tangent point
1777 const double az = fmod(data.sky.az-phi + 2*M_PI, 2*M_PI);
1778 const double zd = M_PI/2 - atan2(s, c);
1779
1780 const Encoder dev = fDrive.GetSePos()*360 - data.mount;
1781
1782 // --- Output TPoint ---
1783
1784 const string fname = "tpoints-"+to_string(evt.GetTime().NightAsInt())+".txt";
1785 //time.GetAsStr("/%Y/%m/%d");
1786
1787 const bool exist = boost::filesystem::exists(fname);
1788
1789 ofstream fout(fname, ios::app);
1790 if (!exist)
1791 {
1792 fout << "FACT Model TPOINT data file" << endl;
1793 fout << ": ALTAZ" << endl;
1794 fout << "49 48 0 ";
1795 fout << evt.GetTime() << endl;
1796 }
1797 fout << setprecision(7);
1798 fout << fmod(az*180/M_PI+360, 360) << " ";
1799 fout << 90-zd*180/M_PI << " ";
1800 fout << fmod(data.mount.az+360, 360) << " ";
1801 fout << 90-data.mount.zd << " ";
1802 fout << dev.az << " "; // delta az
1803 fout << -dev.zd << " "; // delta el
1804 fout << 90-data.sky.zd * 180/M_PI << " ";
1805 fout << data.sky.az * 180/M_PI << " ";
1806 fout << setprecision(10);
1807 fout << data.mjd << " ";
1808 fout << setprecision(7);
1809 fout << ptr[6] << " "; // center.mag
1810 fout << ptr[9] << " "; // star.mag
1811 fout << ptr[4] << " "; // center.x
1812 fout << ptr[5] << " "; // center.y
1813 fout << ptr[7] << " "; // star.x
1814 fout << ptr[8] << " "; // star.y
1815 fout << ptr[2] << " "; // num leds
1816 fout << ptr[3] << " "; // num rings
1817 fout << ptr[0] << " "; // dx (de-rotated)
1818 fout << ptr[1] << " "; // dy (de-rotated)
1819 fout << ptr[10] << " "; // rotation angle
1820 fout << fPointingSetup.source.mag << " ";
1821 fout << fPointingSetup.source.name;
1822 fout << endl;
1823
1824 DimTPoint dim;
1825 dim.fRa = data.pointing.ra * 12/M_PI;
1826 dim.fDec = data.pointing.dec * 180/M_PI;
1827 dim.fNominalZd = data.sky.zd * 180/M_PI;
1828 dim.fNominalAz = data.sky.az * 180/M_PI;
1829 dim.fPointingZd = zd * 180/M_PI;
1830 dim.fPointingAz = az * 180/M_PI;
1831 dim.fFeedbackZd = data.mount.zd;
1832 dim.fFeedbackAz = data.mount.az;
1833 dim.fNumLeds = uint16_t(ptr[2]);
1834 dim.fNumRings = uint16_t(ptr[3]);
1835 dim.fCenterX = ptr[4];
1836 dim.fCenterY = ptr[5];
1837 dim.fCenterMag = ptr[6];
1838 dim.fStarX = ptr[7];
1839 dim.fStarY = ptr[8];
1840 dim.fStarMag = ptr[9];
1841 dim.fRotation = ptr[10];
1842 dim.fDx = ptr[0];
1843 dim.fDy = ptr[1];
1844 dim.fRealMag = fPointingSetup.source.mag;
1845
1846 fDrive.UpdateTPoint(evt.GetTime(), dim, fPointingSetup.source.name);
1847
1848 ostringstream txt;
1849 txt << "TPoint recorded [" << zd*180/M_PI << "/" << az*180/M_PI << " | "
1850 << data.sky.zd*180/M_PI << "/" << data.sky.az*180/M_PI << " | "
1851 << data.mount.zd << "/" << data.mount.az << " | "
1852 << dx*180/M_PI << "/" << dy*180/M_PI << "]";
1853 T::Info(txt);
1854
1855 return T::GetCurrentState();
1856 }
1857
1858 // -------------------------- Helpers -----------------------------------
1859
1860 double GetDevAbs(double nomzd, double meszd, double devaz)
1861 {
1862 nomzd *= M_PI/180;
1863 meszd *= M_PI/180;
1864 devaz *= M_PI/180;
1865
1866 const double x = sin(meszd) * sin(nomzd) * cos(devaz);
1867 const double y = cos(meszd) * cos(nomzd);
1868
1869 return acos(x + y) * 180/M_PI;
1870 }
1871
1872 double ReadAngle(istream &in)
1873 {
1874 char sgn;
1875 uint16_t d, m;
1876 float s;
1877
1878 in >> sgn >> d >> m >> s;
1879
1880 const double ret = ((60.0 * (60.0 * (double)d + (double)m) + s))/3600.;
1881 return sgn=='-' ? -ret : ret;
1882 }
1883
1884 bool CheckRange(ZdAz pos)
1885 {
1886 if (pos.zd<fPointingMin.zd)
1887 {
1888 T::Error("Zenith distance "+to_string(pos.zd)+" below limit "+to_string(fPointingMin.zd));
1889 return false;
1890 }
1891
1892 if (pos.zd>fPointingMax.zd)
1893 {
1894 T::Error("Zenith distance "+to_string(pos.zd)+" exceeds limit "+to_string(fPointingMax.zd));
1895 return false;
1896 }
1897
1898 if (pos.az<fPointingMin.az)
1899 {
1900 T::Error("Azimuth angle "+to_string(pos.az)+" below limit "+to_string(fPointingMin.az));
1901 return false;
1902 }
1903
1904 if (pos.az>fPointingMax.az)
1905 {
1906 T::Error("Azimuth angle "+to_string(pos.az)+" exceeds limit "+to_string(fPointingMax.az));
1907 return false;
1908 }
1909
1910 return true;
1911 }
1912
1913 PointingData CalcPointingPos(double mjd)
1914 {
1915 return fPointingModel.CalcPointingPos(fPointingSetup, mjd, fWeather, fWeatherTimeout);
1916 }
1917
1918 // ----------------------------- SDO Commands ------------------------------
1919
1920 int RequestSdo(const EventImp &evt)
1921 {
1922 // FIXME: STop telescope
1923 if (!CheckEventSize(evt.GetSize(), "RequestSdo", 6))
1924 return T::kSM_FatalError;
1925
1926 const uint16_t node = evt.Get<uint16_t>();
1927 const uint16_t index = evt.Get<uint16_t>(2);
1928 const uint16_t subidx = evt.Get<uint16_t>(4);
1929
1930 if (node!=1 && node !=3)
1931 {
1932 T::Error("Node id must be 1 (az) or 3 (zd).");
1933 return T::GetCurrentState();
1934 }
1935
1936 if (subidx>0xff)
1937 {
1938 T::Error("Subindex must not be larger than 255.");
1939 return T::GetCurrentState();
1940 }
1941
1942 fDrive.RequestSdo(node, index, subidx);
1943
1944 return T::GetCurrentState();
1945 }
1946
1947 int SendSdo(const EventImp &evt)
1948 {
1949 if (!CheckEventSize(evt.GetSize(), "SendSdo", 6+8))
1950 return T::kSM_FatalError;
1951
1952 const uint16_t node = evt.Get<uint16_t>();
1953 const uint16_t index = evt.Get<uint16_t>(2);
1954 const uint16_t subidx = evt.Get<uint16_t>(4);
1955 const uint64_t value = evt.Get<uint64_t>(6);
1956
1957 if (node!=1 && node!=3)
1958 {
1959 T::Error("Node id must be 1 (az) or 3 (zd).");
1960 return T::GetCurrentState();
1961 }
1962
1963 if (subidx>0xff)
1964 {
1965 T::Error("Subindex must not be larger than 255.");
1966 return T::GetCurrentState();
1967 }
1968
1969 fDrive.SendSdo(node, index, subidx, value);
1970
1971 return T::GetCurrentState();
1972 }
1973
1974 // --------------------- Moving and tracking ---------------------
1975
1976 uint16_t fStep;
1977 bool fIsTracking;
1978 Acceleration fAccPointing;
1979 Acceleration fAccTracking;
1980 Acceleration fAccMax;
1981 double fMaxPointingResidual;
1982 double fPointingVelocity;
1983
1984 int InitMovement(const ZdAz &sky, bool tracking=false, const string &name="")
1985 {
1986 fMovementTarget = fPointingModel.SkyToMount(sky);
1987
1988 // Check whether bending is valid!
1989 if (!CheckRange(sky*(180/M_PI)))
1990 return StopMovement();
1991
1992 fStep = 0;
1993 fIsTracking = tracking;
1994
1995 fDrive.SetRpmMode(false); // *NEW* (Stop a previous tracking to avoid the pointing command to be ignored)
1996 fDrive.SetAcceleration(fAccPointing);
1997
1998 if (!tracking)
1999 fDrive.UpdateSource(Time(), name, false);
2000 else
2001 {
2002 const array<double, 5> dim =
2003 {{
2004 fPointingSetup.source.ra,
2005 fPointingSetup.source.dec,
2006 fPointingSetup.wobble_offset * 180/M_PI,
2007 fPointingSetup.wobble_angle * 180/M_PI,
2008 fPointingSetup.orbit_period * 24*60
2009 }};
2010 fDrive.UpdateSource(fPointingSetup.start, dim, fPointingSetup.source.name);
2011 }
2012
2013 return State::kMoving;
2014 }
2015
2016 int MoveTo(const EventImp &evt)
2017 {
2018 if (!CheckEventSize(evt.GetSize(), "MoveTo", 16))
2019 return T::kSM_FatalError;
2020
2021 const double *dat = evt.Ptr<double>();
2022
2023 ostringstream out;
2024 out << "Pointing telescope to Zd=" << dat[0] << "deg Az=" << dat[1] << "deg";
2025 T::Message(out);
2026
2027 return InitMovement(ZdAz(dat[0]*M_PI/180, dat[1]*M_PI/180));
2028 }
2029
2030 int InitTracking()
2031 {
2032 fPointingSetup.start = Time().Mjd();
2033
2034 const PointingData data = CalcPointingPos(fPointingSetup.start);
2035
2036 ostringstream out;
2037 out << "Tracking position now at Zd=" << data.sky.zd*180/M_PI << "deg Az=" << data.sky.az*180/M_PI << "deg";
2038 T::Info(out);
2039
2040 return InitMovement(data.sky, true);
2041 }
2042
2043 int StartTracking(const Source &src, double offset, double angle, double period=0)
2044 {
2045 ostringstream out;
2046 out << "Tracking Ra=" << src.ra << "h Dec=" << src.dec << "deg";
2047 if (!src.name.empty())
2048 out << " [" << src.name << "]";
2049 T::Info(out);
2050
2051 fPointingSetup.planet = kENone;
2052 fPointingSetup.source = src;
2053 fPointingSetup.orbit_period = period / 1440; // [min->day]
2054 fPointingSetup.wobble_angle = angle * M_PI/180; // [deg->rad]
2055 fPointingSetup.wobble_offset = offset * M_PI/180; // [deg->rad]
2056
2057 return InitTracking();
2058 }
2059
2060 int TrackCelest(const Planets_t &p)
2061 {
2062 switch (p)
2063 {
2064 case kEMoon: fPointingSetup.source.name = "Moon"; break;
2065 case kEVenus: fPointingSetup.source.name = "Venus"; break;
2066 case kEMars: fPointingSetup.source.name = "Mars"; break;
2067 case kEJupiter: fPointingSetup.source.name = "Jupiter"; break;
2068 case kESaturn: fPointingSetup.source.name = "Saturn"; break;
2069 default:
2070 T::Error("TrackCelest - Celestial object "+to_string(p)+" not yet supported.");
2071 return T::GetCurrentState();
2072 }
2073
2074 fPointingSetup.planet = p;
2075 fPointingSetup.wobble_offset = 0;
2076
2077 fDrive.UpdateSource(Time(), fPointingSetup.source.name, true);
2078
2079 return InitTracking();
2080 }
2081
2082 int Park()
2083 {
2084 ostringstream out;
2085 out << "Parking telescope at Zd=" << fParkingPos.zd << "deg Az=" << fParkingPos.az << "deg";
2086 T::Message(out);
2087
2088 const int rc = InitMovement(ZdAz(fParkingPos.zd*M_PI/180, fParkingPos.az*M_PI/180), false, "Park");
2089 return rc==State::kMoving ? State::kParking : rc;
2090 }
2091
2092/*
2093 int StartWobble(const double &srcra, const double &srcdec,
2094 const double &woboff, const double &wobang,
2095 const string name="")
2096 {
2097
2098
2099 const double ra = srcra *M_PI/ 12;
2100 const double dec = srcdec*M_PI/180;
2101 const double off = woboff*M_PI/180;
2102 const double dir = wobang*M_PI/180;
2103
2104 if (off==0)
2105 {
2106 const array<double, 6> dim = {{ srcra, srcdec, srcra, srcdec, 0, 0 }};
2107 UpdateSource(dim, src.name);
2108 return StartTracking(src);//srcra, srcdec);
2109 }
2110
2111 const double cosdir = cos(dir);
2112 const double sindir = sin(dir);
2113 const double cosoff = cos(off);
2114 const double sinoff = sin(off);
2115 const double cosdec = cos(dec);
2116 const double sindec = sin(dec);
2117
2118 const double sintheta = sindec*cosoff + cosdec*sinoff*cosdir;
2119 if (sintheta >= 1)
2120 {
2121 T::Error("cos(Zd) > 1");
2122 return T::GetCurrentState();
2123 }
2124
2125 const double costheta = sqrt(1 - sintheta*sintheta);
2126
2127 const double cosdeltara = (cosoff - sindec*sintheta)/(cosdec*costheta);
2128 const double sindeltara = sindir*sinoff/costheta;
2129
2130 const double ndec = asin(sintheta)*180/M_PI;
2131 const double nra = (atan2(sindeltara, cosdeltara) + ra)*12/M_PI;
2132
2133 const array<double, 6> dim = {{ srcra, srcdec, nra, ndec, woboff, wobang }};
2134 UpdateSource(dim, src.name);
2135
2136 return StartTracking(nra, ndec);
2137 }
2138*/
2139
2140 int Wobble(const EventImp &evt)
2141 {
2142 if (!CheckEventSize(evt.GetSize(), "Wobble", 32))
2143 return T::kSM_FatalError;
2144
2145 const double *dat = evt.Ptr<double>();
2146
2147 Source src;
2148 src.ra = dat[0];
2149 src.dec = dat[1];
2150 return StartTracking(src, dat[2], dat[3]);
2151 }
2152
2153 int Orbit(const EventImp &evt)
2154 {
2155 if (!CheckEventSize(evt.GetSize(), "Orbit", 40))
2156 return T::kSM_FatalError;
2157
2158 const double *dat = evt.Ptr<double>();
2159
2160 Source src;
2161 src.ra = dat[0];
2162 src.dec = dat[1];
2163 return StartTracking(src, dat[2], dat[3], dat[4]);
2164 }
2165
2166 const sources::const_iterator GetSourceFromDB(const char *ptr, const char *last)
2167 {
2168 if (find(ptr, last, '\0')==last)
2169 {
2170 T::Fatal("TrackWobble - The name transmitted by dim is not null-terminated.");
2171 throw uint32_t(T::kSM_FatalError);
2172 }
2173
2174 const string name(ptr);
2175
2176 const sources::const_iterator it = fSources.find(name);
2177 if (it==fSources.end())
2178 {
2179 T::Error("Source '"+name+"' not found in list.");
2180 throw uint32_t(T::GetCurrentState());
2181 }
2182
2183 return it;
2184 }
2185
2186 int TrackWobble(const EventImp &evt)
2187 {
2188 if (evt.GetSize()<2)
2189 {
2190 ostringstream msg;
2191 msg << "TrackWobble - Received event has " << evt.GetSize() << " bytes, but expected at least 3.";
2192 T::Fatal(msg);
2193 return T::kSM_FatalError;
2194 }
2195
2196 if (evt.GetSize()==2)
2197 {
2198 ostringstream msg;
2199 msg << "TrackWobble - Source name missing.";
2200 T::Error(msg);
2201 return T::GetCurrentState();
2202 }
2203
2204 const uint16_t wobble = evt.GetUShort();
2205 if (wobble!=1 && wobble!=2)
2206 {
2207 ostringstream msg;
2208 msg << "TrackWobble - Wobble id " << wobble << " undefined, only 1 and 2 allowed.";
2209 T::Error(msg);
2210 return T::GetCurrentState();
2211 }
2212
2213 const char *ptr = evt.Ptr<char>(2);
2214 const char *last = ptr+evt.GetSize()-2;
2215
2216 try
2217 {
2218 const sources::const_iterator it = GetSourceFromDB(ptr, last);
2219
2220 const Source &src = it->second;
2221 return StartTracking(src, src.offset, src.angles[wobble-1]);
2222 }
2223 catch (const uint32_t &e)
2224 {
2225 return e;
2226 }
2227 }
2228
2229 int StartTrackWobble(const char *ptr, size_t size, const double &offset=0, const double &angle=0, double time=0)
2230 {
2231 const char *last = ptr+size;
2232
2233 try
2234 {
2235 const sources::const_iterator it = GetSourceFromDB(ptr, last);
2236
2237 const Source &src = it->second;
2238 return StartTracking(src, offset<0?0.6/*src.offset*/:offset, angle, time);
2239 }
2240 catch (const uint32_t &e)
2241 {
2242 return e;
2243 }
2244 }
2245
2246 int Track(const EventImp &evt)
2247 {
2248 if (!CheckEventSize(evt.GetSize(), "Track", 16))
2249 return T::kSM_FatalError;
2250
2251 Source src;
2252
2253 src.name = "";
2254 src.ra = evt.Get<double>(0);
2255 src.dec = evt.Get<double>(8);
2256
2257 return StartTracking(src, 0, 0);
2258 }
2259
2260 int TrackSource(const EventImp &evt)
2261 {
2262 if (evt.GetSize()<16)
2263 {
2264 ostringstream msg;
2265 msg << "TrackOn - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2266 T::Fatal(msg);
2267 return T::kSM_FatalError;
2268 }
2269
2270 if (evt.GetSize()==16)
2271 {
2272 ostringstream msg;
2273 msg << "TrackOn - Source name missing.";
2274 T::Error(msg);
2275 return T::GetCurrentState();
2276 }
2277
2278 const double offset = evt.Get<double>(0);
2279 const double angle = evt.Get<double>(8);
2280
2281 return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, offset, angle);
2282 }
2283
2284 int TrackOn(const EventImp &evt)
2285 {
2286 if (evt.GetSize()==0)
2287 {
2288 ostringstream msg;
2289 msg << "TrackOn - Source name missing.";
2290 T::Error(msg);
2291 return T::GetCurrentState();
2292 }
2293
2294 return StartTrackWobble(evt.Ptr<char>(), evt.GetSize());
2295 }
2296
2297 int TrackOrbit(const EventImp &evt)
2298 {
2299 if (evt.GetSize()<16)
2300 {
2301 ostringstream msg;
2302 msg << "TrackOrbit - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2303 T::Fatal(msg);
2304 return T::kSM_FatalError;
2305 }
2306 if (evt.GetSize()==16)
2307 {
2308 ostringstream msg;
2309 msg << "TrackOrbit - Source name missing.";
2310 T::Error(msg);
2311 return T::GetCurrentState();
2312 }
2313
2314 const double angle = evt.Get<double>(0);
2315 const double time = evt.Get<double>(8);
2316
2317 return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, -1, angle, time);
2318 }
2319
2320 int StopMovement()
2321 {
2322 fDrive.SetAcceleration(fAccMax);
2323 fDrive.SetRpmMode(false);
2324
2325 fTrackingLoop.cancel();
2326
2327 fDrive.UpdateSource(Time(), "", false);
2328
2329 return State::kStopping;
2330 }
2331
2332 // --------------------- Others ---------------------
2333
2334 int TPoint()
2335 {
2336 T::Info("TPoint initiated.");
2337 Dim::SendCommandNB("TPOINT/EXECUTE");
2338 return T::GetCurrentState();
2339 }
2340
2341 int SetLedBrightness(const EventImp &evt)
2342 {
2343 if (!CheckEventSize(evt.GetSize(), "SetLedBrightness", 8))
2344 return T::kSM_FatalError;
2345
2346 const uint32_t *led = evt.Ptr<uint32_t>();
2347
2348 fDrive.SetLedVoltage(led[0], led[1]);
2349
2350 return T::GetCurrentState();
2351 }
2352
2353 int SetLedsOff()
2354 {
2355 fDrive.SetLedVoltage(0, 0);
2356 return T::GetCurrentState();
2357 }
2358
2359 // --------------------- Internal ---------------------
2360
2361 int SetVerbosity(const EventImp &evt)
2362 {
2363 if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 2))
2364 return T::kSM_FatalError;
2365
2366 fDrive.SetVerbosity(evt.GetUShort());
2367
2368 return T::GetCurrentState();
2369 }
2370
2371 int Print()
2372 {
2373 for (auto it=fSources.begin(); it!=fSources.end(); it++)
2374 {
2375 const string &name = it->first;
2376 const Source &src = it->second;
2377
2378 T::Out() << name << ",";
2379 T::Out() << src.ra << "," << src.dec << "," << src.offset << ",";
2380 T::Out() << src.angles[0] << "," << src.angles[1] << endl;
2381 }
2382 return T::GetCurrentState();
2383 }
2384
2385 int Unlock()
2386 {
2387 const int rc = CheckState();
2388 return rc<0 ? State::kInitialized : rc;
2389 }
2390
2391 int ReloadSources()
2392 {
2393 try
2394 {
2395 ReadDatabase();
2396 }
2397 catch (const exception &e)
2398 {
2399 T::Error("Reading sources from databse failed: "+string(e.what()));
2400 }
2401 return T::GetCurrentState();
2402 }
2403
2404 int Disconnect()
2405 {
2406 // Close all connections
2407 fDrive.PostClose(false);
2408
2409 /*
2410 // Now wait until all connection have been closed and
2411 // all pending handlers have been processed
2412 poll();
2413 */
2414
2415 return T::GetCurrentState();
2416 }
2417
2418 int Reconnect(const EventImp &evt)
2419 {
2420 // Close all connections to supress the warning in SetEndpoint
2421 fDrive.PostClose(false);
2422
2423 // Now wait until all connection have been closed and
2424 // all pending handlers have been processed
2425 ba::io_service::poll();
2426
2427 if (evt.GetBool())
2428 fDrive.SetEndpoint(evt.GetString());
2429
2430 // Now we can reopen the connection
2431 fDrive.PostClose(true);
2432
2433 return T::GetCurrentState();
2434 }
2435
2436 // ========================= Tracking code =============================
2437
2438 int UpdateTrackingPosition()
2439 {
2440 // First calculate deviation between
2441 // command position and nominal position
2442 //fPointing.mount = sepos; // [deg] ref pos for alignment
2443 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2444
2445 // Get current position and calculate deviation
2446 const Encoder sepos = fDrive.GetSePos()*360; // [deg]
2447 const Encoder dev = sepos - data.mount;
2448
2449 // Calculate absolut deviation on the sky
2450 const double absdev = GetDevAbs(data.mount.zd, sepos.zd, dev.az)*3600;
2451
2452 // Smoothing
2453 fDevBuffer[fDevCount++%5] = absdev;
2454
2455 // Calculate average
2456 const uint8_t cnt = fDevCount<5 ? fDevCount : 5;
2457 const double avgdev = accumulate(fDevBuffer.begin(), fDevBuffer.begin()+cnt, 0.)/cnt;
2458
2459 // Count the consecutive number of avgdev below fDeviationLimit
2460 if (avgdev<fDeviationLimit)
2461 fTrackingCounter++;
2462 else
2463 fTrackingCounter = 0;
2464
2465 const double ha = fmod(fDrive.GetSeTime(),1)*24 - Nova::ORM().lng/15;
2466
2467 array<double, 12> dim;
2468 dim[0] = data.pointing.ra * 12/M_PI; // Ra [h] optical axis
2469 dim[1] = data.pointing.dec * 180/M_PI; // Dec [deg] optical axis
2470 dim[2] = ha - data.pointing.ra; // Ha [h] optical axis
2471 dim[3] = data.source.ra * 12/M_PI; // SrcRa [h] source position
2472 dim[4] = data.source.dec * 180/M_PI; // SrcDec [deg] source position
2473 dim[5] = ha - data.source.ra; // SrcHa [h] source position
2474 dim[6] = data.sky.zd * 180/M_PI; // Zd [deg] optical axis
2475 dim[7] = data.sky.az * 180/M_PI; // Az [deg] optical axis
2476 dim[8] = dev.zd; // dZd [deg] control deviation
2477 dim[9] = dev.az; // dAz [deg] control deviation
2478 dim[10] = absdev; // dev [arcsec] absolute control deviation
2479 dim[11] = avgdev; // dev [arcsec] average control deviation
2480
2481 fDrive.UpdateTracking(fDrive.GetSeTime(), dim);
2482
2483 if (fDrive.GetVerbosity())
2484 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Deviation [deg] " << absdev << "\"|" << avgdev << "\"|" << fDevCount<< " dZd=" << dev.zd*3600 << "\" dAz=" << dev.az*3600 << "\"" << endl;
2485
2486 // Maximum deviation execeeded -> fall back to Tracking state
2487 if (T::GetCurrentState()==State::kOnTrack && avgdev>fDeviationMax)
2488 return State::kTracking;
2489
2490 // Condition for OnTrack state achieved -> enhance to OnTrack state
2491 if (T::GetCurrentState()==State::kTracking && fTrackingCounter>=fDeviationCounter)
2492 return State::kOnTrack;
2493
2494 // No state change
2495 return T::GetCurrentState();
2496 }
2497
2498 void UpdatePointingPosition()
2499 {
2500 const Encoder sepos = fDrive.GetSePos()*360; // [deg] ref pos for alignment
2501
2502 const ZdAz pos = fPointingModel.MountToSky(sepos);
2503
2504 array<double, 2> data;
2505 data[0] = pos.zd*180/M_PI; // Zd [deg]
2506 data[1] = pos.az*180/M_PI; // Az [deg]
2507 fDrive.UpdatePointing(fDrive.GetSeTime(), data);
2508
2509 if (fDrive.GetVerbosity())
2510 T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Position [deg] " << pos.zd*180/M_PI << " " << pos.az*180/M_PI << endl;
2511 }
2512/*
2513 void TrackingLoop2(const boost::system::error_code &error=boost::system::error_code())
2514 {
2515 if (error==ba::error::basic_errors::operation_aborted)
2516 return;
2517
2518 if (error)
2519 {
2520 ostringstream str;
2521 str << "TrackingLoop: " << error.message() << " (" << error << ")";// << endl;
2522 T::Error(str);
2523 return;
2524 }
2525
2526 if (T::GetCurrentState()!=State::kTracking &&
2527 T::GetCurrentState()!=State::kOnTrack)
2528 return;
2529
2530 //
2531 // Update speed as often as possible.
2532 // make sure, that dt is around 10 times larger than the
2533 // update time
2534 //
2535 // The loop should not be executed faster than the ramp of
2536 // a change in the velocity can be followed.
2537 //
2538 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(2000/8));
2539
2540 // We want to reach the theoretical position exactly in about 7.6s
2541 const float dt = 2.0;
2542
2543 // Get current position and corresponding time
2544 const double mjd = fDrive.GetSeTime();
2545 //const Encoder sepos = fDrive.GetSePos(); // [rev]
2546
2547 // Request Target position for Now+dt
2548 //fPointing.mount = sepos*360; // [deg] ref pos for alignment
2549
2550 fPointing.Update(mjd+dt/24/3600, fWeather, fWeatherTimeout);
2551 if (!CheckRange(fPointing.sky))
2552 {
2553 StopMovement();
2554 T::HandleNewState(State::kAllowedRangeExceeded, 0, "by TrackingLoop");
2555 return;
2556 }
2557
2558 // Destination position at t+dt in re-units
2559 const Encoder dest = fPointing.mount*(1./360); // [rev]
2560
2561 // Current position
2562 const Encoder sepos = fDrive.GetSePos(); // [rev]
2563
2564 // Now calculate the distance to move from now
2565 // to a time in t+dt.
2566 const Encoder dist = dest-sepos; // [rev]
2567
2568 // This is to avoid that we are always too slow
2569 // On average 30ms have been passed since the last report was
2570 // received, it is fair to assume that on average als0 30ms
2571 // will pass until this report arrives. Without that correction
2572 // we were always too fast (60ms = 3% von 2000ms)
2573 const double deltat = 60./1000/60;//(Time().Mjd() - mjd)/1440; //[min]
2574
2575 // Assume a mainly linear movements. We can estimate the position
2576 // at which we are now
2577 static Velocity last = 0;
2578
2579 // Velocity to go [rev/min] to reach the right position at time t+dt
2580 // correct for the duration of RaDec2AltAz
2581 const Velocity vt = (dist - last*deltat) / (dt/60 - deltat); //[rev/min]
2582
2583 last = vt;
2584
2585 if (fDrive.GetVerbosity()>1)
2586 {
2587 T::Out() << "Destination [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2588 T::Out() << "Position [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2589 T::Out() << "Distance [arcmin] " << dist.zd *360*60 << " " << dist.az *360*60 << endl;
2590 T::Out() << "Velocity [rpm] " << vt.zd << " " << vt.az << endl;
2591 T::Out() << "Delta T (enc) [ms] " << fabs(mjd-fDrive.fPdoTime2[0].Mjd())*24*3600*1000 << endl;
2592 T::Out() << "Delta T (now) [ms] " << (Time().Mjd()-mjd)*24*3600*1000 << endl;
2593 }
2594
2595 // Tracking loop every 250ms
2596 // Vorsteuerung 2s
2597 // Delta T (enc) 5ms, every 5th, 25ms
2598 // Delta T (now) equal dist 5ms-35 plus equal dist 25-55 (0.2%-2% of 2s)
2599
2600 //
2601 // FIXME: check if the drive is fast enough to follow the star
2602 //
2603 // Velocity units (would be 100 for %)
2604
2605 fDrive.SetTrackingVelocity(vt);
2606
2607 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2608 this, ba::placeholders::error));
2609 }
2610*/
2611 void TrackingLoop(const boost::system::error_code &error=boost::system::error_code())
2612 {
2613 if (error==ba::error::basic_errors::operation_aborted)
2614 return;
2615
2616 if (error)
2617 {
2618 ostringstream str;
2619 str << "TrackingLoop: " << error.message() << " (" << error << ")";// << endl;
2620 T::Error(str);
2621 return;
2622 }
2623
2624 if (T::GetCurrentState()!=State::kTracking &&
2625 T::GetCurrentState()!=State::kOnTrack)
2626 return;
2627
2628 //
2629 // Update speed as often as possible.
2630 // make sure, that dt is around 10 times larger than the
2631 // update time
2632 //
2633 // The loop should not be executed faster than the ramp of
2634 // a change in the velocity can be followed.
2635 //
2636 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(250));
2637
2638 const double mjd = Time().Mjd();
2639
2640 // I assume that it takes about 50ms for the value to be
2641 // transmitted and the drive needs time to follow as well (maybe
2642 // more than 50ms), therefore the calculated speec is calculated
2643 // for a moment 50ms in the future
2644 const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2645 const PointingData data0 = CalcPointingPos(mjd-0.45/24/3600);
2646 const PointingData data1 = CalcPointingPos(mjd+0.55/24/3600);
2647
2648 const Encoder dest = data.mount *(1./360); // [rev]
2649 const Encoder dest0 = data0.mount*(1./360); // [rev]
2650 const Encoder dest1 = data1.mount*(1./360); // [rev]
2651
2652 if (!CheckRange(data1.sky))
2653 {
2654 StopMovement();
2655 T::HandleNewState(State::kAllowedRangeExceeded, 0, "by TrackingLoop");
2656 return;
2657 }
2658
2659 // Current position
2660 const Encoder sepos = fDrive.GetSePos(); // [rev]
2661
2662 // Now calculate the current velocity
2663 const Encoder dist = dest1 - dest0; // [rev] Distance between t-1s and t+1s
2664 const Velocity vel = dist/(1./60); // [rev/min] Actual velocity of the pointing position
2665
2666 const Encoder dev = sepos - dest; // [rev] Current control deviation
2667 const Velocity vt = vel - dev/(1./60); // [rev/min] Correct velocity by recent control deviation
2668 // correct control deviation with 5s
2669 if (fDrive.GetVerbosity()>1)
2670 {
2671 T::Out() << "Ideal position [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2672 T::Out() << "Encoder pos. [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2673 T::Out() << "Deviation [arcmin] " << dev.zd *360*60 << " " << dev.az *360*60 << endl;
2674 T::Out() << "Distance 1s [arcmin] " << dist.zd *360*60 << " " << dist.az *360*60 << endl;
2675 T::Out() << "Velocity 1s [rpm] " << vt.zd << " " << vt.az << endl;
2676 T::Out() << "Delta T (enc) [ms] " << fabs(mjd-fDrive.fPdoTime2[0].Mjd())*24*3600*1000 << endl;
2677 T::Out() << "Delta T (now) [ms] " << (Time().Mjd()-mjd)*24*3600*1000 << endl;
2678 }
2679
2680 // Tracking loop every 250ms
2681 // Vorsteuerung 2s
2682 // Delta T (enc) 5ms, every 5th, 25ms
2683 // Delta T (now) equal dist 5ms-35 plus equal dist 25-55 (0.2%-2% of 2s)
2684
2685 //
2686 // FIXME: check if the drive is fast enough to follow the star
2687 //
2688 // Velocity units (would be 100 for %)
2689
2690 fDrive.SetTrackingVelocity(vt);
2691
2692 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2693 this, ba::placeholders::error));
2694 }
2695
2696 // =====================================================================
2697
2698 int CheckState()
2699 {
2700 if (!fDrive.IsConnected())
2701 return State::kDisconnected;
2702
2703 if (!fDrive.IsOnline())
2704 return State::kUnavailable;
2705
2706 // FIXME: This can prevent parking in case e.g.
2707 // of e8029 Position limit exceeded
2708 if (fDrive.HasError())
2709 {
2710 if (T::GetCurrentState()==State::kOnTrack ||
2711 T::GetCurrentState()==State::kTracking ||
2712 T::GetCurrentState()==State::kMoving ||
2713 T::GetCurrentState()==State::kParking)
2714 return StopMovement();
2715
2716 if (T::GetCurrentState()==State::kStopping && fDrive.IsMoving())
2717 return State::kStopping;
2718
2719 return StateMachineImp::kSM_Error;
2720 }
2721
2722 // This can happen if one of the drives is not in RF.
2723 // Usually this only happens when the drive is not yet in RF
2724 // or an error was just cleared. Usually there is no way that
2725 // a drive goes below the RF state during operation without
2726 // a warning or error message.
2727 if (fDrive.IsOnline() && !fDrive.IsReady())
2728 return State::kAvailable;
2729
2730 if (fDrive.IsOnline() && fDrive.IsBlocked())
2731 return State::kBlocked;
2732
2733 // This is the case as soon as the init commands were send
2734 // after a connection to the SPS was established
2735 if (fDrive.IsOnline() && fDrive.IsReady() && !fDrive.IsInitialized())
2736 return State::kArmed;
2737
2738 return -1;
2739 }
2740
2741 int Execute()
2742 {
2743 const Time now;
2744 if (now>fSunRise)
2745 {
2746 if (T::GetCurrentState()>State::kLocked)
2747 return Park();
2748
2749 if (T::GetCurrentState()==State::kLocked)
2750 {
2751 fSunRise = now.GetNextSunRise();
2752
2753 ostringstream msg;
2754 msg << "Next sun-rise will be at " << fSunRise;
2755 T::Info(msg);
2756
2757 return State::kLocked;
2758 }
2759 }
2760
2761 if (T::GetCurrentState()==State::kLocked)
2762 return State::kLocked;
2763
2764 // FIXME: Send STOP if IsPositioning or RpmActive but no
2765 // Moving or Tracking state
2766
2767 const int rc = CheckState();
2768 if (rc>0)
2769 return rc;
2770 /*
2771
2772 if (!fDrive.IsConnected())
2773 return State::kDisconnected;
2774
2775 if (!fDrive.IsOnline())
2776 return State::kUnavailable;
2777
2778 // FIXME: This can prevent parking in case e.g.
2779 // of e8029 Position limit exceeded
2780 if (fDrive.HasError())
2781 {
2782 if (T::GetCurrentState()==State::kOnTrack ||
2783 T::GetCurrentState()==State::kTracking ||
2784 T::GetCurrentState()==State::kMoving ||
2785 T::GetCurrentState()==State::kParking)
2786 return StopMovement();
2787
2788 if (T::GetCurrentState()==State::kStopping && fDrive.IsMoving())
2789 return State::kStopping;
2790
2791 return StateMachineImp::kSM_Error;
2792 }
2793
2794 // This can happen if one of the drives is not in RF.
2795 // Usually this only happens when the drive is not yet in RF
2796 // or an error was just cleared. Usually there is no way that
2797 // a drive goes below the RF state during operation without
2798 // a warning or error message.
2799 if (fDrive.IsOnline() && !fDrive.IsReady())
2800 return State::kAvailable;
2801
2802 if (fDrive.IsOnline() && fDrive.IsBlocked())
2803 return State::kBlocked;
2804
2805 // This is the case as soon as the init commands were send
2806 // after a connection to the SPS was established
2807 if (fDrive.IsOnline() && fDrive.IsReady() && !fDrive.IsInitialized())
2808 return State::kArmed;
2809*/
2810 // Once every second
2811 static time_t lastTime = 0;
2812 const time_t tm = time(NULL);
2813 if (lastTime!=tm && fDrive.IsInitialized())
2814 {
2815 lastTime=tm;
2816
2817 UpdatePointingPosition();
2818
2819 if (T::GetCurrentState()==State::kTracking || T::GetCurrentState()==State::kOnTrack)
2820 return UpdateTrackingPosition();
2821 }
2822
2823 if (T::GetCurrentState()==State::kStopping && !fDrive.IsMoving())
2824 return State::kArmed;
2825
2826 if ((T::GetCurrentState()==State::kMoving ||
2827 T::GetCurrentState()==State::kParking) && !fDrive.IsMoving())
2828 {
2829 if (fIsTracking && fStep==1)
2830 {
2831 // Init tracking
2832 fDrive.SetAcceleration(fAccTracking);
2833 fDrive.SetRpmMode(true);
2834
2835 fDevCount = 0;
2836 fTrackingCounter = 0;
2837
2838 fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(1));
2839 fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2840 this, ba::placeholders::error));
2841
2842 fPointingSetup.start = Time().Mjd();
2843
2844 const PointingData data = CalcPointingPos(fPointingSetup.start);
2845
2846 ostringstream out;
2847 out << "Start tracking at Ra=" << data.pointing.ra*12/M_PI << "h Dec=" << data.pointing.dec*180/M_PI << "deg";
2848 T::Info(out);
2849
2850 return State::kTracking;
2851 }
2852
2853 // Get feedback 2
2854 const Encoder dest = fMovementTarget*(1./360); // [rev]
2855 const Encoder sepos = fDrive.GetSePos(); // [rev]
2856
2857 // Calculate residual to move deviation
2858 const Encoder dist = dest - sepos; // [rev]
2859
2860 // Check which axis should still be moved
2861 Encoder cd = dist; // [rev]
2862 cd *= 1./fMaxPointingResidual; // Scale to units of the maximum residual
2863 cd = cd.Abs();
2864
2865 // Check if there is a control deviation on the axis
2866 const bool cdzd = cd.zd>1;
2867 const bool cdaz = cd.az>1;
2868
2869 if (!fIsTracking)
2870 {
2871 // check if we reached the correct position already
2872 if (!cdzd && !cdaz)
2873 {
2874 T::Info("Target position reached in "+to_string(fStep)+" steps.");
2875 return T::GetCurrentState()==State::kParking ? State::kLocked : State::kArmed;
2876 }
2877
2878 if (fStep==10)
2879 {
2880 T::Error("Target position not reached in "+to_string(fStep)+" steps.");
2881 return State::kPositioningFailed;
2882 }
2883 }
2884
2885 const Encoder t = dist.Abs()/fDrive.GetVelUnit();
2886
2887 const Velocity vel =
2888 t.zd > t.az ?
2889 Velocity(1, t.zd==0?0:t.az/t.zd) :
2890 Velocity(t.az==0?0:t.zd/t.az, 1);
2891
2892 if (fDrive.GetVerbosity())
2893 {
2894 T::Out() << "Moving step " << fStep << endl;
2895 T::Out() << "Encoder [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2896 T::Out() << "Destination [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2897 T::Out() << "Residual [deg] " << dist.zd *360 << " " << dist.az *360 << endl;
2898 T::Out() << "Residual/max [1] " << cd.zd << " " << cd.az << endl;
2899 T::Out() << "Rel. time [1] " << t.zd << " " << t.az << endl;
2900 T::Out() << "Rel. velocity [1] " << vel.zd << " " << vel.az << endl;
2901 }
2902
2903 fDrive.SetPointingVelocity(vel, fPointingVelocity);
2904 fDrive.StartAbsolutePositioning(dest, cdzd, cdaz);
2905
2906 ostringstream out;
2907 if (fStep==0)
2908 out << "Moving to encoder Zd=" << dest.zd*360 << "deg Az=" << dest.az*360 << "deg";
2909 else
2910 out << "Moving residual of dZd=" << dist.zd*360*60 << "' dAz=" << dist.az*360*60 << "'";
2911 T::Info(out);
2912
2913 fStep++;
2914 }
2915
2916 return T::GetCurrentState()>=State::kInitialized ?
2917 T::GetCurrentState() : State::kInitialized;
2918 }
2919
2920public:
2921 StateMachineDrive(ostream &out=cout) :
2922 StateMachineAsio<T>(out, "DRIVE_CONTROL"), fDrive(*this, *this),
2923 fTrackingLoop(*this), fSunRise(Time().GetNextSunRise()), fDevBuffer(5)
2924 {
2925
2926 T::Subscribe("MAGIC_WEATHER/DATA")
2927 (bind(&StateMachineDrive::HandleWeatherData, this, placeholders::_1));
2928
2929 T::Subscribe("TPOINT/DATA")
2930 (bind(&StateMachineDrive::HandleTPoint, this, placeholders::_1));
2931
2932 /*
2933 kDisconnected = 1,
2934 kConnected,
2935 kLocked,
2936 kUnavailable, // IndraDrives not connected
2937 kAvailable, // IndraDrives connected, but not in Af
2938 kBlocked, // Drive blocked by manual operation of emergency button
2939 kArmed, // IndraDrives Af, not yet initialized
2940 kInitialized, // IndraDrives Af, initialized
2941 kStopping,
2942 kParking,
2943 kMoving,
2944 kTracking,
2945 kOnTrack,
2946
2947 */
2948 // State names
2949 T::AddStateName(State::kDisconnected, "Disconnected",
2950 "No connection to SPS");
2951 T::AddStateName(State::kConnected, "Connected",
2952 "Connection to SPS, no information received yet");
2953
2954 T::AddStateName(State::kLocked, "Locked",
2955 "Drive system is locked (will not accept commands)");
2956
2957 T::AddStateName(State::kUnavailable, "Unavailable",
2958 "Connected to SPS, no connection to at least one IndraDrives");
2959 T::AddStateName(State::kAvailable, "Available",
2960 "Connected to SPS and to IndraDrives, but at least one drive not in RF");
2961 T::AddStateName(State::kBlocked, "Blocked",
2962 "Drive system is blocked by manual operation or an released emergeny button");
2963 T::AddStateName(State::kArmed, "Armed",
2964 "Connected to SPS and IndraDrives in RF, but not yet initialized");
2965 T::AddStateName(State::kInitialized, "Initialized",
2966 "Connected to SPS and IndraDrives in RF and initialized");
2967
2968 T::AddStateName(State::kStopping, "Stopping",
2969 "Stop command sent, waiting for telescope to be still");
2970 T::AddStateName(State::kParking, "Parking",
2971 "Telescope in parking operation, waiting for telescope to be still");
2972 T::AddStateName(State::kMoving, "Moving",
2973 "Telescope moving");
2974 T::AddStateName(State::kTracking, "Tracking",
2975 "Telescope in tracking mode");
2976 T::AddStateName(State::kOnTrack, "OnTrack",
2977 "Telescope tracking stable");
2978
2979 T::AddStateName(State::kPositioningFailed, "PositioningFailed",
2980 "Target position was not reached within ten steps");
2981 T::AddStateName(State::kAllowedRangeExceeded, "OutOfRange",
2982 "Telecope went out of range during tracking");
2983
2984
2985 T::AddEvent("REQUEST_SDO", "S:3", State::kArmed)
2986 (bind(&StateMachineDrive::RequestSdo, this, placeholders::_1))
2987 ("Request an SDO from the drive"
2988 "|node[uint32]:Node identifier (1:az, 3:zd)"
2989 "|index[uint32]:SDO index"
2990 "|subindex[uint32]:SDO subindex");
2991
2992 T::AddEvent("SET_SDO", "S:3;X:1", State::kArmed)
2993 (bind(&StateMachineDrive::SendSdo, this, placeholders::_1))
2994 ("Request an SDO from the drive"
2995 "|node[uint32]:Node identifier (1:az, 3:zd)"
2996 "|index[uint32]:SDO index"
2997 "|subindex[uint32]:SDO subindex"
2998 "|value[uint64]:Value");
2999
3000 // Drive Commands
3001 T::AddEvent("MOVE_TO", "D:2", State::kInitialized) // ->ZDAZ
3002 (bind(&StateMachineDrive::MoveTo, this, placeholders::_1))
3003 ("Move the telescope to the given local sky coordinates"
3004 "|Zd[deg]:Zenith distance"
3005 "|Az[deg]:Azimuth");
3006
3007 T::AddEvent("TRACK", "D:2", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3008 (bind(&StateMachineDrive::Track, this, placeholders::_1))
3009 ("Move the telescope to the given sky coordinates and start tracking them"
3010 "|Ra[h]:Right ascension"
3011 "|Dec[deg]:Declination");
3012
3013 T::AddEvent("WOBBLE", "D:4", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3014 (bind(&StateMachineDrive::Wobble, this, placeholders::_1))
3015 ("Move the telescope to the given wobble position around the given sky coordinates and start tracking them"
3016 "|Ra[h]:Right ascension"
3017 "|Dec[deg]:Declination"
3018 "|Offset[deg]:Wobble offset"
3019 "|Angle[deg]:Wobble angle");
3020
3021 T::AddEvent("ORBIT", "D:5", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3022 (bind(&StateMachineDrive::Orbit, this, placeholders::_1))
3023 ("Move the telescope in a circle around the source"
3024 "|Ra[h]:Right ascension"
3025 "|Dec[deg]:Declination"
3026 "|Offset[deg]:Wobble offset"
3027 "|Angle[deg]:Starting angle"
3028 "|Period[min]:Time for one orbit");
3029
3030 T::AddEvent("TRACK_SOURCE", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3031 (bind(&StateMachineDrive::TrackSource, this, placeholders::_1))
3032 ("Move the telescope to the given wobble position around the given source and start tracking"
3033 "|Offset[deg]:Wobble offset"
3034 "|Angle[deg]:Wobble angle"
3035 "|Name[string]:Source name");
3036
3037 T::AddEvent("TRACK_WOBBLE", "S:1;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3038 (bind(&StateMachineDrive::TrackWobble, this, placeholders::_1))
3039 ("Move the telescope to the given wobble position around the given source and start tracking"
3040 "|Id:Wobble angle id (1 or 2)"
3041 "|Name[string]:Source name");
3042
3043 T::AddEvent("TRACK_ORBIT", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3044 (bind(&StateMachineDrive::TrackOrbit, this, placeholders::_1))
3045 ("Move the telescope in a circle around the source"
3046 "|Angle[deg]:Starting angle"
3047 "|Period[min]:Time for one orbit"
3048 "|Name[string]:Source name");
3049
3050 T::AddEvent("TRACK_ON", "C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
3051 (bind(&StateMachineDrive::TrackOn, this, placeholders::_1))
3052 ("Move the telescope to the given position and start tracking"
3053 "|Name[string]:Source name");
3054/*
3055 T::AddEvent("RESUME", StateMachineImp::kSM_Error)
3056 (bind(&StateMachineDrive::Resume, this))
3057 ("If drive is in Error state, this can b used to resume the last tracking command, if the last command sent to cosy was a tracking command.");
3058*/
3059 T::AddEvent("MOON", State::kInitialized, State::kTracking, State::kOnTrack)
3060 (bind(&StateMachineDrive::TrackCelest, this, kEMoon))
3061 ("Start tracking the moon");
3062 T::AddEvent("VENUS", State::kInitialized, State::kTracking, State::kOnTrack)
3063 (bind(&StateMachineDrive::TrackCelest, this, kEVenus))
3064 ("Start tracking Venus");
3065 T::AddEvent("MARS", State::kInitialized, State::kTracking, State::kOnTrack)
3066 (bind(&StateMachineDrive::TrackCelest, this, kEMars))
3067 ("Start tracking Mars");
3068 T::AddEvent("JUPITER", State::kInitialized, State::kTracking, State::kOnTrack)
3069 (bind(&StateMachineDrive::TrackCelest, this, kEJupiter))
3070 ("Start tracking Jupiter");
3071 T::AddEvent("SATURN", State::kInitialized, State::kTracking, State::kOnTrack)
3072 (bind(&StateMachineDrive::TrackCelest, this, kESaturn))
3073 ("Start tracking Saturn");
3074
3075 // FIXME: What to do in error state?
3076 T::AddEvent("PARK", State::kInitialized, State::kMoving, State::kTracking, State::kOnTrack)
3077 (bind(&StateMachineDrive::Park, this))
3078 ("Park the telescope");
3079
3080 T::AddEvent("STOP")
3081 (bind(&StateMachineDrive::StopMovement, this))
3082 ("Stop any kind of movement.");
3083
3084 T::AddEvent("TPOINT", State::kOnTrack)
3085 (bind(&StateMachineDrive::TPoint, this))
3086 ("Take a TPoint");
3087
3088 T::AddEvent("SET_LED_BRIGHTNESS", "I:2")
3089 (bind(&StateMachineDrive::SetLedBrightness, this, placeholders::_1))
3090 ("Set the LED brightness of the top and bottom leds"
3091 "|top[au]:Allowed range 0-32767 for top LEDs"
3092 "|bot[au]:Allowed range 0-32767 for bottom LEDs");
3093
3094 T::AddEvent("LEDS_OFF")
3095 (bind(&StateMachineDrive::SetLedsOff, this))
3096 ("Switch off TPoint LEDs");
3097
3098 T::AddEvent("UNLOCK", Drive::State::kLocked)
3099 (bind(&StateMachineDrive::Unlock, this))
3100 ("Unlock locked state.");
3101
3102 // Verbosity commands
3103 T::AddEvent("SET_VERBOSITY", "S:1")
3104 (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1))
3105 ("Set verbosity state"
3106 "|verbosity[uint16]:disable or enable verbosity for received data (yes/no), except dynamic data");
3107
3108 // Conenction commands
3109 T::AddEvent("DISCONNECT", State::kConnected)
3110 (bind(&StateMachineDrive::Disconnect, this))
3111 ("disconnect from ethernet");
3112
3113 T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
3114 (bind(&StateMachineDrive::Reconnect, this, placeholders::_1))
3115 ("(Re)connect Ethernet connection to SPS, a new address can be given"
3116 "|[host][string]:new ethernet address in the form <host:port>");
3117
3118
3119 T::AddEvent("PRINT")
3120 (bind(&StateMachineDrive::Print, this))
3121 ("Print source list.");
3122
3123 T::AddEvent("RELOAD_SOURCES", State::kDisconnected, State::kConnected, State::kArmed, State::kInitialized, State::kLocked)
3124 (bind(&StateMachineDrive::ReloadSources, this))
3125 ("Reload sources from database after database has changed..");
3126
3127
3128 //fDrive.SetUpdateStatus(std::bind(&StateMachineDrive::UpdateStatus, this, placeholders::_1, placeholders::_2));
3129 fDrive.StartConnect();
3130 }
3131
3132 void SetEndpoint(const string &url)
3133 {
3134 fDrive.SetEndpoint(url);
3135 }
3136
3137 bool AddSource(const string &name, const Source &src)
3138 {
3139 const auto it = fSources.find(name);
3140 if (it!=fSources.end())
3141 T::Warn("Source '"+name+"' already in list... overwriting.");
3142
3143 fSources[name] = src;
3144 return it==fSources.end();
3145 }
3146
3147 void ReadDatabase(bool print=true)
3148 {
3149#ifdef HAVE_SQL
3150 Database db(fDatabase);
3151
3152 T::Message("Connected to '"+db.uri()+"'");
3153
3154 const mysqlpp::StoreQueryResult res =
3155 db.query("SELECT fSourceName, fRightAscension, fDeclination, fWobbleOffset, fWobbleAngle0, fWobbleAngle1, fMagnitude FROM Source").store();
3156
3157 fSources.clear();
3158 for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
3159 {
3160 const string name = (*v)[0].c_str();
3161
3162 Source src;
3163 src.name = name;
3164 src.ra = (*v)[1];
3165 src.dec = (*v)[2];
3166 src.offset = (*v)[3];
3167 src.angles[0] = (*v)[4];
3168 src.angles[1] = (*v)[5];
3169 src.mag = (*v)[6] ? double((*v)[6]) : 0;
3170 AddSource(name, src);
3171
3172 if (!print)
3173 continue;
3174
3175 ostringstream msg;
3176 msg << " " << name << setprecision(8) << ": Ra=" << src.ra << "h Dec=" << src.dec << "deg";
3177 msg << " Wobble=[" << src.offset << "," << src.angles[0] << "," << src.angles[1] << "] Mag=" << src.mag;
3178 T::Message(msg);
3179 }
3180#else
3181 T::Warn("MySQL support not compiled into the program.");
3182#endif
3183 }
3184
3185 int EvalOptions(Configuration &conf)
3186 {
3187 if (!fSunRise)
3188 return 1;
3189
3190 fDrive.SetVerbose(!conf.Get<bool>("quiet"));
3191
3192 fMaxPointingResidual = conf.Get<double>("pointing.max.residual");
3193 fPointingVelocity = conf.Get<double>("pointing.velocity");
3194
3195 fPointingMin = Encoder(conf.Get<double>("pointing.min.zd"),
3196 conf.Get<double>("pointing.min.az"));
3197 fPointingMax = Encoder(conf.Get<double>("pointing.max.zd"),
3198 conf.Get<double>("pointing.max.az"));
3199
3200 fParkingPos.zd = conf.Has("parking-pos.zd") ? conf.Get<double>("parking-pos.zd") : 90;
3201 fParkingPos.az = conf.Has("parking-pos.az") ? conf.Get<double>("parking-pos.az") : 0;
3202
3203 if (!CheckRange(fParkingPos))
3204 return 2;
3205
3206 fAccPointing = Acceleration(conf.Get<double>("pointing.acceleration.zd"),
3207 conf.Get<double>("pointing.acceleration.az"));
3208 fAccTracking = Acceleration(conf.Get<double>("tracking.acceleration.zd"),
3209 conf.Get<double>("tracking.acceleration.az"));
3210 fAccMax = Acceleration(conf.Get<double>("acceleration.max.zd"),
3211 conf.Get<double>("acceleration.max.az"));
3212
3213 fWeatherTimeout = conf.Get<uint16_t>("weather-timeout");
3214
3215 if (fAccPointing>fAccMax)
3216 {
3217 T::Error("Pointing acceleration exceeds maximum acceleration.");
3218 return 3;
3219 }
3220
3221 if (fAccTracking>fAccMax)
3222 {
3223 T::Error("Tracking acceleration exceeds maximum acceleration.");
3224 return 4;
3225 }
3226
3227 fDeviationLimit = conf.Get<uint16_t>("deviation-limit");
3228 fDeviationCounter = conf.Get<uint16_t>("deviation-count");
3229 fDeviationMax = conf.Get<uint16_t>("deviation-max");
3230
3231 const string fname = conf.Get<string>("pointing.model-file");
3232
3233 try
3234 {
3235 fPointingModel.Load(fname);
3236 }
3237 catch (const exception &e)
3238 {
3239 T::Error(e.what());
3240 return 5;
3241 }
3242
3243 const vector<string> &vec = conf.Vec<string>("source");
3244
3245 for (vector<string>::const_iterator it=vec.begin(); it!=vec.end(); it++)
3246 {
3247 istringstream stream(*it);
3248
3249 string name;
3250
3251 int i=0;
3252
3253 Source src;
3254
3255 string buffer;
3256 while (getline(stream, buffer, ','))
3257 {
3258 istringstream is(buffer);
3259
3260 switch (i++)
3261 {
3262 case 0: name = buffer; break;
3263 case 1: src.ra = ReadAngle(is); break;
3264 case 2: src.dec = ReadAngle(is); break;
3265 case 3: is >> src.offset; break;
3266 case 4: is >> src.angles[0]; break;
3267 case 5: is >> src.angles[1]; break;
3268 }
3269
3270 if (is.fail())
3271 break;
3272 }
3273
3274 if (i==3 || i==6)
3275 {
3276 AddSource(name, src);
3277 continue;
3278 }
3279
3280 T::Warn("Resource 'source' not correctly formatted: '"+*it+"'");
3281 }
3282
3283 //fAutoResume = conf.Get<bool>("auto-resume");
3284
3285 if (conf.Has("source-database"))
3286 {
3287 fDatabase = conf.Get<string>("source-database");
3288 ReadDatabase();
3289 }
3290
3291 if (fSunRise.IsValid())
3292 {
3293 ostringstream msg;
3294 msg << "Next sun-rise will be at " << fSunRise;
3295 T::Message(msg);
3296 }
3297
3298 // The possibility to connect should be last, so that
3299 // everything else is already initialized.
3300 SetEndpoint(conf.Get<string>("addr"));
3301
3302 return -1;
3303 }
3304};
3305
3306// ------------------------------------------------------------------------
3307
3308#include "Main.h"
3309
3310
3311template<class T, class S, class R>
3312int RunShell(Configuration &conf)
3313{
3314 return Main::execute<T, StateMachineDrive<S, R>>(conf);
3315}
3316
3317void SetupConfiguration(Configuration &conf)
3318{
3319 po::options_description control("Drive control options");
3320 control.add_options()
3321 ("quiet,q", po_bool(), "Disable debug messages")
3322 ("no-dim,d", po_switch(), "Disable dim services")
3323 ("addr,a", var<string>("sps:5357"), "Network address of cosy")
3324 ("verbosity,v", var<uint16_t>(0), "Vervosity level (0=off; 1=major updates; 2=most updates; 3=frequent updates)")
3325 ("pointing.model-file", var<string>()->required(), "")
3326 ("pointing.max.zd", var<double>( 104.9), "")
3327 ("pointing.max.az", var<double>( 85.0), "")
3328 ("pointing.min.zd", var<double>(-104.9), "")
3329 ("pointing.min.az", var<double>(-295.0), "")
3330 ("pointing.max.residual", var<double>(1./32768), "Maximum residual for a pointing operation [revolutions]")
3331 ("pointing.velocity", var<double>(0.3), "")
3332 ("pointing.acceleration.az", var<double>(0.01), "")
3333 ("pointing.acceleration.zd", var<double>(0.03), "")
3334 ("tracking.acceleration.az", var<double>(0.01), "")
3335 ("tracking.acceleration.zd", var<double>(0.01), "")
3336 ("parking-pos.zd", var<double>(101), "")
3337 ("parking-pos.az", var<double>(0), "")
3338 ("acceleration.max.az", var<double>(0.03), "")
3339 ("acceleration.max.zd", var<double>(0.09), "")
3340 ("weather-timeout", var<uint16_t>(300), "")
3341 ("deviation-limit", var<uint16_t>(90), "Deviation limit in arcsec to get 'OnTrack'")
3342 ("deviation-count", var<uint16_t>(3), "Minimum number of reported deviation below deviation-limit to get 'OnTrack'")
3343 ("deviation-max", var<uint16_t>(180), "Maximum deviation in arcsec allowed to keep status 'OnTrack'")
3344 ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
3345 ("source", vars<string>(), "Additional source entry in the form \"name,hh:mm:ss,dd:mm:ss\"")
3346 ;
3347
3348 conf.AddOptions(control);
3349}
3350
3351/*
3352 Extract usage clause(s) [if any] for SYNOPSIS.
3353 Translators: "Usage" and "or" here are patterns (regular expressions) which
3354 are used to match the usage synopsis in program output. An example from cp
3355 (GNU coreutils) which contains both strings:
3356 Usage: cp [OPTION]... [-T] SOURCE DEST
3357 or: cp [OPTION]... SOURCE... DIRECTORY
3358 or: cp [OPTION]... -t DIRECTORY SOURCE...
3359 */
3360void PrintUsage()
3361{
3362 cout <<
3363 "The drivectrl is an interface to cosy.\n"
3364 "\n"
3365 "The default is that the program is started without user intercation. "
3366 "All actions are supposed to arrive as DimCommands. Using the -c "
3367 "option, a local shell can be initialized. With h or help a short "
3368 "help message about the usuage can be brought to the screen.\n"
3369 "\n"
3370 "Usage: drivectrl [-c type] [OPTIONS]\n"
3371 " or: drivectrl [OPTIONS]\n";
3372 cout << endl;
3373}
3374
3375void PrintHelp()
3376{
3377 Main::PrintHelp<StateMachineDrive<StateMachine,ConnectionDrive>>();
3378
3379 /* Additional help text which is printed after the configuration
3380 options goes here */
3381
3382 /*
3383 cout << "bla bla bla" << endl << endl;
3384 cout << endl;
3385 cout << "Environment:" << endl;
3386 cout << "environment" << endl;
3387 cout << endl;
3388 cout << "Examples:" << endl;
3389 cout << "test exam" << endl;
3390 cout << endl;
3391 cout << "Files:" << endl;
3392 cout << "files" << endl;
3393 cout << endl;
3394 */
3395}
3396
3397int main(int argc, const char* argv[])
3398{
3399 Configuration conf(argv[0]);
3400 conf.SetPrintUsage(PrintUsage);
3401 Main::SetupConfiguration(conf);
3402 SetupConfiguration(conf);
3403
3404 if (!conf.DoParse(argc, argv, PrintHelp))
3405 return 127;
3406
3407 //try
3408 {
3409 // No console access at all
3410 if (!conf.Has("console"))
3411 {
3412 if (conf.Get<bool>("no-dim"))
3413 return RunShell<LocalStream, StateMachine, ConnectionDrive>(conf);
3414 else
3415 return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(conf);
3416 }
3417 // Cosole access w/ and w/o Dim
3418 if (conf.Get<bool>("no-dim"))
3419 {
3420 if (conf.Get<int>("console")==0)
3421 return RunShell<LocalShell, StateMachine, ConnectionDrive>(conf);
3422 else
3423 return RunShell<LocalConsole, StateMachine, ConnectionDrive>(conf);
3424 }
3425 else
3426 {
3427 if (conf.Get<int>("console")==0)
3428 return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
3429 else
3430 return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
3431 }
3432 }
3433 /*catch (std::exception& e)
3434 {
3435 cerr << "Exception: " << e.what() << endl;
3436 return -1;
3437 }*/
3438
3439 return 0;
3440}
Note: See TracBrowser for help on using the repository browser.