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

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