API
 
Loading...
Searching...
No Matches
modalGainOpt.hpp
Go to the documentation of this file.
1/** \file modalGainOpt.hpp
2 * \brief The MagAO-X PSD-based gain optimizer header file
3 *
4 * \ingroup modalGainOpt_files
5 */
6
7#ifndef modalGainOpt_hpp
8#define modalGainOpt_hpp
9
10#include <mx/mxException.hpp>
11#include <mx/ao/analysis/clGainOpt.hpp>
12#include <mx/ao/analysis/clAOLinearPredictor.hpp>
13
14#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
15#include "../../magaox_git_version.h"
16
17/** \defgroup modalGainOpt
18 * \brief The MagAO-X application to perform PSD-based gain optimization
19 *
20 * <a href="../handbook/operating/software/apps/modalGainOpt.html">Application Documentation</a>
21 *
22 * \ingroup apps
23 *
24 */
25
26/** \defgroup modalGainOpt_files
27 * \ingroup modalGainOpt
28 */
29
30namespace MagAOX
31{
32namespace app
33{
34
35#define MGO_BREADCRUMB
36
37// #define MGO_BREADCRUMB std::cerr << __FILE__ << ' ' << __LINE__ << '\n';
38
40{
41 static std::string configSection()
42 {
43 return "psdShmim";
44 };
45
46 static std::string indiPrefix()
47 {
48 return "psd";
49 };
50};
51
53{
54 static std::string configSection()
55 {
56 return "freqShmim";
57 };
58
59 static std::string indiPrefix()
60 {
61 return "freq";
62 };
63};
64
65struct gainFactShmimT
66{
67 static std::string configSection()
68 {
69 return "gainFactShmim";
70 };
71
72 static std::string indiPrefix()
73 {
74 return "gainFact";
75 };
76};
77
78struct multFactShmimT
79{
80 static std::string configSection()
81 {
82 return "multFactShmim";
83 };
84
85 static std::string indiPrefix()
86 {
87 return "multFact";
88 };
89};
90
91struct pcGainFactShmimT
92{
93 static std::string configSection()
94 {
95 return "pcGainFactShmim";
96 };
97
98 static std::string indiPrefix()
99 {
100 return "pcGainFact";
101 };
102};
103
104struct pcMultFactShmimT
105{
106 static std::string configSection()
107 {
108 return "pcMultFactShmim";
109 };
110
111 static std::string indiPrefix()
112 {
113 return "pcMultFact";
114 };
115};
116
118{
119 static std::string configSection()
120 {
121 return "numpccoeffShmim";
122 };
123
124 static std::string indiPrefix()
125 {
126 return "numpccoeff";
127 };
128};
129
130struct acoeffShmimT
131{
132 static std::string configSection()
133 {
134 return "acoeffShmim";
135 };
136
137 static std::string indiPrefix()
138 {
139 return "acoeff";
140 };
141};
142
143struct bcoeffShmimT
144{
145 static std::string configSection()
146 {
147 return "bcoeffShmim";
148 };
149
150 static std::string indiPrefix()
151 {
152 return "bcoeff";
153 };
154};
155
157{
158 static std::string configSection()
159 {
160 return "gainCalShmim";
161 };
162
163 static std::string indiPrefix()
164 {
165 return "gainCal";
166 };
167};
168
170{
171 static std::string configSection()
172 {
173 return "gainCalFactShmim";
174 };
175
176 static std::string indiPrefix()
177 {
178 return "gainCalFact";
179 };
180};
181
183{
184 static std::string configSection()
185 {
186 return "tauShmim";
187 };
188
189 static std::string indiPrefix()
190 {
191 return "tau";
192 };
193};
194
196{
197 static std::string configSection()
198 {
199 return "noiseShmim";
200 };
201
202 static std::string indiPrefix()
203 {
204 return "noise";
205 };
206};
207
209{
210 static std::string configSection()
211 {
212 return "wfsavgShmim";
213 };
214
215 static std::string indiPrefix()
216 {
217 return "wfsavg";
218 };
219};
220
222{
223 static std::string configSection()
224 {
225 return "wfsmaskShmim";
226 };
227
228 static std::string indiPrefix()
229 {
230 return "wfsmask";
231 };
232};
233
234/// The MagAO-X PSD-based gain optimizer
235/**
236 * \ingroup modalGainOpt
237 */
238class modalGainOpt : public MagAOXApp<true>,
239 dev::shmimMonitor<modalGainOpt, psdShmimT>,
240 dev::shmimMonitor<modalGainOpt, freqShmimT>,
241 dev::shmimMonitor<modalGainOpt, gainFactShmimT>,
242 dev::shmimMonitor<modalGainOpt, multFactShmimT>,
243 dev::shmimMonitor<modalGainOpt, pcGainFactShmimT>,
244 dev::shmimMonitor<modalGainOpt, pcMultFactShmimT>,
245 dev::shmimMonitor<modalGainOpt, numpccoeffShmimT>,
246 dev::shmimMonitor<modalGainOpt, acoeffShmimT>,
247 dev::shmimMonitor<modalGainOpt, bcoeffShmimT>,
248 dev::shmimMonitor<modalGainOpt, gainCalShmimT>,
249 dev::shmimMonitor<modalGainOpt, gainCalFactShmimT>,
250 dev::shmimMonitor<modalGainOpt, tauShmimT>,
251 dev::shmimMonitor<modalGainOpt, noiseShmimT>,
252 dev::shmimMonitor<modalGainOpt, wfsavgShmimT>,
253 dev::shmimMonitor<modalGainOpt, wfsmaskShmimT>
254
255{
256
257 // Give the test harness access.
258 friend class modalGainOpt_test;
259
275
276 public:
292
293 typedef std::chrono::time_point<std::chrono::steady_clock> timePointT;
294 typedef std::chrono::duration<double> durationT;
295
296 protected:
297 /** \name Configurable Parameters
298 *@{
299 */
300
301 int m_loopNum{ 1 }; ///< The number of the loop. Used to set shmim names, as in aolN_mgainfact.
302
303 std::string m_loopName; ///< The name of the loop control INDI device name.
304
305 std::string m_wfsDevice{ "camwfs" };
306
307 std::string m_psdDevice{ "hopsds" }; /**< The INDI device name of the PSD calculator. Defaults to aolN_modevalPSDs
308 where N is m_loopNum.*/
309
310 std::string m_opticalGainDevice {"strehl"};
311 std::string m_opticalGainProperty {"strehl_optimal"};
312 std::string m_opticalGainElement {"pyramid"};
313
314 bool m_autoUpdate{ false }; ///< Flag controlling whether gains are automatically updated
315 bool m_opticalGainUpdate {false}; ///< Flag controlling whether optical gain is automatically updated;
316
317 float m_gainGain{ 0.1 }; ///< The gain to use for closed-loop gain updates. Default is 0.1.
318
320
322
323 int m_extrapOL {0}; ///< Which extrapolation method to use for the OL PSD. 0 is none. The only other option is 1.
324
325 ///@}
326
329
330 bool m_updateOnce{ false }; ///< Flag to trigger a single update with gain.
331
332 bool m_dump{ false }; ///< Flag to trigger a single update with no gain.
333
334 float m_fps{ 0 };
335
336 /// Each mode gets its own gain optimizer
337 std::vector<mx::AO::analysis::clGainOpt<float>> m_goptCurrent;
338 std::vector<mx::AO::analysis::clGainOpt<float>> m_goptSI;
339 std::vector<mx::AO::analysis::clGainOpt<float>> m_goptLP;
340 std::vector<mx::AO::analysis::clAOLinearPredictor<float>> m_linPred;
341
342 bool m_goptUpdated{ true }; ///< Tracks if a parameter has updated requiring updates to the m_gopt entries.
343 bool m_pcgoptUpdated{ true }; ///< Tracks if a parameter has updated requiring updates to the m_gopt entries.
344
345 bool m_freqUpdated{ true }; /**< Tracks if the frequency scale has updated, which necessitates additional calcs.
346 If true, implies m_goptUpdate == true.*/
347 float m_psdTime{ 1 };
348 float m_psdAvgTime{ 10 };
350
351 std::vector<float> m_freq;
352
353 mx::improc::eigenImage<float> m_clPSDs;
354 mx::improc::eigenImage<float> m_clXferCurrent;
355 mx::improc::eigenImage<float> m_clXferSI;
356 mx::improc::eigenImage<float> m_clXferLP;
357
358 std::vector<std::vector<float>> m_olPSDs;
359 std::vector<std::vector<float>> m_nPSDs;
360
361 std::vector<float> m_modeVarCL;
362 std::vector<float> m_modeVarOL;
363
365
366 std::vector<float> m_optGainSI;
367 std::vector<float> m_gmaxSI; ///< The previously calculated maximum gains for LP
368 std::vector<float> m_modeVarSI;
369 std::vector<int> m_timesOnSI;
371
372 std::vector<float> m_optGainLP;
373 std::vector<float> m_gmaxLP; ///< The previously calculated maximum gains for LP
374 std::vector<float> m_modeVarLP;
375 std::vector<int> m_timesOnLP;
377
378 bool m_loop{ false };
379
380 float m_opticalGain{ 1 };
381
383
384 float m_gain{ 0 };
385
386 float m_mult{ 1 };
387
388 float m_siGain{ 0 };
389
390 float m_siMult{ 1 };
391
392 bool m_doPCCalcs{ true };
393
394 float m_pcGain{ 0 };
395
396 float m_pcMult{ 0 };
397
398 bool m_pcOn{ false };
399
400 std::vector<float> m_gainFacts;
401
402 std::vector<float> m_multFacts;
403
404 std::vector<float> m_pcGainFacts;
405
406 std::vector<float> m_pcMultFacts;
407
408 std::vector<uint32_t> m_Na; // The latest user specified number of a coefficients
409
410 std::vector<uint32_t> m_NaCurrent; // The current number of a coefficients
411
412 std::vector<uint32_t> m_Nb; // The latest user specified number of b coefficients
413
414 std::vector<uint32_t> m_NbCurrent; // The current number of b coefficients
415
417
419
420 int m_nRegCycles{ 60 }; ///< How often to regularize each mode
421
422 std::vector<int> m_regCounter; ///< Counters to track when this mode was last regularized
423
424 std::vector<float> m_regScale; ///< The regularization scale factors for each mode
425
426 std::vector<float> m_gainCals;
427
428 std::vector<float> m_gainCalFacts;
429
430 std::vector<float> m_taus;
431
433
436 float m_counts{ 0 };
437 float m_emg{ 1 };
438 int m_npix{ 0 };
439
440 int m_sinceChange{ -1 };
441
442 std::string m_olPSDShmimName;
447
451
454
456
457 IMAGE *m_olPSDStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the open loop PSDs
458 IMAGE *m_noisePSDStream{ nullptr }; /**< The ImageStreamIO shared memory buffer to publish the noise
459 PSDs (single value per mode)*/
460 IMAGE *m_clXferCurrentStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the SI ETF
461 IMAGE *m_clXferSIStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the SI ETF
462 IMAGE *m_clXferLPStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the LP ETF
463
464 IMAGE *m_optGainStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the optimal gains
465
466 IMAGE *m_optGainSIStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the SI optimal gains
467 IMAGE *m_maxGainSIStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the SI max gains
468
469 IMAGE *m_optGainLPStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the LP optimal gains
470 IMAGE *m_maxGainLPStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the LP max gains
471
472 IMAGE *m_modevarStream{ nullptr }; ///< The ImageStreamIO shared memory buffer to publish the LP optimal gains
473
474 public:
475 /// Default c'tor.
476 modalGainOpt();
477
478 /// D'tor, declared and defined for noexcept.
480 {
481 }
482
483 virtual void setupConfig();
484
485 /// Implementation of loadConfig logic, separated for testing.
486 /** This is called by loadConfig().
487 */
488 int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration
489 from which to load values*/ );
490
491 virtual void loadConfig();
492
493 /// Startup function
494 /**
495 *
496 */
497 virtual int appStartup();
498
499 /// Implementation of the FSM for modalGainOpt.
500 /**
501 * \returns 0 on no critical error
502 * \returns -1 on an error requiring shutdown
503 */
504 virtual int appLogic();
505
506 /// Shutdown the app.
507 /**
508 *
509 */
510 virtual int appShutdown();
511
512 int allocatePCShmims();
513
514 int allocate( const psdShmimT & ///< [in] tag to differentiate shmimMonitor parents.
515 );
516
517 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
518 const psdShmimT & ///< [in] tag to differentiate shmimMonitor parents.
519 );
520
521 int allocate( const freqShmimT & ///< [in] tag to differentiate shmimMonitor parents.
522 );
523
524 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
525 const freqShmimT & ///< [in] tag to differentiate shmimMonitor parents.
526 );
527
528 int allocate( const gainFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
529 );
530
531 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
532 const gainFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
533 );
534
535 int allocate( const multFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
536 );
537
538 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
539 const multFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
540 );
541
542 int allocate( const pcGainFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
543 );
544
545 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
546 const pcGainFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
547 );
548
549 int allocate( const pcMultFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
550 );
551
552 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
553 const pcMultFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
554 );
555
556 int allocate( const numpccoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
557 );
558
559 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
560 const numpccoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
561 );
562
563 int allocate( const acoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
564 );
565
566 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
567 const acoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
568 );
569
570 int allocate( const bcoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
571 );
572
573 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
574 const bcoeffShmimT & ///< [in] tag to differentiate shmimMonitor parents.
575 );
576
577 int allocate( const gainCalShmimT & ///< [in] tag to differentiate shmimMonitor parents.
578 );
579
580 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
581 const gainCalShmimT & ///< [in] tag to differentiate shmimMonitor parents.
582 );
583
584 int allocate( const gainCalFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
585 );
586
587 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
588 const gainCalFactShmimT & ///< [in] tag to differentiate shmimMonitor parents.
589 );
590
591 int allocate( const tauShmimT & ///< [in] tag to differentiate shmimMonitor parents.
592 );
593
594 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
595 const tauShmimT & ///< [in] tag to differentiate shmimMonitor parents.
596 );
597
598 int allocate( const noiseShmimT & ///< [in] tag to differentiate shmimMonitor parents.
599 );
600
601 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
602 const noiseShmimT & ///< [in] tag to differentiate shmimMonitor parents.
603 );
604
605 int allocate( const wfsavgShmimT & ///< [in] tag to differentiate shmimMonitor parents.
606 );
607
608 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
609 const wfsavgShmimT & ///< [in] tag to differentiate shmimMonitor parents.
610 );
611
612 int allocate( const wfsmaskShmimT & ///< [in] tag to differentiate shmimMonitor parents.
613 );
614
615 int processImage( void *curr_src, ///< [in] pointer to the start of the current frame
616 const wfsmaskShmimT & ///< [in] tag to differentiate shmimMonitor parents.
617 );
618
619 /// Check that all sizes and allocations have occurred
620 int checkSizes();
621
622 protected:
623 /// Mutex for synchronizing updates.
624 std::mutex m_goptMutex;
625
626 /// Flag used to indicate to the goptThread that it should stop calculations ASAP
627 bool m_updating{ false };
628
629 /** \name Gain Optimization Thread
630 *
631 * @{
632 */
633 int m_goptThreadPrio{ 0 }; ///< Priority of the gain optimization thread.
634
635 std::string m_goptThreadCpuset; ///< The cpuset to use for the gain optimization thread.
636
637 std::thread m_goptThread; ///< The gain optimization thread.
638
639 bool m_goptThreadInit{ true }; ///< Initialization flag for the gain optimization thread.
640
641 pid_t m_goptThreadID{ 0 }; ///< gain optimization thread PID.
642
643 pcf::IndiProperty m_goptThreadProp; ///< The property to hold the gain optimization thread details.
644
645 sem_t m_goptSemaphore; ///< Semaphore used to synchronize the psdShmim thread and the gopt thread.
646
647 float noisePSD( int n );
648
649 /// Gain Optimization thread starter function
650 static void goptThreadStart( modalGainOpt *p /**< [in] pointer to this */ );
651
652 /// Gain optimization thread function
653 /** Runs until m_shutdown is true.
654 */
655 void goptThreadExec();
656
657 ///@}
658
659 public:
660 /** \name INDI
661 * @{
662 */
663
664 pcf::IndiProperty m_indiP_autoUpdate;
665 pcf::IndiProperty m_indiP_updateOnce;
666 pcf::IndiProperty m_indiP_dump;
667
668 pcf::IndiProperty m_indiP_opticalGain;
669
670 pcf::IndiProperty m_indiP_gainGain;
671
672 pcf::IndiProperty m_indiP_emg;
673 pcf::IndiProperty m_indiP_psdTime;
674 pcf::IndiProperty m_indiP_psdAvgTime;
675 pcf::IndiProperty m_indiP_loop;
676 pcf::IndiProperty m_indiP_siGain;
677 pcf::IndiProperty m_indiP_siMult;
678 pcf::IndiProperty m_indiP_pcGain;
679 pcf::IndiProperty m_indiP_pcMult;
680 pcf::IndiProperty m_indiP_pcOn;
681
682 pcf::IndiProperty m_indiP_extrapOL;
683
684 pcf::IndiProperty m_indiP_modesOn;
685
686 pcf::IndiProperty m_indiP_opticalGainSource;
687 pcf::IndiProperty m_indiP_opticalGainUpdate;
688
689
705
708
709
710 ///@}
711};
712
733
735{
736 config.add( "loop.number",
737 "",
738 "loop.number",
739 argType::Required,
740 "loop",
741 "number",
742 false,
743 "int",
744 "The number of the loop. Used to set shmim names, as in aolN_mgainfact." );
745
746 config.add( "loop.name",
747 "",
748 "loop.name",
749 argType::Required,
750 "loop",
751 "name",
752 false,
753 "string",
754 "The name of the loop control INDI device name." );
755
756 config.add( "loop.psdDev",
757 "",
758 "loop.psdDev",
759 argType::Required,
760 "loop",
761 "psdDev",
762 false,
763 "string",
764 "The INDI device name of the PSD calculator. Defaults to aolN_modevalPSDs where N is loop.number." );
765
766 config.add( "loop.autoUpdate",
767 "",
768 "loop.autoUpdate",
769 argType::Required,
770 "loop",
771 "autoUpdate",
772 false,
773 "bool",
774 "Flag controlling whether the gains are auto updated. Also settable via INDI." );
775
776 config.add( "loop.gainGain",
777 "",
778 "loop.gainGain",
779 argType::Required,
780 "loop",
781 "gainGain",
782 false,
783 "float",
784 "The gain to use for closed-loop gain updates. Default is 0.1" );
785
801}
802
803int modalGainOpt::loadConfigImpl( mx::app::appConfigurator &_config )
804{
805 _config( m_loopNum, "loop.number" );
806 _config( m_loopName, "loop.name" );
807 _config( m_autoUpdate, "loop.autoUpdate" );
808 _config( m_autoUpdate, "loop.gainGain" );
809
810 char shmim[1024];
811
812 _config( m_psdDevice, "loop.psdDev" );
813
814 snprintf( shmim, sizeof( shmim ), "aol%d_clpsds", m_loopNum );
817
818 snprintf( shmim, sizeof( shmim ), "aol%d_freq", m_loopNum );
821
822 snprintf( shmim, sizeof( shmim ), "aol%d_mgainfact", m_loopNum );
825
826 snprintf( shmim, sizeof( shmim ), "aol%d_mmultfact", m_loopNum );
829
830 snprintf( shmim, sizeof( shmim ), "aol%d_mpcgainfact", m_loopNum );
833
834 snprintf( shmim, sizeof( shmim ), "aol%d_mpcmultfact", m_loopNum );
837
838 snprintf( shmim, sizeof( shmim ), "aol%d_numpccoeff", m_loopNum );
841
842 snprintf( shmim, sizeof( shmim ), "aol%d_acoeff", m_loopNum );
845
846 snprintf( shmim, sizeof( shmim ), "aol%d_bcoeff", m_loopNum );
849
850 snprintf( shmim, sizeof( shmim ), "aol%d_mgaincal", m_loopNum );
853
854 snprintf( shmim, sizeof( shmim ), "aol%d_mgaincalfact", m_loopNum );
857
858 snprintf( shmim, sizeof( shmim ), "aol%d_looptau", m_loopNum );
861
862 snprintf( shmim, sizeof( shmim ), "aol%d_mnoiseparam", m_loopNum );
865
866 snprintf( shmim, sizeof( shmim ), "aol%d_wfsavg", m_loopNum );
869
870 snprintf( shmim, sizeof( shmim ), "aol%d_wfsmask", m_loopNum );
873
874 snprintf( shmim, sizeof( shmim ), "aol%d_olpsds", m_loopNum );
875 m_olPSDShmimName = shmim;
876
877 snprintf( shmim, sizeof( shmim ), "aol%d_noisepsds", m_loopNum );
878 m_noisePSDShmimName = shmim;
879
880 snprintf( shmim, sizeof( shmim ), "aol%d_clxferCurrent", m_loopNum );
882
883 snprintf( shmim, sizeof( shmim ), "aol%d_clxferSI", m_loopNum );
884 m_clXferSIShmimName = shmim;
885
886 snprintf( shmim, sizeof( shmim ), "aol%d_clxferLP", m_loopNum );
887 m_clXferLPShmimName = shmim;
888
889 snprintf( shmim, sizeof( shmim ), "aol%d_mgainoptimal", m_loopNum );
890 m_optGainShmimName = shmim;
891
892 snprintf( shmim, sizeof( shmim ), "aol%d_mgainoptimalSI", m_loopNum );
893 m_optGainSIShmimName = shmim;
894
895 snprintf( shmim, sizeof( shmim ), "aol%d_mgainmaxSI", m_loopNum );
896 m_maxGainSIShmimName = shmim;
897
898 snprintf( shmim, sizeof( shmim ), "aol%d_mgainoptimalLP", m_loopNum );
899 m_optGainLPShmimName = shmim;
900
901 snprintf( shmim, sizeof( shmim ), "aol%d_mgainmaxLP", m_loopNum );
902 m_maxGainLPShmimName = shmim;
903
904 snprintf( shmim, sizeof( shmim ), "aol%d_mmodevar", m_loopNum );
905 m_modevarShmimName = shmim;
906
907 return 0;
908}
909
911{
912 loadConfigImpl( config );
913}
914
916{
932
936
938 m_indiP_opticalGain, "opticalGain", 0, 1, 0.01, "%0.01f", "Optical Gain", "Gain Opt." );
939 CREATE_REG_INDI_NEW_NUMBERF( m_indiP_gainGain, "gainGain", 0, 1, 0.01, "%0.01f", "Gain Gain", "Gain Opt." );
940
944 REG_INDI_SETPROP( m_indiP_loop, m_loopName, "loop_state" );
946 REG_INDI_SETPROP( m_indiP_siMult, m_loopName, "loop_multcoeff" );
947 REG_INDI_SETPROP( m_indiP_pcGain, m_loopName, "loop_pcgain" );
948 REG_INDI_SETPROP( m_indiP_pcMult, m_loopName, "loop_pcmultcoeff" );
950
952
953 CREATE_REG_INDI_RO_NUMBER(m_indiP_modesOn, "num_modes", "number of modes", "Gain Opt.");
954 indi::addNumberElement(m_indiP_modesOn, "current", 0, 2400, 1, "%d", "Applied Modes");
955 indi::addNumberElement(m_indiP_modesOn, "integrator", 0, 2400, 1, "%d", "SI optimal");
956 indi::addNumberElement(m_indiP_modesOn, "predictor", 0, 2400, 1, "%d", "LP optimal");
957
959
961
962 if( sem_init( &m_goptSemaphore, 0, 0 ) < 0 )
963 {
964 return log<software_critical, -1>( { __FILE__, __LINE__, errno, 0, "Initializing gopt semaphore" } );
965 }
966
973 "gainopt",
975
977 return 0;
978}
979
981{
997
998 XWCAPP_THREAD_CHECK( m_goptThread, "gainopt" );
999
1015
1016 if( m_autoUpdate )
1017 {
1018 updateSwitchIfChanged( m_indiP_autoUpdate, "toggle", pcf::IndiElement::On, INDI_OK );
1019 }
1020 else
1021 {
1022 updateSwitchIfChanged( m_indiP_autoUpdate, "toggle", pcf::IndiElement::Off, INDI_IDLE );
1023 }
1024
1025 if( m_updateOnce )
1026 {
1027 updateSwitchIfChanged( m_indiP_updateOnce, "request", pcf::IndiElement::On, INDI_OK );
1028 }
1029 else
1030 {
1031 updateSwitchIfChanged( m_indiP_updateOnce, "request", pcf::IndiElement::Off, INDI_IDLE );
1032 }
1033
1034 if( m_dump )
1035 {
1036 updateSwitchIfChanged( m_indiP_dump, "request", pcf::IndiElement::On, INDI_OK );
1037 }
1038 else
1039 {
1040 updateSwitchIfChanged( m_indiP_dump, "request", pcf::IndiElement::Off, INDI_IDLE );
1041 }
1042
1044 {
1045 updateSwitchIfChanged( m_indiP_opticalGainUpdate, "toggle", pcf::IndiElement::On, INDI_OK );
1046 }
1047 else
1048 {
1049 updateSwitchIfChanged( m_indiP_opticalGainUpdate, "toggle", pcf::IndiElement::Off, INDI_IDLE );
1050 }
1051
1053
1054 updatesIfChanged<float>( m_indiP_gainGain, { "current", "target" }, { m_gainGain, m_gainGain } );
1055
1056 if(m_extrapOL == 0)
1057 {
1058 updateSwitchIfChanged(m_indiP_extrapOL, "toggle", pcf::IndiElement::Off);
1059 }
1060 else
1061 {
1062 updateSwitchIfChanged(m_indiP_extrapOL, "toggle", pcf::IndiElement::On);
1063 }
1064
1065 updatesIfChanged<int>(m_indiP_modesOn, {"current","integrator","predictor"}, {m_modesOn, m_modesOnSI, m_modesOnLP});
1066
1067
1068 return 0;
1069}
1070
1072{
1074
1090
1091 if( m_olPSDStream != nullptr )
1092 {
1095 m_olPSDStream = nullptr;
1096
1099 m_noisePSDStream = nullptr;
1100
1103 m_clXferCurrentStream = nullptr;
1104
1107 m_clXferSIStream = nullptr;
1108
1111 m_clXferLPStream = nullptr;
1112 }
1113
1114 if( m_optGainStream != nullptr )
1115 {
1118 m_optGainStream = nullptr;
1119
1122 m_optGainSIStream = nullptr;
1123
1126 m_maxGainSIStream = nullptr;
1127
1130 m_optGainLPStream = nullptr;
1131
1134 m_maxGainLPStream = nullptr;
1135
1138 m_modevarStream = nullptr;
1139 }
1140
1141 return 0;
1142}
1143
1145{
1146 // mutex should be locked before calling this
1147
1150 {
1151 mx::improc::eigenImage<uint32_t> Npc( m_nModes, 2 );
1152 Npc.setConstant( m_defaultNCoeff );
1153
1155 {
1156 return log<software_error, -1>( { __FILE__, __LINE__, "error creating numpccoeffShmim" } );
1157 }
1158
1160
1161 std::cerr << "created numppccoeff shmim\n";
1162 }
1163
1166 {
1168 {
1169 return log<software_error, -1>( { __FILE__, __LINE__, "error creating acoeffShmim" } );
1170 }
1171
1172 std::cerr << "created acoeff shmim\n";
1173 }
1174
1177 {
1179 {
1180 return log<software_error, -1>( { __FILE__, __LINE__, "error creating bcoeffShmim" } );
1181 }
1182
1183 std::cerr << "created bcoeff shmim\n";
1184 }
1185
1188 {
1190 {
1191 return log<software_error, -1>( { __FILE__, __LINE__, "error creating pcGainFactShmim" } );
1192 }
1193
1194 std::cerr << "created pcGainFact shmim\n";
1195 }
1196
1199 {
1201 {
1202 return log<software_error, -1>( { __FILE__, __LINE__, "error creating pcMultFactShmim" } );
1203 }
1204
1205 std::cerr << "created pcMultFact shmim\n";
1206 }
1207
1208 return 0;
1209}
1210
1212{
1213 static_cast<void>( dummy );
1214
1215 m_updating = true;
1216 std::lock_guard<std::mutex> lock( m_goptMutex );
1217
1218 m_updating = true;
1219
1222
1223 m_clPSDs.resize( m_nFreq, m_nModes );
1224
1225 m_clXferCurrent.resize( m_nFreq, m_nModes );
1226 m_clXferSI.resize( m_nFreq, m_nModes );
1227 m_clXferLP.resize( m_nFreq, m_nModes );
1228
1229 m_olPSDs.resize( m_nModes );
1230 m_nPSDs.resize( m_nModes );
1231
1232 for( size_t n = 0; n < m_olPSDs.size(); ++n )
1233 {
1234 m_olPSDs[n].resize( m_nFreq );
1235 m_nPSDs[n].resize( m_nFreq );
1236 }
1237
1238 m_modeVarOL.resize( m_nModes );
1239
1240 m_optGainSI.resize( m_nModes );
1241 m_gmaxSI.resize( m_nModes );
1242 m_modeVarSI.resize( m_nModes );
1243 m_timesOnSI.resize( m_nModes, 5 );
1244
1245
1246 m_optGainLP.resize( m_nModes );
1247 m_modeVarLP.resize( m_nModes );
1248 m_timesOnLP.resize( m_nModes, 5 );
1249
1250 if( m_olPSDStream != nullptr &&
1251 ( m_olPSDStream->md->size[0] != m_nFreq || m_olPSDStream->md->size[1] != m_nModes ) )
1252 {
1255 m_olPSDStream = nullptr;
1256
1259 m_noisePSDStream = nullptr;
1260
1263 m_clXferCurrentStream = nullptr;
1264
1267 m_clXferSIStream = nullptr;
1268
1271 m_clXferLPStream = nullptr;
1272 }
1273
1274 if( m_olPSDStream == nullptr )
1275 {
1276 m_olPSDStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1277 uint32_t imsize[3];
1278
1279 imsize[0] = m_nFreq;
1280 imsize[1] = m_nModes;
1281 imsize[2] = 1;
1283 m_olPSDShmimName.c_str(),
1284 3,
1285 imsize,
1287 -1,
1288 1,
1290 0,
1292 0 );
1293
1294 m_olPSDStream->md->cnt0 = 0;
1295 m_olPSDStream->md->cnt1 = 0;
1296
1297 m_noisePSDStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1298
1300 m_noisePSDShmimName.c_str(),
1301 3,
1302 imsize,
1304 -1,
1305 1,
1307 0,
1309 0 );
1310
1311 m_noisePSDStream->md->cnt0 = 0;
1312 m_noisePSDStream->md->cnt1 = 0;
1313
1314 m_clXferCurrentStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1315
1318 3,
1319 imsize,
1321 -1,
1322 1,
1324 0,
1326 0 );
1327
1328 m_clXferCurrentStream->md->cnt0 = 0;
1329 m_clXferCurrentStream->md->cnt1 = 0;
1330
1331 m_clXferSIStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1332
1334 m_clXferSIShmimName.c_str(),
1335 3,
1336 imsize,
1338 -1,
1339 1,
1341 0,
1343 0 );
1344
1345 m_clXferSIStream->md->cnt0 = 0;
1346 m_clXferSIStream->md->cnt1 = 0;
1347
1348 m_clXferLPStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1349
1351 m_clXferLPShmimName.c_str(),
1352 3,
1353 imsize,
1355 -1,
1356 1,
1358 0,
1360 0 );
1361
1362 m_clXferLPStream->md->cnt0 = 0;
1363 m_clXferLPStream->md->cnt1 = 0;
1364 }
1365
1366 if( m_optGainStream != nullptr &&
1367 ( m_optGainStream->md->size[0] != psdShmimMonitorT::m_height || m_optGainStream->md->size[1] != 1 ) )
1368 {
1371 m_optGainStream = nullptr;
1372
1375 m_optGainSIStream = nullptr;
1376
1379 m_maxGainSIStream = nullptr;
1380
1383 m_optGainLPStream = nullptr;
1384
1387 m_maxGainLPStream = nullptr;
1388 }
1389
1390 if( m_optGainStream == nullptr )
1391 {
1392 m_optGainStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1393 uint32_t imsize[3];
1394
1395 imsize[0] = m_nModes;
1396 imsize[1] = 1;
1397 imsize[2] = 1;
1399 m_optGainShmimName.c_str(),
1400 3,
1401 imsize,
1403 -1,
1404 1,
1406 0,
1408 0 );
1409
1410 m_optGainStream->md->cnt0 = 0;
1411 m_optGainStream->md->cnt1 = 0;
1412
1413 m_optGainSIStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1414
1416 m_optGainSIShmimName.c_str(),
1417 3,
1418 imsize,
1420 -1,
1421 1,
1423 0,
1425 0 );
1426
1427 m_optGainSIStream->md->cnt0 = 0;
1428 m_optGainSIStream->md->cnt1 = 0;
1429
1430 m_maxGainSIStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1431
1433 m_maxGainSIShmimName.c_str(),
1434 3,
1435 imsize,
1437 -1,
1438 1,
1440 0,
1442 0 );
1443
1444 m_maxGainSIStream->md->cnt0 = 0;
1445 m_maxGainSIStream->md->cnt1 = 0;
1446
1447 m_optGainLPStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1448
1450 m_optGainLPShmimName.c_str(),
1451 3,
1452 imsize,
1454 -1,
1455 1,
1457 0,
1459 0 );
1460
1461 m_optGainLPStream->md->cnt0 = 0;
1462 m_optGainLPStream->md->cnt1 = 0;
1463
1464 m_maxGainLPStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1465
1467 m_maxGainLPShmimName.c_str(),
1468 3,
1469 imsize,
1471 -1,
1472 1,
1474 0,
1476 0 );
1477
1478 m_maxGainLPStream->md->cnt0 = 0;
1479 m_maxGainLPStream->md->cnt1 = 0;
1480
1481 m_modevarStream = static_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1482
1483 imsize[0] = 3;
1485 imsize[2] = 1;
1486
1488 m_modevarShmimName.c_str(),
1489 3,
1490 imsize,
1492 -1,
1493 1,
1495 0,
1497 0 );
1498
1499 m_modevarStream->md->cnt0 = 0;
1500 m_modevarStream->md->cnt1 = 0;
1501 }
1502
1503 m_sinceChange = -1;
1504
1505 m_updating = false;
1506 return 0;
1507}
1508
1509int modalGainOpt::processImage( void *curr_src, const psdShmimT &dummy )
1510{
1511 static_cast<void>( dummy );
1512
1513 ++m_sinceChange;
1514
1515 if( m_psdAvgTime <= 0 || m_psdTime <= 0 ) // Safety check, shouldn't happen but means we need to wait.
1516 {
1517 return 0;
1518 }
1519
1521
1522 if( m_sinceChange < deadTime )
1523 {
1524 return 0;
1525 }
1526
1527 // Here we would update psds, but don't do that if we're in the middle of calculating
1528 std::unique_lock<std::mutex> lock( m_goptMutex, std::try_to_lock );
1529 if( !lock.owns_lock() )
1530 {
1531 ///\todo update a frame-missed counter
1532 return 0;
1533 }
1534
1535 m_updating = true;
1536
1537 m_clPSDs = Eigen::Map<Eigen::Array<float, -1, -1>>(
1539
1540 m_updating = false;
1541
1542 lock.unlock();
1543
1544 if( sem_post( &m_goptSemaphore ) < 0 )
1545 {
1546 return log<software_critical, -1>( { __FILE__, __LINE__, errno, 0, "Error posting to semaphore" } );
1547 }
1548
1549 return 0;
1550}
1551
1553{
1554 static_cast<void>( dummy );
1555
1556 return 0;
1557}
1558
1559int modalGainOpt::processImage( void *curr_src, const freqShmimT &dummy )
1560{
1561 static_cast<void>( dummy );
1562
1564 {
1565 return log<software_error, -1>( { __FILE__, __LINE__, "got freq with width not 1" } );
1566 }
1567
1568 bool change = false;
1569
1570 float *f = static_cast<float *>( curr_src );
1571
1573
1574 if( sz != m_freq.size() )
1575 {
1576 change = true;
1577 }
1578
1579 if( !change ) // f is same size
1580 {
1581 for( size_t n = 0; n < sz; ++n )
1582 {
1583 if( f[n] != m_freq[n] )
1584 {
1585 change = true;
1586 break;
1587 }
1588 }
1589 }
1590
1591 if( change )
1592 {
1593 m_updating = true;
1594 std::lock_guard<std::mutex> lock( m_goptMutex );
1595
1596 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1597
1598 m_freq.resize( sz );
1599
1600 for( size_t n = 0; n < sz; ++n )
1601 {
1602 m_freq[n] = f[n];
1603 }
1604
1605 m_fps = 2 * m_freq.back();
1606
1607 m_sinceChange = -1;
1608 m_goptUpdated = true;
1609 m_freqUpdated = true;
1610
1611 m_updating = false;
1612 std::cerr << "got freq: " << sz << '\n';
1613 std::cerr << " fps: " << m_fps << '\n';
1614 }
1615
1616 return 0;
1617}
1618
1620{
1621 static_cast<void>( dummy );
1622
1624 {
1625 return log<software_error, -1>( { __FILE__, __LINE__, "got gains with height not 1" } );
1626 }
1627
1628 return 0;
1629}
1630
1631int modalGainOpt::processImage( void *curr_src, const gainFactShmimT &dummy )
1632{
1633 static_cast<void>( dummy );
1634
1635 bool change = false;
1636
1638
1639 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
1640
1641 if( w != m_gainFacts.size() )
1642 {
1643 m_updating = true;
1644 lock.lock();
1645 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1646
1647 change = true;
1648 m_gainFacts.resize( w );
1649 }
1650
1651 float *g = static_cast<float *>( curr_src );
1652
1653 for( uint32_t n = 0; n < w; ++n )
1654 {
1655 if( change || m_gainFacts[n] != g[n] )
1656 {
1657 if( !change )
1658 {
1659 m_updating = true;
1660 lock.lock();
1661 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1662 change = true;
1663 }
1664
1665 m_gainFacts[n] = g[n];
1666 }
1667 }
1668
1669 if( change )
1670 {
1671 if( m_loop )
1672 {
1673 m_sinceChange = -1;
1674 }
1675
1676 if(!m_pcOn)
1677 {
1678 int modesOn = 0;
1679
1680 for(size_t n = 0; n < m_gainFacts.size(); ++n)
1681 {
1682 if(m_gainFacts[n] > 0)
1683 {
1684 ++modesOn;
1685 }
1686 }
1687
1689 }
1690
1691
1692 m_updating = false;
1693 std::cerr << "got gains: " << m_gainFacts.size() << "\n";
1694
1695 lock.unlock();
1696 }
1697
1698 return 0;
1699}
1700
1702{
1703 static_cast<void>( dummy );
1704
1706 {
1707 return log<software_error, -1>( { __FILE__, __LINE__, "got multcoeffs with height not 1" } );
1708 }
1709
1710 return 0;
1711}
1712
1713int modalGainOpt::processImage( void *curr_src, const multFactShmimT &dummy )
1714{
1715 static_cast<void>( dummy );
1716
1717 bool change = false;
1718
1720
1721 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
1722
1723 if( w != m_multFacts.size() )
1724 {
1725 m_updating = true;
1726 lock.lock();
1727 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1728
1729 change = true;
1730 m_multFacts.resize( w );
1731 }
1732
1733 float *m = static_cast<float *>( curr_src );
1734
1735 for( uint32_t n = 0; n < w; ++n )
1736 {
1737 if( change || m_multFacts[n] != m[n] )
1738 {
1739 if( !change )
1740 {
1741 m_updating = true;
1742 lock.lock();
1743 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1744
1745 change = true;
1746 }
1747
1748 m_multFacts[n] = m[n];
1749 }
1750 }
1751
1752 if( change )
1753 {
1754 if( m_loop )
1755 {
1756 m_sinceChange = -1;
1757 }
1758
1759 m_updating = false;
1760 m_goptUpdated = true;
1761
1762 std::cerr << "got mcs: " << m_multFacts.size() << " " << w << "\n";
1763 lock.unlock();
1764 }
1765
1766 return 0;
1767}
1768
1770{
1771 static_cast<void>( dummy );
1772
1774 {
1775 return log<software_error, -1>( { __FILE__, __LINE__, "got pc gains with height not 1" } );
1776 }
1777
1778 return 0;
1779}
1780
1781int modalGainOpt::processImage( void *curr_src, const pcGainFactShmimT &dummy )
1782{
1783 static_cast<void>( dummy );
1784
1785 bool change = false;
1786
1788
1789 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
1790
1791 if( w != m_pcGainFacts.size() )
1792 {
1793 m_updating = true;
1794 lock.lock();
1795 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1796
1797 change = true;
1798 m_pcGainFacts.resize( w );
1799 }
1800
1801 float *g = static_cast<float *>( curr_src );
1802
1803 for( uint32_t n = 0; n < w; ++n )
1804 {
1805 if( change || m_pcGainFacts[n] != g[n] )
1806 {
1807 if( !change )
1808 {
1809 m_updating = true;
1810 lock.lock();
1811 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1812 change = true;
1813 }
1814
1815 m_pcGainFacts[n] = g[n];
1816 }
1817 }
1818
1819 if( change )
1820 {
1821 if( m_loop )
1822 {
1823 m_sinceChange = -1;
1824 }
1825
1826 if(m_pcOn)
1827 {
1828 int modesOn = 0;
1829
1830 for(size_t n = 0; n < m_pcGainFacts.size(); ++n)
1831 {
1832 if(m_gainFacts[n] > 0)
1833 {
1834 ++modesOn;
1835 }
1836 }
1837
1839 }
1840
1841 m_updating = false;
1842 std::cerr << "got pc gains: " << m_pcGainFacts.size() << "\n";
1843
1844 lock.unlock();
1845 }
1846
1847 return 0;
1848}
1849
1851{
1852 static_cast<void>( dummy );
1853
1855 {
1856 return log<software_error, -1>( { __FILE__, __LINE__, "got pcMultcoeffs with height not 1" } );
1857 }
1858
1859 return 0;
1860}
1861
1862int modalGainOpt::processImage( void *curr_src, const pcMultFactShmimT &dummy )
1863{
1864 static_cast<void>( dummy );
1865
1866 bool change = false;
1867
1869
1870 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
1871
1872 if( w != m_pcMultFacts.size() )
1873 {
1874 m_updating = true;
1875 lock.lock();
1876 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1877
1878 change = true;
1879 m_pcMultFacts.resize( w );
1880 }
1881
1882 float *m = static_cast<float *>( curr_src );
1883
1884 for( uint32_t n = 0; n < w; ++n )
1885 {
1886 if( change || m_pcMultFacts[n] != m[n] )
1887 {
1888 if( !change )
1889 {
1890 m_updating = true;
1891 lock.lock();
1892 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1893
1894 change = true;
1895 }
1896
1897 m_pcMultFacts[n] = m[n];
1898 }
1899 }
1900
1901 if( change )
1902 {
1903 if( m_loop )
1904 {
1905 m_sinceChange = -1;
1906 }
1907
1908 m_updating = false;
1909 m_pcgoptUpdated = true;
1910
1911 lock.unlock();
1912 std::cerr << "got mcs: " << m_multFacts.size() << "\n";
1913 }
1914
1915 return 0;
1916}
1917
1919{
1920 static_cast<void>( dummy );
1921
1923 {
1924 return log<software_error, -1>( { __FILE__, __LINE__, "got numpccoeff's with height not 2" } );
1925 }
1926
1927 std::cerr << "numpccoeffShmimMonitorT::allocate\n";
1928 return 0;
1929}
1930
1931int modalGainOpt::processImage( void *curr_src, const numpccoeffShmimT &dummy )
1932{
1933 static_cast<void>( dummy );
1934
1935 bool change = false;
1936
1938
1939 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
1940
1941 if( w != m_Na.size() || w != m_Nb.size() || w != m_regCounter.size() || w != m_regScale.size() )
1942 {
1943 m_updating = true;
1944 lock.lock();
1945 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1946
1947 change = true;
1948 m_Na.resize( w );
1949 m_Nb.resize( w );
1950
1951 m_regCounter.resize( w );
1952
1953 // Initialize the regCounter
1954 int nc = 0;
1955
1956 for( size_t n = 0; n < m_regCounter.size(); ++n )
1957 {
1958 m_regCounter[n] = nc;
1959 ++nc;
1960
1961 if( nc >= m_nRegCycles )
1962 {
1963 nc = 0;
1964 }
1965 }
1966
1967 m_regScale.resize( w, -999 );
1968 m_gmaxLP.resize( w, 0 );
1969 }
1970
1971 mx::improc::eigenMap<uint32_t> Npc( reinterpret_cast<uint32_t *>( curr_src ), w, 2 );
1972
1973 for( uint32_t n = 0; n < w; ++n )
1974 {
1975 if( change || m_Na[n] != Npc( n, 0 ) || m_Nb[n] != Npc( n, 1 ) )
1976 {
1977 if( !change )
1978 {
1979 m_updating = true;
1980 lock.lock();
1981 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
1982
1983 change = true;
1984 }
1985
1986 m_Na[n] = Npc( n, 0 );
1987 m_Nb[n] = Npc( n, 1 );
1988 }
1989 }
1990
1991 if( change )
1992 {
1993 if( m_loop )
1994 {
1995 m_sinceChange = -1;
1996 }
1997
1998 m_updating = false;
1999 m_goptUpdated = true;
2000
2001 lock.unlock();
2002 std::cerr << "got num pc coeffs: " << m_Na.size() << "\n";
2003 }
2004
2005 return 0;
2006}
2007
2009{
2010 static_cast<void>( dummy );
2011
2012 return 0;
2013}
2014
2015int modalGainOpt::processImage( void *curr_src, const acoeffShmimT &dummy )
2016{
2017 static_cast<void>( dummy );
2018
2019 bool change = false;
2020
2021 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2022
2025
2026 // If there's a size change we lock
2027 if( w - 1 != m_as.rows() || h != m_as.cols() || h != m_NaCurrent.size() )
2028 {
2029 m_updating = true;
2030 lock.lock();
2031 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2032
2033 change = true;
2034 m_NaCurrent.resize( h );
2035 m_as.resize( w - 1, h );
2036 }
2037
2038 eigenMap<float> ac( reinterpret_cast<float *>( curr_src ), w, h );
2039
2040 for( uint32_t cc = 0; cc < h; ++cc )
2041 {
2042 if( change || m_NaCurrent[cc] != ac( 0, cc ) )
2043 {
2044 if( !change )
2045 {
2046 m_updating = true;
2047 lock.lock();
2048 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2049
2050 change = true;
2051 }
2052
2053 m_NaCurrent[cc] = ac( 0, cc );
2054 }
2055
2056 for( uint32_t rr = 1; rr < w; ++rr )
2057 {
2058 if( change || m_as( rr - 1, cc ) != ac( rr, cc ) )
2059 {
2060 if( !change )
2061 {
2062 m_updating = true;
2063 lock.lock();
2064 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2065
2066 change = true;
2067 }
2068
2069 m_as( rr - 1, cc ) = ac( rr, cc );
2070 }
2071 }
2072 }
2073
2074 if( change )
2075 {
2076 if( m_loop && m_pcOn )
2077 {
2078 m_sinceChange = -1;
2079 }
2080
2081 m_updating = false;
2082 m_pcgoptUpdated = true;
2083
2084 lock.unlock();
2085 std::cerr << "got a coeffs: " << w << ' ' << h << ' ' << m_NaCurrent.size() << "\n";
2086 }
2087
2088 return 0;
2089}
2090
2092{
2093 static_cast<void>( dummy );
2094
2095 return 0;
2096}
2097
2098int modalGainOpt::processImage( void *curr_src, const bcoeffShmimT &dummy )
2099{
2100 static_cast<void>( dummy );
2101
2102 bool change = false;
2103
2104 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2105
2108
2109 // If there's a size change we lock
2110 if( w - 1 != m_bs.rows() || h != m_bs.cols() || h != m_NbCurrent.size() )
2111 {
2112 m_updating = true;
2113 lock.lock();
2114 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2115
2116 change = true;
2117
2118 m_NbCurrent.resize( h );
2119 m_bs.resize( w - 1, h );
2120 }
2121
2122 eigenMap<float> bc( reinterpret_cast<float *>( curr_src ), w, h );
2123
2124 for( uint32_t cc = 0; cc < h; ++cc )
2125 {
2126 if( change || m_NbCurrent[cc] != bc( 0, cc ) )
2127 {
2128 if( !change )
2129 {
2130 m_updating = true;
2131 lock.lock();
2132 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2133
2134 change = true;
2135 }
2136
2137 m_NbCurrent[cc] = bc( 0, cc );
2138 }
2139
2140 for( uint32_t rr = 1; rr < w; ++rr )
2141 {
2142 if( change || m_bs( rr - 1, cc ) != bc( rr, cc ) )
2143 {
2144 if( !change )
2145 {
2146 m_updating = true;
2147 lock.lock();
2148 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2149
2150 change = true;
2151 }
2152
2153 m_bs( rr - 1, cc ) = bc( rr, cc );
2154 }
2155 }
2156 }
2157
2158 if( change )
2159 {
2160 if( m_loop && m_pcOn )
2161 {
2162 m_sinceChange = -1;
2163 }
2164
2165 m_updating = false;
2166 m_pcgoptUpdated = true;
2167
2168 std::cerr << "got b coeffs: " << w << ' ' << h << ' ' << m_NbCurrent.size() << "\n";
2169
2170 lock.unlock();
2171 }
2172
2173 return 0;
2174}
2175
2177{
2178 static_cast<void>( dummy );
2179
2180 return 0;
2181}
2182
2183int modalGainOpt::processImage( void *curr_src, const gainCalShmimT &dummy )
2184{
2185 static_cast<void>( dummy );
2186
2188 {
2189 return log<software_error, -1>( { __FILE__, __LINE__, "got gainCals with height not 1" } );
2190 }
2191
2192 bool change = false;
2193
2195
2196 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2197
2198 if( w != m_gainCals.size() )
2199 {
2200 m_updating = true;
2201 lock.lock();
2202
2203 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2204
2205 change = true;
2206 m_gainCals.resize( w );
2207 }
2208
2209 float *g = static_cast<float *>( curr_src );
2210
2211 for( uint32_t n = 0; n < w; ++n )
2212 {
2213 if( change || m_gainCals[n] != g[n] )
2214 {
2215 if( !change )
2216 {
2217 m_updating = true;
2218 lock.lock();
2219 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2220
2221 change = true;
2222 }
2223
2224 m_gainCals[n] = g[n];
2225 }
2226 }
2227
2228 if( change )
2229 {
2230 m_sinceChange = -1;
2231 m_updating = false;
2232 std::cerr << "got gainCals: " << m_gainCals.size() << "\n";
2233 lock.unlock();
2234 }
2235
2236 return 0;
2237}
2238
2240{
2241 static_cast<void>( dummy );
2242
2243 return 0;
2244}
2245
2246int modalGainOpt::processImage( void *curr_src, const gainCalFactShmimT &dummy )
2247{
2248 static_cast<void>( dummy );
2249
2251 {
2252 return log<software_error, -1>( { __FILE__, __LINE__, "got gainCalFacts with height not 1" } );
2253 }
2254
2255 bool change = false;
2256
2258
2259 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2260
2261 if( w != m_gainCalFacts.size() )
2262 {
2263 m_updating = true;
2264 lock.lock();
2265 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2266
2267 change = true;
2268 m_gainCalFacts.resize( w );
2269 }
2270
2271 float *g = static_cast<float *>( curr_src );
2272
2273 for( uint32_t n = 0; n < w; ++n )
2274 {
2275 if( change || m_gainCalFacts[n] != g[n] )
2276 {
2277 if( !change )
2278 {
2279 m_updating = true;
2280 lock.lock();
2281 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2282
2283 change = true;
2284 }
2285
2286 m_gainCalFacts[n] = g[n];
2287 }
2288 }
2289
2290 if( change )
2291 {
2292 m_sinceChange = -1;
2293 m_updating = false;
2294 std::cerr << "got gainCalsFacts: " << m_gainCalFacts.size() << "\n";
2295 lock.unlock();
2296 }
2297
2298 return 0;
2299}
2300
2302{
2303 static_cast<void>( dummy );
2304
2305 return 0;
2306}
2307
2308int modalGainOpt::processImage( void *curr_src, const tauShmimT &dummy )
2309{
2310 static_cast<void>( dummy );
2311
2313 {
2314 return log<software_error, -1>( { __FILE__, __LINE__, "got tau with height not 1" } );
2315 }
2316
2317 bool change = false;
2318
2320
2321 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2322
2323 if( w != m_taus.size() )
2324 {
2325 m_updating = true;
2326 lock.lock();
2327 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2328
2329 change = true;
2330 m_taus.resize( w );
2331 }
2332
2333 float *t = static_cast<float *>( curr_src );
2334
2335 for( uint32_t n = 0; n < w; ++n )
2336 {
2337 if( change || m_taus[n] != t[n] )
2338 {
2339 if( !change )
2340 {
2341 m_updating = true;
2342 lock.lock();
2343 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2344
2345 change = true;
2346 }
2347
2348 m_taus[n] = t[n];
2349 }
2350 }
2351
2352 if( change )
2353 {
2354 m_sinceChange = -1;
2355 m_updating = false;
2356 m_goptUpdated = true;
2357 std::cerr << "got taus: " << m_taus.size() << "\n";
2358 lock.unlock();
2359 }
2360 return 0;
2361}
2362
2364{
2365 static_cast<void>( dummy );
2366
2367 return 0;
2368}
2369
2370int modalGainOpt::processImage( void *curr_src, const noiseShmimT &dummy )
2371{
2372 static_cast<void>( dummy );
2373
2375 {
2376 return log<software_error, -1>( { __FILE__, __LINE__, "got tau with width not 3" } );
2377 }
2378
2379 bool change = false;
2380
2382
2383 std::unique_lock<std::mutex> lock( m_goptMutex, std::defer_lock );
2384
2385 if( h != m_noiseParams.cols() )
2386 {
2387 m_updating = true;
2388 lock.lock();
2389 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2390
2391 change = true;
2392 m_noiseParams.resize( 3, h );
2393 }
2394
2395 mx::improc::eigenMap<float> np( static_cast<float *>( curr_src ), 3, h );
2396
2397 for( uint32_t n = 0; n < h; ++n )
2398 {
2399 if( change || m_noiseParams( 0, n ) != np( 0, n ) || m_noiseParams( 1, n ) != np( 1, n ) ||
2400 m_noiseParams( 2, n ) != np( 2, n ) )
2401 {
2402 if( !change )
2403 {
2404 m_updating = true;
2405 lock.lock();
2406 m_updating = true; // Make sure it didn't get set to false by thread that had the lock
2407
2408 change = true;
2409 }
2410
2411 m_noiseParams.col( n ) = np.col( n );
2412 }
2413 }
2414
2415 if( change )
2416 {
2417 m_sinceChange = -1;
2418 m_updating = false;
2419 m_goptUpdated = true;
2420 std::cerr << "got noise params: " << m_noiseParams.rows() << " x " << m_noiseParams.cols() << "\n";
2421 lock.unlock();
2422 }
2423 return 0;
2424}
2425
2427{
2428 static_cast<void>( dummy );
2429
2430 std::cerr << "Got WFS avg: " << wfsavgShmimMonitorT::m_width << " x " << wfsavgShmimMonitorT::m_height << '\n';
2431 return 0;
2432}
2433
2434int modalGainOpt::processImage( void *curr_src, const wfsavgShmimT &dummy )
2435{
2436 static_cast<void>( dummy );
2437
2438 m_wfsavg = mx::improc::eigenMap<float>(
2440
2441 if( m_wfsavg.rows() == m_wfsmask.rows() && m_wfsavg.cols() == m_wfsmask.cols() )
2442 {
2443 m_counts = ( m_wfsavg * m_wfsmask ).sum();
2444
2445 // std::cerr << "counts: " << m_counts << '\n';
2446 }
2447
2448 return 0;
2449}
2450
2452{
2453 static_cast<void>( dummy );
2454
2455 std::cerr << "Got WFS mask: " << wfsmaskShmimMonitorT::m_width << " x " << wfsmaskShmimMonitorT::m_height << '\n';
2456 return 0;
2457}
2458
2459int modalGainOpt::processImage( void *curr_src, const wfsmaskShmimT &dummy )
2460{
2461 static_cast<void>( dummy );
2462
2463 m_wfsmask = mx::improc::eigenMap<float>(
2465
2466 m_npix = m_wfsmask.sum();
2467
2468 if( m_wfsavg.rows() == m_wfsmask.rows() && m_wfsavg.cols() == m_wfsmask.cols() )
2469 {
2470 m_counts = ( m_wfsavg * m_wfsmask ).sum();
2471 }
2472
2473 return 0;
2474}
2475
2477{
2478 static std::vector<bool> logged( 50, false );
2479
2480 int L = 0;
2481 if( m_clPSDs.rows() == 0 || m_clPSDs.cols() == 0 ) // somehow here without any data
2482 {
2483 if( !logged[L] )
2484 {
2485 log<software_error>( { __FILE__, __LINE__, "PSDs have not been updated" } );
2486 }
2487 logged[L] = true;
2488 return -1;
2489 }
2490 logged[L++] = false;
2491
2492 if( static_cast<size_t>( m_clPSDs.rows() ) != m_freq.size() )
2493 {
2494 if( !logged[L] )
2495 {
2496 log<software_error>( { __FILE__, __LINE__, "PSDs and freq size mismatch" } );
2497 }
2498 logged[L] = true;
2499 return -1;
2500 }
2501 logged[L++] = false;
2502
2503 if( m_nModes != m_gainFacts.size() )
2504 {
2505 if( !logged[L] )
2506 {
2507 log<software_error>( { __FILE__, __LINE__, "PSDs and gains number of modes mismatch" } );
2508 }
2509 logged[L] = true;
2510 return -1;
2511 }
2512 logged[L++] = false;
2513
2514 if( m_nModes != m_multFacts.size() )
2515 {
2516 if( !logged[L] )
2517 {
2518 log<software_error>( { __FILE__, __LINE__, "PSDs and mult coeffs number of modes mismatch" } );
2519 }
2520 logged[L] = true;
2521 return -1;
2522 }
2523 logged[L++] = false;
2524
2525 if( m_nModes != m_gainCals.size() )
2526 {
2527 if( !logged[L] )
2528 {
2529 log<software_error>( { __FILE__, __LINE__, "PSDs and gain cals number of modes mismatch" } );
2530 }
2531 logged[L] = true;
2532 return -1;
2533 }
2534 logged[L++] = false;
2535
2536 if( m_nModes != m_gainCalFacts.size() )
2537 {
2538 if( !logged[L] )
2539 {
2540 log<software_error>( { __FILE__, __LINE__, "PSDs and gain cal facts number of modes mismatch" } );
2541 }
2542 logged[L] = true;
2543 return -1;
2544 }
2545 logged[L++] = false;
2546
2547 if( m_nModes != m_taus.size() )
2548 {
2549 if( !logged[L] )
2550 {
2551 log<software_error>( { __FILE__, __LINE__, "Loop taus have not been set" } );
2552 }
2553 logged[L] = true;
2554 return -1;
2555 }
2556 logged[L++] = false;
2557
2558 /*if( m_clPSDs.cols() != m_noiseParams.cols() || m_noiseParams.rows() != 3 )
2559 {
2560 if( !logged[L] )
2561 {
2562 log<software_error>( { __FILE__, __LINE__, "noise params have not been set" } );
2563 }
2564 logged[L] = true;
2565 return -1;
2566 }
2567 logged[L++] = false;*/
2568
2569 if( m_fps <= 0 )
2570 {
2571 if( !logged[L] )
2572 {
2573 log<software_error>( { __FILE__, __LINE__, "Loop fps has not been set" } );
2574 }
2575 logged[L] = true;
2576 return -1;
2577 }
2578 logged[L++] = false;
2579
2580 if( m_olPSDStream == nullptr )
2581 {
2582 if( !logged[L] )
2583 {
2584 log<software_error>( { __FILE__, __LINE__, "m_olPSDStream is not allocated" } );
2585 }
2586 logged[L] = true;
2587 return -1;
2588 }
2589 logged[L++] = false;
2590
2591 if( m_noisePSDStream == nullptr )
2592 {
2593 if( !logged[L] )
2594 {
2595 log<software_error>( { __FILE__, __LINE__, "m_noisePSDStream is not allocated" } );
2596 }
2597 logged[L] = true;
2598 return -1;
2599 }
2600 logged[L++] = false;
2601
2602 if( m_clXferCurrentStream == nullptr )
2603 {
2604 if( !logged[L] )
2605 {
2606 log<software_error>( { __FILE__, __LINE__, "m_clXferCurrentStream is not allocated" } );
2607 }
2608 logged[L] = true;
2609 return -1;
2610 }
2611 logged[L++] = false;
2612
2613 if( m_clXferSIStream == nullptr )
2614 {
2615 if( !logged[L] )
2616 {
2617 log<software_error>( { __FILE__, __LINE__, "m_clXferSIStream is not allocated" } );
2618 }
2619 logged[L] = true;
2620 return -1;
2621 }
2622 logged[L++] = false;
2623
2624 if( m_optGainStream == nullptr )
2625 {
2626 if( !logged[L] )
2627 {
2628 log<software_error>( { __FILE__, __LINE__, "optGainsStream is not allocated" } );
2629 }
2630 logged[L] = true;
2631 return -1;
2632 }
2633 logged[L++] = false;
2634
2635 if( m_optGainSIStream == nullptr )
2636 {
2637 if( !logged[L] )
2638 {
2639 log<software_error>( { __FILE__, __LINE__, "optGainsStream SI is not allocated" } );
2640 }
2641 logged[L] = true;
2642 return -1;
2643 }
2644 logged[L++] = false;
2645
2646 // Check the PC shmims that we don't automatically create
2647 if( m_Na.size() != m_nModes || m_NaCurrent.size() != m_nModes || (size_t)m_as.cols() != m_nModes ||
2648 m_Nb.size() != m_nModes || m_NbCurrent.size() != m_nModes || (size_t)m_bs.cols() != m_nModes ||
2649 m_pcGainFacts.size() != m_nModes || m_pcMultFacts.size() != m_nModes )
2650 {
2651
2652 if( allocatePCShmims() < 0 )
2653 {
2654 log<software_error>( { __FILE__, __LINE__, "error allocating PC shmims" } );
2655 }
2656
2657 if( m_Na.size() != m_nModes || m_NaCurrent.size() != m_nModes || (size_t)m_as.cols() != m_nModes ||
2658 m_Nb.size() != m_nModes || m_NbCurrent.size() != m_nModes || (size_t)m_bs.cols() != m_nModes ||
2659 m_pcGainFacts.size() != m_nModes || m_pcMultFacts.size() != m_nModes )
2660 {
2661
2662 if( !logged[L] )
2663 {
2664 log<software_error>( { __FILE__, __LINE__, "PC shmims not allcoated" } );
2665 }
2666 logged[L] = true;
2667 return -1;
2668 }
2669 else
2670 {
2671 logged[L] = false;
2672 }
2673 }
2674 else
2675 {
2676 logged[L] = false;
2677 }
2678 ++L;
2679
2680 if( m_clXferLPStream == nullptr )
2681 {
2682 if( !logged[L] )
2683 {
2684 log<software_error>( { __FILE__, __LINE__, "m_clXferLPStream is not allocated" } );
2685 }
2686 logged[L] = true;
2687 return -1;
2688 }
2689 logged[L++] = false;
2690
2691 if( m_optGainLPStream == nullptr )
2692 {
2693 if( !logged[L] )
2694 {
2695 log<software_error>( { __FILE__, __LINE__, "m_optGainLPStream is not allocated" } );
2696 }
2697 logged[L] = true;
2698 return -1;
2699 }
2700 logged[L++] = false;
2701
2702 if( m_nModes != m_optGainLP.size() )
2703 {
2704 if( !logged[L] )
2705 {
2706 log<software_error>( { __FILE__, __LINE__, "m_optGainLP is not allocated" } );
2707 }
2708 logged[L] = true;
2709 return -1;
2710 }
2711 logged[L++] = false;
2712
2713 return 0;
2714}
2715
2717{
2718 float mc = m_noiseParams( 1, n ) * m_counts / m_emg;
2719
2720 float snr2 = ( mc * mc ) / ( mc + m_npix * pow( m_noiseParams( 2, n ) / m_emg, 2 ) );
2721
2722 return 0.05 * m_noiseParams( 0, n ) / snr2;
2723}
2724
2729
2731{
2733
2734 while( m_goptThreadInit == true && shutdown() == 0 )
2735 {
2736 sleep( 1 );
2737 }
2738
2739 while( shutdown() == 0 )
2740 {
2741 timespec ts;
2742 XWC_SEM_WAIT_TS_RETVOID( ts, 1, 0 );
2743
2744 if( sem_timedwait( &m_goptSemaphore, &ts ) == 0 )
2745 {
2746 std::lock_guard<std::mutex> lock( m_goptMutex );
2747
2748 if( checkSizes() < 0 )
2749 {
2750 std::cerr << "check sizes failed\n";
2751 continue; // we just wait
2752 }
2753
2754 // m_doPCCalcs = false;
2755
2757 {
2758 if( m_goptCurrent.size() != m_gainFacts.size() )
2759 {
2760 m_freqUpdated = true; // force freq update in this case
2761 }
2762
2763 std::cerr << "updating gopt structures\n";
2764
2765 m_goptCurrent.resize( m_gainFacts.size() );
2766 m_goptSI.resize( m_gainFacts.size() );
2767 m_goptLP.resize( m_gainFacts.size() );
2768 m_linPred.resize( m_gainFacts.size() );
2769
2770 for( size_t n = 0; n < m_goptCurrent.size(); ++n )
2771 {
2772 m_goptCurrent[n].Ti( 1.0 / m_fps );
2773 m_goptCurrent[n].tau( m_taus[n] );
2774
2775 m_goptSI[n].Ti( 1.0 / m_fps );
2776 m_goptSI[n].tau( m_taus[n] );
2777
2778 m_goptLP[n].Ti( 1.0 / m_fps );
2779 m_goptLP[n].tau( m_taus[n] );
2780
2781 if( !m_pcOn )
2782 {
2783 m_goptCurrent[n].setLeakyIntegrator( m_mult * m_multFacts[n] );
2784 }
2785 else
2786 {
2787 std::vector<float> ta( m_NaCurrent[n] );
2788 for( size_t m = 0; m < ta.size(); ++m )
2789 {
2790 ta[m] = m_as( m, n );
2791 }
2792 m_goptCurrent[n].a( ta );
2793
2794 std::vector<float> tb( m_NbCurrent[n] );
2795 for( size_t m = 0; m < tb.size(); ++m )
2796 {
2797 tb[m] = m_bs( m, n );
2798 }
2799 m_goptCurrent[n].b( tb );
2800
2801 m_goptCurrent[n].remember( m_pcMult * m_pcMultFacts[n] );
2802 }
2803
2804 m_goptSI[n].setLeakyIntegrator( m_mult * m_multFacts[n] );
2805
2806 if( m_freqUpdated )
2807 {
2808 m_goptCurrent[n].f( m_freq );
2809 m_goptSI[n].f( m_freq );
2810 m_goptLP[n].f( m_freq );
2811 }
2812
2813 m_gmaxSI[n] = m_goptSI[n].maxStableGain();
2814 }
2815
2816 m_goptUpdated = false;
2817 m_pcgoptUpdated = false;
2818 m_freqUpdated = false;
2819
2820 std::cerr << "done.\n";
2821 }
2822
2824 if( m_updating )
2825 {
2826 continue;
2827 }
2828
2829 timePointT t0 = std::chrono::steady_clock::now();
2830
2832
2833 int off = 0;
2834 int offLP = 0;
2835
2836#pragma omp parallel for num_threads( 15 )
2837 for( size_t n = 0; n < m_goptCurrent.size(); ++n )
2838 {
2839 if( m_updating || m_shutdown )
2840 {
2841 continue; // don't break b/c of omp
2842 }
2843
2844 if( !m_pcOn )
2845 {
2847
2848 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2849 {
2850 m_clXferCurrent( f, n ) =
2852 }
2853 }
2854 else
2855 {
2856 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2857 {
2858 m_clXferCurrent( f, n ) =
2860 }
2861 }
2862
2864 // Calculate the OL PSD with the current gopt (PC or SI)
2866 if( !m_loop )
2867 {
2869 for( size_t f = 1; f < m_goptCurrent[n].f_size(); ++f )
2870 {
2871 m_olPSDs[n][f] = m_clPSDs( f, n ) / og2;
2872 }
2873 }
2874 else
2875 {
2877 for( size_t f = 1; f < m_goptCurrent[n].f_size(); ++f )
2878 {
2879 m_olPSDs[n][f] = ( m_clPSDs( f, n ) / og2 ) / m_clXferCurrent( f, n );
2880 }
2881 }
2882
2884
2885 m_olPSDs[n][0] = m_olPSDs[n][1];
2886
2887 bool flagOff = false;
2888 float extrap = 0;
2889
2890 // Calculate the noise as the 25th percentile in log10
2891 // this is to avoid spikes that make the noise too high
2892 size_t f0 = 0.5 * m_goptCurrent[n].f_size();
2893 size_t f1 = m_goptCurrent[n].f_size();
2894
2895 std::vector<float> npsd( f1 - f0 );
2896 for( size_t f = f0; f < f1; ++f )
2897 {
2898 npsd[f - f0] = log10( m_olPSDs[n][f] );
2899 }
2900 float pct = 0.25;
2901
2903 if( n < 2 )
2904 {
2905 pct = 0.05;
2906 }
2907
2908 auto nth = npsd.begin() + pct * ( f1 - f0 );
2909 std::nth_element( npsd.begin(), nth, npsd.end() );
2910
2912
2913 float noise = pow( 10, *nth );
2914 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2915 {
2916 m_nPSDs[n][f] = noise;
2917 }
2918
2919 if( m_extrapOL )
2920 {
2921 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2922 {
2923 m_olPSDs[n][f] = m_olPSDs[n][f] - m_nPSDs[n][f];
2924 }
2925
2927
2928 // Calculate an average extrapolation point referenced to 1 Hz
2929
2930 extrap = 0;
2931 int nexp = 0;
2932 int noff = 0;
2933 for( size_t f = 1; f < 0.05 * m_freq.size(); ++f )
2934 {
2935 // This is a threshold for "too noisy"
2936 ///\todo make configurable
2937 if( m_olPSDs[n][f] <= 0.1 * m_nPSDs[n][f] )
2938 {
2939 ++noff;
2940 continue;
2941 }
2942 extrap += log10( m_olPSDs[n][f] * pow( m_freq[f] / m_freq[1], 8. / 3. ) );
2943 ++nexp;
2944 }
2945
2946 extrap = pow( 10, extrap / nexp );
2947
2949 if( noff > 0.5 * ( 0.05 * m_freq.size() - 1 ) && n > 1 )
2950 {
2951 flagOff = true;
2952 }
2953 }
2954
2955 // flagOff = false;
2956
2958
2959 if( flagOff )
2960 {
2962 #pragma omp critical
2963 {
2964 ++off;
2965 }
2966
2967 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2968 {
2969 m_olPSDs[n][f] = m_nPSDs[n][f];
2970 }
2971
2972 m_modeVarOL[n] = mx::sigproc::psdVar( m_freq, m_olPSDs[n] );
2973
2974 m_optGainSI[n] = 0;
2976
2977 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
2978 {
2979 m_clXferSI( f, n ) = 1;
2980 }
2981
2982 m_timesOnSI[n] = 0;
2983 }
2984 else
2985 {
2987
2988 if( m_extrapOL )
2989 {
2990 std::vector<float> tosmooth( m_nFreq );
2991
2992 for( size_t f = 0; f < m_nFreq; ++f )
2993 {
2994 if( m_olPSDs[n][f] < 0 )
2995 {
2996 tosmooth[f] = extrap * pow( m_freq[1] / m_freq[f], 8. / 3. );
2997 }
2998 else
2999 {
3000 tosmooth[f] = m_olPSDs[n][f];
3001 }
3002 }
3003
3004 std::vector<float> l10( m_nFreq ), smol( m_nFreq );
3005 std::vector<int> smw( m_nFreq );
3006 smw[0] = 2;
3007 smw[1] = 2;
3008 l10[0] = log10( tosmooth[0] );
3009 l10[1] = log10( tosmooth[1] );
3010
3011 for( size_t f = 2; f < smw.size(); ++f )
3012 {
3013 smw[f] = 2 + static_cast<float>( f ) / 10;
3014 l10[f] = log10( tosmooth[f] );
3015 }
3016
3017 mx::math::vectorSmoothMean( smol, l10, smw );
3018
3019 for( size_t f = 0; f < m_nFreq; ++f )
3020 {
3021 smol[f] = pow( 10, smol[f] );
3022 }
3023
3024 for( size_t f = 0; f < m_nFreq; ++f )
3025 {
3026 if( m_olPSDs[n][f] < m_nPSDs[n][f] )
3027 {
3028 m_olPSDs[n][f] = smol[f];
3029 }
3030 }
3031 }
3032
3034 m_modeVarOL[n] = mx::sigproc::psdVar( m_freq, m_olPSDs[n] );
3035
3036 m_optGainSI[n] =
3037 m_goptSI[n].optGainOpenLoop( m_modeVarSI[n], m_olPSDs[n], m_nPSDs[n], m_gmaxSI[n], false );
3038
3039 if( ( m_modeVarSI[n] - m_modeVarOL[n] ) / m_modeVarOL[n] > -0.001 )
3040 {
3041 #pragma omp critical
3042 {
3043 ++off;
3044 }
3045
3047 m_optGainSI[n] = 0;
3049
3050 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3051 {
3052 m_clXferSI( f, n ) = 1;
3053 }
3054
3055 m_timesOnSI[n] = 0;
3056 }
3057 else if( m_timesOnSI[n] < 5 )
3058 {
3059 #pragma omp critical
3060 {
3061 ++off;
3062 }
3063
3065 // Would be on, but we debounce
3066 m_optGainSI[n] = 0;
3068
3069 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3070 {
3071 m_clXferSI( f, n ) = 1;
3072 }
3073
3074 ++m_timesOnSI[n];
3075 }
3076 else
3077 {
3079 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3080 {
3081 m_clXferSI( f, n ) = m_goptSI[n].clETF2( f, m_optGainSI[n] );
3082 }
3083
3084 ++m_timesOnSI[n];
3085 }
3086 }
3087
3088 if( m_doPCCalcs && !flagOff )
3089 {
3091 if( m_regScale[n] == -999 || m_regCounter[n] >= m_nRegCycles )
3092 {
3094 float min_sc;
3095 float gmax_lp = 0;
3096
3098 m_optGainLP[n],
3099 m_modeVarLP[n],
3100 min_sc,
3101 m_goptLP[n],
3102 m_olPSDs[n],
3103 m_nPSDs[n],
3104 m_Na[n] ) < 0 )
3105 {
3107
3108 m_optGainLP[n] = 0;
3110
3111 ///\todo what to do about coeffs?
3112 }
3113
3115
3116 if( m_regScale[n] == -999 )
3117 {
3120 }
3121 else
3122 {
3124 m_regCounter[n] = 0;
3125 }
3126
3127 m_regScale[n] = min_sc;
3128 m_gmaxLP[n] = gmax_lp;
3129 }
3130 else
3131 {
3133 // use pre-regularized version
3134 float psdReg = m_olPSDs[n][0];
3136 m_olPSDs[n], m_nPSDs[n], psdReg * pow( 10, -m_regScale[n] / 10 ), m_Na[n] ) < 0 )
3137 {
3138 m_optGainLP[n] = 0;
3140
3141 ///\todo what to do about coeffs?
3142 }
3143 else
3144 {
3145 m_goptLP[n].a( m_linPred[n].m_lp.m_c );
3146 m_goptLP[n].b( m_linPred[n].m_lp.m_c );
3147
3148 m_optGainLP[n] = m_goptLP[n].optGainOpenLoop(
3149 m_modeVarLP[n], m_olPSDs[n], m_nPSDs[n], m_gmaxLP[n], false );
3150 }
3151 ++m_regCounter[n];
3152 }
3153
3155
3156 /*if( ( m_modeVarLP[n] - m_modeVarOL[n] ) / m_modeVarOL[n] > -0.001 )
3157 {
3158 m_optGainLP[n] = 0;
3159 m_modeVarLP[n] = m_modeVarOL[n];
3160
3161 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3162 {
3163 m_clXferLP( f, n ) = 1;
3164 }
3165
3166 m_timesOnLP[n] = 0;
3167 }
3168 else*/
3169 if( ( m_modeVarLP[n] - m_modeVarSI[n] ) / m_modeVarSI[n] > -0.001 )
3170 {
3171 #pragma omp critical
3172 {
3173 ++offLP;
3174 }
3175
3176
3180
3181 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3182 {
3183 m_clXferLP( f, n ) = m_clXferSI( f, n );
3184 }
3185
3186 m_timesOnLP[n] = 0;
3187 }
3188 else if( m_timesOnLP[n] < 5 )
3189 {
3190 #pragma omp critical
3191 {
3192 ++offLP;
3193 }
3194
3195
3199
3200 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3201 {
3202 m_clXferLP( f, n ) = m_clXferSI( f, n );
3203 }
3204
3205 ++m_timesOnLP[n];
3206 }
3207 else
3208 {
3210 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3211 {
3212 m_clXferLP( f, n ) = m_goptLP[n].clETF2( f, m_optGainLP[n] );
3213 }
3214
3215 ++m_timesOnLP[n];
3216 }
3217 }
3218 else
3219 {
3220 #pragma omp critical
3221 {
3222 ++offLP;
3223 }
3224
3226 m_optGainLP[n] = 0;
3227
3228 ++m_regCounter[n];
3229
3230 for( size_t f = 0; f < m_goptCurrent[n].f_size(); ++f )
3231 {
3232 m_clXferLP( f, n ) = 1;
3233 }
3234
3235 m_timesOnLP[n] = 0;
3236 }
3237
3239 }
3240
3242
3245
3246
3247
3248
3249 if( m_updating )
3250 {
3251 continue; // don't break b/c of omp
3252 }
3253
3254 timePointT t1 = std::chrono::steady_clock::now();
3255 durationT dt = t1 - t0;
3256
3257 std::cerr << "Optimization took " << dt.count() << " seconds\n";
3258
3259 /*float totVar = 0;
3260 for( size_t n = 0; n < m_modeVarSI.size(); ++n )
3261 {
3262 totVar += m_modeVarSI[n];
3263 }*/
3264
3265 // std::cerr << "total variance: " << totVar << '\n';
3266
3267 float *f = m_optGainStream->array.F;
3268 float *fSI = m_optGainSIStream->array.F;
3269 float *fmaxSI = m_maxGainSIStream->array.F;
3270 float *fLP = m_optGainLPStream->array.F;
3271 float *fmaxLP = m_optGainLPStream->array.F;
3272
3273 mx::improc::eigenMap<float> mvs( m_modevarStream->array.F, 3, m_modeVarSI.size() );
3274
3275 m_optGainStream->md->write = 1;
3276 m_optGainSIStream->md->write = 1;
3277 m_maxGainSIStream->md->write = 1;
3278 m_optGainLPStream->md->write = 1;
3279 m_maxGainLPStream->md->write = 1;
3280 m_modevarStream->md->write = 1;
3281
3282 for( size_t n = 0; n < m_optGainSI.size(); ++n )
3283 {
3284 // if( m_timesOnSI[n] > 5 )
3285 {
3287 fSI[n] = f[n];
3289 }
3290
3291 // if( m_timesOnLP[n] > 5 )
3292 {
3295 }
3296
3297 mvs( 0, n ) = m_modeVarOL[n];
3298 mvs( 1, n ) = m_modeVarSI[n];
3299 mvs( 2, n ) = m_modeVarLP[n];
3300 }
3301
3308
3309 if( m_autoUpdate || m_updateOnce || m_dump )
3310 {
3311 float *f = gainFactShmimMonitorT::m_imageStream.array.F;
3312
3314 if( m_dump )
3315 {
3316 for( size_t n = 0; n < m_nModes; ++n )
3317 {
3318 // if( m_timesOnSI[n] > 5 )
3319 {
3321 }
3322 }
3323 }
3324 else
3325 {
3326 for( size_t n = 0; n < m_nModes; ++n )
3327 {
3328 // if( m_timesOnSI[n] > 5 )
3329 {
3330 f[n] = f[n] + m_gainGain *
3332 f[n] );
3333 }
3334 }
3335 }
3336
3338
3339 if( m_doPCCalcs )
3340 {
3342 float *fa = acoeffShmimMonitorT::m_imageStream.array.F;
3343 float *fb = bcoeffShmimMonitorT::m_imageStream.array.F;
3344
3348
3349 if( m_dump )
3350 {
3351 for( size_t n = 0; n < m_optGainLP.size(); ++n )
3352 {
3353 // if( m_timesOnLP[n] > 5 )
3354 {
3356
3357 // To be safe we treat this as if Na and Nb can be different, but they can't be.
3358 fa[n] = m_Na[n];
3359 fb[n] = m_Nb[n];
3360 for( uint32_t k = 0; k < m_Na[n]; ++k )
3361 {
3362 fa[1 + k] = m_goptLP[n].a()[k];
3363 }
3364 for( uint32_t k = m_Na[n]; k < acoeffShmimMonitorT::m_width - 1; ++k )
3365 {
3366 fa[1 + k] = 0;
3367 }
3368
3369 for( uint32_t k = 0; k < m_Nb[n]; ++k )
3370 {
3371 fb[1 + k] = m_goptLP[n].b()[k];
3372 }
3373 for( uint32_t k = m_Nb[n]; k < bcoeffShmimMonitorT::m_width - 1; ++k )
3374 {
3375 fb[1 + k] = 0;
3376 }
3377 }
3378 }
3379 }
3380 else
3381 {
3382 for( size_t n = 0; n < m_optGainSI.size(); ++n )
3383 {
3384 // if( m_timesOnLP[n] > 5 )
3385 {
3386 fpc[n] = fpc[n] +
3387 m_gainGain *
3389 fpc[n] );
3390
3391 // To be safe we treat this as if Na and Nb can be different, but they can't be.
3392 fa[n] = m_Na[n];
3393 fb[n] = m_Nb[n];
3394 for( uint32_t k = 0; k < m_Na[n]; ++k )
3395 {
3396 fa[1 + k] = fa[1 + k] + m_gainGain * ( m_goptLP[n].a()[k] - fa[1 + k] );
3397 }
3398 for( uint32_t k = m_Na[n]; k < acoeffShmimMonitorT::m_width - 1; ++k )
3399 {
3400 fa[1 + k] = 0;
3401 }
3402
3403 for( uint32_t k = 0; k < m_Nb[n]; ++k )
3404 {
3405 fb[1 + k] = fb[1 + k] + m_gainGain * ( m_goptLP[n].b()[k] - fb[1 + k] );
3406 }
3407 for( uint32_t k = m_Nb[n]; k < bcoeffShmimMonitorT::m_width - 1; ++k )
3408 {
3409 fb[1 + k] = 0;
3410 }
3411 }
3412 }
3413 }
3414
3418 }
3419
3420 if( m_dump )
3421 {
3422 log<text_log>( "gains updated by dump", logPrio::LOG_NOTICE );
3423 m_dump = false;
3424 }
3425 else if( m_updateOnce && !m_autoUpdate )
3426 {
3427 log<text_log>( "gains updated once", logPrio::LOG_NOTICE );
3428 }
3429
3430 m_updateOnce = false;
3431 }
3432
3433 if( m_loop & m_autoUpdate )
3434 {
3435 m_sinceChange = -1;
3436 }
3437
3438 // Update OL PSDs and Transfer Functions
3439 m_olPSDStream->md->write = 1;
3440 m_noisePSDStream->md->write = 1;
3441 m_clXferCurrentStream->md->write = 1;
3442 m_clXferSIStream->md->write = 1;
3443 m_clXferLPStream->md->write = 1;
3444
3445 for( size_t q = 0; q < m_olPSDs.size(); ++q )
3446 {
3447 memcpy( m_olPSDStream->array.F + q * m_olPSDs[0].size(),
3448 m_olPSDs[q].data(),
3449 m_olPSDs[0].size() * sizeof( float ) );
3450
3451 // m_noisePSDStream->array.F[q] = m_nPSDs[q][0];
3452 memcpy( m_noisePSDStream->array.F + q * m_nPSDs[0].size(),
3453 m_nPSDs[q].data(),
3454 m_nPSDs[0].size() * sizeof( float ) );
3455 }
3456
3457 memcpy( m_clXferCurrentStream->array.F,
3458 m_clXferCurrent.data(),
3459 m_clXferCurrent.rows() * m_clXferCurrent.cols() * sizeof( float ) );
3460 memcpy(
3461 m_clXferSIStream->array.F, m_clXferSI.data(), m_clXferSI.rows() * m_clXferSI.cols() * sizeof( float ) );
3462 memcpy(
3463 m_clXferLPStream->array.F, m_clXferLP.data(), m_clXferLP.rows() * m_clXferLP.cols() * sizeof( float ) );
3464
3470 }
3471 else
3472 {
3473 /* Check for why we timed out */
3474 /* ETIMEDOUT just means keep waiting */
3475 if( errno == ETIMEDOUT )
3476 {
3477 // Could Update gopts if needed (requires size checks and requires mutex lock)
3478 // Probably not worth it for pred. control anyway.
3479 continue;
3480 }
3481
3482 /* EINTER probably indicates time to shutdown, loop wil exit if m_shutdown is set */
3483 if( errno == EINTR )
3484 {
3485 continue;
3486 }
3487
3488 /*Otherwise, report an error.*/
3489 log<software_error>( { __FILE__, __LINE__, errno, "sem_timedwait" } );
3490 break;
3491 }
3492 }
3493}
3494
3495INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_autoUpdate )( const pcf::IndiProperty &ipRecv )
3496{
3497 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_autoUpdate, ipRecv );
3498
3499 if( ipRecv.find( "toggle" ) )
3500 {
3501 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3502 {
3503 if( !m_autoUpdate )
3504 {
3505 log<text_log>( "updating gains", logPrio::LOG_NOTICE );
3506 }
3507 m_autoUpdate = true;
3508 }
3509 else
3510 {
3511 if( m_autoUpdate )
3512 {
3513 log<text_log>( "stopped updating gains", logPrio::LOG_NOTICE );
3514 }
3515 m_autoUpdate = false;
3516 }
3517 }
3518
3519 return 0;
3520}
3521
3522INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_updateOnce )( const pcf::IndiProperty &ipRecv )
3523{
3524 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_updateOnce, ipRecv );
3525
3526 if( ipRecv.find( "request" ) )
3527 {
3528 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3529 {
3530 m_updateOnce = true;
3531 }
3532 else
3533 {
3534 m_updateOnce = false;
3535 }
3536 }
3537
3538 return 0;
3539}
3540
3541INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_dump )( const pcf::IndiProperty &ipRecv )
3542{
3543 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_dump, ipRecv );
3544
3545 if( ipRecv.find( "request" ) )
3546 {
3547 if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
3548 {
3549 m_dump = true;
3550 }
3551 else
3552 {
3553 m_dump = false;
3554 }
3555 }
3556
3557 return 0;
3558}
3559
3560INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_opticalGain )( const pcf::IndiProperty &ipRecv )
3561{
3562 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_opticalGain, ipRecv );
3563
3564 float target;
3565 if( indiTargetUpdate( m_indiP_opticalGain, target, ipRecv, true ) < 0 )
3566 {
3567 log<software_error>( { __FILE__, __LINE__ } );
3568 return -1;
3569 }
3570
3571 m_opticalGain = target;
3572
3573 return 0;
3574}
3575
3576INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_opticalGainUpdate )( const pcf::IndiProperty &ipRecv )
3577{
3578 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_opticalGainUpdate, ipRecv );
3579
3580 if(ipRecv.find("toggle"))
3581 {
3582 if(ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
3583 {
3584 m_opticalGainUpdate = false;
3585 }
3586 else
3587 {
3588 m_opticalGainUpdate = true;
3589
3590 if(m_opticalGainSource > 0 && m_opticalGainSource < 1)
3591 {
3592 m_opticalGain = m_opticalGainSource;
3593 }
3594 }
3595 }
3596
3597 return 0;
3598}
3599
3600INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_opticalGainSource )( const pcf::IndiProperty &ipRecv )
3601{
3602 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_opticalGainSource, ipRecv );
3603
3604 if( ipRecv.find( m_opticalGainElement ) )
3605 {
3606 float opticalg = ipRecv[m_opticalGainElement].get<float>();
3607
3608 opticalg = (floor(opticalg * 100 + 0.5))/100.;
3609
3610 if(opticalg > 0 && opticalg < 1)
3611 {
3612 m_opticalGainSource = opticalg;
3613 }
3614
3615 if(m_opticalGainUpdate)
3616 {
3617 m_opticalGain = m_opticalGainSource;
3618 }
3619 }
3620 return 0;
3621}
3622
3623INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_gainGain )( const pcf::IndiProperty &ipRecv )
3624{
3625 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_gainGain, ipRecv );
3626
3627 float target;
3628 if( indiTargetUpdate( m_indiP_gainGain, target, ipRecv, true ) < 0 )
3629 {
3630 log<software_error>( { __FILE__, __LINE__ } );
3631 return -1;
3632 }
3633
3634 m_gainGain = target;
3635
3636 return 0;
3637}
3638
3639INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_emg )( const pcf::IndiProperty &ipRecv )
3640{
3641 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_emg, ipRecv );
3642
3643 if( ipRecv.find( "current" ) )
3644 {
3645 float emg = ipRecv["current"].get<float>();
3646
3647 if( emg != m_emg )
3648 {
3649 m_emg = emg;
3650 std::cerr << "Got EMG: " << m_emg << '\n';
3651 }
3652 }
3653 return 0;
3654}
3655
3656INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_psdTime )( const pcf::IndiProperty &ipRecv )
3657{
3658 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdTime, ipRecv );
3659
3660 if( ipRecv.find( "current" ) )
3661 {
3662 float psdTime = ipRecv["current"].get<float>();
3663
3664 if( psdTime != m_psdTime )
3665 {
3666 m_updating = true;
3667 std::lock_guard<std::mutex> lock( m_goptMutex );
3668 m_updating = true;
3669
3670 m_psdTime = psdTime;
3671
3672 m_sinceChange = -1;
3673 m_updating = false;
3674
3675 std::cerr << "Got psdTime: " << m_psdTime << '\n';
3676 }
3677 }
3678 return 0;
3679}
3680
3681INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_psdAvgTime )( const pcf::IndiProperty &ipRecv )
3682{
3683 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_psdAvgTime, ipRecv );
3684
3685 if( ipRecv.find( "current" ) )
3686 {
3687 float psdAvgTime = ipRecv["current"].get<float>();
3688
3689 if( psdAvgTime != m_psdAvgTime )
3690 {
3691 m_updating = true;
3692 std::lock_guard<std::mutex> lock( m_goptMutex );
3693 m_updating = true;
3694
3695 m_psdAvgTime = psdAvgTime;
3696
3697 m_sinceChange = -1;
3698 m_updating = false;
3699
3700 std::cerr << "Got psdAvgTime: " << m_psdAvgTime << '\n';
3701 }
3702 }
3703
3704 return 0;
3705}
3706
3707INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_loop )( const pcf::IndiProperty &ipRecv )
3708{
3709 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_loop, ipRecv );
3710
3711 if( ipRecv.find( "toggle" ) )
3712 {
3713 bool state;
3714
3715 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3716 {
3717 state = true;
3718 }
3719 else
3720 {
3721 state = false;
3722 }
3723
3724 if( state != m_loop )
3725 {
3726 m_updating = true;
3727 std::lock_guard<std::mutex> lock( m_goptMutex );
3728 m_updating = true;
3729
3730 m_loop = state;
3731
3732 if( !m_loop )
3733 {
3734 m_autoUpdate = false;
3735 m_dump = false;
3736 }
3737
3738 m_sinceChange = -1;
3739 m_updating = false;
3740 std::cerr << "Got loop: " << m_loop << '\n';
3741 }
3742 }
3743
3744 return 0;
3745}
3746
3747INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_siGain )( const pcf::IndiProperty &ipRecv )
3748{
3749 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_siGain, ipRecv );
3750
3751 if( ipRecv.find( "current" ) )
3752 {
3753 float gain = ipRecv["current"].get<float>();
3754
3755 if( gain != m_gain && !m_pcOn )
3756 {
3757 m_updating = true;
3758 std::lock_guard<std::mutex> lock( m_goptMutex );
3759 m_updating = true;
3760
3761 m_gain = gain;
3762
3763 if( m_loop )
3764 {
3765 m_sinceChange = -1;
3766 }
3767
3768 m_updating = false;
3769
3770 std::cerr << "Got gain: " << m_gain << '\n';
3771 }
3772 else
3773 {
3774 m_gain = gain; // for the m_pcOn case
3775 }
3776 }
3777
3778 return 0;
3779}
3780
3781INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_siMult )( const pcf::IndiProperty &ipRecv )
3782{
3783 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_siMult, ipRecv );
3784
3785 if( ipRecv.find( "current" ) )
3786 {
3787 float mc = ipRecv["current"].get<float>();
3788
3789 if( mc != m_mult && !m_pcOn )
3790 {
3791 m_updating = true;
3792 std::lock_guard<std::mutex> lock( m_goptMutex );
3793 m_updating = true;
3794
3795 m_mult = mc;
3796
3797 if( m_loop )
3798 {
3799 m_sinceChange = -1;
3800 }
3801
3802 m_goptUpdated = true;
3803 m_updating = false;
3804 std::cerr << "Got mc: " << m_mult << '\n';
3805 }
3806 else
3807 {
3808 m_mult = mc; // for the m_pcOn case
3809 }
3810 }
3811
3812 return 0;
3813}
3814
3815INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_pcGain )( const pcf::IndiProperty &ipRecv )
3816{
3817 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_pcGain, ipRecv );
3818
3819 if( ipRecv.find( "current" ) )
3820 {
3821 float gain = ipRecv["current"].get<float>();
3822
3823 if( gain != m_pcGain && m_pcOn )
3824 {
3825 m_updating = true;
3826 std::lock_guard<std::mutex> lock( m_goptMutex );
3827 m_updating = true;
3828
3829 m_pcGain = gain;
3830
3831 if( m_loop )
3832 {
3833 m_sinceChange = -1;
3834 }
3835
3836 m_updating = false;
3837
3838 std::cerr << "Got pc gain: " << m_pcGain << '\n';
3839 }
3840 else
3841 {
3842 m_pcGain = gain; // for the !m_pcOn case
3843 }
3844 }
3845
3846 return 0;
3847}
3848
3849INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_pcMult )( const pcf::IndiProperty &ipRecv )
3850{
3851 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_pcMult, ipRecv );
3852
3853 if( ipRecv.find( "current" ) )
3854 {
3855 float mc = ipRecv["current"].get<float>();
3856
3857 if( mc != m_pcMult && m_pcOn )
3858 {
3859 m_updating = true;
3860 std::lock_guard<std::mutex> lock( m_goptMutex );
3861 m_updating = true;
3862
3863 m_pcMult = mc;
3864
3865 if( m_loop )
3866 {
3867 m_sinceChange = -1;
3868 }
3869
3870 m_pcgoptUpdated = true;
3871 m_updating = false;
3872 std::cerr << "Got pc mc: " << m_pcMult << '\n';
3873 }
3874 else
3875 {
3876 m_pcMult = mc; // for the !m_pcOn case
3877 }
3878 }
3879
3880 return 0;
3881}
3882
3883INDI_SETCALLBACK_DEFN( modalGainOpt, m_indiP_pcOn )( const pcf::IndiProperty &ipRecv )
3884{
3885 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_pcOn, ipRecv );
3886
3887 if( ipRecv.find( "toggle" ) )
3888 {
3889 bool state;
3890
3891 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3892 {
3893 state = true;
3894 }
3895 else
3896 {
3897 state = false;
3898 }
3899
3900 if( state != m_pcOn )
3901 {
3902 m_updating = true;
3903 std::lock_guard<std::mutex> lock( m_goptMutex );
3904 m_updating = true;
3905
3906 m_pcOn = state;
3907
3908 m_sinceChange = -1;
3909 m_updating = false;
3910 std::cerr << "Got pcOn: " << std::boolalpha << m_pcOn << '\n';
3911 }
3912 }
3913
3914 return 0;
3915}
3916
3917INDI_NEWCALLBACK_DEFN( modalGainOpt, m_indiP_extrapOL )( const pcf::IndiProperty &ipRecv )
3918{
3919 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_extrapOL, ipRecv );
3920
3921 if( ipRecv.find( "toggle" ) )
3922 {
3923 int ext;
3924
3925 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
3926 {
3927 ext = true;
3928 }
3929 else
3930 {
3931 ext = false;
3932 }
3933
3934 if( ext != m_extrapOL )
3935 {
3936 m_updating = true;
3937 std::lock_guard<std::mutex> lock( m_goptMutex );
3938 m_updating = true;
3939
3940 m_extrapOL = ext;
3941
3942 m_sinceChange = -1;
3943 m_updating = false;
3944 std::cerr << "Got extrap: " << m_extrapOL << '\n';
3945 }
3946 }
3947
3948 return 0;
3949}
3950} // namespace app
3951} // namespace MagAOX
3952
3953#endif // modalGainOpt_hpp
#define XWCAPP_THREAD_CHECK(thrdSt, thrdName)
Error handling wrapper for checking on thread status with tryjoin.
#define XWCAPP_THREAD_START(thrdSt, thrdInit, thrdId, thrdProp, thrdPrio, thrdCpuset, thrdName, thrdStart)
Error handling wrapper for the threadStart function of the XWCApp.
#define XWCAPP_THREAD_STOP(thrdSt)
Error handlng wrapper for stopping a thread.
The base-class for XWCTk applications.
stateCodes::stateCodeT state()
Get the current state code.
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
int shutdown()
Get the value of the shutdown flag.
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI switch element value if it has changed.
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
uint32_t m_width
The width of the images in the stream.
uint32_t m_height
The height of the images in the stream.
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
int create(uint32_t width, uint32_t height, uint32_t depth, uint8_t datatype, void *initData=nullptr)
Create the image.
The MagAO-X PSD-based gain optimizer.
pcf::IndiProperty m_indiP_emg
std::vector< mx::AO::analysis::clGainOpt< float > > m_goptCurrent
Each mode gets its own gain optimizer.
eigenImage< float > m_wfsavg
pcf::IndiProperty m_indiP_gainGain
bool m_opticalGainUpdate
Flag controlling whether optical gain is automatically updated;.
std::vector< uint32_t > m_NbCurrent
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_updateOnce)
dev::shmimMonitor< modalGainOpt, numpccoeffShmimT > numpccoeffShmimMonitorT
mx::improc::eigenImage< float > m_clXferCurrent
~modalGainOpt() noexcept
D'tor, declared and defined for noexcept.
std::vector< float > m_modeVarCL
IMAGE * m_clXferLPStream
The ImageStreamIO shared memory buffer to publish the LP ETF.
dev::shmimMonitor< modalGainOpt, wfsmaskShmimT > wfsmaskShmimMonitorT
std::vector< int > m_regCounter
Counters to track when this mode was last regularized.
std::vector< float > m_gmaxLP
The previously calculated maximum gains for LP.
std::vector< float > m_pcGainFacts
std::vector< mx::AO::analysis::clGainOpt< float > > m_goptLP
std::chrono::time_point< std::chrono::steady_clock > timePointT
eigenImage< float > m_as
dev::shmimMonitor< modalGainOpt, pcGainFactShmimT > pcGainFactShmimMonitorT
pid_t m_goptThreadID
gain optimization thread PID.
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_autoUpdate)
dev::shmimMonitor< modalGainOpt, pcMultFactShmimT > pcMultFactShmimMonitorT
std::vector< float > m_taus
dev::shmimMonitor< modalGainOpt, gainCalShmimT > gainCalShmimMonitorT
int allocate(const psdShmimT &)
std::vector< int > m_timesOnLP
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_dump)
std::vector< float > m_freq
pcf::IndiProperty m_indiP_siGain
virtual int appLogic()
Implementation of the FSM for modalGainOpt.
std::vector< float > m_modeVarSI
pcf::IndiProperty m_indiP_autoUpdate
int m_loopNum
The number of the loop. Used to set shmim names, as in aolN_mgainfact.
std::vector< float > m_gainCalFacts
pcf::IndiProperty m_indiP_dump
std::vector< float > m_gainFacts
dev::shmimMonitor< modalGainOpt, bcoeffShmimT > bcoeffShmimMonitorT
int checkSizes()
Check that all sizes and allocations have occurred.
int m_goptThreadPrio
Priority of the gain optimization thread.
pcf::IndiProperty m_indiP_opticalGainUpdate
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_pcOn)
std::vector< float > m_optGainLP
sem_t m_goptSemaphore
Semaphore used to synchronize the psdShmim thread and the gopt thread.
IMAGE * m_optGainLPStream
The ImageStreamIO shared memory buffer to publish the LP optimal gains.
std::string m_goptThreadCpuset
The cpuset to use for the gain optimization thread.
pcf::IndiProperty m_indiP_loop
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_siGain)
mx::improc::eigenImage< float > m_clPSDs
dev::shmimMonitor< modalGainOpt, wfsavgShmimT > wfsavgShmimMonitorT
std::vector< float > m_gmaxSI
The previously calculated maximum gains for LP.
pcf::IndiProperty m_indiP_siMult
pcf::IndiProperty m_indiP_pcOn
IMAGE * m_olPSDStream
The ImageStreamIO shared memory buffer to publish the open loop PSDs.
IMAGE * m_clXferSIStream
The ImageStreamIO shared memory buffer to publish the SI ETF.
std::vector< std::vector< float > > m_olPSDs
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_loop)
eigenImage< float > m_wfsmask
pcf::IndiProperty m_indiP_modesOn
bool m_goptUpdated
Tracks if a parameter has updated requiring updates to the m_gopt entries.
mx::improc::eigenImage< float > m_clXferLP
dev::shmimMonitor< modalGainOpt, freqShmimT > freqShmimMonitorT
std::string m_loopName
The name of the loop control INDI device name.
std::chrono::duration< double > durationT
dev::shmimMonitor< modalGainOpt, gainFactShmimT > gainFactShmimMonitorT
IMAGE * m_maxGainLPStream
The ImageStreamIO shared memory buffer to publish the LP max gains.
bool m_updateOnce
Flag to trigger a single update with gain.
pcf::IndiProperty m_indiP_updateOnce
dev::shmimMonitor< modalGainOpt, tauShmimT > tauShmimMonitorT
eigenImage< float > m_noiseParams
std::vector< mx::AO::analysis::clAOLinearPredictor< float > > m_linPred
pcf::IndiProperty m_goptThreadProp
The property to hold the gain optimization thread details.
static void goptThreadStart(modalGainOpt *p)
Gain Optimization thread starter function.
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_psdAvgTime)
eigenImage< float > m_bs
bool m_dump
Flag to trigger a single update with no gain.
mx::improc::eigenImage< float > m_clXferSI
pcf::IndiProperty m_indiP_psdAvgTime
std::vector< float > m_modeVarLP
IMAGE * m_optGainStream
The ImageStreamIO shared memory buffer to publish the optimal gains.
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_opticalGainSource)
std::vector< float > m_optGainSI
IMAGE * m_optGainSIStream
The ImageStreamIO shared memory buffer to publish the SI optimal gains.
IMAGE * m_clXferCurrentStream
The ImageStreamIO shared memory buffer to publish the SI ETF.
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_extrapOL)
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_psdTime)
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
std::vector< float > m_gainCals
virtual int appStartup()
Startup function.
std::thread m_goptThread
The gain optimization thread.
void goptThreadExec()
Gain optimization thread function.
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_opticalGain)
pcf::IndiProperty m_indiP_opticalGain
dev::shmimMonitor< modalGainOpt, psdShmimT > psdShmimMonitorT
bool m_updating
Flag used to indicate to the goptThread that it should stop calculations ASAP.
virtual int appShutdown()
Shutdown the app.
bool m_goptThreadInit
Initialization flag for the gain optimization thread.
int processImage(void *curr_src, const psdShmimT &)
std::vector< float > m_modeVarOL
std::vector< float > m_pcMultFacts
std::mutex m_goptMutex
Mutex for synchronizing updates.
dev::shmimMonitor< modalGainOpt, multFactShmimT > multFactShmimMonitorT
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_siMult)
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_emg)
bool m_autoUpdate
Flag controlling whether gains are automatically updated.
dev::shmimMonitor< modalGainOpt, gainCalFactShmimT > gainCalFactShmimMonitorT
std::vector< uint32_t > m_NaCurrent
std::vector< int > m_timesOnSI
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_pcMult)
IMAGE * m_modevarStream
The ImageStreamIO shared memory buffer to publish the LP optimal gains.
int m_extrapOL
Which extrapolation method to use for the OL PSD. 0 is none. The only other option is 1.
std::vector< float > m_multFacts
dev::shmimMonitor< modalGainOpt, acoeffShmimT > acoeffShmimMonitorT
std::vector< float > m_regScale
The regularization scale factors for each mode.
bool m_pcgoptUpdated
Tracks if a parameter has updated requiring updates to the m_gopt entries.
pcf::IndiProperty m_indiP_opticalGainSource
std::vector< uint32_t > m_Nb
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_opticalGainUpdate)
std::vector< std::vector< float > > m_nPSDs
IMAGE * m_maxGainSIStream
The ImageStreamIO shared memory buffer to publish the SI max gains.
float m_gainGain
The gain to use for closed-loop gain updates. Default is 0.1.
pcf::IndiProperty m_indiP_pcGain
std::vector< mx::AO::analysis::clGainOpt< float > > m_goptSI
dev::shmimMonitor< modalGainOpt, noiseShmimT > noiseShmimMonitorT
INDI_SETCALLBACK_DECL(modalGainOpt, m_indiP_pcGain)
INDI_NEWCALLBACK_DECL(modalGainOpt, m_indiP_gainGain)
std::vector< uint32_t > m_Na
pcf::IndiProperty m_indiP_extrapOL
pcf::IndiProperty m_indiP_psdTime
int m_nRegCycles
How often to regularize each mode.
pcf::IndiProperty m_indiP_pcMult
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
#define CREATE_REG_INDI_NEW_TOGGLESWITCH(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
#define CREATE_REG_INDI_NEW_REQUESTSWITCH(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
#define CREATE_REG_INDI_NEW_NUMBERF(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as float, using the standard callback na...
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
#define REG_INDI_SETPROP(prop, devName, propName)
Register a SET INDI property with the class, using the standard callback name.
#define CREATE_REG_INDI_RO_NUMBER(prop, name, label, group)
Create and register a RO INDI property as a number, using the standard callback name.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
#define INDI_IDLE
Definition indiUtils.hpp:27
#define INDI_OK
Definition indiUtils.hpp:28
#define MGO_BREADCRUMB
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:59
const pcf::IndiProperty & ipRecv
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:26
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
#define XWC_SEM_WAIT_TS_RETVOID(ts, sec, nsec)
Add the wait time to a timespec for a sem_timedwait call, with no value returned on error.
Definition semUtils.hpp:20
#define SHMIMMONITORT_APP_STARTUP(SHMIMMONITORT)
Call shmimMonitorT::appStartup with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_UPDATE_INDI(SHMIMMONITORT)
Call shmimMonitorT::updateINDI with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_SETUP_CONFIG(SHMIMMONITORT, cfig)
Call shmimMonitorT::setupConfig with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_APP_LOGIC(SHMIMMONITORT)
Call shmimMonitorT::appLogic with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_APP_SHUTDOWN(SHMIMMONITORT)
Call shmimMonitorT::appShutodwn with error checking for a typedef-ed shmimMonitor.
#define SHMIMMONITORT_LOAD_CONFIG(SHMIMMONITORT, cfig)
Call shmimMonitorT::loadConfig with error checking for a typedef-ed shmimMonitor.
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
static std::string configSection()
static std::string indiPrefix()
static std::string indiPrefix()
static std::string configSection()
Software CRITICAL log entry.
Software ERR log entry.