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  float m_delta0 {0};
77  float m_delta1 {0};
78 
79  mx::improc::eigenImage<float> m_commands;
80 
81  float m_ggain {0};
82 
83  std::vector<float> m_gains;
84 
85  bool m_ctrlEnabled {false};
86 
87 public:
88  /// Default c'tor.
89  alignLoop();
90 
91  /// D'tor, declared and defined for noexcept.
92  ~alignLoop() noexcept
93  {}
94 
95  virtual void setupConfig();
96 
97  /// Implementation of loadConfig logic, separated for testing.
98  /** This is called by loadConfig().
99  */
100  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
101 
102  virtual void loadConfig();
103 
104  /// Startup function
105  /**
106  *
107  */
108  virtual int appStartup();
109 
110  /// Implementation of the FSM for alignLoop.
111  /**
112  * \returns 0 on no critical error
113  * \returns -1 on an error requiring shutdown
114  */
115  virtual int appLogic();
116 
117  /// Shutdown the app.
118  /**
119  *
120  */
121  virtual int appShutdown();
122 
123  int toggleLoop( bool onoff );
124 
125  // shmimMonitor interface:
126  int allocate( const dev::shmimT &);
127 
128  int processImage( void* curr_src,
129  const dev::shmimT &
130  );
131 
132  int sendCommands(std::vector<float> & commands);
133 
134  //INDI
135 
136  pcf::IndiProperty m_indiP_deltas;
137 
138  pcf::IndiProperty m_indiP_ggain;
139  pcf::IndiProperty m_indiP_ctrlEnabled;
140 
143 
144  static int st_setCallBack_ctrl( void * app, const pcf::IndiProperty &ipRecv)
145  {
146  return static_cast<alignLoop *>(app)->setCallBack_ctrl(ipRecv);
147  }
148 
149  int setCallBack_ctrl(const pcf::IndiProperty &ipRecv);
150 
152  pcf::IndiProperty m_indiP_upstream; ///< Property used to report the loop state
153 
155 };
156 
157 alignLoop::alignLoop() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
158 {
159 
160  return;
161 }
162 
164 {
166 
167  config.add("ctrl.devices", "", "ctrl.devices", argType::Required, "ctrl", "devices", false, "string", "Device names of the controller(s) (one per element).");
168  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");
169  config.add("ctrl.currents", "", "ctrl.currents", argType::Required, "ctrl", "currents", false, "vector<string>", "current elements of the properties on which base the commands.");
170  config.add("ctrl.targets", "", "ctrl.targets", argType::Required, "ctrl", "targets", false, "vector<string>", "target elements of the properties to which to send the commands.");
171 
172  config.add("loop.gain", "", "loop.gain", argType::Required, "loop", "gain", false, "float", "default global loop gain.");
173  config.add("loop.intMat", "", "loop.intMat", argType::Required, "loop", "intMat", false, "string", "file name of the interaction matrix.");
174  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.");
175  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.");
176  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.");
177 
178 }
179 
180 int alignLoop::loadConfigImpl( mx::app::appConfigurator & _config )
181 {
182  shmimMonitorT::loadConfig(_config);
183 
184  _config(m_ctrlDevices, "ctrl.devices");
185  _config(m_ctrlProperties, "ctrl.properties");
186  _config(m_ctrlCurrents, "ctrl.currents");
187  _config(m_ctrlTargets, "ctrl.targets");
188  _config(m_ggain, "loop.gain");
189  _config(m_intMatFile, "loop.intMat");
190  _config(m_defaultGains, "loop.gains");
191  _config(m_upstreamDevice, "loop.upstream");
192  _config(m_upstreamProperty, "loop.upstreamProperty");
193  return 0;
194 }
195 
197 {
198  loadConfigImpl(config);
199 }
200 
202 {
203  if(shmimMonitorT::appStartup() < 0)
204  {
205  return log<software_error,-1>({__FILE__, __LINE__});
206  }
207 
208  if(m_ctrlTargets.size() != m_ctrlDevices.size())
209  {
210  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and ctrl.devices are not the same size"});
211  }
212 
213  if(m_ctrlTargets.size() != m_ctrlProperties.size())
214  {
215  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and ctrl.properties are not the same size"});
216  }
217 
218  if(m_ctrlTargets.size() != m_ctrlCurrents.size())
219  {
220  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Currents and ctrl.properties are not the same size"});
221  }
222 
223  if(m_ctrlTargets.size() != m_defaultGains.size())
224  {
225  if(m_defaultGains.size()==1)
226  {
227  float g = m_defaultGains[0];
228  m_defaultGains.resize(m_ctrlTargets.size(), g);
229  log<text_log>("Setting loop.gains gains to be same size as ctrl.Targets", logPrio::LOG_NOTICE);
230  }
231  else
232  {
233  return log<software_error, -1>({__FILE__, __LINE__, "ctrl.Targets and loop.gains are not the same size"});
234  }
235  }
236 
237  CREATE_REG_INDI_RO_NUMBER( m_indiP_deltas, "deltas", "Deltas", "Deltas");
238  m_indiP_deltas.add(pcf::IndiElement("delta0"));
239  m_indiP_deltas["delta0"] = 0;
240  m_indiP_deltas.add(pcf::IndiElement("delta1"));
241  m_indiP_deltas["delta1"] = 0;
242 
243  m_gains.resize(m_defaultGains.size());
244  for(size_t n=0; n < m_defaultGains.size(); ++n) m_gains[n] = m_defaultGains[n];
245 
246  createStandardIndiNumber<unsigned>( m_indiP_ggain, "loop_gain", 0, 1, 0, "%0.2f");
247  m_indiP_ggain["current"] = m_ggain;
248  m_indiP_ggain["target"] = m_ggain;
250  {
251  log<software_error>({__FILE__,__LINE__});
252  return -1;
253  }
254 
257  {
258  log<software_error>({__FILE__,__LINE__});
259  return -1;
260  }
261 
262  m_currents.resize(m_ctrlDevices.size(), -1e15);
263 
264  m_indiP_ctrl.resize(m_ctrlDevices.size());
265 
266  for(size_t n=0; n< m_ctrlDevices.size();++n)
267  {
269  }
270 
271  m_commands.resize(m_ctrlTargets.size(), m_ctrlTargets.size()) ;
272  m_commands.setZero();
273 
274  m_intMat.resize(m_ctrlTargets.size(), m_ctrlTargets.size()) ;
275  m_intMat.setZero();
276  m_intMat(1,0) = 0.926;
277  m_intMat(1,1) = -0.370;
278  m_intMat(0,0) = 0.185;
279  m_intMat(0,1) = 0.926;
280 
281 /*
282  std::string intMatPath = m_calibDir + "/" + m_intMatFile;
283 
284  mx::fits::fitsFile<float> ff;
285  try
286  {
287  ff.read(m_intMat, intMatPath);
288  }
289  catch(...)
290  {
291  return log<software_error, -1>({__FILE__, __LINE__, "error reading loop.intMatFile. Does it exist?"});
292  }
293 
294  if(m_intMat.rows() != m_ctrlTargets.size())
295  {
296  return log<software_error, -1>({__FILE__, __LINE__, "interaction matrix wrong size: rows do not match sensor Targets"});
297  }
298 
299  if(m_intMat.cols() != m_correctorTargets.size())
300  {
301  return log<software_error, -1>({__FILE__, __LINE__, "interaction matrix wrong size: cols do not match corrector Targets"});
302  }
303 */
304 
305  //Get the loop state for managing offloading
306  if(m_upstreamDevice != "")
307  {
309  }
310 
311  return 0;
312 }
313 
315 {
316  if( shmimMonitorT::appLogic() < 0)
317  {
318  return log<software_error,-1>({__FILE__,__LINE__});
319  }
320 
322 
323  return 0;
324 }
325 
327 {
329 
330  return 0;
331 }
332 
333 int alignLoop::toggleLoop(bool onoff)
334 {
335  if(!m_ctrlEnabled && onoff) //not enabled so change
336  {
337  m_ctrlEnabled = true;
338  log<loop_closed>();
339  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::On, INDI_OK);
340  return 0;
341  }
342 
343  if(m_ctrlEnabled && !onoff)
344  {
345  m_ctrlEnabled = false;
346  log<loop_open>();
347  updateSwitchIfChanged(m_indiP_ctrlEnabled, "toggle", pcf::IndiElement::Off, INDI_IDLE);
348 
349  return 0;
350  }
351 
352  return 0;
353 }
354 
355 inline
357 {
358  static_cast<void>(dummy);
359 
360  std::lock_guard<std::mutex> guard(m_indiMutex);
361 
364 
365  return 0;
366 }
367 
368 inline
369 int alignLoop::processImage( void* curr_src,
370  const dev::shmimT & dummy
371  )
372 {
373  static_cast<void>(dummy);
374 
375  for(unsigned nn=0; nn < shmimMonitorT::m_width*shmimMonitorT::m_height; ++nn)
376  {
377  m_measurements.data()[nn] = (static_cast<float*>(curr_src)) [nn];
378  }
379 
380 
381  m_delta0 = m_measurements(0,0);
382  m_delta1 = m_measurements(1,0);
383 
384  std::cout << "measurements: ";
385  for(int cc = 0; cc < m_measurements.rows(); ++cc)
386  {
387  std::cout << m_measurements(cc,0) << " ";
388  }
389  std::cout << "\n";
390 
391  if(m_currents[0] < -1e14) return 0;
392 
393  m_commands.matrix() = m_intMat.matrix() * m_measurements.matrix();
394 
395 
396 
397  std::cout << "delta commands: ";
398  for(int cc = 0; cc < m_measurements.rows(); ++cc)
399  {
400  std::cout << -m_commands(cc,0) << " ";
401  }
402  std::cout << "\n";
403 
404  std::vector<float> commands;
405  commands.resize(m_measurements.rows());
406 
407  std::cout << "commands: ";
408  for(int cc = 0; cc < m_measurements.rows(); ++cc)
409  {
410  commands[cc] = m_currents[cc] - m_ggain*m_gains[cc]*m_commands(cc,0);
411  std::cout << commands[cc] << " ";
412  }
413  std::cout << "\n";
414 
415  //And send commands.
416  int rv;
417  if(m_ctrlEnabled)
418  {
419  rv = sendCommands(commands);
420  }
421  else
422  {
423  rv = 0;
424  }
425 
426  updateIfChanged(m_indiP_deltas, std::vector<std::string>({"delta0", "delta1"}), std::vector<float>({m_delta0, m_delta1}));
427  return rv;
428 }
429 
430 inline
431 int alignLoop::sendCommands(std::vector<float> & commands)
432 {
433  for(size_t n=0; n < m_ctrlDevices.size(); ++n)
434  {
435  pcf::IndiProperty ip(pcf::IndiProperty::Number);
436 
437  ip.setDevice(m_ctrlDevices[n]);
438  ip.setName(m_ctrlProperties[n]);
439  ip.add(pcf::IndiElement(m_ctrlTargets[n]));
440  ip[m_ctrlTargets[n]] = commands[n];
441 
442  sendNewProperty(ip);
443  }
444 
445  return 0;
446 }
447 
448 INDI_NEWCALLBACK_DEFN(alignLoop, m_indiP_ggain)(const pcf::IndiProperty &ipRecv)
449 {
450  if(ipRecv.getName() != m_indiP_ggain.getName())
451  {
452  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
453  return -1;
454  }
455 
456  float target;
457 
458  if( indiTargetUpdate( m_indiP_ggain, target, ipRecv, true) < 0)
459  {
460  log<software_error>({__FILE__,__LINE__});
461  return -1;
462  }
463 
464  m_ggain = target;
465 
466  updateIfChanged(m_indiP_ggain, "current", m_ggain);
467  updateIfChanged(m_indiP_ggain, "target", m_ggain);
468 
469  log<text_log>("set global gain to " + std::to_string(m_ggain), logPrio::LOG_NOTICE);
470 
471  return 0;
472 }
473 
474 INDI_NEWCALLBACK_DEFN(alignLoop, m_indiP_ctrlEnabled)(const pcf::IndiProperty &ipRecv)
475 {
476  if(ipRecv.getName() != m_indiP_ctrlEnabled.getName())
477  {
478  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
479  return -1;
480  }
481 
482  //switch is toggled to on
483  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
484  {
485  return toggleLoop(true);
486  }
487 
488  //switch is toggle to off
489  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
490  {
491  return toggleLoop(false);
492  }
493 
494  return 0;
495 }
496 
497 inline
498 int alignLoop::setCallBack_ctrl(const pcf::IndiProperty &ipRecv)
499 {
500  for(size_t n = 0; n < m_ctrlDevices.size(); ++n)
501  {
502  if( ipRecv.getDevice() == m_ctrlDevices[n])
503  {
504  if(ipRecv.getName() == m_ctrlProperties[n] && ipRecv.find(m_ctrlCurrents[n]))
505  {
506  m_currents[n] = ipRecv[m_ctrlCurrents[n]].get<float>();
507  }
508  }
509  }
510 
511  return 0;
512 }
513 
514 INDI_SETCALLBACK_DEFN(alignLoop, m_indiP_upstream)(const pcf::IndiProperty &ipRecv)
515 {
516  if(ipRecv.getName() != m_indiP_upstream.getName())
517  {
518  return log<software_error>({__FILE__,__LINE__, "wrong INDI property received"});
519  }
520 
521  if(!ipRecv.find("toggle")) return 0;
522 
523  if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On && m_upstreamFollowClosed)
524  {
525  std::cerr << "upstream on\n";
526  return toggleLoop(true);
527  }
528  else if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
529  {
530  std::cerr << "upstream off\n";
531  return toggleLoop(false);
532  }
533 
534  return 0;
535 }
536 
537 
538 } //namespace app
539 } //namespace MagAOX
540 
541 #endif //alignLoop_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:73
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:3120
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2297
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:2543
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:3144
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1804
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:545
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:3268
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:2792
The MagAO-X xxxxxxxx.
Definition: alignLoop.hpp:37
int processImage(void *curr_src, const dev::shmimT &)
Definition: alignLoop.hpp:369
pcf::IndiProperty m_indiP_ctrlEnabled
Definition: alignLoop.hpp:139
std::vector< std::string > m_ctrlProperties
Definition: alignLoop.hpp:54
mx::improc::eigenImage< float > m_commands
Definition: alignLoop.hpp:79
static int st_setCallBack_ctrl(void *app, const pcf::IndiProperty &ipRecv)
Definition: alignLoop.hpp:144
int toggleLoop(bool onoff)
Definition: alignLoop.hpp:333
std::vector< float > m_gains
Definition: alignLoop.hpp:83
virtual int appStartup()
Startup function.
Definition: alignLoop.hpp:201
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)
pcf::IndiProperty m_indiP_deltas
Definition: alignLoop.hpp:136
int allocate(const dev::shmimT &)
Definition: alignLoop.hpp:356
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:314
std::string m_upstreamProperty
Definition: alignLoop.hpp:67
int setCallBack_ctrl(const pcf::IndiProperty &ipRecv)
Definition: alignLoop.hpp:498
virtual int appShutdown()
Shutdown the app.
Definition: alignLoop.hpp:326
pcf::IndiProperty m_indiP_ggain
Definition: alignLoop.hpp:138
std::vector< float > m_currents
Definition: alignLoop.hpp:58
~alignLoop() noexcept
D'tor, declared and defined for noexcept.
Definition: alignLoop.hpp:92
alignLoop()
Default c'tor.
Definition: alignLoop.hpp:157
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:180
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:152
virtual void setupConfig()
Definition: alignLoop.hpp:163
int sendCommands(std::vector< float > &commands)
Definition: alignLoop.hpp:431
virtual void loadConfig()
Definition: alignLoop.hpp:196
uint32_t m_width
The width of the images in the stream.
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
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.
int 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:208
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
Definition: indiMacros.hpp:282
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
Definition: indiMacros.hpp:385
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:55
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
std::ostream & cout()
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
INDI_SETCALLBACK_DEFN(adcTracker, m_indiP_teldata)(const pcf
Definition: adcTracker.hpp:461
updateIfChanged(m_indiP_angle, "target", m_angle)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
Software ERR log entry.