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