API
w2tcsOffloader.hpp
Go to the documentation of this file.
1 /** \file w2tcsOffloader.hpp
2  * \brief The MagAO-X Woofer To Telescope Control System (TCS) offloading manager
3  *
4  * \ingroup app_files
5  */
6 
7 #ifndef w2tcsOffloader_hpp
8 #define w2tcsOffloader_hpp
9 
10 #include <limits>
11 
12 #include <mx/improc/eigenCube.hpp>
13 #include <mx/improc/eigenImage.hpp>
14 
15 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
16 #include "../../magaox_git_version.h"
17 
18 namespace MagAOX
19 {
20 namespace app
21 {
22 
23 /** \defgroup w2tcsOffloader Woofer to TCS Offloading
24  * \brief Monitors the averaged woofer shape, fits Zernikes, and sends it to INDI.
25  *
26  * <a href="../handbook/operating/software/apps/w2tcsOffloader.html">Application Documentation</a>
27  *
28  * \ingroup apps
29  *
30  */
31 
32 /** \defgroup w2tcsOffloader_files Woofer to TCS Offloading
33  * \ingroup w2tcsOffloader
34  */
35 
36 /** MagAO-X application to control offloading the woofer to the TCS.
37  *
38  * \ingroup w2tcsOffloader
39  *
40  */
41 class w2tcsOffloader : public MagAOXApp<true>, public dev::shmimMonitor<w2tcsOffloader>
42 {
43 
44  //Give the test harness access.
45  friend class w2tcsOffloader_test;
46 
47  friend class dev::shmimMonitor<w2tcsOffloader>;
48 
49  //The base shmimMonitor type
51 
52  ///Floating point type in which to do all calculations.
53  typedef float realT;
54 
55 protected:
56 
57  /** \name Configurable Parameters
58  *@{
59  */
60 
61  std::string m_wZModesPath;
62  std::string m_wMaskPath;
63  std::vector<std::string> m_elNames;
64  std::vector<realT> m_zCoeffs;
65  float m_gain {0.1};
66  int m_nModes {2};
67  float m_norm {1.0};
68 
69  ///@}
70 
71  mx::improc::eigenCube<realT> m_wZModes;
72  mx::improc::eigenImage<realT> m_woofer;
73  mx::improc::eigenImage<realT> m_wMask;
74 
75 public:
76  /// Default c'tor.
78 
79  /// D'tor, declared and defined for noexcept.
80  ~w2tcsOffloader() noexcept
81  {}
82 
83  virtual void setupConfig();
84 
85  /// Implementation of loadConfig logic, separated for testing.
86  /** This is called by loadConfig().
87  */
88  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
89 
90  virtual void loadConfig();
91 
92  /// Startup function
93  /**
94  *
95  */
96  virtual int appStartup();
97 
98  /// Implementation of the FSM for w2tcsOffloader.
99  /**
100  * \returns 0 on no critical error
101  * \returns -1 on an error requiring shutdown
102  */
103  virtual int appLogic();
104 
105  /// Shutdown the app.
106  /**
107  *
108  */
109  virtual int appShutdown();
110 
111 
112 
113 
114  int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
115 
116  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
117  const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
118  );
119 
120 
121 protected:
122 
123  pcf::IndiProperty m_indiP_gain;
124  pcf::IndiProperty m_indiP_nModes;
125  pcf::IndiProperty m_indiP_zCoeffs;
126 
127  pcf::IndiProperty m_indiP_zero;
128 
133 };
134 
135 inline
136 w2tcsOffloader::w2tcsOffloader() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
137 {
138  return;
139 }
140 
141 inline
143 {
145 
146  config.add("offload.wZModesPath", "", "offload.wZModesPath", argType::Required, "offload", "wZModesPath", false, "string", "The path to the woofer Zernike modes.");
147  config.add("offload.wMaskPath", "", "offload.wMaskPath", argType::Required, "offload", "wMaskPath", false, "string", "Path to the woofer Zernike mode mask.");
148  config.add("offload.gain", "", "offload.gain", argType::Required, "offload", "gain", false, "float", "The starting offload gain. Default is 0.1.");
149  config.add("offload.nModes", "", "offload.nModes", argType::Required, "offload", "nModes", false, "int", "Number of modes to offload to the TCS.");
150 }
151 
152 inline
153 int w2tcsOffloader::loadConfigImpl( mx::app::appConfigurator & _config )
154 {
155 
156  shmimMonitorT::loadConfig(_config);
157 
158  _config(m_wZModesPath, "offload.wZModesPath");
159  _config(m_wMaskPath, "offload.wMaskPath");
160  _config(m_gain, "offload.gain");
161  _config(m_nModes, "offload.nModes");
162 
163  return 0;
164 }
165 
166 inline
168 {
169  loadConfigImpl(config);
170 }
171 
172 inline
174 
175  mx::fits::fitsFile<float> ff;
176  if(ff.read(m_wZModes, m_wZModesPath) < 0)
177  {
178  return log<text_log,-1>("Could not open mode cube file", logPrio::LOG_ERROR);
179  }
180 
181  m_zCoeffs.resize(m_wZModes.planes(), 0);
182 
183  if(ff.read(m_wMask, m_wMaskPath) < 0)
184  {
185  return log<text_log,-1>("Could not open mode mask file", logPrio::LOG_ERROR);
186  }
187 
188  m_norm = m_wMask.sum();
189 
190  createStandardIndiNumber<unsigned>( m_indiP_gain, "gain", 0, 1, 0, "%0.2f");
191  m_indiP_gain["current"] = m_gain;
192  m_indiP_gain["target"] = m_gain;
193 
195  {
196  log<software_error>({__FILE__,__LINE__});
197  return -1;
198  }
199 
200  createStandardIndiNumber<unsigned>( m_indiP_nModes, "nModes", 1, std::numeric_limits<unsigned>::max(), 1, "%u");
201  m_indiP_nModes["current"] = m_nModes;
202 
204  {
205  log<software_error>({__FILE__,__LINE__});
206  return -1;
207  }
208 
209  REG_INDI_NEWPROP(m_indiP_zCoeffs, "zCoeffs", pcf::IndiProperty::Number);
210 
211 
212  m_elNames.resize(m_zCoeffs.size());
213  for(size_t n=0; n < m_zCoeffs.size(); ++n)
214  {
215  //std::string el = std::to_string(n);
216  m_elNames[n] = mx::ioutils::convertToString<size_t, 2, '0'>(n);
217 
218  m_indiP_zCoeffs.add( pcf::IndiElement(m_elNames[n]) );
219  m_indiP_zCoeffs[m_elNames[n]].set(0);
220  }
221 
222  if(shmimMonitorT::appStartup() < 0)
223  {
224  return log<software_error,-1>({__FILE__, __LINE__});
225  }
226 
227 
228  createStandardIndiRequestSw( m_indiP_zero, "zero", "zero loop");
230  {
231  log<software_error>({__FILE__,__LINE__});
232  return -1;
233  }
234 
236 
237  return 0;
238 }
239 
240 inline
242 {
243  if( shmimMonitorT::appLogic() < 0)
244  {
245  return log<software_error,-1>({__FILE__,__LINE__});
246  }
247 
248 
249  std::unique_lock<std::mutex> lock(m_indiMutex);
250 
251  if(shmimMonitorT::updateINDI() < 0)
252  {
253  log<software_error>({__FILE__, __LINE__});
254  }
255 
256 
257  return 0;
258 }
259 
260 inline
262 {
264 
265 
266  return 0;
267 }
268 
269 inline
271 {
272  static_cast<void>(dummy); //be unused
273 
274  //std::unique_lock<std::mutex> lock(m_indiMutex);
275 
277 
278  //state(stateCodes::OPERATING);
279 
280  return 0;
281 }
282 
283 inline
284 int w2tcsOffloader::processImage( void * curr_src,
285  const dev::shmimT & dummy
286  )
287 {
288  static_cast<void>(dummy); //be unused (what is this?)
289 
290  // Replace this:
291  // project zernikes onto avg image
292  // update INDI properties with coeffs
293 
294  for(size_t i=0; i < m_zCoeffs.size(); ++i)
295  {
296  /* update requested nModes and explicitly zero out any
297  modes that shouldn't be offloaded (but might have been
298  previously set)
299  */
300  if(i < m_nModes)
301  {
302  float coeff;
303  coeff = ( Eigen::Map<mx::improc::eigenImage<realT>>((float *)curr_src, shmimMonitorT::m_width, shmimMonitorT::m_height) * m_wZModes.image(i) * m_wMask).sum() / m_norm;
304  m_indiP_zCoeffs[m_elNames[i]] = m_gain * coeff;
305  }
306  else
307  {
308  m_indiP_zCoeffs[m_elNames[i]] = 0.;
309  }
310  }
311 
312  m_indiP_zCoeffs.setState (pcf::IndiProperty::Ok);
313  m_indiDriver->sendSetProperty (m_indiP_zCoeffs);
314 
315 
316  // loop over something like this
317  //z0 = (im * basis.image(0)*mask).sum()/norm;
318 
319 
320  return 0;
321 }
322 
323 // update this: mode coefficients (maybe they shouldn't be settable. How to handle?)
324 
325 INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_gain)(const pcf::IndiProperty &ipRecv)
326 {
327  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_gain, ipRecv);
328 
329  float target;
330 
331  if( indiTargetUpdate( m_indiP_gain, target, ipRecv, true) < 0)
332  {
333  log<software_error>({__FILE__,__LINE__});
334  return -1;
335  }
336 
337  m_gain = target;
338 
339  updateIfChanged(m_indiP_gain, "current", m_gain);
340  updateIfChanged(m_indiP_gain, "target", m_gain);
341 
342  log<text_log>("set gain to " + std::to_string(m_gain), logPrio::LOG_NOTICE);
343 
344  return 0;
345 }
346 
347 INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_nModes)(const pcf::IndiProperty &ipRecv)
348 {
349  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_nModes, ipRecv);
350 
351  unsigned target;
352 
353  if( indiTargetUpdate( m_indiP_nModes, target, ipRecv, true) < 0)
354  {
355  log<software_error>({__FILE__,__LINE__});
356  return -1;
357  }
358 
359  m_nModes = target;
360 
361  updateIfChanged(m_indiP_nModes, "current", m_nModes);
362  updateIfChanged(m_indiP_nModes, "target", m_nModes);
363 
364  log<text_log>("set nModes to " + std::to_string(m_nModes), logPrio::LOG_NOTICE);
365 
366  return 0;
367 }
368 
369 INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zCoeffs)(const pcf::IndiProperty &ipRecv)
370 {
371  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zCoeffs, ipRecv);
372 
373 
374  for(size_t n=0; n < m_zCoeffs.size(); ++n)
375  {
376  if(ipRecv.find(m_elNames[n]))
377  {
378  realT zcoeff = ipRecv[m_elNames[n]].get<realT>();
379  m_zCoeffs[n] = zcoeff;
380  }
381  }
382  return 0;
383 
384 
385  return log<software_error,-1>({__FILE__,__LINE__, "invalid indi property name"});
386 }
387 
388 INDI_NEWCALLBACK_DEFN(w2tcsOffloader, m_indiP_zero)(const pcf::IndiProperty &ipRecv)
389 {
390  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zero, ipRecv);
391 
392  float target;
393 
394  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
395  {
396  m_woofer.setZero();
397  log<text_log>("set zero", logPrio::LOG_NOTICE);
398  }
399  return 0;
400 }
401 
402 } //namespace app
403 } //namespace MagAOX
404 
405 #endif //w2tcsOffloader_hpp
406 
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
Definition: MagAOXApp.hpp:2352
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.
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
Definition: MagAOXApp.hpp:537
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
uint32_t m_width
The width of the images in the stream.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
int appShutdown()
Shuts down the shmimMonitor thread.
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_nModes)
virtual int appLogic()
Implementation of the FSM for w2tcsOffloader.
mx::improc::eigenImage< realT > m_wMask
std::vector< realT > m_zCoeffs
mx::improc::eigenCube< realT > m_wZModes
int allocate(const dev::shmimT &dummy)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
dev::shmimMonitor< w2tcsOffloader > shmimMonitorT
pcf::IndiProperty m_indiP_nModes
mx::improc::eigenImage< realT > m_woofer
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_gain)
virtual int appStartup()
Startup function.
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zero)
pcf::IndiProperty m_indiP_gain
INDI_NEWCALLBACK_DECL(w2tcsOffloader, m_indiP_zCoeffs)
~w2tcsOffloader() noexcept
D'tor, declared and defined for noexcept.
float realT
Floating point type in which to do all calculations.
pcf::IndiProperty m_indiP_zCoeffs
std::vector< std::string > m_elNames
virtual int appShutdown()
Shutdown the app.
int processImage(void *curr_src, const dev::shmimT &dummy)
pcf::IndiProperty m_indiP_zero
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
#define REG_INDI_NEWPROP(prop, propName, type)
Register a NEW INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:229
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
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
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.
A simple text log, a string-type log.
Definition: text_log.hpp:24