API
dmModulator.hpp
Go to the documentation of this file.
1 /** \file dmModulator.hpp
2  * \brief The MagAO-X DM speckle maker header file
3  *
4  * \ingroup dmModulator_files
5  */
6 
7 #ifndef dmModulator_hpp
8 #define dmModulator_hpp
9 
10 #include <mx/improc/eigenCube.hpp>
11 #include <mx/ioutils/fits/fitsFile.hpp>
12 #include <mx/improc/eigenImage.hpp>
13 #include <mx/ioutils/stringUtils.hpp>
14 #include <mx/sys/timeUtils.hpp>
15 #include <mx/sigproc/fourierModes.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/randomT.hpp>
21 
22 /** \defgroup dmModulator
23  * \brief The DM speckle maker app
24  *
25  * Creates a set of fourier modes to generate speckles, then applies them to a DM channel
26  * at a specified rate.
27  *
28  *
29  * <a href="../handbook/operating/software/apps/dmModulator.html">Application Documentation</a>
30  *
31  * \ingroup apps
32  *
33  */
34 
35 /** \defgroup dmModulator_files
36  * \ingroup dmModulator
37  */
38 
39 
40 namespace MagAOX
41 {
42 namespace app
43 {
44 
45 /// The MagAO-X DM mode commander
46 /**
47  * \ingroup dmModulator
48  */
49 class dmModulator : public MagAOXApp<true>
50 {
51 
52  typedef float realT;
53 
54  friend class dmModulator_test;
55 
56 protected:
57 
58  /** \name Configurable Parameters
59  *@{
60  */
61 
62  std::string m_shapeCube;
63 
64  std::string m_dmName; ///< The descriptive name of this dm. Default is the channel name.
65 
66  std::string m_dmChannelName; ///< The name of the DM channel to write to.
67 
68  std::string m_dmTriggerChannel; ///< The DM channel to monitor as a trigger
69 
70  int m_triggerSemaphore {9}; ///< The semaphore to use (default 9)
71 
72  bool m_trigger {true}; ///< Run in trigger mode if true (default)
73 
74  realT m_amp {0.01}; ///< The speckle amplitude on the DM
75 
76  realT m_frequency {2000}; ///< The frequency to modulate at if not triggering (default 2000 Hz)
77 
78  bool m_noise = true;
79  ///@}
80 
81 
82  mx::math::normDistT<realT> m_normDist;
83 
84  mx::improc::eigenCube<realT> m_shapes;
85 
86  IMAGE m_imageStream;
87  uint32_t m_width {0}; ///< The width of the image
88  uint32_t m_height {0}; ///< The height of the image.
89 
91 
92  uint8_t m_dataType{0}; ///< The ImageStreamIO type code.
93  size_t m_typeSize {0}; ///< The size of the type, in bytes.
94 
95  bool m_opened {true};
96  bool m_restart {false};
97 
98  bool m_modulating {false};
99 
100 public:
101  /// Default c'tor.
102  dmModulator();
103 
104  /// D'tor, declared and defined for noexcept.
105  ~dmModulator() noexcept
106  {}
107 
108  virtual void setupConfig();
109 
110  /// Implementation of loadConfig logic, separated for testing.
111  /** This is called by loadConfig().
112  */
113  int loadConfigImpl( mx::app::appConfigurator & _config /**< [in] an application configuration from which to load values*/);
114 
115  virtual void loadConfig();
116 
117  /// Startup function
118  /**
119  *
120  */
121  virtual int appStartup();
122 
123  /// Implementation of the FSM for dmModulator.
124  /**
125  * \returns 0 on no critical error
126  * \returns -1 on an error requiring shutdown
127  */
128  virtual int appLogic();
129 
130  /// Shutdown the app.
131  /**
132  *
133  */
134  virtual int appShutdown();
135 
136 protected:
137 
138  /** \name Modulator Thread
139  * This thread sends the signal to the dm at the prescribed frequency
140  *
141  * @{
142  */
143  int m_modThreadPrio {60}; ///< Priority of the modulator thread, should normally be > 00.
144 
145  std::string m_modThreadCpuset; ///< The cpuset for the modulator thread.
146 
147  std::thread m_modThread; ///< A separate thread for the modulation
148 
149  bool m_modThreadInit {true}; ///< Synchronizer to ensure f.g. thread initializes before doing dangerous things.
150 
151  pid_t m_modThreadID {0}; ///< Modulate thread PID.
152 
153  pcf::IndiProperty m_modThreadProp; ///< The property to hold the modulator thread details.
154 
155  ///Thread starter, called by modThreadStart on thread construction. Calls modThreadExec.
156  static void modThreadStart( dmModulator * d /**< [in] a pointer to a dmModulator instance (normally this) */);
157 
158  /// Execute the frame grabber main loop.
159  void modThreadExec();
160 
161  ///@}
162 
163  //INDI:
164 protected:
165  //declare our properties
166  pcf::IndiProperty m_indiP_dm;
167  pcf::IndiProperty m_indiP_amp;
168  pcf::IndiProperty m_indiP_frequency;
169  pcf::IndiProperty m_indiP_trigger;
170  pcf::IndiProperty m_indiP_modulating;
171  pcf::IndiProperty m_indiP_zero;
172 
173  std::vector<std::string> m_elNames;
174 public:
180 
181 };
182 
183 dmModulator::dmModulator() : MagAOXApp(MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED)
184 {
185  return;
186 }
187 
189 {
190  config.add("dm.shapeCube", "", "dm.shapeCube", argType::Required, "dm", "shapeCube", false, "string", "Full path to the FITS file containing the shapes to modulate on this DM.");
191  config.add("dm.name", "", "dm.name", argType::Required, "dm", "name", false, "string", "The descriptive name of this dm. Default is the channel name.");
192  config.add("dm.channelName", "", "dm.channelName", argType::Required, "dm", "channelName", false, "string", "The name of the DM channel to write to.");
193  config.add("dm.triggerChannel", "", "dm.triggerChannel", argType::Required, "dm", "triggerChannel", false, "string", "The name of the DM channel to trigger on.");
194  config.add("dm.triggerSemaphore", "", "dm.triggerSemaphore", argType::Required, "dm", "triggerSemaphore", false, "int", "The semaphore to use (default 9).");
195  config.add("dm.trigger", "", "dm.trigger", argType::True, "dm", "trigger", false, "bool", "Run in trigger mode if true (default).");
196  config.add("dm.amp", "", "dm.amp", argType::Required, "dm", "amp", false, "float", "The speckle amplitude on the DM (default 0.01).");
197 
198  config.add("dm.frequency", "", "dm.frequency", argType::Required, "dm", "frequency", false, "float", "The frequency to modulate at if not triggering (default 2000 Hz).");
199 
200  config.add("modulator.threadPrio", "", "modulator.threadPrio", argType::Required, "modulator", "threadPrio", false, "int", "The real-time priority of the modulator thread.");
201 
202  config.add("modulator.cpuset", "", "modulator.cpuset", argType::Required, "modulator", "cpuset", false, "string", "The cpuset to assign the modulator thread to.");
203 
204 }
205 
206 int dmModulator::loadConfigImpl( mx::app::appConfigurator & _config )
207 {
208  _config(m_dmChannelName, "dm.channelName");
209 
211  _config(m_dmName, "dm.name");
212 
213  _config(m_dmTriggerChannel, "dm.triggerChannel");
214  _config(m_triggerSemaphore, "dm.triggerSemaphore");
215  if(_config.isSet("dm.trigger")) _config(m_trigger, "dm.trigger");
216  _config(m_amp, "dm.amp");
217  _config(m_frequency, "dm.frequency");
218  _config(m_frequency, "dm.frequency");
219  _config(m_shapeCube,"dm.shapeCube" );
220  _config(m_modThreadPrio, "modulator.threadPrio");
221  _config(m_modThreadCpuset, "modulator.cpuset");
222 
223  return 0;
224 }
225 
227 {
228  loadConfigImpl(config);
229 }
230 
232 {
233  mx::fits::fitsFile<realT> ff;
234 
235  if(ff.read(m_shapes, m_shapeCube) < 0)
236  {
237  return log<text_log,-1>("Could not open mode cube file", logPrio::LOG_ERROR);
238  }
239 
240  REG_INDI_NEWPROP_NOCB(m_indiP_dm, "dm", pcf::IndiProperty::Text);
241  m_indiP_dm.add(pcf::IndiElement("name"));
242  m_indiP_dm["name"] = m_dmName;
243  m_indiP_dm.add(pcf::IndiElement("channel"));
244  m_indiP_dm["channel"] = m_dmChannelName;
245 
246  createStandardIndiNumber<float>(m_indiP_amp, "amp", -1, 0,1, "%f");
247  m_indiP_amp["current"] = m_amp;
248  m_indiP_amp["target"] = m_amp;
250 
251  createStandardIndiNumber<float>(m_indiP_frequency, "frequency", 0, 0,10000, "%f");
252  m_indiP_frequency["current"] = m_frequency;
253  m_indiP_frequency["target"] = m_frequency;
255 
258  {
259  log<software_error>({__FILE__,__LINE__});
260  return -1;
261  }
262  if(m_trigger)
263  {
264  m_indiP_trigger["toggle"] = pcf::IndiElement::On;
265  }
266  else
267  {
268  m_indiP_trigger["toggle"] = pcf::IndiElement::Off;
269  }
270 
273  {
274  log<software_error>({__FILE__,__LINE__});
275  return -1;
276  }
277 
280  {
281  log<software_error>({__FILE__,__LINE__});
282  return -1;
283  }
284 
286  {
287  log<software_critical>({__FILE__, __LINE__});
288  return -1;
289  }
290 
292 
293 
294  return 0;
295 }
296 
298 {
300  {
301  m_opened = false;
302  m_restart = false; //Set this up front, since we're about to restart.
303 
304  if( ImageStreamIO_openIm(&m_imageStream, m_dmChannelName.c_str()) == 0)
305  {
306  if(m_imageStream.md[0].sem < 10) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
307  {
308  ImageStreamIO_closeIm(&m_imageStream);
309  }
310  else
311  {
312  m_opened = true;
313  }
314  }
315 
316  //Only bother to try if previous worked and we have a spec
317  if(m_opened == true && m_dmTriggerChannel != "")
318  {
319  if( ImageStreamIO_openIm(&m_triggerStream, m_dmTriggerChannel.c_str()) == 0)
320  {
321  if(m_triggerStream.md[0].sem < 10) ///<\todo this is hardcoded in ImageStreamIO.c -- should be a define
322  {
323  ImageStreamIO_closeIm(&m_triggerStream);
324  m_opened = false;
325  }
326  }
327  }
328 
329  if(m_opened)
330  {
332  }
333  }
334 
336  {
337  m_dataType = m_imageStream.md[0].datatype;
338  m_typeSize = ImageStreamIO_typesize(m_dataType);
339  m_width = m_imageStream.md[0].size[0];
340  m_height = m_imageStream.md[0].size[1];
341 
342 
343  if(m_dataType != _DATATYPE_FLOAT )
344  {
345  return log<text_log,-1>("Data type of DM channel is not float.", logPrio::LOG_CRITICAL);
346  }
347 
348  if(m_typeSize != sizeof(realT))
349  {
350  return log<text_log,-1>("Type-size mismatch, realT is not float.", logPrio::LOG_CRITICAL);
351  }
352 
354  }
355 
356  return 0;
357 }
358 
360 {
361  if(m_modThread.joinable())
362  {
363  try
364  {
365  m_modThread.join(); //this will throw if it was already joined
366  }
367  catch(...)
368  {
369  }
370  }
371 
372  return 0;
373 }
374 
375 
376 inline
378 {
379  d->modThreadExec();
380 }
381 
382 inline
384 {
385  m_modThreadID = syscall(SYS_gettid);
386 
387  mx::improc::eigenCube<realT> ampShapes;
388 
389  mx::improc::eigenImage<realT> noise;
390 
391  //Wait fpr the thread starter to finish initializing this thread.
392  while( (m_modThreadInit == true || state() != stateCodes::READY) && m_shutdown == 0)
393  {
394  sleep(1);
395  }
396 
397  while(m_shutdown == 0)
398  {
399  if(!m_modulating && !m_shutdown) //If we aren't modulating we sleep for 1/2 a second
400  {
401  mx::sys::milliSleep(500);
402  }
403 
404  if(m_modulating && !m_shutdown)
405  {
406 
407  int64_t freqNsec = (1.0/m_frequency)*1e9;
409  int64_t dnsec;
410 
411  int idx = 0;
412 
413  timespec modstart;
414  timespec currtime;
415 
416  bool triggered = false;
417  sem_t * sem = nullptr;
418  if(m_dmTriggerChannel == "")
419  {
420  m_trigger = false;
421  indi::updateSwitchIfChanged(m_indiP_trigger, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE);
422  }
423  else if(m_trigger == true)
424  {
425  ImageStreamIO_semflush(&m_triggerStream, m_triggerSemaphore);
426 
427  sem = m_triggerStream.semptr[m_triggerSemaphore]; ///< The semaphore to monitor for new image data
428  }
429 
430  ampShapes.resize(m_shapes.rows(), m_shapes.cols(), m_shapes.planes());
431  noise.resize(m_width, m_height);
432  for(int cc=0;cc<noise.cols(); ++cc)
433  {
434  for(int rr=0;rr<noise.rows(); ++rr)
435  {
436  noise(rr,cc) = m_normDist*m_amp;
437  }
438  }
439 
440  for(int p =0; p < ampShapes.planes(); ++p)
441  {
442  ampShapes.image(p) = m_shapes.image(p) * m_amp;
443  }
444  updateIfChanged(m_indiP_amp, "current", m_amp);
445 
446  log<text_log>("started modulating",logPrio::LOG_NOTICE);
447 
448  dnsec = freqNsec;
449  clock_gettime(CLOCK_REALTIME, &modstart);
450 
451  while(m_modulating && !m_shutdown)
452  {
453  if(m_trigger)
454  {
455  timespec ts;
456 
457  if(clock_gettime(CLOCK_REALTIME, &ts) < 0)
458  {
459  log<software_critical>({__FILE__,__LINE__,errno,0,"clock_gettime"});
460  return;
461  }
462 
463  ts.tv_sec += 1;
464 
465  if(sem_timedwait(sem, &ts) == 0)
466  {
467  triggered = true;
468  }
469  else
470  {
471  triggered = false;
472 
473  //Check for why we timed out
474  if(errno == EINTR) break; //This indicates signal interrupted us, time to restart or shutdown, loop will exit normally if flags set.
475 
476  //ETIMEDOUT just means we should wait more.
477  //Otherwise, report an error.
478  if(errno != ETIMEDOUT)
479  {
480  log<software_error>({__FILE__, __LINE__,errno, "sem_timedwait"});
481  break;
482  }
483  }
484  }
485  else
486  {
487  mx::sys::nanoSleep(0.5*dnsec);
488  clock_gettime(CLOCK_REALTIME, &currtime);
489 
490  dnsec = (currtime.tv_sec - modstart.tv_sec)*1000000000 + (currtime.tv_nsec - modstart.tv_nsec);
491  triggered = false;
492  }
493 
494  if(dnsec >= freqNsec || triggered)
495  {
496  //Do the write
497 
498  m_imageStream.md->write = 1;
499 
500  if(m_noise)
501  {
502  memcpy(m_imageStream.array.raw, noise.data(), m_width*m_height*m_typeSize);
503  }
504  else
505  {
506  memcpy(m_imageStream.array.raw, ampShapes.image(idx).data(), m_width*m_height*m_typeSize);
507  }
508 
509  m_imageStream.md->atime = currtime;
510  m_imageStream.md->writetime = currtime;
511 
512  if(!m_trigger) m_imageStream.md->cnt0++;
513 
514  m_imageStream.md->write=0;
515  ImageStreamIO_sempost(&m_imageStream,-1);
516 
517  ++idx;
518  if(idx >= m_shapes.planes()) idx=0;
519 
520  if(m_noise)
521  {
522  for(int cc=0;cc<noise.cols(); ++cc)
523  {
524  for(int rr=0;rr<noise.rows(); ++rr)
525  {
526  noise(rr,cc) = m_normDist*m_amp;
527  }
528  }
529  }
530 
531  if(!m_trigger)
532  {
533  modstart.tv_nsec += freqNsec;
534  if(modstart.tv_nsec >= 1000000000)
535  {
536  modstart.tv_nsec -= 1000000000;
537  modstart.tv_sec += 1;
538  }
539  dnsec = freqNsec;
540  }
541  }
542  }
543  log<text_log>("stopped modulating", logPrio::LOG_NOTICE);
544  //Always zero when done
545  clock_gettime(CLOCK_REALTIME, &currtime);
546  m_imageStream.md->write = 1;
547 
548  memset(m_imageStream.array.raw, 0.0, m_width*m_height*m_typeSize);
549 
550  m_imageStream.md->atime = currtime;
551  m_imageStream.md->writetime = currtime;
552 
553  if(!m_trigger) m_imageStream.md->cnt0++;
554 
555  m_imageStream.md->write=0;
556  ImageStreamIO_sempost(&m_imageStream,-1);
557  log<text_log>("zeroed");
558 
559  }
560  }
561 
562 }
563 
564 INDI_NEWCALLBACK_DEFN(dmModulator, m_indiP_amp)(const pcf::IndiProperty &ipRecv)
565 {
566  if(ipRecv.getName() != m_indiP_amp.getName())
567  {
568  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
569  return -1;
570  }
571 
572  float amp = -1000000000;
573 
574  if( ipRecv.find("current") )
575  {
576  amp = ipRecv["current"].get<float>();
577  }
578 
579  if( ipRecv.find("target") )
580  {
581  amp = ipRecv["target"].get<float>();
582  }
583 
584  if(amp == -1000000000)
585  {
586  log<software_error>({__FILE__,__LINE__, "Invalid requested amp: " + std::to_string(amp)});
587  return 0;
588  }
589 
590  std::unique_lock<std::mutex> lock(m_indiMutex);
591  m_amp = amp;
592  updateIfChanged(m_indiP_amp, "target", m_amp);
593  return 0;
594 }
595 
596 INDI_NEWCALLBACK_DEFN(dmModulator, m_indiP_frequency)(const pcf::IndiProperty &ipRecv)
597 {
598  if(ipRecv.getName() != m_indiP_frequency.getName())
599  {
600  log<software_error>({__FILE__,__LINE__, "wrong INDI property received."});
601  return -1;
602  }
603 
604  float freq = -1;
605 
606  if( ipRecv.find("current") )
607  {
608  freq = ipRecv["current"].get<float>();
609  }
610 
611  if( ipRecv.find("target") )
612  {
613  freq = ipRecv["target"].get<float>();
614  }
615 
616  if(freq < 0)
617  {
618  log<software_error>({__FILE__,__LINE__, "Invalid requested frequency: " + std::to_string(freq)});
619  return 0;
620  }
621 
622  std::unique_lock<std::mutex> lock(m_indiMutex);
623  m_frequency = freq;
624  updateIfChanged(m_indiP_frequency, "target", m_frequency);
625  return 0;
626 }
627 
628 INDI_NEWCALLBACK_DEFN(dmModulator, m_indiP_trigger)(const pcf::IndiProperty &ipRecv)
629 {
630  if(ipRecv.getName() != m_indiP_trigger.getName())
631  {
632  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
633  return -1;
634  }
635 
636  if(!ipRecv.find("toggle")) return 0;
637 
638  std::unique_lock<std::mutex> lock(m_indiMutex);
639 
640  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
641  {
642  m_trigger = false;
643  indi::updateSwitchIfChanged(m_indiP_trigger, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE);
644  }
645 
646  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
647  {
648  m_trigger = true;
649  indi::updateSwitchIfChanged(m_indiP_trigger, "toggle", pcf::IndiElement::On, m_indiDriver, INDI_OK);
650  }
651 
652  return 0;
653 }
654 
655 INDI_NEWCALLBACK_DEFN(dmModulator, m_indiP_modulating)(const pcf::IndiProperty &ipRecv)
656 {
657  if(ipRecv.getName() != m_indiP_modulating.getName())
658  {
659  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
660  return -1;
661  }
662 
663  if(!ipRecv.find("toggle")) return 0;
664 
665  std::unique_lock<std::mutex> lock(m_indiMutex);
666 
667  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::Off)
668  {
669  m_modulating = false;
670  indi::updateSwitchIfChanged(m_indiP_modulating, "toggle", pcf::IndiElement::Off, m_indiDriver, INDI_IDLE);
671  }
672 
673  if( ipRecv["toggle"].getSwitchState() == pcf::IndiElement::On)
674  {
675  m_modulating = true;
676  indi::updateSwitchIfChanged(m_indiP_modulating, "toggle", pcf::IndiElement::On, m_indiDriver, INDI_OK);
677  }
678 
679 
680  return 0;
681 }
682 
683 INDI_NEWCALLBACK_DEFN(dmModulator, m_indiP_zero)(const pcf::IndiProperty &ipRecv)
684 {
685  if(ipRecv.getName() != m_indiP_zero.getName())
686  {
687  log<software_error>({__FILE__, __LINE__, "invalid indi property received"});
688  return -1;
689  }
690 
691  if(m_modulating == true)
692  {
693  log<text_log>("zero requested but currently modulating", logPrio::LOG_NOTICE);
694  return 0;
695  }
696 
697  if(!ipRecv.find("request")) return 0;
698 
699  if( ipRecv["request"].getSwitchState() == pcf::IndiElement::On)
700  {
701  m_imageStream.md->write = 1;
702 
703  memset(m_imageStream.array.raw, 0, m_width*m_height*m_typeSize);
704  timespec currtime;
705  clock_gettime(CLOCK_REALTIME, &currtime);
706  m_imageStream.md->atime = currtime;
707  m_imageStream.md->writetime = currtime;
708 
709  m_imageStream.md->cnt0++;
710 
711  m_imageStream.md->write=0;
712  ImageStreamIO_sempost(&m_imageStream,-1);
713  log<text_log>("zeroed");
714  }
715 
716 
717  return 0;
718 }
719 
720 
721 } //namespace app
722 } //namespace MagAOX
723 
724 #endif //dmModulator_hpp
The base-class for MagAO-X applications.
Definition: MagAOXApp.hpp:75
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, pcf::IndiProperty::PropertyStateType ipState=pcf::IndiProperty::Ok)
Update an INDI property element value if it has changed.
Definition: MagAOXApp.hpp:2877
int createStandardIndiRequestSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single request element.
Definition: MagAOXApp.hpp:2352
stateCodes::stateCodeT state()
Get the current state code.
Definition: MagAOXApp.hpp:2082
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
int createStandardIndiToggleSw(pcf::IndiProperty &prop, const std::string &name, const std::string &label="", const std::string &group="")
Create a standard R/W INDI switch with a single toggle element.
Definition: MagAOXApp.hpp:2321
int m_shutdown
Flag to signal it's time to shutdown. When not 0, the main loop exits.
Definition: MagAOXApp.hpp:102
indiDriver< MagAOXApp > * m_indiDriver
The INDI driver wrapper. Constructed and initialized by execute, which starts and stops communication...
Definition: MagAOXApp.hpp:537
static int log(const typename logT::messageT &msg, logPrioT level=logPrio::LOG_DEFAULT)
Make a log entry.
Definition: MagAOXApp.hpp:1590
int threadStart(std::thread &thrd, bool &thrdInit, pid_t &tpid, pcf::IndiProperty &thProp, int thrdPrio, const std::string &cpuset, const std::string &thrdName, thisPtr *thrdThis, Function &&thrdStart)
Start a thread, using this class's privileges to set priority, etc.
Definition: MagAOXApp.hpp:1950
The MagAO-X DM mode commander.
Definition: dmModulator.hpp:50
~dmModulator() noexcept
D'tor, declared and defined for noexcept.
std::vector< std::string > m_elNames
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_amp)
bool m_modThreadInit
Synchronizer to ensure f.g. thread initializes before doing dangerous things.
pcf::IndiProperty m_indiP_amp
realT m_frequency
The frequency to modulate at if not triggering (default 2000 Hz)
Definition: dmModulator.hpp:76
virtual int appLogic()
Implementation of the FSM for dmModulator.
virtual int appStartup()
Startup function.
mx::improc::eigenCube< realT > m_shapes
Definition: dmModulator.hpp:84
std::thread m_modThread
A separate thread for the modulation.
pid_t m_modThreadID
Modulate thread PID.
pcf::IndiProperty m_indiP_trigger
virtual int appShutdown()
Shutdown the app.
pcf::IndiProperty m_indiP_dm
virtual void loadConfig()
std::string m_dmChannelName
The name of the DM channel to write to.
Definition: dmModulator.hpp:66
static void modThreadStart(dmModulator *d)
Thread starter, called by modThreadStart on thread construction. Calls modThreadExec.
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_zero)
dmModulator()
Default c'tor.
mx::math::normDistT< realT > m_normDist
Definition: dmModulator.hpp:82
void modThreadExec()
Execute the frame grabber main loop.
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_modulating)
std::string m_modThreadCpuset
The cpuset for the modulator thread.
uint32_t m_height
The height of the image.
Definition: dmModulator.hpp:88
virtual void setupConfig()
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_trigger)
int m_modThreadPrio
Priority of the modulator thread, should normally be > 00.
size_t m_typeSize
The size of the type, in bytes.
Definition: dmModulator.hpp:93
friend class dmModulator_test
Definition: dmModulator.hpp:54
realT m_amp
The speckle amplitude on the DM.
Definition: dmModulator.hpp:74
int loadConfigImpl(mx::app::appConfigurator &_config)
Implementation of loadConfig logic, separated for testing.
pcf::IndiProperty m_indiP_modulating
std::string m_dmName
The descriptive name of this dm. Default is the channel name.
Definition: dmModulator.hpp:64
pcf::IndiProperty m_indiP_frequency
uint32_t m_width
The width of the image.
Definition: dmModulator.hpp:87
bool m_trigger
Run in trigger mode if true (default)
Definition: dmModulator.hpp:72
pcf::IndiProperty m_modThreadProp
The property to hold the modulator thread details.
uint8_t m_dataType
The ImageStreamIO type code.
Definition: dmModulator.hpp:92
int m_triggerSemaphore
The semaphore to use (default 9)
Definition: dmModulator.hpp:70
pcf::IndiProperty m_indiP_zero
INDI_NEWCALLBACK_DECL(dmModulator, m_indiP_frequency)
std::string m_dmTriggerChannel
The DM channel to monitor as a trigger.
Definition: dmModulator.hpp:68
#define REG_INDI_NEWPROP_NOCB(prop, propName, type)
Register a NEW INDI property with the class, with no callback.
Definition: indiMacros.hpp:247
#define INDI_NEWCALLBACK(prop)
Get the name of the static callback wrapper for a new property.
Definition: indiMacros.hpp:207
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
@ CONNECTED
The application has connected to the device or service.
Definition: stateCodes.hpp:45
@ NOTCONNECTED
The application is not connected to the device or service.
Definition: stateCodes.hpp:44
#define INDI_IDLE
Definition: indiUtils.hpp:28
#define INDI_OK
Definition: indiUtils.hpp:29
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:206
const pcf::IndiProperty & ipRecv
void nanoSleep(unsigned long nsec)
INDI_NEWCALLBACK_DEFN(acesxeCtrl, m_indiP_windspeed)(const pcf
Definition: acesxeCtrl.hpp:687
Definition: dm.hpp:24
constexpr static logPrioT LOG_CRITICAL
The process can not continue and will shut down (fatal)
Definition: logPriority.hpp:37
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
A simple text log, a string-type log.
Definition: text_log.hpp:24