API
dmPokeWFS.hpp
Go to the documentation of this file.
1 /** \file dmPokeWFS.hpp
2  * \brief The MagAO-X DM Poke Centering header file
3  *
4  * \ingroup dmPokeWFS_files
5  */
6 
7 #ifndef dmPokeWFS_hpp
8 #define dmPokeWFS_hpp
9 
10 #include <mx/improc/eigenImage.hpp>
11 #include <mx/improc/milkImage.hpp>
12 #include <mx/improc/eigenCube.hpp>
13 using namespace mx::improc;
14 
15 #include "../../ImageStreamIO/pixaccess.hpp"
16 
17 /** \defgroup dmPokeWFS
18  * \brief The MagAO-X device to coordinate poking a deformable mirror's actuators and synchronize reads of a camera image.
19  *
20  * <a href="../handbook/operating/software/apps/dmPokeWFS.html">Application Documentation</a>
21  *
22  * \ingroup apps
23  *
24  */
25 
26 
27 /** \defgroup dmPokeWFS_files
28  * \ingroup dmPokeWFS
29  */
30 
31 namespace MagAOX
32 {
33 namespace app
34 {
35 namespace dev
36 {
37 
38 
39 
40 /// A base class to coordinate poking a deformable mirror's actuators and synchronizedreads of a camera image.
41 /** CRTP class `derivedT` has the following requirements:
42  *
43  * - Must be derived from MagAOXApp<true>
44  *
45  * - Must be derived from `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>` (replace DERIVEDNAME with derivedT class name)
46  *
47  * - Must be derived from `dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>` (replace DERIVEDNAME with derivedT class name)
48  *
49  * - Must contain the following friend declarations (replace DERIVEDNAME with derivedT class name):
50  * \code
51  * friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT>;
52  * friend class dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT>;
53  * friend class dev::dmPokeWFS<DERIVEDNAME>
54  * \endcode
55  *
56  * - Must contain the following typedefs (replace DERIVEDNAME with derivedT class name):
57  * \code
58  * typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::wfsShmimT> shmimMonitorT;
59  * typedef dev::shmimMonitor<DERIVEDNAME, dev::dmPokeWFS<DERIVEDNAME>::darkShmimT> darkShmimMonitorT;
60  * typedef dev::dmPokeWFS<DERIVEDNAME> dmPokeWFST;
61  *
62  * \endcode
63  * - Must provide the following interfaces:
64  * \code
65  * shmimMonitorT & shmimMonitor()
66  * {
67  * return *static_cast<shmimMonitorT *>(this);
68  * }
69  *
70  * darkShmimMonitorT & darkShmimMonitor()
71  * {
72  * return *static_cast<darkShmimMonitorT *>(this);
73  * }
74  * \endcode
75  *
76  * - If derivedT has additional shmimMonitor parents, you will need to include these lines in the class
77  * declaration:
78  * \code
79  * using dmPokeWFST::allocate;
80  * using dmPokeWFST::processImage;
81  * \endcode
82  *
83  * - Must provide the following interface:
84  * \code
85  * // Run the sensor steps
86  * // Coordinates the actions of poking and collecting images.
87  * // Upon completion this calls runSensor. If \p firstRun == true, one time
88  * // actions such as taking a dark can be executed.
89  * //
90  * // returns 0 on success
91  * // returns < 0 on an error
92  * int runSensor(bool firstRun ///< [in] flag indicating this is the first call. triggers taking a dark if true.
93  * );
94  * \endcode
95  *
96  * - Must provide the following interface:
97  * \code
98  * // Analyze the poke image
99  * // This analyzes the resulting poke images.
100  * //
101  * // returns 0 on success
102  * // returns < 0 on an error
103  * int analyzeSensor();
104  * \endcode
105  * At the conclusion of analyzeSensor the measured signal (e.g. deltaX and deltaY) should be updated and set in m_indiP_measurement.
106  * The function \ref updateMeasurement() can be used for this. However, the updating of the loop counter and the subsequent INDI
107  * property update is handled automatically after that.
108  *
109  * - Must be a telemeter with the following interface:
110  *
111  * - Must be derived from `dev::telemeter<DERIVEDNAME>` (replace DERIVEDNAME with derivedT class name) and meet the requirements
112  * of `dev::telemeter`
113  *
114  * - In the function `derivedT::checkRecordTimes()` required by `dev::telemeter`, the `telem_pokeloop` type must be checked.
115  * The minimum `derivedT::checkRecordTimes()` is:
116  * \code
117  * int checkRecordTimes()
118  * {
119  * return telemeterT::checkRecordTimes(telem_pokeloop());
120  * }
121  * \endcode
122  *
123  * - Must call this base class's setupConfig(), loadConfig(), appStartup(), appStartup(), and appShutdown() in the
124  * appropriate functions. For convenience the following macros are defined to provide error checking:
125  * \code
126  * DMPOKEWFS_SETUP_CONFIG( cfig )
127  * DMPOKEWFS_LOAD_CONFIG( cfig )
128  * DMPOKEWFS_APP_STARTUP
129  * DMPOKEWFS_APP_LOGIC
130  * DMPOKEWFS_APP_SHUTDOWN
131  * \endcode
132  *
133  * \ingroup appdev
134  */
135 template<class derivedT>
136 class dmPokeWFS
137 {
138 
139 public:
140 
141  struct wfsShmimT
142  {
143  static std::string configSection()
144  {
145  return "wfscam";
146  };
147 
148  static std::string indiPrefix()
149  {
150  return "wfscam";
151  };
152  };
153 
154  struct darkShmimT
155  {
156  static std::string configSection()
157  {
158  return "wfsdark";
159  };
160 
161  static std::string indiPrefix()
162  {
163  return "wfsdark";
164  };
165  };
166 
167 protected:
168 
169  /** \name Configurable Parameters
170  *@{
171  */
172 
173  std::string m_wfsCamDevName; ///<INDI device name of the WFS camera. Default is wfscam.shmimName.
174 
175  double m_wfsSemWait {1.5}; ///< The time in sec to wait on the WFS semaphore. Default 0.5 sec.
176 
177  double m_imageSemWait {0.5}; ///< The time in sec to wait on the image semaphore. Default 0.5 sec.
178 
179  unsigned m_nPokeImages {5}; ///< The number of images to average for the poke images. Default is 5.
180 
181  unsigned m_nPokeAverage {10}; ///< The number of poke sequences to average. Default is 10.
182 
183  std::string m_dmChan;
184 
185  std::vector<int> m_poke_x;
186  std::vector<int> m_poke_y;
187 
188  float m_poke_amp {0.0};
189 
190  float m_dmSleep {10000}; ///<The time to sleep for the DM command to be applied, in microseconds. Default is 10000.
191 
192  ///@}
193 
194  std::mutex m_wfsImageMutex;
195 
196  mx::improc::milkImage<float> m_rawImage;
197 
198  mx::improc::milkImage<float> m_pokeImage;
199  mx::improc::eigenImage<float> m_pokeLocal;
200 
201  float (*wfsPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the image data as float
202 
203  float m_wfsFps {-1}; ///< The WFS camera FPS
204 
205  mx::improc::eigenImage<float> m_darkImage; ///< The dark image
206 
207  bool m_darkValid {false}; ///< Flag indicating if dark is valid based on its size.
208 
209  float (*darkPixget)(void *, size_t) {nullptr}; ///< Pointer to a function to extract the dark image data as float
210 
211  mx::improc::milkImage<float> m_dmStream;
212 
213  mx::improc::eigenImage<float> m_dmImage;
214 
215  float m_deltaX {0};
216  float m_deltaY {0};
217  uint64_t m_counter {0};
218 
219 public:
220 
221  /**\name MagAOXApp Interface
222  *
223  * @{
224  */
225 
226  /// Setup the configuration system
227  /**
228  * This should be called in `derivedT::setupConfig` as
229  * \code
230  dmPokeWFS<derivedT,realT>::setupConfig(config);
231  \endcode
232  * with appropriate error checking.
233  */
234  int setupConfig( mx::app::appConfigurator & config /**< [in] an application configuration to load values to*/);
235 
236  /// load the configuration system results
237  /**
238  * This should be called in `derivedT::loadConfig` as
239  * \code
240  dmPokeWFS<derivedT,realT>::loadConfig(config);
241  \endcode
242  * with appropriate error checking.
243  */
244  int loadConfig( mx::app::appConfigurator & config /**< [in] an application configuration from which to load values */);
245 
246  /// Startup function
247  /**
248  * This should be called in `derivedT::appStartup` as
249  * \code
250  dmPokeWFS<derivedT,realT>::appStartup();
251  \endcode
252  * with appropriate error checking.
253  *
254  * \returns 0 on success
255  * \returns -1 on error, which is logged.
256  */
257  int appStartup();
258 
259  /// dmPokeWFS application logic
260  /** This should be called in `derivedT::appLogic` as
261  * \code
262  dmPokeWFS<derivedT,realT>::appLogic();
263  \endcode
264  * with appropriate error checking.
265  *
266  * \returns 0 on success
267  * \returns -1 on error, which is logged.
268  */
269  int appLogic();
270 
271  /// dmPokeWFS shutdown
272  /** This should be called in `derivedT::appShutdown` as
273  * \code
274  dmPokeWFS<derivedT,realT>::appShutdown();
275  \endcode
276  * with appropriate error checking.
277  *
278  * \returns 0 on success
279  * \returns -1 on error, which is logged.
280  */
281  int appShutdown();
282 
283  ///@}
284 
285  /** \name shmimMonitor Interface
286  * @{
287  */
288 
289  int allocate( const wfsShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
290 
291  int processImage( void * curr_src, ///< [in] pointer to the start of the current frame
292  const wfsShmimT & ///< [in] tag to differentiate shmimMonitor parents.
293  );
294  ///@}
295 
296 
297  /** \name darkShmimMonitor Interface
298  * @{
299  */
300 
301  int allocate( const darkShmimT & /**< [in] tag to differentiate shmimMonitor parents.*/);
302 
303  int processImage( void * curr_src, ///< [in] pointer to the start of the current frame
304  const darkShmimT & ///< [in] tag to differentiate shmimMonitor parents.
305  );
306  ///@}
307 
308  /** \name WFS Thread
309  * This thread coordinates the WFS process
310  *
311  * @{
312  */
313 protected:
314 
315  int m_wfsThreadPrio {1}; ///< Priority of the WFS thread, should normally be > 00.
316 
317  std::string m_wfsCpuset; ///< The cpuset for the framegrabber thread. Ignored if empty (the default).
318 
319  std::thread m_wfsThread; ///< A separate thread for the actual WFSing
320 
321  bool m_wfsThreadInit {true}; ///< Synchronizer to ensure wfs thread initializes before doing dangerous things.
322 
323  pid_t m_wfsThreadID {0}; ///< WFS thread PID.
324 
325  pcf::IndiProperty m_wfsThreadProp; ///< The property to hold the WFS thread details.
326 
327  ///Thread starter, called by wfsThreadStart on thread construction. Calls wfsThreadExec.
328  static void wfsThreadStart( dmPokeWFS * s /**< [in] a pointer to an streamWriter instance (normally this) */);
329 
330  /// Execute the frame grabber main loop.
332 
333  sem_t m_wfsSemaphore; ///< Semaphore used to signal the WFS thread to start WFSing.
334 
335  unsigned m_wfsSemWait_sec {1}; ///< The timeout for the WFS semaphore, seconds component.
336 
337  unsigned m_wfsSemWait_nsec {0}; ///< The timeoutfor the WFS semaphore, nanoseconds component.
338 
339  int m_measuring {0}; ///< Status of measuring: 0 no, 1 single in progress, 2 continuous in progress.
340 
341  bool m_single {false}; ///< True a single measurement is in progress.
342 
343  bool m_continuous {false}; ///< True if continuous measurements are in progress.
344 
345  bool m_stopMeasurement {false}; ///< Used to request that the measurement in progress stop.
346 
347  ///@}
348 
349  sem_t m_imageSemaphore; ///< Semaphore used to signal that an image is ready
350 
351  unsigned m_imageSemWait_sec {1}; ///< The timeout for the image semaphore, seconds component.
352 
353  unsigned m_imageSemWait_nsec {0}; ///< The timeout for the image semaphore, nanoseconds component.
354 
355 
356  /// Apply a single DM poke pattern and record the results
357  /** This accumulates m_nPokeImages*m_nPokeAverage images in m_pokeLocal, so m_pokeLocal
358  * should be zeroed before the first call to this (e.g. for a +1 poke),
359  * but not zeroed before the second call (e.g. for the -1 poke). You also need
360  * to 0 the DM after finishing a poke pair.
361  * See basicRunSensor() for how to use.
362  *
363  * \returns +1 if exit is due to shutdown or stop request
364  * \returns 0 if no error
365  * \returns -1 if an error occurs
366  */
367  int basicTimedPoke(float pokeSign /**< [in] the sign, and possibly a scaling, to apply to m_pokeAmplitude*/);
368 
369  /// Run the basic +/- poke sensor steps
370  /** Coordinates the actions of poking and collecting images.
371  *
372  * This can be called from the derived class runSensor.
373  *
374  * \returns +1 if exit is due to shutdown or stop request
375  * \returns 0 if no error
376  * \returns -1 if an error occurs
377  */
379 
380  int updateMeasurement( float deltaX,
381  float deltaY
382  );
383 
384  /** \name INDI Interface
385  * @{
386  */
387 protected:
388 
389  pcf::IndiProperty m_indiP_poke_amp;
390  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_poke_amp);
391 
392  pcf::IndiProperty m_indiP_nPokeImages;
393  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeImages);
394 
395  pcf::IndiProperty m_indiP_nPokeAverage;
396  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeAverage);
397 
398  pcf::IndiProperty m_indiP_wfsFps; ///< Property to get the FPS from the WFS camera
399  INDI_SETCALLBACK_DECL(derivedT, m_indiP_wfsFps);
400 
401  pcf::IndiProperty m_indiP_single; ///< Switch to start a single measurement
402  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_single);
403 
404  pcf::IndiProperty m_indiP_continuous; ///< Switch to start continuous measurement
405  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_continuous);
406 
407  pcf::IndiProperty m_indiP_stop; ///< Switch to request that measurement stop
408  INDI_NEWCALLBACK_DECL(derivedT, m_indiP_stop);
409 
410  pcf::IndiProperty m_indiP_measurement; ///< Property to report the delta measurement, including the loop counter.
411 
412 
413  ///@}
414 
415  /** \name Telemeter Interface
416  * @{
417  */
418 
419  int recordTelem(const telem_pokeloop *);
420 
421  int recordPokeLoop(bool force = false);
422 
423  ///@}
424 
425 private:
426  derivedT & derived()
427  {
428  return *static_cast<derivedT *>(this);
429  }
430 
431 };
432 
433 template<class derivedT>
434 int dmPokeWFS<derivedT>::setupConfig(mx::app::appConfigurator & config)
435 {
436  if(derived().shmimMonitor().setupConfig(config) < 0)
437  {
438  derivedT::template log<software_error>({__FILE__, __LINE__, "shmimMonitorT::setupConfig"});
439  return -1;
440  }
441 
442  config.add("wfscam.camDevName", "", "wfscam.camDevName", argType::Required, "wfscam", "camDevName", false, "string", "INDI device name of the WFS camera. Default is wfscam.shmimName.");
443  config.add("wfscam.loopSemWait", "", "wfscam.loopSemWait", argType::Required, "wfscam", "loopSemWait", false, "float", "The semaphore wait time for the wfs loop start signal");
444  config.add("wfscam.imageSemWait", "", "wfscam.imageSemWait", argType::Required, "wfscam", "imageSemWait", false, "float", "The semaphore wait time for the image availability signal");
445 
446  if(derived().darkShmimMonitor().setupConfig(config) < 0)
447  {
448  derivedT::template log<software_error>({__FILE__, __LINE__, "darkShmimMonitorT::setupConfig"});
449  return -1;
450  }
451 
452  config.add("pokecen.dmChannel", "", "pokecen.dmChannel", argType::Required, "pokecen", "dmChannel", false, "string", "The dm channel to use for pokes, e.g. dm01disp06.");
453  config.add("pokecen.pokeX", "", "pokecen.pokeX", argType::Required, "pokecen", "pokeX", false, "vector<int>", "The x-coordinates of the actuators to poke. ");
454  config.add("pokecen.pokeY", "", "pokecen.pokeY", argType::Required, "pokecen", "pokeY", false, "vector<int>", "The y-coordinates of the actuators to poke. ");
455  config.add("pokecen.pokeAmp", "", "pokecen.pokeAmp", argType::Required, "pokecen", "pokeAmp", false, "float", "The poke amplitude, in DM command units. Default is 0.");
456  config.add("pokecen.dmSleep", "", "pokecen.dmSleep", argType::Required, "pokecen", "dmSleep", false, "float", "The time to sleep for the DM command to be applied, in microseconds. Default is 10000.");
457  config.add("pokecen.nPokeImages", "", "pokecen.nPokeImages", argType::Required, "pokecen", "nPokeImages", false, "int", "The number of poke images to average. Default 5.");
458  config.add("pokecen.nPokeAverage", "", "pokecen.nPokeAverage", argType::Required, "pokecen", "nPokeAverage", false, "int", "The number of poke sequences to average. Default 10.");
459 
460 
461  return 0;
462 }
463 
464 
465 template<class derivedT>
466 int dmPokeWFS<derivedT>::loadConfig( mx::app::appConfigurator & config)
467 {
468  if(derived().shmimMonitor().loadConfig(config) < 0)
469  {
470  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "shmimMonitorT::loadConfig"});
471  }
472 
473  m_wfsCamDevName = derived().shmimMonitor().shmimName();
474  config(m_wfsCamDevName, "wfscam.camDevName");
475 
476  //configure the semaphore waits
477  config(m_wfsSemWait, "wfscam.loopSemWait");
478 
479  m_wfsSemWait_sec = floor(m_wfsSemWait);
480  m_wfsSemWait_nsec = (m_wfsSemWait - m_wfsSemWait_sec) * 1e9;
481 
482  config(m_imageSemWait, "wfscam.imageSemWait");
483 
484  m_imageSemWait_sec = floor(m_imageSemWait);
485  m_imageSemWait_nsec = (m_imageSemWait - m_imageSemWait_sec) * 1e9;
486 
487  if(derived().darkShmimMonitor().loadConfig(config) < 0)
488  {
489  return derivedT::template log<software_error, -1>({__FILE__, __LINE__, "darkShmimMonitorT::loadConfig"});
490  }
491 
492  config(m_dmChan, "pokecen.dmChannel");
493 
494  config(m_poke_x, "pokecen.pokeX");
495 
496  config(m_poke_y, "pokecen.pokeY");
497 
498  if(m_poke_x.size() == 0 || (m_poke_x.size() != m_poke_y.size()))
499  {
500  return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "invalid poke specification"});
501  }
502 
503  config(m_poke_amp, "pokecen.pokeAmp");
504 
505  config(m_dmSleep, "pokecen.dmSleep");
506 
507  config(m_nPokeImages, "pokecen.nPokeImages");
508 
509  config(m_nPokeAverage, "pokecen.nPokeAverage");
510 
511  return 0;
512 }
513 
514 template<class derivedT>
516 {
517  if( derived().shmimMonitor().appStartup() < 0)
518  {
519  return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
520  }
521 
522  if( derived().darkShmimMonitor().appStartup() < 0)
523  {
524  return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
525  }
526 
527  CREATE_REG_INDI_NEW_NUMBERF_DERIVED(m_indiP_poke_amp, "poke_amp", -1, 1, 1e-1, "%0.01f", "", "");
528  m_indiP_poke_amp["current"].setValue(m_poke_amp);
529  m_indiP_poke_amp["target"].setValue(m_poke_amp);
530 
531  CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeImages, "nPokeImages", 1, 1000, 1, "%d", "", "");
532  m_indiP_nPokeImages["current"].setValue(m_nPokeImages);
533  m_indiP_nPokeImages["target"].setValue(m_nPokeImages);
534 
535  CREATE_REG_INDI_NEW_NUMBERI_DERIVED(m_indiP_nPokeAverage, "nPokeAverage", 1, 1000, 1, "%d", "", "");
536  m_indiP_nPokeAverage["current"].setValue(m_nPokeAverage);
537  m_indiP_nPokeAverage["target"].setValue(m_nPokeAverage);
538 
539  REG_INDI_SETPROP_DERIVED(m_indiP_wfsFps, m_wfsCamDevName, std::string("fps"));
540 
541  CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_single, "single");
542 
543  CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED( m_indiP_continuous, "continuous");
544 
545  CREATE_REG_INDI_NEW_REQUESTSWITCH_DERIVED( m_indiP_stop, "stop");
546 
547  derived().template registerIndiPropertyReadOnly( m_indiP_measurement, "measurement", pcf::IndiProperty::Number, pcf::IndiProperty::ReadOnly, pcf::IndiProperty::Idle);
548  m_indiP_measurement.add({"delta_x", 0.0});
549  m_indiP_measurement.add({"delta_y", 0.0});
550  m_indiP_measurement.add({"counter", 0});
551 
552  if(sem_init(&m_wfsSemaphore, 0,0) < 0)
553  {
554  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing wfs semaphore"});
555  }
556 
557  if(sem_init(&m_imageSemaphore, 0,0) < 0)
558  {
559  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno,0, "Initializing image semaphore"});
560  }
561 
562  if(derived().template threadStart( m_wfsThread, m_wfsThreadInit, m_wfsThreadID, m_wfsThreadProp, m_wfsThreadPrio, m_wfsCpuset, "wfs", this, wfsThreadStart) < 0)
563  {
564  return derivedT::template log<software_critical,-1>({__FILE__, __LINE__});
565  }
566 
567  return 0;
568 }
569 
570 template<class derivedT>
572 {
573 
574  if( derived().shmimMonitor().appLogic() < 0)
575  {
576  return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
577  }
578 
579  if( derived().darkShmimMonitor().appLogic() < 0)
580  {
581  return derivedT::template log<software_error, -1>({__FILE__,__LINE__});
582  }
583 
584  //first do a join check to see if other threads have exited.
585  //these will throw if the threads are really gone
586  try
587  {
588  if(pthread_tryjoin_np(m_wfsThread.native_handle(),0) == 0)
589  {
590  derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
591  return -1;
592  }
593  }
594  catch(...)
595  {
596  derivedT::template log<software_error>({__FILE__, __LINE__, "WFS thread has exited"});
597  return -1;
598  }
599 
600  if(m_measuring > 0)
601  {
602  if(m_continuous)
603  {
604  derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
605  }
606  else
607  {
608  derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
609  }
610 
611  if(m_single)
612  {
613  derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::On, INDI_OK);
614  }
615  else
616  {
617  derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
618  }
619  }
620  else
621  {
622  derived().template updateSwitchIfChanged(m_indiP_continuous, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
623  derived().template updateSwitchIfChanged(m_indiP_single, "toggle", pcf::IndiElement::SwitchStateType::Off, INDI_IDLE);
624  }
625 
626  derived().template updateIfChanged( m_indiP_nPokeImages, "current", m_nPokeImages);
627  derived().template updateIfChanged( m_indiP_nPokeAverage, "current", m_nPokeAverage);
628  derived().template updateIfChanged( m_indiP_poke_amp, "current", m_poke_amp);
629 
630  return 0;
631 }
632 
633 template<class derivedT>
635 {
636  if(derived().shmimMonitor().appShutdown() < 0)
637  {
638  derivedT::template log<software_error>({__FILE__, __LINE__, "error from shmimMonitorT::appShutdown"});
639  }
640 
641  if(derived().darkShmimMonitor().appShutdown() < 0)
642  {
643  derivedT::template log<software_error>({__FILE__, __LINE__, "error from darkShmimMonitorT::appShutdown"});
644  }
645 
646  if (m_wfsThread.joinable())
647  {
648  pthread_kill(m_wfsThread.native_handle(), SIGUSR1);
649  try
650  {
651  m_wfsThread.join(); // this will throw if it was already joined
652  }
653  catch (...)
654  {
655  }
656  }
657 
658  return 0;
659 }
660 
661 template<class derivedT>
663 {
664  static_cast<void>(dummy); //be unused
665 
666  std::unique_lock<std::mutex> lock(m_wfsImageMutex);
667 
668  m_rawImage.create( derived().m_configName + "_raw", derived().shmimMonitor().width(), derived().shmimMonitor().height());
669 
670  wfsPixget = getPixPointer<float>(derived().shmimMonitor().dataType());
671 
672  try
673  {
674  m_dmStream.open(m_dmChan);
675  }
676  catch(const std::exception& e)
677  {
678  return derivedT::template log<software_error,-1>({__FILE__, __LINE__, std::string("exception opening DM: ") + e.what()});
679  }
680 
681  m_dmImage.resize(m_dmStream.rows(), m_dmStream.cols());
682 
683  if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
684  derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
685  {
686  m_darkValid = true;
687  }
688  else
689  {
690  m_darkValid = false;
691  }
692 
693  if(m_pokeImage.rows() != derived().shmimMonitor().width() || m_pokeImage.cols() != derived().shmimMonitor().height())
694  {
695  m_pokeImage.create(derived().m_configName + "_poke", derived().shmimMonitor().width(), derived().shmimMonitor().height());
696  }
697 
698  m_pokeLocal.resize(derived().shmimMonitor().width(), derived().shmimMonitor().height());
699 
700  return 0;
701 }
702 
703 template<class derivedT>
704 int dmPokeWFS<derivedT>::processImage( void * curr_src,
705  const wfsShmimT & dummy
706  )
707 {
708  static_cast<void>(dummy); //be unused
709 
710  std::unique_lock<std::mutex> lock(m_wfsImageMutex);
711 
712  float * data = m_rawImage().data();
713  float * darkData = m_darkImage.data();
714 
715  //Copy the data out as float no matter what type it is
716  uint64_t Npix = derived().shmimMonitor().width()*derived().shmimMonitor().height();
717 
718  if(m_darkValid)
719  {
720  for(unsigned nn=0; nn < Npix; ++nn)
721  {
722  data[nn] = wfsPixget(curr_src, nn) - darkData[nn];
723  }
724  }
725  else
726  {
727  for(unsigned nn=0; nn < Npix; ++nn)
728  {
729  data[nn] = wfsPixget(curr_src, nn);
730  }
731  }
732 
733  if(sem_post(&m_imageSemaphore) < 0)
734  {
735  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
736  }
737 
738  return 0;
739 }
740 
741 //--dark shmim
742 
743 template<class derivedT>
745 {
746  static_cast<void>(dummy); //be unused
747 
748  std::unique_lock<std::mutex> lock(m_wfsImageMutex);
749 
750  m_darkImage.resize(derived().darkShmimMonitor().width(), derived().darkShmimMonitor().height());
751 
752  darkPixget = getPixPointer<float>(derived().darkShmimMonitor().dataType());
753 
754  if(derived().darkShmimMonitor().width() == derived().shmimMonitor().width() &&
755  derived().darkShmimMonitor().height() == derived().shmimMonitor().height() )
756  {
757  std::cerr << "dark is valid " << derived().darkShmimMonitor().width() << " " << derived().shmimMonitor().width() << " ";
758  std::cerr << derived().darkShmimMonitor().height() << " " << derived().shmimMonitor().height() << "\n";
759  m_darkValid = true;
760  }
761  else
762  {
763  m_darkValid = false;
764  }
765 
766  return 0;
767 }
768 
769 template<class derivedT>
770 int dmPokeWFS<derivedT>::processImage( void * curr_src,
771  const darkShmimT & dummy
772  )
773 {
774  static_cast<void>(dummy); //be unused
775 
776  std::unique_lock<std::mutex> lock(m_wfsImageMutex);
777 
778  float * darkData = m_darkImage.data();
779 
780  //Copy the data out as float no matter what type it is
781  uint64_t nPix = derived().darkShmimMonitor().width()*derived().darkShmimMonitor().height();
782  for(unsigned nn=0; nn < nPix; ++nn)
783  {
784  darkData[nn] = darkPixget(curr_src, nn);
785  }
786 
787  return 0;
788 }
789 
790 template<class derivedT>
792 {
793  d->wfsThreadExec();
794 }
795 
796 template<class derivedT>
798 {
799  m_wfsThreadID = syscall(SYS_gettid);
800 
801  //Wait fpr the thread starter to finish initializing this thread.
802  while(m_wfsThreadInit == true && derived().m_shutdown == 0)
803  {
804  sleep(1);
805  }
806 
807  while(derived().m_shutdown == 0)
808  {
809  timespec ts;
810  XWC_SEM_WAIT_TS_RETVOID_DERIVED(ts, m_wfsSemWait_sec, m_wfsSemWait_nsec);
811 
812  XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_wfsSemaphore, ts )
813 
814  //Lock a mutex here
815  if(m_single)
816  {
817  m_measuring = 1;
818  }
819  else if(m_continuous)
820  {
821  m_measuring = 2;
822  }
823  else
824  {
825  m_measuring = 0;
826  return;
827  }
828 
829  derived().template state(stateCodes::OPERATING);
830 
831  while(!m_pokeImage.valid())
832  {
833  mx::sys::milliSleep(10);
834  }
835 
836  m_stopMeasurement = false;
837 
838  bool firstRun = true;
839 
840  while(!m_stopMeasurement && !derived().m_shutdown)
841  {
842  if( derived().runSensor(firstRun) < 0)
843  {
844  derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
845  break;
846  }
847 
848  if(m_stopMeasurement || derived().m_shutdown)
849  {
850  break;
851  }
852 
853  if( derived().analyzeSensor() < 0)
854  {
855  derivedT::template log<software_error>({__FILE__, __LINE__, "runSensor returned error"});
856  break;
857  }
858 
859  ++m_counter;
860  derived().updateIfChanged(m_indiP_measurement, "counter", m_counter);
861  derived().recordPokeLoop();
862 
863  firstRun = false;
864 
865  if(m_measuring == 1)
866  {
867  break;
868  }
869  }
870 
871  m_measuring = 0;
872  m_single = 0;
873  m_continuous = 0;
874 
875  derived().template state(stateCodes::READY);
876 
877 
878  } //outer loop, will exit if derived().m_shutdown==true
879 
880  return;
881 
882 }
883 
884 template<class derivedT>
886 {
887  timespec ts;
888 
889  int sign = 1;
890  if(pokeSign < 0) sign = -1;
891 
892  //Prepare the DM image with the pokes
893  m_dmImage.setZero();
894 
895  for(size_t nn = 0; nn < m_poke_x.size(); ++nn)
896  {
897  m_dmImage( m_poke_x[nn], m_poke_y[nn]) = pokeSign*m_poke_amp;
898  }
899 
900  //This is where the pokes are applied to the DM
901  m_dmStream = m_dmImage;
902 
903  mx::sys::microSleep(m_dmSleep);
904 
905  //flush semaphore so we take the _next_ good image
906  XWC_SEM_FLUSH_DERIVED(m_imageSemaphore);
907 
908  //** And wait one image to be sure we are on a whole poke**//
909  XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
910  bool ready = false;
911  while(!ready && !(m_stopMeasurement || derived().m_shutdown))
912  {
913  XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
914  else
915  {
916  ready = true;
917  }
918  }
919 
920  uint32_t n = 0;
921  while(n < m_nPokeImages && !(m_stopMeasurement || derived().m_shutdown))
922  {
923  //** Now we record the poke image **//
924  XWC_SEM_WAIT_TS_DERIVED(ts, m_imageSemWait_sec, m_imageSemWait_nsec);
925  XWC_SEM_TIMEDWAIT_LOOP_DERIVED( m_imageSemaphore, ts )
926 
927  //If here, we got an image. m_rawImage will have been updated
928  m_pokeLocal += sign*m_rawImage();
929 
930  ++n;
931  }
932 
933  if(m_stopMeasurement || derived().m_shutdown)
934  {
935  m_dmImage.setZero();
936  m_dmStream = m_dmImage;
937  return 1;
938  }
939 
940  return 0;
941 }
942 
943 template<class derivedT>
945 {
946  int rv;
947 
948  if(!m_pokeImage.valid())
949  {
950  return derivedT::template log<software_error,-1>({__FILE__, __LINE__, "poke image is not allocated"});
951  }
952 
953  m_pokeLocal.setZero();
954 
955  for(unsigned nseq = 0; nseq < m_nPokeAverage; ++nseq)
956  {
957 
958  //************** positive POKE **********************/
959 
960  rv = basicTimedPoke(+1);
961 
962  if(rv < 0)
963  {
964  derivedT::template log<software_error>({__FILE__, __LINE__});
965  return rv;
966  }
967  else if (rv > 0) // shutdown
968  {
969  return rv;
970  }
971 
972  if(m_stopMeasurement || derived().m_shutdown)
973  {
974  break;
975  }
976 
977  //************** NEGATIVE POKE **********************/
978 
979  rv = basicTimedPoke(-1);
980 
981  if(rv < 0)
982  {
983  derivedT::template log<software_error>({__FILE__, __LINE__});
984  return rv;
985  }
986  else if (rv > 0) // shutdown
987  {
988  return rv;
989  }
990 
991  if(m_stopMeasurement || derived().m_shutdown)
992  {
993  break;
994  }
995  }
996 
997  try
998  {
999  m_pokeImage = m_pokeLocal/(2.0*m_nPokeImages*m_nPokeAverage);
1000  }
1001  catch(const std::exception& e)
1002  {
1003  return derivedT::template log<software_error,-1>({__FILE__, __LINE__, e.what()});
1004  }
1005 
1006 
1007 
1008  m_dmImage.setZero();
1009  m_dmStream = m_dmImage;
1010 
1011  return 0;
1012 }
1013 
1014 template<class derivedT>
1016  float deltaY
1017  )
1018 {
1019  m_deltaX = deltaX;
1020  m_deltaY = deltaY;
1021  m_indiP_measurement["delta_x"] = deltaX;
1022  m_indiP_measurement["delta_y"] = deltaY;
1023 
1024  return 0;
1025 }
1026 
1027 template<class derivedT>
1028 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeImages )(const pcf::IndiProperty &ipRecv)
1029 {
1030  INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeImages, ipRecv)
1031 
1032  float target;
1033 
1034  if( derived().template indiTargetUpdate(m_indiP_nPokeImages, target, ipRecv, false) < 0)
1035  {
1036  return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1037  }
1038 
1039  m_nPokeImages = target;
1040 
1041  return 0;
1042 }
1043 
1044 template<class derivedT>
1045 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_nPokeAverage )(const pcf::IndiProperty &ipRecv)
1046 {
1047  INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_nPokeAverage, ipRecv)
1048 
1049  float target;
1050 
1051  if( derived().template indiTargetUpdate(m_indiP_nPokeAverage, target, ipRecv, false) < 0)
1052  {
1053  return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1054  }
1055 
1056  m_nPokeAverage = target;
1057 
1058  return 0;
1059 }
1060 
1061 template<class derivedT>
1062 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_poke_amp )(const pcf::IndiProperty &ipRecv)
1063 {
1064  INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_poke_amp, ipRecv)
1065 
1066  float target;
1067 
1068  if( derived().template indiTargetUpdate(m_indiP_poke_amp, target, ipRecv, false) < 0)
1069  {
1070  return derivedT::template log<software_error,-1>({__FILE__, __LINE__});
1071  }
1072 
1073  m_poke_amp = target;
1074 
1075  return 0;
1076 }
1077 
1078 template<class derivedT>
1079 INDI_SETCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_wfsFps )(const pcf::IndiProperty &ipRecv)
1080 {
1082 
1083  if( ipRecv.find("current") != true ) //this isn't valid
1084  {
1085  return 0;
1086  }
1087 
1088  m_wfsFps = ipRecv["current"].get<float>();
1089 
1090  return 0;
1091 }
1092 
1093 template<class derivedT>
1094 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_single )(const pcf::IndiProperty &ipRecv)
1095 {
1097 
1098  if( ipRecv.find("toggle") != true ) //this isn't valid
1099  {
1100  return -1;
1101  }
1102 
1103  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1104  {
1105  if(m_measuring == 0)
1106  {
1107  m_continuous = 0;
1108  m_single = 1;
1109  if(sem_post(&m_wfsSemaphore) < 0)
1110  {
1111  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
1112  }
1113  }
1114  }
1115 
1116  return 0;
1117 }
1118 
1119 template<class derivedT>
1120 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_continuous )(const pcf::IndiProperty &ipRecv)
1121 {
1122  INDI_VALIDATE_CALLBACK_PROPS_DERIVED(m_indiP_continuous, ipRecv)
1123 
1124  if( ipRecv.find("toggle") != true ) //this isn't valid
1125  {
1126  return -1;
1127  }
1128 
1129  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On )
1130  {
1131  if(m_measuring == 0)
1132  {
1133  m_continuous = 1;
1134  m_single = 0;
1135  if(sem_post(&m_wfsSemaphore) < 0)
1136  {
1137  return derivedT::template log<software_critical, -1>({__FILE__, __LINE__, errno, 0, "Error posting to semaphore"});
1138  }
1139  }
1140  }
1141  else if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off )
1142  {
1143  if(m_measuring != 0)
1144  {
1145  m_stopMeasurement = true;
1146  }
1147  }
1148 
1149  return 0;
1150 }
1151 
1152 template<class derivedT>
1153 INDI_NEWCALLBACK_DEFN( dmPokeWFS<derivedT>, m_indiP_stop )(const pcf::IndiProperty &ipRecv)
1154 {
1156 
1157  if( ipRecv.find("request") != true ) //this isn't valid
1158  {
1159  return -1;
1160  }
1161 
1162  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On )
1163  {
1164  if(m_measuring != 0)
1165  {
1166  m_stopMeasurement = true;
1167  }
1168  }
1169 
1170  return 0;
1171 }
1172 
1173 template<class derivedT>
1174 int dmPokeWFS<derivedT>::recordTelem(const telem_pokeloop *)
1175 {
1176  return recordPokeLoop(true);
1177 }
1178 
1179 template<class derivedT>
1181 {
1182  static int measuring = -1;
1183  static float deltaX = std::numeric_limits<float>::max();
1184  static float deltaY = std::numeric_limits<float>::max();
1185  static uint64_t counter = std::numeric_limits<uint64_t>::max();
1186 
1187  if(force || (m_counter != counter) || (m_deltaX != deltaX) || (m_deltaY != deltaY) || (m_measuring != measuring))
1188  {
1189  uint8_t meas = m_measuring;
1190  derived().template telem<telem_pokeloop>({meas, m_deltaX, m_deltaY, m_counter});
1191 
1192  measuring = m_measuring;
1193  deltaX = m_deltaX;
1194  deltaY = m_deltaY;
1195  counter = m_counter;
1196  }
1197 
1198  return 0;
1199 }
1200 
1201 /// Call dmPokeWFS::setupConfig with error checking
1202 /**
1203  * \param cfig the application configurator
1204  */
1205 #define DMPOKEWFS_SETUP_CONFIG( cfig ) \
1206  if(dmPokeWFST::setupConfig(cfig) < 0) \
1207  { \
1208  log<software_error>({__FILE__, __LINE__, "Error from dmPokeWFST::setupConfig"}); \
1209  m_shutdown = true; \
1210  return; \
1211  }
1212 
1213 /// Call dmPokeWFS::loadConfig with error checking
1214 /** This must be inside a function that returns int, e.g. the standard loadConfigImpl.
1215  * \param cfig the application configurator
1216  */
1217 #define DMPOKEWFS_LOAD_CONFIG( cfig ) \
1218  if(dmPokeWFST::loadConfig(cfig) < 0) \
1219  { \
1220  return log<software_error,-1>({__FILE__, __LINE__, "Error from dmPokeWFST::loadConfig"}); \
1221  }
1222 
1223 /// Call dmPokeWFS::appStartup with error checking
1224 #define DMPOKEWFS_APP_STARTUP \
1225  if( dmPokeWFST::appStartup() < 0) \
1226  { \
1227  return log<software_error, -1>({__FILE__,__LINE__}); \
1228  }
1229 
1230 /// Call dmPokeWFS::appLogic with error checking
1231 #define DMPOKEWFS_APP_LOGIC \
1232  if( dmPokeWFST::appLogic() < 0) \
1233  { \
1234  return log<software_error, -1>({__FILE__,__LINE__}); \
1235  }
1236 
1237 /// Call dmPokeWFS::appShutdown with error checking
1238 #define DMPOKEWFS_APP_SHUTDOWN \
1239  if(dmPokeWFST::appShutdown() < 0) \
1240  { \
1241  log<software_error>({__FILE__, __LINE__, "error from dmPokeWFST::appShutdown"}); \
1242  }
1243 
1244 } //namespace dev
1245 } //namespace app
1246 } //namespace MagAOX
1247 
1248 #endif //dmPokeWFS_hpp
A base class to coordinate poking a deformable mirror's actuators and synchronizedreads of a camera i...
Definition: dmPokeWFS.hpp:137
pcf::IndiProperty m_indiP_poke_amp
Definition: dmPokeWFS.hpp:389
mx::improc::eigenImage< float > m_pokeLocal
Definition: dmPokeWFS.hpp:199
mx::improc::milkImage< float > m_rawImage
Definition: dmPokeWFS.hpp:196
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeAverage)
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_continuous)
std::string m_wfsCamDevName
INDI device name of the WFS camera. Default is wfscam.shmimName.
Definition: dmPokeWFS.hpp:173
void wfsThreadExec()
Execute the frame grabber main loop.
Definition: dmPokeWFS.hpp:797
int updateMeasurement(float deltaX, float deltaY)
Definition: dmPokeWFS.hpp:1015
sem_t m_imageSemaphore
Semaphore used to signal that an image is ready.
Definition: dmPokeWFS.hpp:349
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_single)
static void wfsThreadStart(dmPokeWFS *s)
Thread starter, called by wfsThreadStart on thread construction. Calls wfsThreadExec.
Definition: dmPokeWFS.hpp:791
sem_t m_wfsSemaphore
Semaphore used to signal the WFS thread to start WFSing.
Definition: dmPokeWFS.hpp:333
pcf::IndiProperty m_indiP_nPokeAverage
Definition: dmPokeWFS.hpp:395
std::vector< int > m_poke_y
Definition: dmPokeWFS.hpp:186
int processImage(void *curr_src, const darkShmimT &)
Definition: dmPokeWFS.hpp:770
mx::improc::eigenImage< float > m_dmImage
Definition: dmPokeWFS.hpp:213
int appShutdown()
dmPokeWFS shutdown
Definition: dmPokeWFS.hpp:634
pcf::IndiProperty m_wfsThreadProp
The property to hold the WFS thread details.
Definition: dmPokeWFS.hpp:325
mx::improc::milkImage< float > m_pokeImage
Definition: dmPokeWFS.hpp:198
pcf::IndiProperty m_indiP_single
Switch to start a single measurement.
Definition: dmPokeWFS.hpp:401
pcf::IndiProperty m_indiP_measurement
Property to report the delta measurement, including the loop counter.
Definition: dmPokeWFS.hpp:410
int basicTimedPoke(float pokeSign)
Apply a single DM poke pattern and record the results.
Definition: dmPokeWFS.hpp:885
int allocate(const wfsShmimT &)
Definition: dmPokeWFS.hpp:662
mx::improc::milkImage< float > m_dmStream
Pointer to a function to extract the dark image data as float.
Definition: dmPokeWFS.hpp:209
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_nPokeImages)
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
Definition: dmPokeWFS.hpp:466
pcf::IndiProperty m_indiP_wfsFps
Property to get the FPS from the WFS camera.
Definition: dmPokeWFS.hpp:398
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_poke_amp)
pcf::IndiProperty m_indiP_stop
Switch to request that measurement stop.
Definition: dmPokeWFS.hpp:407
pcf::IndiProperty m_indiP_nPokeImages
Definition: dmPokeWFS.hpp:392
INDI_NEWCALLBACK_DECL(derivedT, m_indiP_stop)
int basicRunSensor()
Run the basic +/- poke sensor steps.
Definition: dmPokeWFS.hpp:944
int allocate(const darkShmimT &)
Definition: dmPokeWFS.hpp:744
int recordPokeLoop(bool force=false)
Definition: dmPokeWFS.hpp:1180
std::thread m_wfsThread
A separate thread for the actual WFSing.
Definition: dmPokeWFS.hpp:319
int appStartup()
Startup function.
Definition: dmPokeWFS.hpp:515
INDI_SETCALLBACK_DECL(derivedT, m_indiP_wfsFps)
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
Definition: dmPokeWFS.hpp:434
std::string m_wfsCpuset
The cpuset for the framegrabber thread. Ignored if empty (the default).
Definition: dmPokeWFS.hpp:317
int appLogic()
dmPokeWFS application logic
Definition: dmPokeWFS.hpp:571
int recordTelem(const telem_pokeloop *)
Definition: dmPokeWFS.hpp:1174
mx::improc::eigenImage< float > m_darkImage
The dark image.
Definition: dmPokeWFS.hpp:205
int processImage(void *curr_src, const wfsShmimT &)
Definition: dmPokeWFS.hpp:704
pcf::IndiProperty m_indiP_continuous
Switch to start continuous measurement.
Definition: dmPokeWFS.hpp:404
std::vector< int > m_poke_x
Definition: dmPokeWFS.hpp:185
#define INDI_NEWCALLBACK_DEFN(class, prop)
Define the callback for a new property request.
Definition: indiMacros.hpp:89
#define CREATE_REG_INDI_NEW_NUMBERI_DERIVED(prop, name, min, max, step, format, label, group)
Create and register a NEW INDI property as a standard number as int, using the standard callback name...
Definition: indiMacros.hpp:500
#define CREATE_REG_INDI_NEW_REQUESTSWITCH_DERIVED(prop, name)
Create and register a NEW INDI property as a standard request switch, using the standard callback nam...
Definition: indiMacros.hpp:540
#define INDI_SETCALLBACK_DEFN(class, prop)
Define the callback for a set property request.
Definition: indiMacros.hpp:118
#define CREATE_REG_INDI_NEW_TOGGLESWITCH_DERIVED(prop, name)
Create and register a NEW INDI property as a standard toggle switch, using the standard callback name...
Definition: indiMacros.hpp:520
#define CREATE_REG_INDI_NEW_NUMBERF_DERIVED(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...
Definition: indiMacros.hpp:472
#define INDI_VALIDATE_CALLBACK_PROPS_DERIVED(prop1, prop2)
Standard check for matching INDI properties in a callback in a CRTP base class.
Definition: indiMacros.hpp:197
#define REG_INDI_SETPROP_DERIVED(prop, devName, propName)
Definition: indiMacros.hpp:288
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
std::ostream & cerr()
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void updateSwitchIfChanged(pcf::IndiProperty &p, const std::string &el, const pcf::IndiElement::SwitchStateType &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:212
const pcf::IndiProperty & ipRecv
Definition: MagAOXApp.hpp:3434
std::unique_lock< std::mutex > lock(m_indiMutex)
Definition: dm.hpp:24
#define XWC_SEM_FLUSH_DERIVED(sem)
#define XWC_SEM_WAIT_TS_RETVOID_DERIVED(ts, sec, nsec)
Add the wait time to a timespec for a sem_timedwait call, with no value returned on error,...
#define XWC_SEM_TIMEDWAIT_LOOP_DERIVED(sem, ts)
Perform a sem_timedwait in the context of a standard loop in MagAO-X code using the derived class.
#define XWC_SEM_WAIT_TS_DERIVED(ts, sec, nsec)
Add the wait time to a timespec for a sem_timedwait call, with -1 returned on error.