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 <atomic>
11#include <cmath>
12#include <iostream>
13
14#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
15#include "../../magaox_git_version.h"
16
17#include "../../libMagAOX/app/dev/telemeter.hpp"
18
19// #define LOG_TCS_STATUS
20
21/** \defgroup tcsInterface
22 * \brief The MagAO-X application to do interface with the Clay TCS
23 *
24 * <a href="../handbook/operating/software/apps/tcsInterface.html">Application Documentation</a>
25 *
26 * \ingroup apps
27 *
28 */
29
30/** \defgroup tcsInterface_files
31 * \ingroup tcsInterface
32 */
33
34namespace MagAOX
35{
36namespace app
37{
38
39/// The MagAO-X Clay Telescope TCS Interface
40/**
41 * \ingroup tcsInterface
42 */
43class tcsInterface : public MagAOXApp<true>, public dev::ioDevice, public dev::telemeter<tcsInterface>
44{
45
46 // Give the test harness access.
47 friend class tcsInterface_test;
48
49 friend class dev::telemeter<tcsInterface>;
50
51 protected:
52 /** \name lab mode
53 * @{
54 */
55
56 /// Tracks whether the app is operating in lab mode rather than on-sky mode.
57 bool m_labMode{ true };
58
59 pcf::IndiProperty m_indiP_labMode;
61
62 ///@}
63
64 /** \name TCS Networking
65 *@{
66 */
67
68 std::string m_deviceAddr{ "localhost" }; ///< The IP address or resolvable name of the TCS.
69 int m_devicePort{ 5811 }; ///< The IP port for TCS communications. Should be the command port. Default is 5811
71
72 /// Mutex for locking TCS communications.
73 std::mutex m_tcsMutex;
74
76
77 ///@}
78
79 // Telescope time:
80 double m_telST{ 0 };
81
82 pcf::IndiProperty m_indiP_teltime;
83
84 // Telescope position:
85 double m_telEpoch{ 0 };
86 double m_telRA{ 0 };
87 double m_telDec{ 0 };
88 double m_telEl{ 0 };
89 double m_telHA{ 0 };
90 double m_telAM{ 0 };
91 double m_telRotOff{ 0 };
92
93 pcf::IndiProperty m_indiP_telpos;
94
95 /// \name Telescope Data
96 /**@{
97 */
98 int m_telROI{ 0 }; ///< The rotator of interest
99 int m_telTracking{ 0 }; ///< tracking state
100 int m_telGuiding{ 0 }; ///< guider moving state
101 int m_telSlewing{ 0 }; ///< slewing state
102 int m_telGuiderMoving{ 0 }; ///< guider moving state
103 double m_telAz{ 0 }; ///< azimuth
104 double m_telZd{ 0 }; ///< zenith distance
105 double m_telPA{ 0 }; ///< parallactic angle
106 double m_telDomeAz{ 0 }; ///< dome azimuth
107 int m_telDomeStat{ 0 }; ///< dome status
108
109 pcf::IndiProperty m_indiP_teldata;
110 ///@}
111
112 /// \name Telescope Catalog Information
113 /**@{
114 */
115 double m_catRA{ 0 }; ///< Catalog right ascension [degrees]
116 double m_catDec{ 0 }; ///< Catalog declination [degrees]
117 double m_catEp{ 0 }; ///< Catalog epoch
118 double m_catRo{ 0 }; ///< Catalog rotator offset
119 std::string m_catRm; ///< Catalog rotator mode
120 std::string m_catObj; ///< Catalog object name
121
122 pcf::IndiProperty m_indiP_catalog; ///< INDI Property for the catalog text information
123 pcf::IndiProperty m_indiP_catdata; ///< INDI Property for the catalog data
124 ///@}
125
126 // Telescope Vane-End positions
127 double m_telSecZ{ 0 };
128 double m_telEncZ{ 0 };
129 double m_telSecX{ 0 };
130 double m_telEncX{ 0 };
131 double m_telSecY{ 0 };
132 double m_telEncY{ 0 };
133 double m_telSecH{ 0 };
134 double m_telEncH{ 0 };
135 double m_telSecV{ 0 };
136 double m_telEncV{ 0 };
137
138 pcf::IndiProperty m_indiP_vaneend; ///< INDI Property for the vane end positions
139
140 // Environment
141 double m_wxtemp{ 0 }; ///< Outside temperature, Celsius
142 double m_wxpres{ 0 }; ///< Outside pressue, millibars
143 double m_wxhumid{ 0 }; ///< Outside humidity, percent
144 double m_wxwind{ 0 }; ///< outside wind intensity, mph
145 double m_wxwdir{ 0 }; ///< outside wind direction, degrees
146 double m_ttruss{ 0 }; ///< Telescope truss temperature, Celsius
147 double m_tcell{ 0 }; ///< Primary mirror cell temperature, Celsius
148 double m_tseccell{ 0 }; ///< Secondary mirror cell temperature, Celsius
149 double m_tambient{ 0 }; ///< Dome air temperature, Celsius
150 double m_wxdewpoint{ 0 }; ///< Dew point from weather station
151
152 pcf::IndiProperty m_indiP_env; ///< INDI Property for environment
153
154 // Seeing
155 double m_dimm_el{ 0 }; ///< DIMM elevation at time of seeing measurement
156 double m_dimm_fwhm_corr{ 0 }; ///< DIMM elevation corrected FWHM
157 int m_dimm_time{ 0 }; ///< Seconds since midnight of DIMM measurement.
158
159 double m_mag1_fwhm_corr{ 0 }; ///< MAG1 elevation corrected FWHM
160 int m_mag1_time{ 0 }; ///< Seconds since midnight of MAG1 measurement.
161
162 double m_mag2_fwhm_corr{ 0 }; ///< MAG2 elevation corrected FWHM
163 int m_mag2_time{ 0 }; ///< Seconds since midnight of MAG2 measurement.
164
165 pcf::IndiProperty m_indiP_seeing; ///< INDI Property for seeing
166
167 public:
168 /// Default c'tor.
169 tcsInterface();
170
171 /// D'tor, declared and defined for noexcept.
173 {
174 }
175
176 virtual void setupConfig();
177
178 /// Implementation of loadConfig logic, separated for testing.
179 /** This is called by loadConfig().
180 */
181 int loadConfigImpl(
182 mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
183
184 virtual void loadConfig();
185
186 /// Startup function
187 /**
188 *
189 */
190 virtual int appStartup();
191
192 /// Implementation of the FSM for tcsInterface.
193 /**
194 * \returns 0 on no critical error
195 * \returns -1 on an error requiring shutdown
196 */
197 virtual int appLogic();
198
199 /// Shutdown the app.
200 /**
201 *
202 */
203 virtual int appShutdown();
204
205 int getMagTelStatus( std::string &response, const std::string &statreq );
206
207 int sendMagTelCommand( const std::string &command, int timeout );
208
209 int parse_xms( double &x, double &m, double &s, const std::string &xmsstr );
210
211 std::vector<std::string> parse_teldata( std::string &tdat );
212
213 // The "dump" commands:
214
215 int getTelTime();
216 int getTelPos();
217 int getTelData();
218 int getCatData();
219 int getVaneData();
220 int getEnvData();
221
222 /// Record bad seeing measurements on errors in getSeeing().
223 int badSeeing();
224
225 /// Query, parse, and record seeing measurements
226 int getSeeing();
227
228 int updateINDI();
229
230 /** \name Telemeter Interface
231 * @{
232 */
233 /// Check whether any telemetry records are due.
234 int checkRecordTimes();
235
236 /// Record telescope-position telemetry on a forced telemeter update.
237 int recordTelem( const telem_telpos * );
238
239 /// Record telescope-status telemetry on a forced telemeter update.
240 int recordTelem( const telem_teldata * );
241
242 /// Record vane-position telemetry on a forced telemeter update.
243 int recordTelem( const telem_telvane * );
244
245 /// Record telescope-environment telemetry on a forced telemeter update.
246 int recordTelem( const telem_telenv * );
247
248 /// Record catalog telemetry on a forced telemeter update.
249 int recordTelem( const telem_telcat * );
250
251 /// Record seeing telemetry on a forced telemeter update.
252 int recordTelem( const telem_telsee * );
253
254 /// Record tip/tilt offload-control telemetry on a forced telemeter update.
255 int recordTelem( const telem_tcsi_tiptilt * );
256
257 /// Record focus offload-control telemetry on a forced telemeter update.
258 int recordTelem( const telem_tcsi_focus * );
259
260 /// Record lab-mode telemetry on a forced telemeter update.
261 int recordTelem( const telem_tcsi_labmode * );
262
263 /// Record telescope-position telemetry when values change.
264 int recordTelPos( bool force = false );
265
266 /// Record telescope-status telemetry when values change.
267 int recordTelData( bool force = false );
268
269 /// Record vane-position telemetry when values change.
270 int recordTelVane( bool force = false );
271
272 /// Record telescope-environment telemetry when values change.
273 int recordTelEnv( bool force = false );
274
275 /// Record catalog telemetry when values change.
276 int recordTelCat( bool force = false );
277
278 /// Record seeing telemetry when values change.
279 int recordTelSee( bool force = false );
280
281 /// Record tip/tilt offload-control telemetry when values change.
282 int recordTcsiTipTilt( bool force = false );
283
284 /// Record focus offload-control telemetry when values change.
285 int recordTcsiFocus( bool force = false );
286
287 /// Record lab-mode telemetry when values change.
288 int recordTcsiLabMode( bool force = false );
289
290 ///@}
291
292 /// Tracks the current loop state shared between the INDI callback and the offload thread.
293 std::atomic<int> m_loopState{ 0 };
294 pcf::IndiProperty m_indiP_loopState; ///< Property used to report the loop state
295
297
298 /** \name Pyramid Nudging and Acquisition
299 * Handling of nudges on pyramid tip.
300 * @{
301 */
302
303 // The Pyramid to AEG control matrix
304 float m_pyrNudge_C_00{ 1 };
305 float m_pyrNudge_C_01{ 0 };
306 float m_pyrNudge_C_10{ 0 };
307 float m_pyrNudge_C_11{ 1 };
309
310 float m_pyrNudge_ang{ 45.0 };
311 float m_pyrNudge_ang0{ 0.0 };
312 float m_pyrNudge_parity{ -1 };
313
314 int sendPyrNudge( float x, float y, float z );
315
316 pcf::IndiProperty m_indiP_pyrNudge; ///< Property used to request a pyramid nudge
318
319 int m_acqZdSign{ -1 };
320 float m_acqAz0{ 18.5 };
321 float m_acqAzOff{ 0 };
322 float m_acqEl0{ 10 };
323 float m_acqElOff{ 0 };
324 float m_acqFocus{ 1400 };
325
326 int acquireFromGuider();
327
328 pcf::IndiProperty m_indiP_acqFromGuider; ///< Property used to request a pyramid nudge
330
331 ///@}
332
333 /** \name Woofer Offloading
334 * Handling of offloads from the average woofer shape to the telescope
335 * @{
336 */
337
338 bool m_offloadThreadInit{ true }; ///< Initialization flag for the offload thread.
339
340 pid_t m_offloadThreadID{ 0 }; ///< Offload thread pid.
341
342 pcf::IndiProperty m_offloadThreadProp; ///< Offload thread INDI property.
343
344 std::thread m_offloadThread; ///< The offloading thread.
345
346 /// Offload thread starter function
347 static void offloadThreadStart( tcsInterface *t /**< [in] pointer to this */ );
348
349 /// Offload thread function
350 /** Runs until m_shutdown is true.
351 */
352 void offloadThreadExec();
353
354 /// Apply tip/tilt offload controls to an averaged request vector.
355 int doTToffload( float TT_0, /**< [in] averaged tip request */
356 float TT_1 /**< [in] averaged tilt request */
357 );
358
359 /// Send a tip/tilt offload command to the selected target.
360 int sendTToffload( float TT_0, /**< [in] tip offload command */
361 float TT_1 /**< [in] tilt offload command */
362 );
363
364 /// Apply focus offload controls to an averaged request value.
365 int doFoffload( float F_0 /**< [in] averaged focus request */
366 );
367
368 /// Send a focus offload command to the telescope.
369 int sendFoffload( float F_0 /**< [in] focus offload command */
370 );
371
372 pcf::IndiProperty
373 m_indiP_offloadCoeffs; ///< Property used to report the latest woofer modal coefficients for offloading
374
376
377 /// Protects shared offload control parameters accessed by callbacks and the offload thread.
379
380 /// Protects the offload request ring and its queue-state indices across producer and consumer threads.
381 std::mutex m_offloadMutex;
382
383 /// Ring buffer of modal offload requests indexed by mode and request slot.
384 std::vector<std::vector<float>> m_offloadRequests;
385
386 /// Index of the oldest valid request in the offload ring.
387 size_t m_firstRequest{ 0 };
388
389 /// Index of the newest valid request in the offload ring.
390 size_t m_lastRequest{ std::numeric_limits<size_t>::max() };
391
392 /// Number of valid requests currently stored in the offload ring.
393 size_t m_nRequests{ 0 };
394
395 /// Request count last processed by the offload thread.
396 size_t m_last_nRequests{ 0 };
397
398 // The TT control matrix -- LAb
399 float m_lab_offlTT_C_00{ 0.17 };
400 float m_lab_offlTT_C_01{ 1.03 };
401 float m_lab_offlTT_C_10{ -1.03 };
402 float m_lab_offlTT_C_11{ 0.48 };
403
404 // The TT control matrix -- Telescope
405 float m_offlTT_C_00{ -0.5 };
406 float m_offlTT_C_01{ 0 };
407 float m_offlTT_C_10{ 0 };
408 float m_offlTT_C_11{ -0.25 };
409
410 /// Tracks whether tip/tilt offloading is currently enabled.
411 bool m_offlTT_enabled{ false };
412 bool m_offlTT_dump{ false };
413 /// The number of recent requests included in each tip/tilt offload average.
414 float m_offlTT_avgInt{ 1.0 };
415 /// The operator-set gain applied to tip/tilt offload requests.
416 float m_offlTT_gain{ 0.1 };
417 /// The deadband threshold applied to tip/tilt offload requests.
418 float m_offlTT_thresh{ 0.1 };
419
420 pcf::IndiProperty m_indiP_offlTTenable;
422
423 pcf::IndiProperty m_indiP_offlTTdump;
425
426 pcf::IndiProperty m_indiP_offlTTavgInt;
428
429 pcf::IndiProperty m_indiP_offlTTgain;
431
432 pcf::IndiProperty m_indiP_offlTTthresh;
434
435 // The Focus control constant
436 float m_offlCFocus_00{ 1 };
437
438 /// Tracks whether focus offloading is currently enabled.
439 bool m_offlF_enabled{ false };
440 bool m_offlF_dump{ false };
441 /// The number of recent requests included in each focus offload average.
442 float m_offlF_avgInt{ 1.0 };
443 /// The operator-set gain applied to focus offload requests.
444 float m_offlF_gain{ 0.1 };
445 /// The deadband threshold applied to focus offload requests.
446 float m_offlF_thresh{ 0.1 };
447
448 pcf::IndiProperty m_indiP_offlFenable;
450
451 pcf::IndiProperty m_indiP_offlFdump;
453
454 pcf::IndiProperty m_indiP_offlFavgInt;
456
457 pcf::IndiProperty m_indiP_offlFgain;
459
460 pcf::IndiProperty m_indiP_offlFthresh;
462
463 float m_offlCComa_00{ 1 };
464 float m_offlCComa_01{ 0 };
465 float m_offlCComa_10{ 1 };
466 float m_offlCComa_11{ 0 };
467
468 ///@}
469};
470
471inline tcsInterface::tcsInterface() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
472{
473 return;
474}
475
477{
478 config.add( "labMode",
479 "",
480 "labMode",
481 argType::Required,
482 "",
483 "labMode",
484 false,
485 "bool",
486 "Flag to enable lab mode. Default is true." );
487
488 config.add( "pyrNudger.C_00",
489 "",
490 "pyrNudger.C_00",
491 argType::Required,
492 "pyrNudger",
493 "C_00",
494 false,
495 "float",
496 "Pyramid to AEG control matrix [0,0] of a 2x2 matrix" );
497 config.add( "pyrNudger.C_01",
498 "",
499 "pyrNudger.C_01",
500 argType::Required,
501 "pyrNudger",
502 "C_01",
503 false,
504 "float",
505 "Pyramid to AEG control matrix [0,1] of a 2x2 matrix " );
506 config.add( "pyrNudger.C_10",
507 "",
508 "pyrNudger.C_10",
509 argType::Required,
510 "pyrNudger",
511 "C_10",
512 false,
513 "float",
514 "Pyramid to AEG control matrix [1,0] of a 2x2 matrix " );
515 config.add( "pyrNudger.C_11",
516 "",
517 "pyrNudger.C_11",
518 argType::Required,
519 "pyrNudger",
520 "C_11",
521 false,
522 "float",
523 "Pyramid to AEG control matrix [1,1] of a 2x2 matrix " );
524
525 config.add( "pyrNudger.ang", "", "pyrNudger.ang", argType::Required, "pyrNudger", "ang", false, "float", "" );
526 config.add( "pyrNudger.ang0", "", "pyrNudger.ang0", argType::Required, "pyrNudger0", "ang0", false, "float", "" );
527 config.add(
528 "pyrNudger.parity", "", "pyrNudger.parity", argType::Required, "pyrNudger", "parity", false, "float", "" );
529
530 config.add( "pyrNudger.F_sign",
531 "",
532 "pyrNudger.F_sign",
533 argType::Required,
534 "pyrNudger",
535 "F_sign",
536 false,
537 "int",
538 "Pyramid to AEG control matrix [1,1] of a 2x2 matrix " );
539
540 config.add( "acqFromGuider.zdSign",
541 "",
542 "acqFromGuider.zdSign",
543 argType::Required,
544 "acqFromGuider",
545 "zdSign",
546 false,
547 "int",
548 "Sign of the Zd to rotation angle, +1 or -1, -1 default" );
549 config.add( "acqFromGuider.az0",
550 "",
551 "acqFromGuider.az0",
552 argType::Required,
553 "acqFromGuider",
554 "az0",
555 false,
556 "float",
557 "az component of acquisition vector a 0 zd." );
558 config.add( "acqFromGuider.azoff",
559 "",
560 "acqFromGuider.azoff",
561 argType::Required,
562 "acqFromGuider",
563 "azoff",
564 false,
565 "float",
566 "static offset to az component of acquisition vector" );
567 config.add( "acqFromGuider.el0",
568 "",
569 "acqFromGuider.el0",
570 argType::Required,
571 "acqFromGuider",
572 "el0",
573 false,
574 "float",
575 "el component of acquisition vector a 0 zd." );
576 config.add( "acqFromGuider.eloff",
577 "",
578 "acqFromGuider.eloff",
579 argType::Required,
580 "acqFromGuider",
581 "eloff",
582 false,
583 "float",
584 "static offset to el component of acquisition vector" );
585 config.add( "acqFromGuider.focus",
586 "",
587 "acqFromGuider.focus",
588 argType::Required,
589 "acqFromGuider",
590 "focus",
591 false,
592 "float",
593 "static offset for focus acquisition" );
594
595 config.add( "offload.TT_avgInt",
596 "",
597 "offload.TT_avgInt",
598 argType::Required,
599 "offload",
600 "TT_avgInt",
601 false,
602 "float",
603 "Woofer to Telescope T/T offload averaging interval [sec] " );
604 config.add( "offload.TT_gain",
605 "",
606 "offload.TT_gain",
607 argType::Required,
608 "offload",
609 "TT_gain",
610 false,
611 "float",
612 "Woofer to Telescope T/T offload gain" );
613 config.add( "offload.TT_thresh",
614 "",
615 "offload.TT_thresh",
616 argType::Required,
617 "offload",
618 "TT_thresh",
619 false,
620 "float",
621 "Woofer to Telescope T/T offload threshold" );
622
623 config.add( "offload.lab_TT_C_00",
624 "",
625 "offload.lab_TT_C_00",
626 argType::Required,
627 "offload",
628 "lab_TT_C_00",
629 false,
630 "float",
631 "Woofer to TTM T/T offload control matrix [0,0] of a 2x2 matrix" );
632 config.add( "offload.lab_TT_C_01",
633 "",
634 "offload.lab_TT_C_01",
635 argType::Required,
636 "offload",
637 "lab_TT_C_01",
638 false,
639 "float",
640 "Woofer to TTM T/T offload control matrix [0,1] of a 2x2 matrix " );
641 config.add( "offload.lab_TT_C_10",
642 "",
643 "offload.lab_TT_C_10",
644 argType::Required,
645 "offload",
646 "lab_TT_C_10",
647 false,
648 "float",
649 "Woofer to TTM T/T offload control matrix [1,0] of a 2x2 matrix " );
650 config.add( "offload.lab_TT_C_11",
651 "",
652 "offload.lab_TT_C_11",
653 argType::Required,
654 "offload",
655 "lab_TT_C_11",
656 false,
657 "float",
658 "Woofer to TTM T/T offload control matrix [1,1] of a 2x2 matrix " );
659
660 config.add( "offload.TT_C_00",
661 "",
662 "offload.TT_C_00",
663 argType::Required,
664 "offload",
665 "TT_C_00",
666 false,
667 "float",
668 "Woofer to Telescope T/T offload control matrix [0,0] of a 2x2 matrix" );
669 config.add( "offload.TT_C_01",
670 "",
671 "offload.TT_C_01",
672 argType::Required,
673 "offload",
674 "TT_C_01",
675 false,
676 "float",
677 "Woofer to Telescope T/T offload control matrix [0,1] of a 2x2 matrix " );
678 config.add( "offload.TT_C_10",
679 "",
680 "offload.TT_C_10",
681 argType::Required,
682 "offload",
683 "TT_C_10",
684 false,
685 "float",
686 "Woofer to Telescope T/T offload control matrix [1,0] of a 2x2 matrix " );
687 config.add( "offload.TT_C_11",
688 "",
689 "offload.TT_C_11",
690 argType::Required,
691 "offload",
692 "TT_C_11",
693 false,
694 "float",
695 "Woofer to Telescope T/T offload control matrix [1,1] of a 2x2 matrix " );
696
697 config.add( "offload.F_avgInt",
698 "",
699 "offload.F_avgInt",
700 argType::Required,
701 "offload",
702 "F_avgInt",
703 false,
704 "float",
705 "Woofer to Telescope Focus offload averaging interval [sec] " );
706 config.add( "offload.F_gain",
707 "",
708 "offload.F_gain",
709 argType::Required,
710 "offload",
711 "F_gain",
712 false,
713 "float",
714 "Woofer to Telescope Focus offload gain" );
715 config.add( "offload.F_thresh",
716 "",
717 "offload.F_thresh",
718 argType::Required,
719 "offload",
720 "F_thresh",
721 false,
722 "float",
723 "Woofer to Telescope Focus offload threshold" );
724
725 config.add( "offload.CFocus00",
726 "",
727 "offload.CFocus00",
728 argType::Required,
729 "offload",
730 "CFocus00",
731 false,
732 "float",
733 "Woofer to Telescope Focus offload control scale factor." );
734
735 config.add( "offload.CComa00",
736 "",
737 "offload.CComa00",
738 argType::Required,
739 "offload",
740 "CComa00",
741 false,
742 "float",
743 "Woofer to Telescope Coma offload control matrix [0,0] of a 2x2 matrix" );
744 config.add( "offload.CComa01",
745 "",
746 "offload.CComa01",
747 argType::Required,
748 "offload",
749 "CComa01",
750 false,
751 "float",
752 "Woofer to Telescope Coma offload control matrix [0,1] of a 2x2 matrix " );
753 config.add( "offload.CComa10",
754 "",
755 "offload.CComa10",
756 argType::Required,
757 "offload",
758 "CComa10",
759 false,
760 "float",
761 "Woofer to Telescope Coma offload control matrix [1,0] of a 2x2 matrix " );
762 config.add( "offload.CComa11",
763 "",
764 "offload.CComa11",
765 argType::Required,
766 "offload",
767 "CComa11",
768 false,
769 "float",
770 "Woofer to Telescope Coma offload control matrix [1,1] of a 2x2 matrix " );
771
772 config.add( "device.address",
773 "",
774 "device.address",
775 argType::Required,
776 "device",
777 "address",
778 false,
779 "string",
780 "The IP address or resolvable name of the TCS." );
781 config.add( "device.port",
782 "",
783 "device.port",
784 argType::Required,
785 "device",
786 "port",
787 false,
788 "int",
789 "The IP port for TCS communications. Should be the command port. Default is 5811." );
790
793}
794
795inline int tcsInterface::loadConfigImpl( mx::app::appConfigurator &_config )
796{
797
798 _config( m_labMode, "labMode" );
799
800 _config( m_pyrNudge_C_00, "pyrNudger.C_00" );
801 _config( m_pyrNudge_C_01, "pyrNudger.C_01" );
802 _config( m_pyrNudge_C_10, "pyrNudger.C_10" );
803 _config( m_pyrNudge_C_11, "pyrNudger.C_11" );
804
805 _config( m_pyrNudge_ang, "pyrNudger.ang" );
806 _config( m_pyrNudge_ang0, "pyrNudger.ang0" );
807 _config( m_pyrNudge_parity, "pyrNudger.parity" );
808
809 _config( m_acqZdSign, "acqFromGuider.zdSign" );
810 _config( m_acqAz0, "acqFromGuider.az0" );
811 _config( m_acqAzOff, "acqFromGuider.azoff" );
812 _config( m_acqEl0, "acqFromGuider.el0" );
813 _config( m_acqElOff, "acqFromGuider.eloff" );
814 _config( m_acqFocus, "acqFromGuider.focus" );
815
816 _config( m_offlTT_avgInt, "offload.TT_avgInt" );
817 _config( m_offlTT_gain, "offload.TT_gain" );
818 _config( m_offlTT_thresh, "offload.TT_thresh" );
819
820 _config( m_lab_offlTT_C_00, "offload.lab_TT_C_00" );
821 _config( m_lab_offlTT_C_01, "offload.lab_TT_C_01" );
822 _config( m_lab_offlTT_C_10, "offload.lab_TT_C_10" );
823 _config( m_lab_offlTT_C_11, "offload.lab_TT_C_11" );
824
825 _config( m_offlTT_C_00, "offload.TT_C_00" );
826 _config( m_offlTT_C_01, "offload.TT_C_01" );
827 _config( m_offlTT_C_10, "offload.TT_C_10" );
828 _config( m_offlTT_C_11, "offload.TT_C_11" );
829
830 _config( m_offlF_avgInt, "offload.F_avgInt" );
831 _config( m_offlF_gain, "offload.F_gain" );
832 _config( m_offlF_thresh, "offload.F_thresh" );
833
834 _config( m_offlCFocus_00, "offload.CFocus00" );
835
836 _config( m_offlCComa_00, "offload.CComa00" );
837 _config( m_offlCComa_01, "offload.CComa01" );
838 _config( m_offlCComa_10, "offload.CComa10" );
839 _config( m_offlCComa_11, "offload.CComa11" );
840
841 _config( m_deviceAddr, "device.address" );
842 _config( m_devicePort, "device.port" );
843
845
847
848 return 0;
849}
850
852{
853 loadConfigImpl( config );
854}
855
857{
858 bool labMode;
859 float offlTTAvgInt;
860 float offlTTGain;
861 float offlTTThresh;
862 float offlFAvgInt;
863 float offlFGain;
864 float offlFThresh;
865
866 { // mutex scope
867 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
868 labMode = m_labMode;
875 }
876
878 if( labMode )
879 {
880 m_indiP_labMode["toggle"].setSwitchState( pcf::IndiElement::On );
881 log<text_log>( "lab mode ON", logPrio::LOG_NOTICE );
882 }
883 else
884 {
885 m_indiP_labMode["toggle"].setSwitchState( pcf::IndiElement::Off );
886 log<text_log>( "lab mode OFF", logPrio::LOG_NOTICE );
887 }
888
889 updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
890
891 createROIndiNumber( m_indiP_teltime, "teltime", "Telscope Time", "TCS" );
892 indi::addNumberElement<double>(
893 m_indiP_teltime, "sidereal_time", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
894 m_indiP_teltime["sidereal_time"] = m_telST;
896
897 createROIndiNumber( m_indiP_telpos, "telpos", "Telscope Position", "TCS" );
898 indi::addNumberElement<double>( m_indiP_telpos, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
899 m_indiP_telpos["epoch"] = m_telEpoch;
900 indi::addNumberElement<double>( m_indiP_telpos, "ra", 0, 360, 0, "%0.6f" );
901 m_indiP_telpos["ra"] = m_telRA;
902 indi::addNumberElement<double>( m_indiP_telpos, "dec", -90, 90, 0, "%0.6f" );
903 m_indiP_telpos["dec"] = m_telDec;
904 indi::addNumberElement<double>( m_indiP_telpos, "el", 0, 90, 0, "%0.6f" );
905 m_indiP_telpos["el"] = m_telEl;
906 indi::addNumberElement<double>( m_indiP_telpos, "ha", -180, 160, 0, "%0.6f" );
907 m_indiP_telpos["ha"] = m_telHA;
908 indi::addNumberElement<double>( m_indiP_telpos, "am", 0, 4, 0, "%0.2f" );
909 m_indiP_telpos["am"] = m_telAM;
910 indi::addNumberElement<double>( m_indiP_telpos, "rotoff", 0, 360, 0, "%0.6f" );
911 m_indiP_telpos["rotoff"] = m_telRotOff;
912
914
915 createROIndiNumber( m_indiP_teldata, "teldata", "Telscope Data", "TCS" );
916 indi::addNumberElement<int>( m_indiP_teldata, "roi", 0, 10, 1, "%d" );
917 m_indiP_teldata["roi"] = m_telROI;
918 indi::addNumberElement<int>( m_indiP_teldata, "tracking", 0, 1, 1, "%d" );
919 m_indiP_teldata["tracking"] = m_telTracking;
920 indi::addNumberElement<int>( m_indiP_teldata, "guiding", 0, 1, 1, "%d" );
921 m_indiP_teldata["guiding"] = m_telGuiding;
922 indi::addNumberElement<int>( m_indiP_teldata, "slewing", 0, 1, 1, "%d" );
923 m_indiP_teldata["slewing"] = m_telSlewing;
924 indi::addNumberElement<int>( m_indiP_teldata, "guider_moving", 0, 1, 1, "%d" );
925 m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
926 indi::addNumberElement<double>( m_indiP_teldata, "az", 0, 360, 0, "%0.6f" );
927 m_indiP_teldata["az"] = m_telAz;
928 indi::addNumberElement<double>( m_indiP_teldata, "zd", 0, 90, 0, "%0.6f" );
929 m_indiP_teldata["zd"] = m_telZd;
930 indi::addNumberElement<double>( m_indiP_teldata, "pa", 0, 360, 0, "%0.6f" );
931 m_indiP_teldata["pa"] = m_telPA;
932 indi::addNumberElement<double>( m_indiP_teldata, "dome_az", 0, 360, 0, "%0.6f" );
933 m_indiP_teldata["dome_az"] = m_telDomeAz;
934 indi::addNumberElement<int>( m_indiP_teldata, "dome_stat", 0, 1, 1, "%d" );
935 m_indiP_teldata["dome_stat"] = m_telDomeStat;
936
938
939 createROIndiText( m_indiP_catalog, "catalog", "object", "Catalog Entry", "TCS", "Object Name" );
940 m_indiP_catalog.add( pcf::IndiElement( "rotmode" ) );
941 m_indiP_catalog["rotmode"].setLabel( "Rotator Mode" );
942
944
945 createROIndiNumber( m_indiP_catdata, "catdata", "Catalog Entry Data", "TCS" );
946 indi::addNumberElement<double>( m_indiP_catdata, "ra", 0, 360, 0, "%0.6f" );
947 m_indiP_catdata["ra"] = m_catRA;
948 indi::addNumberElement<double>( m_indiP_catdata, "dec", -90, 90, 0, "%0.6f" );
949 m_indiP_catdata["dec"] = m_catDec;
950 indi::addNumberElement<double>( m_indiP_catdata, "epoch", 0, std::numeric_limits<double>::max(), 0, "%0.6f" );
951 m_indiP_catdata["epoch"] = m_catEp;
952 indi::addNumberElement<double>( m_indiP_catdata, "rotoff", 0, 360, 0, "%0.6f" );
953 m_indiP_catdata["rotoff"] = m_catRo;
954
956
957 createROIndiNumber( m_indiP_vaneend, "vaneend", "Vane End Data", "TCS" );
958 indi::addNumberElement<double>( m_indiP_vaneend,
959 "secz",
960 std::numeric_limits<double>::lowest(),
961 std::numeric_limits<double>::max(),
962 0,
963 "%0.6f" );
964 m_indiP_vaneend["secz"] = m_telSecZ;
965 indi::addNumberElement<double>( m_indiP_vaneend,
966 "encz",
967 std::numeric_limits<double>::lowest(),
968 std::numeric_limits<double>::max(),
969 0,
970 "%0.6f" );
971 m_indiP_vaneend["encz"] = m_telEncZ;
972 indi::addNumberElement<double>( m_indiP_vaneend,
973 "secx",
974 std::numeric_limits<double>::lowest(),
975 std::numeric_limits<double>::max(),
976 0,
977 "%0.6f" );
978 m_indiP_vaneend["secx"] = m_telSecX;
979 indi::addNumberElement<double>( m_indiP_vaneend,
980 "encx",
981 std::numeric_limits<double>::lowest(),
982 std::numeric_limits<double>::max(),
983 0,
984 "%0.6f" );
985 m_indiP_vaneend["encx"] = m_telEncX;
986 indi::addNumberElement<double>( m_indiP_vaneend,
987 "secy",
988 std::numeric_limits<double>::lowest(),
989 std::numeric_limits<double>::max(),
990 0,
991 "%0.6f" );
992 m_indiP_vaneend["secy"] = m_telSecY;
993 indi::addNumberElement<double>( m_indiP_vaneend,
994 "ency",
995 std::numeric_limits<double>::lowest(),
996 std::numeric_limits<double>::max(),
997 0,
998 "%0.6f" );
999 m_indiP_vaneend["ency"] = m_telEncY;
1000 indi::addNumberElement<double>( m_indiP_vaneend,
1001 "sech",
1002 std::numeric_limits<double>::lowest(),
1003 std::numeric_limits<double>::max(),
1004 0,
1005 "%0.6f" );
1006 m_indiP_vaneend["sech"] = m_telSecH;
1007 indi::addNumberElement<double>( m_indiP_vaneend,
1008 "ench",
1009 std::numeric_limits<double>::lowest(),
1010 std::numeric_limits<double>::max(),
1011 0,
1012 "%0.6f" );
1013 m_indiP_vaneend["ench"] = m_telEncH;
1014 indi::addNumberElement<double>( m_indiP_vaneend,
1015 "secv",
1016 std::numeric_limits<double>::lowest(),
1017 std::numeric_limits<double>::max(),
1018 0,
1019 "%0.6f" );
1020 m_indiP_vaneend["secv"] = m_telSecV;
1021 indi::addNumberElement<double>( m_indiP_vaneend,
1022 "encv",
1023 std::numeric_limits<double>::lowest(),
1024 std::numeric_limits<double>::max(),
1025 0,
1026 "%0.6f" );
1027 m_indiP_vaneend["encv"] = m_telEncV;
1028
1030
1031 createROIndiNumber( m_indiP_env, "environment", "Environment Data", "TCS" );
1032 indi::addNumberElement<double>( m_indiP_env,
1033 "temp-out",
1034 std::numeric_limits<double>::lowest(),
1035 std::numeric_limits<double>::max(),
1036 0,
1037 "%0.2f" );
1038 m_indiP_env["temp-out"] = m_wxtemp;
1039 indi::addNumberElement<double>( m_indiP_env,
1040 "pressure",
1041 std::numeric_limits<double>::lowest(),
1042 std::numeric_limits<double>::max(),
1043 0,
1044 "%0.2f" );
1045 m_indiP_env["pressure"] = m_wxpres;
1046 indi::addNumberElement<double>( m_indiP_env,
1047 "humidity",
1048 std::numeric_limits<double>::lowest(),
1049 std::numeric_limits<double>::max(),
1050 0,
1051 "%0.2f" );
1052 m_indiP_env["humidity"] = m_wxhumid;
1053 indi::addNumberElement<double>(
1054 m_indiP_env, "wind", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f" );
1055 m_indiP_env["wind"] = m_wxwind;
1056 indi::addNumberElement<double>(
1057 m_indiP_env, "winddir", std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max(), 0, "%0.2f" );
1058 m_indiP_env["winddir"] = m_wxwdir;
1059 indi::addNumberElement<double>( m_indiP_env,
1060 "temp-truss",
1061 std::numeric_limits<double>::lowest(),
1062 std::numeric_limits<double>::max(),
1063 0,
1064 "%0.2f" );
1065 m_indiP_env["temp-truss"] = m_ttruss;
1066 indi::addNumberElement<double>( m_indiP_env,
1067 "temp-cell",
1068 std::numeric_limits<double>::lowest(),
1069 std::numeric_limits<double>::max(),
1070 0,
1071 "%0.2f" );
1072 m_indiP_env["temp-cell"] = m_tcell;
1073 indi::addNumberElement<double>( m_indiP_env,
1074 "temp-seccell",
1075 std::numeric_limits<double>::lowest(),
1076 std::numeric_limits<double>::max(),
1077 0,
1078 "%0.2f" );
1079 m_indiP_env["temp-seccell"] = m_tseccell;
1080 indi::addNumberElement<double>( m_indiP_env,
1081 "temp-amb",
1082 std::numeric_limits<double>::lowest(),
1083 std::numeric_limits<double>::max(),
1084 0,
1085 "%0.2f" );
1086 m_indiP_env["temp-amb"] = m_tambient;
1087 indi::addNumberElement<double>( m_indiP_env,
1088 "dewpoint",
1089 std::numeric_limits<double>::lowest(),
1090 std::numeric_limits<double>::max(),
1091 0,
1092 "%0.2f" );
1093 m_indiP_env["dewpoint"] = m_wxdewpoint;
1094
1096
1097 createROIndiNumber( m_indiP_seeing, "seeing", "Seeing Data", "TCS" );
1098 indi::addNumberElement<unsigned>( m_indiP_seeing,
1099 "dimm_time",
1100 std::numeric_limits<unsigned>::lowest(),
1101 std::numeric_limits<unsigned>::max(),
1102 0,
1103 "%d" );
1104 m_indiP_seeing["dimm_time"] = m_dimm_time;
1105 indi::addNumberElement<double>( m_indiP_seeing,
1106 "dimm_fwhm_corr",
1107 std::numeric_limits<double>::lowest(),
1108 std::numeric_limits<double>::max(),
1109 0,
1110 "%0.2f" );
1111 m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
1112 indi::addNumberElement<unsigned>( m_indiP_seeing,
1113 "mag1_time",
1114 std::numeric_limits<unsigned>::lowest(),
1115 std::numeric_limits<unsigned>::max(),
1116 0,
1117 "%d" );
1118 m_indiP_seeing["mag1_time"] = m_mag1_time;
1119 indi::addNumberElement<double>( m_indiP_seeing,
1120 "mag1_fwhm_corr",
1121 std::numeric_limits<double>::lowest(),
1122 std::numeric_limits<double>::max(),
1123 0,
1124 "%0.2f" );
1125 m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
1126 indi::addNumberElement<unsigned>( m_indiP_seeing,
1127 "mag2_time",
1128 std::numeric_limits<unsigned>::lowest(),
1129 std::numeric_limits<unsigned>::max(),
1130 0,
1131 "%d" );
1132 m_indiP_seeing["mag2_time"] = m_mag2_time;
1133 indi::addNumberElement<double>( m_indiP_seeing,
1134 "mag2_fwhm_corr",
1135 std::numeric_limits<double>::lowest(),
1136 std::numeric_limits<double>::max(),
1137 0,
1138 "%0.2f" );
1139 m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
1140
1142
1144
1145 if( dev::ioDevice::appStartup() < 0 )
1146 {
1147 return log<software_error, -1>( { __FILE__, __LINE__ } );
1148 }
1149
1151 {
1152 return log<software_error, -1>( { __FILE__, __LINE__ } );
1153 }
1154
1155 REG_INDI_NEWPROP( m_indiP_pyrNudge, "pyrNudge", pcf::IndiProperty::Number );
1156 m_indiP_pyrNudge.add( pcf::IndiElement( "y" ) );
1157 m_indiP_pyrNudge.add( pcf::IndiElement( "x" ) );
1158 m_indiP_pyrNudge.add( pcf::IndiElement( "z" ) );
1159
1162 {
1164 return -1;
1165 }
1166
1169 {
1171 return -1;
1172 }
1173
1176 {
1178 return -1;
1179 }
1180
1181 createStandardIndiNumber( m_indiP_offlTTavgInt, "offlTT_avgInt", 0, 3600, 1, "%d" );
1182 m_indiP_offlTTavgInt["current"].set( offlTTAvgInt );
1183 m_indiP_offlTTavgInt["target"].set( offlTTAvgInt );
1185 {
1187 return -1;
1188 }
1189
1190 createStandardIndiNumber( m_indiP_offlTTgain, "offlTT_gain", 0.0, 1.0, 0.0, "%0.2f" );
1191 m_indiP_offlTTgain["current"].set( offlTTGain );
1192 m_indiP_offlTTgain["target"].set( offlTTGain );
1194 {
1196 return -1;
1197 }
1198
1199 createStandardIndiNumber( m_indiP_offlTTthresh, "offlTT_thresh", 0.0, 1.0, 0.0, "%0.2f" );
1200 m_indiP_offlTTthresh["current"].set( offlTTThresh );
1201 m_indiP_offlTTthresh["target"].set( offlTTThresh );
1203 {
1205 return -1;
1206 }
1207
1210 {
1212 return -1;
1213 }
1214
1217 {
1219 return -1;
1220 }
1221
1222 createStandardIndiNumber( m_indiP_offlFavgInt, "offlF_avgInt", 0, 3600, 1, "%d" );
1223 m_indiP_offlFavgInt["current"].set( offlFAvgInt );
1224 m_indiP_offlFavgInt["target"].set( offlFAvgInt );
1226 {
1228 return -1;
1229 }
1230
1231 createStandardIndiNumber( m_indiP_offlFgain, "offlF_gain", 0.0, 1.0, 0.0, "%0.2f" );
1232 m_indiP_offlFgain["current"].set( offlFGain );
1233 m_indiP_offlFgain["target"].set( offlFGain );
1235 {
1237 return -1;
1238 }
1239
1240 createStandardIndiNumber( m_indiP_offlFthresh, "offlF_thresh", 0.0, 1.0, 0.0, "%0.2f" );
1241 m_indiP_offlFthresh["current"].set( offlFThresh );
1242 m_indiP_offlFthresh["target"].set( offlFThresh );
1244 {
1246 return -1;
1247 }
1248
1249 // Get the loop state for managing offloading
1250 REG_INDI_SETPROP( m_indiP_loopState, "holoop", "loop_state" );
1251
1252 m_offloadRequests.resize( 5 );
1253 for( size_t n = 0; n < m_offloadRequests.size(); ++n )
1254 m_offloadRequests[n].resize( 10, 0 );
1255
1260 0,
1261 "",
1262 "offload",
1263 this,
1264 offloadThreadStart ) < 0 )
1265 {
1267 return -1;
1268 }
1269
1270 // Register to receive the coeff updates from Kyle
1271 REG_INDI_SETPROP( m_indiP_offloadCoeffs, "w2tcsOffloader", "zCoeffs" );
1272
1273 recordTcsiTipTilt( true );
1274 recordTcsiFocus( true );
1275 recordTcsiLabMode( true );
1276
1278
1279 return 0;
1280}
1281
1283{
1284 if( state() == stateCodes::ERROR )
1285 {
1286 int rv = m_sock.serialInit( m_deviceAddr.c_str(), m_devicePort );
1287
1288 if( rv != 0 )
1289 {
1291 log<text_log>( "In state ERROR, connection lost, will retry.", logPrio::LOG_ERROR );
1292 return 0;
1293 }
1294
1295 log<text_log>( "In state ERROR, but connected. Trying to continue.", logPrio::LOG_WARNING );
1296
1298 }
1299
1301 {
1302 static int lastrv = 0; // Used to handle a change in error within the same state. Make general?
1303 static int lasterrno = 0;
1304
1305 int rv = m_sock.serialInit( m_deviceAddr.c_str(), m_devicePort );
1306
1307 if( rv == 0 )
1308 {
1310
1311 if( !stateLogged() )
1312 {
1313 std::stringstream logs;
1314 logs << "Connected to " << m_deviceAddr << ":" << m_devicePort;
1315 log<text_log>( logs.str() );
1316 }
1317 lastrv = rv;
1318 lasterrno = errno;
1319 }
1320 else
1321 {
1322 if( !stateLogged() )
1323 {
1324 log<text_log>( { "Failed to connect to " + m_deviceAddr + ":" + std::to_string( m_devicePort ) },
1326 }
1327 if( rv != lastrv )
1328 {
1330 lastrv = rv;
1331 }
1332 if( errno != lasterrno )
1333 {
1335 lasterrno = errno;
1336 }
1337
1339
1340 return 0;
1341 }
1342 }
1343
1344 if( state() == stateCodes::CONNECTED )
1345 {
1346 std::string response;
1347
1348 // If any of these are unsuccesful we go around without recording data.
1349 if( getTelTime() < 0 )
1350 {
1351 return 0; // app state will be set based on what the error was
1352 }
1353
1354 if( getTelPos() < 0 )
1355 {
1356 return 0; // app state will be set based on what the error was
1357 }
1358
1359 if( getTelData() < 0 )
1360 {
1361 return 0;
1362 }
1363
1364 if( getCatData() < 0 )
1365 {
1366 return 0;
1367 }
1368
1369 if( getVaneData() < 0 )
1370 {
1371 return 0;
1372 }
1373
1374 if( getEnvData() < 0 )
1375 {
1376 return 0;
1377 }
1378
1379 if( getSeeing() < 0 )
1380 {
1381 return 0;
1382 }
1383
1385
1386 if( updateINDI() < 0 )
1387 {
1388 log<text_log>( "Error from updateINDI", logPrio::LOG_ERROR );
1389 return 0;
1390 }
1391 }
1392
1393 return 0;
1394}
1395
1397{
1398 // Wait for offload thread to exit on m_shutdown.
1399 if( m_offloadThread.joinable() )
1400 {
1401 try
1402 {
1403 m_offloadThread.join(); // this will throw if it was already joined
1404 }
1405 catch( ... )
1406 {
1407 }
1408 }
1409
1410 return 0;
1411}
1412
1413inline int tcsInterface::getMagTelStatus( std::string &response, const std::string &statreq )
1414{
1415
1416 int stat;
1417 char answer[512];
1418 std::string statreq_nl;
1419
1420#ifdef LOG_TCS_STATUS
1421 log<text_log>( "Sending status request: " + statreq );
1422#endif
1423
1424 std::lock_guard<std::mutex> guard( m_tcsMutex );
1425
1427 statreq_nl += '\n';
1428 stat = m_sock.serialOut( statreq_nl.c_str(), statreq_nl.length() );
1429
1430 if( stat != NETSERIAL_E_NOERROR )
1431 {
1432 log<text_log>( "Error sending status request: " + statreq, logPrio::LOG_ERROR );
1433 response = "";
1434 return 0;
1435 }
1436
1438
1439 if( stat <= 0 )
1440 {
1441 log<text_log>( "No response received to status request: " + statreq, logPrio::LOG_ERROR );
1442 response = "";
1443 return 0;
1444 }
1445
1446 char *nl = strchr( answer, '\n' );
1447 if( nl )
1448 answer[nl - answer] = '\0';
1449
1450#ifdef LOG_TCS_STATUS
1451 log<text_log>( std::string( "Received response: " ) + answer );
1452#endif
1453
1454 response = answer;
1455
1456 return 0;
1457} // int tcsInterface::getMagTelStatus
1458
1459inline int tcsInterface::sendMagTelCommand( const std::string &command, int timeout )
1460{
1461 int stat;
1462 char answer[512];
1463 std::string command_nl;
1464
1465#ifdef LOG_TCS_STATUS
1466 log<text_log>( "Sending command: " + command );
1467#endif
1468
1469 std::lock_guard<std::mutex> guard( m_tcsMutex );
1470
1472 command_nl += '\n';
1473 stat = m_sock.serialOut( command_nl.c_str(), command_nl.length() );
1474
1475 if( stat != NETSERIAL_E_NOERROR )
1476 {
1477 log<text_log>( "Error sending command: " + command, logPrio::LOG_ERROR );
1478 return -1000;
1479 }
1480
1481 stat = m_sock.serialInString( answer, sizeof( answer ), timeout, '\n' );
1482
1483 if( stat <= 0 )
1484 {
1485 log<text_log>( "No response received to command: " + command, logPrio::LOG_ERROR );
1486 return -1000;
1487 }
1488
1489 char *nl = strchr( answer, '\n' );
1490 if( nl )
1491 answer[nl - answer] = '\0';
1492
1493#ifdef LOG_TCS_STATUS
1494 log<text_log>( std::string( "Received response: " ) + answer );
1495#endif
1496
1497 return atoi( answer );
1498
1499} // int tcsInterface::sendMagTelCommand
1500
1501inline std::vector<std::string> tcsInterface::parse_teldata( std::string &tdat )
1502{
1503 std::vector<std::string> vres;
1504
1505 std::string tok;
1506
1507 int pos1, pos2;
1508
1509 // skip all leading spaces
1510 pos1 = tdat.find_first_not_of( " ", 0 );
1511
1512 if( pos1 == -1 )
1513 pos1 = 0;
1514
1515 pos2 = tdat.find_first_of( " ", pos1 );
1516
1517 while( pos2 > 0 )
1518 {
1519 tok = tdat.substr( pos1, pos2 - pos1 );
1520
1521 vres.push_back( tok );
1522
1523 // now move past end of current spaces - might be more than one.
1524 pos1 = pos2;
1525
1526 pos2 = tdat.find_first_not_of( " ", pos1 );
1527
1528 // and then find the end of this value.
1529 pos1 = pos2;
1530
1531 pos2 = tdat.find_first_of( " ", pos1 );
1532 }
1533
1534 // If there is another value, we pick it up here.
1535 if( pos1 >= 0 )
1536 {
1537 pos2 = tdat.length();
1538
1539 tok = tdat.substr( pos1, pos2 - pos1 );
1540
1541 pos2 = tok.find_first_of( " \n\r", 0 );
1542
1543 if( pos2 >= 0 )
1544 tok.erase( pos2, tok.length() - pos2 );
1545
1546 vres.push_back( tok );
1547 }
1548
1549 return vres;
1550}
1551
1552inline int tcsInterface::parse_xms( double &x, double &m, double &s, const std::string &xmsstr )
1553{
1554 size_t st, en;
1555
1556 int sgn = 1;
1557
1558 st = 0;
1559 en = xmsstr.find( ':', st );
1560
1561 // Check not found
1562 if( en == std::string::npos )
1563 {
1564 log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (missing ':')" } );
1565 return -1;
1566 }
1567
1568 // Check 0 length or invalid
1569 if( en - st < 2 || en < st )
1570 {
1571 log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (0 length or invalid)" } );
1572 return -1;
1573 }
1574
1575 std::string xstr;
1576 try
1577 {
1578 xstr = xmsstr.substr( st, en - st );
1579 }
1580 catch( const std::exception &e )
1581 {
1582 log<software_error>( { __FILE__, __LINE__, e.what() } );
1583 return -1;
1584 }
1585
1586 try
1587 {
1588 x = std::stod( xstr );
1589 }
1590 catch( const std::exception &e )
1591 {
1592 log<software_error>( { __FILE__, __LINE__, e.what() } );
1593 return -1;
1594 }
1595
1596 // Check for negative
1597 if( std::signbit( x ) )
1598 sgn = -1;
1599 if( xmsstr[0] == '-' )
1600 sgn = -1;
1601
1602 st = en + 1;
1603
1604 en = xmsstr.find( ':', st );
1605
1606 // Check not found
1607 if( en == std::string::npos )
1608 {
1609 log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (missing ':')" } );
1610 return -1;
1611 }
1612
1613 // Check 0 length or invalid
1614 if( en - st < 2 || en < st )
1615 {
1616 log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s (0 length or invalid)" } );
1617 return -1;
1618 }
1619
1620 std::string mstr;
1621 try
1622 {
1623 mstr = xmsstr.substr( st, en - st );
1624 }
1625 catch( const std::exception &e )
1626 {
1627 log<software_error>( { __FILE__, __LINE__, e.what() } );
1628 return -1;
1629 }
1630
1631 try
1632 {
1633 m = sgn * std::stod( mstr );
1634 }
1635 catch( const std::exception &e )
1636 {
1637 log<software_error>( { __FILE__, __LINE__, e.what() } );
1638 return -1;
1639 }
1640
1641 st = en + 1;
1642
1643 if( st >= xmsstr.length() - 1 )
1644 {
1645 log<software_error>( { __FILE__, __LINE__, "error parsing x:m:s" } );
1646 return -1;
1647 }
1648
1649 std::string sstr;
1650 try
1651 {
1652 sstr = xmsstr.substr( st, xmsstr.length() - st );
1653 }
1654 catch( const std::exception &e )
1655 {
1656 log<software_error>( { __FILE__, __LINE__, e.what() } );
1657 return -1;
1658 }
1659
1660 try
1661 {
1662 s = sgn * std::stod( sstr );
1663 }
1664 catch( const std::exception &e )
1665 {
1666 log<software_error>( { __FILE__, __LINE__, e.what() } );
1667 return -1;
1668 }
1669
1670 return 0;
1671}
1672
1674{
1675 double h, m, s;
1676
1677 std::vector<std::string> pdat;
1678 std::string posstr;
1679
1680 if( getMagTelStatus( posstr, "datetime" ) < 0 )
1681 {
1683 log<text_log>( "Error getting telescope position (telpos)", logPrio::LOG_ERROR );
1684 return -1;
1685 }
1686
1688
1689 if( pdat[0] == "-1" )
1690 {
1692 log<text_log>( "Error getting telescope time (datetime): TCS returned -1", logPrio::LOG_WARNING );
1693 return -1;
1694 }
1695
1696 if( pdat.size() != 3 )
1697 {
1699 log<text_log>( "Error getting telescope time (datetime): TCS response wrong size, returned " +
1700 std::to_string( pdat.size() ) + " values",
1702 return -1;
1703 }
1704
1705 if( parse_xms( h, m, s, pdat[2] ) != 0 )
1706 {
1707 log<text_log>( "Error parsing telescope ST", logPrio::LOG_WARNING );
1708 return -1;
1709 }
1710
1711 m_telST = ( h + m / 60. + s / 3600. );
1712
1713 return 0;
1714} // int tcsInterface::getTelTime()
1715
1717{
1718 double h, m, s;
1719
1720 std::vector<std::string> pdat;
1721 std::string posstr;
1722
1723 if( getMagTelStatus( posstr, "telpos" ) < 0 )
1724 {
1726 log<text_log>( "Error getting telescope position (telpos)", logPrio::LOG_ERROR );
1727 return -1;
1728 }
1729
1731
1732 if( pdat[0] == "-1" )
1733 {
1735 log<text_log>( "Error getting telescope position (telpos): TCS returned -1", logPrio::LOG_WARNING );
1736 return -1;
1737 }
1738
1739 if( pdat.size() != 6 )
1740 {
1742 log<text_log>( "Error getting telescope position (telpos): TCS response wrong size, returned " +
1743 std::to_string( pdat.size() ) + " values",
1745 return -1;
1746 }
1747
1748 if( parse_xms( h, m, s, pdat[0] ) != 0 )
1749 {
1750 log<text_log>( "Error parsing telescope RA", logPrio::LOG_WARNING );
1751 return -1;
1752 }
1753
1754 m_telRA = ( h + m / 60. + s / 3600. ) * 15.;
1755
1756 if( parse_xms( h, m, s, pdat[1] ) != 0 )
1757 {
1758 log<text_log>( "Error parsing telescope Dec", logPrio::LOG_WARNING );
1759 return -1;
1760 }
1761
1762 m_telDec = h + m / 60. + s / 3600.;
1763
1764 // m_telEl = strtod(pdat[1].c_str(),0);// * 3600.;
1765
1766 m_telEpoch = strtod( pdat[2].c_str(), 0 );
1767
1768 if( parse_xms( h, m, s, pdat[3] ) != 0 )
1769 {
1770 log<text_log>( "Error parsing telescope HA", logPrio::LOG_WARNING );
1771 return -1;
1772 }
1773
1774 /************ BUG: this won't handle -0!
1775 */
1776 m_telHA = h + m / 60. + s / 3600.;
1777
1778 m_telAM = strtod( pdat[4].c_str(), 0 );
1779
1780 m_telRotOff = strtod( pdat[5].c_str(), 0 );
1781
1782 if( recordTelPos() < 0 )
1783 {
1784 return log<software_error, -1>( { __FILE__, __LINE__ } );
1785 }
1786
1787 return 0;
1788} // int tcsInterface::getTelPos()
1789
1791{
1792 std::string xstr;
1793 std::vector<std::string> tdat;
1794
1795 if( getMagTelStatus( xstr, "teldata" ) < 0 )
1796 {
1798 log<text_log>( "Error getting telescope data (teldata)", logPrio::LOG_ERROR );
1799 return -1;
1800 }
1801
1802 tdat = parse_teldata( xstr );
1803
1804 if( tdat[0] == "-1" )
1805 {
1807 log<text_log>( "Error getting telescope data (teldata): TCS returned -1", logPrio::LOG_WARNING );
1808 return -1;
1809 }
1810
1811 if( tdat.size() != 10 )
1812 {
1814 log<text_log>( "[TCS] Error getting telescope data (teldata): TCS response wrong size, returned " +
1815 std::to_string( tdat.size() ) + " values",
1817 return -1;
1818 }
1819
1820 m_telROI = atoi( tdat[0].c_str() );
1821
1822 // Parse the telguide string
1823 char bit[2] = { 0, 0 };
1824 bit[1] = 0;
1825 bit[0] = tdat[1].c_str()[0];
1826 m_telTracking = atoi( bit );
1827 bit[0] = tdat[1].c_str()[1];
1828 m_telGuiding = atoi( bit );
1829
1830 // parse the gdrmountmv string
1831 bit[0] = tdat[2].c_str()[0];
1832 m_telSlewing = atoi( bit );
1833 bit[0] = tdat[2].c_str()[1];
1835
1836 // number 3 is mountmv
1837
1838 m_telAz = strtod( tdat[4].c_str(), 0 );
1839
1840 m_telEl = strtod( tdat[5].c_str(), 0 );
1841
1842 m_telZd = strtod( tdat[6].c_str(), 0 ); // * 3600.;
1843
1844 m_telPA = strtod( tdat[7].c_str(), 0 );
1845
1846 m_telDomeAz = strtod( tdat[8].c_str(), 0 );
1847
1848 m_telDomeStat = atoi( tdat[9].c_str() );
1849
1850 if( recordTelData() < 0 )
1851 {
1852 return log<software_error, -1>( { __FILE__, __LINE__ } );
1853 }
1854
1855 return 0;
1856} // int tcsInterface::getTelData()
1857
1859{
1860 double h, m, s;
1861
1862 std::vector<std::string> cdat;
1863 std::string cstr;
1864
1865 if( getMagTelStatus( cstr, "catdata" ) < 0 )
1866 {
1868 log<text_log>( "Error getting catalog data (catdata)", logPrio::LOG_ERROR );
1869 return -1;
1870 }
1871
1872 cdat = parse_teldata( cstr );
1873
1874 if( cdat[0] == "-1" )
1875 {
1877 log<text_log>( "Error getting catalog data (catdata): TCS returned -1", logPrio::LOG_WARNING );
1878 return -1;
1879 }
1880
1881 if( cdat.size() != 6 )
1882 {
1883 bool pointing = false;
1884
1885 if( cdat.size() == 7 )
1886 {
1887 if( cdat[6] == "Pointing" )
1888 {
1889 pointing = true;
1890 }
1891 }
1892
1893 if( !pointing )
1894 {
1895 // This can occur if no target selected by operator
1896 log<text_log>( "Catalog data (catdata): TCS response wrong size, returned " +
1897 std::to_string( cdat.size() ) + " values",
1899
1900 for( size_t n = 0; n < cdat.size(); ++n )
1901 {
1902 std::cerr << n << " " << cdat[n] << "\n";
1903 }
1904 m_catRA = 0;
1905
1906 m_catDec = 0;
1907
1908 m_catEp = 0;
1909
1910 m_catRo = 0;
1911
1912 m_catRm = "";
1913
1914 m_catObj = "none";
1915
1916 return 1;
1917 }
1918 }
1919
1920 if( parse_xms( h, m, s, cdat[0] ) != 0 )
1921 {
1922 log<text_log>( "Error parsing catalog RA", logPrio::LOG_WARNING );
1923 return -1;
1924 }
1925
1926 m_catRA = ( h + m / 60. + s / 3600. ) * 15.;
1927
1928 if( parse_xms( h, m, s, cdat[1] ) != 0 )
1929 {
1930 log<text_log>( "Error parsing catalog Dec", logPrio::LOG_WARNING );
1931 return -1;
1932 }
1933
1934 m_catDec = h + m / 60. + s / 3600.;
1935
1936 m_catEp = strtod( cdat[2].c_str(), 0 );
1937
1938 m_catRo = strtod( cdat[3].c_str(), 0 );
1939
1940 m_catRm = cdat[4];
1941
1942 m_catObj = cdat[5];
1943
1944 return 0;
1945} // int tcsInterface::getCatData()
1946
1948{
1949 std::string xstr;
1950 std::vector<std::string> vedat;
1951
1952 if( getMagTelStatus( xstr, "vedata" ) < 0 )
1953 {
1955 log<text_log>( "Error getting telescope secondary positions (vedata)", logPrio::LOG_ERROR );
1956 return -1;
1957 }
1958
1960
1961 if( vedat[0] == "-1" )
1962 {
1964 log<text_log>( "Error getting telescope secondary positions (vedata): TCS returned -1", logPrio::LOG_WARNING );
1965 return -1;
1966 }
1967
1968 if( vedat.size() != 10 )
1969 {
1971 log<text_log>( "Error getting telescope secondary positions (vedata): TCS response wrong size, returned " +
1972 std::to_string( vedat.size() ) + " values",
1974 return -1;
1975 }
1976
1977 m_telSecZ = strtod( vedat[0].c_str(), 0 );
1978 m_telEncZ = strtod( vedat[1].c_str(), 0 );
1979 m_telSecX = strtod( vedat[2].c_str(), 0 );
1980 m_telEncX = strtod( vedat[3].c_str(), 0 );
1981 m_telSecY = strtod( vedat[4].c_str(), 0 );
1982 m_telEncY = strtod( vedat[5].c_str(), 0 );
1983 m_telSecH = strtod( vedat[6].c_str(), 0 );
1984 m_telEncH = strtod( vedat[7].c_str(), 0 );
1985 m_telSecV = strtod( vedat[8].c_str(), 0 );
1986 m_telEncV = strtod( vedat[9].c_str(), 0 );
1987
1988 if( recordTelVane() < 0 )
1989 {
1990 return log<software_error, -1>( { __FILE__, __LINE__ } );
1991 }
1992
1993 return 0;
1994} // int tcsInterface::getVaneData()
1995
1997{
1998 std::string estr;
1999 std::vector<std::string> edat;
2000
2001 if( getMagTelStatus( estr, "telenv" ) < 0 )
2002 {
2004 log<text_log>( "Error getting telescope environment data (telenv)", logPrio::LOG_ERROR );
2005 return -1;
2006 }
2007
2008 edat = parse_teldata( estr );
2009
2010 if( edat[0] == "-1" )
2011 {
2013 log<text_log>( "Error getting telescope environment data (telenv): TCS returned -1", logPrio::LOG_WARNING );
2014 return -1;
2015 }
2016
2017 if( edat.size() != 10 )
2018 {
2020 log<text_log>( "Error getting telescope environment data (telenv): TCS response wrong size, returned " +
2021 std::to_string( edat.size() ) + "values",
2023 return -1;
2024 }
2025
2026 m_wxtemp = strtod( edat[0].c_str(), 0 );
2027 m_wxpres = strtod( edat[1].c_str(), 0 );
2028 m_wxhumid = strtod( edat[2].c_str(), 0 );
2029 m_wxwind = strtod( edat[3].c_str(), 0 );
2030 m_wxwdir = strtod( edat[4].c_str(), 0 );
2031 m_ttruss = strtod( edat[5].c_str(), 0 );
2032 m_tcell = strtod( edat[6].c_str(), 0 );
2033 m_tseccell = strtod( edat[7].c_str(), 0 );
2034 m_tambient = strtod( edat[8].c_str(), 0 );
2035 m_wxdewpoint = strtod( edat[9].c_str(), 0 );
2036
2037 if( recordTelEnv() < 0 )
2038 {
2039 return log<software_error, -1>( { __FILE__, __LINE__ } );
2040 }
2041
2042 return 0;
2043} // int tcsInterface::getEnvData()
2044
2046{
2047 m_dimm_time = 0;
2048 m_dimm_fwhm_corr = -1;
2049 m_mag1_time = 0;
2050 m_mag1_fwhm_corr = -1;
2051 m_mag2_time = 0;
2052 m_mag2_fwhm_corr = -1;
2053
2054 if( recordTelSee() < 0 )
2055 {
2056 return log<software_error, -1>( { __FILE__, __LINE__ } );
2057 }
2058
2059 return 0;
2060}
2061
2063{
2064 static int last_query = 0;
2065
2066 if( time( 0 ) - last_query > m_seeingInterval )
2067 {
2069 time_t dt;
2070
2071 int rv = system( "query_seeing > /dev/null" );
2072 if( rv < 0 )
2073 {
2074 if( badSeeing() < 0 )
2075 {
2077 }
2078
2079 if( !m_labMode )
2080 {
2081 log<software_error>( { __FILE__, __LINE__, "Error from seeing query" } );
2082 return -1;
2083 }
2084
2085 return 0;
2086 }
2087
2088 last_query = time( 0 );
2089 sec_midnight = last_query % 86400;
2090
2091 std::ifstream fin;
2092 std::string label, datestr, timestr, scopestr, fwhmcorrstr;
2093 double h, m, s;
2094 // note: query seeing picks a temp file with the username included
2095 // to reduce file clobbering conflicts
2096 fin.open( "/tmp/xsup_query_seeing.txt" );
2097
2098 if( fin.fail() )
2099 {
2100 if( badSeeing() < 0 )
2101 {
2103 }
2104
2105 if( !m_labMode )
2106 {
2107 log<software_error>( { __FILE__, __LINE__, "Error reading dimm seeing" } );
2108 return -1;
2109 }
2110 }
2111
2112 fin >> label;
2113 fin >> label;
2114 fin >> label;
2115 fin >> label;
2116
2117 fin >> datestr;
2118 fin >> timestr;
2119 fin >> scopestr;
2121
2122 // Handle no values from query
2123 if( !fin )
2124 {
2125 if( badSeeing() < 0 )
2126 {
2128 }
2129
2130 if( !m_labMode )
2131 {
2132 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
2133 return -1;
2134 }
2135 else
2136 {
2137 return 0;
2138 }
2139 }
2140
2141 if( scopestr != "dimm" )
2142 {
2144 { __FILE__, __LINE__, "Unexpected order of telescope names. Instead of dimm got: " + scopestr } );
2145 }
2146
2147 parse_xms( h, m, s, timestr );
2148
2149 m_dimm_time = (int)( h * 3600 + m * 60 + s + 0.5 );
2150
2152 if( dt < 0 )
2153 dt = 86400 - dt;
2154
2155 if( dt > 300 || m_dimm_fwhm_corr <= 0 )
2156 {
2157 m_dimm_fwhm_corr = -1;
2158 }
2159
2160 // Next scope: mag1/baade
2161 fin >> datestr;
2162 fin >> timestr;
2163 fin >> scopestr;
2165
2166 // Handle no values from query
2167 if( !fin )
2168 {
2169 if( badSeeing() < 0 )
2170 {
2172 }
2173
2174 if( !m_labMode )
2175 {
2176 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
2177 return -1;
2178 }
2179 else
2180 {
2181 return 0;
2182 }
2183 }
2184
2185 if( scopestr != "baade" )
2186 {
2188 { __FILE__, __LINE__, "Unexpected order of telescope names. Instead of baade got: " + scopestr } );
2189 }
2190
2191 parse_xms( h, m, s, timestr );
2192
2193 m_mag1_time = (int)( h * 3600 + m * 60 + s + 0.5 );
2194
2196 if( dt < 0 )
2197 dt = 86400 - dt;
2198
2199 if( dt > 300 || m_mag1_fwhm_corr <= 0 )
2200 {
2201 m_mag1_fwhm_corr = -1;
2202 }
2203
2204 // Last scope: mag2/clay
2205 fin >> datestr;
2206 fin >> timestr;
2207 fin >> scopestr;
2209
2210 // Handle no values from query
2211 if( !fin )
2212 {
2213 if( badSeeing() < 0 )
2214 {
2216 }
2217
2218 if( !m_labMode )
2219 {
2220 log<software_error>( { __FILE__, __LINE__, "query_seeing returned not enough values" } );
2221 return -1;
2222 }
2223 else
2224 {
2225 return 0;
2226 }
2227 }
2228
2229 if( scopestr != "clay" )
2230 {
2232 { __FILE__, __LINE__, "Unexpected order of telescope names. Instead of clay got: " + scopestr } );
2233 }
2234
2235 fin.close();
2236
2237 parse_xms( h, m, s, timestr );
2238
2239 m_mag2_time = (int)( h * 3600 + m * 60 + s + 0.5 );
2240
2242 if( dt < 0 )
2243 dt = 86400 - dt;
2244
2245 if( dt > 300 || m_mag2_fwhm_corr <= 0 )
2246 {
2247 m_mag2_fwhm_corr = -1;
2248 }
2249
2250 if( recordTelSee() < 0 )
2251 {
2252 return log<software_error, -1>( { __FILE__, __LINE__ } );
2253 }
2254 }
2255
2256 return 0;
2257}
2258
2260{
2261 try
2262 {
2263 updateIfChanged( m_indiP_teltime, "sidereal_time", m_telST, INDI_OK );
2264 // m_indiP_teltime["sidereal_time"] = m_telST;
2265 }
2266 catch( ... )
2267 {
2268 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2269 return -1;
2270 }
2271
2272 /* try
2273 {
2274 m_indiP_teltime.setState(INDI_OK);
2275 }
2276 catch(...)
2277 {
2278 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2279 return -1;
2280 }
2281
2282 try
2283 {
2284 m_indiDriver->sendSetProperty(m_indiP_teltime);
2285 }
2286 catch(...)
2287 {
2288 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2289 return -1;
2290 }*/
2291
2292 try
2293 {
2296 std::vector<std::string>( { "epoch", "ra", "dec", "el", "ha", "am", "rotoff" } ),
2297 std::vector<double>( { m_telEpoch, m_telRA, m_telDec, m_telEl, m_telHA, m_telAM, m_telRotOff } ),
2299 INDI_OK );
2300 /*m_indiP_telpos["epoch"] = m_telEpoch;
2301 m_indiP_telpos["ra"] = m_telRA;
2302 m_indiP_telpos["dec"] = m_telDec;
2303 m_indiP_telpos["el"] = m_telEl;
2304 m_indiP_telpos["ha"] = m_telHA;
2305 m_indiP_telpos["am"] = m_telAM;
2306 m_indiP_telpos["rotoff"] = m_telRotOff;*/
2307 }
2308 catch( ... )
2309 {
2310 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2311 return -1;
2312 }
2313
2314 /*try
2315 {
2316 m_indiP_telpos.setState(INDI_OK);
2317 }
2318 catch(...)
2319 {
2320 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2321 return -1;
2322 }
2323
2324 try
2325 {
2326 m_indiDriver->sendSetProperty (m_indiP_telpos);
2327 }
2328 catch(...)
2329 {
2330 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2331 return -1;
2332 }*/
2333
2334 try
2335 {
2336 m_indiP_teldata["roi"] = m_telROI;
2337 m_indiP_teldata["tracking"] = m_telTracking;
2338 m_indiP_teldata["guiding"] = m_telGuiding;
2339 m_indiP_teldata["slewing"] = m_telSlewing;
2340 m_indiP_teldata["guider_moving"] = m_telGuiderMoving;
2341 m_indiP_teldata["az"] = m_telAz;
2342 m_indiP_teldata["zd"] = m_telZd;
2343 m_indiP_teldata["pa"] = m_telPA;
2344 m_indiP_teldata["dome_az"] = m_telDomeAz;
2345 m_indiP_teldata["dome_stat"] = m_telDomeStat;
2346 }
2347 catch( ... )
2348 {
2349 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2350 return -1;
2351 }
2352
2353 try
2354 {
2355 m_indiP_teldata.setState( INDI_OK );
2356 }
2357 catch( ... )
2358 {
2359 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2360 return -1;
2361 }
2362
2363 try
2364 {
2365 m_indiDriver->sendSetProperty( m_indiP_teldata );
2366 }
2367 catch( ... )
2368 {
2369 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2370 return -1;
2371 }
2372
2373 try
2374 {
2376 std::vector<std::string>( { "object", "rotmode" } ),
2377 std::vector<std::string>( { m_catObj, m_catRm } ),
2379 INDI_OK );
2380 // m_indiP_catalog["object"] = m_catObj;
2381 // m_indiP_catalog["rotmode"] = m_catRo;
2382 }
2383 catch( ... )
2384 {
2385 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2386 return -1;
2387 }
2388
2389 /* try
2390 {
2391 m_indiP_catalog.setState(INDI_OK);
2392 }
2393 catch(...)
2394 {
2395 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2396 return -1;
2397 }
2398
2399 try
2400 {
2401 m_indiDriver->sendSetProperty (m_indiP_catalog);
2402 }
2403 catch(...)
2404 {
2405 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2406 return -1;
2407 }*/
2408
2409 try
2410 {
2412 std::vector<std::string>( { "ra", "dec", "epoch", "rotoff" } ),
2413 std::vector<double>( { m_catRA, m_catDec, m_catEp, m_catRo } ),
2415 INDI_OK );
2416 /*m_indiP_catdata["ra"] = m_catRA;
2417 m_indiP_catdata["dec"] = m_catDec;
2418 m_indiP_catdata["epoch"] = m_catEp;
2419 m_indiP_catdata["rotoff"] = m_catRo;*/
2420 }
2421 catch( ... )
2422 {
2423 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2424 return -1;
2425 }
2426
2427 /* try
2428 {
2429 m_indiP_catdata.setState(INDI_OK);
2430 }
2431 catch(...)
2432 {
2433 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2434 return -1;
2435 }
2436
2437 try
2438 {
2439 m_indiDriver->sendSetProperty (m_indiP_catdata);
2440 }
2441 catch(...)
2442 {
2443 log<software_error>({__FILE__,__LINE__,"INDI library exception"});
2444 return -1;
2445 }*/
2446
2447 //---- Vane End ----//
2448 try
2449 {
2450 m_indiP_vaneend["secz"] = m_telSecZ;
2451 m_indiP_vaneend["encz"] = m_telEncZ;
2452 m_indiP_vaneend["secx"] = m_telSecX;
2453 m_indiP_vaneend["encx"] = m_telEncX;
2454 m_indiP_vaneend["secy"] = m_telSecY;
2455 m_indiP_vaneend["ency"] = m_telEncY;
2456 m_indiP_vaneend["sech"] = m_telSecH;
2457 m_indiP_vaneend["ench"] = m_telEncH;
2458 m_indiP_vaneend["secv"] = m_telSecV;
2459 m_indiP_vaneend["encv"] = m_telEncV;
2460 }
2461 catch( ... )
2462 {
2463 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2464 return -1;
2465 }
2466
2467 try
2468 {
2469 m_indiP_vaneend.setState( INDI_OK );
2470 }
2471 catch( ... )
2472 {
2473 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2474 return -1;
2475 }
2476
2477 try
2478 {
2479 m_indiDriver->sendSetProperty( m_indiP_vaneend );
2480 }
2481 catch( ... )
2482 {
2483 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2484 return -1;
2485 }
2486
2487 //---- Environment ----//
2488 try
2489 {
2490 m_indiP_env["temp-out"] = m_wxtemp;
2491 m_indiP_env["pressure"] = m_wxpres;
2492 m_indiP_env["humidity"] = m_wxhumid;
2493 m_indiP_env["wind"] = m_wxwind;
2494 m_indiP_env["winddir"] = m_wxwdir;
2495 m_indiP_env["temp-truss"] = m_ttruss;
2496 m_indiP_env["temp-cell"] = m_tcell;
2497 m_indiP_env["temp-seccell"] = m_tseccell;
2498 m_indiP_env["temp-amb"] = m_tambient;
2499 m_indiP_env["dewpoint"] = m_wxdewpoint;
2500 }
2501 catch( ... )
2502 {
2503 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2504 return -1;
2505 }
2506
2507 try
2508 {
2509 m_indiP_env.setState( INDI_OK );
2510 }
2511 catch( ... )
2512 {
2513 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2514 return -1;
2515 }
2516
2517 try
2518 {
2519 m_indiDriver->sendSetProperty( m_indiP_env );
2520 }
2521 catch( ... )
2522 {
2523 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2524 return -1;
2525 }
2526
2527 //---- Seeing ----//
2528 try
2529 {
2530 m_indiP_seeing["dimm_time"] = m_dimm_time;
2531 m_indiP_seeing["dimm_fwhm_corr"] = m_dimm_fwhm_corr;
2532
2533 m_indiP_seeing["mag1_time"] = m_mag1_time;
2534 m_indiP_seeing["mag1_fwhm_corr"] = m_mag1_fwhm_corr;
2535
2536 m_indiP_seeing["mag2_time"] = m_mag2_time;
2537 m_indiP_seeing["mag2_fwhm_corr"] = m_mag2_fwhm_corr;
2538 }
2539 catch( ... )
2540 {
2541 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2542 return -1;
2543 }
2544
2545 try
2546 {
2547 m_indiP_seeing.setState( INDI_OK );
2548 }
2549 catch( ... )
2550 {
2551 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2552 return -1;
2553 }
2554
2555 try
2556 {
2557 m_indiDriver->sendSetProperty( m_indiP_seeing );
2558 }
2559 catch( ... )
2560 {
2561 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2562 return -1;
2563 }
2564
2565 //--- Offloading ---//
2566
2567 bool offlTTDump;
2568 bool offlTTEnabled;
2569 float offlTTAvgInt;
2570 float offlTTGain;
2571 float offlTTThresh;
2572 bool offlFDump;
2573 bool offlFEnabled;
2574 float offlFAvgInt;
2575 float offlFGain;
2576 float offlFThresh;
2577
2578 { // mutex scope
2579 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
2590 }
2591
2592 try
2593 {
2594 if( offlTTDump )
2595 {
2596 updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK );
2597 }
2598 else
2599 {
2600 updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::Off, INDI_IDLE );
2601 }
2602
2603 if( offlTTEnabled )
2604 {
2605 updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
2606 }
2607 else
2608 {
2609 updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
2610 }
2611
2615 }
2616 catch( ... )
2617 {
2618 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2619 return -1;
2620 }
2621
2622 try
2623 {
2624 if( offlFDump )
2625 {
2626 updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK );
2627 }
2628 else
2629 {
2630 updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::Off, INDI_IDLE );
2631 }
2632
2633 if( offlFEnabled )
2634 {
2635 updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK );
2636 }
2637 else
2638 {
2639 updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
2640 }
2641
2645 }
2646 catch( ... )
2647 {
2648 log<software_error>( { __FILE__, __LINE__, "INDI library exception" } );
2649 return -1;
2650 }
2651
2652 return 0;
2653}
2654
2667
2669{
2670 recordTelPos( true );
2671 return 0;
2672}
2673
2675{
2676 recordTelData( true );
2677 return 0;
2678}
2679
2681{
2682 recordTelVane( true );
2683 return 0;
2684}
2685
2687{
2688 recordTelEnv( true );
2689 return 0;
2690}
2691
2693{
2694 recordTelCat( true );
2695 return 0;
2696}
2697
2699{
2700 recordTelSee( true );
2701 return 0;
2702}
2703
2705{
2706 recordTcsiTipTilt( true );
2707 return 0;
2708}
2709
2711{
2712 recordTcsiFocus( true );
2713 return 0;
2714}
2715
2717{
2718 recordTcsiLabMode( true );
2719 return 0;
2720}
2721
2722inline int tcsInterface::recordTelPos( bool force )
2723{
2724 static double lastEpoch = 0;
2725 static double lastRA = 0;
2726 static double lastDec = 0;
2727 static double lastEl = 0;
2728 static double lastHA = 0;
2729 static double lastAM = 0;
2730 static double lastRotOff = 0;
2731
2732 if( force || lastEpoch != m_telEpoch || lastRA != m_telRA || lastDec != m_telDec || lastEl != m_telEl ||
2734 {
2736
2738 lastRA = m_telRA;
2739 lastDec = m_telDec;
2740 lastEl = m_telEl;
2741 lastHA = m_telHA;
2742 lastAM = m_telAM;
2744 }
2745
2746 return 0;
2747}
2748
2749inline int tcsInterface::recordTelData( bool force )
2750{
2751 static int lastROI = -999;
2752 static int lastTracking = -999;
2753 static int lastGuiding = -999;
2754 static int lastSlewing = -999;
2755 static int lastGuiderMoving = -999;
2756 static double lastAz = 0;
2757 static double lastZd = 0;
2758 static double lastPA = 0;
2759 static double lastDomeAz = 0;
2760 static int lastDomeStat = -999;
2761
2765 {
2771 m_telAz,
2772 m_telZd,
2773 m_telPA,
2775 m_telDomeStat } );
2776
2777 lastROI = m_telROI;
2782 lastAz = m_telAz;
2783 lastZd = m_telZd;
2784 lastPA = m_telPA;
2787 }
2788
2789 return 0;
2790}
2791
2792inline int tcsInterface::recordTelVane( bool force )
2793{
2794 static double lastSecZ = -999;
2795 static double lastEncZ = -999;
2796 static double lastSecX = -999;
2797 static double lastEncX = -999;
2798 static double lastSecY = -999;
2799 static double lastEncY = -999;
2800 static double lastSecH = -999;
2801 static double lastEncH = -999;
2802 static double lastSecV = -999;
2803 static double lastEncV = -999;
2804
2808 {
2810 m_telEncZ,
2811 m_telSecX,
2812 m_telEncX,
2813 m_telSecY,
2814 m_telEncY,
2815 m_telSecH,
2816 m_telEncH,
2817 m_telSecV,
2818 m_telEncV } );
2819
2830 }
2831
2832 return 0;
2833}
2834
2835inline int tcsInterface::recordTelEnv( bool force )
2836{
2837 static double lastWxtemp = -999;
2838 static double lastWxpres = -999;
2839 static double lastWxhumid = -999;
2840 static double lastWxwind = -999;
2841 static double lastWxwdir = -999;
2842 static double lastTtruss = -999;
2843 static double lastTcell = -999;
2844 static double lastTseccell = -999;
2845 static double lastTambient = -999;
2846 static double lastWxdewpoint = -999;
2847
2851 {
2853 m_wxpres,
2854 m_wxhumid,
2855 m_wxwind,
2856 m_wxwdir,
2857 m_ttruss,
2858 m_tcell,
2859 m_tseccell,
2860 m_tambient,
2861 m_wxdewpoint } );
2862
2873 }
2874
2875 return 0;
2876}
2877
2878inline int tcsInterface::recordTelCat( bool force )
2879{
2880 static std::string last_catObj;
2881 static std::string last_catRm;
2882 static double last_catRA = 0;
2883 static double last_catDec = 0;
2884 static double last_catEp = 0;
2885 static double last_catRo = 0;
2886
2889 {
2891
2898 }
2899
2900 return 0;
2901}
2902
2903inline int tcsInterface::recordTelSee( bool force )
2904{
2905 static int last_dimm_time = 0;
2906 static double last_dimm_fwhm_corr = 0;
2907
2908 static int last_mag1_time = 0;
2909 static double last_mag1_fwhm_corr = 0;
2910
2911 static int last_mag2_time = 0;
2912 static double last_mag2_fwhm_corr = 0;
2913
2917
2918 {
2921
2924
2927
2930 }
2931
2932 return 0;
2933}
2934
2935inline int tcsInterface::recordTcsiTipTilt( bool force )
2936{
2937 bool enabled;
2938 float avgInt;
2939 float gain;
2940 float thresh;
2941
2942 { // mutex scope
2943 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
2944 enabled = m_offlTT_enabled;
2945 avgInt = m_offlTT_avgInt;
2946 gain = m_offlTT_gain;
2947 thresh = m_offlTT_thresh;
2948 }
2949
2950 static bool lastEnabled = !enabled;
2951 static float lastAvgInt = std::numeric_limits<float>::quiet_NaN();
2952 static float lastGain = std::numeric_limits<float>::quiet_NaN();
2953 static float lastThresh = std::numeric_limits<float>::quiet_NaN();
2954
2955 if( force || lastEnabled != enabled || lastAvgInt != avgInt || lastGain != gain || lastThresh != thresh )
2956 {
2957 telem<telem_tcsi_tiptilt>( { enabled, avgInt, gain, thresh } );
2958
2959 lastEnabled = enabled;
2960 lastAvgInt = avgInt;
2961 lastGain = gain;
2962 lastThresh = thresh;
2963 }
2964
2965 return 0;
2966}
2967
2968inline int tcsInterface::recordTcsiFocus( bool force )
2969{
2970 bool enabled;
2971 float avgInt;
2972 float gain;
2973 float thresh;
2974
2975 { // mutex scope
2976 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
2977 enabled = m_offlF_enabled;
2978 avgInt = m_offlF_avgInt;
2979 gain = m_offlF_gain;
2980 thresh = m_offlF_thresh;
2981 }
2982
2983 static bool lastEnabled = !enabled;
2984 static float lastAvgInt = std::numeric_limits<float>::quiet_NaN();
2985 static float lastGain = std::numeric_limits<float>::quiet_NaN();
2986 static float lastThresh = std::numeric_limits<float>::quiet_NaN();
2987
2988 if( force || lastEnabled != enabled || lastAvgInt != avgInt || lastGain != gain || lastThresh != thresh )
2989 {
2990 telem<telem_tcsi_focus>( { enabled, avgInt, gain, thresh } );
2991
2992 lastEnabled = enabled;
2993 lastAvgInt = avgInt;
2994 lastGain = gain;
2995 lastThresh = thresh;
2996 }
2997
2998 return 0;
2999}
3000
3001inline int tcsInterface::recordTcsiLabMode( bool force )
3002{
3003 bool labMode;
3004
3005 { // mutex scope
3006 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3007 labMode = m_labMode;
3008 }
3009
3010 static bool lastLabMode = !labMode;
3011
3012 if( force || lastLabMode != labMode )
3013 {
3014 telem<telem_tcsi_labmode>( { labMode } );
3015
3016 lastLabMode = labMode;
3017 }
3018
3019 return 0;
3020}
3021
3022int tcsInterface::sendPyrNudge( float x, float y, float z )
3023{
3024 if( x != 0 || y != 0 )
3025 {
3026 float dx = m_pyrNudge_C_00 * x + m_pyrNudge_C_01 * y;
3027 float dy = m_pyrNudge_C_10 * x + m_pyrNudge_C_11 * y;
3028
3029 // float cs = cos((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
3030 // float ss = sin((m_pyrNudge_ang + m_pyrNudge_ang0 + m_telZd)*3.14159*180);
3031
3032 // float dx = x * cs - y * ss;
3033 // float dy = m_pyrNudge_parity*(x * ss + y * cs);
3034
3035 char ttstr[64];
3036 snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", dx, dy );
3037
3038 ///\todo need logtypes for nudges and offloads
3039 log<text_log>( std::string( "[PYRNUDGE] " ) + ttstr, logPrio::LOG_NOTICE );
3041 {
3042 log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
3043 return -1;
3044 }
3045 }
3046
3047 if( z != 0 )
3048 {
3049 char ttstr[64];
3050 snprintf( ttstr, sizeof( ttstr ), "zimr %f", z * m_pyrNudge_F_sign );
3051
3052 log<text_log>( std::string( "[PYRNUDGE] " ) + ttstr, logPrio::LOG_NOTICE );
3054 {
3055 log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
3056 return -1;
3057 }
3058 }
3059
3060 return 0;
3061}
3062
3064{
3065 // Convert current telescope rotator angle to radians
3066 float q = ( m_acqZdSign * m_telZd ) * 3.14159 / 180.;
3067
3068 // The rotation matrix
3069 float az = m_acqAz0 * cos( q ) - m_acqEl0 * sin( q ) + m_acqAzOff;
3070 float el = m_acqAz0 * sin( q ) + m_acqEl0 * cos( q ) + m_acqElOff;
3071
3072 char ttstr[64];
3073 snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", az, el );
3074
3075 ///\todo need logtypes for nudges and offloads
3076 log<text_log>( std::string( "[ACQUIRE] " ) + ttstr, logPrio::LOG_NOTICE );
3078 {
3079 log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
3080 return -1;
3081 }
3082
3083 float z = m_acqFocus;
3084 snprintf( ttstr, sizeof( ttstr ), "zimr %f", z * m_pyrNudge_F_sign );
3085
3086 log<text_log>( std::string( "[ACQUIRE] " ) + ttstr, logPrio::LOG_NOTICE );
3088 {
3089 log<software_error>( { __FILE__, __LINE__, std::string( "error sending command: " ) + ttstr } );
3090 return -1;
3091 }
3092
3093 return 0;
3094}
3095
3100
3102{
3103 // Get the thread PID immediately so the caller can return.
3105
3106 static int last_loopState = -1;
3107
3108 while( ( m_offloadThreadInit == true || state() != stateCodes::CONNECTED ) && shutdown() == 0 )
3109 {
3110 sleep( 1 );
3111 }
3112
3113 float avg_TT_0;
3114 float avg_TT_1;
3115 int sincelast_TT = 0;
3116
3117 float avg_F_0;
3118 int sincelast_F = 0;
3119
3120 while( shutdown() == 0 )
3121 {
3122 int loopState = m_loopState.load();
3123 float offlTTAvgInt;
3124 float offlFAvgInt;
3125
3126 { // mutex scope
3127 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3130 }
3131
3132 // Check if loop open
3133 if( loopState == 0 )
3134 {
3135 // If this is new, then reset the averaging buffer
3136 if( loopState != last_loopState )
3137 {
3138 { // mutex scope
3139 std::lock_guard<std::mutex> lock( m_offloadMutex );
3140 m_firstRequest = 0;
3141 m_lastRequest = std::numeric_limits<size_t>::max();
3142 m_nRequests = 0;
3143 m_last_nRequests = 0;
3144 }
3145 sincelast_TT = 0;
3146 sincelast_F = 0;
3147 }
3148 sleep( 1 );
3150 continue;
3151 }
3152
3153 // Check if loop paused
3154 if( loopState == 1 )
3155 {
3156 if( loopState != last_loopState )
3157 {
3158 sincelast_TT = 0;
3159 sincelast_F = 0;
3160 }
3161 sleep( 1 );
3163 continue;
3164 }
3165
3166 // Ok loop closed
3167
3168 bool haveNewRequests = false;
3169
3170 { // mutex scope
3171 std::lock_guard<std::mutex> lock( m_offloadMutex );
3173 }
3174
3175 // If we got a new offload request, process it
3176 if( haveNewRequests )
3177 {
3178 // std::cerr << m_firstRequest << " " << m_lastRequest << " " << m_nRequests << std::endl;
3179
3180 ///\todo offloading: These sections ought to be separate functions for clarity
3181 { // mutex scope
3182 std::lock_guard<std::mutex> lock( m_offloadMutex );
3183
3184 /* --- TT --- */
3185 avg_TT_0 = 0;
3186 avg_TT_1 = 0;
3187
3188 int navg = 0;
3189
3190 size_t i = m_lastRequest;
3191
3192 for( size_t n = 0; n < offlTTAvgInt; ++n )
3193 {
3196 ++navg;
3197
3198 if( i == m_firstRequest )
3199 break;
3200
3201 if( i == 0 )
3202 i = m_offloadRequests[0].size() - 1;
3203 else
3204 --i;
3205 }
3206
3207 avg_TT_0 /= navg;
3208 avg_TT_1 /= navg;
3209
3210 /* --- Focus --- */
3211 avg_F_0 = 0;
3212
3213 navg = 0;
3214
3215 i = m_lastRequest;
3216
3217 for( size_t n = 0; n < offlFAvgInt; ++n )
3218 {
3220 ++navg;
3221
3222 if( i == m_firstRequest )
3223 break;
3224
3225 if( i == 0 )
3226 i = m_offloadRequests[0].size() - 1;
3227 else
3228 --i;
3229 }
3230
3231 avg_F_0 /= navg;
3232
3234 }
3235
3236 ++sincelast_TT;
3237 if( sincelast_TT >= offlTTAvgInt )
3238 {
3240 sincelast_TT = 0;
3241 }
3242
3243 ++sincelast_F;
3244 if( sincelast_F >= offlFAvgInt )
3245 {
3247 sincelast_F = 0;
3248 }
3249 }
3251
3252 sleep( 1 );
3253 }
3254
3255 return;
3256}
3257
3258int tcsInterface::doTToffload( float tt_0, float tt_1 )
3259{
3260 bool offlTTDump;
3261 float offlTTGain;
3262 float offlTTThresh;
3263 bool offlTTEnabled;
3264 bool labMode;
3265
3266 { // mutex scope
3267 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3272 labMode = m_labMode;
3273 }
3274
3275 if( offlTTDump )
3276 {
3277 log<text_log>( "[OFFL] TT dumping: " + std::to_string( tt_0 ) + " " + std::to_string( tt_1 ) );
3279 { // mutex scope
3280 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3281 m_offlTT_dump = false;
3282 }
3283 }
3284 else
3285 {
3286 tt_0 *= offlTTGain;
3287 tt_1 *= offlTTGain;
3288
3289 if( fabs( tt_0 ) < offlTTThresh )
3290 tt_0 = 0;
3291 if( fabs( tt_1 ) < offlTTThresh )
3292 tt_1 = 0;
3293
3294 if( tt_0 == 0 && tt_1 == 0 )
3295 {
3296 // Do nothing
3297 }
3298 else if( offlTTEnabled )
3299 {
3300 log<text_log>( "[OFFL] TT offloading: " + std::to_string( tt_0 ) + " " + std::to_string( tt_1 ) );
3302 }
3303 else if( !labMode )
3304 {
3305 log<text_log>( "TT offload above threshold but TT offloading disabled", logPrio::LOG_WARNING );
3306 }
3307 }
3308
3309 return 0;
3310}
3311
3312int tcsInterface::sendTToffload( float tt_0, float tt_1 )
3313{
3314 bool labMode;
3315
3316 { // mutex scope
3317 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3318 labMode = m_labMode;
3319 }
3320
3321 if( labMode ) // If labMode we send offloads to the modulator
3322 {
3323 pcf::IndiProperty ip( pcf::IndiProperty::Number );
3324 ip.setDevice( "modwfs" );
3325 ip.setName( "offset12" );
3326 ip.add( pcf::IndiElement( "dC1" ) );
3327 ip.add( pcf::IndiElement( "dC2" ) );
3328
3330
3331 ip["dC1"] = tt_0;
3332 ip["dC2"] = tt_1;
3333
3335 return 0;
3336 }
3337
3338 char ttstr[64];
3339
3340 snprintf( ttstr, sizeof( ttstr ), "aeg %f %f", tt_0, tt_1 );
3341
3342 log<text_log>( std::string( "[OFFL] sending: " ) + ttstr );
3343
3345}
3346
3348{
3349 bool offlFDump;
3350 float offlFGain;
3351 float offlFThresh;
3352 bool offlFEnabled;
3353 bool labMode;
3354
3355 { // mutex scope
3356 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3361 labMode = m_labMode;
3362 }
3363
3364 if( offlFDump )
3365 {
3366 log<text_log>( "[OFFL] Focus dumping: " + std::to_string( F_0 ) );
3367 sendFoffload( F_0 );
3368 { // mutex scope
3369 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3370 m_offlF_dump = false;
3371 }
3372 }
3373 else
3374 {
3375 F_0 *= offlFGain;
3376
3377 if( fabs( F_0 ) < offlFThresh )
3378 F_0 = 0;
3379
3380 if( F_0 == 0 )
3381 {
3382 }
3383 else if( offlFEnabled )
3384 {
3385 log<text_log>( "[OFFL] Focus sending: " + std::to_string( F_0 ) );
3386 sendFoffload( F_0 );
3387 }
3388 else if( !labMode )
3389 {
3390
3391 log<text_log>( "Focus offload above threshold but Focus offloading disabled", logPrio::LOG_WARNING );
3392 }
3393 }
3394 return 0;
3395}
3396
3398{
3399 char fstr[64];
3400
3401 // Use zimr to update the IMA values
3402 snprintf( fstr, sizeof( fstr ), "zimr %f", F_0 );
3403
3404 log<text_log>( std::string( "[OFFL] sending: " ) + fstr );
3405
3407}
3408
3409INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_labMode )( const pcf::IndiProperty &ipRecv )
3410{
3411 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_labMode, ipRecv );
3412
3413 if( !ipRecv.find( "toggle" ) )
3414 {
3415 return log<software_error, -1>( { __FILE__, __LINE__, "no toggle element" } );
3416 }
3417
3418 bool labMode;
3419
3420 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3421 {
3422 labMode = true;
3423 }
3424 else
3425 {
3426 labMode = false;
3427 }
3428
3429 bool changed = false;
3430
3431 { // mutex scope
3432 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3433 if( m_labMode != labMode )
3434 {
3435 m_labMode = labMode;
3436 changed = true;
3437 }
3438 }
3439
3440 if( !changed )
3441 {
3442 return 0;
3443 }
3444
3445 if( labMode )
3446 {
3447 log<text_log>( "lab mode ON", logPrio::LOG_NOTICE );
3448 updateSwitchIfChanged( m_indiP_labMode, "toggle", pcf::IndiElement::On, INDI_OK );
3449 }
3450 else
3451 {
3452 log<text_log>( "lab mode OFF", logPrio::LOG_NOTICE );
3453 updateSwitchIfChanged( m_indiP_labMode, "toggle", pcf::IndiElement::Off, INDI_OK );
3454 }
3455
3456 recordTcsiLabMode();
3457
3458 return 0;
3459}
3460
3461INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_pyrNudge )( const pcf::IndiProperty &ipRecv )
3462{
3463 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_pyrNudge, ipRecv );
3464
3465 float x = 0;
3466 float y = 0;
3467 float z = 0;
3468
3469 if( ipRecv.find( "y" ) )
3470 {
3471 x = ipRecv["y"].get<float>();
3472 }
3473
3474 if( ipRecv.find( "x" ) )
3475 {
3476 y = ipRecv["x"].get<float>();
3477 }
3478
3479 if( ipRecv.find( "z" ) )
3480 {
3481 z = ipRecv["z"].get<float>();
3482 }
3483
3484 return sendPyrNudge( x, y, z );
3485}
3486
3487INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_acqFromGuider )( const pcf::IndiProperty &ipRecv )
3488{
3489 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_acqFromGuider, ipRecv );
3490
3491 if( !ipRecv.find( "request" ) )
3492 {
3493 return 0;
3494 }
3495
3496 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3497 {
3498 return acquireFromGuider();
3499 }
3500
3501 return 0;
3502}
3503
3504INDI_SETCALLBACK_DEFN( tcsInterface, m_indiP_loopState )( const pcf::IndiProperty &ipRecv )
3505{
3506 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_loopState, ipRecv );
3507
3508 if( !ipRecv.find( "toggle" ) )
3509 return 0;
3510
3511 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3512 {
3513 m_loopState.store( 2 );
3514 }
3515 else
3516 {
3517 m_loopState.store( 0 );
3518 }
3519
3520 return 0;
3521}
3522
3523INDI_SETCALLBACK_DEFN( tcsInterface, m_indiP_offloadCoeffs )( const pcf::IndiProperty &ipRecv )
3524{
3525 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offloadCoeffs, ipRecv );
3526
3527 if( m_loopState.load() != 2 )
3528 return 0;
3529
3530 // Tip-Tilt
3531 float tt0 = ipRecv["00"].get<float>();
3532 float tt1 = ipRecv["01"].get<float>();
3533
3534 bool labMode;
3535 float labOfflTTC00;
3536 float labOfflTTC01;
3537 float labOfflTTC10;
3538 float labOfflTTC11;
3539 float offlTTC00;
3540 float offlTTC01;
3541 float offlTTC10;
3542 float offlTTC11;
3543 float offlCFocus00;
3544 float offlCComa00;
3545 float offlCComa01;
3546 float offlCComa10;
3547 float offlCComa11;
3548
3549 { // mutex scope
3550 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3551 labMode = m_labMode;
3552 labOfflTTC00 = m_lab_offlTT_C_00;
3553 labOfflTTC01 = m_lab_offlTT_C_01;
3554 labOfflTTC10 = m_lab_offlTT_C_10;
3555 labOfflTTC11 = m_lab_offlTT_C_11;
3556 offlTTC00 = m_offlTT_C_00;
3557 offlTTC01 = m_offlTT_C_01;
3558 offlTTC10 = m_offlTT_C_10;
3559 offlTTC11 = m_offlTT_C_11;
3560 offlCFocus00 = m_offlCFocus_00;
3561 offlCComa00 = m_offlCComa_00;
3562 offlCComa01 = m_offlCComa_01;
3563 offlCComa10 = m_offlCComa_10;
3564 offlCComa11 = m_offlCComa_11;
3565 }
3566
3567 { // mutex scope
3568 std::lock_guard<std::mutex> lock( m_offloadMutex );
3569
3570 size_t nextReq = m_lastRequest + 1;
3571
3572 if( nextReq >= m_offloadRequests[0].size() )
3573 nextReq = 0;
3574
3575 if( labMode )
3576 {
3577 m_offloadRequests[0][nextReq] = labOfflTTC00 * tt0 + labOfflTTC01 * tt1;
3578 m_offloadRequests[1][nextReq] = labOfflTTC10 * tt0 + labOfflTTC11 * tt1;
3579 }
3580 else
3581 {
3582 m_offloadRequests[0][nextReq] = offlTTC00 * tt0 + offlTTC01 * tt1;
3583 m_offloadRequests[1][nextReq] = offlTTC10 * tt0 + offlTTC11 * tt1;
3584 }
3585
3586 // Focus
3587 float f0 = ipRecv["02"].get<float>();
3588
3589 m_offloadRequests[2][nextReq] = offlCFocus00 * f0;
3590
3591 // Coma
3592 float c0 = ipRecv["03"].get<float>();
3593 float c1 = ipRecv["04"].get<float>();
3594
3595 m_offloadRequests[3][nextReq] = offlCComa00 * c0 + offlCComa01 * c1;
3596 m_offloadRequests[4][nextReq] = offlCComa10 * c0 + offlCComa11 * c1;
3597
3598 // Now update circ buffer
3599 m_lastRequest = nextReq;
3600 ++m_nRequests;
3601 if( m_nRequests > m_offloadRequests[0].size() )
3602 ++m_firstRequest;
3603 if( m_firstRequest >= m_offloadRequests[0].size() )
3604 m_firstRequest = 0;
3605 }
3606 return 0;
3607}
3608
3609INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTenable )( const pcf::IndiProperty &ipRecv )
3610{
3611 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTenable, ipRecv );
3612
3613 if( ipRecv.getName() != m_indiP_offlTTenable.getName() )
3614 {
3615 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
3616 return -1;
3617 }
3618
3619 if( !ipRecv.find( "toggle" ) )
3620 return 0;
3621
3622 bool changed = false;
3623
3624 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3625 {
3626 { // mutex scope
3627 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3628 if( m_offlTT_enabled == false )
3629 {
3630 m_offlTT_enabled = true;
3631 changed = true;
3632 }
3633 }
3634 if( changed )
3635 {
3636 updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::On, INDI_OK );
3637 recordTcsiTipTilt();
3638 }
3639 }
3640 else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
3641 {
3642 { // mutex scope
3643 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3644 if( m_offlTT_enabled == true )
3645 {
3646 m_offlTT_enabled = false;
3647 changed = true;
3648 }
3649 }
3650 if( changed )
3651 {
3652 updateSwitchIfChanged( m_indiP_offlTTenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
3653 recordTcsiTipTilt();
3654 }
3655 }
3656
3657 return 0;
3658}
3659
3660INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTdump )( const pcf::IndiProperty &ipRecv )
3661{
3662 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTdump, ipRecv );
3663
3664 if( !ipRecv.find( "request" ) )
3665 return 0;
3666
3667 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3668 {
3669 updateSwitchIfChanged( m_indiP_offlTTdump, "request", pcf::IndiElement::On, INDI_OK );
3670
3671 { // mutex scope
3672 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3673 m_offlTT_dump = true;
3674 }
3675 }
3676
3677 return 0;
3678}
3679
3680INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTavgInt )( const pcf::IndiProperty &ipRecv )
3681{
3682 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTavgInt, ipRecv );
3683
3684 int target;
3685
3686 if( indiTargetUpdate( m_indiP_offlTTavgInt, target, ipRecv, true ) < 0 )
3687 {
3688 log<software_error>( { __FILE__, __LINE__ } );
3689 return -1;
3690 }
3691
3692 { // mutex scope
3693 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3694 m_offlTT_avgInt = target;
3695 }
3696 recordTcsiTipTilt();
3697
3698 return 0;
3699}
3700
3701INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTgain )( const pcf::IndiProperty &ipRecv )
3702{
3703 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTgain, ipRecv );
3704
3705 float target;
3706
3707 if( indiTargetUpdate( m_indiP_offlTTgain, target, ipRecv, true ) < 0 )
3708 {
3709 log<software_error>( { __FILE__, __LINE__ } );
3710 return -1;
3711 }
3712
3713 { // mutex scope
3714 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3715 m_offlTT_gain = target;
3716 }
3717 recordTcsiTipTilt();
3718
3719 return 0;
3720}
3721
3722INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlTTthresh )( const pcf::IndiProperty &ipRecv )
3723{
3724 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlTTthresh, ipRecv );
3725
3726 float target;
3727
3728 if( indiTargetUpdate( m_indiP_offlTTthresh, target, ipRecv, true ) < 0 )
3729 {
3730 log<software_error>( { __FILE__, __LINE__ } );
3731 return -1;
3732 }
3733
3734 { // mutex scope
3735 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3736 m_offlTT_thresh = target;
3737 }
3738 recordTcsiTipTilt();
3739
3740 return 0;
3741}
3742
3743INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFenable )( const pcf::IndiProperty &ipRecv )
3744{
3745 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFenable, ipRecv );
3746
3747 if( !ipRecv.find( "toggle" ) )
3748 return 0;
3749
3750 bool changed = false;
3751
3752 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3753 {
3754 { // mutex scope
3755 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3756 if( m_offlF_enabled == false )
3757 {
3758 m_offlF_enabled = true;
3759 changed = true;
3760 }
3761 }
3762 if( changed )
3763 {
3764 updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::On, INDI_OK );
3765 recordTcsiFocus();
3766 }
3767 }
3768 else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
3769 {
3770 { // mutex scope
3771 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3772 if( m_offlF_enabled == true )
3773 {
3774 m_offlF_enabled = false;
3775 changed = true;
3776 }
3777 }
3778 if( changed )
3779 {
3780 updateSwitchIfChanged( m_indiP_offlFenable, "toggle", pcf::IndiElement::Off, INDI_IDLE );
3781 recordTcsiFocus();
3782 }
3783 }
3784
3785 return 0;
3786}
3787
3788INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFdump )( const pcf::IndiProperty &ipRecv )
3789{
3790 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFdump, ipRecv );
3791
3792 if( !ipRecv.find( "request" ) )
3793 return 0;
3794
3795 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3796 {
3797 updateSwitchIfChanged( m_indiP_offlFdump, "request", pcf::IndiElement::On, INDI_OK );
3798
3799 { // mutex scope
3800 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3801 m_offlF_dump = true;
3802 }
3803 }
3804
3805 return 0;
3806}
3807
3808INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFavgInt )( const pcf::IndiProperty &ipRecv )
3809{
3810 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFavgInt, ipRecv );
3811
3812 int target;
3813
3814 if( indiTargetUpdate( m_indiP_offlFavgInt, target, ipRecv, true ) < 0 )
3815 {
3816 log<software_error>( { __FILE__, __LINE__ } );
3817 return -1;
3818 }
3819
3820 { // mutex scope
3821 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3822 m_offlF_avgInt = target;
3823 }
3824 recordTcsiFocus();
3825
3826 return 0;
3827}
3828
3829INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFgain )( const pcf::IndiProperty &ipRecv )
3830{
3831 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFgain, ipRecv );
3832
3833 float target;
3834
3835 if( indiTargetUpdate( m_indiP_offlFgain, target, ipRecv, true ) < 0 )
3836 {
3837 log<software_error>( { __FILE__, __LINE__ } );
3838 return -1;
3839 }
3840
3841 { // mutex scope
3842 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3843 m_offlF_gain = target;
3844 }
3845 recordTcsiFocus();
3846
3847 return 0;
3848}
3849
3850INDI_NEWCALLBACK_DEFN( tcsInterface, m_indiP_offlFthresh )( const pcf::IndiProperty &ipRecv )
3851{
3852 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_offlFthresh, ipRecv );
3853
3854 float target;
3855
3856 std::cerr << "Got offl thresh\n";
3857
3858 if( indiTargetUpdate( m_indiP_offlFthresh, target, ipRecv, true ) < 0 )
3859 {
3860 log<software_error>( { __FILE__, __LINE__ } );
3861 return -1;
3862 }
3863
3864 { // mutex scope
3865 std::lock_guard<std::mutex> lock( m_offloadCtrlMutex );
3866 m_offlF_thresh = target;
3867 }
3868 recordTcsiFocus();
3869
3870 return 0;
3871}
3872
3873} // namespace app
3874} // namespace MagAOX
3875
3876#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.
float m_offlTT_avgInt
The number of recent requests included in each tip/tilt offload average.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlFgain)
int checkRecordTimes()
Check whether any telemetry records are due.
INDI_NEWCALLBACK_DECL(tcsInterface, m_indiP_offlTTgain)
double m_dimm_el
DIMM elevation at time of seeing measurement.
size_t m_lastRequest
Index of the newest valid request in the offload ring.
pcf::IndiProperty m_indiP_offlTTdump
size_t m_last_nRequests
Request count last processed by the offload thread.
double m_wxdewpoint
Dew point from weather station.
int recordTelem(const telem_telpos *)
Record telescope-position telemetry on a forced telemeter update.
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 recordTcsiTipTilt(bool force=false)
Record tip/tilt offload-control telemetry when values change.
int m_telGuiding
guider moving state
int recordTcsiFocus(bool force=false)
Record focus offload-control telemetry when values change.
float m_offlF_avgInt
The number of recent requests included in each focus offload average.
int sendMagTelCommand(const std::string &command, int timeout)
int recordTelCat(bool force=false)
Record catalog telemetry when values change.
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)
Send a tip/tilt offload command to the selected target.
pcf::IndiProperty m_indiP_pyrNudge
Property used to request a pyramid nudge.
int recordTcsiLabMode(bool force=false)
Record lab-mode telemetry when values change.
int recordTelPos(bool force=false)
Record telescope-position telemetry when values change.
int recordTelEnv(bool force=false)
Record telescope-environment telemetry when values change.
pcf::IndiProperty m_indiP_telpos
int recordTelData(bool force=false)
Record telescope-status telemetry when values change.
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
float m_offlF_gain
The operator-set gain applied to focus offload requests.
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
float m_offlF_thresh
The deadband threshold applied to focus offload requests.
bool m_offlTT_enabled
Tracks whether tip/tilt offloading is currently enabled.
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.
size_t m_nRequests
Number of valid requests currently stored in the offload ring.
int getSeeing()
Query, parse, and record seeing measurements.
int sendFoffload(float F_0)
Send a focus offload command to the telescope.
int badSeeing()
Record bad seeing measurements on errors in getSeeing().
bool m_labMode
Tracks whether the app is operating in lab mode rather than on-sky mode.
pcf::IndiProperty m_indiP_offlTTenable
double m_ttruss
Telescope truss temperature, Celsius.
int doTToffload(float TT_0, float TT_1)
Apply tip/tilt offload controls to an averaged request vector.
pcf::IndiProperty m_indiP_catalog
INDI Property for the catalog text information.
double m_wxhumid
Outside humidity, percent.
virtual int appStartup()
Startup function.
bool m_offlF_enabled
Tracks whether focus offloading is currently enabled.
int recordTelVane(bool force=false)
Record vane-position telemetry when values change.
virtual int appShutdown()
Shutdown the app.
int recordTelSee(bool force=false)
Record seeing telemetry when values change.
int doFoffload(float F_0)
Apply focus offload controls to an averaged request value.
float m_offlTT_gain
The operator-set gain applied to tip/tilt offload requests.
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
int m_telSlewing
slewing state
std::mutex m_offloadMutex
Protects the offload request ring and its queue-state indices across producer and consumer threads.
int m_telROI
The rotator of interest.
int m_telTracking
tracking state
size_t m_firstRequest
Index of the oldest valid request in the offload ring.
float m_offlTT_thresh
The deadband threshold applied to tip/tilt offload requests.
double m_catRo
Catalog rotator offset.
std::mutex m_offloadCtrlMutex
Protects shared offload control parameters accessed by callbacks and the offload thread.
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
Ring buffer of modal offload requests indexed by mode and request slot.
int sendPyrNudge(float x, float y, float z)
INDI_SETCALLBACK_DECL(tcsInterface, m_indiP_loopState)
std::atomic< int > m_loopState
Tracks the current loop state shared between the INDI callback and the offload thread.
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.
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
std::unique_lock< std::mutex > lock(m_indiMutex)
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.
@ 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.
Software ERR log entry.
Focus offload-control telemetry.
tcsInterface lab-mode telemetry.
Tip/tilt offload-control telemetry.
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