API
hoPredCtrl.hpp
Go to the documentation of this file.
1 /** \file hoPredCtrl.hpp
2  * \brief The MagAO-X tweeter to woofer offloading manager
3  *
4  * \ingroup app_files
5  */
6 
7 #ifndef hoPredCtrl_hpp
8 #define hoPredCtrl_hpp
9 
10 #include <iostream>
11 #include <limits>
12 #include <chrono>
13 #include <thread>
14 
15 
16 #include <mx/improc/eigenCube.hpp>
17 #include <mx/improc/eigenImage.hpp>
18 using namespace mx::improc;
19 
20 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
21 #include "../../magaox_git_version.h"
22 
23 #include "predictive_controller.cuh"
24 
25 using namespace DDSPC;
26 
27 namespace MagAOX
28 {
29 namespace app
30 {
31 
32 struct darkShmimT
33 {
34  static std::string configSection()
35  {
36  return "darkShmim";
37  };
38 
39  static std::string indiPrefix()
40  {
41  return "dark";
42  };
43 };
44 
45 /** \defgroup hoPredCtrl Tweeter to Woofer Offloading
46  * \brief Monitors the averaged tweeter shape, and sends it to the woofer.
47  *
48  * <a href="../handbook/operating/software/apps/hoPredCtrl.html">Application Documentation</a>
49  *
50  * \ingroup apps
51  *
52  */
53 
54 /** \defgroup hoPredCtrl_files Tweeter to Woofer Offloading
55  * \ingroup hoPredCtrl
56  */
57 
58 /** MagAO-X application to control offloading the tweeter to the woofer.
59  *
60  * \ingroup hoPredCtrl
61  *
62  */
63 class hoPredCtrl : public MagAOXApp<true>, public dev::shmimMonitor<hoPredCtrl>, public dev::shmimMonitor<hoPredCtrl,darkShmimT>
64 {
65 
66  //Give the test harness access.
67  friend class hoPredCtrl_test;
68 
69  friend class dev::shmimMonitor<hoPredCtrl>;
71 
72  //The base shmimMonitor type
74 
75  //The dark shmimMonitor type
77 
78  ///Floating point type in which to do all calculations.
79  typedef float realT;
80 
81 protected:
82 
83  /** \name Configurable Parameters
84  *@{
85  */
86 
87  // std::string m_twRespMPath;
88  // std::string m_dmChannel;
89  // float m_gain {0.1};
90  // float m_leak {0.0};
91  // float m_actLim {7.0}; ///< the upper limit on woofer actuator commands. default is 7.0.
92 
93  ///@}
94 
95  std::string m_pupilMaskFilename; // aol1_wfsmask.fits
96  std::string m_interaction_matrix_filename; // aol1_modesWFS.fits in cacao
97  std::string m_mapping_matrix_filename; // aol1_DMmodes.fits
98  std::string m_refWavefront_filename; // aol1_wfsref.fits
100 
101  // std::string m_actuator_mask_filename; //
102 
103  // IMAGE m_dmStream;
104  size_t m_pwfsWidth {0}; ///< The width of the image
105  size_t m_pwfsHeight {0}; ///< The height of the image.
106 
107  size_t m_quadWidth {0}; ///< The width of the image
108  size_t m_quadHeight {0}; ///< The height of the image.
109 
110  uint8_t m_pwfsDataType{0}; ///< The ImageStreamIO type code.
111  size_t m_pwfsTypeSize {0}; ///< The size of the type, in bytes.
112 
113  unsigned long long duration;
114  unsigned long long iterations;
115 
116  // The wavefront sensor variables
119 
120  eigenImage<realT> m_interaction_matrix;
121  eigenImage<realT> m_mapping_matrix;
122 
123  eigenImage<realT> m_pupilMask;
124  eigenImage<realT> m_measurementVector;
125  eigenImage<realT> m_refWavefront;
126 
128 
130 
131  realT (*pwfs_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
132 
133  // The dark image parameters
134  eigenImage<realT> m_darkImage;
135  realT (*dark_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
136  bool m_darkSet {false};
137 
138  // Predictive control parameters
139  float m_clip_val;
142  int m_numHist; ///< The number of past states to use for the prediction
143  int m_numFut; ///< The number of future states to predict
144  realT m_gamma; ///< The forgetting factore (0, 1)
145 
146  realT m_inv_covariance; ///< The starting point of the inverse covariance matrix
147  realT m_lambda; ///< The regularization parameter
148 
149  // Integrator commands
153 
154  DDSPC::PredictiveController* controller;
155 
156  // Learning time
162 
164 
165  // Interaction for the DM
166  float* m_command;
168 
169  // If true we can just use a mask to map the controlled subset of actuators to the 50x50 shmim.
170  // Otherwise we should use another matrix that maps mode coefficients to DM actuator patterns.
172  eigenImage<realT> m_illuminated_actuators_mask;
173  eigenImage<realT> m_shaped_command; // 50x50
174 
175  std::string m_dmChannel;
176  IMAGE m_dmStream;
177  uint32_t m_dmWidth {0}; ///< The width of the image
178  uint32_t m_dmHeight {0}; ///< The height of the image.
179 
180  uint8_t m_dmDataType{0}; ///< The ImageStreamIO type code.
181  size_t m_dmTypeSize {0}; ///< The size of the type, in bytes.
182 
183  bool m_dmOpened {false};
184  bool m_dmRestart {false};
185 
186  std::string savepath;
187 
188  pcf::IndiProperty m_indiP_controlToggle;
189  pcf::IndiProperty m_indiP_predictorToggle;
190  pcf::IndiProperty m_indiP_reset_bufferRequest;
191  pcf::IndiProperty m_indiP_reset_modelRequest;
192  pcf::IndiProperty m_indiP_reset_cleanRequest;
193 
195 
196  pcf::IndiProperty m_indiP_learningSteps;
197  pcf::IndiProperty m_indiP_learningIterations;
198  pcf::IndiProperty m_indiP_explorationRms;
199  pcf::IndiProperty m_indiP_explorationSteps;
200  pcf::IndiProperty m_indiP_reset_exploreRequest;
201  pcf::IndiProperty m_indiP_zeroRequest;
202 
203  pcf::IndiProperty m_indiP_saveRequest;
204  pcf::IndiProperty m_indiP_loadRequest;
205  pcf::IndiProperty m_indiP_timestamp;
206 
207  // The control parameters
208  pcf::IndiProperty m_indiP_lambda;
209  pcf::IndiProperty m_indiP_clipval;
210  pcf::IndiProperty m_indiP_gamma;
211 
212  // Integrator parameters
213  pcf::IndiProperty m_indiP_intgain;
214  pcf::IndiProperty m_indiP_intleak;
215 
216  // pcf::IndiProperty m_indiP_inv_cov;
217 
218  /*
219  TODO:
220  5) Create a Double variant? Use a typedef for the variables.
221  6) Create a GUI.
222  7) Update reconstruction matrix.
223  8) Update pupil mask.
224  9) Add a toggle to create an empty loop?
225  10) Save and load model.
226  11) Reset to loaded model.
227  */
228 
229  // Control states
230  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_controlToggle);
231  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_bufferRequest);
232  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_modelRequest);
233  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_cleanRequest);
234  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_updateControllerRequest);
235  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_exploreRequest);
236  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_zeroRequest);
237  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_saveRequest);
238  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_loadRequest);
239  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_timestamp);
240 
241 
242  // Control parameters
243  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_learningSteps);
244  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_learningIterations);
245  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_explorationRms);
246  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_explorationSteps);
250 
251  INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_predictorToggle);
254 
255 public:
256  /// Default c'tor.
257  hoPredCtrl();
258 
259  /// D'tor, declared and defined for noexcept.
260  ~hoPredCtrl() noexcept
261  {}
262 
263  virtual void setupConfig();
264 
265  /// Implementation of loadConfig logic, separated for testing.
266  /** This is called by loadConfig().
267  */
268  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
269 
270  virtual void loadConfig();
271 
272  /// Startup function
273  /**
274  *
275  */
276  virtual int appStartup();
277 
278  /// Implementation of the FSM for hoPredCtrl.
279  /**
280  * \returns 0 on no critical error
281  * \returns -1 on an error requiring shutdown
282  */
283  virtual int appLogic();
284 
285  /// Shutdown the app.
286  /**
287  *
288  */
289  virtual int appShutdown();
290 
291  int allocate( const dev::shmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
292  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
293  const dev::shmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
294  );
295 
296  int allocate( const darkShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
297 
298  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
299  const darkShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
300  );
301 
302  int zero();
303  int send_dm_command();
304  int map_command_vector_to_dmshmim();
305  int set_pupil_mask(std::string pupil_mask_filename);
306 
307  void save_state(){};
308 
309 protected:
310 
311 
312 };
313 
314 inline
315 hoPredCtrl::hoPredCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
316 {
318  return;
319 }
320 
321 inline
323 {
326 
327  config.add("parameters.calib_directory", "", "parameters.calib_directory", argType::Required, "parameters", "calib_directory", false, "string", "The path to the PyWFS pupil mask.");
328  config.add("parameters.pupil_mask", "", "parameters.pupil_mask", argType::Required, "parameters", "pupil_mask", false, "string", "The path to the PyWFS pupil mask.");
329  config.add("parameters.interaction_matrix", "", "parameters.interaction_matrix", argType::Required, "parameters", "interaction_matrix", false, "string", "The path to the PyWFS interaction matrix.");
330  config.add("parameters.mapping_matrix", "", "parameters.mapping_matrix", argType::Required, "parameters", "mapping_matrix", false, "string", "The path to the DM mapping matrix.");
331  config.add("parameters.act_mask", "", "parameters.act_mask", argType::Required, "parameters", "act_mask", false, "string", "The path to the PyWFS interaction matrix.");
332  config.add("parameters.reference_image", "", "parameters.reference_image", argType::Required, "parameters", "reference_image", false, "string", "The path to the PyWFS interaction matrix.");
333 
334 
335  config.add("parameters.Nhist", "", "parameters.Nhist", argType::Required, "parameters", "Nhist", false, "int", "The history length.");
336  config.add("parameters.Nfut", "", "parameters.Nfut", argType::Required, "parameters", "Nfut", false, "int", "The prediction horizon.");
337  config.add("parameters.gamma", "", "parameters.gamma", argType::Required, "parameters", "gamma", false, "float", "The prediction horizon.");
338  config.add("parameters.inv_covariance", "", "parameters.inv_covariance", argType::Required, "parameters", "inv_covariance", false, "float", "The prediction horizon.");
339  config.add("parameters.lambda", "", "parameters.lambda", argType::Required, "parameters", "lambda", false, "float", "The prediction horizon.");
340  config.add("parameters.clip_val", "", "parameters.clip_val", argType::Required, "parameters", "clip_val", false, "float", "The update clip value.");
341 
342  //
343  config.add("parameters.learning_steps", "", "parameters.learning_steps", argType::Required, "parameters", "learning_steps", false, "int", "The update clip value.");
344  config.add("parameters.learning_iterations", "", "parameters.learning_iterations", argType::Required, "parameters", "learning_iterations", false, "int", "The amount of learning cycles.");
345  config.add("parameters.exploration_steps", "", "parameters.exploration_steps", argType::Required, "parameters", "exploration_steps", false, "int", "The update clip value.");
346  config.add("parameters.exploration_rms", "", "parameters.exploration_rms", argType::Required, "parameters", "exploration_rms", false, "float", "The update clip value.");
347 
348  // Read in the learning parameters as a vector.
349  // config.add("parameters.exploration_steps", "", "parameters.exploration_steps", argType::Required, "parameters", "exploration_steps", false, "vector<int>", "The number of steps for each training iteration.");
350  // config.add("parameters.exploration_rms", "", "parameters.exploration_rms", argType::Required, "parameters", "exploration_rms", false, "vector<double>", "The rms for each training iteration.");
351  // config.add("parameters.exploration_lambda", "", "parameters.exploration_lambda", argType::Required, "parameters", "exploration_lambda", false, "vector<double>", "The regularization for each training iteration.");
352 
353  //
354  config.add("parameters.channel", "", "parameters.channel", argType::Required, "parameters", "channel", false, "string", "The DM channel to control.");
355 
356  // The integrator parameters
357  config.add("integrator.gain", "", "integrator.gain", argType::Required, "integrator", "gain", false, "float", "The integrator gain value.");
358  config.add("integrator.leakage", "", "integrator.leakage", argType::Required, "integrator", "leakage", false, "float", "The integrator leakage.");
359 }
360 
361 inline
362 int hoPredCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
363 {
364 
365  shmimMonitorT::loadConfig(_config);
366  darkMonitorT::loadConfig(_config);
367 
368  // The integrator control parameters
369  _config(m_intgain, "integrator.gain");
370  _config(m_intleak, "integrator.leakage");
371 
372  // Calibration files
373  std::string calibration_directory;
374  _config(calibration_directory, "parameters.calib_directory");
375 
376  _config(m_pupilMaskFilename, "parameters.pupil_mask");
377  m_pupilMaskFilename = calibration_directory + m_pupilMaskFilename;
378  std::cout << m_pupilMaskFilename << std::endl;
379 
380  _config(m_interaction_matrix_filename, "parameters.interaction_matrix");
383 
384  _config(m_mapping_matrix_filename, "parameters.mapping_matrix");
385  m_mapping_matrix_filename = calibration_directory + m_mapping_matrix_filename;
386  std::cout << m_mapping_matrix_filename << std::endl;
387 
388  _config(m_refWavefront_filename, "parameters.reference_image");
389  m_refWavefront_filename = calibration_directory + m_refWavefront_filename;
390  std::cout << m_refWavefront_filename << std::endl;
391 
392  // Controller parameters
393  _config(m_numHist, "parameters.Nhist");
394  std::cout << "Nhist="<< m_numHist << std::endl;
395  _config(m_numFut, "parameters.Nfut");
396  std::cout << "Nfut="<< m_numFut << std::endl;
397 
398  _config(m_gamma, "parameters.gamma");
399  std::cout << "gamma="<< m_gamma << std::endl;
400  _config(m_inv_covariance, "parameters.inv_covariance");
401  std::cout << "inv_covariance="<< m_inv_covariance << std::endl;
402  _config(m_lambda, "parameters.lambda");
403  std::cout << "lambda="<< m_lambda << std::endl;
404  _config(m_clip_val, "parameters.clip_val");
405  std::cout << "clip_val="<< m_clip_val << std::endl;
406 
407  // The learning parameters
408  _config(m_exploration_steps, "parameters.exploration_steps");
409  _config(m_learning_steps, "parameters.learning_steps");
411  _config(m_learning_iterations, "parameters.learning_iterations");
412 
413  _config(m_exploration_rms, "parameters.exploration_rms");
414  std::cout << "Nexplore:: "<< m_exploration_steps << " with " << m_exploration_rms << " rms." << std::endl;
415 
416  _config(m_dmChannel, "parameters.channel");
417  std::cout << "Open DM tweeter channel at " << m_dmChannel << std::endl;
418  std::cout << "Done reading config Impl." << std::endl;
419 
420  return 0;
421 }
422 
423 inline
425 {
426  loadConfigImpl(config);
427 }
428 
429 inline
431 {
432 
433  if(shmimMonitorT::appStartup() < 0)
434  {
435  return log<software_error,-1>({__FILE__, __LINE__});
436  }
437 
438  if(darkMonitorT::appStartup() < 0)
439  {
440  return log<software_error,-1>({__FILE__, __LINE__});
441  }
442 
443  createStandardIndiToggleSw( m_indiP_controlToggle, "control", "Control State", "Loop Controls");
445 
446  createStandardIndiRequestSw( m_indiP_reset_bufferRequest, "reset_buffer", "Reset the data buffers", "Loop Controls");
448 
449  createStandardIndiRequestSw( m_indiP_reset_modelRequest, "reset_model", "Reset the RLS model", "Loop Controls");
451 
452  createStandardIndiRequestSw( m_indiP_reset_cleanRequest, "clean", "Clean the complete model.", "Loop Controls");
454 
455  createStandardIndiRequestSw( m_indiP_updateControllerRequest, "calc_controller", "Update the controller.", "Loop Controls");
457 
458  createStandardIndiRequestSw( m_indiP_reset_exploreRequest, "reset_exploration", "Reset the exploration model", "Loop Controls");
460 
461  createStandardIndiRequestSw( m_indiP_zeroRequest, "zero", "Zero the dm", "Loop Controls");
463 
464  createStandardIndiRequestSw( m_indiP_saveRequest, "save", "Save the controller", "Loop Controls");
466 
467  createStandardIndiRequestSw( m_indiP_loadRequest, "load", "Load the controller", "Loop Controls");
469 
470  createStandardIndiNumber<uint64_t>( m_indiP_timestamp, "timestamp", -1, 20000000000000000, 1, "%d", "Timestamp", "Loading the controller");
472 
473  createStandardIndiNumber<int>( m_indiP_learningSteps, "learning_steps", -1, 200000, 1, "%d", "Learning Steps", "Learning control");
475 
476  createStandardIndiNumber<int>( m_indiP_learningSteps, "learning_steps", -1, 200000, 1, "%d", "Learning Steps", "Learning control");
478 
479  createStandardIndiNumber<int>( m_indiP_learningIterations, "learning_iterations", -1, 200000, 1, "%d", "Learning iterations", "Learning control");
481 
482  createStandardIndiNumber<float>( m_indiP_explorationRms, "exploration_rms", 0.0, 1.0, 0.00001, "%0.4f", "Learning Steps", "Learning control");
484 
485  createStandardIndiNumber<float>( m_indiP_explorationSteps, "exploration_steps", 0, 200000, 1, "%d", "Exploration Steps", "Learning control");
487 
488  createStandardIndiNumber<float>( m_indiP_gamma, "gamma", 0, 1.0, 0.0001, "%0.3f", "Forgetting parameter", "Learning control");
490 
491  createStandardIndiNumber<float>( m_indiP_lambda, "lambda", 0, 1000.0, 0.0001, "%0.3f", "Regularization", "Learning control");
493 
494  createStandardIndiNumber<float>( m_indiP_clipval, "clipval", 0, 1000.0, 0.0001, "%0.3f", "Regularization", "Learning control");
496 
497  // createStandardIndiNumber<int>( m_indiP_inv_cov, "lambda", 0, 1e8, 0.1, "%0.3f", "Inverse Covariance", "Learning control");
498  // registerIndiPropertyNew( m_indiP_inv_cov, INDI_NEWCALLBACK(m_indiP_inv_cov) );
499 
500  createStandardIndiToggleSw( m_indiP_predictorToggle, "use_predictor", "Choose controller", "Loop Controls");
502 
503  createStandardIndiNumber<float>( m_indiP_intgain, "intgain", 0, 1.0, 0.0001, "%0.3f", "Integrator gain", "Learning control");
505 
506  createStandardIndiNumber<float>( m_indiP_intleak, "intleak", 0, 1.0, 0.0001, "%0.3f", "Integrator gain", "Learning control");
508 
510 
511  return 0;
512 }
513 
514 inline
516 {
517  if( shmimMonitorT::appLogic() < 0)
518  {
519  return log<software_error,-1>({__FILE__,__LINE__});
520  }
521 
522 
523  if( darkMonitorT::appLogic() < 0)
524  {
525  return log<software_error,-1>({__FILE__,__LINE__});
526  }
527 
528  std::unique_lock<std::mutex> lock(m_indiMutex);
529 
530  if(shmimMonitorT::updateINDI() < 0)
531  {
532  log<software_error>({__FILE__, __LINE__});
533  }
534 
535  if(darkMonitorT::updateINDI() < 0)
536  {
537  log<software_error>({__FILE__, __LINE__});
538  }
539 
540  // Send updates to indi if the values changed due to changes caused by software
541 
549 
552  // updateIfChanged(m_indiP_inv_cov, "current", m_inv_covariance);
553 
554  if(m_is_closed_loop){
555  updateSwitchIfChanged(m_indiP_controlToggle, "toggle", pcf::IndiElement::On, INDI_OK);
556  }else{
557  updateSwitchIfChanged(m_indiP_controlToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
558  }
559 
561  updateSwitchIfChanged(m_indiP_predictorToggle, "toggle", pcf::IndiElement::On, INDI_OK);
562  }else{
563  updateSwitchIfChanged(m_indiP_predictorToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
564  }
565 
566 
567  return 0;
568 }
569 
570 inline
572 {
574 
576 
577  m_shaped_command.setZero();
578  send_dm_command();
579  delete controller;
580 
581  if(m_temp_command){
582  delete m_temp_command;
583  }
584 
585  return 0;
586 }
587 
588 inline
590 {
591  static_cast<void>(dummy); //be unused
592 
593  // Or get from config
595 
596  // Wavefront sensor setup
598  m_quadWidth = m_pwfsWidth / 2;
599  std::cout << "Width " << m_pwfsWidth << std::endl;
600 
603  std::cout << "Height " << m_pwfsHeight << std::endl;
604 
606  std::cout << "Start reading in calibration files\n";
607 
608  // Read in the pupil mask
609  mx::fits::fitsFile<realT> ff;
610  eigenCube<realT> temp_matrix;
611 
612  //
613  ff.read(temp_matrix, m_interaction_matrix_filename);
614  // std::cout << temp_matrix.shape() << "\n";
615  std::cerr << "Read a " << temp_matrix.rows() << " x " << temp_matrix.cols() << " x " << temp_matrix.planes() << " interaction matrix.\n";
616  m_interaction_matrix = temp_matrix.asVectors().matrix().transpose().array();
617 
618  //
619  std::cerr << "Read a " << m_interaction_matrix.rows() << " x " << m_interaction_matrix.cols() << " interaction matrix.\n";
621 
622  bool use_cacao_calib = true;
623  if(use_cacao_calib){
624 
625  for(int row_i = 0; row_i < m_interaction_matrix.rows(); row_i++ ){
626 
627  realT norm = 0;
628  for(int col_i = 0; col_i < m_interaction_matrix.cols(); col_i++ ){
629  if(col_i==(25*30))
630  std::cout << m_interaction_matrix(row_i, col_i) << std::endl;
631 
632  norm += m_interaction_matrix(row_i, col_i) * m_interaction_matrix(row_i, col_i);
633  }
634 
635  std::cout << "Norm for mode " << row_i << " " << norm << std::endl;
636 
637 
638  for(int col_i = 0; col_i < m_interaction_matrix.cols(); col_i++ ){
639  m_interaction_matrix(row_i, col_i) = m_interaction_matrix(row_i, col_i) / norm;
640 
641  if(col_i==(25*30))
642  std::cout << m_interaction_matrix(row_i, col_i) << std::endl;
643 
644  //
645  }
646 
647  realT test_norm = 0.0;
648  for(int col_i = 0; col_i < m_interaction_matrix.cols(); col_i++ ){
649  test_norm += m_interaction_matrix(row_i, col_i) * m_interaction_matrix(row_i, col_i);
650  }
651  std::cout << "Test Norm for mode " << row_i << " " << test_norm << std::endl;
652  }
653 
654  }
655 
656  // Read in the reference image
658  std::cerr << "Read a " << m_refWavefront.rows() << " x " << m_refWavefront.cols() << " reference image.\n";
659 
660  // Read in the pupil mask
661  ff.read(temp_matrix, m_mapping_matrix_filename);
662  m_mapping_matrix = temp_matrix.asVectors();
663 
664  std::cerr << "Read a " << m_mapping_matrix.rows() << " x " << m_mapping_matrix.cols() << " mapping matrix.\n";
666 
667  m_temp_command = new float[m_numVoltages];
668  for(int i = 0; i < m_numVoltages; ++i)
669  m_temp_command[i] = 0.001;
671  std::cerr << "Initialized temp command.\n";
672 
673  // Allocate the DM
674  if(m_dmOpened){
675  ImageStreamIO_closeIm(&m_dmStream);
676  }
677 
678  m_dmOpened = false;
679  m_dmRestart = false; //Set this up front, since we're about to restart.
680  use_actuators = true;
681 
682  if( ImageStreamIO_openIm(&m_dmStream, m_dmChannel.c_str()) == 0){
683  if(m_dmStream.md[0].sem < 10){
684  ImageStreamIO_closeIm(&m_dmStream);
685  }else{
686  m_dmOpened = true;
687  }
688  }
689 
690  if(!m_dmOpened){
691  // log<software_error>({__FILE__, __LINE__, m_dmChannel + " not opened."});
692 
693  log<text_log>( m_dmChannel + " not opened.", logPrio::LOG_NOTICE);
694  return -1;
695  }else{
696  m_dmWidth = m_dmStream.md->size[0];
697  m_dmHeight = m_dmStream.md->size[1];
698 
699  m_dmDataType = m_dmStream.md->datatype;
700  m_dmTypeSize = sizeof(float);
701 
702  log<text_log>( "Opened " + m_dmChannel + " " + std::to_string(m_dmWidth) + " x " + std::to_string(m_dmHeight) + " with data type: " + std::to_string(m_dmDataType), logPrio::LOG_NOTICE);
704  std::cout << m_shaped_command.rows() << " x " << m_shaped_command.cols() << '\n';
705  m_shaped_command.setZero();
706  send_dm_command();
707  }
708 
709  // Controller setup
711  controller->set_interaction_matrix(m_interaction_matrix.data());
712  controller->set_mapping_matrix(m_mapping_matrix.data());
713  std::cerr << "Finished intializing the controller.\n";
714 
715  // mx::fits::fitsFile<realT> ff2;
716  // ff2.read(m_illuminated_actuators_mask, m_actuator_mask_filename);
717  // std::cerr << "Read a " << m_illuminated_actuators_mask.rows() << " x " << m_illuminated_actuators_mask.cols() << " actuator mask.\n";
718 
719  controller->create_exploration_buffer(m_exploration_rms, m_exploration_steps);
720  std::cerr << "Initialized exploration buffer.\n";
721 
722  //Initialize dark image if not correct size.
725  m_darkImage.setZero();
726  m_darkSet = false;
727  }
728 
729  duration = 0;
730  iterations = 0;
731  m_is_closed_loop = false;
732 
733  m_use_predictive_control = false;
734  controller->controller->set_integrator(m_use_predictive_control, m_intgain, m_intleak);
735  std::cerr << "Finished setup.\n";
736 
737  average_pupil_intensity = -100000.0;
738 
739  savepath = "/data/users/xsup/PredCtrlData/";
740  return 0;
741 }
742 
743 inline
744 int hoPredCtrl::processImage( void * curr_src, const dev::shmimT & dummy )
745 {
746 
747  static_cast<void>(dummy); //be unused
748  auto start = std::chrono::steady_clock::now();
749 
750  Eigen::Map<eigenImage<unsigned short>> pwfsIm( static_cast<unsigned short *>(curr_src), m_pwfsHeight, m_pwfsWidth);
751  // Calculate the norm
752  realT pwfs_norm = 0;
753 
755  // realT mean_value = 0;
756  realT Ia = 0, Ib = 0, Ic = 0, Id = 0;
757  realT total_norm = 0;
758  size_t number_of_pixels = 0;
759 
760  size_t ki = 0;
761  for(uint32_t col_i=0; col_i < m_quadWidth; ++col_i){
762  for(uint32_t row_i=0; row_i < m_quadHeight; ++row_i){
763  // Select the pixel from the correct quadrant and subtract dark
764  Ic = (realT)pwfsIm(row_i, col_i) - m_darkImage(row_i, col_i);
765  Id = (realT)pwfsIm(row_i + m_quadWidth, col_i) - m_darkImage(row_i + m_quadWidth, col_i);
766  Ia = (realT)pwfsIm(row_i, col_i + m_quadHeight) - m_darkImage(row_i, col_i + m_quadHeight);
767  Ib = (realT)pwfsIm(row_i + m_quadWidth, col_i + m_quadHeight) - m_darkImage(row_i + m_quadWidth, col_i + m_quadHeight);
768 
769  // Calculate the norm
770  // TODO: Add an exponential learning to the PWFS norm?
771  pwfs_norm = Ia + Ib + Ic + Id;
772 
773  // Take all linear combinations of the measurements and concatenate in vector
774  if(m_pupilMask(row_i, col_i) > 0.5){
775  m_measurementVector(ki, 0) = (Ia - Ib + Ic - Id) / pwfs_norm;
776  m_measurementVector(ki + m_illuminatedPixels, 0) = (Ia + Ib - Ic - Id) / pwfs_norm;
777  m_measurementVector(ki + 2 * m_illuminatedPixels, 0) = (Ia - Ib - Ic + Id) / pwfs_norm;
778  ++ki;
779  total_norm += pwfs_norm;
780  }
781  }
782  }
783 
784  total_norm /= ki;
785 
786  }else{
787 
788  for(uint32_t col_i=0; col_i < m_pwfsWidth; ++col_i){
789  for(uint32_t row_i=0; row_i < m_pwfsHeight; ++row_i){
790  pwfs_norm += m_pupilMask(row_i,col_i) * ((realT)pwfsIm(row_i, col_i) - m_darkImage(row_i, col_i));
791  }
792  }
793 
794  // Extract the illuminated pixels.
795  size_t ki = 0;
796  for(uint32_t col_i=0; col_i < m_pwfsWidth; ++col_i){
797  for(uint32_t row_i=0; row_i < m_pwfsHeight; ++row_i){
798  // Subtract a dark and the reference.
799  m_measurementVector(ki, 0) = m_pupilMask(row_i,col_i) * (((realT)pwfsIm(row_i, col_i) - m_darkImage(row_i, col_i)) / pwfs_norm - m_refWavefront(row_i, col_i));
800  ++ki;
801  }
802  }
803 
804  }
805 
806  controller->add_measurement(m_measurementVector.data());
807 
808  // Okay reconstruction matrix is used correctly!
809  // The error is now in the slope measurement.
810  if( m_is_closed_loop ){
811  m_command = controller->get_command(m_clip_val);
812 
813  // This works!
814  if(use_actuators){
816  }
817  send_dm_command();
818 
819  // Only learn if we have a predictor
821 
822  // If -1 always learn, otherwise learn for N steps
823  if(m_learning_counter == -1){
824 
825  controller->update_predictor();
826  controller->update_controller();
827 
828  }else if(m_learning_counter > 0){
829 
830  controller->update_predictor();
831  controller->update_controller();
832  m_learning_counter -= 1;
833 
834  }
835 
839 
840  m_lambda /= 10.0;
841 
842  controller->set_zero(); // set current control to zero
843  zero(); // set the DM shape to zero
844  controller->create_exploration_buffer(m_exploration_rms, m_exploration_steps); // Make a new buffer
845  controller->set_new_regularization(m_lambda); // set a new regularization
846  controller->reset_data_buffer(); // remove the history
847  }
848 
849 
850  }
851 
852  }
853 
854  auto end = std::chrono::steady_clock::now();
855 
856 
857  if(iterations == 0){
858  duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
859  }else{
860  duration = 0.95 * duration + 0.05 * std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
861  }
862  iterations += 1;
863 
864  if(iterations % 10000 == 0){
865  // std::cout << "PWFS NORM: " << pwfs_norm << std::endl;
866  // std::cout << m_measurementVector(25*30,0)/1e-5 << " " << m_measurementVector(25*30+1,0)/1e-5 << " " << m_measurementVector(25*30+2,0)/1e-5 << std::endl;
867  // controller->m_measurement->print(true);
868  std::cout << "elapsed " << (double)duration << " us." << std::endl;
869  std::cout << '\n';
870  }
871 
872  return 0;
873 }
874 
875 inline
876 int hoPredCtrl::set_pupil_mask(std::string pupil_mask_filename){
877 /*
878  This function reads in the filename to create a pupil mask and to initialize the measurement vector.
879 */
880 
881  // Read in the pupil mask
882  mx::fits::fitsFile<realT> ff;
883  ff.read(m_pupilMask, pupil_mask_filename);
884  std::cerr << "Read a " << m_pupilMask.rows() << " x " << m_pupilMask.cols() << " matrix.\n";
885 
886  // Count the number of pixels that are used for the wavefront sensing
887  realT * data = m_pupilMask.data();
889 
891  m_measurement_size = m_pupilMask.rows() * m_pupilMask.cols();
892  }else{
893  for(size_t nn=0; nn < m_pupilMask.rows() * m_pupilMask.cols(); ++nn){
894  if(data[nn] > 0.5)
896  }
898  }
899 
900  std::cout << "Number of illuminated pixels :: " << m_illuminatedPixels << std::endl;
901  std::cout << "Measurement vector size :: " << m_measurement_size << std::endl;
902 
903  // Create the measurement vector
905  m_measurementVector.setZero();
906 
907  return 0;
908 }
909 
910 
911 inline
913 {
914 
915  static_cast<void>(dummy); //be unused
916 
917  m_darkSet = false;
918 
920 
921  dark_pixget = getPixPointer<realT>(darkMonitorT::m_dataType);
922 
923  if(dark_pixget == nullptr)
924  {
925  log<software_error>({__FILE__, __LINE__, "bad data type"});
926  return -1;
927  }
928  std::cout << "Allocated dark frames stuff. \n";
929 
930 
931  return 0;
932 }
933 
934 inline
935 int hoPredCtrl::processImage( void * curr_src,
936  const darkShmimT & dummy
937  )
938 {
939 
940  static_cast<void>(dummy); //be unused
941 
942  realT * data = m_darkImage.data();
943 
944  for(unsigned nn=0; nn < darkMonitorT::m_width*darkMonitorT::m_height; ++nn)
945  {
946  //data[nn] = *( (int16_t * ) (curr_src + nn*shmimMonitorT::m_dmDataType));
947  data[nn] = dark_pixget(curr_src, nn);
948  }
949 
950  m_darkSet = true;
951 
952 
953  return 0;
954 }
955 
956 
957 inline
959 {
960 
961  m_shaped_command.setZero();
962  send_dm_command();
963  return 0;
964 
965 }
966 
967 inline
969 
970  // Convert the actuators modes into a 50x50 image.
971  /*
972  This function maps the command vector to a masked 2D image. A mapping is implicitely assumed due to the way the array is accessed.
973  */
974 
975 
976  // The new output of the controller is a nact length vector. So this can be replaced by a single copy statement.
977  // For now let's keep the dumb copy.
978  int ki = 0;
979  for(uint32_t col_i=0; col_i < m_dmHeight; ++col_i){
980  for(uint32_t row_i=0; row_i < m_dmHeight; ++row_i){
981  m_shaped_command(row_i, col_i) = m_command[ki];
982  ki += 1;
983  }
984  }
985 
986 
987  return 0;
988 }
989 
990 inline
992  // Check if processImage is running
993  // while(m_dmStream.md[0].write == 1);
994 
995  m_dmStream.md[0].write = 1;
996  memcpy(m_dmStream.array.raw, m_shaped_command.data(), 2500 * sizeof(float));
997  m_dmStream.md[0].cnt0++;
998  m_dmStream.md[0].write = 0;
999 
1000  ImageStreamIO_sempost(&m_dmStream,-1);
1001 
1002  // log<text_log>("zeroed", logPrio::LOG_NOTICE);W
1003  return 0;
1004 }
1005 
1006 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_learningSteps )(const pcf::IndiProperty &ipRecv)
1007 {
1008  if(ipRecv.getName() != m_indiP_learningSteps.getName())
1009  {
1010  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1011  return -1;
1012  }
1013 
1014  int current = -1;
1015  int target = -1;
1016 
1017  if(ipRecv.find("current"))
1018  {
1019  current = ipRecv["current"].get<int>();
1020  }
1021 
1022  if(ipRecv.find("target"))
1023  {
1024  target = ipRecv["target"].get<int>();
1025  }
1026 
1027  if(target == -1) target = current;
1028 
1029  if(target == -1)
1030  {
1031  return 0;
1032  }
1033 
1034  std::lock_guard<std::mutex> guard(m_indiMutex);
1035 
1036  m_learning_counter = target;
1037  m_learning_steps = target;
1038 
1039  updateIfChanged(m_indiP_learningSteps, "target", m_learning_counter);
1040 
1041  return 0;
1042 }
1043 
1044 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_learningIterations )(const pcf::IndiProperty &ipRecv)
1045 {
1046  if(ipRecv.getName() != m_indiP_learningIterations.getName())
1047  {
1048  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1049  return -1;
1050  }
1051 
1052  int current = -1;
1053  int target = -1;
1054 
1055  if(ipRecv.find("current"))
1056  {
1057  current = ipRecv["current"].get<int>();
1058  }
1059 
1060  if(ipRecv.find("target"))
1061  {
1062  target = ipRecv["target"].get<int>();
1063  }
1064 
1065  if(target == -1) target = current;
1066 
1067  if(target == -1)
1068  {
1069  return 0;
1070  }
1071 
1072  std::lock_guard<std::mutex> guard(m_indiMutex);
1073 
1074  m_learning_iterations = target;
1075 
1076  updateIfChanged(m_indiP_learningIterations, "target", m_learning_iterations);
1077 
1078  return 0;
1079 }
1080 
1081 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_explorationRms )(const pcf::IndiProperty &ipRecv)
1082 {
1083  if(ipRecv.getName() != m_indiP_explorationRms.getName())
1084  {
1085  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1086  return -1;
1087  }
1088 
1089  float current = -1;
1090  float target = -1;
1091 
1092  if(ipRecv.find("current"))
1093  {
1094  current = ipRecv["current"].get<float>();
1095  }
1096 
1097  if(ipRecv.find("target"))
1098  {
1099  target = ipRecv["target"].get<float>();
1100  }
1101 
1102  if(target == -1) target = current;
1103 
1104  if(target == -1)
1105  {
1106  return 0;
1107  }
1108 
1109  std::lock_guard<std::mutex> guard(m_indiMutex);
1110 
1111  m_exploration_rms = target;
1112  std::cout << "New expl. rms: " << m_exploration_rms << "\n";
1113 
1114  updateIfChanged(m_indiP_explorationRms, "target", m_exploration_rms);
1115 
1116  return 0;
1117 }
1118 
1119 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_explorationSteps )(const pcf::IndiProperty &ipRecv)
1120 {
1121  if(ipRecv.getName() != m_indiP_explorationSteps.getName())
1122  {
1123  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1124  return -1;
1125  }
1126 
1127  float current = -1;
1128  float target = -1;
1129 
1130  if(ipRecv.find("current"))
1131  {
1132  current = ipRecv["current"].get<float>();
1133  }
1134 
1135  if(ipRecv.find("target"))
1136  {
1137  target = ipRecv["target"].get<float>();
1138  }
1139 
1140  if(target == -1) target = current;
1141 
1142  if(target == -1)
1143  {
1144  return 0;
1145  }
1146 
1147  std::lock_guard<std::mutex> guard(m_indiMutex);
1148 
1149  m_exploration_steps = target;
1150 
1151  updateIfChanged(m_indiP_explorationSteps, "target", m_exploration_steps);
1152 
1153  return 0;
1154 }
1155 
1156 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_lambda )(const pcf::IndiProperty &ipRecv)
1157 {
1158  if(ipRecv.getName() != m_indiP_lambda.getName()){
1159  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1160  return -1;
1161  }
1162 
1163  float current = -1;
1164  float target = -1;
1165 
1166  if(ipRecv.find("current"))
1167  current = ipRecv["current"].get<float>();
1168 
1169  if(ipRecv.find("target"))
1170  target = ipRecv["target"].get<float>();
1171 
1172  if(target == -1) target = current;
1173 
1174  if(target == -1)
1175  return 0;
1176 
1177  std::lock_guard<std::mutex> guard(m_indiMutex);
1178  if(!m_is_closed_loop){
1179  m_lambda = target;
1180  controller->set_new_regularization(m_lambda);
1181  updateIfChanged(m_indiP_lambda, "target", m_lambda);
1182  }else{
1183  log<text_log>("Lambda not changed. Loop is still running.", logPrio::LOG_NOTICE);
1184  }
1185 
1186  return 0;
1187 }
1188 
1189 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_clipval )(const pcf::IndiProperty &ipRecv)
1190 {
1191  if(ipRecv.getName() != m_indiP_clipval.getName()){
1192  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1193  return -1;
1194  }
1195 
1196  float current = -1;
1197  float target = -1;
1198 
1199  if(ipRecv.find("current"))
1200  current = ipRecv["current"].get<float>();
1201 
1202  if(ipRecv.find("target"))
1203  target = ipRecv["target"].get<float>();
1204 
1205  if(target == -1) target = current;
1206 
1207  if(target == -1)
1208  return 0;
1209 
1210  std::lock_guard<std::mutex> guard(m_indiMutex);
1211  if(!m_is_closed_loop){
1212  m_clip_val = target;
1213  updateIfChanged(m_indiP_clipval, "target", m_clip_val);
1214  }else{
1215  log<text_log>("Clip value not changed. Loop is still running.", logPrio::LOG_NOTICE);
1216  }
1217 
1218  return 0;
1219 }
1220 
1221 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_gamma )(const pcf::IndiProperty &ipRecv)
1222 {
1223  if(ipRecv.getName() != m_indiP_gamma.getName()){
1224  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1225  return -1;
1226  }
1227 
1228  float current = -1;
1229  float target = -1;
1230 
1231  if(ipRecv.find("current"))
1232  current = ipRecv["current"].get<float>();
1233 
1234  if(ipRecv.find("target"))
1235  target = ipRecv["target"].get<float>();
1236 
1237  if(target == -1) target = current;
1238 
1239  if(target == -1)
1240  return 0;
1241 
1242  std::lock_guard<std::mutex> guard(m_indiMutex);
1243 
1244  if(!m_is_closed_loop){
1245  m_gamma = target;
1246 
1247  controller->set_new_gamma(m_gamma);
1248  updateIfChanged(m_indiP_gamma, "target", m_gamma);
1249  }else{
1250  log<text_log>("Gamma value not changed. Loop is still running.", logPrio::LOG_NOTICE);
1251  }
1252 
1253  return 0;
1254 }
1255 
1256 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_intgain )(const pcf::IndiProperty &ipRecv)
1257 {
1258  if(ipRecv.getName() != m_indiP_intgain.getName()){
1259  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1260  return -1;
1261  }
1262 
1263  float current = -1;
1264  float target = -1;
1265 
1266  if(ipRecv.find("current"))
1267  current = ipRecv["current"].get<float>();
1268 
1269  if(ipRecv.find("target"))
1270  target = ipRecv["target"].get<float>();
1271 
1272  if(target == -1) target = current;
1273 
1274  if(target == -1)
1275  return 0;
1276 
1277  std::lock_guard<std::mutex> guard(m_indiMutex);
1278 
1279 
1280  m_intgain = target;
1281  controller->controller->set_integrator(m_use_predictive_control, m_intgain, m_intleak);
1282  updateIfChanged(m_indiP_intgain, "target", m_intgain);
1283 
1284  // if(!m_is_closed_loop){
1285  // }else{
1286  // log<text_log>("Integrator gain value not changed. Loop is still running.", logPrio::LOG_NOTICE);
1287  // }
1288 
1289  return 0;
1290 }
1291 
1292 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_intleak )(const pcf::IndiProperty &ipRecv)
1293 {
1294  if(ipRecv.getName() != m_indiP_intleak.getName()){
1295  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1296  return -1;
1297  }
1298 
1299  float current = -1;
1300  float target = -1;
1301 
1302  if(ipRecv.find("current"))
1303  current = ipRecv["current"].get<float>();
1304 
1305  if(ipRecv.find("target"))
1306  target = ipRecv["target"].get<float>();
1307 
1308  if(target == -1) target = current;
1309 
1310  if(target == -1)
1311  return 0;
1312 
1313  std::lock_guard<std::mutex> guard(m_indiMutex);
1314 
1315 
1316  m_intleak = target;
1317  controller->controller->set_integrator(m_use_predictive_control, m_intgain, m_intleak);
1318  updateIfChanged(m_indiP_intleak, "target", m_intleak);
1319 
1320  // if(!m_is_closed_loop){
1321  // }else{
1322  // log<text_log>("Integrator leakage value not changed. Loop is still running.", logPrio::LOG_NOTICE);
1323  // }
1324 
1325  return 0;
1326 }
1327 
1328 
1329 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_timestamp )(const pcf::IndiProperty &ipRecv)
1330 {
1331  if(ipRecv.getName() != m_indiP_timestamp.getName()){
1332  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
1333  return -1;
1334  }
1335 
1336  uint64_t current = -1;
1337  uint64_t target = -1;
1338 
1339  if(ipRecv.find("current"))
1340  current = ipRecv["current"].get<uint64_t>();
1341 
1342  if(ipRecv.find("target"))
1343  target = ipRecv["target"].get<uint64_t>();
1344 
1345  if(target == -1) target = current;
1346 
1347  if(target == -1)
1348  return 0;
1349 
1350  std::lock_guard<std::mutex> guard(m_indiMutex);
1351 
1352  loading_timestamp = target;
1353  updateIfChanged(m_indiP_timestamp, "target", loading_timestamp);
1354 
1355  return 0;
1356 }
1357 
1358 
1359 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_controlToggle )(const pcf::IndiProperty &ipRecv)
1360 {
1361  if(ipRecv.getName() != m_indiP_controlToggle.getName())
1362  {
1363  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1364  return -1;
1365  }
1366 
1367  //switch is toggled to on
1368  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1369  {
1370  if(!m_is_closed_loop) //not offloading so change
1371  {
1372  // m_woofer.setZero(); //always zero when offloading starts
1373  // log<text_log>("zeroed", logPrio::LOG_NOTICE);
1374  // m_offloading = true;
1375  m_is_closed_loop = true;
1376  log<text_log>("started closed-loop operation", logPrio::LOG_NOTICE);
1377  updateSwitchIfChanged(m_indiP_controlToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
1378 
1379  }
1380  return 0;
1381  }
1382 
1383  //switch is toggle to off
1384  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
1385  {
1386  if(m_is_closed_loop) //offloading so change it
1387  {
1388  m_is_closed_loop = false;
1389  log<text_log>("stopped closed-loop operation", logPrio::LOG_NOTICE);
1390  updateSwitchIfChanged(m_indiP_controlToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
1391  }
1392  return 0;
1393  }
1394 
1395  return 0;
1396 }
1397 
1398 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_predictorToggle )(const pcf::IndiProperty &ipRecv)
1399 {
1400  if(ipRecv.getName() != m_indiP_predictorToggle.getName())
1401  {
1402  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1403  return -1;
1404  }
1405 
1406  //switch is toggled to on
1407  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
1408  {
1409  m_use_predictive_control = true;
1410  controller->controller->set_integrator(m_use_predictive_control, m_intgain, m_intleak);
1411  log<text_log>("Switched to predictive control.", logPrio::LOG_NOTICE);
1412  updateSwitchIfChanged(m_indiP_predictorToggle, "toggle", pcf::IndiElement::On, INDI_BUSY);
1413  return 0;
1414  }
1415 
1416  //switch is toggled to off
1417  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
1418  {
1419  if(!m_is_closed_loop)
1420  {
1421  m_use_predictive_control = false;
1422  controller->controller->set_integrator(m_use_predictive_control, m_intgain, m_intleak);
1423  log<text_log>("Switched to integrator.", logPrio::LOG_NOTICE);
1424  updateSwitchIfChanged(m_indiP_predictorToggle, "toggle", pcf::IndiElement::Off, INDI_IDLE);
1425  }
1426  return 0;
1427  }
1428 
1429  return 0;
1430 }
1431 
1432 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_reset_bufferRequest )(const pcf::IndiProperty &ipRecv)
1433 {
1434  if(ipRecv.getName() != m_indiP_reset_bufferRequest.getName())
1435  {
1436  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1437  return -1;
1438  }
1439 
1440  if(!ipRecv.find("request")) return 0;
1441 
1442  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1443  {
1444  std::lock_guard<std::mutex> guard(m_indiMutex);
1445  controller->reset_data_buffer();
1446  updateSwitchIfChanged(m_indiP_reset_bufferRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1447  }
1448 
1449  return 0;
1450 }
1451 
1452 
1453 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_reset_exploreRequest )(const pcf::IndiProperty &ipRecv)
1454 {
1455  if(ipRecv.getName() != m_indiP_reset_exploreRequest.getName())
1456  {
1457  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1458  return -1;
1459  }
1460 
1461  if(!ipRecv.find("request")) return 0;
1462 
1463  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1464  {
1465  std::lock_guard<std::mutex> guard(m_indiMutex);
1466  controller->create_exploration_buffer(m_exploration_rms, m_exploration_steps);
1467  updateSwitchIfChanged(m_indiP_reset_exploreRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1468  }
1469 
1470  return 0;
1471 }
1472 
1473 
1474 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_reset_modelRequest )(const pcf::IndiProperty &ipRecv)
1475 {
1476  if(ipRecv.getName() != m_indiP_reset_modelRequest.getName())
1477  {
1478  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1479  return -1;
1480  }
1481 
1482  if(!ipRecv.find("request")) return 0;
1483 
1484  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1485  {
1486  std::lock_guard<std::mutex> guard(m_indiMutex);
1487  controller->reset_controller();
1488  updateSwitchIfChanged(m_indiP_reset_modelRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1489  }
1490 
1491  return 0;
1492 }
1493 
1494 
1495 
1496 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_reset_cleanRequest )(const pcf::IndiProperty &ipRecv)
1497 {
1498  if(ipRecv.getName() != m_indiP_reset_cleanRequest.getName())
1499  {
1500  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1501  return -1;
1502  }
1503 
1504  if(!ipRecv.find("request")) return 0;
1505 
1506  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1507  {
1508  std::lock_guard<std::mutex> guard(m_indiMutex);
1509 
1510  // Regenerate the exploration buffer
1511  controller->create_exploration_buffer(m_exploration_rms, m_exploration_steps);
1512 
1513  // Reset the controller
1514  controller->reset_controller();
1515  controller->reset_data_buffer(); // Clear the data buffer
1516 
1517  // Set the current DM shape and command to zero
1518  zero();
1519  controller->set_zero();
1520 
1521  updateSwitchIfChanged(m_indiP_reset_cleanRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1522  }
1523 
1524  return 0;
1525 }
1526 
1527 
1528 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_updateControllerRequest )(const pcf::IndiProperty &ipRecv)
1529 {
1530  if(ipRecv.getName() != m_indiP_updateControllerRequest.getName())
1531  {
1532  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1533  return -1;
1534  }
1535 
1536  if(!ipRecv.find("request")) return 0;
1537 
1538  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1539  {
1540  std::lock_guard<std::mutex> guard(m_indiMutex);
1541 
1542  // Regenerate the exploration buffer
1543  controller->update_controller();
1544  updateSwitchIfChanged(m_indiP_updateControllerRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1545  }
1546 
1547  return 0;
1548 }
1549 
1550 
1551 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_zeroRequest )(const pcf::IndiProperty &ipRecv)
1552 {
1553  if(ipRecv.getName() != m_indiP_zeroRequest.getName())
1554  {
1555  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1556  return -1;
1557  }
1558 
1559  if(!ipRecv.find("request")) return 0;
1560 
1561  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1562  {
1563 
1564  if(!m_is_closed_loop){
1565  zero();
1566  controller->set_zero();
1567  }else{
1568  m_shaped_command.setZero();
1569  }
1570 
1571  updateSwitchIfChanged(m_indiP_zeroRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1572  }
1573 
1574  return 0;
1575 }
1576 
1577 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_saveRequest )(const pcf::IndiProperty &ipRecv)
1578 {
1579  if(ipRecv.getName() != m_indiP_saveRequest.getName())
1580  {
1581  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1582  return -1;
1583  }
1584 
1585  if(!ipRecv.find("request")) return 0;
1586 
1587  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1588  {
1589  controller->save_state(savepath);
1590  updateSwitchIfChanged(m_indiP_saveRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1591  }
1592 
1593  return 0;
1594 }
1595 
1596 
1597 INDI_NEWCALLBACK_DEFN(hoPredCtrl, m_indiP_loadRequest )(const pcf::IndiProperty &ipRecv)
1598 {
1599  if(ipRecv.getName() != m_indiP_loadRequest.getName())
1600  {
1601  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
1602  return -1;
1603  }
1604 
1605  if(!ipRecv.find("request")) return 0;
1606 
1607  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1608  {
1609  controller->load_state(savepath, std::to_string(loading_timestamp));
1610  updateSwitchIfChanged(m_indiP_loadRequest, "request", pcf::IndiElement::Off, INDI_IDLE);
1611  }
1612 
1613  return 0;
1614 }
1615 
1616 } //namespace app
1617 } //namespace MagAOX
1618 
1619 #endif //hoPredCtrl_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
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:2877
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.
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
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.
bool m_getExistingFirst
If set to true by derivedT, any existing image will be grabbed and sent to processImage before waitin...
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
pcf::IndiProperty m_indiP_explorationSteps
Definition: hoPredCtrl.hpp:199
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_exploreRequest)
pcf::IndiProperty m_indiP_zeroRequest
Definition: hoPredCtrl.hpp:201
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_timestamp)
pcf::IndiProperty m_indiP_reset_exploreRequest
Definition: hoPredCtrl.hpp:200
pcf::IndiProperty m_indiP_explorationRms
Definition: hoPredCtrl.hpp:198
DDSPC::PredictiveController * controller
Definition: hoPredCtrl.hpp:154
pcf::IndiProperty m_indiP_intgain
Definition: hoPredCtrl.hpp:213
eigenImage< realT > m_mapping_matrix
Definition: hoPredCtrl.hpp:121
int processImage(void *curr_src, const dev::shmimT &dummy)
Definition: hoPredCtrl.hpp:744
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_loadRequest)
realT(* dark_pixget)(void *, size_t)
Definition: hoPredCtrl.hpp:135
pcf::IndiProperty m_indiP_learningSteps
Definition: hoPredCtrl.hpp:196
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_zeroRequest)
int m_numFut
The number of future states to predict.
Definition: hoPredCtrl.hpp:143
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_clipval)
unsigned long long iterations
Definition: hoPredCtrl.hpp:114
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_controlToggle)
pcf::IndiProperty m_indiP_lambda
Definition: hoPredCtrl.hpp:208
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_intgain)
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_updateControllerRequest)
realT m_inv_covariance
The starting point of the inverse covariance matrix.
Definition: hoPredCtrl.hpp:146
int m_numHist
The number of past states to use for the prediction.
Definition: hoPredCtrl.hpp:142
virtual int appLogic()
Implementation of the FSM for hoPredCtrl.
Definition: hoPredCtrl.hpp:515
size_t m_pwfsHeight
The height of the image.
Definition: hoPredCtrl.hpp:105
uint32_t m_dmWidth
The width of the image.
Definition: hoPredCtrl.hpp:177
virtual int appStartup()
Startup function.
Definition: hoPredCtrl.hpp:430
std::string m_pupilMaskFilename
Definition: hoPredCtrl.hpp:95
pcf::IndiProperty m_indiP_reset_cleanRequest
Definition: hoPredCtrl.hpp:192
pcf::IndiProperty m_indiP_clipval
Definition: hoPredCtrl.hpp:209
uint32_t m_dmHeight
The height of the image.
Definition: hoPredCtrl.hpp:178
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_explorationSteps)
pcf::IndiProperty m_indiP_saveRequest
Definition: hoPredCtrl.hpp:203
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_gamma)
realT m_gamma
The forgetting factore (0, 1)
Definition: hoPredCtrl.hpp:144
dev::shmimMonitor< hoPredCtrl > shmimMonitorT
Definition: hoPredCtrl.hpp:73
dev::shmimMonitor< hoPredCtrl, darkShmimT > darkMonitorT
Definition: hoPredCtrl.hpp:76
eigenImage< realT > m_measurementVector
Definition: hoPredCtrl.hpp:124
eigenImage< realT > m_shaped_command
Definition: hoPredCtrl.hpp:173
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_lambda)
std::string m_mapping_matrix_filename
Definition: hoPredCtrl.hpp:97
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_bufferRequest)
eigenImage< realT > m_darkImage
Pointer to a function to extract the image data as our desired type realT.
Definition: hoPredCtrl.hpp:131
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition: hoPredCtrl.hpp:362
realT m_lambda
The regularization parameter.
Definition: hoPredCtrl.hpp:147
unsigned long long duration
Definition: hoPredCtrl.hpp:113
virtual int appShutdown()
Shutdown the app.
Definition: hoPredCtrl.hpp:571
eigenImage< realT > m_illuminated_actuators_mask
Definition: hoPredCtrl.hpp:172
pcf::IndiProperty m_indiP_reset_modelRequest
Definition: hoPredCtrl.hpp:191
bool m_darkSet
Pointer to a function to extract the image data as our desired type realT.
Definition: hoPredCtrl.hpp:136
virtual void setupConfig()
Definition: hoPredCtrl.hpp:322
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_explorationRms)
pcf::IndiProperty m_indiP_learningIterations
Definition: hoPredCtrl.hpp:197
uint8_t m_dmDataType
The ImageStreamIO type code.
Definition: hoPredCtrl.hpp:180
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_learningSteps)
pcf::IndiProperty m_indiP_controlToggle
Definition: hoPredCtrl.hpp:188
size_t m_pwfsWidth
The width of the image.
Definition: hoPredCtrl.hpp:104
int allocate(const dev::shmimT &dummy)
Definition: hoPredCtrl.hpp:589
pcf::IndiProperty m_indiP_predictorToggle
Definition: hoPredCtrl.hpp:189
pcf::IndiProperty m_indiP_timestamp
Definition: hoPredCtrl.hpp:205
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_modelRequest)
pcf::IndiProperty m_indiP_loadRequest
Definition: hoPredCtrl.hpp:204
float realT
Floating point type in which to do all calculations.
Definition: hoPredCtrl.hpp:79
virtual void loadConfig()
Definition: hoPredCtrl.hpp:424
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_reset_cleanRequest)
eigenImage< realT > m_pupilMask
Definition: hoPredCtrl.hpp:123
std::string m_refWavefront_filename
Definition: hoPredCtrl.hpp:98
size_t m_quadWidth
The width of the image.
Definition: hoPredCtrl.hpp:107
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_learningIterations)
pcf::IndiProperty m_indiP_reset_bufferRequest
Definition: hoPredCtrl.hpp:190
pcf::IndiProperty m_indiP_intleak
Definition: hoPredCtrl.hpp:214
std::string m_interaction_matrix_filename
Definition: hoPredCtrl.hpp:96
size_t m_quadHeight
The height of the image.
Definition: hoPredCtrl.hpp:108
pcf::IndiProperty m_indiP_gamma
Definition: hoPredCtrl.hpp:210
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_intleak)
eigenImage< realT > m_refWavefront
Definition: hoPredCtrl.hpp:125
size_t m_dmTypeSize
The size of the type, in bytes.
Definition: hoPredCtrl.hpp:181
eigenImage< realT > m_interaction_matrix
Definition: hoPredCtrl.hpp:120
~hoPredCtrl() noexcept
D'tor, declared and defined for noexcept.
Definition: hoPredCtrl.hpp:260
pcf::IndiProperty m_indiP_updateControllerRequest
Definition: hoPredCtrl.hpp:194
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_saveRequest)
int set_pupil_mask(std::string pupil_mask_filename)
Definition: hoPredCtrl.hpp:876
INDI_NEWCALLBACK_DECL(hoPredCtrl, m_indiP_predictorToggle)
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_BUSY
Definition: indiUtils.hpp:30
#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
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_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
static std::string configSection()
Definition: hoPredCtrl.hpp:34
static std::string indiPrefix()
Definition: hoPredCtrl.hpp:39
Software ERR log entry.