API
tcsInterface.hpp
Go to the documentation of this file.
1 /** \file tcsInterface.hpp
2  * \brief The MagAO-X TCS Interface header file
3  *
4  * \ingroup tcsInterface_files
5  */
6 
7 #ifndef tcsInterface_hpp
8 #define tcsInterface_hpp
9 
10 #include <cmath>
11 
12 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
13 #include "../../magaox_git_version.h"
14 
15 
16 #include "../../libMagAOX/app/dev/telemeter.hpp"
17 
18 //#define LOG_TCS_STATUS
19 
20 /** \defgroup tcsInterface
21  * \brief The MagAO-X application to do interface with the Clay TCS
22  *
23  * <a href="../handbook/operating/software/apps/tcsInterface.html">Application Documentation</a>
24  *
25  * \ingroup apps
26  *
27  */
28 
29 /** \defgroup tcsInterface_files
30  * \ingroup tcsInterface
31  */
32 
33 namespace MagAOX
34 {
35 namespace app
36 {
37 
38 /// The MagAO-X Clay Telescope TCS Interface
39 /**
40  * \ingroup tcsInterface
41  */
42 class tcsInterface : public MagAOXApp<true>, public dev::ioDevice, public dev::telemeter<tcsInterface>
43 {
44 
45  //Give the test harness access.
46  friend class tcsInterface_test;
47 
48  friend class dev::telemeter<tcsInterface>;
49 
50 
51 protected:
52 
53  /** \name Configurable Parameters
54  *@{
55  */
56 
57  std::string m_deviceAddr {"localhost"}; ///< The IP address or resolvable name of the TCS.
58  int m_devicePort {5811}; ///< The IP port for TCS communications. Should be the command port. Default is 5811
60 
61  bool m_labMode {true};
62 
63  ///@}
64 
65 
67 
68  ///Mutex for locking INDI communications.
69  std::mutex m_tcsMutex;
70 
71  //Telescope time:
72  double m_telST {0};
73 
74  pcf::IndiProperty m_indiP_teltime;
75 
76 
77  //Telescope position:
78  double m_telEpoch {0};
79  double m_telRA {0};
80  double m_telDec {0};
81  double m_telEl {0};
82  double m_telHA {0};
83  double m_telAM {0};
84  double m_telRotOff {0};
85 
86  pcf::IndiProperty m_indiP_telpos;
87 
88  /// \name Telescope Data
89  /**@{
90  */
91  int m_telROI {0}; ///< The rotator of interest
92  int m_telTracking {0}; ///< tracking state
93  int m_telGuiding {0}; ///< guider moving state
94  int m_telSlewing {0}; ///< slewing state
95  int m_telGuiderMoving {0}; ///< guider moving state
96  double m_telAz {0}; ///< azimuth
97  double m_telZd {0}; ///< zenith distance
98  double m_telPA {0}; ///< parallactic angle
99  double m_telDomeAz {0}; ///< dome azimuth
100  int m_telDomeStat {0}; ///< dome status
101 
102  pcf::IndiProperty m_indiP_teldata;
103  ///@}
104 
105  /// \name Telescope Catalog Information
106  /**@{
107  */
108  double m_catRA {0}; ///< Catalog right ascension [degrees]
109  double m_catDec {0}; ///< Catalog declination [degrees]
110  double m_catEp {0}; ///< Catalog epoch
111  double m_catRo {0}; ///< Catalog rotator offset
112  std::string m_catRm; ///< Catalog rotator mode
113  std::string m_catObj; ///< Catalog object name
114 
115  pcf::IndiProperty m_indiP_catalog; ///< INDI Property for the catalog text information
116  pcf::IndiProperty m_indiP_catdata; ///< INDI Property for the catalog data
117  ///@}
118 
119  //Telescope Vane-End positions
120  double m_telSecZ {0};
121  double m_telEncZ {0};
122  double m_telSecX {0};
123  double m_telEncX {0};
124  double m_telSecY {0};
125  double m_telEncY {0};
126  double m_telSecH {0};
127  double m_telEncH {0};
128  double m_telSecV {0};
129  double m_telEncV {0};
130 
131  pcf::IndiProperty m_indiP_vaneend; ///< INDI Property for the vane end positions
132 
133 
134  //Environment
135  double m_wxtemp {0}; ///< Outside temperature, Celsius
136  double m_wxpres {0}; ///< Outside pressue, millibars
137  double m_wxhumid {0}; ///< Outside humidity, percent
138  double m_wxwind {0}; ///< outside wind intensity, mph
139  double m_wxwdir {0}; ///< outside wind direction, degrees
140  double m_ttruss {0}; ///< Telescope truss temperature, Celsius
141  double m_tcell {0}; ///< Primary mirror cell temperature, Celsius
142  double m_tseccell {0}; ///< Secondary mirror cell temperature, Celsius
143  double m_tambient {0}; ///< Dome air temperature, Celsius
144  double m_wxdewpoint {0}; ///<Dew point from weather station
145 
146  pcf::IndiProperty m_indiP_env; ///< INDI Property for environment
147 
148  //Seeing
149  double m_dimm_el {0}; ///< DIMM elevation at time of seeing measurement
150  double m_dimm_fwhm {0}; ///< DIMM raw FWHM
151  double m_dimm_fwhm_corr {0}; ///< DIMM elevation corrected FWHM
152  int m_dimm_time {0}; ///< Seconds since midnight of DIMM measurement.
153 
154  double m_mag1_el {0}; ///< MAG1 elevation at time of seeing measurement
155  double m_mag1_fwhm {0}; ///< MAG1 raw FWHM
156  double m_mag1_fwhm_corr {0}; ///< MAG1 elevation corrected FWHM
157  int m_mag1_time {0}; ///< Seconds since midnight of MAG1 measurement.
158 
159  double m_mag2_el {0}; ///< MAG2 elevation at time of seeing measurement
160  double m_mag2_fwhm {0}; ///< MAG2 raw FWHM
161  double m_mag2_fwhm_corr {0}; ///< MAG2 elevation corrected FWHM
162  int m_mag2_time {0}; ///< Seconds since midnight of MAG2 measurement.
163 
164  pcf::IndiProperty m_indiP_seeing; ///< INDI Property for seeing
165 
166 public:
167  /// Default c'tor.
168  tcsInterface();
169 
170  /// D'tor, declared and defined for noexcept.
171  ~tcsInterface() noexcept
172  {}
173 
174  virtual void setupConfig();
175 
176  /// Implementation of loadConfig logic, separated for testing.
177  /** This is called by loadConfig().
178  */
179  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
180 
181  virtual void loadConfig();
182 
183  /// Startup function
184  /**
185  *
186  */
187  virtual int appStartup();
188 
189  /// Implementation of the FSM for tcsInterface.
190  /**
191  * \returns 0 on no critical error
192  * \returns -1 on an error requiring shutdown
193  */
194  virtual int appLogic();
195 
196  /// Shutdown the app.
197  /**
198  *
199  */
200  virtual int appShutdown();
201 
202  int getMagTelStatus( std::string & response,
203  const std::string &statreq
204  );
205 
206  int sendMagTelCommand( const std::string &command,
207  int timeout
208  );
209 
210  int parse_xms( double &x,
211  double &m,
212  double &s,
213  const std::string & xmsstr
214  );
215 
216  std::vector<std::string> parse_teldata( std::string &tdat );
217 
218 
219  //The "dump" commands:
220 
221  int getTelTime();
222  int getTelPos();
223  int getTelData();
224  int getCatData();
225  int getVaneData();
226  int getEnvData();
227  int getSeeing();
228 
229  int updateINDI();
230 
231  /** \name Telemeter Interface
232  * @{
233  */
234  int checkRecordTimes();
235 
236  int recordTelem( const telem_telpos * );
237 
238  int recordTelem( const telem_teldata * );
239 
240  int recordTelem( const telem_telvane * );
241 
242  int recordTelem( const telem_telenv * );
243 
244  int recordTelem( const telem_telcat *);
245 
246  int recordTelem( const telem_telsee *);
247 
248  int recordTelPos(bool force = false);
249 
250  int recordTelData(bool force = false);
251 
252  int recordTelVane(bool force = false);
253 
254  int recordTelEnv(bool force = false);
255 
256  int recordTelCat(bool force = false);
257 
258  int recordTelSee(bool force = false);
259 
260  ///@}
261 
262  int m_loopState {0};
263  pcf::IndiProperty m_indiP_loopState; ///< Property used to report the loop state
264 
266 
267  /** \name Pyramid Nudging and Acquisition
268  * Handling of nudges on pyramid tip.
269  * @{
270  */
271 
272  //The Pyramid to AEG control matrix
273  float m_pyrNudge_C_00 {1};
274  float m_pyrNudge_C_01 {0};
275  float m_pyrNudge_C_10 {0};
276  float m_pyrNudge_C_11 {1};
277  float m_pyrNudge_F_sign {1};
278 
279  float m_pyrNudge_ang {45.0};
280  float m_pyrNudge_ang0 {0.0};
281  float m_pyrNudge_parity {-1};
282 
283  int sendPyrNudge( float x,
284  float y,
285  float z
286  );
287 
288  pcf::IndiProperty m_indiP_pyrNudge; ///< Property used to request a pyramid nudge
290 
291  int m_acqZdSign {-1};
292  float m_acqAz0 {18.5};
293  float m_acqAzOff {0};
294  float m_acqEl0 {10};
295  float m_acqElOff {0};
296  float m_acqFocus{1400};
297 
298  int acquireFromGuider();
299 
300  pcf::IndiProperty m_indiP_acqFromGuider; ///< Property used to request a pyramid nudge
302 
303  ///@}
304 
305  /** \name Woofer Offloading
306  * Handling of offloads from the average woofer shape to the telescope
307  * @{
308  */
309 
310  bool m_offloadThreadInit {true}; ///< Initialization flag for the offload thread.
311 
312  pid_t m_offloadThreadID {0}; ///< Offload thread pid.
313 
314  pcf::IndiProperty m_offloadThreadProp; ///< Offload thread INDI property.
315 
316  std::thread m_offloadThread; ///< The offloading thread.
317 
318  /// Offload thread starter function
319  static void offloadThreadStart( tcsInterface * t /**< [in] pointer to this */);
320 
321  /// Offload thread function
322  /** Runs until m_shutdown is true.
323  */
324  void offloadThreadExec();
325 
326  int doTToffload( float TT_0,
327  float TT_1
328  );
329 
330  int sendTToffload( float TT_0,
331  float TT_1
332  );
333 
334  int doFoffload( float F_0 );
335 
336  int sendFoffload( float F_0 );
337 
338 
339  pcf::IndiProperty m_indiP_offloadCoeffs; ///< Property used to report the latest woofer modal coefficients for offloading
340 
342 
343  std::vector<std::vector<float>> m_offloadRequests;
344  size_t m_firstRequest {0};
345  size_t m_lastRequest {std::numeric_limits<size_t>::max()};
346  size_t m_nRequests {0};
347  size_t m_last_nRequests {0};
348 
349  //The TT control matrix -- LAb
350  float m_lab_offlTT_C_00 {0.17};
351  float m_lab_offlTT_C_01 {1.03};
352  float m_lab_offlTT_C_10 {-1.03};
353  float m_lab_offlTT_C_11 {0.48};
354 
355  //The TT control matrix -- Telescope
356  float m_offlTT_C_00 {-0.5};
357  float m_offlTT_C_01 {0};
358  float m_offlTT_C_10 {0};
359  float m_offlTT_C_11 {-0.25};
360 
361  bool m_offlTT_enabled {false};
362  bool m_offlTT_dump {false};
363  float m_offlTT_avgInt {1.0};
364  float m_offlTT_gain {0.1};
365  float m_offlTT_thresh {0.1};
366 
367  pcf::IndiProperty m_indiP_offlTTenable;
369 
370  pcf::IndiProperty m_indiP_offlTTdump;
372 
373  pcf::IndiProperty m_indiP_offlTTavgInt;
375 
376  pcf::IndiProperty m_indiP_offlTTgain;
378 
379  pcf::IndiProperty m_indiP_offlTTthresh;
381 
382  //The Focus control constant
383  float m_offlCFocus_00 {1};
384 
385  bool m_offlF_enabled {false};
386  bool m_offlF_dump {false};
387  float m_offlF_avgInt {1.0};
388  float m_offlF_gain {0.1};
389  float m_offlF_thresh {0.1};
390 
391  pcf::IndiProperty m_indiP_offlFenable;
393 
394  pcf::IndiProperty m_indiP_offlFdump;
396 
397  pcf::IndiProperty m_indiP_offlFavgInt;
399 
400  pcf::IndiProperty m_indiP_offlFgain;
402 
403  pcf::IndiProperty m_indiP_offlFthresh;
405 
406 
407 
408 
409 
410  float m_offlCComa_00 {1};
411  float m_offlCComa_01 {0};
412  float m_offlCComa_10 {1};
413  float m_offlCComa_11 {0};
414 
415  ///@}
416 };
417 
418 inline
419 tcsInterface::tcsInterface() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
420 {
421  return;
422 }
423 
424 inline
426 {
427  config.add("labMode", "", "labMode", argType::Required, "", "labMode", false, "bool", "Flag to enable lab mode. Default is true.");
428 
429  config.add("pyrNudger.C_00", "", "pyrNudger.C_00", argType::Required, "pyrNudger", "C_00", false, "float", "Pyramid to AEG control matrix [0,0] of a 2x2 matrix");
430  config.add("pyrNudger.C_01", "", "pyrNudger.C_01", argType::Required, "pyrNudger", "C_01", false, "float", "Pyramid to AEG control matrix [0,1] of a 2x2 matrix ");
431  config.add("pyrNudger.C_10", "", "pyrNudger.C_10", argType::Required, "pyrNudger", "C_10", false, "float", "Pyramid to AEG control matrix [1,0] of a 2x2 matrix ");
432  config.add("pyrNudger.C_11", "", "pyrNudger.C_11", argType::Required, "pyrNudger", "C_11", false, "float", "Pyramid to AEG control matrix [1,1] of a 2x2 matrix ");
433 
434  config.add("pyrNudger.ang", "", "pyrNudger.ang", argType::Required, "pyrNudger", "ang", false, "float", "");
435  config.add("pyrNudger.ang0", "", "pyrNudger.ang0", argType::Required, "pyrNudger0", "ang0", false, "float", "");
436  config.add("pyrNudger.parity", "", "pyrNudger.parity", argType::Required, "pyrNudger", "parity", false, "float", "");
437 
438  config.add("pyrNudger.F_sign", "", "pyrNudger.F_sign", argType::Required, "pyrNudger", "F_sign", false, "int", "Pyramid to AEG control matrix [1,1] of a 2x2 matrix ");
439 
440  config.add("acqFromGuider.zdSign", "", "acqFromGuider.zdSign", argType::Required, "acqFromGuider", "zdSign", false, "int", "Sign of the Zd to rotation angle, +1 or -1, -1 default");
441  config.add("acqFromGuider.az0", "", "acqFromGuider.az0", argType::Required, "acqFromGuider", "az0", false, "float", "az component of acquisition vector a 0 zd.");
442  config.add("acqFromGuider.azoff", "", "acqFromGuider.azoff", argType::Required, "acqFromGuider", "azoff", false, "float", "static offset to az component of acquisition vector");
443  config.add("acqFromGuider.el0", "", "acqFromGuider.el0", argType::Required, "acqFromGuider", "el0", false, "float", "el component of acquisition vector a 0 zd.");
444  config.add("acqFromGuider.eloff", "", "acqFromGuider.eloff", argType::Required, "acqFromGuider", "eloff", false, "float", "static offset to el component of acquisition vector");
445  config.add("acqFromGuider.focus", "", "acqFromGuider.focus", argType::Required, "acqFromGuider", "focus", false, "float", "static offset for focus acquisition");
446 
447  config.add("offload.TT_avgInt", "", "offload.TT_avgInt", argType::Required, "offload", "TT_avgInt", false, "float", "Woofer to Telescope T/T offload averaging interval [sec] ");
448  config.add("offload.TT_gain", "", "offload.TT_gain", argType::Required, "offload", "TT_gain", false, "float", "Woofer to Telescope T/T offload gain");
449  config.add("offload.TT_thresh", "", "offload.TT_thresh", argType::Required, "offload", "TT_thresh", false, "float", "Woofer to Telescope T/T offload threshold");
450 
451  config.add("offload.lab_TT_C_00", "", "offload.lab_TT_C_00", argType::Required, "offload", "lab_TT_C_00", false, "float", "Woofer to TTM T/T offload control matrix [0,0] of a 2x2 matrix");
452  config.add("offload.lab_TT_C_01", "", "offload.lab_TT_C_01", argType::Required, "offload", "lab_TT_C_01", false, "float", "Woofer to TTM T/T offload control matrix [0,1] of a 2x2 matrix ");
453  config.add("offload.lab_TT_C_10", "", "offload.lab_TT_C_10", argType::Required, "offload", "lab_TT_C_10", false, "float", "Woofer to TTM T/T offload control matrix [1,0] of a 2x2 matrix ");
454  config.add("offload.lab_TT_C_11", "", "offload.lab_TT_C_11", argType::Required, "offload", "lab_TT_C_11", false, "float", "Woofer to TTM T/T offload control matrix [1,1] of a 2x2 matrix ");
455 
456  config.add("offload.TT_C_00", "", "offload.TT_C_00", argType::Required, "offload", "TT_C_00", false, "float", "Woofer to Telescope T/T offload control matrix [0,0] of a 2x2 matrix");
457  config.add("offload.TT_C_01", "", "offload.TT_C_01", argType::Required, "offload", "TT_C_01", false, "float", "Woofer to Telescope T/T offload control matrix [0,1] of a 2x2 matrix ");
458  config.add("offload.TT_C_10", "", "offload.TT_C_10", argType::Required, "offload", "TT_C_10", false, "float", "Woofer to Telescope T/T offload control matrix [1,0] of a 2x2 matrix ");
459  config.add("offload.TT_C_11", "", "offload.TT_C_11", argType::Required, "offload", "TT_C_11", false, "float", "Woofer to Telescope T/T offload control matrix [1,1] of a 2x2 matrix ");
460 
461 
462  config.add("offload.F_avgInt", "", "offload.F_avgInt", argType::Required, "offload", "F_avgInt", false, "float", "Woofer to Telescope Focus offload averaging interval [sec] ");
463  config.add("offload.F_gain", "", "offload.F_gain", argType::Required, "offload", "F_gain", false, "float", "Woofer to Telescope Focus offload gain");
464  config.add("offload.F_thresh", "", "offload.F_thresh", argType::Required, "offload", "F_thresh", false, "float", "Woofer to Telescope Focus offload threshold");
465 
466  config.add("offload.CFocus00", "", "offload.CFocus00", argType::Required, "offload", "CFocus00", false, "float", "Woofer to Telescope Focus offload control scale factor.");
467 
468  config.add("offload.CComa00", "", "offload.CComa00", argType::Required, "offload", "CComa00", false, "float", "Woofer to Telescope Coma offload control matrix [0,0] of a 2x2 matrix");
469  config.add("offload.CComa01", "", "offload.CComa01", argType::Required, "offload", "CComa01", false, "float", "Woofer to Telescope Coma offload control matrix [0,1] of a 2x2 matrix ");
470  config.add("offload.CComa10", "", "offload.CComa10", argType::Required, "offload", "CComa10", false, "float", "Woofer to Telescope Coma offload control matrix [1,0] of a 2x2 matrix ");
471  config.add("offload.CComa11", "", "offload.CComa11", argType::Required, "offload", "CComa11", false, "float", "Woofer to Telescope Coma offload control matrix [1,1] of a 2x2 matrix ");
472 
473  config.add("device.address", "", "device.address", argType::Required, "device", "address", false, "string", "The IP address or resolvable name of the TCS.");
474  config.add("device.port", "", "device.port", argType::Required, "device", "port", false, "int", "The IP port for TCS communications. Should be the command port. Default is 5811.");
475 
478 }
479 
480 inline
481 int tcsInterface::loadConfigImpl( mx::app::appConfigurator & _config )
482 {
483 
484  _config(m_labMode, "labMode");
485  std::cerr << "\n\n labMode= " << m_labMode << "\n\n";
486 
487  _config(m_pyrNudge_C_00, "pyrNudger.C_00");
488  _config(m_pyrNudge_C_01, "pyrNudger.C_01");
489  _config(m_pyrNudge_C_10, "pyrNudger.C_10");
490  _config(m_pyrNudge_C_11, "pyrNudger.C_11");
491 
492  _config(m_pyrNudge_ang, "pyrNudger.ang");
493  _config(m_pyrNudge_ang0, "pyrNudger.ang0");
494  _config(m_pyrNudge_parity, "pyrNudger.parity");
495 
496  _config(m_acqZdSign, "acqFromGuider.zdSign");
497  _config(m_acqAz0, "acqFromGuider.az0");
498  _config(m_acqAzOff, "acqFromGuider.azoff");
499  _config(m_acqEl0, "acqFromGuider.el0");
500  _config(m_acqElOff, "acqFromGuider.eloff");
501  _config(m_acqFocus, "acqFromGuider.focus");
502 
503  _config(m_offlTT_avgInt, "offload.TT_avgInt");
504  _config(m_offlTT_gain, "offload.TT_gain");
505  _config(m_offlTT_thresh, "offload.TT_thresh");
506 
507  _config(m_lab_offlTT_C_00, "offload.lab_TT_C_00");
508  _config(m_lab_offlTT_C_01, "offload.lab_TT_C_01");
509  _config(m_lab_offlTT_C_10, "offload.lab_TT_C_10");
510  _config(m_lab_offlTT_C_11, "offload.lab_TT_C_11");
511 
512  _config(m_offlTT_C_00, "offload.TT_C_00");
513  _config(m_offlTT_C_01, "offload.TT_C_01");
514  _config(m_offlTT_C_10, "offload.TT_C_10");
515  _config(m_offlTT_C_11, "offload.TT_C_11");
516 
517  _config(m_offlF_avgInt, "offload.F_avgInt");
518  _config(m_offlF_gain, "offload.F_gain");
519  _config(m_offlF_thresh, "offload.F_thresh");
520 
521  _config(m_offlCFocus_00, "offload.CFocus00");
522 
523  _config(m_offlCComa_00, "offload.CComa00");
524  _config(m_offlCComa_01, "offload.CComa01");
525  _config(m_offlCComa_10, "offload.CComa10");
526  _config(m_offlCComa_11, "offload.CComa11");
527 
528  _config(m_deviceAddr, "device.address");
529  _config(m_devicePort, "device.port");
530 
531  dev::ioDevice::loadConfig(_config);
532 
534 
535  return 0;
536 }
537 
538 inline
540 {
541  loadConfigImpl(config);
542 }
543 
544 inline
546 {
547  createROIndiNumber( m_indiP_teltime, "teltime", "Telscope Time", "TCS");
548  indi::addNumberElement<double>( m_indiP_teltime, "sidereal_time", 0, std::numeric_limits<double>::max(), 0, "%0.6f");
549  m_indiP_teltime["sidereal_time"] = m_telST;
551 
552  createROIndiNumber( m_indiP_telpos, "telpos", "Telscope Position", "TCS");
553  indi::addNumberElement<double>( m_indiP_telpos, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f");
554  m_indiP_telpos["epoch"] = m_telEpoch;
555  indi::addNumberElement<double>( m_indiP_telpos, "ra", 0, 360, 0, "%0.6f");
556  m_indiP_telpos["ra"] = m_telRA;
557  indi::addNumberElement<double>( m_indiP_telpos, "dec", -90, 90, 0, "%0.6f");
558  m_indiP_telpos["dec"] = m_telDec;
559  indi::addNumberElement<double>( m_indiP_telpos, "el", 0, 90, 0, "%0.6f");
560  m_indiP_telpos["el"] = m_telEl;
561  indi::addNumberElement<double>( m_indiP_telpos, "ha", -180, 160, 0, "%0.6f");
562  m_indiP_telpos["ha"] = m_telHA;
563  indi::addNumberElement<double>( m_indiP_telpos, "am", 0, 4, 0, "%0.2f");
564  m_indiP_telpos["am"] = m_telAM;
565  indi::addNumberElement<double>( m_indiP_telpos, "rotoff", 0, 360, 0, "%0.6f");
566  m_indiP_telpos["rotoff"] = m_telRotOff;
567 
569 
570 
571  createROIndiNumber( m_indiP_teldata, "teldata", "Telscope Data", "TCS");
572  indi::addNumberElement<int>( m_indiP_teldata, "roi", 0, 10, 1, "%d");
573  m_indiP_teldata["roi"] = m_telROI;
574  indi::addNumberElement<int>( m_indiP_teldata, "tracking", 0, 1, 1, "%d");
575  m_indiP_teldata["tracking"] = m_telTracking;
576  indi::addNumberElement<int>( m_indiP_teldata, "guiding", 0, 1, 1, "%d");
577  m_indiP_teldata["guiding"] = m_telGuiding;
578  indi::addNumberElement<int>( m_indiP_teldata, "slewing", 0, 1, 1, "%d");
579  m_indiP_teldata["slewing"] = m_telSlewing;
580  indi::addNumberElement<int>( m_indiP_teldata, "guider_moving", 0, 1, 1, "%d");
581  m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
582  indi::addNumberElement<double>( m_indiP_teldata, "az", 0, 360, 0, "%0.6f");
583  m_indiP_teldata["az"] = m_telAz;
584  indi::addNumberElement<double>( m_indiP_teldata, "zd", 0, 90, 0, "%0.6f");
585  m_indiP_teldata["zd"] = m_telZd;
586  indi::addNumberElement<double>( m_indiP_teldata, "pa", 0, 360, 0, "%0.6f");
587  m_indiP_teldata["pa"] = m_telPA;
588  indi::addNumberElement<double>( m_indiP_teldata, "dome_az", 0, 360, 0, "%0.6f");
589  m_indiP_teldata["dome_az"] = m_telDomeAz;
590  indi::addNumberElement<int>( m_indiP_teldata, "dome_stat", 0, 1, 1, "%d");
591  m_indiP_teldata["dome_stat"] = m_telDomeStat;
592 
594 
595 
596  createROIndiText( m_indiP_catalog, "catalog", "object", "Catalog Entry", "TCS", "Object Name");
597  m_indiP_catalog.add(pcf::IndiElement("rotmode"));
598  m_indiP_catalog["rotmode"].setLabel("Rotator Mode");
599 
601 
602  createROIndiNumber( m_indiP_catdata, "catdata", "Catalog Entry Data", "TCS");
603  indi::addNumberElement<double>( m_indiP_catdata, "ra", 0, 360, 0, "%0.6f");
604  m_indiP_catdata["ra"] = m_catRA;
605  indi::addNumberElement<double>( m_indiP_catdata, "dec", -90, 90, 0, "%0.6f");
606  m_indiP_catdata["dec"] = m_catDec;
607  indi::addNumberElement<double>( m_indiP_catdata, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f");
608  m_indiP_catdata["epoch"] = m_catEp;
609  indi::addNumberElement<double>( m_indiP_catdata, "rotoff", 0, 360, 0, "%0.6f");
610  m_indiP_catdata["rotoff"] = m_catRo;
611 
613 
614 
615 
616  createROIndiNumber( m_indiP_vaneend, "vaneend", "Vane End Data", "TCS");
617  indi::addNumberElement<double>( m_indiP_vaneend, "secz", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
618  m_indiP_vaneend["secz"] = m_telSecZ;
619  indi::addNumberElement<double>( m_indiP_vaneend, "encz", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
620  m_indiP_vaneend["encz"] = m_telEncZ;
621  indi::addNumberElement<double>( m_indiP_vaneend, "secx", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
622  m_indiP_vaneend["secx"] = m_telSecX;
623  indi::addNumberElement<double>( m_indiP_vaneend, "encx", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
624  m_indiP_vaneend["encx"] = m_telEncX;
625  indi::addNumberElement<double>( m_indiP_vaneend, "secy", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
626  m_indiP_vaneend["secy"] = m_telSecY;
627  indi::addNumberElement<double>( m_indiP_vaneend, "ency", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
628  m_indiP_vaneend["ency"] = m_telEncY;
629  indi::addNumberElement<double>( m_indiP_vaneend, "sech", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
630  m_indiP_vaneend["sech"] = m_telSecH;
631  indi::addNumberElement<double>( m_indiP_vaneend, "ench", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
632  m_indiP_vaneend["ench"] = m_telEncH;
633  indi::addNumberElement<double>( m_indiP_vaneend, "secv", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
634  m_indiP_vaneend["secv"] = m_telSecV;
635  indi::addNumberElement<double>( m_indiP_vaneend, "encv", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.6f");
636  m_indiP_vaneend["encv"] = m_telEncV;
637 
639 
640 
641  createROIndiNumber( m_indiP_env, "environment", "Environment Data", "TCS");
642  indi::addNumberElement<double>( m_indiP_env, "temp-out", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
643  m_indiP_env["temp-out"] = m_wxtemp;
644  indi::addNumberElement<double>( m_indiP_env, "pressure", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
645  m_indiP_env["pressure"] = m_wxpres;
646  indi::addNumberElement<double>( m_indiP_env, "humidity", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
647  m_indiP_env["humidity"] = m_wxhumid;
648  indi::addNumberElement<double>( m_indiP_env, "wind", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
649  m_indiP_env["wind"] = m_wxwind;
650  indi::addNumberElement<double>( m_indiP_env, "winddir", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
651  m_indiP_env["winddir"] = m_wxwdir;
652  indi::addNumberElement<double>( m_indiP_env, "temp-truss", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
653  m_indiP_env["temp-truss"] = m_ttruss;
654  indi::addNumberElement<double>( m_indiP_env, "temp-cell", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
655  m_indiP_env["temp-cell"] = m_tcell;
656  indi::addNumberElement<double>( m_indiP_env, "temp-seccell", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
657  m_indiP_env["temp-seccell"] = m_tseccell;
658  indi::addNumberElement<double>( m_indiP_env, "temp-amb", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
659  m_indiP_env["temp-amb"] = m_tambient;
660  indi::addNumberElement<double>( m_indiP_env, "dewpoint", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
661  m_indiP_env["dewpoint"] = m_wxdewpoint;
662 
664 
665  createROIndiNumber( m_indiP_seeing, "seeing", "Seeing Data", "TCS");
666  indi::addNumberElement<unsigned>( m_indiP_seeing, "dimm_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0, "%d");
667  m_indiP_seeing["dimm_time"] = m_dimm_time;
668  indi::addNumberElement<double>( m_indiP_seeing, "dimm_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
669  m_indiP_seeing["dimm_el"] = m_dimm_el;
670  indi::addNumberElement<double>( m_indiP_seeing, "dimm_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
671  m_indiP_seeing["dimm_fwhm"] = m_dimm_fwhm;
672  indi::addNumberElement<double>( m_indiP_seeing, "dimm_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
673  m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
674  indi::addNumberElement<unsigned>( m_indiP_seeing, "mag1_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0, "%d");
675  m_indiP_seeing["mag1_time"] = m_mag1_time;
676  indi::addNumberElement<double>( m_indiP_seeing, "mag1_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
677  m_indiP_seeing["mag1_el"] = m_mag1_el;
678  indi::addNumberElement<double>( m_indiP_seeing, "mag1_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
679  m_indiP_seeing["mag1_fwhm"] = m_mag1_fwhm;
680  indi::addNumberElement<double>( m_indiP_seeing, "mag1_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
681  m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
682  indi::addNumberElement<unsigned>( m_indiP_seeing, "mag2_time", std::numeric_limits<unsigned>::lowest(), std::numeric_limits<unsigned>::max(), 0, "%d");
683  m_indiP_seeing["mag2_time"] = m_mag2_time;
684  indi::addNumberElement<double>( m_indiP_seeing, "mag2_el", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
685  m_indiP_seeing["mag2_el"] = m_mag2_el;
686  indi::addNumberElement<double>( m_indiP_seeing, "mag2_fwhm", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
687  m_indiP_seeing["mag2_fwhm"] = m_mag2_fwhm;
688  indi::addNumberElement<double>( m_indiP_seeing, "mag2_fwhm_corr", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f");
689  m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
690 
692 
693  signal(SIGPIPE, SIG_IGN);
694 
695 
696  if(dev::ioDevice::appStartup() < 0)
697  {
698  return log<software_error,-1>({__FILE__,__LINE__});
699  }
700 
702  {
703  return log<software_error,-1>({__FILE__,__LINE__});
704  }
705 
706  REG_INDI_NEWPROP(m_indiP_pyrNudge, "pyrNudge", pcf::IndiProperty::Number);
707  m_indiP_pyrNudge.add(pcf::IndiElement("y"));
708  m_indiP_pyrNudge.add(pcf::IndiElement("x"));
709  m_indiP_pyrNudge.add(pcf::IndiElement("z"));
710 
712  if( registerIndiPropertyNew( m_indiP_acqFromGuider, st_newCallBack_m_indiP_acqFromGuider) < 0)
713  {
714  log<software_error>({__FILE__,__LINE__});
715  return -1;
716  }
717 
719  if( registerIndiPropertyNew( m_indiP_offlTTdump, st_newCallBack_m_indiP_offlTTdump) < 0)
720  {
721  log<software_error>({__FILE__,__LINE__});
722  return -1;
723  }
724 
726  if( registerIndiPropertyNew( m_indiP_offlTTenable, st_newCallBack_m_indiP_offlTTenable) < 0)
727  {
728  log<software_error>({__FILE__,__LINE__});
729  return -1;
730  }
731 
732  createStandardIndiNumber( m_indiP_offlTTavgInt, "offlTT_avgInt", 0, 3600, 1, "%d");
733  m_indiP_offlTTavgInt["current"].set(m_offlTT_avgInt);
734  m_indiP_offlTTavgInt["target"].set(m_offlTT_avgInt);
735  if( registerIndiPropertyNew( m_indiP_offlTTavgInt, st_newCallBack_m_indiP_offlTTavgInt) < 0)
736  {
737  log<software_error>({__FILE__,__LINE__});
738  return -1;
739  }
740 
741  createStandardIndiNumber( m_indiP_offlTTgain, "offlTT_gain", 0.0, 1.0, 0.0, "%0.2f");
742  m_indiP_offlTTgain["current"].set(m_offlTT_gain);
743  m_indiP_offlTTgain["target"].set(m_offlTT_gain);
744  if( registerIndiPropertyNew( m_indiP_offlTTgain, st_newCallBack_m_indiP_offlTTgain) < 0)
745  {
746  log<software_error>({__FILE__,__LINE__});
747  return -1;
748  }
749 
750  createStandardIndiNumber( m_indiP_offlTTthresh, "offlTT_thresh", 0.0, 1.0, 0.0, "%0.2f");
751  m_indiP_offlTTthresh["current"].set(m_offlTT_thresh);
752  m_indiP_offlTTthresh["target"].set(m_offlTT_thresh);
753  if( registerIndiPropertyNew( m_indiP_offlTTthresh, st_newCallBack_m_indiP_offlTTthresh) < 0)
754  {
755  log<software_error>({__FILE__,__LINE__});
756  return -1;
757  }
758 
760  if( registerIndiPropertyNew( m_indiP_offlFdump, st_newCallBack_m_indiP_offlFdump) < 0)
761  {
762  log<software_error>({__FILE__,__LINE__});
763  return -1;
764  }
765 
767  if( registerIndiPropertyNew( m_indiP_offlFenable, st_newCallBack_m_indiP_offlFenable) < 0)
768  {
769  log<software_error>({__FILE__,__LINE__});
770  return -1;
771  }
772 
773  createStandardIndiNumber( m_indiP_offlFavgInt, "offlF_avgInt", 0, 3600, 1, "%d");
774  m_indiP_offlFavgInt["current"].set(m_offlF_avgInt);
775  m_indiP_offlFavgInt["target"].set(m_offlF_avgInt);
776  if( registerIndiPropertyNew( m_indiP_offlFavgInt, st_newCallBack_m_indiP_offlFavgInt) < 0)
777  {
778  log<software_error>({__FILE__,__LINE__});
779  return -1;
780  }
781 
782  createStandardIndiNumber( m_indiP_offlFgain, "offlF_gain", 0.0, 1.0, 0.0, "%0.2f");
783  m_indiP_offlFgain["current"].set(m_offlF_gain);
784  m_indiP_offlFgain["target"].set(m_offlF_gain);
785  if( registerIndiPropertyNew( m_indiP_offlFgain, st_newCallBack_m_indiP_offlFgain) < 0)
786  {
787  log<software_error>({__FILE__,__LINE__});
788  return -1;
789  }
790 
791  createStandardIndiNumber( m_indiP_offlFthresh, "offlF_thresh", 0.0, 1.0, 0.0, "%0.2f");
792  m_indiP_offlFthresh["current"].set(m_offlF_thresh);
793  m_indiP_offlFthresh["target"].set(m_offlF_thresh);
794  if( registerIndiPropertyNew( m_indiP_offlFthresh, st_newCallBack_m_indiP_offlFthresh) < 0)
795  {
796  log<software_error>({__FILE__,__LINE__});
797  return -1;
798  }
799 
800  //Get the loop state for managing offloading
801  REG_INDI_SETPROP(m_indiP_loopState, "holoop", "loop_state");
802 
803 
804  m_offloadRequests.resize(5);
805  for(size_t n=0; n < m_offloadRequests.size();++n) m_offloadRequests[n].resize(10,0);
806 
807 
809  {
810  log<software_error>({__FILE__, __LINE__});
811  return -1;
812  }
813 
814 
815  //Register to receive the coeff updates from Kyle
816  REG_INDI_SETPROP(m_indiP_offloadCoeffs, "w2tcsOffloader", "zCoeffs");
817 
818 
820 
821 
822  return 0;
823 }
824 
825 inline
827 {
828  if(state() == stateCodes::ERROR)
829  {
830  int rv = m_sock.serialInit(m_deviceAddr.c_str(), m_devicePort);
831 
832  if(rv != 0)
833  {
835  log<text_log>("In state ERROR, connection lost, will retry.", logPrio::LOG_ERROR);
836  return 0;
837  }
838 
839  log<text_log>("In state ERROR, but connected. Trying to continue.", logPrio::LOG_WARNING);
840 
842  }
843 
845  {
846  static int lastrv = 0; //Used to handle a change in error within the same state. Make general?
847  static int lasterrno = 0;
848 
849 
850  int rv = m_sock.serialInit(m_deviceAddr.c_str(), m_devicePort);
851 
852  if(rv == 0)
853  {
855 
856  if(!stateLogged())
857  {
858  std::stringstream logs;
859  logs << "Connected to " << m_deviceAddr << ":" << m_devicePort;
860  log<text_log>(logs.str());
861  }
862  lastrv = rv;
863  lasterrno = errno;
864  }
865  else
866  {
867  if(!stateLogged())
868  {
869  log<text_log>({"Failed to connect to " + m_deviceAddr + ":" + std::to_string(m_devicePort)}, logPrio::LOG_ERROR);
870  }
871  if( rv != lastrv )
872  {
873  log<software_error>( {__FILE__,__LINE__, 0, rv, tty::ttyErrorString(rv)} );
874  lastrv = rv;
875  }
876  if( errno != lasterrno )
877  {
878  log<software_error>( {__FILE__,__LINE__, errno});
879  lasterrno = errno;
880  }
881  return 0;
882  }
883  }
884 
886  {
887  std::string response;
888 
889  //If any of these are unsuccesful we go around without recording data.
890  if(getTelTime() < 0)
891  {
892  return 0; //app state will be set based on what the error was
893  }
894 
895  if(getTelPos() < 0)
896  {
897  return 0; //app state will be set based on what the error was
898  }
899 
900  if(getTelData() < 0)
901  {
902  return 0;
903  }
904 
905  if(getCatData() < 0)
906  {
907  return 0;
908  }
909 
910  if(getVaneData() < 0)
911  {
912  return 0;
913  }
914 
915  if(getEnvData() < 0)
916  {
917  return 0;
918  }
919 
920 
921  if(getSeeing() < 0)
922  {
923  return 0;
924  }
925 
927 
928  if(updateINDI() < 0)
929  {
930  log<text_log>("Error from updateINDI", logPrio::LOG_ERROR);
931  return 0;
932  }
933  }
934 
935 
936 
937  return 0;
938 }
939 
940 inline
942 {
943  //Wait for offload thread to exit on m_shutdown.
944  if(m_offloadThread.joinable())
945  {
946  try
947  {
948  m_offloadThread.join(); //this will throw if it was already joined
949  }
950  catch(...)
951  {
952  }
953  }
954 
955  return 0;
956 }
957 
958 inline
959 int tcsInterface::getMagTelStatus( std::string & response,
960  const std::string &statreq
961  )
962 {
963 
964  int stat;
965  char answer[512];
966  std::string statreq_nl;
967 
968  #ifdef LOG_TCS_STATUS
969  log<text_log>("Sending status request: " + statreq);
970  #endif
971 
972  std::lock_guard<std::mutex> guard(m_tcsMutex);
973 
974  statreq_nl = statreq;
975  statreq_nl += '\n';
976  stat = m_sock.serialOut(statreq_nl.c_str(), statreq_nl.length());
977 
978  if(stat != NETSERIAL_E_NOERROR)
979  {
980  log<text_log>("Error sending status request: " + statreq, logPrio::LOG_ERROR);
981  response = "";
982  return 0;
983  }
984 
985  stat = m_sock.serialInString(answer, 512, m_readTimeout, '\n');
986 
987  if(stat <= 0)
988  {
989  log<text_log>("No response received to status request: " + statreq, logPrio::LOG_ERROR);
990  response = "";
991  return 0;
992  }
993 
994  char * nl = strchr(answer, '\n');
995  if(nl) answer[nl-answer] = '\0';
996 
997  #ifdef LOG_TCS_STATUS
998  log<text_log>(std::string("Received response: ") + answer);
999  #endif
1000 
1001  response = answer;
1002 
1003  return 0;
1004 }//int tcsInterface::getMagTelStatus
1005 
1006 inline
1007 int tcsInterface::sendMagTelCommand( const std::string &command,
1008  int timeout
1009  )
1010 {
1011  int stat;
1012  char answer[512];
1013  std::string command_nl;
1014 
1015  #ifdef LOG_TCS_STATUS
1016  log<text_log>("Sending command: " + command);
1017  #endif
1018 
1019  std::lock_guard<std::mutex> guard(m_tcsMutex);
1020 
1021  command_nl = command;
1022  command_nl += '\n';
1023  stat = m_sock.serialOut(command_nl.c_str(), command_nl.length());
1024 
1025  if(stat != NETSERIAL_E_NOERROR)
1026  {
1027  log<text_log>("Error sending command: " + command, logPrio::LOG_ERROR);
1028  return -1000;
1029  }
1030 
1031  stat = m_sock.serialInString(answer, sizeof(answer), timeout, '\n');
1032 
1033  if(stat <= 0)
1034  {
1035  log<text_log>("No response received to command: " + command, logPrio::LOG_ERROR);
1036  return -1000;
1037  }
1038 
1039  char * nl = strchr(answer, '\n');
1040  if(nl) answer[nl-answer] = '\0';
1041 
1042  #ifdef LOG_TCS_STATUS
1043  log<text_log>(std::string("Received response: ") + answer);
1044  #endif
1045 
1046  return atoi(answer);
1047 
1048 }//int tcsInterface::sendMagTelCommand
1049 
1050 inline
1051 std::vector<std::string> tcsInterface::parse_teldata( std::string &tdat )
1052 {
1053  std::vector<std::string> vres;
1054 
1055  std::string tok;
1056 
1057  int pos1, pos2;
1058 
1059 
1060  //skip all leading spaces
1061  pos1 = tdat.find_first_not_of(" " , 0);
1062 
1063  if(pos1 == -1) pos1 = 0;
1064 
1065  pos2 = tdat.find_first_of(" ", pos1);
1066 
1067  while(pos2 > 0)
1068  {
1069  tok = tdat.substr(pos1, pos2-pos1);
1070 
1071  vres.push_back(tok);
1072 
1073  //now move past end of current spaces - might be more than one.
1074  pos1 = pos2;
1075 
1076  pos2 = tdat.find_first_not_of(" ", pos1);
1077 
1078  //and then find the end of this value.
1079  pos1 = pos2;
1080 
1081  pos2 = tdat.find_first_of(" ", pos1);
1082  }
1083 
1084  //If there is another value, we pick it up here.
1085  if(pos1 >= 0)
1086  {
1087  pos2 = tdat.length();
1088 
1089  tok = tdat.substr(pos1, pos2-pos1);
1090 
1091  pos2 = tok.find_first_of(" \n\r", 0);
1092 
1093  if(pos2 >= 0) tok.erase(pos2, tok.length()-pos2);
1094 
1095  vres.push_back(tok);
1096  }
1097 
1098  return vres;
1099 }
1100 
1101 inline
1102 int tcsInterface::parse_xms( double &x,
1103  double &m,
1104  double &s,
1105  const std::string & xmsstr
1106  )
1107 {
1108  size_t st, en;
1109 
1110  int sgn = 1;
1111 
1112  st = 0;
1113  en = xmsstr.find(':', st);
1114 
1115  //Check not found
1116  if(en == std::string::npos)
1117  {
1118  log<software_error>({__FILE__, __LINE__, "error parsing x:m:s"});
1119  return -1;
1120  }
1121 
1122  //Check 0 length or invalid
1123  if(en - st < 2 || en < st)
1124  {
1125  log<software_error>({__FILE__, __LINE__, "error parsing x:m:s"});
1126  return -1;
1127  }
1128 
1129  std::string xstr;
1130  try
1131  {
1132  xstr = xmsstr.substr(st, en-st);
1133  }
1134  catch(const std::exception & e)
1135  {
1136  log<software_error>({__FILE__, __LINE__, e.what()});
1137  return -1;
1138  }
1139 
1140  try
1141  {
1142  x = std::stod(xstr);
1143  }
1144  catch(const std::exception & e)
1145  {
1146  log<software_error>({__FILE__, __LINE__, e.what()});
1147  return -1;
1148  }
1149 
1150  //Check for negative
1151  if(std::signbit(x)) sgn = -1;
1152  if(xmsstr[0] == '-') sgn = -1;
1153 
1154  st = en + 1;
1155 
1156  en = xmsstr.find(':', st);
1157 
1158  //Check not found
1159  if(en == std::string::npos)
1160  {
1161  log<software_error>({__FILE__, __LINE__, "error parsing x:m:s"});
1162  return -1;
1163  }
1164 
1165  //Check 0 length or invalid
1166  if(en - st < 2 || en < st)
1167  {
1168  log<software_error>({__FILE__, __LINE__, "error parsing x:m:s"});
1169  return -1;
1170  }
1171 
1172  std::string mstr;
1173  try
1174  {
1175  mstr = xmsstr.substr(st, en-st);
1176  }
1177  catch(const std::exception & e)
1178  {
1179  log<software_error>({__FILE__, __LINE__, e.what()});
1180  return -1;
1181  }
1182 
1183  try
1184  {
1185  m = sgn*std::stod(mstr);
1186  }
1187  catch(const std::exception & e)
1188  {
1189  log<software_error>({__FILE__, __LINE__, e.what()});
1190  return -1;
1191  }
1192 
1193  st = en+1;
1194 
1195  if(st >= xmsstr.length() - 1)
1196  {
1197  log<software_error>({__FILE__, __LINE__, "error parsing x:m:s"});
1198  return -1;
1199  }
1200 
1201  std::string sstr;
1202  try
1203  {
1204  sstr = xmsstr.substr(st, xmsstr.length()-st);
1205  }
1206  catch(const std::exception & e)
1207  {
1208  log<software_error>({__FILE__, __LINE__, e.what()});
1209  return -1;
1210  }
1211 
1212  try
1213  {
1214  s = sgn*std::stod(sstr);
1215  }
1216  catch(const std::exception & e)
1217  {
1218  log<software_error>({__FILE__, __LINE__, e.what()});
1219  return -1;
1220  }
1221 
1222  return 0;
1223 }
1224 
1225 inline
1227 {
1228  double h,m,s;
1229 
1230  std::vector<std::string> pdat;
1231  std::string posstr;
1232 
1233  if(getMagTelStatus( posstr, "datetime") < 0)
1234  {
1236  log<text_log>("Error getting telescope position (telpos)", logPrio::LOG_ERROR);
1237  return -1;
1238  }
1239 
1240  pdat = parse_teldata(posstr);
1241 
1242  if(pdat[0] == "-1")
1243  {
1245  log<text_log>("Error getting telescope time (datetime): TCS returned -1", logPrio::LOG_WARNING);
1246  return -1;
1247  }
1248 
1249  if(pdat.size() != 3)
1250  {
1252  log<text_log>("Error getting telescope position (datetime): TCS response wrong size, returned " + std::to_string(pdat.size()) + " values", logPrio::LOG_WARNING);
1253  return -1;
1254  }
1255 
1256  if(parse_xms(h,m,s,pdat[2]) != 0)
1257  {
1258  log<text_log>("Error parsing telescope ST", logPrio::LOG_WARNING);
1259  return -1;
1260  }
1261 
1262  m_telST = (h + m/60. + s/3600.);
1263 
1264  return 0;
1265 }//int tcsInterface::getTelTime()
1266 
1267 inline
1269 {
1270  double h,m,s;
1271 
1272  std::vector<std::string> pdat;
1273  std::string posstr;
1274 
1275  if(getMagTelStatus( posstr, "telpos") < 0)
1276  {
1278  log<text_log>("Error getting telescope position (telpos)", logPrio::LOG_ERROR);
1279  return -1;
1280  }
1281 
1282  pdat = parse_teldata(posstr);
1283 
1284  if(pdat[0] == "-1")
1285  {
1287  log<text_log>("Error getting telescope position (telpos): TCS returned -1", logPrio::LOG_WARNING);
1288  return -1;
1289  }
1290 
1291  if(pdat.size() != 6)
1292  {
1294  log<text_log>("Error getting telescope position (telpos): TCS response wrong size, returned " + std::to_string(pdat.size()) + " values", logPrio::LOG_WARNING);
1295  return -1;
1296  }
1297 
1298  if(parse_xms(h,m,s,pdat[0]) != 0)
1299  {
1300  log<text_log>("Error parsing telescope RA", logPrio::LOG_WARNING);
1301  return -1;
1302  }
1303 
1304  m_telRA = (h + m/60. + s/3600.)*15.;
1305 
1306  if(parse_xms(h,m,s,pdat[1]) != 0)
1307  {
1308  log<text_log>("Error parsing telescope Dec", logPrio::LOG_WARNING);
1309  return -1;
1310  }
1311 
1312  m_telDec = h + m/60. + s/3600.;
1313 
1314  //m_telEl = strtod(pdat[1].c_str(),0);// * 3600.;
1315 
1316  m_telEpoch = strtod(pdat[2].c_str(),0);
1317 
1318  if(parse_xms( h, m, s, pdat[3]) != 0)
1319  {
1320  log<text_log>("Error parsing telescope HA", logPrio::LOG_WARNING);
1321  return -1;
1322  }
1323 
1324  /************ BUG: this won't handle -0!
1325  */
1326  m_telHA = h + m/60. + s/3600.;
1327 
1328  m_telAM = strtod(pdat[4].c_str(),0);
1329 
1330  m_telRotOff = strtod(pdat[5].c_str(),0);
1331 
1332  if( recordTelPos() < 0)
1333  {
1334  return log<software_error,-1>({__FILE__,__LINE__});
1335  }
1336 
1337  return 0;
1338 }//int tcsInterface::getTelPos()
1339 
1340 inline
1342 {
1343  std::string xstr;
1344  std::vector<std::string> tdat;
1345 
1346  if(getMagTelStatus(xstr, "teldata") < 0)
1347  {
1349  log<text_log>("Error getting telescope data (teldata)", logPrio::LOG_ERROR);
1350  return -1;
1351  }
1352 
1353  tdat = parse_teldata(xstr);
1354 
1355  if(tdat[0] == "-1")
1356  {
1358  log<text_log>("Error getting telescope data (teldata): TCS returned -1", logPrio::LOG_WARNING);
1359  return -1;
1360  }
1361 
1362  if(tdat.size() != 10)
1363  {
1365  log<text_log>("[TCS] Error getting telescope data (teldata): TCS response wrong size, returned " + std::to_string(tdat.size()) + " values", logPrio::LOG_WARNING);
1366  return -1;
1367  }
1368 
1369 
1370  m_telROI = atoi(tdat[0].c_str());
1371 
1372  //Parse the telguide string
1373  char bit[2] = {0,0};
1374  bit[1] = 0;
1375  bit[0] = tdat[1].c_str()[0];
1376  m_telTracking= atoi(bit);
1377  bit[0] = tdat[1].c_str()[1];
1378  m_telGuiding= atoi(bit);
1379 
1380  //parse the gdrmountmv string
1381  bit[0] = tdat[2].c_str()[0];
1382  m_telSlewing= atoi(bit);
1383  bit[0] = tdat[2].c_str()[1];
1384  m_telGuiderMoving = atoi(bit);
1385 
1386  //number 3 is mountmv
1387 
1388  m_telAz = strtod(tdat[4].c_str(),0);
1389 
1390  m_telEl = strtod(tdat[5].c_str(),0);
1391 
1392  m_telZd = strtod(tdat[6].c_str(),0);// * 3600.;
1393 
1394  m_telPA = strtod(tdat[7].c_str(),0);
1395 
1396  m_telDomeAz = strtod(tdat[8].c_str(),0);
1397 
1398  m_telDomeStat = atoi(tdat[9].c_str());
1399 
1400  if( recordTelData() < 0)
1401  {
1402  return log<software_error,-1>({__FILE__,__LINE__});
1403  }
1404 
1405  return 0;
1406 }//int tcsInterface::getTelData()
1407 
1408 inline
1410 {
1411  double h, m,s;
1412 
1413  std::vector<std::string> cdat;
1414  std::string cstr;
1415 
1416  if( getMagTelStatus(cstr, "catdata") < 0)
1417  {
1419  log<text_log>("Error getting catalog data (catdata)", logPrio::LOG_ERROR);
1420  return -1;
1421  }
1422 
1423  cdat = parse_teldata(cstr);
1424 
1425  if(cdat[0] == "-1")
1426  {
1428  log<text_log>("Error getting catalog data (catdata): TCS returned -1", logPrio::LOG_WARNING);
1429  return -1;
1430  }
1431 
1432  if(cdat.size() != 6)
1433  {
1434  //This can occur if no target selected by operator
1435  log<text_log>("Catalog data (catdata): TCS response wrong size, returned " + std::to_string(cdat.size()) + " values", logPrio::LOG_WARNING);
1436  m_catRA = 0;
1437 
1438  m_catDec = 0;
1439 
1440  m_catEp = 0;
1441 
1442  m_catRo = 0;
1443 
1444  m_catRm = "";
1445 
1446  m_catObj = "none";
1447 
1448  return 1;
1449  }
1450 
1451  if(parse_xms(h,m,s,cdat[0]) != 0)
1452  {
1453  log<text_log>("Error parsing catalog RA", logPrio::LOG_WARNING);
1454  return -1;
1455  }
1456 
1457  m_catRA = (h + m/60. + s/3600.)*15.;
1458 
1459  if(parse_xms(h,m,s, cdat[1] ) != 0)
1460  {
1461  log<text_log>("Error parsing catalog Dec", logPrio::LOG_WARNING);
1462  return -1;
1463  }
1464 
1465  m_catDec = h + m/60. + s/3600.;
1466 
1467  m_catEp = strtod(cdat[2].c_str(),0);
1468 
1469  m_catRo = strtod(cdat[3].c_str(),0);
1470 
1471  m_catRm = cdat[4];
1472 
1473  m_catObj = cdat[5];
1474 
1475  return 0;
1476 }//int tcsInterface::getCatData()
1477 
1478 inline
1480 {
1481  std::string xstr;
1482  std::vector<std::string> vedat;
1483 
1484  if(getMagTelStatus(xstr,"vedata") < 0)
1485  {
1487  log<text_log>("Error getting telescope secondary positions (vedata)",logPrio::LOG_ERROR);
1488  return -1;
1489  }
1490 
1491  vedat = parse_teldata(xstr);
1492 
1493  if(vedat[0] == "-1")
1494  {
1496  log<text_log>("Error getting telescope secondary positions (vedata): TCS returned -1",logPrio::LOG_WARNING);
1497  return -1;
1498  }
1499 
1500  if(vedat.size() != 10)
1501  {
1503  log<text_log>("Error getting telescope secondary positions (vedata): TCS response wrong size, returned " + std::to_string(vedat.size()) + " values",logPrio::LOG_WARNING);
1504  return -1;
1505  }
1506 
1507 
1508  m_telSecZ = strtod(vedat[0].c_str(),0);
1509  m_telEncZ = strtod(vedat[1].c_str(),0);
1510  m_telSecX = strtod(vedat[2].c_str(),0);
1511  m_telEncX = strtod(vedat[3].c_str(),0);
1512  m_telSecY = strtod(vedat[4].c_str(),0);
1513  m_telEncY = strtod(vedat[5].c_str(),0);
1514  m_telSecH = strtod(vedat[6].c_str(),0);
1515  m_telEncH = strtod(vedat[7].c_str(),0);
1516  m_telSecV = strtod(vedat[8].c_str(),0);
1517  m_telEncV = strtod(vedat[9].c_str(),0);
1518 
1519  if( recordTelVane() < 0)
1520  {
1521  return log<software_error,-1>({__FILE__,__LINE__});
1522  }
1523 
1524  return 0;
1525 }//int tcsInterface::getVaneData()
1526 
1527 inline
1529 {
1530  std::string estr;
1531  std::vector<std::string> edat;
1532 
1533  if(getMagTelStatus(estr,"telenv") < 0)
1534  {
1536  log<text_log>("Error getting telescope environment data (telenv)",logPrio::LOG_ERROR);
1537  return -1;
1538  }
1539 
1540  edat = parse_teldata(estr);
1541 
1542  if(edat[0] == "-1")
1543  {
1545  log<text_log>("Error getting telescope environment data (telenv): TCS returned -1",logPrio::LOG_WARNING);
1546  return -1;
1547  }
1548 
1549  if(edat.size() != 10)
1550  {
1552  log<text_log>("Error getting telescope environment data (telenv): TCS response wrong size, returned " + std::to_string(edat.size()) + "values",logPrio::LOG_WARNING);
1553  return -1;
1554  }
1555 
1556  m_wxtemp = strtod(edat[0].c_str(), 0);
1557  m_wxpres = strtod(edat[1].c_str(), 0);
1558  m_wxhumid = strtod(edat[2].c_str(), 0);
1559  m_wxwind = strtod(edat[3].c_str(), 0);
1560  m_wxwdir = strtod(edat[4].c_str(), 0);
1561  m_ttruss = strtod(edat[5].c_str(), 0);
1562  m_tcell = strtod(edat[6].c_str(), 0);
1563  m_tseccell = strtod(edat[7].c_str(), 0);
1564  m_tambient = strtod(edat[8].c_str(), 0);
1565  m_wxdewpoint = strtod(edat[9].c_str(),0);
1566 
1567  if( recordTelEnv() < 0)
1568  {
1569  return log<software_error,-1>({__FILE__,__LINE__});
1570  }
1571 
1572  return 0;
1573 } //int tcsInterface::getEnvData()
1574 
1575 inline
1577 {
1578  static int last_query = 0;
1579 
1580 
1581 
1582  if(time(0) - last_query > m_seeingInterval)
1583  {
1584  time_t sec_midnight;
1585  time_t dt;
1586 
1587  int rv = system("query_seeing > /dev/null");
1588  if(rv < 0)
1589  {
1590  log<software_error>({__FILE__, __LINE__, "Error from seeing query"});
1591  return -1;
1592  }
1593 
1594 
1595  last_query = time(0);
1596  sec_midnight = last_query % 86400;
1597 
1598  std::ifstream fin;
1599  std::string label, datestr, timestr, fwhmstr;
1600  double h, m,s;
1601 
1602  /* process DIMM */
1603  fin.open("/tmp/dimm.tsv");
1604 
1605  if(fin.fail())
1606  {
1607  log<software_error>({__FILE__, __LINE__, "Error reading dimm seeing"});
1608  return -1;
1609  }
1610 
1611  fin >> label;
1612  fin >> label;
1613  fin >> label;
1614  fin >> datestr;
1615  fin >> timestr;
1616 
1617  fin >> m_dimm_fwhm;
1618  fin >> m_dimm_el;
1619  fin.close();
1620 
1621  parse_xms(h, m, s, timestr);
1622 
1623  m_dimm_time = (int) (h*3600 + m*60 + s + 0.5);
1624 
1625  dt = sec_midnight - m_dimm_time;
1626  if(dt < 0) dt = 86400-dt;
1627 
1628  if(dt > 300 ||m_dimm_fwhm <= 0)
1629  {
1630  m_dimm_fwhm = -1;
1631  m_dimm_fwhm_corr = -1;
1632  }
1633  else
1634  {
1635  m_dimm_fwhm_corr = m_dimm_fwhm * pow(cos( (90.-m_dimm_el)*3.14159/180.), 3./5.);
1636  }
1637 
1638  /* process mag1 */
1639  fin.open("/tmp/mag1.tsv");
1640 
1641  if(fin.fail())
1642  {
1643  log<software_error>({__FILE__, __LINE__, "Error reading mag1 seeing"});
1644  return -1;
1645  }
1646 
1647  fin >> label;
1648  fin >> label;
1649  fin >> label;
1650  fin >> datestr;
1651  fin >> timestr;
1652 
1653  fin >> m_mag1_fwhm;
1654  fin >> m_mag1_el;
1655  fin.close();
1656 
1657  parse_xms(h, m, s, timestr);
1658 
1659  m_mag1_time = (int) (h*3600 + m*60 + s + 0.5);
1660 
1661  dt = sec_midnight - m_mag1_time;
1662  if(dt < 0) dt = 86400-dt;
1663 
1664  if(dt > 300 ||m_mag1_fwhm <= 0)
1665  {
1666  m_mag1_fwhm = -1;
1667  m_mag1_fwhm_corr = -1;
1668  }
1669  else
1670  {
1671  m_mag1_fwhm_corr = m_mag1_fwhm * pow(cos( (90.-m_mag1_el)*3.14159/180.), 3./5.);
1672  }
1673 
1674  /* process mag2 */
1675  fin.open("/tmp/mag2.tsv");
1676 
1677  if(fin.fail())
1678  {
1679  log<software_error>({__FILE__, __LINE__, "Error reading mag2 seeing"});
1680  return -1;
1681  }
1682 
1683  fin >> label;
1684  fin >> label;
1685  fin >> label;
1686  fin >> datestr;
1687  fin >> timestr;
1688 
1689  fin >> m_mag2_fwhm;
1690  fin >> m_mag2_el;
1691  fin.close();
1692 
1693  parse_xms(h, m, s, timestr);
1694 
1695  m_mag2_time = (int) (h*3600 + m*60 + s + 0.5);
1696 
1697  dt = sec_midnight - m_mag2_time;
1698  if(dt < 0) dt = 86400-dt;
1699 
1700  if(dt > 300 ||m_mag2_fwhm <= 0)
1701  {
1702  m_mag2_fwhm = -1;
1703  m_mag2_fwhm_corr = -1;
1704  }
1705  else
1706  {
1707  m_mag2_fwhm_corr = m_mag2_fwhm * pow(cos( (90.-m_mag2_el)*3.14159/180.), 3./5.);
1708  }
1709 
1710  if( recordTelSee() < 0)
1711  {
1712  return log<software_error,-1>({__FILE__,__LINE__});
1713  }
1714 
1715  }
1716 
1717  return 0;
1718 }
1719 
1720 inline
1722 {
1723  try
1724  {
1725  updateIfChanged(m_indiP_teltime, "sidereal_time", m_telST, INDI_OK);
1726 // m_indiP_teltime["sidereal_time"] = m_telST;
1727  }
1728  catch(...)
1729  {
1730  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1731  return -1;
1732  }
1733 
1734  /* try
1735  {
1736  m_indiP_teltime.setState(INDI_OK);
1737  }
1738  catch(...)
1739  {
1740  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1741  return -1;
1742  }
1743 
1744  try
1745  {
1746  m_indiDriver->sendSetProperty(m_indiP_teltime);
1747  }
1748  catch(...)
1749  {
1750  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1751  return -1;
1752  }*/
1753 
1754  try
1755  {
1756  indi::updateIfChanged(m_indiP_telpos, std::vector<std::string>({"epoch", "ra", "dec", "el", "ha", "am", "rotoff"}),
1758  /*m_indiP_telpos["epoch"] = m_telEpoch;
1759  m_indiP_telpos["ra"] = m_telRA;
1760  m_indiP_telpos["dec"] = m_telDec;
1761  m_indiP_telpos["el"] = m_telEl;
1762  m_indiP_telpos["ha"] = m_telHA;
1763  m_indiP_telpos["am"] = m_telAM;
1764  m_indiP_telpos["rotoff"] = m_telRotOff;*/
1765  }
1766  catch(...)
1767  {
1768  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1769  return -1;
1770  }
1771 
1772  /*try
1773  {
1774  m_indiP_telpos.setState(INDI_OK);
1775  }
1776  catch(...)
1777  {
1778  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1779  return -1;
1780  }
1781 
1782  try
1783  {
1784  m_indiDriver->sendSetProperty (m_indiP_telpos);
1785  }
1786  catch(...)
1787  {
1788  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1789  return -1;
1790  }*/
1791 
1792  try
1793  {
1794  m_indiP_teldata["roi"] = m_telROI;
1795  m_indiP_teldata["tracking"] = m_telTracking;
1796  m_indiP_teldata["guiding"] = m_telGuiding;
1797  m_indiP_teldata["slewing"] = m_telSlewing;
1798  m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
1799  m_indiP_teldata["az"] = m_telAz;
1800  m_indiP_teldata["zd"] = m_telZd;
1801  m_indiP_teldata["pa"] = m_telPA;
1802  m_indiP_teldata["dome_az"] = m_telDomeAz;
1803  m_indiP_teldata["dome_stat"] = m_telDomeStat;
1804  }
1805  catch(...)
1806  {
1807  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1808  return -1;
1809  }
1810 
1811  try
1812  {
1813  m_indiP_teldata.setState(INDI_OK);
1814  }
1815  catch(...)
1816  {
1817  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1818  return -1;
1819  }
1820 
1821  try
1822  {
1823  m_indiDriver->sendSetProperty (m_indiP_teldata);
1824  }
1825  catch(...)
1826  {
1827  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1828  return -1;
1829  }
1830 
1831  try
1832  {
1833  indi::updateIfChanged(m_indiP_catalog, std::vector<std::string>({"object", "rotmode"}), std::vector<std::string>({m_catObj, m_catRm}), m_indiDriver, INDI_OK);
1834  // m_indiP_catalog["object"] = m_catObj;
1835  //m_indiP_catalog["rotmode"] = m_catRo;
1836  }
1837  catch(...)
1838  {
1839  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1840  return -1;
1841  }
1842 
1843 
1844 /* try
1845  {
1846  m_indiP_catalog.setState(INDI_OK);
1847  }
1848  catch(...)
1849  {
1850  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1851  return -1;
1852  }
1853 
1854  try
1855  {
1856  m_indiDriver->sendSetProperty (m_indiP_catalog);
1857  }
1858  catch(...)
1859  {
1860  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1861  return -1;
1862  }*/
1863 
1864  try
1865  {
1866  indi::updateIfChanged(m_indiP_catdata, std::vector<std::string>({"ra", "dec", "epoch", "rotoff"}),
1867  std::vector<double>({ m_catRA, m_catDec, m_catEp, m_catRo}), m_indiDriver, INDI_OK);
1868  /*m_indiP_catdata["ra"] = m_catRA;
1869  m_indiP_catdata["dec"] = m_catDec;
1870  m_indiP_catdata["epoch"] = m_catEp;
1871  m_indiP_catdata["rotoff"] = m_catRo;*/
1872  }
1873  catch(...)
1874  {
1875  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1876  return -1;
1877  }
1878 
1879 /* try
1880  {
1881  m_indiP_catdata.setState(INDI_OK);
1882  }
1883  catch(...)
1884  {
1885  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1886  return -1;
1887  }
1888 
1889  try
1890  {
1891  m_indiDriver->sendSetProperty (m_indiP_catdata);
1892  }
1893  catch(...)
1894  {
1895  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1896  return -1;
1897  }*/
1898 
1899  //---- Vane End ----//
1900  try
1901  {
1902  m_indiP_vaneend["secz"] = m_telSecZ;
1903  m_indiP_vaneend["encz"] = m_telEncZ;
1904  m_indiP_vaneend["secx"] = m_telSecX;
1905  m_indiP_vaneend["encx"] = m_telEncX;
1906  m_indiP_vaneend["secy"] = m_telSecY;
1907  m_indiP_vaneend["ency"] = m_telEncY;
1908  m_indiP_vaneend["sech"] = m_telSecH;
1909  m_indiP_vaneend["ench"] = m_telEncH;
1910  m_indiP_vaneend["secv"] = m_telSecV;
1911  m_indiP_vaneend["encv"] = m_telEncV;
1912  }
1913  catch(...)
1914  {
1915  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1916  return -1;
1917  }
1918 
1919  try
1920  {
1921  m_indiP_vaneend.setState(INDI_OK);
1922  }
1923  catch(...)
1924  {
1925  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1926  return -1;
1927  }
1928 
1929  try
1930  {
1931  m_indiDriver->sendSetProperty (m_indiP_vaneend);
1932  }
1933  catch(...)
1934  {
1935  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1936  return -1;
1937  }
1938 
1939 
1940  //---- Environment ----//
1941  try
1942  {
1943  m_indiP_env["temp-out"] = m_wxtemp;
1944  m_indiP_env["pressure"] = m_wxpres;
1945  m_indiP_env["humidity"] = m_wxhumid;
1946  m_indiP_env["wind"] = m_wxwind;
1947  m_indiP_env["winddir"] = m_wxwdir;
1948  m_indiP_env["temp-truss"] = m_ttruss;
1949  m_indiP_env["temp-cell"] = m_tcell;
1950  m_indiP_env["temp-seccell"] = m_tseccell;
1951  m_indiP_env["temp-amb"] = m_tambient;
1952  m_indiP_env["dewpoint"] = m_wxdewpoint;
1953  }
1954  catch(...)
1955  {
1956  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1957  return -1;
1958  }
1959 
1960  try
1961  {
1962  m_indiP_env.setState(INDI_OK);
1963  }
1964  catch(...)
1965  {
1966  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1967  return -1;
1968  }
1969 
1970  try
1971  {
1972  m_indiDriver->sendSetProperty (m_indiP_env);
1973  }
1974  catch(...)
1975  {
1976  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
1977  return -1;
1978  }
1979 
1980 
1981  //---- Seeing ----//
1982  try
1983  {
1984  m_indiP_seeing["dimm_time"] = m_dimm_time;
1985  m_indiP_seeing["dimm_el"] = m_dimm_el;
1986  m_indiP_seeing["dimm_fwhm"] = m_dimm_fwhm;
1987  m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
1988 
1989  m_indiP_seeing["mag1_time"] = m_mag1_time;
1990  m_indiP_seeing["mag1_el"] = m_mag1_el;
1991  m_indiP_seeing["mag1_fwhm"] = m_mag1_fwhm;
1992  m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
1993 
1994  m_indiP_seeing["mag2_time"] = m_mag2_time;
1995  m_indiP_seeing["mag2_el"] = m_mag2_el;
1996  m_indiP_seeing["mag2_fwhm"] = m_mag2_fwhm;
1997  m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
1998  }
1999  catch(...)
2000  {
2001  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2002  return -1;
2003  }
2004 
2005  try
2006  {
2007  m_indiP_seeing.setState(INDI_OK);
2008  }
2009  catch(...)
2010  {
2011  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2012  return -1;
2013  }
2014 
2015  try
2016  {
2017  m_indiDriver->sendSetProperty (m_indiP_seeing);
2018  }
2019  catch(...)
2020  {
2021  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2022  return -1;
2023  }
2024 
2025  //--- Offloading ---//
2026 
2027 
2028  try
2029  {
2030  if(m_offlTT_dump)
2031  {
2032  updateSwitchIfChanged(m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK);
2033  }
2034  else
2035  {
2036  updateSwitchIfChanged(m_indiP_offlTTdump, "request", pcf::IndiElement::Off, INDI_IDLE);
2037  }
2038 
2039  if(m_offlTT_enabled)
2040  {
2041  updateSwitchIfChanged(m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK);
2042  }
2043  else
2044  {
2045  updateSwitchIfChanged(m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
2046  }
2047 
2051 
2052  }
2053  catch(...)
2054  {
2055  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2056  return -1;
2057  }
2058 
2059  try
2060  {
2061  if(m_offlF_dump)
2062  {
2063  updateSwitchIfChanged(m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK);
2064  }
2065  else
2066  {
2067  updateSwitchIfChanged(m_indiP_offlFdump, "request", pcf::IndiElement::Off, INDI_IDLE);
2068  }
2069 
2070  if(m_offlF_enabled)
2071  {
2072  updateSwitchIfChanged(m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK);
2073  }
2074  else
2075  {
2076  updateSwitchIfChanged(m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
2077  }
2078 
2082 
2083  }
2084  catch(...)
2085  {
2086  log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2087  return -1;
2088  }
2089 
2090  return 0;
2091 }
2092 
2093 inline
2095 {
2097 }
2098 
2099 inline
2101 {
2102  recordTelPos(true);
2103  return 0;
2104 }
2105 
2106 inline
2108 {
2109  recordTelData(true);
2110  return 0;
2111 }
2112 
2113 inline
2115 {
2116  recordTelVane(true);
2117  return 0;
2118 }
2119 
2120 inline
2122 {
2123  recordTelEnv(true);
2124  return 0;
2125 }
2126 
2127 inline
2129 {
2130  recordTelCat(true);
2131  return 0;
2132 }
2133 
2134 inline
2136 {
2137  recordTelSee(true);
2138  return 0;
2139 }
2140 
2141 inline
2143 {
2144  static double lastEpoch = 0;
2145  static double lastRA = 0;
2146  static double lastDec = 0;
2147  static double lastEl = 0;
2148  static double lastHA = 0;
2149  static double lastAM = 0;
2150  static double lastRotOff = 0;
2151 
2152  if( force || lastEpoch != m_telEpoch ||
2153  lastRA != m_telRA ||
2154  lastDec != m_telDec ||
2155  lastEl != m_telEl ||
2156  lastHA != m_telHA ||
2157  lastAM != m_telAM ||
2158  lastRotOff != m_telRotOff )
2159  {
2160  telem<telem_telpos>({m_telEpoch, m_telRA, m_telDec, m_telEl, m_telHA, m_telAM, m_telRotOff});
2161 
2162  lastEpoch = m_telEpoch;
2163  lastRA = m_telRA;
2164  lastDec = m_telDec;
2165  lastEl = m_telEl;
2166  lastHA = m_telHA;
2167  lastAM = m_telAM;
2168  lastRotOff = m_telRotOff;
2169  }
2170 
2171  return 0;
2172 }
2173 
2174 inline
2176 {
2177  static int lastROI = -999;
2178  static int lastTracking = -999;
2179  static int lastGuiding = -999;
2180  static int lastSlewing = -999;
2181  static int lastGuiderMoving = -999;
2182  static double lastAz = 0;
2183  static double lastZd = 0;
2184  static double lastPA = 0;
2185  static double lastDomeAz = 0;
2186  static int lastDomeStat = -999;
2187 
2188  if( force || lastROI != m_telROI ||
2189  lastTracking != m_telTracking ||
2190  lastGuiding != m_telGuiding ||
2191  lastSlewing != m_telSlewing ||
2192  lastGuiderMoving != m_telGuiderMoving ||
2193  lastAz != m_telAz ||
2194  lastZd != m_telZd ||
2195  lastPA != m_telPA ||
2196  lastDomeAz != m_telDomeAz ||
2197  lastDomeStat != m_telDomeStat )
2198  {
2200 
2201  lastROI = m_telROI;
2202  lastTracking = m_telTracking;
2203  lastGuiding = m_telGuiding;
2204  lastSlewing = m_telSlewing;
2205  lastGuiderMoving = m_telGuiderMoving;
2206  lastAz = m_telAz;
2207  lastZd = m_telZd;
2208  lastPA = m_telPA;
2209  lastDomeAz = m_telDomeAz;
2210  lastDomeStat = m_telDomeStat;
2211  }
2212 
2213  return 0;
2214 }
2215 
2216 inline
2218 {
2219  static double lastSecZ = -999;
2220  static double lastEncZ = -999;
2221  static double lastSecX = -999;
2222  static double lastEncX = -999;
2223  static double lastSecY = -999;
2224  static double lastEncY = -999;
2225  static double lastSecH = -999;
2226  static double lastEncH = -999;
2227  static double lastSecV = -999;
2228  static double lastEncV = -999;
2229 
2230  if( force || lastSecZ != m_telSecZ ||
2231  lastEncZ != m_telEncZ ||
2232  lastSecX != m_telSecX ||
2233  lastEncX != m_telEncX ||
2234  lastSecY != m_telSecY ||
2235  lastEncY != m_telEncY ||
2236  lastSecH != m_telSecH ||
2237  lastEncH != m_telEncH ||
2238  lastSecV != m_telSecV ||
2239  lastEncV != m_telEncV )
2240  {
2242 
2243  lastSecZ = m_telSecZ;
2244  lastEncZ = m_telEncZ;
2245  lastSecX = m_telSecX;
2246  lastEncX = m_telEncX;
2247  lastSecY = m_telSecY;
2248  lastEncY = m_telEncY;
2249  lastSecH = m_telSecH;
2250  lastEncH = m_telEncH;
2251  lastSecV = m_telSecV;
2252  lastEncV = m_telEncV;
2253  }
2254 
2255  return 0;
2256 }
2257 
2258 inline
2260 {
2261  static double lastWxtemp = -999;
2262  static double lastWxpres = -999;
2263  static double lastWxhumid = -999;
2264  static double lastWxwind = -999;
2265  static double lastWxwdir = -999;
2266  static double lastTtruss = -999;
2267  static double lastTcell = -999;
2268  static double lastTseccell = -999;
2269  static double lastTambient = -999;
2270  static double lastWxdewpoint = -999;
2271 
2272 
2273  if( force || lastWxtemp != m_wxtemp ||
2274  lastWxpres != m_wxpres ||
2275  lastWxhumid != m_wxhumid ||
2276  lastWxwind != m_wxwind ||
2277  lastWxwdir != m_wxwdir ||
2278  lastTtruss != m_ttruss ||
2279  lastTcell != m_tcell ||
2280  lastTseccell != m_tseccell ||
2281  lastTambient != m_tambient ||
2282  lastWxdewpoint != m_wxdewpoint )
2283  {
2285 
2286  lastWxtemp = m_wxtemp;
2287  lastWxpres = m_wxpres;
2288  lastWxhumid = m_wxhumid;
2289  lastWxwind = m_wxwind;
2290  lastWxwdir = m_wxwdir;
2291  lastTtruss = m_ttruss;
2292  lastTcell = m_tcell;
2293  lastTseccell = m_tseccell;
2294  lastTambient = m_tambient;
2295  lastWxdewpoint = m_wxdewpoint;
2296 
2297  }
2298 
2299  return 0;
2300 }
2301 
2302 inline
2304 {
2305  static std::string last_catObj;
2306  static std::string last_catRm;
2307  static double last_catRA = 0;
2308  static double last_catDec = 0;
2309  static double last_catEp = 0;
2310  static double last_catRo = 0;
2311 
2312  if(force || m_catObj != last_catObj ||
2313  m_catRm != last_catRm ||
2314  m_catRA != last_catRA ||
2315  m_catDec != last_catDec ||
2316  m_catEp != last_catEp ||
2317  m_catRo != last_catRo )
2318  {
2319  telem<telem_telcat>({m_catObj, m_catRm, m_catRA, m_catDec, m_catEp, m_catRo});
2320 
2321  last_catObj = m_catObj;
2322  last_catRm = m_catRm;
2323  last_catRA = m_catRA;
2324  last_catDec = m_catDec;
2325  last_catEp = m_catEp;
2326  last_catRo = m_catRo;
2327  }
2328 
2329  return 0;
2330 }
2331 
2332 inline
2334 {
2335  static int last_dimm_time = 0;
2336  static double last_dimm_el = 0;
2337  static double last_dimm_fwhm = 0;
2338  //static double last_dimm_fwhm_corr = 0;
2339 
2340  static int last_mag1_time = 0;
2341  static double last_mag1_el = 0;
2342  static double last_mag1_fwhm = 0;
2343  static double last_mag1_fwhm_corr = 0;
2344 
2345  static int last_mag2_time = 0;
2346  static double last_mag2_el = 0;
2347  static double last_mag2_fwhm = 0;
2348  static double last_mag2_fwhm_corr = 0;
2349 
2350  if(force || m_dimm_time != last_dimm_time ||
2351  m_dimm_el != last_dimm_el ||
2352  m_dimm_fwhm != last_dimm_fwhm ||
2353  /*m_dimm_fwhm_corr != last_dimm_fwhm_corr || //ignore corrected dimm*/
2354  m_mag1_time != last_mag1_time ||
2355  m_mag1_el != last_mag1_el ||
2356  m_mag1_fwhm != last_mag1_fwhm ||
2357  m_mag1_fwhm_corr != last_mag1_fwhm_corr ||
2358  m_mag2_time != last_mag2_time ||
2359  m_mag2_el != last_mag2_el ||
2360  m_mag2_fwhm != last_mag2_fwhm ||
2361  m_mag2_fwhm_corr != last_mag2_fwhm_corr)
2362 
2363  {
2365 
2366  last_dimm_time = m_dimm_time;
2367  last_dimm_el = m_dimm_el;
2368  last_dimm_fwhm = m_dimm_fwhm;
2369  //last_dimm_fwhm_corr = m_dimm_fwhm_corr;
2370 
2371  last_mag1_time = m_mag1_time;
2372  last_mag1_el = m_mag1_el;
2373  last_mag1_fwhm = m_mag1_fwhm;
2374  last_mag1_fwhm_corr = m_mag1_fwhm_corr;
2375 
2376  last_mag2_time = m_mag2_time;
2377  last_mag2_el = m_mag2_el;
2378  last_mag2_fwhm = m_mag2_fwhm;
2379  last_mag2_fwhm_corr = m_mag2_fwhm_corr;
2380  }
2381 
2382  return 0;
2383 }
2384 
2386  float y,
2387  float z
2388  )
2389 {
2390  if(x != 0 || y != 0)
2391  {
2392  float dx = m_pyrNudge_C_00 * x + m_pyrNudge_C_01 * y;
2393  float dy = m_pyrNudge_C_10 * x + m_pyrNudge_C_11 * y;
2394 
2395  //float cs = cos((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
2396  //float ss = sin((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
2397 
2398  //float dx = x * cs - y * ss;
2399  //float dy = m_pyrNudge_parity*(x * ss + y * cs);
2400 
2401  char ttstr[64];
2402  snprintf(ttstr, sizeof(ttstr) , "aeg %f %f", dx, dy);
2403 
2404  ///\todo need logtypes for nudges and offloads
2405  log<text_log>(std::string("[PYRNUDGE] ") + ttstr, logPrio::LOG_NOTICE);
2406  if(sendMagTelCommand(ttstr, m_readTimeout) < 0)
2407  {
2408  log<software_error>({__FILE__,__LINE__, std::string("error sending command: ") + ttstr});
2409  return -1;
2410  }
2411  }
2412 
2413  if(z != 0)
2414  {
2415  char ttstr[64];
2416  snprintf(ttstr, sizeof(ttstr) , "zimr %f", z*m_pyrNudge_F_sign);
2417 
2418  log<text_log>(std::string("[PYRNUDGE] ") + ttstr, logPrio::LOG_NOTICE);
2419  if(sendMagTelCommand(ttstr, m_readTimeout) < 0)
2420  {
2421  log<software_error>({__FILE__,__LINE__, std::string("error sending command: ") + ttstr});
2422  return -1;
2423  }
2424  }
2425 
2426  return 0;
2427 }
2428 
2430 {
2431  //Convert current telescope rotator angle to radians
2432  float q = (m_acqZdSign*m_telZd)*3.14159/180.;
2433 
2434  //The rotation matrix
2435  float az = m_acqAz0*cos(q) - m_acqEl0*sin(q) + m_acqAzOff;
2436  float el = m_acqAz0*sin(q) + m_acqEl0*cos(q) + m_acqElOff;
2437 
2438  char ttstr[64];
2439  snprintf(ttstr, sizeof(ttstr) , "aeg %f %f", az, el);
2440 
2441  ///\todo need logtypes for nudges and offloads
2442  log<text_log>(std::string("[ACQUIRE] ") + ttstr, logPrio::LOG_NOTICE);
2443  if(sendMagTelCommand(ttstr, m_readTimeout) < 0)
2444  {
2445  log<software_error>({__FILE__,__LINE__, std::string("error sending command: ") + ttstr});
2446  return -1;
2447  }
2448 
2449  float z = m_acqFocus;
2450  snprintf(ttstr, sizeof(ttstr) , "zimr %f", z*m_pyrNudge_F_sign);
2451 
2452  log<text_log>(std::string("[ACQUIRE] ") + ttstr, logPrio::LOG_NOTICE);
2453  if(sendMagTelCommand(ttstr, m_readTimeout) < 0)
2454  {
2455  log<software_error>({__FILE__,__LINE__, std::string("error sending command: ") + ttstr});
2456  return -1;
2457  }
2458 
2459  return 0;
2460 }
2461 
2463 {
2464  t->offloadThreadExec();
2465 }
2466 
2468 {
2469  //Get the thread PID immediately so the caller can return.
2470  m_offloadThreadID = syscall(SYS_gettid);
2471 
2472  static int last_loopState = -1;
2473 
2474  while( (m_offloadThreadInit == true || state() != stateCodes::CONNECTED) && shutdown() == 0)
2475  {
2476  sleep(1);
2477  }
2478 
2479 
2480  float avg_TT_0;
2481  float avg_TT_1;
2482  int sincelast_TT = 0;
2483 
2484  float avg_F_0;
2485  int sincelast_F = 0;
2486 
2487  while(shutdown() == 0)
2488  {
2489  //Check if loop open
2490  if(m_loopState == 0)
2491  {
2492  //If this is new, then reset the averaging buffer
2493  if(m_loopState != last_loopState)
2494  {
2495  m_firstRequest = 0;
2496  m_lastRequest = std::numeric_limits<size_t>::max();
2497  m_nRequests = 0;
2498  m_last_nRequests = 0;
2499 
2500  }
2501  sleep(1);
2502  last_loopState = m_loopState;
2503  continue;
2504  }
2505 
2506  //Check if loop paused
2507  if(m_loopState == 1)
2508  {
2509  sleep(1);
2510  last_loopState = m_loopState;
2511  continue;
2512  }
2513 
2514  //Ok loop closed
2515 
2516  if(m_firstRequest == m_lastRequest) continue; //this really should mutexed instead
2517 
2518  //If we got a new offload request, process it
2520  {
2521  //std::cerr << m_firstRequest << " " << m_lastRequest << " " << m_nRequests << std::endl;
2522 
2523  ///\todo offloading: These sections ought to be separate functions for clarity
2524  /* --- TT --- */
2525  avg_TT_0 = 0;
2526  avg_TT_1 = 0;
2527 
2528  int navg = 0;
2529 
2530  size_t i = m_lastRequest;
2531 
2532  for(size_t n=0; n < m_offlTT_avgInt; ++n)
2533  {
2534  avg_TT_0 += m_offloadRequests[0][i];
2535  avg_TT_1 += m_offloadRequests[1][i];
2536  ++navg;
2537 
2538  if(i== m_firstRequest) break;
2539 
2540  if(i == 0) i = m_offloadRequests[0].size()-1;
2541  else --i;
2542  }
2543 
2544  avg_TT_0 /= navg;
2545  avg_TT_1 /= navg;
2546 
2547  ++sincelast_TT;
2548  if(sincelast_TT > m_offlTT_avgInt)
2549  {
2550  doTToffload(avg_TT_0, avg_TT_1);
2551  sincelast_TT = 0;
2552  }
2553 
2554 
2555  /* --- Focus --- */
2556  avg_F_0 = 0;
2557 
2558  navg = 0;
2559 
2560  i = m_lastRequest;
2561 
2562  for(size_t n=0; n < m_offlF_avgInt; ++n)
2563  {
2564  avg_F_0 += m_offloadRequests[2][i];
2565  ++navg;
2566 
2567  if(i== m_firstRequest) break;
2568 
2569  if(i == 0) i = m_offloadRequests[0].size()-1;
2570  else --i;
2571  }
2572 
2573  avg_F_0 /= navg;
2574 
2575  ++sincelast_F;
2576  if(sincelast_F > m_offlF_avgInt)
2577  {
2578  doFoffload(avg_F_0);
2579  sincelast_F = 0;
2580  }
2581 
2582 
2583 
2584 
2585 
2587  }
2588  last_loopState = m_loopState;
2589 
2590  sleep(1);
2591 
2592 
2593  }
2594 
2595  return;
2596 }
2597 
2599  float tt_1
2600  )
2601 {
2602  if(m_offlTT_dump)
2603  {
2604  log<text_log>("[OFFL] TT dumping: " + std::to_string(tt_0) + " " + std::to_string(tt_1));
2605  sendTToffload(tt_0, tt_1);
2606  m_offlTT_dump = false;
2607  }
2608  else
2609  {
2610  tt_0 *= m_offlTT_gain;
2611  tt_1 *= m_offlTT_gain;
2612 
2613  if(fabs(tt_0) < m_offlTT_thresh) tt_0 = 0;
2614  if(fabs(tt_1) < m_offlTT_thresh) tt_1 = 0;
2615 
2616 
2617  if(tt_0 ==0 && tt_1 == 0)
2618  {
2619  //Do nothing
2620  }
2621  else if(m_offlTT_enabled)
2622  {
2623  log<text_log>("[OFFL] TT offloading: " + std::to_string(tt_0) + " " + std::to_string(tt_1));
2624  sendTToffload(tt_0, tt_1);
2625  }
2626  else
2627  {
2628  log<text_log>("TT offload above threshold but TT offloading disabled", logPrio::LOG_WARNING);
2629  }
2630  }
2631  return 0;
2632 
2633 }
2634 
2636  float tt_1
2637  )
2638 {
2639 
2640  if(m_labMode)
2641  {
2642  pcf::IndiProperty ip(pcf::IndiProperty::Number);
2643  ip.setDevice("modwfs");
2644  ip.setName("offset12");
2645  ip.add(pcf::IndiElement("dC1"));
2646  ip.add(pcf::IndiElement("dC2"));
2647 
2648  sendNewProperty (ip);
2649 
2650  ip["dC1"] = tt_0;
2651  ip["dC2"] = tt_1;
2652 
2653  sendNewProperty(ip);
2654  return 0;
2655  }
2656 
2657  char ttstr[64];
2658 
2659 
2660  snprintf(ttstr, sizeof(ttstr) , "aeg %f %f", tt_0, tt_1);
2661 
2662  log<text_log>(std::string("[OFFL] sending: ") + ttstr);
2663 
2664  return sendMagTelCommand(ttstr, m_readTimeout);
2665 }
2666 
2668 {
2669  if(m_offlF_dump)
2670  {
2671  log<text_log>("[OFFL] Focus dumping: " + std::to_string(F_0));
2672  sendFoffload(F_0);
2673  m_offlF_dump = false;
2674  }
2675  else
2676  {
2677  F_0 *= m_offlF_gain;
2678 
2679  if(fabs(F_0) < m_offlF_thresh) F_0 = 0;
2680 
2681  if(F_0 == 0)
2682  {
2683 
2684  }
2685  else if(m_offlF_enabled)
2686  {
2687  log<text_log>("[OFFL] Focus sending: " + std::to_string(F_0));
2688  sendFoffload(F_0);
2689  }
2690  else
2691  {
2692  log<text_log>("Focus offload above threshold but Focus offloading disabled", logPrio::LOG_WARNING);
2693  }
2694  }
2695  return 0;
2696 
2697 }
2698 
2700 {
2701  char fstr[64];
2702 
2703  //Use zimr to update the IMA values
2704  snprintf(fstr, sizeof(fstr), "zimr %f", F_0);
2705 
2706  log<text_log>(std::string("[OFFL] sending: ") + fstr);
2707 
2708  return sendMagTelCommand(fstr, m_readTimeout);
2709 }
2710 
2711 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_pyrNudge)(const pcf::IndiProperty &ipRecv)
2712 {
2713  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_pyrNudge, ipRecv);
2714 
2715  float x = 0;
2716  float y = 0;
2717  float z = 0;
2718 
2719  if(ipRecv.find("y"))
2720  {
2721  x = ipRecv["y"].get<float>();
2722  }
2723 
2724  if(ipRecv.find("x"))
2725  {
2726  y = ipRecv["x"].get<float>();
2727  }
2728 
2729  if(ipRecv.find("z"))
2730  {
2731  z = ipRecv["z"].get<float>();
2732  }
2733 
2734  return sendPyrNudge(x, y, z);
2735 }
2736 
2737 
2738 
2739 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_acqFromGuider)(const pcf::IndiProperty &ipRecv)
2740 {
2741  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_acqFromGuider, ipRecv);
2742 
2743  if(!ipRecv.find("request"))
2744  {
2745  return 0;
2746  }
2747 
2748  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2749  {
2750  return acquireFromGuider();
2751  }
2752 
2753  return 0;
2754 }
2755 
2756 INDI_SETCALLBACK_DEFN(tcsInterface, m_indiP_loopState)(const pcf::IndiProperty &ipRecv)
2757 {
2758  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_loopState, ipRecv);
2759 
2760  if(!ipRecv.find("toggle")) return 0;
2761 
2762  if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
2763  {
2764  std::cerr << "on\n";
2765  m_loopState = 2;
2766  }
2767  else
2768  {
2769  std::cerr << "off\n";
2770  m_loopState = 0;
2771  }
2772 
2773  return 0;
2774 }
2775 
2776 INDI_SETCALLBACK_DEFN(tcsInterface, m_indiP_offloadCoeffs)(const pcf::IndiProperty &ipRecv)
2777 {
2778  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offloadCoeffs, ipRecv);
2779 
2780  if(m_loopState != 2) return 0;
2781 
2782  size_t nextReq = m_lastRequest + 1;
2783 
2784  if(nextReq >= m_offloadRequests[0].size()) nextReq = 0;
2785 
2786  //Tip-Tilt
2787  float tt0 = ipRecv["00"].get<float>();
2788  float tt1 = ipRecv["01"].get<float>();
2789 
2790  if(m_labMode)
2791  {
2792  m_offloadRequests[0][nextReq] = m_lab_offlTT_C_00 * tt0 + m_lab_offlTT_C_01 * tt1;
2793  m_offloadRequests[1][nextReq] = m_lab_offlTT_C_10 * tt0 + m_lab_offlTT_C_11 * tt1;
2794  }
2795  else
2796  {
2797  m_offloadRequests[0][nextReq] = m_offlTT_C_00 * tt0 + m_offlTT_C_01 * tt1;
2798  m_offloadRequests[1][nextReq] = m_offlTT_C_10 * tt0 + m_offlTT_C_11 * tt1;
2799  }
2800 
2801  //Focus
2802  float f0 = ipRecv["02"].get<float>();
2803 
2804  m_offloadRequests[2][nextReq] = m_offlCFocus_00 * f0;
2805 
2806 
2807  //Coma
2808  float c0 = ipRecv["03"].get<float>();
2809  float c1 = ipRecv["04"].get<float>();
2810 
2811  m_offloadRequests[3][nextReq] = m_offlCComa_00 * c0 + m_offlCComa_01 * c1;
2812  m_offloadRequests[4][nextReq] = m_offlCComa_10 * c0 + m_offlCComa_11 * c1;
2813 
2814  //Now update circ buffer
2815  m_lastRequest = nextReq;
2816  ++m_nRequests;
2817  if(m_nRequests > m_offloadRequests[0].size()) ++m_firstRequest;
2818  if(m_firstRequest >= m_offloadRequests[0].size()) m_firstRequest = 0;
2819  return 0;
2820 }
2821 
2822 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlTTenable)(const pcf::IndiProperty &ipRecv)
2823 {
2824  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlTTenable, ipRecv);
2825 
2826  if(ipRecv.getName() != m_indiP_offlTTenable.getName())
2827  {
2828  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
2829  return -1;
2830  }
2831 
2832  if(!ipRecv.find("toggle")) return 0;
2833 
2834  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_offlTT_enabled == false)
2835  {
2836  updateSwitchIfChanged(m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK);
2837 
2838  m_offlTT_enabled = true;
2839  }
2840  else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlTT_enabled == true)
2841  {
2842  updateSwitchIfChanged(m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
2843 
2844  m_offlTT_enabled = false;
2845  }
2846 
2847  return 0;
2848 
2849 }
2850 
2851 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlTTdump)(const pcf::IndiProperty &ipRecv)
2852 {
2853  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlTTdump, ipRecv);
2854 
2855  if(!ipRecv.find("request")) return 0;
2856 
2857  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2858  {
2859  updateSwitchIfChanged(m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK);
2860 
2861  m_offlTT_dump = true;
2862  }
2863 
2864  return 0;
2865 
2866 }
2867 
2868 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlTTavgInt)(const pcf::IndiProperty &ipRecv)
2869 {
2870  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlTTavgInt, ipRecv);
2871 
2872  int target;
2873 
2874  if( indiTargetUpdate( m_indiP_offlTTavgInt, target, ipRecv, true) < 0)
2875  {
2876  log<software_error>({__FILE__,__LINE__});
2877  return -1;
2878  }
2879 
2880  m_offlTT_avgInt = target;
2881 
2882  return 0;
2883 }
2884 
2885 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlTTgain)(const pcf::IndiProperty &ipRecv)
2886 {
2887  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlTTgain, ipRecv);
2888 
2889  float target;
2890 
2891  if( indiTargetUpdate( m_indiP_offlTTgain, target, ipRecv, true) < 0)
2892  {
2893  log<software_error>({__FILE__,__LINE__});
2894  return -1;
2895  }
2896 
2897  m_offlTT_gain = target;
2898 
2899  return 0;
2900 }
2901 
2902 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlTTthresh)(const pcf::IndiProperty &ipRecv)
2903 {
2904  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlTTthresh, ipRecv);
2905 
2906  float target;
2907 
2908  if( indiTargetUpdate( m_indiP_offlTTthresh, target, ipRecv, true) < 0)
2909  {
2910  log<software_error>({__FILE__,__LINE__});
2911  return -1;
2912  }
2913 
2914  m_offlTT_thresh = target;
2915 
2916  return 0;
2917 }
2918 
2919 
2920 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlFenable)(const pcf::IndiProperty &ipRecv)
2921 {
2922  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlFenable, ipRecv);
2923 
2924  if(!ipRecv.find("toggle")) return 0;
2925 
2926  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_offlF_enabled == false)
2927  {
2928  updateSwitchIfChanged(m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK);
2929 
2930  m_offlF_enabled = true;
2931  }
2932  else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off && m_offlF_enabled == true)
2933  {
2934  updateSwitchIfChanged(m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE);
2935 
2936  m_offlF_enabled = false;
2937  }
2938 
2939  return 0;
2940 
2941 }
2942 
2943 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlFdump)(const pcf::IndiProperty &ipRecv)
2944 {
2945  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlFdump, ipRecv);
2946 
2947  if(!ipRecv.find("request")) return 0;
2948 
2949  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
2950  {
2951  updateSwitchIfChanged(m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK);
2952 
2953  m_offlF_dump = true;
2954  }
2955 
2956  return 0;
2957 
2958 }
2959 
2960 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlFavgInt)(const pcf::IndiProperty &ipRecv)
2961 {
2962  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlFavgInt, ipRecv);
2963 
2964  int target;
2965 
2966  if( indiTargetUpdate( m_indiP_offlFavgInt, target, ipRecv, true) < 0)
2967  {
2968  log<software_error>({__FILE__,__LINE__});
2969  return -1;
2970  }
2971 
2972  m_offlF_avgInt = target;
2973 
2974  return 0;
2975 }
2976 
2977 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlFgain)(const pcf::IndiProperty &ipRecv)
2978 {
2979  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlFgain, ipRecv);
2980 
2981  float target;
2982 
2983  if( indiTargetUpdate( m_indiP_offlFgain, target, ipRecv, true) < 0)
2984  {
2985  log<software_error>({__FILE__,__LINE__});
2986  return -1;
2987  }
2988 
2989  m_offlF_gain = target;
2990 
2991  return 0;
2992 }
2993 
2994 INDI_NEWCALLBACK_DEFN(tcsInterface, m_indiP_offlFthresh)(const pcf::IndiProperty &ipRecv)
2995 {
2996  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_offlFthresh, ipRecv);
2997 
2998  float target;
2999 
3000  std::cerr << "Got offl thresh\n";
3001 
3002  if( indiTargetUpdate( m_indiP_offlFthresh, target, ipRecv, true) < 0)
3003  {
3004  log<software_error>({__FILE__,__LINE__});
3005  return -1;
3006  }
3007 
3008  m_offlF_thresh = target;
3009 
3010  return 0;
3011 }
3012 
3013 } //namespace app
3014 } //namespace MagAOX
3015 
3016 #endif //tcsInterface_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:2877
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
Definition: MagAOXApp.hpp:2352
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
Definition: MagAOXApp.hpp:2321
int createStandardIndiNumber(pcf::IndiProperty &prop, const std::string &name, const T &min, const T &max, const T &step, const std::string &format, const std::string &label="", const std::string &group="")
Create a standard R/W INDI Number property with target and current elements.
Definition: MagAOXApp.hpp:2246
int shutdown()
Get the value of the shutdown flag.
Definition: MagAOXApp.hpp:1146
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
Definition: MagAOXApp.hpp:537
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:2901
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
Definition: MagAOXApp.hpp:2140
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
Definition: MagAOXApp.hpp:2294
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
int createROIndiText(pcf::IndiProperty &prop, const std::string &propName, const std::string &elName, const std::string &propLabel="", const std::string &propGroup="", const std::string &elLabel="")
Create a standard ReadOnly INDI Text property, with at least one element.
Definition: MagAOXApp.hpp:2209
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:1950
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
Definition: MagAOXApp.hpp:3031
The MagAO-X Clay Telescope TCS Interface.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFgain)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTgain)
double m_dimm_el
DIMM elevation at time of seeing measurement.
pcf::IndiProperty m_indiP_offlTTdump
double m_wxdewpoint
Dew point from weather station.
int recordTelem(const telem_telpos *)
virtual int appLogic()
Implementation of the FSM for tcsInterface.
pcf::IndiProperty m_indiP_offlFgain
pcf::IndiProperty m_indiP_acqFromGuider
Property used to request a pyramid nudge.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTthresh)
~tcsInterface() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_seeing
INDI Property for seeing.
double m_mag1_fwhm
MAG1 raw FWHM.
double m_telDomeAz
dome azimuth
int m_telGuiding
guider moving state
int sendMagTelCommand(const std::string &command, int timeout)
int recordTelCat(bool force=false)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFavgInt)
double m_catRA
Catalog right ascension [degrees].
pcf::IndiProperty m_indiP_teltime
double m_mag1_el
MAG1 elevation at time of seeing measurement.
double m_dimm_fwhm_corr
DIMM elevation corrected FWHM.
int m_telGuiderMoving
guider moving state
pcf::IndiProperty m_indiP_env
INDI Property for environment.
int sendTToffload(float TT_0, float TT_1)
pcf::IndiProperty m_indiP_pyrNudge
Property used to request a pyramid nudge.
int recordTelPos(bool force=false)
int recordTelEnv(bool force=false)
pcf::IndiProperty m_indiP_telpos
int recordTelData(bool force=false)
double m_telPA
parallactic angle
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTenable)
double m_wxpres
Outside pressue, millibars.
std::string m_catObj
Catalog object name.
int m_mag2_time
Seconds since midnight of MAG2 measurement.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFthresh)
pcf::IndiProperty m_indiP_offloadCoeffs
Property used to report the latest woofer modal coefficients for offloading.
pcf::IndiProperty m_offloadThreadProp
Offload thread INDI property.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_pyrNudge)
pcf::IndiProperty m_indiP_offlTTgain
pcf::IndiProperty m_indiP_catdata
INDI Property for the catalog data.
bool m_offloadThreadInit
Initialization flag for the offload thread.
int m_mag1_time
Seconds since midnight of MAG1 measurement.
double m_mag2_el
MAG2 elevation at time of seeing measurement.
std::thread m_offloadThread
The offloading thread.
double m_telZd
zenith distance
friend class tcsInterface_test
void offloadThreadExec()
Offload thread function.
int getMagTelStatus(std::string &response, const std::string &statreq)
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFdump)
double m_catDec
Catalog declination [degrees].
double m_wxwind
outside wind intensity, mph
pcf::IndiProperty m_indiP_teldata
int parse_xms(double &x, double &m, double &s, const std::string &xmsstr)
int m_devicePort
The IP port for TCS communications. Should be the command port. Default is 5811.
std::mutex m_tcsMutex
Mutex for locking INDI communications.
double m_mag2_fwhm_corr
MAG2 elevation corrected FWHM.
double m_tcell
Primary mirror cell temperature, Celsius.
double m_mag1_fwhm_corr
MAG1 elevation corrected FWHM.
std::string m_deviceAddr
The IP address or resolvable name of the TCS.
pid_t m_offloadThreadID
Offload thread pid.
pcf::IndiProperty m_indiP_vaneend
INDI Property for the vane end positions.
std::string m_catRm
Catalog rotator mode.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_acqFromGuider)
pcf::IndiProperty m_indiP_offlFenable
INDI_SETCALLBACK_DECL(tcsInterface, m_indiP_offloadCoeffs)
int m_dimm_time
Seconds since midnight of DIMM measurement.
pcf::IndiProperty m_indiP_offlFdump
double m_catEp
Catalog epoch.
double m_tambient
Dome air temperature, Celsius.
tcsInterface()
Default c'tor.
pcf::IndiProperty m_indiP_offlTTenable
double m_ttruss
Telescope truss temperature, Celsius.
int doTToffload(float TT_0, float TT_1)
pcf::IndiProperty m_indiP_catalog
INDI Property for the catalog text information.
double m_wxhumid
Outside humidity, percent.
virtual int appStartup()
Startup function.
int recordTelVane(bool force=false)
virtual int appShutdown()
Shutdown the app.
int recordTelSee(bool force=false)
int m_telDomeStat
dome status
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int m_telSlewing
slewing state
int m_telROI
The rotator of interest.
int m_telTracking
tracking state
double m_catRo
Catalog rotator offset.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFenable)
double m_wxtemp
Outside temperature, Celsius.
pcf::IndiProperty m_indiP_offlTTavgInt
double m_dimm_fwhm
DIMM raw FWHM.
static void offloadThreadStart(tcsInterface *t)
Offload thread starter function.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTdump)
pcf::IndiProperty m_indiP_loopState
Property used to report the loop state.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTavgInt)
double m_mag2_fwhm
MAG2 raw FWHM.
double m_wxwdir
outside wind direction, degrees
double m_tseccell
Secondary mirror cell temperature, Celsius.
pcf::IndiProperty m_indiP_offlFavgInt
std::vector< std::string > parse_teldata(std::string &tdat)
pcf::IndiProperty m_indiP_offlFthresh
pcf::IndiProperty m_indiP_offlTTthresh
std::vector< std::vector< float > > m_offloadRequests
int sendPyrNudge(float x, float y, float z)
INDI_SETCALLBACK_DECL(tcsInterface, m_indiP_loopState)
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:229
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:264
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
Definition: stateCodes.hpp:38
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:44
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition: ttyErrors.cpp:15
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:206
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
INDI_SETCALLBACK_DEFN(MagAOXApp< _useINDI >, m_indiP_powerChannel)(const pcf
Definition: MagAOXApp.hpp:3195
Definition: dm.hpp:24
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
Definition: logPriority.hpp:43
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
#define NETSERIAL_E_NOERROR
Definition: netSerial.hpp:14
An input/output capable device.
Definition: ioDevice.hpp:27
int loadConfig(mx::app::appConfigurator &config)
Load the device section from an application configurator.
Definition: ioDevice.cpp:28
int appStartup()
Perform application startup steps specific to an ioDevice.
Definition: ioDevice.cpp:36
int setupConfig(mx::app::appConfigurator &config)
Setup an application configurator for the device section.
Definition: ioDevice.cpp:20
unsigned m_readTimeout
The read timeout [msec].
Definition: ioDevice.hpp:28
A device which saves telemetry.
Definition: telemeter.hpp:52
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:208
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:253
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:195
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:266
Software ERR log entry.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Log entry recording the build-time git state.
Manage a connectio to a serial device over a network.
Definition: netSerial.hpp:31
int serialOut(const char *buf, int len)
Definition: netSerial.cpp:72
int serialInString(char *buf, int len, int timeout, char terminator)
Definition: netSerial.cpp:127
int serialInit(const char *address, int port)
Definition: netSerial.cpp:34