API
 
Loading...
Searching...
No Matches
dmRecon.hpp
Go to the documentation of this file.
1/** \file dmRecon.hpp
2 * \brief The MagAO-X DM shape reconstructor
3 *
4 * \ingroup dmRecon_files
5 */
6
7#ifndef dmRecon_hpp
8#define dmRecon_hpp
9
10#include <limits>
11
12#include <mx/improc/eigenCube.hpp>
13#include <mx/improc/eigenImage.hpp>
14#include <mx/sigproc/gramSchmidt.hpp>
15#include <mx/math/templateBLAS.hpp>
16
17#include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
18#include "../../magaox_git_version.h"
19
20#include <mx/math/cuda/cudaPtr.hpp>
21#include <mx/math/cuda/cublasHandle.hpp>
22#include <mx/math/cuda/templateCublas.hpp>
23#include <mx/math/eigenLapack.hpp>
24#include <mx/sigproc/basisUtils2D.hpp>
25
26namespace MagAOX
27{
28namespace app
29{
30
31/** \defgroup dmRecon DM Shape Reconstructor
32 * \brief Reconstruct the wavefront corresponding to a DM shape
33 *
34 * <a href="../handbook/operating/software/apps/dmRecon.html">Application Documentation</a>
35 *
36 * \ingroup apps
37 *
38 */
39
40/** \defgroup dmRecon_files DM Shape Reconstructor Files
41 * \ingroup dmRecon
42 */
43
45{
46 static std::string configSection()
47 {
48 return "dmModes";
49 };
50
51 static std::string indiPrefix()
52 {
53 return "dmModes";
54 };
55};
56
58{
59 static std::string configSection()
60 {
61 return "dmMask";
62 };
63
64 static std::string indiPrefix()
65 {
66 return "dmMask";
67 };
68};
69
71{
72 static std::string configSection()
73 {
74 return "dmCommand";
75 };
76
77 static std::string indiPrefix()
78 {
79 return "dmCommand";
80 };
81};
82
83/** MagAO-X application to perform wavefront reconstruction from a DM surface
84 *
85 * \ingroup dmRecon
86 *
87 */
88class dmRecon : public MagAOXApp<true>,
89 public dev::shmimMonitor<dmRecon, dmModesShmimT>,
90 public dev::shmimMonitor<dmRecon, dmMaskShmimT>,
91 public dev::shmimMonitor<dmRecon, dmCommandShmimT>,
92 public dev::frameGrabber<dmRecon>,
93 public dev::telemeter<dmRecon>
94{
95 // Give the test harness access.
96 friend class dmRecon_test;
97
100
103
106
107 friend class dev::frameGrabber<dmRecon>;
109
110 static constexpr bool c_frameGrabber_flippable = false;
111
112 friend class dev::telemeter<dmRecon>;
114
115 /// Floating point type in which to do all calculations.
116 typedef float realT;
117
118 protected:
119 /** \name Configurable Parameters
120 *@{
121 */
122
123 int m_loopNumber{ 1 }; ///< The loop number. Default is 1 as in aol1.
124
125 std::string m_respMPath; ///< Optional response matrix. If set then CM modes are converted by this response.
126
127 std::string m_fpsSource{ "camwfs" }; /**< Device name for getting fps of the loop.
128 This device must have *.fps.current. Default is camwfs*/
129
130 int m_numModes{ 0 }; ///< Number of modes to reconstruct. If 0 (default) all modes in CM are used.
131
133 0 }; /**< Number of modes to use for pseudo-inverse truncation. If 0 (default) all modes are used.*/
134
135 uint16_t m_gpuIndex{ 0 }; /**< Index of the GPU to use for calculations */
136
137 bool m_useGPU{ false }; /**< Flag controlling whether the GPU is used for calculations */
138
139 ///@}
140
141 mx::improc::eigenImage<float> m_respM;
142
144
146
148
149 float m_fps{ 0 }; ///< Current FPS from the FPS source.
150
151 mx::improc::eigenImage<realT> m_PInv; ///< The pseudo-inverse
152
153 // clang-format off
154 #ifdef MXLIB_CUDA
155
156 mx::cuda::cudaPtr<realT> m_PInv_GPU; ///< The pseudo-inverse on the GPU
157
158 #endif
159 // clang-format on
160
161 bool m_dmModesReady{ false }; ///< Flag indicating that the DM modes are ready for processing
162
163 mx::improc::eigenImage<float> m_mask;
164
165 std::vector<size_t> m_maskIDX; ///< The index of masked pixels
166
167 bool m_dmMaskReady{ false }; ///< Flag indicating that the DM mask is ready for processing
168
169 bool m_commandReady{ false }; ///< Flag indicating that all sizes match and arrays are ready for processing
170
171 bool m_fgWaiting{ false }; ///< Flag indicating that the FG thread is waiting for the command thread
172
173 mx::improc::eigenImage<float> m_command; ///< The DM command, copied out of the incoming shmim
174
175 mx::improc::eigenImage<float> m_modevals; ///< The calculated mode amplitudes
176
177 bool m_writeDMf{ false };
178
179 std::string m_monShmimName;
180 mx::improc::milkImage<float> m_modevalMon; ///< The actual calculated modevals.
181
182 mx::improc::milkImage<float> m_modeval;
183 mx::improc::milkImage<float> m_modevalDiff;
184
185 // clang-format off
186 #ifdef MXLIB_CUDA
187
188 mx::cuda::cudaPtr<float> m_command_GPU;
189
190 mx::cuda::cudaPtr<float> m_modevals_GPU;
191
192 #endif
193 // clang-format on
194
195 sem_t m_smSemaphore{ 0 }; ///< Semaphore used to synchronize the fg thread and the dm command thread.
196
197 bool m_updated{ false }; ///< Flag indicating that the mode vals have been updated
198
199 mx::cuda::cublasHandle m_cublas; ///< Handle for the cuBLAS library
200
201 public:
202 /// Default c'tor.
203 dmRecon();
204
205 /// D'tor, declared and defined for noexcept.
207 {
208 }
209
210 virtual void setupConfig();
211
212 /// Implementation of loadConfig logic, separated for testing.
213 /** This is called by loadConfig().
214 */
215 int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration
216 from which to load values*/
217 );
218
219 virtual void loadConfig();
220
221 /// Startup function
222 /**
223 *
224 */
225 virtual int appStartup();
226
227 /// Implementation of the FSM for dmRecon.
228 /**
229 * \returns 0 on no critical error
230 * \returns -1 on an error requiring shutdown
231 */
232 virtual int appLogic();
233
234 /// Shutdown the app.
235 /**
236 *
237 */
238 virtual int appShutdown();
239
240 /// Set the GPU index
241 /** Uses m_gpuIndex. On errors it sets m_useGPU to false.
242 *
243 */
244 int setGPU();
245
246 /// Allocate method for the dm modes shmimMonitor
247 /**
248 * \returns 0 on success
249 * \returns -1 on an error
250 */
251 int allocate( const dmModesShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/ );
252
253 /// Process images for the dm modes shmimMonitor
254 /**
255 * \returns 0 on sucess
256 * \returns -1 on an error
257 */
258 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
259 const dmModesShmimT & ///< [in] tag to differentiate shmimMonitor parents.
260 );
261
262 /// Allocate method for the dm mask shmimMonitor
263 /**
264 * \returns 0 on success
265 * \returns -1 on an error
266 */
267 int allocate( const dmMaskShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/ );
268
269 /// Process images for the dm mask shmimMonitor
270 /**
271 * \returns 0 on sucess
272 * \returns -1 on an error
273 */
274 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
275 const dmMaskShmimT & ///< [in] tag to differentiate shmimMonitor parents.
276 );
277
278 /// Allocate method for the dm command shmimMonitor
279 /**
280 * \returns 0 on success
281 * \returns -1 on an error
282 */
283 int allocate( const dmCommandShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/ );
284
285 /// Process images for the dm command shmimMonitor
286 /**
287 * \returns 0 on sucess
288 * \returns -1 on an error
289 */
290 int processImage( void *curr_src, ///< [in] pointer to start of current frame.
291 const dmCommandShmimT & ///< [in] tag to differentiate shmimMonitor parents.
292 );
293
294 /** \name Framegrabber Interface */
295 /**
296 * @{
297 */
298
300
301 float fps();
302
303 int startAcquisition();
304
306
307 int loadImageIntoStream( void *dest );
308
309 int reconfig();
310
311 ///@}
312 protected:
313 /** \name INDI Interface
314 *
315 * @{
316 */
317 pcf::IndiProperty m_indiP_fpsSource;
319
320 pcf::IndiProperty m_indiP_fps;
321
322 pcf::IndiProperty m_indiP_writeDMf;
324
325 ///@}
326
327 /** \name Telemeter Interface
328 *
329 * @{
330 */
331
332 int checkRecordTimes();
333
334 int recordTelem( const telem_fgtimings * );
335
336 ///@}
337};
338
339inline dmRecon::dmRecon() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
340{
342
343 return;
344}
345
347{
348 config.add( "recon.loopNumber",
349 "",
350 "recon.loopNumber",
351 argType::Required,
352 "recon",
353 "loopNumber",
354 false,
355 "int",
356 "The loop number. Default is 1 as in aol1." );
357
358 config.add( "recon.respMPath",
359 "",
360 "recon.respMPath",
361 argType::Required,
362 "recon",
363 "respMPath",
364 false,
365 "int",
366 "Optional response matrix. If set then CM modes are converted by this response." );
367
368 config.add( "recon.numModes",
369 "",
370 "recon.numModes",
371 argType::Required,
372 "recon",
373 "numModes",
374 false,
375 "int",
376 "Number of modes to reconstruct. If 0 (default) all modes in CM are used." );
377
378 config.add( "recon.inverseNumModes",
379 "",
380 "recon.inverseNumModes",
381 argType::Required,
382 "recon",
383 "inverseNumModes",
384 false,
385 "int",
386 "Number of modes to use for pseudo-inverse truncation. If 0 (default) all modes are used." );
387
388 config.add( "recon.fpsSource",
389 "",
390 "recon.fpsSource",
391 argType::Required,
392 "recon",
393 "fpsSource",
394 false,
395 "string",
396 "Device name for getting fps of the loop. This device should have *.fps.current. "
397 "Default is camwfs" );
398
399 config.add( "recon.gpuIndex",
400 "",
401 "recon.gpuIndex",
402 argType::Required,
403 "recon",
404 "gpuIndex",
405 false,
406 "int",
407 "Index of the GPU to use for calculations. Default is 0." );
408
409 config.add( "recon.useGPU",
410 "",
411 "recon.useGPU",
412 argType::Required,
413 "recon",
414 "useGPU",
415 false,
416 "bool",
417 "Flag controlling whether the GPU is used for calculations. Default is false." );
418
420
422
424
426
427 TELEMETER_SETUP_CONFIG( config );
428}
429
430inline int dmRecon::loadConfigImpl( mx::app::appConfigurator &_config )
431{
432 _config( m_loopNumber, "recon.loopNumber" );
433 _config( m_respMPath, "recon.respMPath" );
434 _config( m_numModes, "recon.numModes" );
435 _config( m_inverseNumModes, "recon.inverseNumModes" );
436 _config( m_fpsSource, "recon.fpsSource" );
437 _config( m_gpuIndex, "recon.gpuIndex" );
438 _config( m_useGPU, "recon.useGPU" );
439
440 std::string loopName = std::format( "aol{}", m_loopNumber );
441
442 dmModesSMT::m_shmimName = loopName + "_CMmodesDM";
445
446 dmCommandSMT::m_shmimName = std::format( "dm{:02}disp_delta", m_loopNumber );
449
450 dmMaskSMT::m_shmimName = std::format( "dm{:02}disp_actmask", m_loopNumber );
453
454 frameGrabberT::m_shmimName = loopName + "_modevalDMf";
456
458
460
461 return 0;
462}
463
465{
466 loadConfigImpl( config );
467}
468
470{
471 REG_INDI_SETPROP( m_indiP_fpsSource, m_fpsSource, std::string( "fps" ) );
472
474 m_indiP_fps.add( pcf::IndiElement( "current" ) );
476 {
477 log<software_error>( { "" } );
478 return -1;
479 }
480
482
483 if( sem_init( &m_smSemaphore, 0, 0 ) < 0 )
484 {
485 log<software_critical>( { errno, "Initializing S.M. semaphore" } );
486 return -1;
487 }
488
489 if( m_respMPath != "" )
490 {
491 mx::fits::fitsFile<float> ff;
492 ff.read( m_respM, m_respMPath );
493
494 m_width = sqrt( m_respM.rows() );
496 }
497
503
505
507
509
510 return 0;
511}
512
514{
518
520
522
523 std::unique_lock<std::mutex> lock( m_indiMutex );
524
525 if( m_writeDMf )
526 {
527 updateSwitchIfChanged( m_indiP_writeDMf, "toggle", pcf::IndiElement::On );
528 }
529 else
530 {
531 updateSwitchIfChanged( m_indiP_writeDMf, "toggle", pcf::IndiElement::Off );
532 }
533
537
539
540 return 0;
541}
542
555
557{
558 // clang-format off
559 #ifdef MXLIB_CUDA // clang-format on
560
561 if( !m_useGPU )
562 {
563 return 0;
564 }
565
566 int deviceCount;
567 int devicecntMax = 100;
568
570
571 if( ce != cudaSuccess )
572 {
573
574 log<software_error>( { std::format( "cudaGetDeviceCount returned error: "
575 "[{}] {}\nNOT USING GPU",
577 cudaGetErrorString( ce ) ) } );
578
579 m_useGPU = false;
580 state( state(), true );
581 return -1;
582 }
583
584 std::string msg = std::format( "CUDA: found {} devices\n", deviceCount );
585
587 {
588 deviceCount = 0;
589 msg += " greater than devicecntMax\n";
590 }
591 if( deviceCount < 0 )
592 {
593 msg += " less than zero\n";
594 }
595
596 if( deviceCount == 0 )
597 {
598 msg += " no devices found!\nNOT USING GPU";
600 m_useGPU = false;
601 state( state(), true );
602 return -1;
603 }
604
605 for( int k = 0; k < deviceCount; k++ )
606 {
609
610 if( ce != cudaSuccess )
611 {
612 msg += std::format( "cudaGetDeviceProperties returned error: "
613 "[{}] {}\nNOT USING GPU",
617 m_useGPU = false;
618 state( state(), true );
619 return -1;
620 }
621
622 int clockRate;
624
625 if( ce != cudaSuccess )
626 {
627 msg += std::format( "cudaGetDeviceAttribute returned error: "
628 "[{}] {}\nNOT USING GPU",
632 m_useGPU = false;
633 state( state(), true );
634 return -1;
635 }
636
637 msg += std::format( " Device {} / {} [ {} ] has compute capability {}.{}.\n",
638 k + 1,
640 deviceProp.name,
641 deviceProp.major,
642 deviceProp.minor );
643
644 msg += std::format( " Total amount of global memory: {} MBytes\n",
645 (float)deviceProp.totalGlobalMem / 1048576.0f );
646
647 msg += std::format( " Multiprocessors: {}\n", deviceProp.multiProcessorCount );
648 msg += std::format( " Clock rate: {} MHz ({} GHz)\n", clockRate * 1e-3f, clockRate * 1e-6f );
649 }
650
651 if( m_gpuIndex >= deviceCount )
652 {
653 msg += std::format( "gpuIndex = {} is not valid for {} devices\nNOT USING GPU", m_gpuIndex, deviceCount );
655 m_useGPU = false;
656 state( state(), true );
657 return -1;
658 }
659
661
662 if( ce != cudaSuccess )
663 {
664 msg += std::format( "cudaSetDevice returned error: "
665 "[{}] {}\nNOT USING GPU",
669 m_useGPU = false;
670 state( state(), true );
671 return -1;
672 }
673
674 msg += std::format( "Set GPU Index to device {} ( {} / {})\n", m_gpuIndex, m_gpuIndex + 1, deviceCount );
675
676 cublasStatus_t cbs = m_cublas.create();
677
679 {
680 msg += std::format( "cublasHandle create returned error: "
681 "[{}] {}\nNOT USING GPU",
685 m_useGPU = false;
686 state( state(), true );
687 return -1;
688 }
689
690 msg += " cuBLAS initialized";
691
693
694 return 0;
695
696 // clang-format off
697 #else // MXLIB_CUDA
698 // clang-format on
699
700 if( m_useGPU )
701 {
702 log<software_error>( { "mxlib was compiled without CUDA support. NOT USING GPU" } );
703
704 m_useGPU = false;
705 state( state(), true );
706 return -1;
707 }
708
709 return 0;
710
711 // clang-format off
712 #endif // MXLIB_CUDA
713 // clang-format on
714}
715
717{
718 m_dmModesReady = false;
719
720 std::cerr << "modes not ready\n";
721
723
724 if( m_respM.rows() == 0 )
725 {
728 }
729
730 if( m_numModes == 0 )
731 {
733 }
734 else
735 {
738 {
740 }
741 }
742
743 // Can't process modes until mask is ready.
745 {
746 mx::sys::milliSleep( 1000 );
748 return 0; // This won't log an error, but setting m_restart will cause it to loop again until sizes match
749 }
750
751 // This will let us go on to processImage
752
753 // Do a type check for float
754 return 0;
755}
756
757int dmRecon::processImage( void *curr_src, const dmModesShmimT & )
758{
759 if( m_dmModesReady == true )
760 {
761 // This means new image has come in. We need to reset and restart everything.
765 return 0;
766 }
767
768 mx::improc::eigenCube<float> dmModes( dmModesSMT::m_width, dmModesSMT::m_height, m_depth );
769
770 for( size_t n = 0; n < dmModesSMT::m_width * dmModesSMT::m_height * m_depth; ++n )
771 {
772 dmModes.data()[n] = reinterpret_cast<float *>( curr_src )[n];
773 }
774
775 // Wait for m_commandReady to become false
776 while( m_commandReady == true && !m_shutdown && dmModesSMT::m_restart == false )
777 {
778 mx::sys::milliSleep( 1000 );
779 }
780
781 if( m_respM.rows() > 0 )
782 {
783 mx::improc::eigenCube<realT> tmpc;
784
785 int nr = sqrt( m_respM.rows() );
786
787 tmpc.resize( nr, nr, dmModes.planes() );
788
789 std::cerr << __LINE__ << '\n';
790
791 for( int p = 0; p < tmpc.planes(); ++p )
792 {
793 // cast to matrices for math
794 Eigen::Map<Eigen::Matrix<float,-1,-1>> outim(tmpc.image(p).data(), nr*nr,1);
795 Eigen::Map<Eigen::Matrix<float,-1,-1>> inim(dmModes.image(p).data(), dmModes.rows()*dmModes.cols(),1);
796
797 outim = (m_respM.matrix() * inim);
798
799 float norm = sqrt(tmpc.image(p).square().sum()/m_maskIDX.size());
800 float scale = sqrt(dmModes.image(p).square().sum()/ (dmModes.rows()*dmModes.cols()));
801
802 tmpc.image(p) *= scale/norm;
803 }
804
805 dmModes = tmpc;
806
807 mx::fits::fitsFile<float> ff;
808 ff.write( "wmodes.fits", dmModes );
809 }
810
811 mx::improc::eigenImage<float> maskedDMModes;
812
813 maskedDMModes.resize( m_maskIDX.size(), dmModes.planes() );
814
815 // Load only the unmasked pixels
816 for( int rr = 0; rr < maskedDMModes.cols(); ++rr )
817 {
818 for( size_t n = 0; n < m_maskIDX.size(); ++n )
819 {
820 maskedDMModes( n, rr ) = dmModes.image( rr ).data()[m_maskIDX[n]];
821 }
822 }
823
825
826 int nRejected;
827
828 realT maxCondition = -1 * m_inverseNumModes; // Specify number of modes to keep. If 0 it's all.
829
830 int rv = mx::math::eigenPseudoInverse( m_PInv, condition, nRejected, maskedDMModes, maxCondition );
831
832 if( rv < 0 )
833 {
834 log<software_error>( { 0, rv, "error in eigenPseudoInverse " } );
835 m_shutdown = 1;
836 return -1;
837 }
838
839 std::cerr << "PInv: " << m_PInv.rows() << ' ' << m_PInv.cols() << '\n';
840
841 mx::fits::fitsFile<float> ff;
842 ff.write( "PInv.fits", m_PInv );
843
844 log<text_log>( std::format( "Inverted CMmodesDM. Rejected {} "
845 "of {} modes, condition numer = {}",
846 nRejected,
847 dmModes.planes(),
848 condition ) );
849
850 m_dmModesReady = true;
851
852 std::cerr << "modes ready\n";
853 return 0;
854}
855
857{
858 m_dmMaskReady = false;
859
860 std::cerr << "mask not ready\n";
861
863
864 // Do a type check for float
865 return 0;
866}
867
868int dmRecon::processImage( void *curr_src, const dmMaskShmimT & )
869{
870 if( m_dmMaskReady == true )
871 {
872 // This means an new image has come in. We need to reset and restart everything.
875
877
878 return 0;
879 }
880
881 // Wait for m_commandReady to become false
882 while( m_commandReady == true && !m_shutdown && dmMaskSMT::m_restart == false )
883 {
884 mx::sys::milliSleep( 1000 );
885 }
886
887 m_mask =
888 mx::improc::eigenMap<float>( reinterpret_cast<float *>( curr_src ), dmMaskSMT::m_width, dmMaskSMT::m_height );
889
890 m_maskIDX.clear();
891
892 size_t n = 0;
893
894 int nmax = 0;
895 for( int rr = 0; rr < m_mask.rows(); ++rr )
896 {
897 for( int cc = 0; cc < m_mask.cols(); ++cc )
898 {
899 if( m_mask( rr, cc ) == 1 )
900 {
901 m_maskIDX.push_back( n );
902 nmax = n;
903 }
904
905 ++n;
906 }
907 }
908
909 std::cerr << n << ' ' << nmax << '\n';
910
911 std::cerr << "Got mask of size " << m_mask.rows() << " x " << m_mask.cols() << " with " << m_maskIDX.size()
912 << " good pixels.\n";
913
914 m_dmMaskReady = true;
915 std::cerr << "mask ready\n";
916 return 0;
917}
918
920{
921 // This is the only place that m_commandReady can be changed
922 m_commandReady = false;
923 std::cerr << "command not ready\n";
924
927 {
930
931 mx::sys::milliSleep( 1000 );
932
933 return 0; // This won't log an error, but setting m_restart will cause it to reconnect again until sizes match
934 }
935
936 if( !m_fgWaiting )
937 {
939 mx::sys::milliSleep( 1000 );
940
942 return 0; // This won't log an error, but setting m_restart will cause it to reconnect again until sizes match
943 }
944
945 m_command.resize( m_maskIDX.size(), 1 );
946
947 m_modevals.resize( m_PInv.rows(), 1 );
948
949 m_modevalMon.create( m_monShmimName, m_PInv.rows(), 1 );
950
951 m_modeval.open(std::format("aol{}_modevalDM", m_loopNumber));
952
953 m_modevalDiff.create( std::format("aol{}_modevalDMf_diff", m_loopNumber), m_PInv.rows(), 1 );
954
955 // clang-format off
956 #ifdef MXLIB_CUDA
957 // clang-format on
958
959 if( m_useGPU )
960 {
961 // Do all initializations and uploads here so it's in the right thread on the right device
962 if( setGPU() < 0 )
963 {
964 log<software_error>( { "setting GPU device failed." } );
965 m_useGPU = false;
966 state( state(), true );
967 return -1;
968 }
969
970 mx::error_t ec = m_PInv_GPU.upload( m_PInv.data(), m_PInv.rows(), m_PInv.cols() );
971
972 if( ec != mx::error_t::noerror )
973 {
974 return log<software_error, -1>( { std::format(
975 "error uploading PInv to GPU: [{}] {}", mx::errorName( ec ), mx::errorMessage( ec ) ) } );
976 }
977
978 ec = m_command_GPU.resize( m_command.rows() * m_command.cols() );
979 if( ec != mx::error_t::noerror )
980 {
981 return log<software_error, -1>( { std::format(
982 "error allocating command on GPU: [{}] {}", mx::errorName( ec ), mx::errorMessage( ec ) ) } );
983 }
984
985 ec = m_modevals_GPU.resize( m_modevals.rows() * m_modevals.cols() );
986 if( ec != mx::error_t::noerror )
987 {
988 return log<software_error, -1>( { std::format(
989 "error allocating modevals on GPU: [{}] {}", mx::errorName( ec ), mx::errorMessage( ec ) ) } );
990 }
991 }
992
993 // clang-format off
994 #endif // MXLIB_CUDA
995 // clang-format on
996
997 m_updated = false;
998 m_commandReady = true;
999
1000 std::cerr << "command ready\n";
1001
1002 return 0;
1003}
1004
1005int dmRecon::processImage( void *curr_src, const dmCommandShmimT & )
1006{
1007 if( !m_commandReady )
1008 {
1010 return 0;
1011 }
1012
1013 // Set atime to now
1015
1016 // extract masked pixels
1017 for( size_t n = 0; n < m_maskIDX.size(); ++n )
1018 {
1019 m_command( n, 0 ) = reinterpret_cast<float *>( curr_src )[m_maskIDX[n]];
1020 }
1021
1022 // clang-format off
1023 #ifdef MXLIB_CUDA // clang-format on
1024 if( !m_useGPU )
1025 {
1026 // CPU:
1027 m_modevals = ( m_PInv.matrix() * m_command.matrix() ).array();
1028 }
1029 else
1030 {
1031 // GPU:
1032 mx::error_t ec = m_command_GPU.upload( m_command.data() );
1033 if( ec != mx::error_t::noerror )
1034 {
1035 return log<software_error, -1>( { std::format(
1036 "error uploading command to GPU: [{}] {}", mx::errorName( ec ), mx::errorMessage( ec ) ) } );
1037 }
1038
1039 float alpha = 1;
1040 float beta = 0;
1041
1042 cublasStatus_t cbs = mx::cuda::cublasTgemv( m_cublas,
1044 m_PInv_GPU.rows(),
1045 m_PInv_GPU.cols(),
1046 &alpha,
1047 m_PInv_GPU.data(),
1048 m_PInv_GPU.rows(),
1049 m_command_GPU.data(),
1050 1,
1051 &beta,
1052 m_modevals_GPU.data(),
1053 1 );
1054
1055 if( cbs != CUBLAS_STATUS_SUCCESS )
1056 {
1057 return log<software_error, -1>( { std::format( "error downloading modevals from GPU: [{}] {}",
1059 cublasGetStatusString( cbs ) ) } );
1060 }
1061
1062 ec = m_modevals_GPU.download( m_modevals.data() );
1063 if( ec != mx::error_t::noerror )
1064 {
1065 return log<software_error, -1>( { std::format(
1066 "error downloading modevals from GPU: [{}] {}", mx::errorName( ec ), mx::errorMessage( ec ) ) } );
1067 }
1068 }
1069
1070 // clang-format off
1071 #else // MXLIB_CUDA
1072
1073 // CPU:
1074 m_modevals = (m_PInv.matrix() * m_command.matrix()).array()
1075
1076 #endif // MXLIB_CUDA
1077 // clang-format on
1078
1079 m_updated = true;
1080
1081 if( m_writeDMf )
1082 {
1083 // trigger framegrabber
1084 if( sem_post( &m_smSemaphore ) < 0 )
1085 {
1086 log<software_critical>( { errno, 0, "Error posting to semaphore" } );
1087 return -1;
1088 }
1089 }
1090
1091 // write to the monitor stream
1092 m_modevalMon.setWrite(1);
1093 m_modevalDiff.setWrite();
1094 for(uint32_t r = 0; r < m_modevalMon.rows(); ++r)
1095 {
1096 m_modevalMon(r,0) = m_modevals(r,0);
1097 m_modevalDiff(r,0) = m_modevals(r,0) - m_modeval(r,0);
1098 }
1099
1100 m_modevalMon.post();
1101 m_modevalDiff.post();
1102
1103 return 0;
1104}
1105
1107{
1108 if( !m_commandReady )
1109 {
1110 m_fgWaiting = true;
1111 mx::sys::milliSleep( 100 );
1112 return -1;
1113 }
1114
1115 m_fgWaiting = false;
1116
1120
1121 static int logged = 0;
1122
1123 if( frameGrabberT::m_imageStream != nullptr )
1124 {
1128 }
1129
1130 // b/c ImageStreamIO prints every single time, and latest version don't support stopping it yet, and that
1131 // isn't thread-safe-able anyway we do our own checks. This is the same code in ImageStreamIO_openIm...
1132 int SM_fd;
1133 char SM_fname[200];
1135 SM_fd = open( SM_fname, O_RDWR );
1136
1137 if( SM_fd == -1 )
1138 {
1139 if( !logged )
1140 {
1141 log<text_log>( "ImageStream " + frameGrabberT::m_shmimName + " not found (yet). Retrying . . .",
1143 logged = 1;
1144 }
1145
1146 return 1;
1147 }
1148
1149 // Found and opened, close it and then use ImageStreamIO
1150 logged = 0;
1151 close( SM_fd );
1152
1153 frameGrabberT::m_imageStream = reinterpret_cast<IMAGE *>( malloc( sizeof( IMAGE ) ) );
1154
1156 {
1158 {
1162
1163 return 1; // We just need to wait for the server process to finish startup.
1164 }
1165 else
1166 {
1167 char SM_fname[200];
1169
1170 struct stat buffer;
1171 int rv = stat( SM_fname, &buffer );
1172
1173 if( rv != 0 )
1174 {
1176 "Could not get inode for " + frameGrabberT::m_shmimName +
1177 ". Source process will need to be restarted." } );
1178
1180
1182
1184
1185 m_shutdown = true;
1186
1187 return -1;
1188 }
1189
1190 frameGrabberT::m_inode = buffer.st_ino;
1191 }
1192 }
1193 else
1194 {
1197
1198 return 1; // be patient
1199 }
1200
1201 return 0;
1202}
1203
1205{
1206 return m_fps;
1207}
1208
1210{
1211
1212 std::cerr << "startAcquisition\n";
1213 return 0;
1214}
1215
1217{
1218 timespec ts;
1219
1220 errno = 0;
1221 if( clock_gettime( CLOCK_REALTIME, &ts ) < 0 )
1222 {
1223 log<software_critical>( { errno, "clock_gettime" } );
1224 return -1;
1225 }
1226
1227 ts.tv_sec += 1;
1228
1229 if( !m_commandReady )
1230 {
1231 return 1;
1232 }
1233
1234 if( sem_timedwait( &m_smSemaphore, &ts ) == 0 )
1235 {
1236 if( m_updated && m_commandReady )
1237 {
1238 return 0;
1239 }
1240 else
1241 {
1242 return 1;
1243 }
1244 }
1245 else
1246 {
1247 return 1;
1248 }
1249
1250 return 0;
1251}
1252
1254{
1255 memcpy( dest, m_modevals.data(), m_modevals.rows() * m_modevals.cols() * sizeof( float ) );
1256
1257 return 0;
1258}
1259
1261{
1262 return 0;
1263}
1264
1265INDI_SETCALLBACK_DEFN( dmRecon, m_indiP_fpsSource )( const pcf::IndiProperty &ipRecv )
1266{
1267 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_fpsSource, ipRecv );
1268
1269 if( ipRecv.find( "current" ) != true ) // this isn't valid
1270 {
1271 return -1;
1272 }
1273
1274 std::lock_guard<std::mutex> guard( m_indiMutex );
1275
1276 realT fps = ipRecv["current"].get<float>();
1277
1278 if( fps != m_fps )
1279 {
1280 m_fps = fps;
1281 updateIfChanged( m_indiP_fps, "current", m_fps );
1282 }
1283
1284 return 0;
1285}
1286
1287INDI_NEWCALLBACK_DEFN( dmRecon, m_indiP_writeDMf )( const pcf::IndiProperty &ipRecv )
1288{
1289 INDI_VALIDATE_CALLBACK_PROPS( m_indiP_writeDMf, ipRecv );
1290
1291 if( ipRecv.find( "toggle" ) != true ) // this isn't valid
1292 {
1293 return -1;
1294 }
1295
1296 std::lock_guard<std::mutex> guard( m_indiMutex );
1297
1298 if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1299 {
1300 m_writeDMf = true;
1301 log<text_log>( "writing modevalDMf", logPrio::LOG_INFO );
1302 updateSwitchIfChanged( m_indiP_writeDMf, "toggle", pcf::IndiElement::On );
1303 }
1304 else
1305 {
1306 m_writeDMf = false;
1307 log<text_log>( "not writing modevalDMf", logPrio::LOG_INFO );
1308 updateSwitchIfChanged( m_indiP_writeDMf, "toggle", pcf::IndiElement::Off );
1309 }
1310
1311 return 0;
1312}
1313
1318
1320{
1321 return recordFGTimings( true );
1322}
1323
1324} // namespace app
1325} // namespace MagAOX
1326
1327#endif // dmRecon_hpp
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.
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.
int createROIndiNumber(pcf::IndiProperty &prop, const std::string &propName, const std::string &propLabel="", const std::string &propGroup="")
Create a ReadOnly INDI Number property.
int registerIndiPropertyReadOnly(pcf::IndiProperty &prop)
Register an INDI property which is read only.
std::mutex m_indiMutex
Mutex for locking INDI communications.
timespec m_currImageTimestamp
The timestamp of the current image.
uint32_t m_width
The width of the image, once deinterlaced etc.
bool m_ownShmim
Flag controlling if the shmim is owned. If true it will be destroyed as needed.
uint8_t m_dataType
The ImageStreamIO type code.
bool m_reconfig
Flag to set if a camera reconfiguration requires a framegrabber reset.
ino_t m_inode
The inode of the image stream file.
IMAGE * m_imageStream
The ImageStreamIO shared memory buffer.
uint32_t m_height
The height of the image, once deinterlaced etc.
uint32_t m_depth
The depth of the circular buffer in the stream.
uint32_t m_width
The width of the images in the stream.
uint32_t m_height
The height of the images in the stream.
bool m_restart
Flag indicating tha the shared memory should be reinitialized.
int m_loopNumber
The loop number. Default is 1 as in aol1.
Definition dmRecon.hpp:123
virtual int appLogic()
Implementation of the FSM for dmRecon.
Definition dmRecon.hpp:513
mx::improc::eigenImage< realT > m_PInv
The pseudo-inverse.
Definition dmRecon.hpp:151
float realT
Floating point type in which to do all calculations.
Definition dmRecon.hpp:116
mx::improc::milkImage< float > m_modevalMon
The actual calculated modevals.
Definition dmRecon.hpp:180
mx::improc::eigenImage< float > m_respM
Definition dmRecon.hpp:141
virtual void setupConfig()
Definition dmRecon.hpp:346
~dmRecon() noexcept
D'tor, declared and defined for noexcept.
Definition dmRecon.hpp:206
std::string m_monShmimName
Definition dmRecon.hpp:179
INDI_SETCALLBACK_DECL(dmRecon, m_indiP_fpsSource)
int processImage(void *curr_src, const dmModesShmimT &)
Process images for the dm modes shmimMonitor.
Definition dmRecon.hpp:757
bool m_dmModesReady
Flag indicating that the DM modes are ready for processing.
Definition dmRecon.hpp:161
int setGPU()
Set the GPU index.
Definition dmRecon.hpp:556
std::string m_fpsSource
Definition dmRecon.hpp:127
dev::frameGrabber< dmRecon > frameGrabberT
Definition dmRecon.hpp:108
pcf::IndiProperty m_indiP_writeDMf
Definition dmRecon.hpp:322
pcf::IndiProperty m_indiP_fpsSource
Definition dmRecon.hpp:317
bool m_fgWaiting
Flag indicating that the FG thread is waiting for the command thread.
Definition dmRecon.hpp:171
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
Definition dmRecon.hpp:430
mx::improc::milkImage< float > m_modevalDiff
Definition dmRecon.hpp:183
INDI_NEWCALLBACK_DECL(dmRecon, m_indiP_writeDMf)
virtual int appStartup()
Startup function.
Definition dmRecon.hpp:469
bool m_updated
Flag indicating that the mode vals have been updated.
Definition dmRecon.hpp:197
mx::improc::eigenImage< float > m_modevals
The calculated mode amplitudes.
Definition dmRecon.hpp:175
static constexpr bool c_frameGrabber_flippable
Definition dmRecon.hpp:110
mx::improc::eigenImage< float > m_mask
Definition dmRecon.hpp:163
std::vector< size_t > m_maskIDX
The index of masked pixels.
Definition dmRecon.hpp:165
dmRecon()
Default c'tor.
Definition dmRecon.hpp:339
mx::improc::eigenImage< float > m_command
The DM command, copied out of the incoming shmim.
Definition dmRecon.hpp:173
dev::shmimMonitor< dmRecon, dmMaskShmimT > dmMaskSMT
Definition dmRecon.hpp:102
dev::shmimMonitor< dmRecon, dmModesShmimT > dmModesSMT
Definition dmRecon.hpp:99
virtual int appShutdown()
Shutdown the app.
Definition dmRecon.hpp:543
mx::cuda::cublasHandle m_cublas
Handle for the cuBLAS library.
Definition dmRecon.hpp:199
dev::telemeter< dmRecon > telemeterT
Definition dmRecon.hpp:113
int m_numModes
Number of modes to reconstruct. If 0 (default) all modes in CM are used.
Definition dmRecon.hpp:130
std::string m_respMPath
Optional response matrix. If set then CM modes are converted by this response.
Definition dmRecon.hpp:125
sem_t m_smSemaphore
Semaphore used to synchronize the fg thread and the dm command thread.
Definition dmRecon.hpp:195
bool m_commandReady
Flag indicating that all sizes match and arrays are ready for processing.
Definition dmRecon.hpp:169
dev::shmimMonitor< dmRecon, dmCommandShmimT > dmCommandSMT
Definition dmRecon.hpp:105
bool m_dmMaskReady
Flag indicating that the DM mask is ready for processing.
Definition dmRecon.hpp:167
int recordTelem(const telem_fgtimings *)
Definition dmRecon.hpp:1319
float m_fps
Current FPS from the FPS source.
Definition dmRecon.hpp:149
int loadImageIntoStream(void *dest)
Definition dmRecon.hpp:1253
virtual void loadConfig()
Definition dmRecon.hpp:464
int allocate(const dmModesShmimT &)
Allocate method for the dm modes shmimMonitor.
Definition dmRecon.hpp:716
mx::improc::milkImage< float > m_modeval
Definition dmRecon.hpp:182
friend class dmRecon_test
Definition dmRecon.hpp:96
pcf::IndiProperty m_indiP_fps
Definition dmRecon.hpp:320
#define FRAMEGRABBER_SETUP_CONFIG(cfig)
Call frameGrabberT::setupConfig with error checking for frameGrabber.
#define FRAMEGRABBER_APP_LOGIC
Call frameGrabberT::appLogic with error checking for frameGrabber.
#define FRAMEGRABBER_APP_SHUTDOWN
Call frameGrabberT::appShutdown with error checking for frameGrabber.
#define FRAMEGRABBER_UPDATE_INDI
Call frameGrabberT::updateINDI with error checking for frameGrabber.
#define FRAMEGRABBER_LOAD_CONFIG(cfig)
Call frameGrabberT::loadConfig with error checking for frameGrabber.
#define FRAMEGRABBER_APP_STARTUP
Call frameGrabberT::appStartup with error checking for frameGrabber.
#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 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.
@ OPERATING
The device is operating, other than homing.
#define INDI_VALIDATE_CALLBACK_PROPS(prop1, prop2)
Standard check for matching INDI properties in a callback.
std::stringstream msg
const pcf::IndiProperty & ipRecv
updateIfChanged(m_indiP_angle, "target", m_angle)
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition dm.hpp:19
static constexpr logPrioT LOG_NOTICE
A normal but significant condition.
static constexpr logPrioT LOG_INFO
Informational. The info log level is the lowest level recorded during normal operations.
#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.
A device base class which saves telemetry.
Definition telemeter.hpp:75
int checkRecordTimes(const telT &tel, telTs... tels)
Check the time of the last record for each telemetry type and make an entry if needed.
static std::string indiPrefix()
Definition dmRecon.hpp:77
static std::string configSection()
Definition dmRecon.hpp:72
static std::string configSection()
Definition dmRecon.hpp:59
static std::string indiPrefix()
Definition dmRecon.hpp:64
static std::string configSection()
Definition dmRecon.hpp:46
static std::string indiPrefix()
Definition dmRecon.hpp:51
Software ERR log entry.
Log entry recording framegrabber timings.
#define TELEMETER_APP_LOGIC
Call telemeter::appLogic with error checking.
#define TELEMETER_LOAD_CONFIG(cfig)
Call telemeter::loadConfig with error checking.
#define TELEMETER_APP_STARTUP
Call telemeter::appStartup with error checking.
#define TELEMETER_SETUP_CONFIG(cfig)
Call telemeter::setupConfig with error checking.
#define TELEMETER_APP_SHUTDOWN
Call telemeter::appShutdown with error checking.