API
adcTracker.hpp
Go to the documentation of this file.
1 /** \file adcTracker.hpp
2  * \brief The MagAO-X ADC Tracker header file
3  *
4  * \ingroup adcTracker_files
5  */
6 
7 #ifndef adcTracker_hpp
8 #define adcTracker_hpp
9 
10 
11 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
12 #include "../../magaox_git_version.h"
13 
14 #include <mx/math/gslInterpolator.hpp>
15 #include <mx/ioutils/readColumns.hpp>
16 
17 /** \defgroup adcTracker
18  * \brief The MagAO-X application to track sky rotation with the atmospheric dispersion corrector.
19  *
20  * <a href="../handbook/operating/software/apps/adcTracker.html">Application Documentation</a>
21  *
22  * \ingroup apps
23  *
24  */
25 
26 /** \defgroup adcTracker_files
27  * \ingroup adcTracker
28  */
29 
30 namespace MagAOX
31 {
32 namespace app
33 {
34 
35 /// The MagAO-X ADC Tracker
36 /**
37  * \ingroup adcTracker
38  */
39 class adcTracker : public MagAOXApp<true>
40 {
41 
42  //Give the test harness access.
43  friend class adcTracker_test;
44 
45 protected:
46 
47  /** \name Configurable Parameters
48  *@{
49  */
50 
51  //here add parameters which will be config-able at runtime
52  std::string m_lookupFile {"adc_lookup_table.txt"}; ///< The name of the file, in the calib directory, containing the adc lookup table. Default is 'adc_lookup_table.txt'.
53 
54 
55  float m_adc1zero {0}; ///< The starting point for ADC 1. Default is 0.
56 
57  int m_adc1lupsign {1}; ///< The sign to apply to the lookup table value for ADC 1
58 
59  float m_adc2zero {0}; ///< The starting point for ADC 2. Default is 0.
60 
61  int m_adc2lupsign {1}; ///< The sign to apply to the lookup table value for ADC 2
62 
63  float m_deltaAngle {0}; ///< The offset angle to apply to the looked-up values, applied to both. Default is 0.
64 
65  float m_adc1delta {0}; ///< The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle. Default is 0.
66 
67  float m_adc2delta {0}; ///< The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle. Default is 0.
68 
69  float m_minZD {5.1}; ///< "The minimum zenith distance at which to interpolate and move the ADCs. Default is 0.
70 
71 
72  std::string m_adc1DevName {"stageadc1"}; ///< The device name of the ADC 1 stage. Default is 'stageadc1'
73  std::string m_adc2DevName {"stageadc2"}; ///< The device name of the ADC 2 stage. Default is 'stageadc2'
74 
75  std::string m_tcsDevName {"tcsi"}; ///< The device name of the TCS Interface providing 'teldata.zd'. Default is 'tcsi'
76 
77  float m_updateInterval {10};
78 
79  ///@}
80 
81  float m_maxZD {60};
82 
83  std::vector<double> m_lupZD;
84  std::vector<double> m_lupADC1;
85  std::vector<double> m_lupADC2;
86 
87  mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>> m_terpADC1;
88  mx::math::gslInterpolator<mx::math::gsl_interp_linear<double>> m_terpADC2;
89 
90  bool m_tracking {false};
91 
92  float m_zd {0};
93 
94 public:
95  /// Default c'tor.
96  adcTracker();
97 
98  /// D'tor, declared and defined for noexcept.
99  ~adcTracker() noexcept
100  {}
101 
102  virtual void setupConfig();
103 
104  /// Implementation of loadConfig logic, separated for testing.
105  /** This is called by loadConfig().
106  */
107  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
108 
109  virtual void loadConfig();
110 
111  /// Startup function
112  /**
113  *
114  */
115  virtual int appStartup();
116 
117  /// Implementation of the FSM for adcTracker.
118  /**
119  * \returns 0 on no critical error
120  * \returns -1 on an error requiring shutdown
121  */
122  virtual int appLogic();
123 
124  /// Shutdown the app.
125  /**
126  *
127  */
128  virtual int appShutdown();
129 
130 
131  /** @name INDI
132  *
133  * @{
134  */
135 protected:
136 
137  pcf::IndiProperty m_indiP_tracking;
138 
139  pcf::IndiProperty m_indiP_deltaAngle;
140  pcf::IndiProperty m_indiP_deltaADC1;
141  pcf::IndiProperty m_indiP_deltaADC2;
142 
143  pcf::IndiProperty m_indiP_minZD;
144 
145 
146  pcf::IndiProperty m_indiP_teldata;
147 
148 
149  pcf::IndiProperty m_indiP_adc1pos;
150  pcf::IndiProperty m_indiP_adc2pos;
151 
152 public:
154 
158 
160 
162 
163 
164 
165  ///@}
166 };
167 
168 adcTracker::adcTracker() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
169 {
170 
171  return;
172 }
173 
175 {
176  config.add("adcs.lookupFile", "", "adcs.lookupFile", argType::Required, "adcs", "lookupFile", false, "string", "The name of the file, in the calib directory, containing the adc lookup table. Default is 'adc_lookup_table.txt'.");
177 
178  config.add("adcs.adc1zero", "", "adcs.adc1zero", argType::Required, "adcs", "adc1zero", false, "float", "The starting point for ADC 1. Default is 0.");
179 
180  config.add("adcs.adc1lupsign", "", "adcs.adc1lupsign", argType::Required, "adcs", "adc1lupsign", false, "int", "The sign to apply for the LUP values for ADC 1. Default is +1.");
181 
182  config.add("adcs.adc2zero", "", "adcs.adc2zero", argType::Required, "adcs", "adc2zero", false, "float", "The starting point for ADC 2. Default is 0.");
183 
184  config.add("adcs.adc2lupsign", "", "adcs.adc2lupsign", argType::Required, "adcs", "adc2lupsign", false, "int", "The sign to apply for the LUP values for ADC 2. Default is +1.");
185 
186  config.add("adcs.deltaAngle", "", "adcs.deltaAngle", argType::Required, "adcs", "deltaAngle", false, "float", "The offset angle to apply to the looked-up values, applied to both. Default is 0.");
187 
188  config.add("adcs.adc1delta", "", "adcs.adc1delta", argType::Required, "adcs", "adc1delta", false, "float", "The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle. Default is 0.");
189 
190  config.add("adcs.adc2delta", "", "adcs.adc2delta", argType::Required, "adcs", "adc2delta", false, "float", "The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle. Default is 0.");
191 
192  config.add("adcs.minZD", "", "adcs.minZD", argType::Required, "adcs", "minZD", false, "float", "The minimum zenith distance at which to interpolate and move the ADCs. Default is 5.1");
193 
194  config.add("adcs.adc1DevName", "", "adcs.adc1devName", argType::Required, "adcs", "adc1DevName", false, "string", "The device name of the ADC 1 stage. Default is 'stageadc1'");
195 
196  config.add("adcs.adc2DevName", "", "adcs.adc2devName", argType::Required, "adcs", "adc2DevName", false, "string", "The device name of the ADC 2 stage. Default is 'stageadc2'");
197 
198  config.add("tcs.devName", "", "tcs.devName", argType::Required, "tcs", "devName", false, "string", "The device name of the TCS Interface providing 'teldata.zd'. Default is 'tcsi'");
199 
200  config.add("tracking.updateInterval", "", "tracking.updateInterval", argType::Required, "tracking", "updateInterval", false, "float", "The interval at which to update positions, in seconds. Default is 10 secs.");
201 }
202 
203 int adcTracker::loadConfigImpl( mx::app::appConfigurator & _config )
204 {
205  _config(m_lookupFile, "adcs.lookupFile");
206  _config(m_adc1zero, "adcs.adc1zero");
207  _config(m_adc1lupsign, "adcs.adc1lupsign");
208  _config(m_adc2zero, "adcs.adc2zero");
209  _config(m_adc2lupsign, "adcs.adc2lupsign");
210  _config(m_deltaAngle, "adcs.deltaAngle");
211  _config(m_adc1delta, "adcs.adc1delta");
212  _config(m_adc2delta, "adcs.adc2delta");
213  _config(m_minZD, "adcs.minZD");
214  _config(m_adc1DevName, "adcs.adc1DevName");
215  _config(m_adc2DevName, "adcs.adc2DevName");
216 
217  _config(m_tcsDevName, "tcs.devName");
218 
219  _config(m_updateInterval, "tracking.updateInterval");
220 
221  return 0;
222 }
223 
225 {
226  loadConfigImpl(config);
227 }
228 
230 {
231 
232  std::string luppath = m_calibDir + "/" + m_lookupFile;
233 
234  std::cerr << "Reading " << luppath << "\n";
235 
236  if(mx::ioutils::readColumns<','>(luppath, m_lupZD, m_lupADC1, m_lupADC2) < 0)
237  {
238  log<software_critical>({__FILE__,__LINE__, "error reading lookup table from " + luppath});
239  return -1;
240  }
241 
242  if(m_lupZD.size() != m_lupADC1.size() || m_lupZD.size()!= m_lupADC2.size())
243  {
244  log<software_critical>({__FILE__,__LINE__, "inconsistent sizes in " + luppath});
245  return -1;
246  }
247 
248  log<text_log>("Read " + std::to_string(m_lupZD.size()) + " points from " + m_lookupFile);
249 
250  m_terpADC1.setup(m_lupZD, m_lupADC1);
251  m_terpADC2.setup(m_lupZD, m_lupADC2);
252 
255 
256  createStandardIndiNumber<float>( m_indiP_deltaAngle, "deltaAngle", 0.0, 180.0, 0, "%0.2f");
257  m_indiP_deltaAngle["target"].set(m_deltaAngle);
258  m_indiP_deltaAngle["current"].set(m_deltaAngle);
260 
261  createStandardIndiNumber<float>( m_indiP_deltaADC1, "deltaADC1", 0.0, 180.0, 0, "%0.2f");
262  m_indiP_deltaADC1["target"].set(m_adc1delta);
263  m_indiP_deltaADC1["current"].set(m_adc1delta);
265 
266  createStandardIndiNumber<float>( m_indiP_deltaADC2, "deltaADC2", 0.0, 180.0, 0, "%0.2f");
267  m_indiP_deltaADC2["target"].set(m_adc2delta);
268  m_indiP_deltaADC2["current"].set(m_adc2delta);
270 
271  createStandardIndiNumber<float>( m_indiP_minZD, "minZD", 0.0, 90.0, 0, "%0.2f");
272  m_indiP_minZD["target"].set(m_minZD);
273  m_indiP_minZD["current"].set(m_minZD);
275 
277 
278  m_indiP_adc1pos = pcf::IndiProperty(pcf::IndiProperty::Number);
279  m_indiP_adc1pos.setDevice(m_adc1DevName);
280  m_indiP_adc1pos.setName("position");
281  m_indiP_adc1pos.add(pcf::IndiElement("target"));
282 
283  m_indiP_adc2pos = pcf::IndiProperty(pcf::IndiProperty::Number);
284  m_indiP_adc2pos.setDevice(m_adc2DevName);
285  m_indiP_adc2pos.setName("position");
286  m_indiP_adc2pos.add(pcf::IndiElement("target"));
287 
289 
290  return 0;
291 }
292 
294 {
295 
296  static double lastupdate = 0;
297 
298  if(m_tracking && mx::sys::get_curr_time() - lastupdate > m_updateInterval)
299  {
300  float dadc1 = 0.0;
301  float dadc2 = 0.0;
302 
303  if(m_zd > m_lupZD.back())
304  {
305  std::cerr << "end of lup\n";
306  dadc1 = m_lupADC1.back();
307  dadc2 = m_lupADC2.back();
308  }
309  else if(m_zd >= m_minZD)
310  {
311  dadc1 = fabs(m_terpADC1(m_zd));
312  dadc2 = fabs(m_terpADC2(m_zd));
313  }
314  else
315  {
316  std::cerr << "zenith limit\n";
317  }
318 
319  float adc1 = m_adc1zero + m_adc1lupsign*(dadc1 + m_adc1delta + m_deltaAngle);
320  float adc2 = m_adc2zero + m_adc2lupsign*(dadc2 + m_adc2delta + m_deltaAngle);
321 
322  std::cerr << "Sending adcs to: " << adc1 << " " << adc2 << "\n";
323 
324 
325  m_indiP_adc1pos["target"] = adc1;
327 
328  m_indiP_adc2pos["target"] = adc2;
330 
331  lastupdate = mx::sys::get_curr_time();
332  }
333  else if(!m_tracking) lastupdate = 0;
334 
335  return 0;
336 }
337 
339 {
340  return 0;
341 }
342 
343 INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_tracking)(const pcf::IndiProperty &ipRecv)
344 {
345  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_tracking, ipRecv);
346 
347  if(!ipRecv.find("toggle")) return 0;
348 
349  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
350  {
351  updateSwitchIfChanged(m_indiP_tracking, "toggle", pcf::IndiElement::On, INDI_IDLE);
352 
353  m_tracking = true;
354 
355  log<text_log>("started ADC rotation tracking");
356  }
357  else
358  {
359  updateSwitchIfChanged(m_indiP_tracking, "toggle", pcf::IndiElement::Off, INDI_IDLE);
360 
361  m_tracking = false;
362 
363  log<text_log>("stopped ADC rotation tracking");
364  }
365 
366  return 0;
367 }
368 
369 INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaAngle)(const pcf::IndiProperty &ipRecv)
370 {
371  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaAngle, ipRecv);
372 
373  float target;
374 
375  if( indiTargetUpdate( m_indiP_deltaAngle, target, ipRecv) < 0)
376  {
377  log<software_error>({__FILE__,__LINE__});
378  return -1;
379  }
380 
381  m_deltaAngle = target;
382 
383  std::lock_guard<std::mutex> guard(m_indiMutex);
384 
385  updateIfChanged(m_indiP_deltaAngle, "current", m_deltaAngle);
386 
387  log<text_log>("set deltaAngle to " + std::to_string(m_deltaAngle));
388 
389  return 0;
390 }
391 
392 INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaADC1)(const pcf::IndiProperty &ipRecv)
393 {
394  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaADC1, ipRecv);
395 
396  float target;
397 
398  if( indiTargetUpdate( m_indiP_deltaADC1, target, ipRecv) < 0)
399  {
400  log<software_error>({__FILE__,__LINE__});
401  return -1;
402  }
403 
404  m_adc1delta = target;
405 
406  std::lock_guard<std::mutex> guard(m_indiMutex);
407 
408  updateIfChanged(m_indiP_deltaADC1, "current", m_adc1delta);
409 
410  log<text_log>("set deltaADC1 to " + std::to_string(m_adc1delta));
411 
412  return 0;
413 }
414 
415 INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_deltaADC2)(const pcf::IndiProperty &ipRecv)
416 {
417  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_deltaADC2, ipRecv);
418 
419  float target;
420 
421  if( indiTargetUpdate( m_indiP_deltaADC2, target, ipRecv) < 0)
422  {
423  log<software_error>({__FILE__,__LINE__});
424  return -1;
425  }
426 
427  m_adc2delta = target;
428 
429  std::lock_guard<std::mutex> guard(m_indiMutex);
430 
431  updateIfChanged(m_indiP_deltaADC2, "current", m_adc2delta);
432 
433  log<text_log>("set deltaADC2 to " + std::to_string(m_adc2delta));
434 
435  return 0;
436 }
437 
438 INDI_NEWCALLBACK_DEFN(adcTracker, m_indiP_minZD)(const pcf::IndiProperty &ipRecv)
439 {
440  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_minZD, ipRecv);
441 
442  float target;
443 
444  if( indiTargetUpdate( m_indiP_minZD, target, ipRecv) < 0)
445  {
446  log<software_error>({__FILE__,__LINE__});
447  return -1;
448  }
449 
450  m_minZD = target;
451 
452  std::lock_guard<std::mutex> guard(m_indiMutex);
453 
454  updateIfChanged(m_indiP_minZD, "current", m_minZD);
455 
456  log<text_log>("set minZD to " + std::to_string(m_minZD));
457 
458  return 0;
459 }
460 
461 INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf::IndiProperty &ipRecv)
462 {
463  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_teldata, ipRecv);
464 
465 
466  if(!ipRecv.find("zd")) return 0;
467 
468  m_zd = ipRecv["zd"].get<float>();
469 
470  return 0;
471 }
472 
473 } //namespace app
474 } //namespace MagAOX
475 
476 #endif //adcTracker_hpp
477 
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
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.
Definition: MagAOXApp.hpp:2321
std::string m_calibDir
The path to calibration files for MagAOX.
Definition: MagAOXApp.hpp:94
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)
Definition: MagAOXApp.hpp:3031
The MagAO-X ADC Tracker.
Definition: adcTracker.hpp:40
pcf::IndiProperty m_indiP_deltaADC1
Definition: adcTracker.hpp:140
std::string m_lookupFile
The name of the file, in the calib directory, containing the adc lookup table. Default is 'adc_lookup...
Definition: adcTracker.hpp:52
virtual int appShutdown()
Shutdown the app.
Definition: adcTracker.hpp:338
std::vector< double > m_lupZD
Definition: adcTracker.hpp:83
std::vector< double > m_lupADC1
Definition: adcTracker.hpp:84
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: adcTracker.hpp:203
float m_deltaAngle
The offset angle to apply to the looked-up values, applied to both. Default is 0.
Definition: adcTracker.hpp:63
float m_adc1delta
The offset angle to apply to the looked-up value for ADC 1, applied in addition to deltaAngle....
Definition: adcTracker.hpp:65
pcf::IndiProperty m_indiP_deltaADC2
Definition: adcTracker.hpp:141
pcf::IndiProperty m_indiP_teldata
Definition: adcTracker.hpp:146
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_tracking)
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC2)
~adcTracker() noexcept
D'tor, declared and defined for noexcept.
Definition: adcTracker.hpp:99
std::string m_adc2DevName
The device name of the ADC 2 stage. Default is 'stageadc2'.
Definition: adcTracker.hpp:73
INDI_SETCALLBACK_DECL(adcTracker, m_indiP_teldata)
pcf::IndiProperty m_indiP_tracking
Definition: adcTracker.hpp:137
mx::math::gslInterpolator< mx::math::gsl_interp_linear< double > > m_terpADC1
Definition: adcTracker.hpp:87
pcf::IndiProperty m_indiP_deltaAngle
Definition: adcTracker.hpp:139
std::string m_adc1DevName
The device name of the ADC 1 stage. Default is 'stageadc1'.
Definition: adcTracker.hpp:72
pcf::IndiProperty m_indiP_adc2pos
Definition: adcTracker.hpp:150
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaAngle)
friend class adcTracker_test
Definition: adcTracker.hpp:43
virtual void setupConfig()
Definition: adcTracker.hpp:174
pcf::IndiProperty m_indiP_minZD
Definition: adcTracker.hpp:143
std::string m_tcsDevName
The device name of the TCS Interface providing 'teldata.zd'. Default is 'tcsi'.
Definition: adcTracker.hpp:75
float m_adc2delta
The offset angle to apply to the looked-up value for ADC 2, applied in addition to deltaAngle....
Definition: adcTracker.hpp:67
mx::math::gslInterpolator< mx::math::gsl_interp_linear< double > > m_terpADC2
Definition: adcTracker.hpp:88
float m_adc1zero
The starting point for ADC 1. Default is 0.
Definition: adcTracker.hpp:55
adcTracker()
Default c'tor.
Definition: adcTracker.hpp:168
virtual int appStartup()
Startup function.
Definition: adcTracker.hpp:229
int m_adc1lupsign
The sign to apply to the lookup table value for ADC 1.
Definition: adcTracker.hpp:57
float m_adc2zero
The starting point for ADC 2. Default is 0.
Definition: adcTracker.hpp:59
pcf::IndiProperty m_indiP_adc1pos
Definition: adcTracker.hpp:149
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_minZD)
int m_adc2lupsign
The sign to apply to the lookup table value for ADC 2.
Definition: adcTracker.hpp:61
std::vector< double > m_lupADC2
Definition: adcTracker.hpp:85
virtual int appLogic()
Implementation of the FSM for adcTracker.
Definition: adcTracker.hpp:293
float m_minZD
"The minimum zenith distance at which to interpolate and move the ADCs. Default is 0.
Definition: adcTracker.hpp:69
INDI_NEWCALLBACK_DECL(adcTracker, m_indiP_deltaADC1)
virtual void loadConfig()
Definition: adcTracker.hpp:224
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:264
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
#define INDI_IDLE
Definition: indiUtils.hpp:28
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:206
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
INDI_SETCALLBACK_DEFN(MagAOXApp< _useINDI >, m_indiP_powerChannel)(const pcf
Definition: MagAOXApp.hpp:3195
Definition: dm.hpp:24