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