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