Line data Source code
1 : /** \file dmPokeXCorr.hpp
2 : * \brief The MagAO-X DM Poke Centering header file
3 : *
4 : * \ingroup dmPokeXCorr_files
5 : */
6 :
7 : #ifndef dmPokeXCorr_hpp
8 : #define dmPokeXCorr_hpp
9 :
10 : #include <mx/improc/imageFilters.hpp>
11 : #include <mx/improc/imageXCorrFFT.hpp>
12 : using namespace mx::improc;
13 :
14 : #include <mx/math/fit/fitGaussian.hpp>
15 :
16 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
17 : #include "../../magaox_git_version.h"
18 :
19 : /** \defgroup dmPokeXCorr
20 : * \brief The MagAO-X application to center a DM pupil by poking actuators
21 : *
22 : * <a href="../handbook/operating/software/apps/dmPokeXCorr.html">Application Documentation</a>
23 : *
24 : * \ingroup apps
25 : *
26 : */
27 :
28 : /** \defgroup dmPokeXCorr_files
29 : * \ingroup dmPokeXCorr
30 : */
31 :
32 : namespace MagAOX
33 : {
34 : namespace app
35 : {
36 :
37 : struct zrespShmimT
38 : {
39 0 : static std::string configSection()
40 : {
41 0 : return "zrespM";
42 : };
43 0 : static std::string indiPrefix()
44 : {
45 0 : return "zrespM";
46 : };
47 : };
48 :
49 : /// The MagAO-X DM to PWFS alignment Application
50 : /**
51 : * \ingroup dmPokeXCorr
52 : */
53 : class dmPokeXCorr : public MagAOXApp<true>,
54 : public dev::dmPokeWFS<dmPokeXCorr>,
55 : public dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::wfsShmimT>,
56 : public dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::darkShmimT>,
57 : public dev::shmimMonitor<dmPokeXCorr, zrespShmimT>,
58 : public dev::telemeter<dmPokeXCorr>
59 : {
60 : // Give the test harness access.
61 : friend class dmPokeXCorr_test;
62 :
63 : friend class dev::dmPokeWFS<dmPokeXCorr>;
64 :
65 : typedef dev::dmPokeWFS<dmPokeXCorr> dmPokeWFST;
66 :
67 : friend class dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::wfsShmimT>;
68 :
69 : typedef dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::wfsShmimT> shmimMonitorT;
70 :
71 : friend class dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::darkShmimT>;
72 :
73 : typedef dev::shmimMonitor<dmPokeXCorr, dev::dmPokeWFS<dmPokeXCorr>::darkShmimT> darkShmimMonitorT;
74 :
75 : friend class dev::shmimMonitor<dmPokeXCorr, zrespShmimT>;
76 :
77 : typedef dev::shmimMonitor<dmPokeXCorr, zrespShmimT> zrespShmimMonitorT;
78 :
79 : friend class dev::telemeter<dmPokeXCorr>;
80 :
81 : typedef dev::telemeter<dmPokeXCorr> telemeterT;
82 :
83 : protected:
84 : /** \name Configurable Parameters
85 : *@{
86 : */
87 :
88 : std::string m_zRespMFile;
89 :
90 : ///@}
91 :
92 : mx::improc::imageXCorrFFT<eigenImage<float>> m_xcorr;
93 :
94 : mx::improc::milkImage<float> m_refIm;
95 :
96 : public:
97 : /// Default c'tor.
98 : dmPokeXCorr();
99 :
100 : /// D'tor, declared and defined for noexcept.
101 0 : ~dmPokeXCorr() noexcept
102 0 : {
103 0 : }
104 :
105 : /**\name MagAOX Interface
106 : *
107 : * @{
108 : */
109 : virtual void setupConfig();
110 :
111 : /// Implementation of loadConfig logic, separated for testing.
112 : /** This is called by loadConfig().
113 : */
114 : int loadConfigImpl(
115 : mx::app::appConfigurator &_config /**< [in] an application configuration from which to load values*/ );
116 :
117 : virtual void loadConfig();
118 :
119 : /// Startup function
120 : /**
121 : *
122 : */
123 : virtual int appStartup();
124 :
125 : /// Implementation of the FSM for dmPokeXCorr.
126 : /**
127 : * \returns 0 on no critical error
128 : * \returns -1 on an error requiring shutdown
129 : */
130 : virtual int appLogic();
131 :
132 : /// Shutdown the app.
133 : /**
134 : *
135 : */
136 : virtual int appShutdown();
137 :
138 : ///@}
139 :
140 0 : shmimMonitorT &shmimMonitor()
141 : {
142 0 : return *dynamic_cast<shmimMonitorT *>( this );
143 : }
144 :
145 0 : darkShmimMonitorT &darkShmimMonitor()
146 : {
147 0 : return *static_cast<darkShmimMonitorT *>( this );
148 : }
149 :
150 : /** \name zrespShmimMonitorT inteface
151 : * @{
152 : */
153 :
154 : int allocate( const zrespShmimT &dummy );
155 :
156 : int processImage( void *curr_src, const zrespShmimT &dummy );
157 :
158 : using dmPokeWFST::allocate;
159 :
160 : using dmPokeWFST::processImage;
161 :
162 : ///@}
163 :
164 : /** \name dmPokeWFS Interface
165 : * @{
166 : */
167 :
168 : /// Run the sensor steps
169 : /** Coordinates the actions of poking and collecting images.
170 : * Upon completion this calls runSensor. If \p firstRun == true, one time
171 : * actions such as taking a dark can be executed.
172 : *
173 : * \returns 0 on success
174 : * \returns \< 0 on an error
175 : */
176 : int runSensor( bool firstRun /**< [in] flag indicating this is the first call. triggers taking a dark if true.*/ );
177 :
178 : /// Analyze the poke image
179 : /** This analyzes the resulting poke image and reports the results.
180 : *
181 : * \returns 0 on success
182 : * \returns \< 0 on an error
183 : */
184 : int analyzeSensor();
185 :
186 : ///@}
187 :
188 : /** \name INDI Interface
189 : * @{
190 : */
191 : protected:
192 : ///@}
193 :
194 : /** \name Telemeter Interface
195 : *
196 : * @{
197 : */
198 : int checkRecordTimes();
199 :
200 : ///@}
201 : };
202 :
203 0 : dmPokeXCorr::dmPokeXCorr() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
204 : {
205 0 : darkShmimMonitorT::m_getExistingFirst = true;
206 0 : zrespShmimMonitorT::m_getExistingFirst = true;
207 :
208 0 : return;
209 0 : }
210 :
211 0 : void dmPokeXCorr::setupConfig()
212 : {
213 0 : DMPOKEWFS_SETUP_CONFIG( config );
214 :
215 0 : SHMIMMONITORT_SETUP_CONFIG( zrespShmimMonitorT, config );
216 :
217 0 : TELEMETER_SETUP_CONFIG( config );
218 : }
219 :
220 0 : int dmPokeXCorr::loadConfigImpl( mx::app::appConfigurator &_config )
221 : {
222 0 : DMPOKEWFS_LOAD_CONFIG( _config );
223 :
224 0 : SHMIMMONITORT_LOAD_CONFIG( zrespShmimMonitorT, _config );
225 :
226 0 : TELEMETER_LOAD_CONFIG( _config );
227 :
228 0 : return 0;
229 : }
230 :
231 0 : void dmPokeXCorr::loadConfig()
232 : {
233 0 : if( loadConfigImpl( config ) < 0 )
234 : {
235 0 : m_shutdown = true;
236 : }
237 0 : }
238 :
239 0 : int dmPokeXCorr::appStartup()
240 : {
241 0 : DMPOKEWFS_APP_STARTUP;
242 :
243 0 : SHMIMMONITORT_APP_STARTUP( zrespShmimMonitorT );
244 :
245 0 : TELEMETER_APP_STARTUP;
246 :
247 : // Gotta connect to the DM stream to find out its size
248 0 : mx::improc::milkImage<float> mdm;
249 : try
250 : {
251 0 : mdm.open( m_dmChan );
252 : }
253 0 : catch( const std::exception &e ) // this can check for invalid_argument and distinguish not existing
254 : {
255 0 : return log<software_error, -1>( { __FILE__, __LINE__, std::string( "exception opening DM: " ) + e.what() } );
256 0 : }
257 :
258 0 : state( stateCodes::READY );
259 :
260 0 : return 0;
261 0 : }
262 :
263 0 : int dmPokeXCorr::appLogic()
264 : {
265 0 : DMPOKEWFS_APP_LOGIC;
266 :
267 0 : SHMIMMONITORT_APP_LOGIC( zrespShmimMonitorT );
268 0 : SHMIMMONITORT_UPDATE_INDI( zrespShmimMonitorT );
269 0 : SHMIMMONITORT_UPDATE_INDI( shmimMonitorT );
270 0 : SHMIMMONITORT_UPDATE_INDI( darkShmimMonitorT );
271 :
272 0 : TELEMETER_APP_LOGIC;
273 :
274 0 : return 0;
275 : }
276 :
277 0 : int dmPokeXCorr::appShutdown()
278 : {
279 0 : DMPOKEWFS_APP_SHUTDOWN;
280 :
281 0 : SHMIMMONITORT_APP_SHUTDOWN( zrespShmimMonitorT );
282 :
283 0 : TELEMETER_APP_SHUTDOWN;
284 :
285 0 : return 0;
286 : }
287 :
288 0 : int dmPokeXCorr::allocate( const zrespShmimT &dummy )
289 : {
290 : static_cast<void>( dummy );
291 :
292 0 : if( m_refIm.rows() != zrespShmimMonitorT::m_width || m_refIm.cols() != zrespShmimMonitorT::m_height )
293 : {
294 0 : m_refIm.create( m_configName + "_refIm", zrespShmimMonitorT::m_width, zrespShmimMonitorT::m_height );
295 : }
296 :
297 0 : return 0;
298 : }
299 :
300 0 : int dmPokeXCorr::processImage( void *curr_src, const zrespShmimT &dummy )
301 : {
302 : static_cast<void>( dummy );
303 :
304 : // Gotta connect to the DM stream to find out its size
305 : // can't assume that we have connected anywhere else.
306 0 : mx::improc::milkImage<float> mdm;
307 : try
308 : {
309 0 : mdm.open( m_dmChan );
310 : }
311 0 : catch( const std::exception &e )
312 : {
313 0 : return log<software_error, -1>( { __FILE__, __LINE__, std::string( "exception opening DM: " ) + e.what() } );
314 0 : }
315 :
316 : mx::improc::eigenCube<float> zRespM(
317 0 : (float *)curr_src, zrespShmimMonitorT::m_width, zrespShmimMonitorT::m_height, zrespShmimMonitorT::m_depth );
318 :
319 0 : mx::improc::eigenImage<float> refIm;
320 0 : refIm.resize( zRespM.rows(), zRespM.cols() );
321 0 : refIm.setZero();
322 :
323 0 : for( size_t n = 0; n < m_poke_x.size(); ++n )
324 : {
325 0 : int actno = m_poke_y[n] * mdm.rows() + m_poke_x[n];
326 :
327 0 : refIm += zRespM.image( actno );
328 : }
329 :
330 0 : m_refIm = refIm;
331 :
332 0 : std::cerr << "Got reference: " << refIm.rows() << " x " << refIm.cols() << '\n';
333 : try
334 : {
335 0 : m_xcorr.peakMethod( xcorrPeakMethod::mftOversamp );
336 0 : m_xcorr.normalize( true );
337 0 : m_xcorr.maxLag( 10 );
338 0 : m_xcorr.tol( 0.001 );
339 :
340 0 : m_xcorr.refIm( refIm, 1 );
341 :
342 : }
343 0 : catch( const std::exception &e )
344 : {
345 0 : return log<software_error, -1>( { __FILE__, __LINE__, std::string( "exception caught: \n" ) + e.what() } );
346 0 : }
347 0 : return 0;
348 0 : }
349 :
350 0 : int dmPokeXCorr::runSensor( bool firstRun )
351 : {
352 : static_cast<void>( firstRun );
353 :
354 0 : int rv = dmPokeWFST::basicRunSensor();
355 :
356 0 : if( rv > 0 )
357 : {
358 0 : return 0;
359 : }
360 0 : else if( rv < 0 )
361 : {
362 0 : log<software_error>( { __FILE__, __LINE__ } );
363 0 : return rv;
364 : }
365 :
366 0 : return 0;
367 : }
368 :
369 0 : int dmPokeXCorr::analyzeSensor()
370 : {
371 0 : if( m_xcorr.refIm().rows() != m_pokeImage().rows() || m_xcorr.refIm().cols() != m_pokeImage().cols() )
372 : {
373 0 : std::cerr << "refIm: " << m_xcorr.refIm().rows() << " x " << m_xcorr.refIm().cols() << '\n';
374 0 : std::cerr << "pokeIm: " << m_pokeImage().rows() << " x " << m_pokeImage().cols() << '\n';
375 0 : return log<software_error, -1>( { __FILE__, __LINE__, "reference is not valid" } );
376 : }
377 :
378 : float xs, ys, pk;
379 :
380 : try
381 : {
382 0 : m_xcorr( xs, ys, pk, m_pokeImage() );
383 : }
384 0 : catch( const std::exception &e )
385 : {
386 0 : return log<software_error, -1>( { __FILE__, __LINE__, std::string( "exception caught: \n" ) + e.what() } );
387 0 : }
388 :
389 0 : std::cerr.precision( 5 );
390 0 : std::cerr << "dmPokeXCorr::analyzeSensor: " << xs << " " << ys << "\n";
391 :
392 0 : if( updateMeasurement( xs, ys ) < 0 )
393 : {
394 0 : return log<software_error, -1>( { __FILE__, __LINE__, "error from dmPokeWFS::updateMeasurement" } );
395 : }
396 :
397 0 : return 0;
398 : }
399 :
400 0 : int dmPokeXCorr::checkRecordTimes()
401 : {
402 0 : return telemeterT::checkRecordTimes( telem_pokeloop() );
403 : }
404 :
405 : } // namespace app
406 : } // namespace MagAOX
407 :
408 : #endif // dmPokeXCorr_hpp
|