API
 
Loading...
Searching...
No Matches
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
33namespace MagAOX
34{
35namespace app
36{
37
38/// The MagAO-X Clay Telescope TCS Interface
39/**
40 * \ingroup tcsInterface
41 */
42class 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
50protected:
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
174public:
175 /// Default c'tor.
176 tcsInterface();
177
178 /// D'tor, declared and defined for noexcept.
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
286
287 float m_pyrNudge_ang {45.0};
288 float m_pyrNudge_ang0 {0.0};
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};
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
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
422inline
423tcsInterface::tcsInterface() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
424{
425 return;
426}
427
428inline
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
484inline
485int 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
535
537
538 return 0;
539}
540
541inline
543{
544 loadConfigImpl(config);
545}
546
547inline
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
713
714
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
732 {
734 return -1;
735 }
736
739 {
741 return -1;
742 }
743
746 {
748 return -1;
749 }
750
751 createStandardIndiNumber( m_indiP_offlTTavgInt, "offlTT_avgInt", 0, 3600, 1, "%d");
755 {
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);
764 {
766 return -1;
767 }
768
769 createStandardIndiNumber( m_indiP_offlTTthresh, "offlTT_thresh", 0.0, 1.0, 0.0, "%0.2f");
773 {
775 return -1;
776 }
777
780 {
782 return -1;
783 }
784
787 {
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);
796 {
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);
805 {
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);
814 {
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 {
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
842inline
844{
845 if(state() == stateCodes::ERROR)
846 {
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
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;
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 {
891 lastrv = rv;
892 }
893 if( errno != lasterrno )
894 {
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
957inline
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
975inline
976int 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
992 statreq_nl += '\n';
993 stat = m_sock.serialOut(statreq_nl.c_str(), statreq_nl.length());
994
996 {
997 log<text_log>("Error sending status request: " + statreq, logPrio::LOG_ERROR);
998 response = "";
999 return 0;
1000 }
1001
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
1023inline
1024int 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
1039 command_nl += '\n';
1040 stat = m_sock.serialOut(command_nl.c_str(), command_nl.length());
1041
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
1067inline
1068std::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
1118inline
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 {
1154 return -1;
1155 }
1156
1157 try
1158 {
1159 x = std::stod(xstr);
1160 }
1161 catch(const std::exception & e)
1162 {
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 {
1197 return -1;
1198 }
1199
1200 try
1201 {
1202 m = sgn*std::stod(mstr);
1203 }
1204 catch(const std::exception & e)
1205 {
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 {
1226 return -1;
1227 }
1228
1229 try
1230 {
1231 s = sgn*std::stod(sstr);
1232 }
1233 catch(const std::exception & e)
1234 {
1236 return -1;
1237 }
1238
1239 return 0;
1240}
1241
1242inline
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
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
1284inline
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
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
1357inline
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
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];
1394 bit[0] = tdat[1].c_str()[1];
1396
1397 //parse the gdrmountmv string
1398 bit[0] = tdat[2].c_str()[0];
1400 bit[0] = tdat[2].c_str()[1];
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
1425inline
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
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
1513inline
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
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
1562inline
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
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
1610inline
1612{
1613 static int last_query = 0;
1614
1615
1616
1617 if(time(0) - last_query > m_seeingInterval)
1618 {
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
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
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
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
1755inline
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
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
2128inline
2133
2134inline
2136{
2137 recordTelPos(true);
2138 return 0;
2139}
2140
2141inline
2143{
2144 recordTelData(true);
2145 return 0;
2146}
2147
2148inline
2150{
2151 recordTelVane(true);
2152 return 0;
2153}
2154
2155inline
2157{
2158 recordTelEnv(true);
2159 return 0;
2160}
2161
2162inline
2164{
2165 recordTelCat(true);
2166 return 0;
2167}
2168
2169inline
2171{
2172 recordTelSee(true);
2173 return 0;
2174}
2175
2176inline
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 ||
2194 {
2196
2198 lastRA = m_telRA;
2199 lastDec = m_telDec;
2200 lastEl = m_telEl;
2201 lastHA = m_telHA;
2202 lastAM = m_telAM;
2204 }
2205
2206 return 0;
2207}
2208
2209inline
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 ||
2228 lastAz != m_telAz ||
2229 lastZd != m_telZd ||
2230 lastPA != m_telPA ||
2233 {
2235
2236 lastROI = m_telROI;
2241 lastAz = m_telAz;
2242 lastZd = m_telZd;
2243 lastPA = m_telPA;
2246 }
2247
2248 return 0;
2249}
2250
2251inline
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
2288 }
2289
2290 return 0;
2291}
2292
2293inline
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 ||
2311 lastWxwind != m_wxwind ||
2312 lastWxwdir != m_wxwdir ||
2313 lastTtruss != m_ttruss ||
2314 lastTcell != m_tcell ||
2318 {
2320
2331
2332 }
2333
2334 return 0;
2335}
2336
2337inline
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 {
2355
2362 }
2363
2364 return 0;
2365}
2366
2367inline
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 ||
2388 /*m_dimm_fwhm_corr != last_dimm_fwhm_corr || //ignore corrected dimm*/
2397
2398 {
2400
2404 //last_dimm_fwhm_corr = m_dimm_fwhm_corr;
2405
2410
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);
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);
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);
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);
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.
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
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);
2538 continue;
2539 }
2540
2541 //Check if loop paused
2542 if(m_loopState == 1)
2543 {
2544 sleep(1);
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 {
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;
2584 {
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 {
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;
2612 {
2614 sincelast_F = 0;
2615 }
2616
2617
2618
2619
2620
2622 }
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));
2641 m_offlTT_dump = false;
2642 }
2643 else
2644 {
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));
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
2685
2686 ip["dC1"] = tt_0;
2687 ip["dC2"] = tt_1;
2688
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
2701}
2702
2704{
2705 if(m_offlF_dump)
2706 {
2707 log<text_log>("[OFFL] Focus dumping: " + std::to_string(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));
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
2746}
2747
2748INDI_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
2789INDI_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
2815INDI_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
2832INDI_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
2850INDI_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
2896INDI_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
2925INDI_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
2942INDI_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
2959INDI_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
2976INDI_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
2994INDI_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
3017INDI_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
3034INDI_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
3051INDI_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
3068INDI_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.
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.
stateCodes::stateCodeT state()
Get the current state code.
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.
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.
int shutdown()
Get the value of the shutdown flag.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
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.
int stateLogged()
Updates and returns the value of m_stateLogged. Will be 0 on first call after a state change,...
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
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.
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.
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)
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
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.
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 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 INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#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...
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
@ ERROR
The application has encountered an error, from which it is recovering (with or without intervention)
@ CONNECTED
The application has connected to the device or service.
@ NOTCONNECTED
The application is not connected to the device or service.
std::string ttyErrorString(int ec)
Get a text explanation of a TTY_E_ error code.
Definition ttyErrors.cpp:15
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:28
#define INDI_OK
Definition indiUtils.hpp:29
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
const pcf::IndiProperty & ipRecv
Definition dm.hpp:24
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_WARNING
A condition has occurred which may become an error, but the process continues.
static constexpr logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
#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.
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
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)
int serialInit(const char *address, int port)
Definition netSerial.cpp:34