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