API
alignLoop.hpp
Go to the documentation of this file.
1 /** \file alignLoop.hpp
2  * \brief The MagAO-X XXXXXX header file
3  *
4  * \ingroup alignLoop_files
5  */
6 
7 #ifndef alignLoop_hpp
8 #define alignLoop_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 /** \defgroup alignLoop
15  * \brief The XXXXXX application to do YYYYYYY
16  *
17  * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
18  *
19  * \ingroup apps
20  *
21  */
22 
23 /** \defgroup alignLoop_files
24  * \ingroup alignLoop
25  */
26 
27 namespace MagAOX
28 {
29 namespace app
30 {
31 
32 /// The MagAO-X xxxxxxxx
33 /**
34  * \ingroup alignLoop
35  */
36 class alignLoop : public MagAOXApp<true>, public dev::shmimMonitor<alignLoop>
37 {
38  //Give the test harness access.
39  friend class alignLoop_test;
40 
41  friend class dev::shmimMonitor<alignLoop>;
42 
43 public:
44  //The base shmimMonitor type
46 
47 protected:
48 
49  /** \name Configurable Parameters
50  *@{
51  */
52 
53  std::vector<std::string> m_ctrlDevices;
54  std::vector<std::string> m_ctrlProperties;
55  std::vector<std::string> m_ctrlCurrents;
56  std::vector<std::string> m_ctrlTargets;
57 
58  std::vector<float> m_currents;
59 
60  std::vector<pcf::IndiProperty> m_indiP_ctrl;
61 
62  std::string m_intMatFile;
63 
64  std::vector<float> m_defaultGains;
65 
66  std::string m_upstreamDevice;
67  std::string m_upstreamProperty {"loop_state"};
68  bool m_upstreamFollowClosed {false};
69 
70 
71  ///@}
72 
73  mx::improc::eigenImage<float> m_intMat;
74 
75  mx::improc::eigenImage<float> m_measurements;
76  mx::improc::eigenImage<float> m_commands;
77 
78  float m_ggain {0};
79 
80  std::vector<float> m_gains;
81 
82  bool m_ctrlEnabled {false};
83 
84 public:
85  /// Default c'tor.
86  alignLoop();
87 
88  /// D'tor, declared and defined for noexcept.
89  ~alignLoop() noexcept
90  {}
91 
92  virtual void setupConfig();
93 
94  /// Implementation of loadConfig logic, separated for testing.
95  /** This is called by loadConfig().
96  */
97  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
98 
99  virtual void loadConfig();
100 
101  /// Startup function
102  /**
103  *
104  */
105  virtual int appStartup();
106 
107  /// Implementation of the FSM for alignLoop.
108  /**
109  * \returns 0 on no critical error
110  * \returns -1 on an error requiring shutdown
111  */
112  virtual int appLogic();
113 
114  /// Shutdown the app.
115  /**
116  *
117  */
118  virtual int appShutdown();
119 
120  int toggleLoop( bool onoff );
121 
122  // shmimMonitor interface:
123  int allocate( const dev::shmimT &);
124 
125  int processImage( void* curr_src,
126  const dev::shmimT &
127  );
128 
129  int sendCommands(std::vector<float> & commands);
130 
131  //INDI
132 
133  pcf::IndiProperty m_indiP_ggain;
134  pcf::IndiProperty m_indiP_ctrlEnabled;
135 
138 
139  static int st_setCallBack_ctrl( void * app, const pcf::IndiProperty &ipRecv)
140  {
141  return static_cast<alignLoop *>(app)->setCallBack_ctrl(ipRecv);
142  }
143 
144  int setCallBack_ctrl(const pcf::IndiProperty &ipRecv);
145 
147  pcf::IndiProperty m_indiP_upstream; ///< Property used to report the loop state
148 
150 };
151 
152 alignLoop::alignLoop() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
153 {
154 
155  return;
156 }
157 
159 {
161 
162  config.add("ctrl.devices", "", "ctrl.devices", argType::Required, "ctrl", "devices", false, "string", "Device names of the controller(s) (one per element).");
163  config.add("ctrl.properties", "", "ctrl.properties", argType::Required, "ctrl", "properties", false, "string", "Properties of the ctrl devices to which to give the commands. One per element");
164  config.add("ctrl.currents", "", "ctrl.currents", argType::Required, "ctrl", "currents", false, "vector<string>", "current elements of the properties on which base the commands.");
165  config.add("ctrl.targets", "", "ctrl.targets", argType::Required, "ctrl", "targets", false, "vector<string>", "target elements of the properties to which to send the commands.");
166 
167  config.add("loop.intMat", "", "loop.intMat", argType::Required, "loop", "intMat", false, "string", "file name of the interaction matrix.");
168  config.add("loop.gains", "", "loop.gains", argType::Required, "loop", "gains", false, "vector<float>", "default loop gains. If single number, it is applied to all axes.");
169  config.add("loop.upstream", "", "loop.upstream", argType::Required, "loop", "upstream", false, "string", "Upstream loop device name. This loop will open, and optionally close, with the upstream loop. Default none.");
170  config.add("loop.upstreamProperty", "", "loop.upstreamProperty", argType::Required, "loop", "upstreamProperty", false, "string", "Property of upstream loop device to follow. Must be a toggle. Default is loop_state.");
171 
172 }
173 
174 int alignLoop::loadConfigImpl( mx::app::appConfigurator & _config )
175 {
176  shmimMonitorT::loadConfig(_config);
177 
178  _config(m_ctrlDevices, "ctrl.devices");
179  _config(m_ctrlProperties, "ctrl.properties");
180  _config(m_ctrlCurrents, "ctrl.currents");
181  _config(m_ctrlTargets, "ctrl.targets");
182  _config(m_intMatFile, "loop.intMat");
183  _config(m_defaultGains, "loop.gains");
184  _config(m_upstreamDevice, "loop.upstream");
185  _config(m_upstreamProperty, "loop.upstreamProperty");
186  return 0;
187 }
188 
190 {
191  loadConfigImpl(config);
192 }
193 
195 {
196  if(shmimMonitorT::appStartup() < 0)
197  {
198  return log<software_error,-1>({__FILE__, __LINE__});
199  }
200 
201  if(m_ctrlTargets.size() != m_ctrlDevices.size())
202  {
203  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and ctrl.devices are not the same size"});
204  }
205 
206  if(m_ctrlTargets.size() != m_ctrlProperties.size())
207  {
208  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and ctrl.properties are not the same size"});
209  }
210 
211  if(m_ctrlTargets.size() != m_ctrlCurrents.size())
212  {
213  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Currents and ctrl.properties are not the same size"});
214  }
215 
216  if(m_ctrlTargets.size() != m_defaultGains.size())
217  {
218  if(m_defaultGains.size()==1)
219  {
220  float g = m_defaultGains[0];
221  m_defaultGains.resize(m_ctrlTargets.size(), g);
222  log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
223  }
224  else
225  {
226  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
227  }
228  }
229 
230  m_gains.resize(m_defaultGains.size());
231  for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
232 
233  createStandardIndiNumber<unsigned>( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f");
234  m_indiP_ggain["current"] = m_ggain;
235  m_indiP_ggain["target"] = m_ggain;
237  {
238  log<software_error>({__FILE__,__LINE__});
239  return -1;
240  }
241 
244  {
245  log<software_error>({__FILE__,__LINE__});
246  return -1;
247  }
248 
249  m_currents.resize(m_ctrlDevices.size(), -1e15);
250 
251  m_indiP_ctrl.resize(m_ctrlDevices.size());
252 
253  for(size_t n=0; n< m_ctrlDevices.size();++n)
254  {
256  }
257  std::string intMatPath = m_calibDir + "/" + m_intMatFile;
258 
259  m_commands.resize(m_ctrlTargets.size(), m_ctrlTargets.size()) ;
260  m_commands.setZero();
261 
262  m_intMat.resize(m_ctrlTargets.size(), m_ctrlTargets.size()) ;
263  m_intMat.setZero();
264  m_intMat(1,0) = 0.926;
265  m_intMat(1,1) = -0.370;
266  m_intMat(0,0) = 0.185;
267  m_intMat(0,1) = 0.926;
268 
269 /*
270  mx::fits::fitsFile<float> ff;
271  try
272  {
273  ff.read(m_intMat, intMatPath);
274  }
275  catch(...)
276  {
277  return log<software_error, -1>({__FILE__, __LINE__, "error reading loop.intMatFile. Does it exist?"});
278  }
279 
280  if(m_intMat.rows() != m_ctrlTargets.size())
281  {
282  return log<software_error, -1>({__FILE__, __LINE__, "interaction matrix wrong size: rows do not match sensor Targets"});
283  }
284 
285  if(m_intMat.cols() != m_correctorTargets.size())
286  {
287  return log<software_error, -1>({__FILE__, __LINE__, "interaction matrix wrong size: cols do not match corrector Targets"});
288  }
289 */
290 
291  //Get the loop state for managing offloading
292  if(m_upstreamDevice != "")
293  {
295  }
296 
297  return 0;
298 }
299 
301 {
302  if( shmimMonitorT::appLogic() < 0)
303  {
304  return log<software_error,-1>({__FILE__,__LINE__});
305  }
306 
308 
309  return 0;
310 }
311 
313 {
315 
316  return 0;
317 }
318 
319 int alignLoop::toggleLoop(bool onoff)
320 {
321  if(!m_ctrlEnabled && onoff) //not enabled so change
322  {
323  m_ctrlEnabled = true;
324  log<loop_closed>();
325  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
326  return 0;
327  }
328 
329  if(m_ctrlEnabled && !onoff)
330  {
331  m_ctrlEnabled = false;
332  log<loop_open>();
333  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
334 
335  return 0;
336  }
337 
338  return 0;
339 }
340 
341 inline
343 {
344  static_cast<void>(dummy);
345 
346  std::lock_guard<std::mutex> guard(m_indiMutex);
347 
350 
351  return 0;
352 }
353 
354 inline
355 int alignLoop::processImage( void* curr_src,
356  const dev::shmimT & dummy
357  )
358 {
359  static_cast<void>(dummy);
360 
361  for(unsigned nn=0; nn < shmimMonitorT::m_width*shmimMonitorT::m_height; ++nn)
362  {
363  m_measurements.data()[nn] = ((float*)curr_src) [nn];
364  }
365 
366 
367 
368  std::cout << "measurements: ";
369  for(int cc = 0; cc < m_measurements.rows(); ++cc)
370  {
371  std::cout << m_measurements(cc,0) << " ";
372  }
373  std::cout << "\n";
374 
375  if(m_currents[0] < -1e14) return 0;
376 
377  m_commands.matrix() = m_intMat.matrix() * m_measurements.matrix();
378 
379  std::cout << "delta commands: ";
380  for(int cc = 0; cc < m_measurements.rows(); ++cc)
381  {
382  std::cout << -m_commands(cc,0) << " ";
383  }
384  std::cout << "\n";
385 
386  std::vector<float> commands;
387  commands.resize(m_measurements.rows());
388 
389  std::cout << "commands: ";
390  for(int cc = 0; cc < m_measurements.rows(); ++cc)
391  {
392  commands[cc] = m_currents[cc] - m_ggain*m_gains[cc]*m_commands(cc,0);
393  std::cout << commands[cc] << " ";
394  }
395  std::cout << "\n";
396 
397  //And send commands.
398  if(m_ctrlEnabled)
399  {
400  return sendCommands(commands);
401  }
402  else
403  {
404  return 0;
405  }
406 }
407 
408 inline
409 int alignLoop::sendCommands(std::vector<float> & commands)
410 {
411  for(size_t n=0; n < m_ctrlDevices.size(); ++n)
412  {
413  pcf::IndiProperty ip(pcf::IndiProperty::Number);
414 
415  ip.setDevice(m_ctrlDevices[n]);
416  ip.setName(m_ctrlProperties[n]);
417  ip.add(pcf::IndiElement(m_ctrlTargets[n]));
418  ip[m_ctrlTargets[n]] = commands[n];
419 
420  sendNewProperty(ip);
421  }
422 
423  return 0;
424 }
425 
426 INDI_NEWCALLBACK_DEFN(alignLoop, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
427 {
428  if(ipRecv.getName() != m_indiP_ggain.getName())
429  {
430  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
431  return -1;
432  }
433 
434  float target;
435 
436  if( indiTargetUpdate( m_indiP_ggain, target, ipRecv, true) < 0)
437  {
438  log<software_error>({__FILE__,__LINE__});
439  return -1;
440  }
441 
442  m_ggain = target;
443 
444  updateIfChanged(m_indiP_ggain, "current", m_ggain);
445  updateIfChanged(m_indiP_ggain, "target", m_ggain);
446 
447  log<text_log>("set global gain to " + std::to_string(m_ggain), logPrio::LOG_NOTICE);
448 
449  return 0;
450 }
451 
452 INDI_NEWCALLBACK_DEFN(alignLoop, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
453 {
454  if(ipRecv.getName() != m_indiP_ctrlEnabled.getName())
455  {
456  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
457  return -1;
458  }
459 
460  //switch is toggled to on
461  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
462  {
463  return toggleLoop(true);
464  }
465 
466  //switch is toggle to off
467  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
468  {
469  return toggleLoop(false);
470  }
471 
472  return 0;
473 }
474 
475 inline
476 int alignLoop::setCallBack_ctrl(const pcf::IndiProperty &ipRecv)
477 {
478  for(size_t n = 0; n < m_ctrlDevices.size(); ++n)
479  {
480  if( ipRecv.getDevice() == m_ctrlDevices[n])
481  {
482  if(ipRecv.getName() == m_ctrlProperties[n] && ipRecv.find(m_ctrlCurrents[n]))
483  {
484  m_currents[n] = ipRecv[m_ctrlCurrents[n]].get<float>();
485  }
486  }
487  }
488 
489  return 0;
490 }
491 
492 INDI_SETCALLBACK_DEFN(alignLoop, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
493 {
494  if(ipRecv.getName() != m_indiP_upstream.getName())
495  {
496  return log<software_error>({__FILE__,__LINE__, "wrong INDI property received"});
497  }
498 
499  if(!ipRecv.find("toggle")) return 0;
500 
501  if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_upstreamFollowClosed)
502  {
503  std::cerr << "upstream on\n";
504  return toggleLoop(true);
505  }
506  else if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
507  {
508  std::cerr << "upstream off\n";
509  return toggleLoop(false);
510  }
511 
512  return 0;
513 }
514 
515 
516 } //namespace app
517 } //namespace MagAOX
518 
519 #endif //alignLoop_hpp
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
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
Definition: MagAOXApp.hpp:2901
std::string m_calibDir
The path to calibration files for MagAOX.
Definition: MagAOXApp.hpp:94
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
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
int registerIndiPropertySet(pcf::IndiProperty &prop, const std::string &devName, const std::string &propName, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is monitored for updates from others.
Definition: MagAOXApp.hpp:2569
The MagAO-X xxxxxxxx.
Definition: alignLoop.hpp:37
int processImage(void *curr_src, const dev::shmimT &)
Definition: alignLoop.hpp:355
pcf::IndiProperty m_indiP_ctrlEnabled
Definition: alignLoop.hpp:134
std::vector< std::string > m_ctrlProperties
Definition: alignLoop.hpp:54
mx::improc::eigenImage< float > m_commands
Definition: alignLoop.hpp:76
static int st_setCallBack_ctrl(void *app, const pcf::IndiProperty &ipRecv)
Definition: alignLoop.hpp:139
int toggleLoop(bool onoff)
Definition: alignLoop.hpp:319
std::vector< float > m_gains
Definition: alignLoop.hpp:80
virtual int appStartup()
Startup function.
Definition: alignLoop.hpp:194
INDI_NEWCALLBACK_DECL(alignLoop, m_indiP_ctrlEnabled)
std::vector< std::string > m_ctrlCurrents
Definition: alignLoop.hpp:55
std::vector< std::string > m_ctrlTargets
Definition: alignLoop.hpp:56
INDI_SETCALLBACK_DECL(alignLoop, m_indiP_upstream)
int allocate(const dev::shmimT &)
Definition: alignLoop.hpp:342
dev::shmimMonitor< alignLoop > shmimMonitorT
Definition: alignLoop.hpp:45
mx::improc::eigenImage< float > m_intMat
Definition: alignLoop.hpp:73
std::vector< std::string > m_ctrlDevices
Definition: alignLoop.hpp:53
std::string m_upstreamDevice
Definition: alignLoop.hpp:66
std::vector< float > m_defaultGains
Definition: alignLoop.hpp:64
std::string m_intMatFile
Definition: alignLoop.hpp:62
virtual int appLogic()
Implementation of the FSM for alignLoop.
Definition: alignLoop.hpp:300
std::string m_upstreamProperty
Definition: alignLoop.hpp:67
int setCallBack_ctrl(const pcf::IndiProperty &ipRecv)
Definition: alignLoop.hpp:476
virtual int appShutdown()
Shutdown the app.
Definition: alignLoop.hpp:312
pcf::IndiProperty m_indiP_ggain
Definition: alignLoop.hpp:133
std::vector< float > m_currents
Definition: alignLoop.hpp:58
~alignLoop() noexcept
D'tor, declared and defined for noexcept.
Definition: alignLoop.hpp:89
alignLoop()
Default c'tor.
Definition: alignLoop.hpp:152
friend class alignLoop_test
Definition: alignLoop.hpp:39
INDI_NEWCALLBACK_DECL(alignLoop, m_indiP_ggain)
std::vector< pcf::IndiProperty > m_indiP_ctrl
Definition: alignLoop.hpp:60
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: alignLoop.hpp:174
mx::improc::eigenImage< float > m_measurements
Definition: alignLoop.hpp:75
pcf::IndiProperty m_indiP_upstream
Property used to report the loop state.
Definition: alignLoop.hpp:147
virtual void setupConfig()
Definition: alignLoop.hpp:158
int sendCommands(std::vector< float > &commands)
Definition: alignLoop.hpp:409
virtual void loadConfig()
Definition: alignLoop.hpp:189
uint32_t m_width
The width of the images in the stream.
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
#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
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
std::ostream & cout()
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_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
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.