FACT++  1.0
drivectrl.cc
Go to the documentation of this file.
1 #include <boost/regex.hpp>
2 #include <boost/algorithm/string.hpp>
3 
4 #ifdef HAVE_SQL
5 #include "Database.h"
6 #endif
7 
8 #include "FACT.h"
9 #include "Dim.h"
10 #include "Event.h"
11 #include "Shell.h"
12 #include "StateMachineDim.h"
13 #include "StateMachineAsio.h"
14 #include "Connection.h"
15 #include "LocalControl.h"
16 #include "Configuration.h"
17 #include "Timers.h"
18 #include "Console.h"
19 
20 #include "HeadersDrive.h"
21 
22 #include "pal.h"
23 #include "externals/nova.h"
24 
25 namespace ba = boost::asio;
26 namespace bs = boost::system;
27 
28 using namespace std;
29 using namespace Drive;
30 
31 // ------------------------------------------------------------------------
32 
33 // The Nova classes are in degree. This is to be used in rad
34 struct 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 
42 struct 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 
49 struct Local
50 {
51  double zd;
52  double az;
53 
54  Local(double _zd=0, double _az=0) : zd(_zd), az(_az) { }
55 };
56 
57 struct 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 
64 struct 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 
75 struct 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 
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 
90 Encoder operator-(const Encoder &a, const Encoder &b)
91 {
92  return Encoder(a.zd-b.zd, a.az-b.az);
93 }
94 Velocity operator-(const Encoder &a, const Velocity &b)
95 {
96  return Velocity(a.zd-b.zd, a.az-b.az);
97 }
98 Velocity operator-(const Velocity &a, const Velocity &b)
99 {
100  return Velocity(a.zd-b.zd, a.az-b.az);
101 }
102 Encoder operator/(const Encoder &a, const Encoder &b)
103 {
104  return Encoder(a.zd/b.zd, a.az/b.az);
105 }
106 
107 struct Weather
108 {
109  float hum;
110  float temp;
111  float press;
113 };
114 
115 struct 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 
133 {
134  kENone = -1,
135  kESun = 0,
137  kEVenus = 2,
138  kEMoon = 3, // earth moon barycentre
139  kEMars = 4,
141  kESaturn = 6,
142  kEUranus = 7,
144  kEPluto = 9,
145 };
146 
147 // ------------------------------------------------------------------------
148 
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 
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 
173 {
174 private:
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 
195 public:
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 
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::ORM().lng * M_PI/180;
371  const double lat = Nova::ORM().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 
482 class ConnectionDrive : public Connection
483 {
484  uint16_t fVerbosity;
485 
486 public:
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 
510 private:
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];
742 public:
743  Time fPdoTime2[2];
744 private:
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["+to_string(node)+"]:"+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["+to_string(node)+"]:"+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==1?"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 
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
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 
1266 public:
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 HasError() const
1311  {
1312  const uint8_t typ0 = fErrCode[0]>>16;
1313  const uint8_t typ1 = fErrCode[1]>>16;
1314  return typ0==0xe || typ0==0xf || typ1==0xe || typ1==0xf;
1315  }
1316 
1317  bool IsOnline() const
1318  {
1319  return fErrCode[0]!=0 && fErrCode[1]!=0;
1320  }
1321 
1322  bool IsReady() const
1323  {
1324  return fStatusAxis[0]&kAxisRf && fStatusAxis[1]&kAxisRf;
1325  }
1326 
1327  bool IsBlocked() const
1328  {
1329  return (fStatusSys&kEmergencyOk)==0 || (fStatusSys&kManualMode);
1330  }
1331 
1332  Encoder GetSePos() const // [rev]
1333  {
1334  return Encoder(double(fPdoPos2[1])/fPosRes[1], double(fPdoPos2[0])/fPosRes[0]);
1335  }
1336 
1337  double GetSeTime() const // [rev]
1338  {
1339  // The maximum difference here should not be larger than 100ms.
1340  // So th error we make on both axes should not exceed 50ms;
1341  return (Time(fPdoTime2[0]).Mjd()+Time(fPdoTime2[1]).Mjd())/2;
1342  }
1343 
1345  {
1346  return Encoder(fVelMax[1], fVelMax[0]);
1347  }
1348 
1349  void SetRpmMode(bool mode)
1350  {
1351  const uint32_t val = mode ? String('s','t','r','t') : String('s','t','o','p');
1352  SendSdo(kNodeAz, kSetRpmMode, val);
1353  SendSdo(kNodeZd, kSetRpmMode, val);
1354  }
1355 
1357  {
1358  SendSdo(kNodeAz, kSetAcc, lrint(acc.az*1000000000+0.5));
1359  SendSdo(kNodeZd, kSetAcc, lrint(acc.zd*1000000000+0.5));
1360  }
1361 
1362  void SetPointingVelocity(const Velocity &vel, double scale=1)
1363  {
1364  SendSdo(kNodeAz, kSetPointVel, lrint(vel.az*fVelMax[0]*scale));
1365  SendSdo(kNodeZd, kSetPointVel, lrint(vel.zd*fVelMax[1]*scale));
1366  }
1368  {
1369  SendSdo(kNodeAz, kSetTrackVel, lrint(vel.az*fVelRes[0]));
1370  SendSdo(kNodeZd, kSetTrackVel, lrint(vel.zd*fVelRes[1]));
1371  }
1372 
1373  void StartAbsolutePositioning(const Encoder &enc, bool zd, bool az)
1374  {
1375  if (az) SendSdo(kNodeAz, kSetPosition, lrint(enc.az*fPosRes[0]));
1376  if (zd) SendSdo(kNodeZd, kSetPosition, lrint(enc.zd*fPosRes[1]));
1377 
1378  // Make sure that the status is set correctly already before the first PDO
1379  if (az) fStatusAxis[0] |= 0x02;
1380  if (zd) fStatusAxis[1] |= 0x02;
1381 
1382  // FIXME: UpdateDim?
1383  }
1384 
1385  void SetLedVoltage(const uint32_t &v1, const uint32_t &v2)
1386  {
1387  SendSdo(kNodeAz, 0x4000, v1);
1388  SendSdo(kNodeZd, 0x4000, v2);
1389  }
1390 };
1391 
1392 
1393 // ------------------------------------------------------------------------
1394 
1395 #include "DimDescriptionService.h"
1396 
1397 class ConnectionDimDrive : public ConnectionDrive
1398 {
1399 private:
1400  DimDescribedService fDimPointing;
1401  DimDescribedService fDimTracking;
1402  DimDescribedService fDimSource;
1403  DimDescribedService fDimTPoint;
1404  DimDescribedService fDimStatus;
1405 
1406  // Update dim from a different thread to ensure that these
1407  // updates cannot block the main eventloop which eventually
1408  // also checks the timeouts
1414 
1415  bool SendPointing(const pair<Time,array<double,2>> &p)
1416  {
1417  fDimPointing.setData(p.second);
1418  fDimPointing.Update(p.first);
1419  return true;
1420  }
1421 
1422  bool SendTracking(const pair<Time,array<double, 12>> &p)
1423  {
1424  fDimTracking.setData(p.second);
1425  fDimTracking.Update(p.first);
1426  return true;
1427  }
1428 
1429  bool SendSource(const tuple<Time,vector<char>,bool> &t)
1430  {
1431  const Time &time = get<0>(t);
1432  const vector<char> &data = get<1>(t);
1433  const bool &tracking = get<2>(t);
1434 
1435  fDimSource.setQuality(tracking);
1436  fDimSource.setData(data);
1437  fDimSource.Update(time);
1438  return true;
1439  }
1440 
1441  bool SendStatus(const pair<Time,array<uint8_t, 3>> &p)
1442  {
1443  fDimStatus.setData(p.second);
1444  fDimStatus.Update(p.first);
1445  return true;
1446  }
1447 
1448  bool SendTPoint(const pair<Time,vector<char>> &p)
1449  {
1450  fDimTPoint.setData(p.second);
1451  fDimTPoint.Update(p.first);
1452  return true;
1453  }
1454 
1455 public:
1456  void UpdatePointing(const Time &t, const array<double, 2> &arr)
1457  {
1458  fQueuePointing.emplace(t, arr);
1459  }
1460 
1461  void UpdateTracking(const Time &t,const array<double, 12> &arr)
1462  {
1463  fQueueTracking.emplace(t, arr);
1464  }
1465 
1466  void UpdateStatus(const Time &t, const array<uint8_t, 3> &arr)
1467  {
1468  fQueueStatus.emplace(t, arr);
1469  }
1470 
1471  void UpdateTPoint(const Time &t, const DimTPoint &data,
1472  const string &name)
1473  {
1474  vector<char> dim(sizeof(data)+name.length()+1);
1475  memcpy(dim.data(), &data, sizeof(data));
1476  memcpy(dim.data()+sizeof(data), name.c_str(), name.length()+1);
1477 
1478  fQueueTPoint.emplace(t, dim);
1479  }
1480 
1481  void UpdateSource(const Time &t, const string &name, bool tracking)
1482  {
1483  vector<char> dat(5*sizeof(double)+31, 0);
1484  strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1485 
1486  fQueueSource.emplace(t, dat, tracking);
1487  }
1488 
1489  void UpdateSource(const Time &t, const array<double, 5> &arr, const string &name="")
1490  {
1491  vector<char> dat(5*sizeof(double)+31, 0);
1492  memcpy(dat.data(), arr.data(), 5*sizeof(double));
1493  strncpy(dat.data()+5*sizeof(double), name.c_str(), 30);
1494 
1495  fQueueSource.emplace(t, dat, true);
1496  }
1497 
1498 public:
1499  ConnectionDimDrive(ba::io_service& ioservice, MessageImp &imp) :
1500  ConnectionDrive(ioservice, imp),
1501  fDimPointing("DRIVE_CONTROL/POINTING_POSITION", "D:1;D:1",
1502  "|Zd[deg]:Zenith distance (derived from encoder readout)"
1503  "|Az[deg]:Azimuth angle (derived from encoder readout)"),
1504  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",
1505  "|Ra[h]:Command right ascension pointing direction (J2000)"
1506  "|Dec[deg]:Command declination pointing direction (J2000)"
1507  "|Ha[h]:Hour angle pointing direction"
1508  "|SrcRa[h]:Right ascension source (J2000)"
1509  "|SrcDec[deg]:Declination source (J2000)"
1510  "|SrcHa[h]:Hour angle source"
1511  "|Zd[deg]:Nominal zenith distance"
1512  "|Az[deg]:Nominal azimuth angle"
1513  "|dZd[deg]:Control deviation Zd"
1514  "|dAz[deg]:Control deviation Az"
1515  "|dev[arcsec]:Absolute control deviation"
1516  "|avgdev[arcsec]:Average control deviation used to define OnTrack"),
1517  fDimSource("DRIVE_CONTROL/SOURCE_POSITION", "D:1;D:1;D:1;D:1;D:1;C:31",
1518  "|Ra_src[h]:Source right ascension"
1519  "|Dec_src[deg]:Source declination"
1520  "|Offset[deg]:Wobble offset"
1521  "|Angle[deg]:Wobble angle"
1522  "|Period[min]:Time for one orbit"
1523  "|Name[string]:Source name if available"),
1524  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",
1525  "|Ra[h]:Command right ascension"
1526  "|Dec[deg]:Command declination"
1527  "|Zd_nom[deg]:Nominal zenith distance"
1528  "|Az_nom[deg]:Nominal azimuth angle"
1529  "|Zd_cur[deg]:Current zenith distance (calculated from image)"
1530  "|Az_cur[deg]:Current azimuth angle (calculated from image)"
1531  "|Zd_enc[deg]:Feedback zenith axis (from encoder)"
1532  "|Az_enc[deg]:Feedback azimuth angle (from encoder)"
1533  "|N_leds[cnt]:Number of detected LEDs"
1534  "|N_rings[cnt]:Number of rings used to calculate the camera center"
1535  "|Xc[pix]:X position of center in CCD camera frame"
1536  "|Yc[pix]:Y position of center in CCD camera frame"
1537  "|Ic[au]:Average intensity (LED intensity weighted with their frequency of occurance in the calculation)"
1538  "|Xs[pix]:X position of start in CCD camera frame"
1539  "|Ys[pix]:Y position of star in CCD camera frame"
1540  "|Ms[mag]:Artifical magnitude of star (calculated from image)"
1541  "|Phi[deg]:Rotation angle of image derived from detected LEDs"
1542  "|Mc[mag]:Catalog magnitude of star"
1543  "|Dx[arcsec]:De-rotated dx"
1544  "|Dy[arcsec]:De-rotated dy"
1545  "|Name[string]:Name of star"),
1546  fDimStatus("DRIVE_CONTROL/STATUS", "C:2;C:1", ""),
1547  fQueuePointing(std::bind(&ConnectionDimDrive::SendPointing, this, placeholders::_1)),
1548  fQueueTracking(std::bind(&ConnectionDimDrive::SendTracking, this, placeholders::_1)),
1549  fQueueSource( std::bind(&ConnectionDimDrive::SendSource, this, placeholders::_1)),
1550  fQueueTPoint( std::bind(&ConnectionDimDrive::SendTPoint, this, placeholders::_1)),
1551  fQueueStatus( std::bind(&ConnectionDimDrive::SendStatus, this, placeholders::_1))
1552  {
1553  }
1554 
1555  // 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
1556 };
1557 
1558 // ------------------------------------------------------------------------
1559 
1560 template <class T, class S>
1561 class StateMachineDrive : public StateMachineAsio<T>
1562 {
1563 private:
1564  S fDrive;
1565 
1566  ba::deadline_timer fTrackingLoop;
1567 
1568  string fDatabase;
1569 
1570  typedef map<string, Source> sources;
1571  sources fSources;
1572 
1575 
1577 
1581 
1582  Time fSunRise;
1583 
1586 
1589  uint16_t fDeviationMax;
1590 
1591  vector<double> fDevBuffer;
1592  uint64_t fDevCount;
1593 
1595 
1596 
1597  // --------------------- DIM Sending ------------------
1598 
1599  bool CheckEventSize(size_t has, const char *name, size_t size)
1600  {
1601  if (has==size)
1602  return true;
1603 
1604  ostringstream msg;
1605  msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
1606  T::Fatal(msg);
1607  return false;
1608  }
1609 
1610  // --------------------- DIM Receiving ------------------
1611 
1612  int HandleWeatherData(const EventImp &evt)
1613  {
1614  if (!CheckEventSize(evt.GetSize(), "HandleWeatherData", 7*4+2))
1615  {
1616  fWeather.time = Time(Time::none);
1617  return T::GetCurrentState();
1618  }
1619 
1620  const float *ptr = evt.Ptr<float>(2);
1621 
1622  fWeather.temp = ptr[0];
1623  fWeather.hum = ptr[2];
1624  fWeather.press = ptr[3];
1625  fWeather.time = evt.GetTime();
1626 
1627  return T::GetCurrentState();
1628  }
1629 
1630  int HandleTPoint(const EventImp &evt)
1631  {
1632  // Skip disconnect events
1633  if (evt.GetSize()==0)
1634  return T::GetCurrentState();
1635 
1636  // skip invalid events
1637  if (!CheckEventSize(evt.GetSize(), "HandleTPoint", 11*8))
1638  return T::GetCurrentState();
1639 
1640  // skip event which are older than one minute
1641  if (Time().UnixTime()-evt.GetTime().UnixTime()>60)
1642  return T::GetCurrentState();
1643 
1644  // Original code in slaTps2c:
1645  //
1646  // From the tangent plane coordinates of a star of known RA,Dec,
1647  // determine the RA,Dec of the tangent point.
1648 
1649  const double *ptr = evt.Ptr<double>();
1650 
1651  // Tangent plane rectangular coordinates
1652  const double dx = ptr[0] * M_PI/648000; // [arcsec -> rad]
1653  const double dy = ptr[1] * M_PI/648000; // [arcsec -> rad]
1654 
1655  const PointingData data = fPointingModel.CalcPointingPos(fPointingSetup, evt.GetTime().Mjd(), fWeather, fWeatherTimeout, true);
1656 
1657  const double x2 = dx*dx;
1658  const double y2 = 1 + dy*dy;
1659 
1660  const double sd = cos(data.sky.zd);//sin(M_PI/2-sky.zd);
1661  const double cd = sin(data.sky.zd);//cos(M_PI/2-sky.zd);
1662  const double sdf = sd*sqrt(x2+y2);
1663  const double r2 = cd*cd*y2 - sd*sd*x2;
1664 
1665  // Case of no solution ("at the pole") or
1666  // two solutions ("over the pole solution")
1667  if (r2<0 || fabs(sdf)>=1)
1668  {
1669  T::Warn("Could not determine pointing direction from TPoint.");
1670  return T::GetCurrentState();
1671  }
1672 
1673  const double r = sqrt(r2);
1674  const double s = sdf - dy * r;
1675  const double c = sdf * dy + r;
1676  const double phi = atan2(dx, r);
1677 
1678  // Spherical coordinates of tangent point
1679  const double az = fmod(data.sky.az-phi + 2*M_PI, 2*M_PI);
1680  const double zd = M_PI/2 - atan2(s, c);
1681 
1682  const Encoder dev = fDrive.GetSePos()*360 - data.mount;
1683 
1684  // --- Output TPoint ---
1685 
1686  const string fname = "tpoints-"+to_string(evt.GetTime().NightAsInt())+".txt";
1687  //time.GetAsStr("/%Y/%m/%d");
1688 
1689  const bool exist = boost::filesystem::exists(fname);
1690 
1691  ofstream fout(fname, ios::app);
1692  if (!exist)
1693  {
1694  fout << "FACT Model TPOINT data file" << endl;
1695  fout << ": ALTAZ" << endl;
1696  fout << "49 48 0 ";
1697  fout << evt.GetTime() << endl;
1698  }
1699  fout << setprecision(7);
1700  fout << fmod(az*180/M_PI+360, 360) << " ";
1701  fout << 90-zd*180/M_PI << " ";
1702  fout << fmod(data.mount.az+360, 360) << " ";
1703  fout << 90-data.mount.zd << " ";
1704  fout << dev.az << " "; // delta az
1705  fout << -dev.zd << " "; // delta el
1706  fout << 90-data.sky.zd * 180/M_PI << " ";
1707  fout << data.sky.az * 180/M_PI << " ";
1708  fout << setprecision(10);
1709  fout << data.mjd << " ";
1710  fout << setprecision(7);
1711  fout << ptr[6] << " "; // center.mag
1712  fout << ptr[9] << " "; // star.mag
1713  fout << ptr[4] << " "; // center.x
1714  fout << ptr[5] << " "; // center.y
1715  fout << ptr[7] << " "; // star.x
1716  fout << ptr[8] << " "; // star.y
1717  fout << ptr[2] << " "; // num leds
1718  fout << ptr[3] << " "; // num rings
1719  fout << ptr[0] << " "; // dx (de-rotated)
1720  fout << ptr[1] << " "; // dy (de-rotated)
1721  fout << ptr[10] << " "; // rotation angle
1722  fout << fPointingSetup.source.mag << " ";
1723  fout << fPointingSetup.source.name;
1724  fout << endl;
1725 
1726  DimTPoint dim;
1727  dim.fRa = data.pointing.ra * 12/M_PI;
1728  dim.fDec = data.pointing.dec * 180/M_PI;
1729  dim.fNominalZd = data.sky.zd * 180/M_PI;
1730  dim.fNominalAz = data.sky.az * 180/M_PI;
1731  dim.fPointingZd = zd * 180/M_PI;
1732  dim.fPointingAz = az * 180/M_PI;
1733  dim.fFeedbackZd = data.mount.zd;
1734  dim.fFeedbackAz = data.mount.az;
1735  dim.fNumLeds = uint16_t(ptr[2]);
1736  dim.fNumRings = uint16_t(ptr[3]);
1737  dim.fCenterX = ptr[4];
1738  dim.fCenterY = ptr[5];
1739  dim.fCenterMag = ptr[6];
1740  dim.fStarX = ptr[7];
1741  dim.fStarY = ptr[8];
1742  dim.fStarMag = ptr[9];
1743  dim.fRotation = ptr[10];
1744  dim.fDx = ptr[0];
1745  dim.fDy = ptr[1];
1746  dim.fRealMag = fPointingSetup.source.mag;
1747 
1748  fDrive.UpdateTPoint(evt.GetTime(), dim, fPointingSetup.source.name);
1749 
1750  ostringstream txt;
1751  txt << "TPoint recorded [" << zd*180/M_PI << "/" << az*180/M_PI << " | "
1752  << data.sky.zd*180/M_PI << "/" << data.sky.az*180/M_PI << " | "
1753  << data.mount.zd << "/" << data.mount.az << " | "
1754  << dx*180/M_PI << "/" << dy*180/M_PI << "]";
1755  T::Info(txt);
1756 
1757  return T::GetCurrentState();
1758  }
1759 
1760  // -------------------------- Helpers -----------------------------------
1761 
1762  double GetDevAbs(double nomzd, double meszd, double devaz)
1763  {
1764  nomzd *= M_PI/180;
1765  meszd *= M_PI/180;
1766  devaz *= M_PI/180;
1767 
1768  const double x = sin(meszd) * sin(nomzd) * cos(devaz);
1769  const double y = cos(meszd) * cos(nomzd);
1770 
1771  return acos(x + y) * 180/M_PI;
1772  }
1773 
1774  double ReadAngle(istream &in)
1775  {
1776  char sgn;
1777  uint16_t d, m;
1778  float s;
1779 
1780  in >> sgn >> d >> m >> s;
1781 
1782  const double ret = ((60.0 * (60.0 * (double)d + (double)m) + s))/3600.;
1783  return sgn=='-' ? -ret : ret;
1784  }
1785 
1786  bool CheckRange(ZdAz pos)
1787  {
1788  if (pos.zd<fPointingMin.zd)
1789  {
1790  T::Error("Zenith distance "+to_string(pos.zd)+" below limit "+to_string(fPointingMin.zd));
1791  return false;
1792  }
1793 
1794  if (pos.zd>fPointingMax.zd)
1795  {
1796  T::Error("Zenith distance "+to_string(pos.zd)+" exceeds limit "+to_string(fPointingMax.zd));
1797  return false;
1798  }
1799 
1800  if (pos.az<fPointingMin.az)
1801  {
1802  T::Error("Azimuth angle "+to_string(pos.az)+" below limit "+to_string(fPointingMin.az));
1803  return false;
1804  }
1805 
1806  if (pos.az>fPointingMax.az)
1807  {
1808  T::Error("Azimuth angle "+to_string(pos.az)+" exceeds limit "+to_string(fPointingMax.az));
1809  return false;
1810  }
1811 
1812  return true;
1813  }
1814 
1816  {
1817  return fPointingModel.CalcPointingPos(fPointingSetup, mjd, fWeather, fWeatherTimeout);
1818  }
1819 
1820  // ----------------------------- SDO Commands ------------------------------
1821 
1822  int RequestSdo(const EventImp &evt)
1823  {
1824  // FIXME: STop telescope
1825  if (!CheckEventSize(evt.GetSize(), "RequestSdo", 6))
1826  return T::kSM_FatalError;
1827 
1828  const uint16_t node = evt.Get<uint16_t>();
1829  const uint16_t index = evt.Get<uint16_t>(2);
1830  const uint16_t subidx = evt.Get<uint16_t>(4);
1831 
1832  if (node!=1 && node !=3)
1833  {
1834  T::Error("Node id must be 1 (az) or 3 (zd).");
1835  return T::GetCurrentState();
1836  }
1837 
1838  if (subidx>0xff)
1839  {
1840  T::Error("Subindex must not be larger than 255.");
1841  return T::GetCurrentState();
1842  }
1843 
1844  fDrive.RequestSdo(node, index, subidx);
1845 
1846  return T::GetCurrentState();
1847  }
1848 
1849  int SendSdo(const EventImp &evt)
1850  {
1851  if (!CheckEventSize(evt.GetSize(), "SendSdo", 6+8))
1852  return T::kSM_FatalError;
1853 
1854  const uint16_t node = evt.Get<uint16_t>();
1855  const uint16_t index = evt.Get<uint16_t>(2);
1856  const uint16_t subidx = evt.Get<uint16_t>(4);
1857  const uint64_t value = evt.Get<uint64_t>(6);
1858 
1859  if (node!=1 && node!=3)
1860  {
1861  T::Error("Node id must be 1 (az) or 3 (zd).");
1862  return T::GetCurrentState();
1863  }
1864 
1865  if (subidx>0xff)
1866  {
1867  T::Error("Subindex must not be larger than 255.");
1868  return T::GetCurrentState();
1869  }
1870 
1871  fDrive.SendSdo(node, index, subidx, value);
1872 
1873  return T::GetCurrentState();
1874  }
1875 
1876  // --------------------- Moving and tracking ---------------------
1877 
1878  uint16_t fStep;
1885 
1886  int InitMovement(const ZdAz &sky, bool tracking=false, const string &name="")
1887  {
1888  fMovementTarget = fPointingModel.SkyToMount(sky);
1889 
1890  // Check whether bending is valid!
1891  if (!CheckRange(sky*(180/M_PI)))
1892  return StopMovement();
1893 
1894  fStep = 0;
1895  fIsTracking = tracking;
1896 
1897  fDrive.SetRpmMode(false); // *NEW* (Stop a previous tracking to avoid the pointing command to be ignored)
1898  fDrive.SetAcceleration(fAccPointing);
1899 
1900  if (!tracking)
1901  fDrive.UpdateSource(Time(), name, false);
1902  else
1903  {
1904  const array<double, 5> dim =
1905  {{
1906  fPointingSetup.source.ra,
1907  fPointingSetup.source.dec,
1908  fPointingSetup.wobble_offset * 180/M_PI,
1909  fPointingSetup.wobble_angle * 180/M_PI,
1910  fPointingSetup.orbit_period * 24*60
1911  }};
1912  fDrive.UpdateSource(fPointingSetup.start, dim, fPointingSetup.source.name);
1913  }
1914 
1915  return State::kMoving;
1916  }
1917 
1918  int MoveTo(const EventImp &evt)
1919  {
1920  if (!CheckEventSize(evt.GetSize(), "MoveTo", 16))
1921  return T::kSM_FatalError;
1922 
1923  const double *dat = evt.Ptr<double>();
1924 
1925  ostringstream out;
1926  out << "Pointing telescope to Zd=" << dat[0] << "deg Az=" << dat[1] << "deg";
1927  T::Message(out);
1928 
1929  return InitMovement(ZdAz(dat[0]*M_PI/180, dat[1]*M_PI/180));
1930  }
1931 
1933  {
1934  fPointingSetup.start = Time().Mjd();
1935 
1936  const PointingData data = CalcPointingPos(fPointingSetup.start);
1937 
1938  ostringstream out;
1939  out << "Tracking position now at Zd=" << data.sky.zd*180/M_PI << "deg Az=" << data.sky.az*180/M_PI << "deg";
1940  T::Info(out);
1941 
1942  return InitMovement(data.sky, true);
1943  }
1944 
1945  int StartTracking(const Source &src, double offset, double angle, double period=0)
1946  {
1947  if (src.ra<0 || src.ra>=24)
1948  {
1949  ostringstream out;
1950  out << "Right ascension out of range [0;24[: Ra=" << src.ra << "h Dec=" << src.dec << "deg";
1951  if (!src.name.empty())
1952  out << " [" << src.name << "]";
1953  T::Error(out);
1955  }
1956  if (src.dec<-90 || src.dec>90)
1957  {
1958  ostringstream out;
1959  out << "Declination out of range [-90;90]: Ra=" << src.ra << "h Dec=" << src.dec << "deg";
1960  if (!src.name.empty())
1961  out << " [" << src.name << "]";
1962  T::Error(out);
1964  }
1965 
1966  ostringstream out;
1967  out << "Tracking Ra=" << src.ra << "h Dec=" << src.dec << "deg";
1968  if (!src.name.empty())
1969  out << " [" << src.name << "]";
1970  T::Info(out);
1971 
1972  fPointingSetup.planet = kENone;
1973  fPointingSetup.source = src;
1974  fPointingSetup.orbit_period = period / 1440; // [min->day]
1975  fPointingSetup.wobble_angle = angle * M_PI/180; // [deg->rad]
1976  fPointingSetup.wobble_offset = offset * M_PI/180; // [deg->rad]
1977 
1978  return InitTracking();
1979  }
1980 
1981  int TrackCelest(const Planets_t &p)
1982  {
1983  switch (p)
1984  {
1985  case kEMoon: fPointingSetup.source.name = "Moon"; break;
1986  case kEVenus: fPointingSetup.source.name = "Venus"; break;
1987  case kEMars: fPointingSetup.source.name = "Mars"; break;
1988  case kEJupiter: fPointingSetup.source.name = "Jupiter"; break;
1989  case kESaturn: fPointingSetup.source.name = "Saturn"; break;
1990  default:
1991  T::Error("TrackCelest - Celestial object "+to_string(p)+" not yet supported.");
1992  return T::GetCurrentState();
1993  }
1994 
1995  fPointingSetup.planet = p;
1996  fPointingSetup.wobble_offset = 0;
1997 
1998  fDrive.UpdateSource(Time(), fPointingSetup.source.name, true);
1999 
2000  return InitTracking();
2001  }
2002 
2003  int Park()
2004  {
2005  ostringstream out;
2006  out << "Parking telescope at Zd=" << fParkingPos.zd << "deg Az=" << fParkingPos.az << "deg";
2007  T::Message(out);
2008 
2009  const int rc = InitMovement(ZdAz(fParkingPos.zd*M_PI/180, fParkingPos.az*M_PI/180), false, "Park");
2010  return rc==State::kMoving ? State::kParking : rc;
2011  }
2012 
2013  int Wobble(const EventImp &evt)
2014  {
2015  if (!CheckEventSize(evt.GetSize(), "Wobble", 32))
2016  return T::kSM_FatalError;
2017 
2018  const double *dat = evt.Ptr<double>();
2019 
2020  Source src;
2021  src.ra = dat[0];
2022  src.dec = dat[1];
2023  return StartTracking(src, dat[2], dat[3]);
2024  }
2025 
2026  int Orbit(const EventImp &evt)
2027  {
2028  if (!CheckEventSize(evt.GetSize(), "Orbit", 40))
2029  return T::kSM_FatalError;
2030 
2031  const double *dat = evt.Ptr<double>();
2032 
2033  Source src;
2034  src.ra = dat[0];
2035  src.dec = dat[1];
2036  return StartTracking(src, dat[2], dat[3], dat[4]);
2037  }
2038 
2039  const sources::const_iterator GetSourceFromDB(const char *ptr, const char *last)
2040  {
2041  if (find(ptr, last, '\0')==last)
2042  {
2043  T::Fatal("TrackWobble - The name transmitted by dim is not null-terminated.");
2044  throw uint32_t(T::kSM_FatalError);
2045  }
2046 
2047  const string name(ptr);
2048 
2049  const sources::const_iterator it = fSources.find(name);
2050  if (it==fSources.end())
2051  {
2052  T::Error("Source '"+name+"' not found in list.");
2053  throw uint32_t(T::GetCurrentState());
2054  }
2055 
2056  return it;
2057  }
2058 
2059  int TrackWobble(const EventImp &evt)
2060  {
2061  if (evt.GetSize()<2)
2062  {
2063  ostringstream msg;
2064  msg << "TrackWobble - Received event has " << evt.GetSize() << " bytes, but expected at least 3.";
2065  T::Fatal(msg);
2066  return T::kSM_FatalError;
2067  }
2068 
2069  if (evt.GetSize()==2)
2070  {
2071  ostringstream msg;
2072  msg << "TrackWobble - Source name missing.";
2073  T::Error(msg);
2074  return T::GetCurrentState();
2075  }
2076 
2077  const uint16_t wobble = evt.GetUShort();
2078  if (wobble!=1 && wobble!=2)
2079  {
2080  ostringstream msg;
2081  msg << "TrackWobble - Wobble id " << wobble << " undefined, only 1 and 2 allowed.";
2082  T::Error(msg);
2083  return T::GetCurrentState();
2084  }
2085 
2086  const char *ptr = evt.Ptr<char>(2);
2087  const char *last = ptr+evt.GetSize()-2;
2088 
2089  try
2090  {
2091  const sources::const_iterator it = GetSourceFromDB(ptr, last);
2092 
2093  const Source &src = it->second;
2094  return StartTracking(src, src.offset, src.angles[wobble-1]);
2095  }
2096  catch (const uint32_t &e)
2097  {
2098  return e;
2099  }
2100  }
2101 
2102  int StartTrackWobble(const char *ptr, size_t size, const double &offset=0, const double &angle=0, double time=0)
2103  {
2104  const char *last = ptr+size;
2105 
2106  try
2107  {
2108  const sources::const_iterator it = GetSourceFromDB(ptr, last);
2109 
2110  const Source &src = it->second;
2111  return StartTracking(src, offset<0?0.6/*src.offset*/:offset, angle, time);
2112  }
2113  catch (const uint32_t &e)
2114  {
2115  return e;
2116  }
2117  }
2118 
2119  int Track(const EventImp &evt)
2120  {
2121  if (!CheckEventSize(evt.GetSize(), "Track", 16))
2122  return T::kSM_FatalError;
2123 
2124  Source src;
2125 
2126  src.name = "";
2127  src.ra = evt.Get<double>(0);
2128  src.dec = evt.Get<double>(8);
2129 
2130  return StartTracking(src, 0, 0);
2131  }
2132 
2133  int TrackSource(const EventImp &evt)
2134  {
2135  if (evt.GetSize()<16)
2136  {
2137  ostringstream msg;
2138  msg << "TrackOn - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2139  T::Fatal(msg);
2140  return T::kSM_FatalError;
2141  }
2142 
2143  if (evt.GetSize()==16)
2144  {
2145  ostringstream msg;
2146  msg << "TrackOn - Source name missing.";
2147  T::Error(msg);
2148  return T::GetCurrentState();
2149  }
2150 
2151  const double offset = evt.Get<double>(0);
2152  const double angle = evt.Get<double>(8);
2153 
2154  return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, offset, angle);
2155  }
2156 
2157  int TrackOn(const EventImp &evt)
2158  {
2159  if (evt.GetSize()==0)
2160  {
2161  ostringstream msg;
2162  msg << "TrackOn - Source name missing.";
2163  T::Error(msg);
2164  return T::GetCurrentState();
2165  }
2166 
2167  return StartTrackWobble(evt.Ptr<char>(), evt.GetSize());
2168  }
2169 
2170  int TrackOrbit(const EventImp &evt)
2171  {
2172  if (evt.GetSize()<16)
2173  {
2174  ostringstream msg;
2175  msg << "TrackOrbit - Received event has " << evt.GetSize() << " bytes, but expected at least 17.";
2176  T::Fatal(msg);
2177  return T::kSM_FatalError;
2178  }
2179  if (evt.GetSize()==16)
2180  {
2181  ostringstream msg;
2182  msg << "TrackOrbit - Source name missing.";
2183  T::Error(msg);
2184  return T::GetCurrentState();
2185  }
2186 
2187  const double angle = evt.Get<double>(0);
2188  const double time = evt.Get<double>(8);
2189 
2190  return StartTrackWobble(evt.Ptr<char>(16), evt.GetSize()-16, -1, angle, time);
2191  }
2192 
2194  {
2195  fDrive.SetAcceleration(fAccMax);
2196  fDrive.SetRpmMode(false);
2197 
2198  fTrackingLoop.cancel();
2199 
2200  fDrive.UpdateSource(Time(), "", false);
2201 
2202  return State::kStopping;
2203  }
2204 
2206  {
2207  const int rc = CheckState();
2208  return rc>0 ? rc : State::kInitialized;
2209  }
2210 
2211  // --------------------- Others ---------------------
2212 
2213  int TPoint()
2214  {
2215  T::Info("TPoint initiated.");
2216  Dim::SendCommandNB("TPOINT/EXECUTE");
2217  return T::GetCurrentState();
2218  }
2219 
2220  int Screenshot(const EventImp &evt)
2221  {
2222  if (evt.GetSize()<2)
2223  {
2224  ostringstream msg;
2225  msg << "Screenshot - Received event has " << evt.GetSize() << " bytes, but expected at least 2.";
2226  T::Fatal(msg);
2227  return T::kSM_FatalError;
2228  }
2229 
2230  if (evt.GetSize()==2)
2231  {
2232  ostringstream msg;
2233  msg << "Screenshot - Filename missing.";
2234  T::Error(msg);
2235  return T::GetCurrentState();
2236  }
2237 
2238  T::Info("Screenshot initiated.");
2239  Dim::SendCommandNB("TPOINT/SCREENSHOT", evt.GetData(), evt.GetSize());
2240  return T::GetCurrentState();
2241  }
2242 
2243  int SetLedBrightness(const EventImp &evt)
2244  {
2245  if (!CheckEventSize(evt.GetSize(), "SetLedBrightness", 8))
2246  return T::kSM_FatalError;
2247 
2248  const uint32_t *led = evt.Ptr<uint32_t>();
2249 
2250  fDrive.SetLedVoltage(led[0], led[1]);
2251 
2252  return T::GetCurrentState();
2253  }
2254 
2256  {
2257  fDrive.SetLedVoltage(0, 0);
2258  return T::GetCurrentState();
2259  }
2260 
2261  // --------------------- Internal ---------------------
2262 
2263  int SetVerbosity(const EventImp &evt)
2264  {
2265  if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 2))
2266  return T::kSM_FatalError;
2267 
2268  fDrive.SetVerbosity(evt.GetUShort());
2269 
2270  return T::GetCurrentState();
2271  }
2272 
2273  int Print()
2274  {
2275  for (auto it=fSources.begin(); it!=fSources.end(); it++)
2276  {
2277  const string &name = it->first;
2278  const Source &src = it->second;
2279 
2280  T::Out() << name << ",";
2281  T::Out() << src.ra << "," << src.dec << "," << src.offset << ",";
2282  T::Out() << src.angles[0] << "," << src.angles[1] << endl;
2283  }
2284  return T::GetCurrentState();
2285  }
2286 
2287  int Unlock()
2288  {
2289  const int rc = CheckState();
2290  return rc<0 ? State::kInitialized : rc;
2291  }
2292 
2294  {
2295  try
2296  {
2297  ReadDatabase();
2298  }
2299  catch (const exception &e)
2300  {
2301  T::Error("Reading sources from databse failed: "+string(e.what()));
2302  }
2303  return T::GetCurrentState();
2304  }
2305 
2307  {
2308  // Close all connections
2309  fDrive.PostClose(false);
2310 
2311  /*
2312  // Now wait until all connection have been closed and
2313  // all pending handlers have been processed
2314  poll();
2315  */
2316 
2317  return T::GetCurrentState();
2318  }
2319 
2320  int Reconnect(const EventImp &evt)
2321  {
2322  // Close all connections to supress the warning in SetEndpoint
2323  fDrive.PostClose(false);
2324 
2325  // Now wait until all connection have been closed and
2326  // all pending handlers have been processed
2327  ba::io_service::poll();
2328 
2329  if (evt.GetBool())
2330  fDrive.SetEndpoint(evt.GetString());
2331 
2332  // Now we can reopen the connection
2333  fDrive.PostClose(true);
2334 
2335  return T::GetCurrentState();
2336  }
2337 
2338  // ========================= Tracking code =============================
2339 
2341  {
2342  // First calculate deviation between
2343  // command position and nominal position
2344  //fPointing.mount = sepos; // [deg] ref pos for alignment
2345  const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2346 
2347  // Get current position and calculate deviation
2348  const Encoder sepos = fDrive.GetSePos()*360; // [deg]
2349  const Encoder dev = sepos - data.mount;
2350 
2351  // Calculate absolut deviation on the sky
2352  const double absdev = GetDevAbs(data.mount.zd, sepos.zd, dev.az)*3600;
2353 
2354  // Smoothing
2355  fDevBuffer[fDevCount++%5] = absdev;
2356 
2357  // Calculate average
2358  const uint8_t cnt = fDevCount<5 ? fDevCount : 5;
2359  const double avgdev = accumulate(fDevBuffer.begin(), fDevBuffer.begin()+cnt, 0.)/cnt;
2360 
2361  // Count the consecutive number of avgdev below fDeviationLimit
2362  if (avgdev<fDeviationLimit)
2363  fTrackingCounter++;
2364  else
2365  fTrackingCounter = 0;
2366 
2367  const double ha = fmod(fDrive.GetSeTime(),1)*24 - Nova::ORM().lng/15;
2368 
2369  array<double, 12> dim;
2370  dim[0] = data.pointing.ra * 12/M_PI; // Ra [h] optical axis
2371  dim[1] = data.pointing.dec * 180/M_PI; // Dec [deg] optical axis
2372  dim[2] = ha - data.pointing.ra; // Ha [h] optical axis
2373  dim[3] = data.source.ra * 12/M_PI; // SrcRa [h] source position
2374  dim[4] = data.source.dec * 180/M_PI; // SrcDec [deg] source position
2375  dim[5] = ha - data.source.ra; // SrcHa [h] source position
2376  dim[6] = data.sky.zd * 180/M_PI; // Zd [deg] optical axis
2377  dim[7] = data.sky.az * 180/M_PI; // Az [deg] optical axis
2378  dim[8] = dev.zd; // dZd [deg] control deviation
2379  dim[9] = dev.az; // dAz [deg] control deviation
2380  dim[10] = absdev; // dev [arcsec] absolute control deviation
2381  dim[11] = avgdev; // dev [arcsec] average control deviation
2382 
2383  fDrive.UpdateTracking(fDrive.GetSeTime(), dim);
2384 
2385  if (fDrive.GetVerbosity())
2386  T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Deviation [deg] " << absdev << "\"|" << avgdev << "\"|" << fDevCount<< " dZd=" << dev.zd*3600 << "\" dAz=" << dev.az*3600 << "\"" << endl;
2387 
2388  // Maximum deviation execeeded -> fall back to Tracking state
2389  if (T::GetCurrentState()==State::kOnTrack && avgdev>fDeviationMax)
2390  return State::kTracking;
2391 
2392  // Condition for OnTrack state achieved -> enhance to OnTrack state
2393  if (T::GetCurrentState()==State::kTracking && fTrackingCounter>=fDeviationCounter)
2394  return State::kOnTrack;
2395 
2396  // No state change
2397  return T::GetCurrentState();
2398  }
2399 
2401  {
2402  const Encoder sepos = fDrive.GetSePos()*360; // [deg] ref pos for alignment
2403 
2404  const ZdAz pos = fPointingModel.MountToSky(sepos);
2405 
2406  array<double, 2> data;
2407  data[0] = pos.zd*180/M_PI; // Zd [deg]
2408  data[1] = pos.az*180/M_PI; // Az [deg]
2409  fDrive.UpdatePointing(fDrive.GetSeTime(), data);
2410 
2411  if (fDrive.GetVerbosity())
2412  T::Out() << Time().GetAsStr(" %H:%M:%S.%f") << " - Position [deg] " << pos.zd*180/M_PI << " " << pos.az*180/M_PI << endl;
2413  }
2414 
2415  void TrackingLoop(const boost::system::error_code &error=boost::system::error_code())
2416  {
2417  if (error==ba::error::basic_errors::operation_aborted)
2418  return;
2419 
2420  if (error)
2421  {
2422  ostringstream str;
2423  str << "TrackingLoop: " << error.message() << " (" << error << ")";// << endl;
2424  T::Error(str);
2425  return;
2426  }
2427 
2428  if (T::GetCurrentState()!=State::kTracking &&
2429  T::GetCurrentState()!=State::kOnTrack)
2430  return;
2431 
2432  //
2433  // Update speed as often as possible.
2434  // make sure, that dt is around 10 times larger than the
2435  // update time
2436  //
2437  // The loop should not be executed faster than the ramp of
2438  // a change in the velocity can be followed.
2439  //
2440  fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(250));
2441 
2442  const double mjd = Time().Mjd();
2443 
2444  // I assume that it takes about 50ms for the value to be
2445  // transmitted and the drive needs time to follow as well (maybe
2446  // more than 50ms), therefore the calculated speec is calculated
2447  // for a moment 50ms in the future
2448  const PointingData data = CalcPointingPos(fDrive.GetSeTime());
2449  const PointingData data0 = CalcPointingPos(mjd-0.45/24/3600);
2450  const PointingData data1 = CalcPointingPos(mjd+0.55/24/3600);
2451 
2452  const Encoder dest = data.mount *(1./360); // [rev]
2453  const Encoder dest0 = data0.mount*(1./360); // [rev]
2454  const Encoder dest1 = data1.mount*(1./360); // [rev]
2455 
2456  if (!CheckRange(data1.sky))
2457  {
2458  StopMovement();
2459  T::HandleNewState(State::kAllowedRangeExceeded, 0, "by TrackingLoop");
2460  return;
2461  }
2462 
2463  // Current position
2464  const Encoder sepos = fDrive.GetSePos(); // [rev]
2465 
2466  // Now calculate the current velocity
2467  const Encoder dist = dest1 - dest0; // [rev] Distance between t-1s and t+1s
2468  const Velocity vel = dist/(1./60); // [rev/min] Actual velocity of the pointing position
2469 
2470  const Encoder dev = sepos - dest; // [rev] Current control deviation
2471  const Velocity vt = vel - dev/(1./60); // [rev/min] Correct velocity by recent control deviation
2472  // correct control deviation with 5s
2473  if (fDrive.GetVerbosity()>1)
2474  {
2475  T::Out() << "Ideal position [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2476  T::Out() << "Encoder pos. [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2477  T::Out() << "Deviation [arcmin] " << dev.zd *360*60 << " " << dev.az *360*60 << endl;
2478  T::Out() << "Distance 1s [arcmin] " << dist.zd *360*60 << " " << dist.az *360*60 << endl;
2479  T::Out() << "Velocity 1s [rpm] " << vt.zd << " " << vt.az << endl;
2480  T::Out() << "Delta T (enc) [ms] " << fabs(mjd-fDrive.fPdoTime2[0].Mjd())*24*3600*1000 << endl;
2481  T::Out() << "Delta T (now) [ms] " << (Time().Mjd()-mjd)*24*3600*1000 << endl;
2482  }
2483 
2484  // Tracking loop every 250ms
2485  // Vorsteuerung 2s
2486  // Delta T (enc) 5ms, every 5th, 25ms
2487  // Delta T (now) equal dist 5ms-35 plus equal dist 25-55 (0.2%-2% of 2s)
2488 
2489  //
2490  // FIXME: check if the drive is fast enough to follow the star
2491  //
2492  // Velocity units (would be 100 for %)
2493 
2494  fDrive.SetTrackingVelocity(vt);
2495 
2496  fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2497  this, ba::placeholders::error));
2498  }
2499 
2500  // =====================================================================
2501 
2503  {
2504  if (!fDrive.IsConnected())
2505  return State::kDisconnected;
2506 
2507  if (!fDrive.IsOnline())
2508  return State::kUnavailable;
2509 
2510  // FIXME: This can prevent parking in case e.g.
2511  // of e8029 Position limit exceeded
2512  if (fDrive.HasError())
2513  {
2514  if (T::GetCurrentState()==State::kOnTrack ||
2515  T::GetCurrentState()==State::kTracking ||
2516  T::GetCurrentState()==State::kMoving ||
2517  T::GetCurrentState()==State::kParking)
2518  return StopMovement();
2519 
2520  if (T::GetCurrentState()==State::kStopping && fDrive.IsMoving())
2521  return State::kStopping;
2522 
2524  }
2525 
2526  // This can happen if one of the drives is not in RF.
2527  // Usually this only happens when the drive is not yet in RF
2528  // or an error was just cleared. Usually there is no way that
2529  // a drive goes below the RF state during operation without
2530  // a warning or error message.
2531  if (fDrive.IsOnline() && fDrive.IsBlocked())
2532  return State::kBlocked;
2533 
2534  if (fDrive.IsOnline() && !fDrive.IsReady())
2535  return State::kAvailable;
2536 
2537  // This is the case as soon as the init commands were send
2538  // after a connection to the SPS was established
2539  if (fDrive.IsOnline() && fDrive.IsReady() && !fDrive.IsInitialized())
2540  return State::kArmed;
2541 
2542  return -1;
2543  }
2544 
2545  int Execute()
2546  {
2547  const Time now;
2548  if (now>fSunRise && T::GetCurrentState()!=State::kParking)
2549  {
2550  fSunRise = now.GetNextSunRise();
2551 
2552  ostringstream msg;
2553  msg << "Next sun-rise will be at " << fSunRise;
2554  T::Info(msg);
2555 
2556  if (T::GetCurrentState()>State::kArmed && T::GetCurrentState()!=StateMachineImp::kError)
2557  return Park();
2558  }
2559 
2560  if (T::GetCurrentState()==State::kLocked)
2561  return State::kLocked;
2562 
2563  // FIXME: Send STOP if IsPositioning or RpmActive but no
2564  // Moving or Tracking state
2565 
2566  const int rc = CheckState();
2567  if (rc>0)
2568  return rc;
2569 
2570  // Once every second
2571  static time_t lastTime = 0;
2572  const time_t tm = time(NULL);
2573  if (lastTime!=tm && fDrive.IsInitialized())
2574  {
2575  lastTime=tm;
2576 
2577  UpdatePointingPosition();
2578 
2579  if (T::GetCurrentState()==State::kTracking || T::GetCurrentState()==State::kOnTrack)
2580  return UpdateTrackingPosition();
2581  }
2582 
2583  if (T::GetCurrentState()==State::kStopping && !fDrive.IsMoving())
2584  return State::kArmed;
2585 
2586  if ((T::GetCurrentState()==State::kMoving ||
2587  T::GetCurrentState()==State::kParking) && !fDrive.IsMoving())
2588  {
2589  if (fIsTracking && fStep==1)
2590  {
2591  // Init tracking
2592  fDrive.SetAcceleration(fAccTracking);
2593  fDrive.SetRpmMode(true);
2594 
2595  fDevCount = 0;
2596  fTrackingCounter = 0;
2597 
2598  fTrackingLoop.expires_from_now(boost::posix_time::milliseconds(1));
2599  fTrackingLoop.async_wait(boost::bind(&StateMachineDrive::TrackingLoop,
2600  this, ba::placeholders::error));
2601 
2602  fPointingSetup.start = Time().Mjd();
2603 
2604  const PointingData data = CalcPointingPos(fPointingSetup.start);
2605 
2606  ostringstream out;
2607  out << "Start tracking at Ra=" << data.pointing.ra*12/M_PI << "h Dec=" << data.pointing.dec*180/M_PI << "deg";
2608  T::Info(out);
2609 
2610  return State::kTracking;
2611  }
2612 
2613  // Get feedback 2
2614  const Encoder dest = fMovementTarget*(1./360); // [rev]
2615  const Encoder sepos = fDrive.GetSePos(); // [rev]
2616 
2617  // Calculate residual to move deviation
2618  const Encoder dist = dest - sepos; // [rev]
2619 
2620  // Check which axis should still be moved
2621  Encoder cd = dist; // [rev]
2622  cd *= 1./fMaxPointingResidual; // Scale to units of the maximum residual
2623  cd = cd.Abs();
2624 
2625  // Check if there is a control deviation on the axis
2626  const bool cdzd = cd.zd>1;
2627  const bool cdaz = cd.az>1;
2628 
2629  if (!fIsTracking)
2630  {
2631  // check if we reached the correct position already
2632  if (!cdzd && !cdaz)
2633  {
2634  T::Info("Target position reached in "+to_string(fStep)+" steps.");
2635  return T::GetCurrentState()==State::kParking ? State::kLocked : State::kArmed;
2636  }
2637 
2638  if (fStep==10)
2639  {
2640  T::Error("Target position not reached in "+to_string(fStep)+" steps.");
2642  }
2643  }
2644 
2645  const Encoder t = dist.Abs()/fDrive.GetVelUnit();
2646 
2647  const Velocity vel =
2648  t.zd > t.az ?
2649  Velocity(1, t.zd==0?0:t.az/t.zd) :
2650  Velocity(t.az==0?0:t.zd/t.az, 1);
2651 
2652  if (fDrive.GetVerbosity())
2653  {
2654  T::Out() << "Moving step " << fStep << endl;
2655  T::Out() << "Encoder [deg] " << sepos.zd*360 << " " << sepos.az*360 << endl;
2656  T::Out() << "Destination [deg] " << dest.zd *360 << " " << dest.az *360 << endl;
2657  T::Out() << "Residual [deg] " << dist.zd *360 << " " << dist.az *360 << endl;
2658  T::Out() << "Residual/max [1] " << cd.zd << " " << cd.az << endl;
2659  T::Out() << "Rel. time [1] " << t.zd << " " << t.az << endl;
2660  T::Out() << "Rel. velocity [1] " << vel.zd << " " << vel.az << endl;
2661  }
2662 
2663  fDrive.SetPointingVelocity(vel, fPointingVelocity);
2664  fDrive.StartAbsolutePositioning(dest, cdzd, cdaz);
2665 
2666  ostringstream out;
2667  if (fStep==0)
2668  out << "Moving to encoder Zd=" << dest.zd*360 << "deg Az=" << dest.az*360 << "deg";
2669  else
2670  out << "Moving residual of dZd=" << dist.zd*360*60 << "' dAz=" << dist.az*360*60 << "'";
2671  T::Info(out);
2672 
2673  fStep++;
2674  }
2675 
2676  return T::GetCurrentState()>=State::kInitialized ?
2677  T::GetCurrentState() : State::kInitialized;
2678  }
2679 
2680 public:
2681  StateMachineDrive(ostream &out=cout) :
2682  StateMachineAsio<T>(out, "DRIVE_CONTROL"), fDrive(*this, *this),
2683  fTrackingLoop(*this), fSunRise(Time().GetNextSunRise()), fDevBuffer(5)
2684  {
2685 
2686  T::Subscribe("MAGIC_WEATHER/DATA")
2687  (bind(&StateMachineDrive::HandleWeatherData, this, placeholders::_1));
2688 
2689  T::Subscribe("TPOINT/DATA")
2690  (bind(&StateMachineDrive::HandleTPoint, this, placeholders::_1));
2691 
2692  // State names
2693  T::AddStateName(State::kDisconnected, "Disconnected",
2694  "No connection to SPS");
2695  T::AddStateName(State::kConnected, "Connected",
2696  "Connection to SPS, no information received yet");
2697 
2698  T::AddStateName(State::kLocked, "Locked",
2699  "Drive system is locked (will not accept commands)");
2700 
2701  T::AddStateName(State::kUnavailable, "Unavailable",
2702  "Connected to SPS, no connection to at least one IndraDrives");
2703  T::AddStateName(State::kAvailable, "Available",
2704  "Connected to SPS and to IndraDrives, but at least one drive not in RF");
2705  T::AddStateName(State::kBlocked, "Blocked",
2706  "Drive system is blocked by manual operation or a pressed emergeny button");
2707  T::AddStateName(State::kArmed, "Armed",
2708  "Connected to SPS and IndraDrives in RF, but not yet initialized");
2709  T::AddStateName(State::kInitialized, "Initialized",
2710  "Connected to SPS and IndraDrives in RF and initialized");
2711 
2712  T::AddStateName(State::kStopping, "Stopping",
2713  "Stop command sent, waiting for telescope to be still");
2714  T::AddStateName(State::kParking, "Parking",
2715  "Telescope in parking operation, waiting for telescope to be still");
2716  T::AddStateName(State::kMoving, "Moving",
2717  "Telescope moving");
2718  T::AddStateName(State::kTracking, "Tracking",
2719  "Telescope in tracking mode");
2720  T::AddStateName(State::kOnTrack, "OnTrack",
2721  "Telescope tracking stable");
2722 
2723  T::AddStateName(State::kPositioningFailed, "PositioningFailed",
2724  "Target position was not reached within ten steps");
2725  T::AddStateName(State::kAllowedRangeExceeded, "OutOfRange",
2726  "Telecope went out of range during tracking");
2727  T::AddStateName(State::kInvalidCoordinates, "InvalidCoordinates",
2728  "Tracking coordinates out of range");
2729 
2730 
2731  T::AddEvent("REQUEST_SDO", "S:3", State::kArmed)
2732  (bind(&StateMachineDrive::RequestSdo, this, placeholders::_1))
2733  ("Request an SDO from the drive"
2734  "|node[uint32]:Node identifier (1:az, 3:zd)"
2735  "|index[uint32]:SDO index"
2736  "|subindex[uint32]:SDO subindex");
2737 
2738  T::AddEvent("SET_SDO", "S:3;X:1", State::kArmed)
2739  (bind(&StateMachineDrive::SendSdo, this, placeholders::_1))
2740  ("Request an SDO from the drive"
2741  "|node[uint32]:Node identifier (1:az, 3:zd)"
2742  "|index[uint32]:SDO index"
2743  "|subindex[uint32]:SDO subindex"
2744  "|value[uint64]:Value");
2745 
2746  // Drive Commands
2747  T::AddEvent("MOVE_TO", "D:2", State::kInitialized) // ->ZDAZ
2748  (bind(&StateMachineDrive::MoveTo, this, placeholders::_1))
2749  ("Move the telescope to the given local sky coordinates"
2750  "|Zd[deg]:Zenith distance"
2751  "|Az[deg]:Azimuth");
2752 
2753  T::AddEvent("TRACK", "D:2", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2754  (bind(&StateMachineDrive::Track, this, placeholders::_1))
2755  ("Move the telescope to the given sky coordinates and start tracking them"
2756  "|Ra[h]:Right ascension"
2757  "|Dec[deg]:Declination");
2758 
2759  T::AddEvent("WOBBLE", "D:4", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2760  (bind(&StateMachineDrive::Wobble, this, placeholders::_1))
2761  ("Move the telescope to the given wobble position around the given sky coordinates and start tracking them"
2762  "|Ra[h]:Right ascension"
2763  "|Dec[deg]:Declination"
2764  "|Offset[deg]:Wobble offset"
2765  "|Angle[deg]:Wobble angle");
2766 
2767  T::AddEvent("ORBIT", "D:5", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2768  (bind(&StateMachineDrive::Orbit, this, placeholders::_1))
2769  ("Move the telescope in a circle around the source"
2770  "|Ra[h]:Right ascension"
2771  "|Dec[deg]:Declination"
2772  "|Offset[deg]:Wobble offset"
2773  "|Angle[deg]:Starting angle"
2774  "|Period[min]:Time for one orbit");
2775 
2776  T::AddEvent("TRACK_SOURCE", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2777  (bind(&StateMachineDrive::TrackSource, this, placeholders::_1))
2778  ("Move the telescope to the given wobble position around the given source and start tracking"
2779  "|Offset[deg]:Wobble offset"
2780  "|Angle[deg]:Wobble angle"
2781  "|Name[string]:Source name");
2782 
2783  T::AddEvent("TRACK_WOBBLE", "S:1;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2784  (bind(&StateMachineDrive::TrackWobble, this, placeholders::_1))
2785  ("Move the telescope to the given wobble position around the given source and start tracking"
2786  "|Id:Wobble angle id (1 or 2)"
2787  "|Name[string]:Source name");
2788 
2789  T::AddEvent("TRACK_ORBIT", "D:2;C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2790  (bind(&StateMachineDrive::TrackOrbit, this, placeholders::_1))
2791  ("Move the telescope in a circle around the source"
2792  "|Angle[deg]:Starting angle"
2793  "|Period[min]:Time for one orbit"
2794  "|Name[string]:Source name");
2795 
2796  T::AddEvent("TRACK_ON", "C", State::kInitialized, State::kTracking, State::kOnTrack) // ->RADEC/GRB
2797  (bind(&StateMachineDrive::TrackOn, this, placeholders::_1))
2798  ("Move the telescope to the given position and start tracking"
2799  "|Name[string]:Source name");
2800 
2802  (bind(&StateMachineDrive::TrackCelest, this, kEMoon))
2803  ("Start tracking the moon");
2804  T::AddEvent("VENUS", State::kInitialized, State::kTracking, State::kOnTrack)
2805  (bind(&StateMachineDrive::TrackCelest, this, kEVenus))
2806  ("Start tracking Venus");
2808  (bind(&StateMachineDrive::TrackCelest, this, kEMars))
2809  ("Start tracking Mars");
2810  T::AddEvent("JUPITER", State::kInitialized, State::kTracking, State::kOnTrack)
2812  ("Start tracking Jupiter");
2813  T::AddEvent("SATURN", State::kInitialized, State::kTracking, State::kOnTrack)
2815  ("Start tracking Saturn");
2816 
2817  // FIXME: What to do in error state?
2819  (bind(&StateMachineDrive::Park, this))
2820  ("Park the telescope");
2821 
2823  (bind(&StateMachineDrive::StopMovement, this))
2824  ("Stop any kind of movement.");
2825 
2827  (bind(&StateMachineDrive::ResetError, this))
2828  ("Acknoledge an internal error (PositioningFailed, AllowedRangeExceeded)");
2829 
2830  T::AddEvent("TPOINT", State::kOnTrack)
2831  (bind(&StateMachineDrive::TPoint, this))
2832  ("Take a TPoint");
2833 
2834  T::AddEvent("SCREENSHOT", "B:1;C")
2835  (bind(&StateMachineDrive::Screenshot, this, placeholders::_1))
2836  ("Take a screenshot"
2837  "|color[bool]:False if just the gray image should be saved."
2838  "|name[string]:Filename");
2839 
2840  T::AddEvent("SET_LED_BRIGHTNESS", "I:2")
2841  (bind(&StateMachineDrive::SetLedBrightness, this, placeholders::_1))
2842  ("Set the LED brightness of the top and bottom leds"
2843  "|top[au]:Allowed range 0-32767 for top LEDs"
2844  "|bot[au]:Allowed range 0-32767 for bottom LEDs");
2845 
2846  T::AddEvent("LEDS_OFF")
2847  (bind(&StateMachineDrive::SetLedsOff, this))
2848  ("Switch off TPoint LEDs");
2849 
2850  T::AddEvent("UNLOCK", Drive::State::kLocked)
2851  (bind(&StateMachineDrive::Unlock, this))
2852  ("Unlock locked state.");
2853 
2854  // Verbosity commands
2855  T::AddEvent("SET_VERBOSITY", "S:1")
2856  (bind(&StateMachineDrive::SetVerbosity, this, placeholders::_1))
2857  ("Set verbosity state"
2858  "|verbosity[uint16]:disable or enable verbosity for received data (yes/no), except dynamic data");
2859 
2860  // Conenction commands
2861  T::AddEvent("DISCONNECT", State::kConnected)
2862  (bind(&StateMachineDrive::Disconnect, this))
2863  ("disconnect from ethernet");
2864 
2865  T::AddEvent("RECONNECT", "O", State::kDisconnected, State::kConnected)
2866  (bind(&StateMachineDrive::Reconnect, this, placeholders::_1))
2867  ("(Re)connect Ethernet connection to SPS, a new address can be given"
2868  "|[host][string]:new ethernet address in the form <host:port>");
2869 
2870 
2871  T::AddEvent("PRINT")
2872  (bind(&StateMachineDrive::Print, this))
2873  ("Print source list.");
2874 
2876  (bind(&StateMachineDrive::ReloadSources, this))
2877  ("Reload sources from database after database has changed..");
2878 
2879 
2880  //fDrive.SetUpdateStatus(std::bind(&StateMachineDrive::UpdateStatus, this, placeholders::_1, placeholders::_2));
2881  fDrive.StartConnect();
2882  }
2883 
2884  void SetEndpoint(const string &url)
2885  {
2886  fDrive.SetEndpoint(url);
2887  }
2888 
2889  bool AddSource(const string &name, const Source &src)
2890  {
2891  const auto it = fSources.find(name);
2892  if (it!=fSources.end())
2893  T::Warn("Source '"+name+"' already in list... overwriting.");
2894 
2895  fSources[name] = src;
2896  return it==fSources.end();
2897  }
2898 
2899  void ReadDatabase(bool print=true)
2900  {
2901 #ifdef HAVE_SQL
2902  Database db(fDatabase);
2903 
2904  T::Message("Connected to '"+db.uri()+"'");
2905 
2906  const mysqlpp::StoreQueryResult res =
2907  db.query("SELECT fSourceName, fRightAscension, fDeclination, fWobbleOffset, fWobbleAngle0, fWobbleAngle1, fMagnitude FROM Source").store();
2908 
2909  fSources.clear();
2910  for (vector<mysqlpp::Row>::const_iterator v=res.begin(); v<res.end(); v++)
2911  {
2912  const string name = (*v)[0].c_str();
2913 
2914  Source src;
2915  src.name = name;
2916  src.ra = (*v)[1];
2917  src.dec = (*v)[2];
2918  src.offset = (*v)[3];
2919  src.angles[0] = (*v)[4];
2920  src.angles[1] = (*v)[5];
2921  src.mag = (*v)[6] ? double((*v)[6]) : 0;
2922  AddSource(name, src);
2923 
2924  if (!print)
2925  continue;
2926 
2927  ostringstream msg;
2928  msg << " " << name << setprecision(8) << ": Ra=" << src.ra << "h Dec=" << src.dec << "deg";
2929  msg << " Wobble=[" << src.offset << "," << src.angles[0] << "," << src.angles[1] << "] Mag=" << src.mag;
2930  T::Message(msg);
2931  }
2932 #else
2933  T::Warn("MySQL support not compiled into the program.");
2934 #endif
2935  }
2936 
2938  {
2939  if (!fSunRise)
2940  return 1;
2941 
2942  fDrive.SetVerbose(!conf.Get<bool>("quiet"));
2943 
2944  fMaxPointingResidual = conf.Get<double>("pointing.max.residual");
2945  fPointingVelocity = conf.Get<double>("pointing.velocity");
2946 
2947  fPointingMin = Encoder(conf.Get<double>("pointing.min.zd"),
2948  conf.Get<double>("pointing.min.az"));
2949  fPointingMax = Encoder(conf.Get<double>("pointing.max.zd"),
2950  conf.Get<double>("pointing.max.az"));
2951 
2952  fParkingPos.zd = conf.Has("parking-pos.zd") ? conf.Get<double>("parking-pos.zd") : 90;
2953  fParkingPos.az = conf.Has("parking-pos.az") ? conf.Get<double>("parking-pos.az") : 0;
2954 
2955  if (!CheckRange(fParkingPos))
2956  return 2;
2957 
2958  fAccPointing = Acceleration(conf.Get<double>("pointing.acceleration.zd"),
2959  conf.Get<double>("pointing.acceleration.az"));
2960  fAccTracking = Acceleration(conf.Get<double>("tracking.acceleration.zd"),
2961  conf.Get<double>("tracking.acceleration.az"));
2962  fAccMax = Acceleration(conf.Get<double>("acceleration.max.zd"),
2963  conf.Get<double>("acceleration.max.az"));
2964 
2965  fWeatherTimeout = conf.Get<uint16_t>("weather-timeout");
2966 
2967  if (fAccPointing>fAccMax)
2968  {
2969  T::Error("Pointing acceleration exceeds maximum acceleration.");
2970  return 3;
2971  }
2972 
2973  if (fAccTracking>fAccMax)
2974  {
2975  T::Error("Tracking acceleration exceeds maximum acceleration.");
2976  return 4;
2977  }
2978 
2979  fDeviationLimit = conf.Get<uint16_t>("deviation-limit");
2980  fDeviationCounter = conf.Get<uint16_t>("deviation-count");
2981  fDeviationMax = conf.Get<uint16_t>("deviation-max");
2982 
2983  const string fname = conf.Get<string>("pointing.model-file");
2984 
2985  try
2986  {
2987  fPointingModel.Load(fname);
2988  }
2989  catch (const exception &e)
2990  {
2991  T::Error(e.what());
2992  return 5;
2993  }
2994 
2995  const vector<string> &vec = conf.Vec<string>("source");
2996 
2997  for (vector<string>::const_iterator it=vec.begin(); it!=vec.end(); it++)
2998  {
2999  istringstream stream(*it);
3000 
3001  string name;
3002 
3003  int i=0;
3004 
3005  Source src;
3006 
3007  string buffer;
3008  while (getline(stream, buffer, ','))
3009  {
3010  istringstream is(buffer);
3011 
3012  switch (i++)
3013  {
3014  case 0: name = buffer; break;
3015  case 1: src.ra = ReadAngle(is); break;
3016  case 2: src.dec = ReadAngle(is); break;
3017  case 3: is >> src.offset; break;
3018  case 4: is >> src.angles[0]; break;
3019  case 5: is >> src.angles[1]; break;
3020  }
3021 
3022  if (is.fail())
3023  break;
3024  }
3025 
3026  if (i==3 || i==6)
3027  {
3028  AddSource(name, src);
3029  continue;
3030  }
3031 
3032  T::Warn("Resource 'source' not correctly formatted: '"+*it+"'");
3033  }
3034 
3035  //fAutoResume = conf.Get<bool>("auto-resume");
3036 
3037  if (conf.Has("source-database"))
3038  {
3039  fDatabase = conf.Get<string>("source-database");
3040  ReadDatabase();
3041  }
3042 
3043  if (fSunRise.IsValid())
3044  {
3045  ostringstream msg;
3046  msg << "Next sun-rise will be at " << fSunRise;
3047  T::Message(msg);
3048  }
3049 
3050  // The possibility to connect should be last, so that
3051  // everything else is already initialized.
3052  SetEndpoint(conf.Get<string>("addr"));
3053 
3054  return -1;
3055  }
3056 };
3057 
3058 // ------------------------------------------------------------------------
3059 
3060 #include "Main.h"
3061 
3062 
3063 template<class T, class S, class R>
3065 {
3066  return Main::execute<T, StateMachineDrive<S, R>>(conf);
3067 }
3068 
3070 {
3071  po::options_description control("Drive control options");
3072  control.add_options()
3073  ("quiet,q", po_bool(), "Disable debug messages")
3074  ("no-dim,d", po_switch(), "Disable dim services")
3075  ("addr,a", var<string>("sps:5357"), "Network address of cosy")
3076  ("verbosity,v", var<uint16_t>(0), "Vervosity level (0=off; 1=major updates; 2=most updates; 3=frequent updates)")
3077  ("pointing.model-file", var<string>()->required(), "Name of the file with the pointing model in use")
3078  ("pointing.max.zd", var<double>( 104.9), "Maximum allowed zenith angle in sky pointing coordinates [deg]")
3079  ("pointing.max.az", var<double>( 85.0), "Maximum allowed azimuth angle in sky pointing coordinates [deg]")
3080  ("pointing.min.zd", var<double>(-104.9), "Minimum allowed zenith angle in sky pointing coordinates [deg]")
3081  ("pointing.min.az", var<double>(-295.0), "Minimum allowed azimuth angle in sky pointing coordinates [deg]")
3082  ("pointing.max.residual", var<double>(1./32768), "Maximum residual for a pointing operation [revolutions]")
3083  ("pointing.velocity", var<double>(0.3), "Moving velocity when pointing [% max]")
3084  ("pointing.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis for pointing operations")
3085  ("pointing.acceleration.zd", var<double>(0.03), "Acceleration for zenith axis for pointing operations")
3086  ("tracking.acceleration.az", var<double>(0.01), "Acceleration for azimuth axis during tracking operations")
3087  ("tracking.acceleration.zd", var<double>(0.01), "Acceleration for zenith axis during tracking operations")
3088  ("parking-pos.zd", var<double>(101), "Parking position zenith angle in sky pointing coordinates [deg]")
3089  ("parking-pos.az", var<double>(0), "Parking position azimuth angle in sky pointing coordinates [deg]")
3090  ("acceleration.max.az", var<double>(0.03), "Maximum allowed acceleration value for azimuth axis")
3091  ("acceleration.max.zd", var<double>(0.09), "Maximum allowed acceleration value for zenith axis")
3092  ("weather-timeout", var<uint16_t>(300), "Timeout [sec] for weather data (after timeout default values are used)")
3093  ("deviation-limit", var<uint16_t>(90), "Deviation limit in arcsec to get 'OnTrack'")
3094  ("deviation-count", var<uint16_t>(3), "Minimum number of reported deviation below deviation-limit to get 'OnTrack'")
3095  ("deviation-max", var<uint16_t>(180), "Maximum deviation in arcsec allowed to keep status 'OnTrack'")
3096  ("source-database", var<string>(), "Database link as in\n\tuser:password@server[:port]/database.")
3097  ("source", vars<string>(), "Additional source entry in the form \"name,hh:mm:ss,dd:mm:ss\"")
3098  ;
3099 
3100  conf.AddOptions(control);
3101 }
3102 
3103 /*
3104  Extract usage clause(s) [if any] for SYNOPSIS.
3105  Translators: "Usage" and "or" here are patterns (regular expressions) which
3106  are used to match the usage synopsis in program output. An example from cp
3107  (GNU coreutils) which contains both strings:
3108  Usage: cp [OPTION]... [-T] SOURCE DEST
3109  or: cp [OPTION]... SOURCE... DIRECTORY
3110  or: cp [OPTION]... -t DIRECTORY SOURCE...
3111  */
3113 {
3114  cout <<
3115  "The drivectrl is an interface to the drive PLC.\n"
3116  "\n"
3117  "The default is that the program is started without user intercation. "
3118  "All actions are supposed to arrive as DimCommands. Using the -c "
3119  "option, a local shell can be initialized. With h or help a short "
3120  "help message about the usuage can be brought to the screen.\n"
3121  "\n"
3122  "Usage: drivectrl [-c type] [OPTIONS]\n"
3123  " or: drivectrl [OPTIONS]\n";
3124  cout << endl;
3125 }
3126 
3128 {
3129  Main::PrintHelp<StateMachineDrive<StateMachine,ConnectionDrive>>();
3130 
3131  /* Additional help text which is printed after the configuration
3132  options goes here */
3133 
3134  /*
3135  cout << "bla bla bla" << endl << endl;
3136  cout << endl;
3137  cout << "Environment:" << endl;
3138  cout << "environment" << endl;
3139  cout << endl;
3140  cout << "Examples:" << endl;
3141  cout << "test exam" << endl;
3142  cout << endl;
3143  cout << "Files:" << endl;
3144  cout << "files" << endl;
3145  cout << endl;
3146  */
3147 }
3148 
3149 int main(int argc, const char* argv[])
3150 {
3151  Configuration conf(argv[0]);
3152  conf.SetPrintUsage(PrintUsage);
3154  SetupConfiguration(conf);
3155 
3156  if (!conf.DoParse(argc, argv, PrintHelp))
3157  return 127;
3158 
3159  //try
3160  {
3161  // No console access at all
3162  if (!conf.Has("console"))
3163  {
3164  if (conf.Get<bool>("no-dim"))
3165  return RunShell<LocalStream, StateMachine, ConnectionDrive>(conf);
3166  else
3167  return RunShell<LocalStream, StateMachineDim, ConnectionDimDrive>(conf);
3168  }
3169  // Cosole access w/ and w/o Dim
3170  if (conf.Get<bool>("no-dim"))
3171  {
3172  if (conf.Get<int>("console")==0)
3173  return RunShell<LocalShell, StateMachine, ConnectionDrive>(conf);
3174  else
3175  return RunShell<LocalConsole, StateMachine, ConnectionDrive>(conf);
3176  }
3177  else
3178  {
3179  if (conf.Get<int>("console")==0)
3180  return RunShell<LocalShell, StateMachineDim, ConnectionDimDrive>(conf);
3181  else
3182  return RunShell<LocalConsole, StateMachineDim, ConnectionDimDrive>(conf);
3183  }
3184  }
3185  /*catch (std::exception& e)
3186  {
3187  cerr << "Exception: " << e.what() << endl;
3188  return -1;
3189  }*/
3190 
3191  return 0;
3192 }
RaDecHa(double _ra, double _dec, double _ha)
Definition: drivectrl.cc:46
double fMaxPointingResidual
Definition: drivectrl.cc:1883
AltAz & operator+=(const AltAz &aa)
Definition: drivectrl.cc:259
bool operator>(const Acceleration &a) const
Definition: drivectrl.cc:84
int StartTrackWobble(const char *ptr, size_t size, const double &offset=0, const double &angle=0, double time=0)
Definition: drivectrl.cc:2102
void SetAcceleration(const Acceleration &acc)
Definition: drivectrl.cc:1356
virtual void UpdateTracking(const Time &, const array< double, 12 > &)
Definition: drivectrl.cc:491
Definition: dns.c:26
vector< double > fDevBuffer
Definition: drivectrl.cc:1591
double GetDevAbs(double nomzd, double meszd, double devaz)
Definition: drivectrl.cc:1762
float press
Definition: drivectrl.cc:111
uint16_t fDeviationMax
Definition: drivectrl.cc:1589
virtual void UpdateSource(const Time &, const string &, bool)
Definition: drivectrl.cc:503
int TrackOn(const EventImp &evt)
Definition: drivectrl.cc:2157
uint16_t GetVerbosity() const
Definition: drivectrl.cc:1278
uint8_t fStatusSys
Definition: drivectrl.cc:768
int EvalOptions(Configuration &conf)
Definition: drivectrl.cc:2937
double mag
Definition: drivectrl.cc:126
void StartAbsolutePositioning(const Encoder &enc, bool zd, bool az)
Definition: drivectrl.cc:1373
array< double, 2 > angles
Definition: drivectrl.cc:129
float period
Definition: HeadersSQM.h:92
AltAz(double _alt, double _az)
Definition: drivectrl.cc:256
int TrackOrbit(const EventImp &evt)
Definition: drivectrl.cc:2170
AltAz & operator-=(const AltAz &aa)
Definition: drivectrl.cc:260
RaDecHa()
Definition: drivectrl.cc:45
void UpdatePointingPosition()
Definition: drivectrl.cc:2400
Encoder fPointingMax
Definition: drivectrl.cc:1585
A general base-class describing events issues in a state machine.
Definition: EventImp.h:11
void SetupConfiguration(Configuration &conf)
Definition: drivectrl.cc:3069
Encoder operator*(double f) const
Definition: drivectrl.cc:70
uint64_t fTrackingCounter
Definition: drivectrl.cc:1594
bool IsOnline() const
Definition: drivectrl.cc:1317
bool SendSource(const tuple< Time, vector< char >, bool > &t)
Definition: drivectrl.cc:1429
Encoder fPointingMin
Definition: drivectrl.cc:1584
int mode
string name
Definition: drivectrl.cc:123
void SetLedVoltage(const uint32_t &v1, const uint32_t &v2)
Definition: drivectrl.cc:1385
void SetupConfiguration(Configuration &conf)
Definition: Main.h:25
int RequestSdo(const EventImp &evt)
Definition: drivectrl.cc:1822
ba::deadline_timer fTrackingLoop
Definition: drivectrl.cc:1566
int main(int argc, const char *argv[])
Definition: drivectrl.cc:3149
float hum
Definition: drivectrl.cc:109
Acceleration fAccPointing
Definition: drivectrl.cc:1880
PointingSetup fPointingSetup
Definition: drivectrl.cc:1579
int i
Definition: db_dim_client.c:21
void setQuality(int quality)
Definition: discpp.cxx:1256
bool SendTracking(const pair< Time, array< double, 12 >> &p)
Definition: drivectrl.cc:1422
void UpdateSource(const Time &t, const string &name, bool tracking)
Definition: drivectrl.cc:1481
The base implementation of a distributed messaging system.
Definition: MessageImp.h:10
const LnLatPosn & ORM()
Definition: nova.h:66
Adds some functionality to boost::posix_time::ptime for our needs.
Definition: Time.h:30
char str[80]
Definition: test_client.c:7
Encoder GetVelUnit() const
Definition: drivectrl.cc:1344
void SetPrintUsage(const std::function< void(void)> &func)
T Get(const std::string &var)
uint32_t NightAsInt() const
Definition: Time.cc:397
void SetTrackingVelocity(const Velocity &vel)
Definition: drivectrl.cc:1367
float mag
Definition: HeadersSQM.h:89
Time GetNextSunRise(double horizon) const
Definition: Time.cc:346
ZdAz MountToSky(const Encoder &mnt) const
Definition: drivectrl.cc:317
ConnectionDrive(ba::io_service &ioservice, MessageImp &imp)
Definition: drivectrl.cc:1267
RaDec pointing
Definition: drivectrl.cc:165
Velocity operator/(double f) const
Definition: drivectrl.cc:60
po::typed_value< bool > * po_switch()
STL namespace.
RaDecHa apparent
Definition: drivectrl.cc:166
Timeout_t(ba::io_service &ioservice, uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v, uint16_t millisec)
Definition: drivectrl.cc:1007
int Reconnect(const EventImp &evt)
Definition: drivectrl.cc:2320
uint64_t fDevCount
Definition: drivectrl.cc:1592
int TrackSource(const EventImp &evt)
Definition: drivectrl.cc:2133
double zd
Definition: drivectrl.cc:51
void HandlePdo1(const uint8_t &node, const uint8_t *data, const Time &tv)
Definition: drivectrl.cc:748
bool HasError() const
Definition: drivectrl.cc:1310
RaDec source
Definition: drivectrl.cc:164
int HandleTPoint(const EventImp &evt)
Definition: drivectrl.cc:1630
Queue< pair< Time, array< uint8_t, 3 > > > fQueueStatus
Definition: drivectrl.cc:1413
std::vector< T > Vec(const std::string &var)
Definition: Queue.h:28
std::list< Timeout_t > fTimeouts
Definition: drivectrl.cc:1016
bool IsInitialized() const
Definition: drivectrl.cc:1303
double fFlop
Definition: drivectrl.cc:177
std::string GetString() const
Definition: EventImp.cc:194
void UpdatePointing(const Time &t, const array< double, 2 > &arr)
Definition: drivectrl.cc:1456
map< string, Source > sources
Definition: drivectrl.cc:1570
int Orbit(const EventImp &evt)
Definition: drivectrl.cc:2026
float temp
Definition: HeadersPFmini.h:56
Encoder fMovementTarget
Definition: drivectrl.cc:1580
int SendSdo(const EventImp &evt)
Definition: drivectrl.cc:1849
double fPointingVelocity
Definition: drivectrl.cc:1884
bool IsBlocked() const
Definition: drivectrl.cc:1327
void StartReadReport()
Definition: drivectrl.cc:1143
void print(std::ostream &out) const
Encoder & operator*=(double f)
Definition: drivectrl.cc:68
int HandleWeatherData(const EventImp &evt)
Definition: drivectrl.cc:1612
ZdAz(double _zd=0, double _az=0)
Definition: drivectrl.cc:77
double fNpae
Definition: drivectrl.cc:178
void SetRpmMode(bool mode)
Definition: drivectrl.cc:1349
RaDec(double _ra, double _dec)
Definition: drivectrl.cc:39
float temp
Definition: drivectrl.cc:110
uint16_t GetUShort() const
Definition: EventImp.h:92
PointingData CalcPointingPos(double mjd)
Definition: drivectrl.cc:1815
void HandleSdo(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx, const uint32_t &val, const Time &tv)
Definition: drivectrl.cc:587
bool IsMoving() const
Definition: drivectrl.cc:1297
void UpdateTPoint(const Time &t, const DimTPoint &data, const string &name)
Definition: drivectrl.cc:1471
void palAmpqk(double ra, double da, double amprms[21], double *rm, double *dm)
Definition: palAmpqk.c:85
double start
Definition: drivectrl.cc:153
void SetVerbosity(const uint16_t &v)
Definition: drivectrl.cc:1273
double az
Definition: drivectrl.cc:52
void HandleReceivedData(const boost::system::error_code &err, size_t bytes_received, int)
Definition: drivectrl.cc:1020
void HandleSdoOk(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx, const Time &)
Definition: drivectrl.cc:659
void ReadDatabase(bool print=true)
Definition: drivectrl.cc:2899
void SendSdo(uint8_t node, uint16_t idx, uint32_t val)
Definition: drivectrl.cc:1292
PointingData CalcPointingPos(const PointingSetup &setup, double _mjd, const Weather &weather, uint16_t timeout, bool tpoint=false)
Definition: drivectrl.cc:365
double palDtt(double dju)
Definition: palDtt.c:75
bool Has(const std::string &var)
void palMappa(double eq, double date, double amprms[21])
Definition: palMappa.c:94
int Screenshot(const EventImp &evt)
Definition: drivectrl.cc:2220
uint16_t fDeviationLimit
Definition: drivectrl.cc:1587
void LogErrorCode(uint32_t node)
Definition: drivectrl.cc:953
Acceleration(double _zd=0, double _az=0)
Definition: drivectrl.cc:83
void palAopqk(double rap, double dap, const double aoprms[14], double *aob, double *zob, double *hob, double *dob, double *rob)
Definition: palAopqk.c:179
double dec
Definition: drivectrl.cc:37
virtual void UpdateSource(const Time &, const array< double, 5 > &, const string &="")
Definition: drivectrl.cc:506
Queue< pair< Time, array< double, 2 > > > fQueuePointing
Definition: drivectrl.cc:1409
Velocity(double _zd=0, double _az=0)
Definition: drivectrl.cc:59
int SetLedBrightness(const EventImp &evt)
Definition: drivectrl.cc:2243
void AddOptions(const po::options_description &opt, bool visible=true)
Definition: Configuration.h:92
double Sign(double val, double alt) const
Definition: drivectrl.cc:263
int TrackCelest(const Planets_t &p)
Definition: drivectrl.cc:1981
void SendSdoRequest(uint8_t node, uint8_t req, uint16_t idx, uint8_t subidx, uint32_t val=0)
Definition: drivectrl.cc:1239
void SetPointingVelocity(const Velocity &vel, double scale=1)
Definition: drivectrl.cc:1362
uint16_t fNumRings
Definition: HeadersDrive.h:74
void setData(const void *ptr, size_t sz)
double UnixTime() const
Definition: Time.cc:195
void Mjd(double mjd)
Definition: Time.cc:145
int TrackWobble(const EventImp &evt)
Definition: drivectrl.cc:2059
double ha
Definition: drivectrl.cc:44
float lat
Definition: HeadersGPS.h:23
virtual Time GetTime() const
Definition: EventImp.h:57
Encoder operator-(const Encoder &a, const Encoder &b)
Definition: drivectrl.cc:90
string ErrCodeToString(uint32_t code) const
Definition: drivectrl.cc:861
void RequestSdo(uint8_t node, uint16_t idx, uint8_t subidx=0)
Definition: drivectrl.cc:1283
bool IsReady() const
Definition: drivectrl.cc:1322
int UpdateTrackingPosition()
Definition: drivectrl.cc:2340
RaDec()
Definition: drivectrl.cc:38
double wobble_angle
Definition: drivectrl.cc:156
bool valid() const
Definition: HeadersFTM.h:271
Source()
Definition: drivectrl.cc:117
static uint32_t String(uint8_t b0=0, uint8_t b1=0, uint8_t b2=0, uint8_t b3=0)
Definition: drivectrl.cc:576
double dec
Definition: drivectrl.cc:125
Encoder mount
Definition: drivectrl.cc:168
void SendCommandNB(const std::string &command)
Definition: Dim.h:30
Warning because the service this data corrsponds to might have been last updated longer ago than Local time
Definition: smartfact.txt:92
int InitMovement(const ZdAz &sky, bool tracking=false, const string &name="")
Definition: drivectrl.cc:1886
bool IsValid() const
Definition: Time.h:90
uint16_t fDeviationCounter
Definition: drivectrl.cc:1588
std::string uri() const
Definition: Database.h:43
bool AddSource(const string &name, const Source &src)
Definition: drivectrl.cc:2889
void HandlePdo3(const uint8_t &node, const uint8_t *data, const Time &tv)
Definition: drivectrl.cc:787
ZdAz operator*(const double &f) const
Definition: drivectrl.cc:78
float hum
Definition: HeadersPFmini.h:55
int MoveTo(const EventImp &evt)
Definition: drivectrl.cc:1918
int StartTracking(const Source &src, double offset, double angle, double period=0)
Definition: drivectrl.cc:1945
double GetSeTime() const
Definition: drivectrl.cc:1337
bool SendStatus(const pair< Time, array< uint8_t, 3 >> &p)
Definition: drivectrl.cc:1441
void TrackingLoop(const boost::system::error_code &error=boost::system::error_code())
Definition: drivectrl.cc:2415
Commandline parsing, resource file parsing and database access.
Definition: Configuration.h:9
void HandleSdoError(const uint8_t &node, const uint16_t &idx, const uint8_t &subidx, const Time &)
Definition: drivectrl.cc:728
int buffer[BUFFSIZE]
Definition: db_dim_client.c:14
Source source
Definition: drivectrl.cc:151
Encoder GetSePos() const
Definition: drivectrl.cc:1332
float height
Definition: HeadersGPS.h:26
Encoder & operator-=(const Encoder &enc)
Definition: drivectrl.cc:69
void HandleTimeout(const std::list< Timeout_t >::iterator &ref, const bs::error_code &error)
Definition: drivectrl.cc:1233
virtual void UpdateStatus(const Time &, const array< uint8_t, 3 > &)
Definition: drivectrl.cc:495
double fAcec
Definition: drivectrl.cc:193
Queue< pair< Time, vector< char > > > fQueueTPoint
Definition: drivectrl.cc:1412
double ra
Definition: drivectrl.cc:124
bool operator==(const SDO &s) const
Definition: drivectrl.cc:998
double wobble_offset
Definition: drivectrl.cc:155
bool SendTPoint(const pair< Time, vector< char >> &p)
Definition: drivectrl.cc:1448
int size
Definition: db_dim_server.c:17
void UpdateSource(const Time &t, const array< double, 5 > &arr, const string &name="")
Definition: drivectrl.cc:1489
uint16_t fVerbosity
Definition: drivectrl.cc:484
float data[4 *1440]
Encoder operator/(const Encoder &a, const Encoder &b)
Definition: drivectrl.cc:102
Acceleration fAccMax
Definition: drivectrl.cc:1882
ConnectionDimDrive(ba::io_service &ioservice, MessageImp &imp)
Definition: drivectrl.cc:1499
PointingSetup(Planets_t p=kENone)
Definition: drivectrl.cc:158
Error, something unexpected happened, but can still be handled by the program.
Definition: MessageImp.h:19
void HandlePdo2(const uint8_t &node, const uint8_t *data, const Time &)
Definition: drivectrl.cc:977
int SetVerbosity(const EventImp &evt)
Definition: drivectrl.cc:2263
Acceleration fAccTracking
Definition: drivectrl.cc:1881
void SendCanFrame(uint16_t cobid, uint8_t m0=0, uint8_t m1=0, uint8_t m2=0, uint8_t m3=0, uint8_t m4=0, uint8_t m5=0, uint8_t m6=0, uint8_t m7=0)
Definition: drivectrl.cc:537
PointingModel fPointingModel
Definition: drivectrl.cc:1578
void ConnectionEstablished()
Definition: drivectrl.cc:1155
Time time
Definition: drivectrl.cc:112
Encoder SkyToMount(AltAz p)
Definition: drivectrl.cc:271
Queue< tuple< Time, vector< char >, bool > > fQueueSource
Definition: drivectrl.cc:1411
double fEces
Definition: drivectrl.cc:190
double fEcec
Definition: drivectrl.cc:192
Queue< pair< Time, array< double, 12 > > > fQueueTracking
Definition: drivectrl.cc:1410
void SendSdo(uint8_t node, uint16_t idx, uint8_t subidx, uint32_t val)
Definition: drivectrl.cc:1287
Error states should be between 0x100 and 0xffff.
Definition: MPointing.h:64
bool GetBool() const
Definition: EventImp.h:90
AltAz(const ZdAz &za)
Definition: drivectrl.cc:257
TT t
Definition: test_client.c:26
void palRdplan(double date, int np, double elong, double phi, double *ra, double *dec, double *diam)
Definition: palRdplan.c:101
Return to feeserver c CVS log Up to[MAIN] dcscvs FeeServer feeserver src Wed FeeServer_v0 v0 dev
Definition: feeserver.c:5
Error()
Definition: HeadersFTM.h:197
bool CheckRange(ZdAz pos)
Definition: drivectrl.cc:1786
Planets_t planet
Definition: drivectrl.cc:152
Velocity operator*(double f) const
Definition: drivectrl.cc:61
double offset
Definition: drivectrl.cc:128
void Load(const string &name)
Definition: drivectrl.cc:197
double orbit_period
Definition: drivectrl.cc:154
void SetEndpoint(const string &url)
Definition: drivectrl.cc:2884
std::string Trim(const std::string &str)
Definition: tools.cc:68
Connection(boost::asio::io_service &io_service, std::ostream &out)
Definition: Connection.cc:454
uint16_t fWeatherTimeout
Definition: drivectrl.cc:1574
po::typed_value< bool > * po_bool(bool def=false)
int Wobble(const EventImp &evt)
Definition: drivectrl.cc:2013
int Track(const EventImp &evt)
Definition: drivectrl.cc:2119
void PrintHelp()
Definition: drivectrl.cc:3127
virtual void UpdatePointing(const Time &, const array< double, 2 > &)
Definition: drivectrl.cc:487
T Get(size_t offset=0) const
Definition: EventImp.h:66
bool CheckEventSize(size_t has, const char *name, size_t size)
Definition: drivectrl.cc:1599
std::string GetAsStr(const char *fmt="%Y-%m-%d %H:%M:%S") const
Definition: Time.cc:240
Maintains an ansynchronous TCP/IP client connection.
Definition: Connection.h:15
void UpdateTracking(const Time &t, const array< double, 12 > &arr)
Definition: drivectrl.cc:1461
virtual const void * GetData() const
Definition: EventImp.h:54
void palAoppa(double date, double dut, double elongm, double phim, double hm, double xp, double yp, double tdk, double pmb, double rh, double wl, double tlr, double aoprms[14])
Definition: palAoppa.c:172
void palMapqkz(double rm, double dm, double amprms[21], double *ra, double *da)
Definition: palMapqkz.c:97
bool DoParse(int argc, const char **argv, const std::function< void()> &func=std::function< void()>())
SDO(uint8_t n, uint8_t r, uint16_t i, uint8_t s, uint32_t v=0)
Definition: drivectrl.cc:995
void UpdateStatus(const Time &t, const array< uint8_t, 3 > &arr)
Definition: drivectrl.cc:1466
double ReadAngle(istream &in)
Definition: drivectrl.cc:1774
Do not initialize the time.
Definition: Time.h:51
int TrackCelest(const string &cmd, const string &source)
Definition: cosyctrl.cc:861
Encoder(double _zd=0, double _az=0)
Definition: drivectrl.cc:66
const T * Ptr(size_t offset=0) const
Definition: EventImp.h:74
const sources::const_iterator GetSourceFromDB(const char *ptr, const char *last)
Definition: drivectrl.cc:2039
Local(double _zd=0, double _az=0)
Definition: drivectrl.cc:54
double ra
Definition: drivectrl.cc:36
Velocity operator/(double t) const
Definition: drivectrl.cc:71
void HandleTimeoutImp(const std::list< Timeout_t >::iterator &ref, const bs::error_code &error)
Definition: drivectrl.cc:1190
StateMachineDrive(ostream &out=cout)
Definition: drivectrl.cc:2681
double end
Planets_t
Definition: drivectrl.cc:132
int RunShell(Configuration &conf)
Definition: drivectrl.cc:3064
double fAces
Definition: drivectrl.cc:191
void PrintUsage()
Definition: drivectrl.cc:3112
Encoder Abs() const
Definition: drivectrl.cc:72
double mjd
Definition: drivectrl.cc:169
virtual void UpdateTPoint(const Time &, const DimTPoint &, const string &)
Definition: drivectrl.cc:499
virtual size_t GetSize() const
Definition: EventImp.h:55
bool SendPointing(const pair< Time, array< double, 2 >> &p)
Definition: drivectrl.cc:1415
uint16_t fNumLeds
Definition: HeadersDrive.h:73
vector< uint8_t > fData
Definition: drivectrl.cc:1018