API
 
Loading...
Searching...
No Matches
hwpTracker.hpp
Go to the documentation of this file.
1/** \file hwpTracker.hpp
2 * \brief The MagAO-X HWP rotation tracker header file
3 *
4 * \ingroup hwpTracker_files
5 */
6
7#ifndef hwpTracker_hpp
8#define hwpTracker_hpp
9
10#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
11#include "../../magaox_git_version.h"
12
13#include <mx/math/gslInterpolation.hpp>
14#include <mx/ioutils/readColumns.hpp>
15
16/** \defgroup hwpTracker
17 * \brief The MagAO-X application to track pupil rotation with the HWP.
18 *
19 * <a href="../handbook/operating/software/apps/hwpTracker.html">Application Documentation</a>
20 *
21 * \ingroup apps
22 *
23 */
24
25/** \defgroup hwpTracker_files
26 * \ingroup hwpTracker
27 */
28
29namespace MagAOX
30{
31namespace app
32{
33
34/// The MagAO-X ADC Tracker
35/**
36 * \ingroup hwpTracker
37 */
38class hwpTracker : public MagAOXApp<true>, public dev::telemeter<hwpTracker>
39{
40
41 // Give the test harness access.
42 friend class hwpTracker_test;
43
44 friend class dev::telemeter<hwpTracker>;
45
47
48 protected:
49 /** \name Configurable Parameters
50 *@{
51 */
52 float m_altitude{ 0 }; ///< Current altitude
53
54 float m_parang{ 0 }; ///< Current parallactic angle
55
56 float m_hwpTrackingOffset{ 0 }; ///< HWP tracking offset
57
58 float m_hwpSetPos{ 0 };
59
60 float m_hwpCurPos{ 0 };
61
62 float m_hwpActualPos{ 0 };
63
64 std::string m_hwpPosName{ "" };
65
66 float m_zero{ 360 }; ///< The zero point of the HWP stage.
67
68 int m_sign{ -1 }; ///< The sign to apply to the calculated HWP angle.
69
70 std::string m_devName{ "stagepolrot" }; ///< The device name of the HWP stage. Default is 'stagehwprot'
71
72 std::string m_tcsDevName{
73 "tcsi" }; ///< The device name of the TCS Interface providing 'teldata.altitude'. Default is 'tcsi'
74
75 float m_updateInterval{ 10 }; ///< The interval at which to update positions, in seconds. Default is 10 secs.
76
77 float m_pupilOffset{ 0 }; ///< The pupil offset used for HWP tracking (must match twice the kTracker offset)
78
79 bool m_tracking{ false }; ///< Is the HWP in ADI synchronization mode?. Default is false.
80
81 public:
82 /// Default c'tor.
83 hwpTracker();
84
85 /// D'tor, declared and defined for noexcept.
87 {
88 }
89
90 virtual void setupConfig();
91
92 /// Implementation of loadConfig logic, separated for testing.
93 /** This is called by loadConfig().
94 */
96 mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
97
98 virtual void loadConfig();
99
100 /// Startup function
101 /**
102 *
103 */
104 virtual int appStartup();
105
106 /// Implementation of the FSM for hwpTracker.
107 /**
108 * \returns 0 on no critical error
109 * \returns -1 on an error requiring shutdown
110 */
111 virtual int appLogic();
112
113 /// Shutdown the app.
114 /**
115 *
116 */
117 virtual int appShutdown();
118
119 virtual std::string getHwpStatus();
120
121 virtual void getHwpTrackingOffset();
122
123 virtual void updateHwpPos();
124
125 /** @name INDI
126 *
127 * @{
128 */
129 protected:
130 pcf::IndiProperty m_indiP_tracking;
131
132 pcf::IndiProperty m_indiP_teldata;
133
134 pcf::IndiProperty m_indiP_hwpSetPos;
135
136 pcf::IndiProperty m_indiP_hwpPosName;
137
138 pcf::IndiProperty m_indiP_hwpTrackingOffset;
139
140 pcf::IndiProperty m_indiP_hwpActualPos;
141
142 pcf::IndiProperty m_indiP_hwpStagePos;
143
144 pcf::IndiProperty m_indiP_stagePolRot;
145
146 pcf::IndiProperty m_indiP_stagePolRotFsm;
147
148 public:
150
152
154
156
158
159 ///@}
160
161 /** \name Telemeter Interface
162 *
163 * @{
164 */
165 int checkRecordTimes();
166
167 int recordTelem( const telem_poltrack * );
168
169 int recordPolTrack( bool force = false );
170
171 ///@}
172};
173
174hwpTracker::hwpTracker() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
175{
176 return;
177}
178
180{
181 config.add( "hwp.zero",
182 "",
183 "hwp.zero",
184 argType::Required,
185 "hwp",
186 "zero",
187 false,
188 "float",
189 "The HWP zero position. Default is 360." );
190
191 config.add( "hwp.sign",
192 "",
193 "hwp.sign",
194 argType::Required,
195 "hwp",
196 "sign",
197 false,
198 "int",
199 "The HWP rotation sign. Default is -1." );
200
201 config.add( "hwp.devName",
202 "",
203 "hwp.devName",
204 argType::Required,
205 "hwp",
206 "devName",
207 false,
208 "string",
209 "The device name of the HWPstage. Default is 'stagepolrot'" );
210
211 config.add( "tcs.devName",
212 "",
213 "tcs.devName",
214 argType::Required,
215 "tcs",
216 "devName",
217 false,
218 "string",
219 "The device name of the TCS Interface providing 'teldata.zd' and `teldata.pa`. Default is 'tcsi'" );
220
221 config.add( "tracking.updateInterval",
222 "",
223 "tracking.updateInterval",
224 argType::Required,
225 "tracking",
226 "updateInterval",
227 false,
228 "float",
229 "The interval at which to update positions, in seconds. Default is 1 sec." );
230
231
232 config.add( "tracking.pupilOffset",
233 "",
234 "tracking.pupilOffset",
235 argType::Required,
236 "tracking",
237 "pupilOffset",
238 false,
239 "float",
240 "The HWP tracking pupil offset. Must match twice the kTracker offset. Default is 0." );
241
242
243 TELEMETER_SETUP_CONFIG( config );
244}
245
246int hwpTracker::loadConfigImpl( mx::app::appConfigurator &_config )
247{
248 _config( m_zero, "hwp.zero" );
249 _config( m_sign, "hwp.sign" );
250 _config( m_devName, "hwp.devName" );
251 _config( m_tcsDevName, "tcs.devName" );
252 _config( m_updateInterval, "tracking.updateInterval" );
253 _config( m_pupilOffset, "tracking.pupilOffset" );
254
256
257 return 0;
258}
259
261{
262 loadConfigImpl( config );
263}
264
266{
267
270
272
274
276
278 m_indiP_hwpSetPos, "hwp_position", -360.0, 360.0, 1e-3, "%.03f", "HWP Set Position", "HWP Status" );
280
281 REG_INDI_NEWPROP_NOCB( m_indiP_hwpTrackingOffset, "hwp_tracking_offset", pcf::IndiProperty::Number );
282 m_indiP_hwpTrackingOffset.add( pcf::IndiElement( "value" ) );
283 m_indiP_hwpTrackingOffset["value"].set( 0 );
284
285 REG_INDI_NEWPROP_NOCB( m_indiP_hwpPosName, "hwp_position_name", pcf::IndiProperty::Text );
286 m_indiP_hwpPosName.add( pcf::IndiElement( "value" ) );
287 m_indiP_hwpPosName["value"].set( "" );
288
289 REG_INDI_NEWPROP_NOCB( m_indiP_hwpActualPos, "hwp_position_actual", pcf::IndiProperty::Number );
290 m_indiP_hwpActualPos.add( pcf::IndiElement( "value" ) );
291 m_indiP_hwpActualPos["value"].set( 0 );
292
293 m_indiP_hwpStagePos = pcf::IndiProperty( pcf::IndiProperty::Number );
294 m_indiP_hwpStagePos.setDevice( m_devName );
295 m_indiP_hwpStagePos.setName( "position" );
296 m_indiP_hwpStagePos.add( pcf::IndiElement( "target" ) );
297
299
301
302 return 0;
303}
304
306{
307
308 static double lastupdate = 0;
309
310 if( m_tracking && mx::sys::get_curr_time() - lastupdate > m_updateInterval )
311 {
312
314
316
317 updateHwpPos();
318
319 lastupdate = mx::sys::get_curr_time();
320 }
321 else
322 {
323 if( !m_tracking )
324 lastupdate = 0;
325 }
326
328
329 return 0;
330}
331
333{
335
336 return 0;
337}
338
340{
341 float tol = 0.5; // degrees
342 if( fabs( m_hwpCurPos - 0 ) < tol )
343 return "Qplus";
344 else if( fabs( m_hwpCurPos - 45 ) < tol )
345 return "Qminus";
346 else if( fabs( m_hwpCurPos - 22.5 ) < tol )
347 return "Uplus";
348 else if( fabs( m_hwpCurPos - 67.5 ) < tol )
349 return "Uminus";
350 else
351 return "Unknown";
352}
353
355{
356 // While on Nasmyth East, the sign is negative
358}
359
361{
363
365
366 std::cerr << "HWP set to: " << hwpActualPos << "\n";
367 std::cerr << "Sending HWP stage to: " << hwpStagePos << "\n";
368 log<text_log>( "HWP set to: " + std::to_string( hwpActualPos ) );
369
372
374}
375
376INDI_NEWCALLBACK_DEFN( hwpTracker, m_indiP_hwpSetPos )( const pcf::IndiProperty &ipRecv )
377{
378
379 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_hwpSetPos, ipRecv );
380
381 if( ipRecv.getName() != m_indiP_hwpSetPos.getName() )
382 {
383 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
384 return -1;
385 }
386
387 if( !ipRecv.find( "target" ) )
388 return 0;
389
390 m_hwpSetPos = ipRecv["target"].get<float>();
391
392 updateIfChanged<float>(m_indiP_hwpSetPos, "target", m_hwpSetPos);
393
394 updateHwpPos();
395
396 return 0;
397}
398
399INDI_NEWCALLBACK_DEFN( hwpTracker, m_indiP_tracking )( const pcf::IndiProperty &ipRecv )
400{
401
402 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_tracking, ipRecv );
403
404 if( ipRecv.getName() != m_indiP_tracking.getName() )
405 {
406 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received." } );
407 return -1;
408 }
409
410 if( !ipRecv.find( "toggle" ) )
411 {
412 return 0;
413 }
414
415 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
416 {
417 updateSwitchIfChanged( m_indiP_tracking, "toggle", pcf::IndiElement::On, INDI_IDLE );
418
419 m_tracking = true;
420
421 getHwpTrackingOffset();
422
423 updateIfChanged<float>( m_indiP_hwpTrackingOffset, "value", m_hwpTrackingOffset );
424
425 updateHwpPos();
426
427 log<text_log>( "started HWP rotation tracking" );
428 }
429 else
430 {
431 updateSwitchIfChanged( m_indiP_tracking, "toggle", pcf::IndiElement::Off, INDI_IDLE );
432
433 m_tracking = false;
434
435 m_hwpTrackingOffset = 0;
436
437 updateIfChanged<float>( m_indiP_hwpTrackingOffset, "value", m_hwpTrackingOffset );
438
439 updateHwpPos();
440
441 log<text_log>( "stopped HWP rotation tracking" );
442 }
443
444 return 0;
445}
446
447INDI_SETCALLBACK_DEFN( hwpTracker, m_indiP_teldata )( const pcf::IndiProperty &ipRecv )
448{
449
450 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_teldata, ipRecv );
451
452 if( ipRecv.getName() != m_indiP_teldata.getName() )
453 {
454 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received" } );
455
456 return -1;
457 }
458
459 if( !ipRecv.find( "zd" ) )
460 return 0;
461
462 if( !ipRecv.find( "pa" ) )
463 return 0;
464
465 m_altitude = 90 - ipRecv["zd"].get<float>();
466
467 m_parang = ipRecv["pa"].get<float>();
468
469 return 0;
470}
471
472INDI_SETCALLBACK_DEFN( hwpTracker, m_indiP_stagePolRot )( const pcf::IndiProperty &ipRecv )
473{
474
475 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_stagePolRot, ipRecv );
476
477 if( ipRecv.getName() != m_indiP_stagePolRot.getName() )
478 {
479 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received" } );
480
481 return -1;
482 }
483
484 if( !ipRecv.find( "current" ) )
485 return 0;
486
487 float hwpStagePos = ipRecv["current"].get<float>();
488
489 m_hwpActualPos = m_sign * (hwpStagePos - m_zero);
490 if (fabs(m_hwpActualPos) < 1e-2)
491 m_hwpActualPos = 0;
492 updateIfChanged<float>(m_indiP_hwpActualPos, "value", m_hwpActualPos);
493
494 m_hwpCurPos = m_hwpActualPos - m_hwpTrackingOffset;
495 // round to two decimal points
496 m_hwpCurPos = std::round(m_hwpCurPos * 100) / 100;
497 updateIfChanged<float>(m_indiP_hwpSetPos, "current", m_hwpCurPos);
498
499 m_hwpPosName = getHwpStatus();
500
501 updateIfChanged<std::string>( m_indiP_hwpPosName, "value", m_hwpPosName );
502
503 recordPolTrack();
504
505 return 0;
506}
507
508INDI_SETCALLBACK_DEFN( hwpTracker, m_indiP_stagePolRotFsm )( const pcf::IndiProperty &ipRecv )
509{
510
511 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_stagePolRotFsm, ipRecv );
512
513 if( ipRecv.getName() != m_indiP_stagePolRotFsm.getName() )
514 {
515 log<software_error>( { __FILE__, __LINE__, "wrong INDI property received" } );
516
517 return -1;
518 }
519
520 if( !ipRecv.find( "state" ) )
521 return 0;
522
523 std::string stagepolrot_state = ipRecv["state"].get<std::string>();
524
525 state(stateCodes::str2CodeFast(stagepolrot_state));
526
527 return 0;
528}
529
534
536{
537 return recordPolTrack( true );
538}
539
541{
542 static float hwpSetPos = 0;
543
544 static float hwpActualPos = 0;
545
546 static std::string hwpPosName = "";
547
548 static bool tracking = false;
549
551 {
553
557 tracking = m_tracking;
558 }
559
560 return 0;
561}
562
563} // namespace app
564} // namespace MagAOX
565
566#endif // hwpTracker_hpp
The base-class for XWCTk applications.
stateCodes::stateCodeT state()
Get the current state code.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
int sendNewProperty(const pcf::IndiProperty &ipSend, const std::string &el, const T &newVal)
Send a newProperty command to another device (using the INDI Client interface)
The MagAO-X ADC Tracker.
virtual int appLogic()
Implementation of the FSM for hwpTracker.
virtual void loadConfig()
virtual void updateHwpPos()
pcf::IndiProperty m_indiP_hwpPosName
int recordPolTrack(bool force=false)
virtual int appShutdown()
Shutdown the app.
INDI_SETCALLBACK_DECL(hwpTracker, m_indiP_stagePolRot)
~hwpTracker() noexcept
D'tor, declared and defined for noexcept.
pcf::IndiProperty m_indiP_hwpTrackingOffset
float m_hwpTrackingOffset
HWP tracking offset.
hwpTracker()
Default c'tor.
INDI_NEWCALLBACK_DECL(hwpTracker, m_indiP_hwpSetPos)
virtual void getHwpTrackingOffset()
pcf::IndiProperty m_indiP_tracking
dev::telemeter< hwpTracker > telemeterT
pcf::IndiProperty m_indiP_stagePolRot
std::string m_tcsDevName
The device name of the TCS Interface providing 'teldata.altitude'. Default is 'tcsi'.
INDI_SETCALLBACK_DECL(hwpTracker, m_indiP_teldata)
friend class hwpTracker_test
pcf::IndiProperty m_indiP_hwpActualPos
float m_altitude
Current altitude.
pcf::IndiProperty m_indiP_teldata
pcf::IndiProperty m_indiP_hwpStagePos
int m_sign
The sign to apply to the calculated HWP angle.
bool m_tracking
Is the HWP in ADI synchronization mode?. Default is false.
INDI_NEWCALLBACK_DECL(hwpTracker, m_indiP_tracking)
virtual int appStartup()
Startup function.
pcf::IndiProperty m_indiP_stagePolRotFsm
int recordTelem(const telem_poltrack *)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
virtual std::string getHwpStatus()
float m_pupilOffset
The pupil offset used for HWP tracking (must match twice the kTracker offset)
float m_updateInterval
The interval at which to update positions, in seconds. Default is 10 secs.
virtual void setupConfig()
float m_parang
Current parallactic angle.
pcf::IndiProperty m_indiP_hwpSetPos
float m_zero
The zero point of the HWP stage.
INDI_SETCALLBACK_DECL(hwpTracker, m_indiP_stagePolRotFsm)
std::string m_devName
The device name of the HWP stage. Default is 'stagehwprot'.
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
@ READY
The device is ready for operation, but is not operating.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
const pcf::IndiProperty & ipRecv
Definition dm.hpp:19
A device base class which saves telemetry.
Definition telemeter.hpp:75
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
static stateCodeT str2CodeFast(const std::string &stateStr)
Get the stateCode corresponding to an ASCII string with minimal checks.
Log entry recording poltrack stage specific status.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_LOAD_CONFIG(cfig)
Call telemeter::loadConfig with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_SETUP_CONFIG(cfig)
Call telemeter::setupConfig with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.