API
userGainCtrl.hpp
Go to the documentation of this file.
1 /** \file userGainCtrl.hpp
2  * \brief The MagAO-X user gain control app
3  *
4  * \ingroup app_files
5  */
6 
7 #ifndef userGainCtrl_hpp
8 #define userGainCtrl_hpp
9 
10 #include <limits>
11 
12 #include <mx/improc/eigenCube.hpp>
13 #include <mx/improc/eigenImage.hpp>
14 #include <mx/ioutils/fits/fitsFile.hpp>
15 
16 #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17 #include "../../magaox_git_version.h"
18 
19 namespace MagAOX
20 {
21 namespace app
22 {
23 
24 uint16_t modesAtBlock( uint16_t b )
25 {
26  int16_t N = 2* (2*b+1 + 1);
27 
28  return (N+1)*(N+1) - 1;
29 }
30 
31 /// Calculate the number of modes in 1 block
32 /** A block is 2 Fourier mode indices wide. At index m, there are 2m linear degrees of freedom.
33  * This gives [(2m+1)(2m+1)-1] total Fourier modes. By considering the difference for 2m and 2(m-1) we
34  * find the number of modes in one index is 16m + 8. Note that m here starts from 1.
35  *
36  * Block number b starts from 0, and is related to m by m = 2b + 1
37  */
38 uint16_t modesInBlock( uint16_t b /**< [in] the block number */)
39 {
40  return 32*b + 24;
41 }
42 
43 /// Calculate the number of blocks and the number of modes per block
44 /** A block is 2 Fourier mode m-indices wide, going around to the m < 0 side. At index m, there are 2m linear degrees of freedom.
45  * Block number b starts from 0, and is related to m by m = 2b + 1. So for b+1 blocks, there are N = 2* (2*b+1 + 1) linear
46  * degrees of freedom, giving (N+1)*(N+1) - 1 total Fourier modes, with 32*b + 24 modes per block b.
47  *
48  * Complicating this is the usual practice of putting pure Zernike modes into the beginning of the basis. This accounts for
49  * this if desired, always splitting Tip/Tilt and Focus into separate blocks. Tip/Tilt can optionally be 2 separate blocks.
50  */
51 int blockModes( std::vector<uint16_t> & blocks, ///< [out] the block structure. The size is the number of blocks, and each entry contains the nubmer of modes in that block
52  std::vector<std::string> & names, ///< [out] the name of each block
53  uint16_t Nmodes, ///< [in] the total number of modes
54  uint16_t Nzern, ///< [in] the number of Zernikes appended at the front
55  bool splitTT ///< [in] whether or not to split tip and tilt
56  )
57 {
58  double Nblocksd = (sqrt(1.0+Nmodes) - 1)/4.;
59  int Nblocks = Nblocksd;
60 
61  if(Nblocks < Nblocksd)
62  {
63  ++Nblocks;
64  }
65 
66  blocks.clear();
67  names.clear();
68 
69  uint16_t tot = 0;
70  if(Nzern > 0)
71  {
72  if(Nzern < 2) //not enough modes for this
73  {
74  //This is dumb, whomever is doing this, you should know.
75  Nblocks += 1;
76  blocks.push_back(1);
77  names.push_back("Tip");
78  tot = 1;
79  }
80  else if(splitTT)
81  {
82  Nblocks += 2;
83  blocks.push_back(1);
84  names.push_back("Tip");
85  blocks.push_back(1);
86  names.push_back("Tilt");
87  tot = 2;
88  }
89  else
90  {
91  Nblocks += 1;
92  blocks.push_back(2);
93  names.push_back("Tip/Tilt");
94  tot = 2;
95  }
96 
97  if(Nzern > 2)
98  {
99  //Focus
100  Nblocks += 1;
101  blocks.push_back(1);
102  names.push_back("Focus");
103  ++tot;
104 
105  if(Nzern > 3)
106  {
107  Nblocks += 1;
108  blocks.push_back(Nzern - 3);
109  names.push_back("Z " + std::to_string(4)+"-" + std::to_string(Nzern));
110  tot += blocks.back();
111  }
112  }
113  }
114 
115  if(tot >= Nmodes) //Here we handle the case of Nzern >= Nmodes.
116  {
117  uint16_t sum = 0;
118  for(size_t n=0; n < blocks.size(); ++n)
119  {
120  sum += blocks[n];
121  }
122 
123  if(sum != Nmodes && sum != Nzern)
124  {
125  return -4;
126  }
127 
128  return 0;
129  }
130 
131  uint16_t currb = 0;
132 
133  while(currb < Nblocks)
134  {
135  uint16_t NAtThis = modesAtBlock(currb);
136 
137  if(NAtThis <= tot) //Still in the Zernikes at the beginning
138  {
139  //--Nblocks;
140  ++currb;
141  continue;
142  }
143 
144  uint16_t Nthis = NAtThis - tot;
145 
146  if(tot + Nthis > Nmodes)
147  {
148  Nthis = Nmodes - tot;
149  }
150 
151  if(Nthis == 0)
152  {
153  break;
154  }
155 
156  blocks.push_back(Nthis);
157  names.push_back("Block " + std::to_string(blocks.size()-1));
158  tot += Nthis;
159  ++currb;
160  }
161 
162  uint16_t sum = 0;
163  for(size_t n=0; n < blocks.size(); ++n)
164  {
165  sum += blocks[n];
166  }
167 
168  if(sum != Nmodes)
169  {
170  return -3;
171  }
172 
173  return 0;
174 
175 }
176 
177 struct gainShmimT
178 {
179  static std::string configSection()
180  {
181  return "gainShmim";
182  };
183 
184  static std::string indiPrefix()
185  {
186  return "gainShmim";
187  };
188 };
189 
191 {
192  static std::string configSection()
193  {
194  return "multcoeffShmim";
195  };
196 
197  static std::string indiPrefix()
198  {
199  return "multcoeffShmim";
200  };
201 };
202 
203 struct limitShmimT
204 {
205  static std::string configSection()
206  {
207  return "limitShmim";
208  };
209 
210  static std::string indiPrefix()
211  {
212  return "limitShmim";
213  };
214 };
215 
216 /** \defgroup userGainCtrl User Interface to Cacao Gains
217  * \brief Tracks the cacao gain factor vector and updates upon request, using blocks.
218  *
219  * <a href="../handbook/operating/software/apps/userGainCtrl.html">Application Documentation</a>
220  *
221  * \ingroup apps
222  *
223  */
224 
225 /** \defgroup userGainCtrl_files User Gain Control
226  * \ingroup userGainCtrl
227  */
228 
229 /** MagAO-X application to provide a user interface to cacao gains
230  *
231  * \ingroup userGainCtrl
232  *
233  */
234 class userGainCtrl : public MagAOXApp<true>, public dev::shmimMonitor<userGainCtrl,gainShmimT>,
235  public dev::shmimMonitor<userGainCtrl,multcoeffShmimT>, public dev::shmimMonitor<userGainCtrl,limitShmimT>,
236  public dev::telemeter<userGainCtrl>
237 {
238 
239  //Give the test harness access.
240  friend class userGainCtrl_test;
241 
245 
247 
248  friend class dev::telemeter<userGainCtrl>;
249 
250 public:
251 
252  //The base shmimMonitor type
256 
257  ///Floating point type in which to do all calculations.
258  typedef float realT;
259 
260 protected:
261 
262  /** \name Configurable Parameters
263  *@{
264  */
265  int m_loopNumber {-1};
266  int m_nZern {0};
267  bool m_splitTT {false};
268 
269  ///@}
270 
271  mx::improc::eigenImage<realT> m_gainsCurrent; ///< The current gains.
272  mx::improc::eigenImage<realT> m_gainsTarget; ///< The target gains.
273 
274  realT (*pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
275 
276  mx::improc::eigenImage<realT> m_mcsCurrent; ///< The current gains.
277  mx::improc::eigenImage<realT> m_mcsTarget; ///< The target gains.
278 
279  realT (*mc_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
280 
281  mx::improc::eigenImage<realT> m_limitsCurrent; ///< The current gains.
282  mx::improc::eigenImage<realT> m_limitsTarget; ///< The target gains.
283 
284  realT (*limit_pixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as our desired type realT.
285 
286  std::vector<uint16_t> m_modeBlockStart;
287  std::vector<uint16_t> m_modeBlockN;
288  std::vector<std::string> m_modeBlockNames;
289 
290  int m_totalNModes {0}; ///< The total number of WFS modes in the calib.
291 
292  std::vector<float> m_modeBlockGains;
293  std::vector<uint8_t> m_modeBlockGainsConstant;
294 
295  std::vector<float> m_modeBlockMCs;
296  std::vector<uint8_t> m_modeBlockMCsConstant;
297 
298  std::vector<float> m_modeBlockLims;
299  std::vector<uint8_t> m_modeBlockLimsConstant;
300 
301  std::mutex m_modeBlockMutex;
302 
303  mx::fits::fitsFile<float> m_ff;
304 
305  int m_singleModeNo {0};
306 
307  float m_powerLawIndex {2};
308  float m_powerLawFloor {0.05};
309 
310 public:
311  /// Default c'tor.
312  userGainCtrl();
313 
314  /// D'tor, declared and defined for noexcept.
315  ~userGainCtrl() noexcept
316  {}
317 
318  virtual void setupConfig();
319 
320  /// Implementation of loadConfig logic, separated for testing.
321  /** This is called by loadConfig().
322  */
323  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
324 
325  virtual void loadConfig();
326 
327  /// Startup function
328  /**
329  *
330  */
331  virtual int appStartup();
332 
333  /// Implementation of the FSM for userGainCtrl.
334  /**
335  * \returns 0 on no critical error
336  * \returns -1 on an error requiring shutdown
337  */
338  virtual int appLogic();
339 
340  /// Shutdown the app.
341  /**
342  *
343  */
344  virtual int appShutdown();
345 
346 protected:
347 
348  //int checkAOCalib(); ///< Test if the AO calib is accessible.
349 
350  //int getAOCalib();
351 
352  int getModeBlocks();
353 
354  int allocate( const gainShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
355 
356  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
357  const gainShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
358  );
359 
360  int writeGains();
361 
362  int setBlockGain( int n,
363  float g
364  );
365 
366  int allocate( const multcoeffShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
367 
368  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
369  const multcoeffShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
370  );
371 
372  int writeMCs();
373 
374  int setBlockMC( int n,
375  float mc
376  );
377 
378  int allocate( const limitShmimT & dummy /**< [in] tag to differentiate shmimMonitor parents.*/);
379 
380  int processImage( void * curr_src, ///< [in] pointer to start of current frame.
381  const limitShmimT & dummy ///< [in] tag to differentiate shmimMonitor parents.
382  );
383 
384  int writeLimits();
385 
386  int setBlockLimit( int n,
387  float l
388  );
389 
390  int setSingleModeNo (int m);
391 
392  int setSingleGain( float g );
393 
394  int setSingleMC( float mc );
395 
396  void updateSingles();
397 
398  void powerLawIndex( float pli );
399 
400  void powerLawFloor( float plf );
401 
402  void powerLawSet();
403 
404  pcf::IndiProperty m_indiP_modes;
405 
406  pcf::IndiProperty m_indiP_zeroAll;
407 
408  std::vector<pcf::IndiProperty> m_indiP_blockGains;
409  std::vector<pcf::IndiProperty> m_indiP_blockMCs;
410  std::vector<pcf::IndiProperty> m_indiP_blockLimits;
411 
412  pcf::IndiProperty m_indiP_singleModeNo;
413  pcf::IndiProperty m_indiP_singleGain;
414  pcf::IndiProperty m_indiP_singleMC;
415 
416  pcf::IndiProperty m_indiP_powerLawIndex;
417  pcf::IndiProperty m_indiP_powerLawFloor;
418  pcf::IndiProperty m_indiP_powerLawSet;
419 
420 public:
421 
423 
425 
427 
429 
433 
434  /// The static callback function to be registered for block gains
435  /** Dispatches to the relevant handler
436  *
437  * \returns 0 on success.
438  * \returns -1 on error.
439  */
440  static int st_newCallBack_blockGains( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
441  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
442  );
443 
444  /// Callback to process a NEW block gain request
445  /**
446  * \returns 0 on success.
447  * \returns -1 on error.
448  */
449  int newCallBack_blockGains( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
450 
451  /// The static callback function to be registered for block mult. coeff.s
452  /** Dispatches to the relevant handler
453  *
454  * \returns 0 on success.
455  * \returns -1 on error.
456  */
457  static int st_newCallBack_blockMCs( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
458  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
459  );
460 
461  /// Callback to process a NEW block mult. coeff.s
462  /**
463  * \returns 0 on success.
464  * \returns -1 on error.
465  */
466  int newCallBack_blockMCs( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
467 
468  /// The static callback function to be registered for block limits
469  /** Dispatches to the relevant handler
470  *
471  * \returns 0 on success.
472  * \returns -1 on error.
473  */
474  static int st_newCallBack_blockLimits( void * app, ///< [in] a pointer to this, will be static_cast-ed to derivedT.
475  const pcf::IndiProperty &ipRecv ///< [in] the INDI property sent with the the new property request.
476  );
477 
478  /// Callback to process a NEW block limits
479  /**
480  * \returns 0 on success.
481  * \returns -1 on error.
482  */
483  int newCallBack_blockLimits( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with the the new property request.*/);
484 
485  /** \name Telemeter Interface
486  *
487  * @{
488  */
489  int checkRecordTimes();
490 
491  int recordTelem( const telem_blockgains * );
492 
493  int recordBlockGains( bool force = false );
494 
495  ///@}
496 };
497 
498 inline
499 userGainCtrl::userGainCtrl() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
500 {
501 
505 
506  return;
507 }
508 
509 inline
511 {
515 
516  config.add("loop.number", "", "loop.number", argType::Required, "loop", "number", false, "int", "The loop number");
517  config.add("blocks.splitTT", "", "blocks.splitTT", argType::Required, "blocks", "splitTT", false, "bool", "If true, the first block is split into two modes.");
518  config.add("blocks.nZern", "", "blocks.nZern", argType::Required, "blocks", "nZern", false, "int", "Number of Zernikes at beginning. T/T and F are split, the rest in their own block.");
519 
520  telemeterT::setupConfig(config);
521 }
522 
523 inline
524 int userGainCtrl::loadConfigImpl( mx::app::appConfigurator & _config )
525 {
526  _config(m_loopNumber, "loop.number");
527  _config(m_splitTT, "blocks.splitTT");
528  _config(m_nZern, "blocks.nZern");
529 
530  shmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mgainfact";
532 
533  mcShmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mmultfact";
535 
536  limitShmimMonitorT::m_shmimName = "aol" + std::to_string(m_loopNumber) + "_mlimitfact";
538 
539  if(telemeterT::loadConfig(_config) < 0)
540  {
541  log<text_log>("Error during telemeter config", logPrio::LOG_CRITICAL);
542  m_shutdown = true;
543  }
544 
545  return 0;
546 }
547 
548 inline
550 {
551  loadConfigImpl(config);
552 }
553 
554 inline
556 {
557  createROIndiNumber( m_indiP_modes, "modes", "Loop Modes", "Loop Controls");
558  indi::addNumberElement(m_indiP_modes, "total", 0, 1, 25000, "Total Modes");
559  indi::addNumberElement(m_indiP_modes, "blocks", 0, 1, 99, "Mode Blocks");
561 
562 
565  {
566  log<software_error>({__FILE__,__LINE__});
567  return -1;
568  }
569 
570  createStandardIndiNumber<int>( m_indiP_singleModeNo, "singleModeNo", 0, 2400 ,0, "%0d", "");
571  m_indiP_singleModeNo["current"].set(m_singleModeNo);
572  m_indiP_singleModeNo["target"].set(m_singleModeNo);
574 
575  createStandardIndiNumber<int>( m_indiP_singleGain, "singleGain", 0, 1.5 ,0, "%0.2f", "");
576  m_indiP_singleGain["current"].set(1);
577  m_indiP_singleGain["target"].set(1);
579 
580  createStandardIndiNumber<int>( m_indiP_singleMC, "singleMC", 0, 1.0 ,0, "%0.2f", "");
581  m_indiP_singleMC["current"].set(1);
582  m_indiP_singleMC["target"].set(1);
584 
585  createStandardIndiNumber<int>( m_indiP_powerLawIndex, "pwrlaw_index", 0, 10.0 ,0, "%0.2f", "");
586  m_indiP_powerLawIndex["current"].set(m_powerLawIndex);
587  m_indiP_powerLawIndex["target"].set(m_powerLawIndex);
589 
590  createStandardIndiNumber<int>( m_indiP_powerLawFloor, "pwrlaw_floor", 0, 1.0 ,0, "%0.2f", "");
591  m_indiP_powerLawFloor["current"].set(m_powerLawFloor);
592  m_indiP_powerLawFloor["target"].set(m_powerLawFloor);
594 
597  {
598  log<software_error>({__FILE__,__LINE__});
599  return -1;
600  }
601 
602  if(shmimMonitorT::appStartup() < 0)
603  {
604  return log<software_error,-1>({__FILE__, __LINE__});
605  }
606 
608  {
609  return log<software_error,-1>({__FILE__, __LINE__});
610  }
611 
613  {
614  return log<software_error,-1>({__FILE__, __LINE__});
615  }
616 
617  if(telemeterT::appStartup() < 0)
618  {
619  return log<software_error,-1>({__FILE__,__LINE__});
620  }
621 
623 
624  return 0;
625 }
626 
627 inline
629 {
630  if( shmimMonitorT::appLogic() < 0)
631  {
632  return log<software_error,-1>({__FILE__,__LINE__});
633  }
634 
635  if( mcShmimMonitorT::appLogic() < 0)
636  {
637  return log<software_error,-1>({__FILE__,__LINE__});
638  }
639 
641  {
642  return log<software_error,-1>({__FILE__,__LINE__});
643  }
644 
647  {
650  if(state() == stateCodes::READY) state(stateCodes::OPERATING); //we just progress all the way through to operating so shmimMonitor will go.
651  }
652 
654  {
655  if(telemeterT::appLogic() < 0)
656  {
657  log<software_error>({__FILE__, __LINE__});
658  return 0;
659  }
660  }
661 
662  std::unique_lock<std::mutex> lock(m_indiMutex);
663 
664  if(shmimMonitorT::updateINDI() < 0)
665  {
666  log<software_error>({__FILE__, __LINE__});
667  }
668 
670  {
671  log<software_error>({__FILE__, __LINE__});
672  }
673 
675  {
676  log<software_error>({__FILE__, __LINE__});
677  }
678 
679  for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
680  {
682  }
683 
684  for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
685  {
687  }
688 
689  for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
690  {
692  }
693 
694  updateSingles();
695 
696  return 0;
697 }
698 
699 inline
701 {
705 
707 
708  return 0;
709 }
710 
711 
712 inline
714 {
716 
717  uint16_t Nb = m_modeBlockN.size();
718 
719  m_modeBlockStart.resize(m_modeBlockN.size());
720  m_modeBlockStart[0] = 0;
721  for(size_t n = 1; n < m_modeBlockN.size(); ++n)
722  {
724  }
725 
726  log<text_log>("loading new gain block structure");
727 
728  m_modeBlockGains.resize(Nb);
729  m_modeBlockGainsConstant.resize(Nb);
730 
731  m_modeBlockMCs.resize(Nb);
732  m_modeBlockMCsConstant.resize(Nb);
733 
734  m_modeBlockLims.resize(Nb);
735  m_modeBlockLimsConstant.resize(Nb);
736 
737  //-- modify INDI vars --
738  std::unique_lock<std::mutex> indilock(m_indiMutex);
739 
741  m_indiP_modes["blocks"] = m_modeBlockStart.size();
742 
743  //First just delete all existing blockXX elements
744  for(int n=0; n < 100; ++n)
745  {
746  char str[16];
747  snprintf(str, sizeof(str), "%02d", n);
748  std::string en = "block";
749  en += str;
750 
751  if(m_indiP_modes.find(en)) m_indiP_modes.remove(en);
752  }
753 
754  //Erase existing block gains
755  if(m_indiP_blockGains.size() > 0)
756  {
757  for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
758  {
759  if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockGains[n]);
760  if(!m_indiNewCallBacks.erase(m_indiP_blockGains[n].createUniqueKey()))
761  {
762  log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockGains[n].createUniqueKey()});
763  }
764  }
765  }
766  m_indiP_blockGains.clear();
767 
768  //Erase existing block mult. coeffs
769  if(m_indiP_blockMCs.size() > 0)
770  {
771  for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
772  {
773  if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockMCs[n]);
774  if(!m_indiNewCallBacks.erase(m_indiP_blockMCs[n].createUniqueKey()))
775  {
776  log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockMCs[n].createUniqueKey()});
777  }
778  }
779  }
780  m_indiP_blockMCs.clear();
781 
782  //Erase existing block limits
783  if(m_indiP_blockLimits.size() > 0)
784  {
785  for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
786  {
787  if(m_indiDriver) m_indiDriver->sendDelProperty(m_indiP_blockLimits[n]);
788  if(!m_indiNewCallBacks.erase(m_indiP_blockLimits[n].createUniqueKey()))
789  {
790  log<software_error>({__FILE__, __LINE__, "failed to erase " + m_indiP_blockLimits[n].createUniqueKey()});
791  }
792  }
793  }
794  m_indiP_blockLimits.clear();
795 
796  m_indiP_blockGains.resize(Nb);
797  m_indiP_blockMCs.resize(Nb);
798  m_indiP_blockLimits.resize(Nb);
799 
800  //Then add in what we want.
801  for(size_t n=0; n < Nb; ++n)
802  {
803  char str[16];
804  int nn = n;
805  snprintf(str, sizeof(str), "%02d", nn);
806  std::string en = "block";
807  en += str;
808  indi::addNumberElement(m_indiP_modes, en, 0, 1, 99, "Block " + std::to_string(nn));
809  m_indiP_modes[en] = m_modeBlockN[n];
810 
811  createStandardIndiNumber<float>( m_indiP_blockGains[n], en + "_gain", 0.0, 10.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Gain", "Loop Controls");
813  if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockGains[n]);
814 
815  createStandardIndiNumber<float>( m_indiP_blockMCs[n], en + "_multcoeff", 0.0, 1.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Mult. Coeff", "Loop Controls");
817  if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockMCs[n]);
818 
819  createStandardIndiNumber<float>( m_indiP_blockLimits[n], en + "_limit", 0.0, 100.0, 0.01, "%0.3f", m_modeBlockNames[n] + " Limit", "Loop Controls");
821  if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_blockLimits[n]);
822  }
823 
824  if(m_indiDriver) m_indiDriver->sendSetProperty (m_indiP_modes); //might not exist yet!
825 
826  return 0;
827 }
828 
829 inline
831 {
832  static_cast<void>(dummy); //be unused
833 
834  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
835 
838 
839  getModeBlocks();
840 
841  pixget = getPixPointer<realT>(shmimMonitorT::m_dataType);
842 
843  return 0;
844 }
845 
846 inline
847 int userGainCtrl::processImage( void * curr_src,
848  const gainShmimT & dummy
849  )
850 {
851  static_cast<void>(dummy); //be unused
852 
854 
855  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
856 
857  realT * data = m_gainsCurrent.data();
858 
859  for(unsigned nn=0; nn < shmimMonitorT::m_width*shmimMonitorT::m_height; ++nn)
860  {
861  data[nn] = pixget(curr_src, nn);
862  }
863 
864  //update blocks here.
865 
866  for(size_t n =0; n < m_modeBlockStart.size(); ++n)
867  {
868  double mng = 0;
869 
870  int NN = 0;
871 
872  for(int m =0; m < m_modeBlockN[n]; ++m)
873  {
874  if(m_modeBlockStart[n] + m >= m_gainsCurrent.rows()) break;
875  mng += m_gainsCurrent(m_modeBlockStart[n] + m,0);
876  ++NN;
877  }
878 
879  m_modeBlockGains[n] = mng / NN;
880 
881  bool constant = true;
882 
883  for(int m =0; m < m_modeBlockN[n]; ++m)
884  {
885  if(m_modeBlockStart[n] + m >= m_gainsCurrent.rows()) break;
887  {
888  constant = false;
889  break;
890  }
891  }
892 
893  m_modeBlockGainsConstant[n] = constant;
894 
895  }
896 
897  for(size_t n=0; n < m_indiP_blockGains.size(); ++n)
898  {
900  }
901 
902  lock.unlock();
903 
905 
906  return 0;
907 }
908 
909 inline
911 {
912  shmimMonitorT::m_imageStream.md->write=1;
913  char * dest = static_cast<char *>(shmimMonitorT::m_imageStream.array.raw);
914 
916 
917  //Set the time of last write
918  clock_gettime(CLOCK_REALTIME, &shmimMonitorT::m_imageStream.md->writetime);
919 
920  //Set the image acquisition timestamp
922 
923  //Update cnt0
924  shmimMonitorT::m_imageStream.md->cnt0++;
925 
926  //And post
927  shmimMonitorT::m_imageStream.md->write=0;
928  ImageStreamIO_sempost(&(shmimMonitorT::m_imageStream),-1);
929 
930  return 0;
931 }
932 
934  float g
935  )
936 {
937  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
938 
940 
941  //Apply a delta to each mode in the block
942  //to preserve intra-block differences
943  for(int m =0; m < m_modeBlockN[n]; ++m)
944  {
945  if(m_modeBlockStart[n] + m > m_gainsTarget.rows() -1) break;
947  }
948  //lock.unlock();
949  recordBlockGains(true);
950  writeGains();
951  return 0;
952 }
953 
954 inline
956 {
957  static_cast<void>(dummy); //be unused
958 
959  int n = 0;
960 
961  while( mcShmimMonitorT::m_width != shmimMonitorT::m_width && n < 100 )
962  {
963  mx::sys::milliSleep(100);
964  ++n;
965  }
966 
968  {
969  return -1;
970  }
971 
972  std::unique_lock<std::mutex> lock(m_indiMutex);
973 
976 
977  mc_pixget = getPixPointer<realT>(mcShmimMonitorT::m_dataType);
978 
979  return 0;
980 }
981 
982 inline
983 int userGainCtrl::processImage( void * curr_src,
984  const multcoeffShmimT & dummy
985  )
986 {
987  static_cast<void>(dummy); //be unused
988 
990 
991  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
992 
993  realT * data = m_mcsCurrent.data();
994 
995  for(unsigned nn=0; nn < mcShmimMonitorT::m_width*mcShmimMonitorT::m_height; ++nn)
996  {
997  data[nn] = mc_pixget(curr_src, nn);
998  }
999 
1000  //update blocks here.
1001 
1002  for(size_t n =0; n < m_modeBlockStart.size(); ++n)
1003  {
1004  double mng = 0;
1005 
1006  int NN = 0;
1007  for(int m =0; m < m_modeBlockN[n]; ++m)
1008  {
1009  if(m_modeBlockStart[n] + m >= m_mcsCurrent.rows()) break;
1010  mng += m_mcsCurrent(m_modeBlockStart[n] + m,0);
1011  ++NN;
1012  }
1013 
1014  m_modeBlockMCs[n] = mng / NN;
1015 
1016 
1017  bool constant = true;
1018 
1019  for(int m =0; m < m_modeBlockN[n]; ++m)
1020  {
1021  if(m_modeBlockStart[n] + m >= m_mcsCurrent.rows()) break;
1022  if(m_mcsCurrent(m_modeBlockStart[n] + m,0) != m_modeBlockMCs[n])
1023  {
1024  constant = false;
1025  break;
1026  }
1027  }
1028 
1029  m_modeBlockMCsConstant[n] = constant;
1030  }
1031 
1032  for(size_t n=0; n < m_indiP_blockMCs.size(); ++n)
1033  {
1034  updateIfChanged(m_indiP_blockMCs[n], "current", m_modeBlockMCs[n]);
1035  }
1036 
1037  lock.unlock();
1038 
1039  recordBlockGains();
1040 
1041  return 0;
1042 }
1043 
1044 inline
1046 {
1047  mcShmimMonitorT::m_imageStream.md->write=1;
1048  char * dest = static_cast<char *>(mcShmimMonitorT::m_imageStream.array.raw);
1049 
1051 
1052  //Set the time of last write
1053  clock_gettime(CLOCK_REALTIME, &mcShmimMonitorT::m_imageStream.md->writetime);
1054 
1055  //Set the image acquisition timestamp
1057 
1058  //Update cnt0
1059  mcShmimMonitorT::m_imageStream.md->cnt0++;
1060 
1061  //And post
1062  mcShmimMonitorT::m_imageStream.md->write=0;
1063  ImageStreamIO_sempost(&(mcShmimMonitorT::m_imageStream),-1);
1064 
1065  return 0;
1066 }
1067 
1069  float mc
1070  )
1071 {
1072  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
1073 
1075 
1076  //Apply a delta to each mode in the block
1077  //to preserve intra-block differences
1078  for(int m =0; m < m_modeBlockN[n]; ++m)
1079  {
1080  if(m_modeBlockStart[n] + m > m_mcsTarget.rows() -1) break;
1081  m_mcsTarget(m_modeBlockStart[n] + m,0) = m_mcsCurrent(m_modeBlockStart[n] + m,0) + (mc- m_modeBlockMCs[n]);
1082  }
1083  lock.unlock();
1084  recordBlockGains(true);
1085  writeMCs();
1086  return 0;
1087 }
1088 
1089 inline
1091 {
1092  static_cast<void>(dummy); //be unused
1093 
1094  int n = 0;
1095 
1097  {
1098  mx::sys::milliSleep(100);
1099  ++n;
1100  }
1101 
1103  {
1104  return -1;
1105  }
1106 
1107  std::unique_lock<std::mutex> lock(m_indiMutex);
1108 
1111 
1112  limit_pixget = getPixPointer<realT>(limitShmimMonitorT::m_dataType);
1113 
1114  return 0;
1115 }
1116 
1117 inline
1118 int userGainCtrl::processImage( void * curr_src,
1119  const limitShmimT & dummy
1120  )
1121 {
1122  static_cast<void>(dummy); //be unused
1123 
1124  recordBlockGains();
1125 
1126  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
1127 
1128  realT * data = m_limitsCurrent.data();
1129 
1130  for(unsigned nn=0; nn < limitShmimMonitorT::m_width*limitShmimMonitorT::m_height; ++nn)
1131  {
1132  data[nn] = limit_pixget(curr_src, nn);
1133  }
1134 
1135  //update blocks here.
1136 
1137  for(size_t n =0; n < m_modeBlockStart.size(); ++n)
1138  {
1139  double mng = 0;
1140 
1141  int NN = 0;
1142  for(int m =0; m < m_modeBlockN[n]; ++m)
1143  {
1144  if(m_modeBlockStart[n] + m >= m_limitsCurrent.rows()) break;
1145  mng += m_limitsCurrent(m_modeBlockStart[n] + m,0);
1146  ++NN;
1147  }
1148 
1149  m_modeBlockLims[n] = mng / NN;
1150 
1151  bool constant = true;
1152 
1153  for(int m =0; m < m_modeBlockN[n]; ++m)
1154  {
1155  if(m_modeBlockStart[n] + m >= m_limitsCurrent.rows()) break;
1157  {
1158  constant = false;
1159  break;
1160  }
1161  }
1162 
1163  m_modeBlockLimsConstant[n] = constant;
1164  }
1165 
1166  for(size_t n=0; n < m_indiP_blockLimits.size(); ++n)
1167  {
1169  }
1170 
1171  lock.unlock();
1172 
1173  recordBlockGains();
1174 
1175  return 0;
1176 }
1177 
1178 inline
1180 {
1182  char * dest = static_cast<char *>(limitShmimMonitorT::m_imageStream.array.raw);// + next_cnt1*m_width*m_height*m_typeSize;
1183 
1185 
1186  //Set the time of last write
1187  clock_gettime(CLOCK_REALTIME, &limitShmimMonitorT::m_imageStream.md->writetime);
1188 
1189  //Set the image acquisition timestamp
1191 
1192  //Update cnt0
1194 
1195  //And post
1197  ImageStreamIO_sempost(&(limitShmimMonitorT::m_imageStream),-1);
1198 
1199  return 0;
1200 }
1201 
1203  float l
1204  )
1205 {
1206  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
1207 
1209 
1210  //Apply a delta to each mode in the block
1211  //to preserve intra-block differences
1212  for(int m =0; m < m_modeBlockN[n]; ++m)
1213  {
1214  if(m_modeBlockStart[n] + m > m_limitsTarget.rows() -1) break;
1216  }
1217  lock.unlock();
1218  recordBlockGains(true);
1219  writeLimits();
1220 
1221  return 0;
1222 }
1223 
1225 {
1226  m_singleModeNo = m;
1227 
1228  updateIfChanged(m_indiP_singleModeNo, "current", m);
1229 
1230  if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return -1;
1231  float g = m_gainsCurrent (m_singleModeNo,0);
1232 
1233  updateIfChanged(m_indiP_singleGain, std::vector<std::string>({"current", "target"}), std::vector<float>({g,g}));
1234 
1235  if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return -1;
1236  float mc = m_mcsCurrent(m_singleModeNo,0);
1237 
1238  updateIfChanged(m_indiP_singleMC, std::vector<std::string>({"current", "target"}), std::vector<float>({mc,mc}));
1239 
1240  return 0;
1241 }
1242 
1244 {
1245  if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return -1;
1246  recordBlockGains();
1247  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
1249  lock.unlock();
1250  recordBlockGains(true);
1251  writeGains();
1252  return 0;
1253 }
1254 
1256 {
1257  if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return -1;
1258  recordBlockGains();
1259  std::unique_lock<std::mutex> lock(m_modeBlockMutex);
1260  m_mcsTarget(m_singleModeNo,0) = mc;
1261  lock.unlock();
1262  recordBlockGains(true);
1263  writeMCs();
1264  return 0;
1265 }
1266 
1268 {
1269  if(m_singleModeNo < 0 || m_singleModeNo >= m_gainsCurrent.rows()) return;
1270  float g = m_gainsCurrent (m_singleModeNo,0);
1271 
1272  updateIfChanged(m_indiP_singleGain, std::vector<std::string>({"current", "target"}), std::vector<float>({g,g}));
1273 
1274  if(m_singleModeNo < 0 || m_singleModeNo >= m_mcsCurrent.rows()) return;
1275  float mc = m_mcsCurrent(m_singleModeNo,0);
1276 
1277  updateIfChanged(m_indiP_singleMC, std::vector<std::string>({"current", "target"}), std::vector<float>({mc,mc}));
1278 
1279 }
1280 
1282 {
1283  if(pli < 0)
1284  {
1285  pli = 0;
1286  }
1287 
1288  m_powerLawIndex = pli;
1289  updateIfChanged(m_indiP_powerLawIndex, std::vector<std::string>({"current", "target"}), std::vector<float>({pli,pli}));
1290 }
1291 
1293 {
1294  m_powerLawFloor = plf;
1295 
1296  updateIfChanged(m_indiP_powerLawFloor, std::vector<std::string>({"current", "target"}), std::vector<float>({plf,plf}));
1297 }
1298 
1300 {
1301  uint16_t block0 = 0;
1302 
1303  if(m_nZern > 0)
1304  {
1305  if(m_nZern > 1)
1306  {
1307  if(m_splitTT)
1308  {
1309  block0 = 2;
1310  }
1311  else
1312  {
1313  block0 = 1;
1314  }
1315  }
1316 
1317  if(m_nZern > 2)
1318  {
1319  ++block0;
1320  }
1321 
1322  if(m_nZern > 3)
1323  {
1324  ++block0;
1325  }
1326  //Now have accounted for T/T and focus.
1327 
1328  uint16_t currb = 1;
1329  while(modesAtBlock(currb) < m_nZern)
1330  {
1331  ++currb;
1332  ++block0;
1333  }
1334  }
1335 
1336  if(block0 >= m_modeBlockStart.size())
1337  {
1338  return;
1339  }
1340 
1341  if(m_powerLawIndex < 0)
1342  {
1343  m_powerLawIndex = 0;
1344  }
1345 
1346  float mode0 = m_modeBlockStart[block0] + 0.5*m_modeBlockN[block0];
1347  float gain0 = m_modeBlockGains[block0];
1348  for(size_t n=block0+1; n < m_modeBlockStart.size(); ++n)
1349  {
1350  float mode = m_modeBlockStart[n] + 0.5*m_modeBlockN[n];
1351 
1352  float imd1=(mode-mode0)/(m_totalNModes-mode0);
1353  float imd2=pow(1.0-imd1, -m_powerLawIndex) * gain0;
1354  float gain=(1-m_powerLawFloor)*imd2+m_powerLawFloor;
1355 
1356  if(gain < 0) gain = 0;
1357 
1358  setBlockGain(n, gain);
1359 
1360  //Now wait on the update, otherwise the next command can overwrite from m_gainsCurrent
1361  int nt = 0;
1362  while(fabs(m_modeBlockGains[n] - gain) > 1e-5 && nt < 100)
1363  {
1364  mx::sys::milliSleep(5);
1365  ++nt;
1366  }
1367  }
1368 
1369  log<text_log>("Set power law: " + std::to_string(m_powerLawIndex) + " " + std::to_string(m_powerLawFloor) +
1370  " starting from block " + std::to_string(block0) + " " + std::to_string(gain0));
1371 
1372 
1373 }
1374 
1375 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_zeroAll)(const pcf::IndiProperty &ipRecv)
1376 {
1377  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_zeroAll, ipRecv);
1378 
1379  if(!ipRecv.find("request")) return 0;
1380 
1381  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1382  {
1383  std::unique_lock<std::mutex> lock(m_indiMutex);
1384 
1385  std::cerr << "Got zero all\n";
1386  m_gainsTarget.setZero();
1387  writeGains();
1388 
1389  updateSwitchIfChanged(m_indiP_zeroAll, "request", pcf::IndiElement::Off, INDI_IDLE);
1390  }
1391 
1392  return 0;
1393 }
1394 
1395 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleModeNo)(const pcf::IndiProperty &ipRecv)
1396 {
1397  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleModeNo, ipRecv);
1398 
1399  int target;
1400 
1401  if( indiTargetUpdate( m_indiP_singleModeNo, target, ipRecv, true) < 0)
1402  {
1403  log<software_error>({__FILE__,__LINE__});
1404  return -1;
1405  }
1406 
1407  setSingleModeNo(target);
1408 
1409  return 0;
1410 }
1411 
1412 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleGain)(const pcf::IndiProperty &ipRecv)
1413 {
1414  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleGain, ipRecv);
1415 
1416  float target;
1417 
1418  if( indiTargetUpdate( m_indiP_singleGain, target, ipRecv, true) < 0)
1419  {
1420  log<software_error>({__FILE__,__LINE__});
1421  return -1;
1422  }
1423 
1424  setSingleGain(target);
1425 
1426  return 0;
1427 }
1428 
1429 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_singleMC)(const pcf::IndiProperty &ipRecv)
1430 {
1431  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_singleMC, ipRecv);
1432 
1433  float target;
1434 
1435  if( indiTargetUpdate( m_indiP_singleMC, target, ipRecv, true) < 0)
1436  {
1437  log<software_error>({__FILE__,__LINE__});
1438  return -1;
1439  }
1440 
1441  setSingleMC(target);
1442 
1443  return 0;
1444 }
1445 
1446 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawIndex)(const pcf::IndiProperty &ipRecv)
1447 {
1448  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawIndex, ipRecv);
1449 
1450  float target;
1451 
1452  if( indiTargetUpdate( m_indiP_powerLawIndex, target, ipRecv, true) < 0)
1453  {
1454  log<software_error>({__FILE__,__LINE__});
1455  return -1;
1456  }
1457 
1458  powerLawIndex(target);
1459 
1460  return 0;
1461 }
1462 
1463 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawFloor)(const pcf::IndiProperty &ipRecv)
1464 {
1465  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawFloor, ipRecv);
1466 
1467  float target;
1468 
1469  if( indiTargetUpdate( m_indiP_powerLawFloor, target, ipRecv, true) < 0)
1470  {
1471  log<software_error>({__FILE__,__LINE__});
1472  return -1;
1473  }
1474 
1475  powerLawFloor(target);
1476 
1477  return 0;
1478 }
1479 
1480 INDI_NEWCALLBACK_DEFN(userGainCtrl, m_indiP_powerLawSet)(const pcf::IndiProperty &ipRecv)
1481 {
1482  INDI_VALIDATE_CALLBACK_PROPS(m_indiP_powerLawSet, ipRecv);
1483 
1484  if(!ipRecv.find("request")) return 0;
1485 
1486  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
1487  {
1488  std::unique_lock<std::mutex> lock(m_indiMutex);
1489 
1490  std::cerr << "Got Power Law\n";
1491  powerLawSet();
1492 
1493  updateSwitchIfChanged(m_indiP_powerLawSet, "request", pcf::IndiElement::Off, INDI_IDLE);
1494  }
1495 
1496  return 0;
1497 }
1498 
1500  const pcf::IndiProperty &ipRecv
1501  )
1502 {
1503  userGainCtrl * _app = static_cast<userGainCtrl *>(app);
1504  return _app->newCallBack_blockGains(ipRecv);
1505 }
1506 
1507 int userGainCtrl::newCallBack_blockGains( const pcf::IndiProperty &ipRecv )
1508 {
1509  if(ipRecv.getDevice() != m_configName)
1510  {
1511  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1512  log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
1513  #endif
1514 
1515  return -1;
1516  }
1517 
1518  if(ipRecv.getName().find("block") != 0)
1519  {
1520  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1521  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1522  #endif
1523 
1524  return -1;
1525  }
1526 
1527  if(ipRecv.getName().find("_gain") != 7)
1528  {
1529  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1530  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1531  #endif
1532 
1533  return -1;
1534  }
1535 
1536  if(ipRecv.getName().size() != 12)
1537  {
1538  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1539  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1540  #endif
1541 
1542  return -1;
1543  }
1544 
1545  #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
1546  return 0;
1547  #endif
1548 
1549  int n = std::stoi(ipRecv.getName().substr(5,2));
1550 
1551  float current = -1;
1552  float target = -1;
1553 
1554  if(ipRecv.find("current"))
1555  {
1556  current = ipRecv["current"].get<double>();
1557  }
1558 
1559  if(ipRecv.find("target"))
1560  {
1561  target = ipRecv["target"].get<double>();
1562  }
1563 
1564  if(target == -1) target = current;
1565 
1566  if(target == -1)
1567  {
1568  return 0;
1569  }
1570 
1571  updateIfChanged(m_indiP_blockGains[n], "target", target);
1572 
1573  return setBlockGain(n, target);
1574 
1575 }
1576 
1578  const pcf::IndiProperty &ipRecv
1579  )
1580 {
1581  userGainCtrl * _app = static_cast<userGainCtrl *>(app);
1582  return _app->newCallBack_blockMCs(ipRecv);
1583 }
1584 
1585 int userGainCtrl::newCallBack_blockMCs( const pcf::IndiProperty &ipRecv )
1586 {
1587  if(ipRecv.getDevice() != m_configName)
1588  {
1589  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1590  log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
1591  #endif
1592 
1593  return -1;
1594  }
1595 
1596  if(ipRecv.getName().find("block") != 0)
1597  {
1598  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1599  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1600  #endif
1601 
1602  return -1;
1603  }
1604 
1605  if(ipRecv.getName().find("_multcoeff") != 7)
1606  {
1607  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1608  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1609  #endif
1610 
1611  return -1;
1612  }
1613 
1614  if(ipRecv.getName().size() != 17)
1615  {
1616  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1617  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1618  #endif
1619 
1620  return -1;
1621  }
1622 
1623  #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
1624  return 0;
1625  #endif
1626 
1627  int n = std::stoi(ipRecv.getName().substr(5,2));
1628 
1629  float current = -1;
1630  float target = -1;
1631 
1632  if(ipRecv.find("current"))
1633  {
1634  current = ipRecv["current"].get<double>();
1635  }
1636 
1637  if(ipRecv.find("target"))
1638  {
1639  target = ipRecv["target"].get<double>();
1640  }
1641 
1642  if(target == -1) target = current;
1643 
1644  if(target == -1)
1645  {
1646  return 0;
1647  }
1648 
1649  updateIfChanged(m_indiP_blockMCs[n], "target", target);
1650 
1651  return setBlockMC(n, target);
1652 
1653 }
1654 
1656  const pcf::IndiProperty &ipRecv
1657  )
1658 {
1659  userGainCtrl * _app = static_cast<userGainCtrl *>(app);
1660  return _app->newCallBack_blockLimits(ipRecv);
1661 }
1662 
1663 int userGainCtrl::newCallBack_blockLimits( const pcf::IndiProperty &ipRecv )
1664 {
1665  if(ipRecv.getDevice() != m_configName)
1666  {
1667  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1668  log<software_error>({__FILE__, __LINE__, "wrong INDI device"});
1669  #endif
1670 
1671  return -1;
1672  }
1673 
1674  if(ipRecv.getName().find("block") != 0)
1675  {
1676  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1677  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1678  #endif
1679 
1680  return -1;
1681  }
1682 
1683  if(ipRecv.getName().find("_limit") != 7)
1684  {
1685  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1686  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1687  #endif
1688 
1689  return -1;
1690  }
1691 
1692  if(ipRecv.getName().size() != 13)
1693  {
1694  #ifndef XWCTEST_INDI_CALLBACK_VALIDATION
1695  log<software_error>({__FILE__, __LINE__, "wrong INDI property"});
1696  #endif
1697 
1698  return -1;
1699  }
1700 
1701  #ifdef XWCTEST_INDI_CALLBACK_VALIDATION
1702  return 0;
1703  #endif
1704 
1705 
1706  int n = std::stoi(ipRecv.getName().substr(5,2));
1707 
1708  float current = -1;
1709  float target = -1;
1710 
1711  if(ipRecv.find("current"))
1712  {
1713  current = ipRecv["current"].get<double>();
1714  }
1715 
1716  if(ipRecv.find("target"))
1717  {
1718  target = ipRecv["target"].get<double>();
1719  }
1720 
1721  if(target == -1) target = current;
1722 
1723  if(target == -1)
1724  {
1725  return 0;
1726  }
1727 
1728  updateIfChanged(m_indiP_blockLimits[n], "target", target);
1729 
1730  return setBlockLimit(n, target);
1731 
1732 }
1733 
1734 inline
1736 {
1738 }
1739 
1740 inline
1742 {
1743  return recordBlockGains(true);
1744 }
1745 
1746 inline
1748 {
1749  static std::vector<float> modeBlockGains;
1750  static std::vector<uint8_t> modeBlockGainsConstant;
1751 
1752  static std::vector<float> modeBlockMCs;
1753  static std::vector<uint8_t> modeBlockMCsConstant;
1754 
1755  static std::vector<float> modeBlockLims;
1756  static std::vector<uint8_t> modeBlockLimsConstant;
1757 
1758  if(!force)
1759  {
1760  if(!(m_modeBlockGains == modeBlockGains)) force = true;
1761  }
1762 
1763  if(!force)
1764  {
1765  if(!(m_modeBlockGainsConstant == modeBlockGainsConstant)) force = true;
1766  }
1767 
1768  if(!force)
1769  {
1770  if(!(m_modeBlockMCs == modeBlockMCs)) force = true;
1771  }
1772 
1773  if(!force)
1774  {
1775  if(!(m_modeBlockMCsConstant == modeBlockMCsConstant)) force = true;
1776  }
1777 
1778  if(!force)
1779  {
1780  if(!(m_modeBlockLims == modeBlockLims)) force = true;
1781  }
1782 
1783  if(!force)
1784  {
1785  if(!(m_modeBlockLimsConstant == modeBlockLimsConstant)) force = true;
1786  }
1787 
1788  if(force)
1789  {
1791  modeBlockGains = m_modeBlockGains;
1792  modeBlockGainsConstant = m_modeBlockGainsConstant;
1793  modeBlockMCs = m_modeBlockMCs;
1794  modeBlockMCsConstant = m_modeBlockMCsConstant;
1795  modeBlockLims = m_modeBlockLims;
1796  modeBlockLimsConstant = m_modeBlockLimsConstant;
1797  }
1798 
1799  return 0;
1800 }
1801 
1802 } //namespace app
1803 } //namespace MagAOX
1804 
1805 #endif //userGainCtrl_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
std::string m_configName
The name of the configuration file (minus .conf).
Definition: MagAOXApp.hpp:88
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 m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:102
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
Definition: MagAOXApp.hpp:537
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
Definition: MagAOXApp.hpp:2294
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
Definition: MagAOXApp.hpp:2437
std::mutex m_indiMutex
Mutex for locking INDI communications.
Definition: MagAOXApp.hpp:540
std::unordered_map< std::string, indiCallBack > m_indiNewCallBacks
Map to hold the NewProperty indiCallBacks for this App, with fast lookup by property name.
Definition: MagAOXApp.hpp:567
uint32_t m_width
The width of the images in the stream.
int updateINDI()
Update the INDI properties for this device controller.
uint32_t m_height
The height of the images in the stream.
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
std::string m_shmimName
The name of the shared memory image, is used in /tmp/<shmimName>.im.shm. Derived classes should set a...
int appShutdown()
Shuts down the shmimMonitor thread.
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
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
std::vector< std::string > m_modeBlockNames
pcf::IndiProperty m_indiP_zeroAll
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawFloor)
static int st_newCallBack_blockLimits(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for block limits.
virtual int appShutdown()
Shutdown the app.
mx::improc::eigenImage< realT > m_mcsTarget
The target gains.
int m_totalNModes
The total number of WFS modes in the calib.
int newCallBack_blockLimits(const pcf::IndiProperty &ipRecv)
Callback to process a NEW block limits.
int recordBlockGains(bool force=false)
std::vector< float > m_modeBlockGains
realT(* mc_pixget)(void *, size_t)
void powerLawIndex(float pli)
mx::improc::eigenImage< realT > m_mcsCurrent
Pointer to a function to extract the image data as our desired type realT.
pcf::IndiProperty m_indiP_powerLawFloor
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleMC)
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_zeroAll)
static int st_newCallBack_blockGains(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for block gains.
dev::telemeter< userGainCtrl > telemeterT
int newCallBack_blockMCs(const pcf::IndiProperty &ipRecv)
Callback to process a NEW block mult. coeff.s.
std::vector< pcf::IndiProperty > m_indiP_blockGains
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawIndex)
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleGain)
static int st_newCallBack_blockMCs(void *app, const pcf::IndiProperty &ipRecv)
The static callback function to be registered for block mult. coeff.s.
~userGainCtrl() noexcept
D'tor, declared and defined for noexcept.
int setBlockLimit(int n, float l)
mx::improc::eigenImage< realT > m_limitsTarget
The target gains.
int allocate(const gainShmimT &dummy)
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_powerLawSet)
std::vector< uint8_t > m_modeBlockGainsConstant
std::vector< uint16_t > m_modeBlockN
std::vector< pcf::IndiProperty > m_indiP_blockMCs
int setBlockMC(int n, float mc)
pcf::IndiProperty m_indiP_modes
void powerLawFloor(float plf)
int setBlockGain(int n, float g)
pcf::IndiProperty m_indiP_singleModeNo
std::vector< uint8_t > m_modeBlockMCsConstant
INDI_NEWCALLBACK_DECL(userGainCtrl, m_indiP_singleModeNo)
std::vector< float > m_modeBlockLims
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
realT(* limit_pixget)(void *, size_t)
dev::shmimMonitor< userGainCtrl, limitShmimT > limitShmimMonitorT
userGainCtrl()
Default c'tor.
realT(* pixget)(void *, size_t)
int processImage(void *curr_src, const gainShmimT &dummy)
pcf::IndiProperty m_indiP_singleMC
float realT
Floating point type in which to do all calculations.
dev::shmimMonitor< userGainCtrl, gainShmimT > shmimMonitorT
pcf::IndiProperty m_indiP_powerLawSet
pcf::IndiProperty m_indiP_powerLawIndex
std::vector< float > m_modeBlockMCs
virtual int appLogic()
Implementation of the FSM for userGainCtrl.
virtual int appStartup()
Startup function.
int recordTelem(const telem_blockgains *)
std::vector< pcf::IndiProperty > m_indiP_blockLimits
std::vector< uint8_t > m_modeBlockLimsConstant
int newCallBack_blockGains(const pcf::IndiProperty &ipRecv)
Callback to process a NEW block gain request.
std::vector< uint16_t > m_modeBlockStart
Pointer to a function to extract the image data as our desired type realT.
mx::improc::eigenImage< realT > m_gainsCurrent
The current gains.
mx::improc::eigenImage< realT > m_gainsTarget
The target gains.
mx::improc::eigenImage< realT > m_limitsCurrent
Pointer to a function to extract the image data as our desired type realT.
pcf::IndiProperty m_indiP_singleGain
mx::fits::fitsFile< float > m_ff
dev::shmimMonitor< userGainCtrl, multcoeffShmimT > mcShmimMonitorT
#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
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:44
#define INDI_IDLE
Definition: indiUtils.hpp:28
std::ostream & cerr()
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
int addNumberElement(pcf::IndiProperty &prop, const std::string &name, const T &min, const T &max, const T &step, const std::string &format, const std::string &label="")
Add a standard INDI Number element.
Definition: indiUtils.hpp:63
uint16_t modesAtBlock(uint16_t b)
const pcf::IndiProperty & ipRecv
INDI_VALIDATE_CALLBACK_PROPS(function, ipRecv)
uint16_t modesInBlock(uint16_t b)
Calculate the number of modes in 1 block.
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
int blockModes(std::vector< uint16_t > &blocks, std::vector< std::string > &names, uint16_t Nmodes, uint16_t Nzern, bool splitTT)
Calculate the number of blocks and the number of modes per block.
Definition: dm.hpp:24
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
A device which saves telemetry.
Definition: telemeter.hpp:52
int appShutdown()
Perform telemeter application shutdown.
Definition: telemeter.hpp:259
int loadConfig(appConfigurator &config)
Load the device section from an application configurator.
Definition: telemeter.hpp:208
int appLogic()
Perform telemeter application logic.
Definition: telemeter.hpp:253
int setupConfig(appConfigurator &config)
Setup an application configurator for the device section.
Definition: telemeter.hpp:195
int appStartup()
Starts the telemetry log thread.
Definition: telemeter.hpp:226
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
Definition: telemeter.hpp:266
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string indiPrefix()
static std::string configSection()
Software ERR log entry.
Log entry recording electronics rack temperature.