API
frameGrabber.hpp
Go to the documentation of this file.
1 /** \file frameGrabber.hpp
2  * \brief The MagAO-X generic frame grabber.
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup app_files
7  */
8 
9 #ifndef frameGrabber_hpp
10 #define frameGrabber_hpp
11 
12 #include <sys/syscall.h>
13 
14 #include <mx/sigproc/circularBuffer.hpp>
15 #include <mx/math/vectorUtils.hpp>
16 #include <mx/improc/imageUtils.hpp>
17 
18 #include <ImageStreamIO/ImageStruct.h>
19 #include <ImageStreamIO/ImageStreamIO.h>
20 
21 #include "../../common/paths.hpp"
22 
23 
24 namespace MagAOX
25 {
26 namespace app
27 {
28 namespace dev
29 {
30 
31 
32 
33 
34 /** MagAO-X generic frame grabber
35  *
36  *
37  * The derived class `derivedT` must expose the following interface
38  * \code
39  //Configures the camera for acquistion, must also set m_width, m_height, and m_dataType
40  //so that the shared memory can be allocated
41  int derivedT::configureAcquisition();
42 
43  //Gets the frames-per-second readout rate
44  //used for the latency statistics
45  float derivedT::fps();
46 
47  //Start acquisition.
48  int derivedT::startAcquisition();
49 
50  //Acquires the data, and checks if it is valid.
51  //This should set m_currImageTimestamp to the image timestamp.
52  // returns 0 if valid, < 0 on error, > 0 on no data.
53  int derivedT::acquireAndCheckValid()
54 
55  //Loads the acquired image into the stream, copying it to the appropriate member of m_imageStream->array.
56  //This could simply be a memcpy.
57  int derivedT::loadImageIntoStream(void * dest);
58 
59  //Take any actions needed to reconfigure the system. Called if m_reconfig is set to true.
60  int derivedT::reconfig()
61  * \endcode
62  * Each of the above functions should return 0 on success, and -1 on an error (except fps).
63  * For `acquireAndCheckValid` >0 will indicate no data but not an error. In most cases,
64  * an appropriate state code, such as NOTCONNECTED, should be set as well.
65  *
66  * A static configuration variable must be defined in derivedT as
67  * \code
68  * static constexpr bool c_frameGrabber_flippable =true; //or: false
69  * \endcode
70  * which determines whether or not the images can be flipped programatically.
71  *
72  * Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic` and `appShutdown`
73  * functions must be placed in the derived class's functions of the same name.
74  *
75  * \ingroup appdev
76  */
77 template<class derivedT>
79 {
80 public:
82 
83 protected:
84 
85  /** \name Configurable Parameters
86  * @{
87  */
88  std::string m_shmimName {""}; ///< The name of the shared memory image, is used in `/tmp/<shmimName>.im.shm`. Derived classes should set a default.
89 
90  int m_fgThreadPrio {2}; ///< Priority of the framegrabber thread, should normally be > 00.
91  std::string m_fgCpuset; ///< The cpuset to assign the framegrabber thread to. Not used if empty, the default.
92 
93  uint32_t m_circBuffLength {1}; ///< Length of the circular buffer, in frames
94 
95  uint16_t m_latencyCircBuffMaxLength {3600}; ///< Maximum length of the latency measurement circular buffers
96  float m_latencyCircBuffMaxTime {5}; ///< Maximum time of the latency meaurement circular buffers
97 
99 
100  ///@}
101 
103 
104  uint32_t m_width {0}; ///< The width of the image, once deinterlaced etc.
105  uint32_t m_height {0}; ///< The height of the image, once deinterlaced etc.
106 
107  uint8_t m_dataType{0}; ///< The ImageStreamIO type code.
108  size_t m_typeSize {0}; ///< The size of the type, in bytes. Result of sizeof.
109 
110  int m_xbinning {0}; ///< The x-binning according to the framegrabber
111  int m_ybinning {0}; ///< The y-binning according to the framegrabber
112 
113 
114  timespec m_currImageTimestamp {0,0}; ///< The timestamp of the current image.
115 
116  bool m_reconfig {false}; ///< Flag to set if a camera reconfiguration requires a framegrabber reset.
117 
118  IMAGE * m_imageStream {nullptr}; ///< The ImageStreamIO shared memory buffer.
119 
120  typedef uint16_t cbIndexT;
121 
122  mx::sigproc::circularBufferIndex<timespec, cbIndexT> m_atimes;
123  mx::sigproc::circularBufferIndex<timespec, cbIndexT> m_wtimes;
124 
125  std::vector<double> m_atimesD;
126  std::vector<double> m_wtimesD;
127  std::vector<double> m_watimesD;
128 
129  timespec m_dummy_ts {0,0};
130  uint64_t m_dummy_cnt {0};
131  char m_dummy_c {0};
132 
133  double m_mna;
134  double m_vara;
135 
136  double m_mnw;
137  double m_varw;
138 
139  double m_mnwa;
140  double m_varwa;
141 
142 
143 
144 
145 public:
146 
147  /// Setup the configuration system
148  /**
149  * This should be called in `derivedT::setupConfig` as
150  * \code
151  framegrabber<derivedT>::setupConfig(config);
152  \endcode
153  * with appropriate error checking.
154  */
155  void setupConfig(mx::app::appConfigurator & config /**< [out] the derived classes configurator*/);
156 
157  /// load the configuration system results
158  /**
159  * This should be called in `derivedT::loadConfig` as
160  * \code
161  framegrabber<derivedT>::loadConfig(config);
162  \endcode
163  * with appropriate error checking.
164  */
165  void loadConfig(mx::app::appConfigurator & config /**< [in] the derived classes configurator*/);
166 
167  /// Startup function
168  /** Starts the framegrabber thread
169  * This should be called in `derivedT::appStartup` as
170  * \code
171  framegrabber<derivedT>::appStartup();
172  \endcode
173  * with appropriate error checking.
174  *
175  * \returns 0 on success
176  * \returns -1 on error, which is logged.
177  */
178  int appStartup();
179 
180  /// Checks the framegrabber thread
181  /** This should be called in `derivedT::appLogic` as
182  * \code
183  framegrabber<derivedT>::appLogic();
184  \endcode
185  * with appropriate error checking.
186  *
187  * \returns 0 on success
188  * \returns -1 on error, which is logged.
189  */
190  int appLogic();
191 
192  /// On power off, sets m_reconfig to true.
193  /** This should be called in `derivedT::onPowerOff` as
194  * \code
195  framegrabber<derivedT>::onPowerOff();
196  \endcode
197  * with appropriate error checking.
198  *
199  * \returns 0 on success
200  * \returns -1 on error, which is logged.
201  */
202  int onPowerOff();
203 
204  /// Shuts down the framegrabber thread
205  /** This should be called in `derivedT::appShutdown` as
206  * \code
207  framegrabber<derivedT>::appShutdown();
208  \endcode
209  * with appropriate error checking.
210  *
211  * \returns 0 on success
212  * \returns -1 on error, which is logged.
213  */
214  int appShutdown();
215 
216 protected:
217 
218 
219  /** \name Framegrabber Thread
220  * This thread actually manages the framegrabbing hardware
221  * @{
222  */
223 
224  bool m_fgThreadInit {true}; ///< Synchronizer for thread startup, to allow priority setting to finish.
225 
226  pid_t m_fgThreadID {0}; ///< The ID of the framegrabber thread.
227 
228  pcf::IndiProperty m_fgThreadProp; ///< The property to hold the f.g. thread details.
229 
230  std::thread m_fgThread; ///< A separate thread for the actual framegrabbings
231 
232  ///Thread starter, called by MagAOXApp::threadStart on thread construction. Calls fgThreadExec.
233  static void fgThreadStart( frameGrabber * o /**< [in] a pointer to a frameGrabber instance (normally this) */);
234 
235  /// Execute framegrabbing.
236  void fgThreadExec();
237 
238 
239  ///@}
240 
241  void * loadImageIntoStreamCopy( void * dest,
242  void * src,
243  size_t width,
244  size_t height,
245  size_t szof
246  );
247 
248 
249  /** \name INDI
250  *
251  *@{
252  */
253 protected:
254  //declare our properties
255 
256  pcf::IndiProperty m_indiP_shmimName; ///< Property used to report the shmim buffer name
257 
258  pcf::IndiProperty m_indiP_frameSize; ///< Property used to report the current frame size
259 
260  pcf::IndiProperty m_indiP_timing;
261 public:
262 
263  /// Update the INDI properties for this device controller
264  /** You should call this once per main loop.
265  * It is not called automatically.
266  *
267  * \returns 0 on success.
268  * \returns -1 on error.
269  */
270  int updateINDI();
271 
272  ///@}
273 
274  /** \name Telemeter Interface
275  * @{
276  */
277 
278  int recordFGTimings( bool force = false );
279 
280  /// @}
281 
282 private:
283  derivedT & derived()
284  {
285  return *static_cast<derivedT *>(this);
286  }
287 };
288 
289 template<class derivedT>
290 void frameGrabber<derivedT>::setupConfig(mx::app::appConfigurator & config)
291 {
292  config.add("framegrabber.threadPrio", "", "framegrabber.threadPrio", argType::Required, "framegrabber", "threadPrio", false, "int", "The real-time priority of the framegrabber thread.");
293 
294  config.add("framegrabber.cpuset", "", "framegrabber.cpuset", argType::Required, "framegrabber", "cpuset", false, "string", "The cpuset to assign the framegrabber thread to.");
295 
296  config.add("framegrabber.shmimName", "", "framegrabber.shmimName", argType::Required, "framegrabber", "shmimName", false, "string", "The name of the ImageStreamIO shared memory image. Will be used as /milk/shm/<shmimName>.im.shm.");
297 
298  config.add("framegrabber.circBuffLength", "", "framegrabber.circBuffLength", argType::Required, "framegrabber", "circBuffLength", false, "size_t", "The length of the circular buffer. Sets m_circBuffLength, default is 1.");
299 
300  config.add("framegrabber.latencyTime", "", "framegrabber.latencyTime", argType::Required, "framegrabber", "latencyTime", false, "float", "The maximum length of time to measure latency timings. Sets m_latencyCircBuffMaxTime, default is 5.");
301 
302  config.add("framegrabber.latencySize", "", "framegrabber.latencySize", argType::Required, "framegrabber", "latencySize", false, "float", "The maximum length of the buffer used to measure latency timings. Sets m_latencyCircBuffMaxLength, default is 3600.");
303 
304 
305  if(derivedT::c_frameGrabber_flippable)
306  {
307  config.add("framegrabber.defaultFlip", "", "framegrabber.defaultFlip", argType::Required, "framegrabber", "defaultFlip", false, "string", "The default flip of the image. Options are flipNone, flipUD, flipLR, flipUDLR. The default is flipNone.");
308  }
309 }
310 
311 template<class derivedT>
312 void frameGrabber<derivedT>::loadConfig(mx::app::appConfigurator & config)
313 {
314  config(m_fgThreadPrio, "framegrabber.threadPrio");
315  config(m_fgCpuset, "framegrabber.cpuset");
316  if(m_shmimName == "") m_shmimName = derived().configName();
317  config(m_shmimName, "framegrabber.shmimName");
318 
319  config(m_circBuffLength, "framegrabber.circBuffLength");
320 
321  if(m_circBuffLength < 1)
322  {
323  m_circBuffLength = 1;
324  derivedT::template log<text_log>("circBuffLength set to 1");
325  }
326 
327  config(m_latencyCircBuffMaxTime, "framegrabber.latencyTime");
328  if(m_latencyCircBuffMaxTime < 0)
329  {
330  m_latencyCircBuffMaxTime = 0;
331  derivedT::template log<text_log>("latencyTime set to 0 (off)");
332  }
333 
334  config(m_latencyCircBuffMaxLength, "framegrabber.latencySize");
335 
336 
337  if(derivedT::c_frameGrabber_flippable)
338  {
339  std::string flip = "flipNone";
340  config(flip, "framegrabber.defaultFlip");
341  if(flip == "flipNone")
342  {
343  m_defaultFlip = fgFlipNone;
344  }
345  else if(flip == "flipUD")
346  {
347  m_defaultFlip = fgFlipUD;
348  }
349  else if(flip == "flipLR")
350  {
351  m_defaultFlip = fgFlipLR;
352  }
353  else if(flip == "flipUDLR")
354  {
355  m_defaultFlip = fgFlipUDLR;
356  }
357  else
358  {
359  derivedT::template log<text_log>({std::string("invalid framegrabber flip specification (") + flip + "), setting flipNone"}, logPrio::LOG_ERROR);
360  m_defaultFlip = fgFlipNone;
361  }
362  }
363 }
364 
365 
366 template<class derivedT>
368 {
369  //Register the shmimName INDI property
370  m_indiP_shmimName = pcf::IndiProperty(pcf::IndiProperty::Text);
371  m_indiP_shmimName.setDevice(derived().configName());
372  m_indiP_shmimName.setName("fg_shmimName");
373  m_indiP_shmimName.setPerm(pcf::IndiProperty::ReadOnly);
374  m_indiP_shmimName.setState(pcf::IndiProperty::Idle);
375  m_indiP_shmimName.add(pcf::IndiElement("name"));
376  m_indiP_shmimName["name"] = m_shmimName;
377 
378  if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr) < 0)
379  {
380  #ifndef FRAMEGRABBER_TEST_NOLOG
381  derivedT::template log<software_error>({__FILE__,__LINE__});
382  #endif
383  return -1;
384  }
385 
386  //Register the frameSize INDI property
387  m_indiP_frameSize = pcf::IndiProperty(pcf::IndiProperty::Number);
388  m_indiP_frameSize.setDevice(derived().configName());
389  m_indiP_frameSize.setName("fg_frameSize");
390  m_indiP_frameSize.setPerm(pcf::IndiProperty::ReadOnly);
391  m_indiP_frameSize.setState(pcf::IndiProperty::Idle);
392  m_indiP_frameSize.add(pcf::IndiElement("width"));
393  m_indiP_frameSize["width"] = 0;
394  m_indiP_frameSize.add(pcf::IndiElement("height"));
395  m_indiP_frameSize["height"] = 0;
396 
397  if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr) < 0)
398  {
399  #ifndef FRAMEGRABBER_TEST_NOLOG
400  derivedT::template log<software_error>({__FILE__,__LINE__});
401  #endif
402  return -1;
403  }
404 
405  //Register the timing INDI property
406  derived().createROIndiNumber( m_indiP_timing, "fg_timing");
407  m_indiP_timing.add(pcf::IndiElement("acq_fps"));
408  m_indiP_timing.add(pcf::IndiElement("acq_jitter"));
409  m_indiP_timing.add(pcf::IndiElement("write_fps"));
410  m_indiP_timing.add(pcf::IndiElement("write_jitter"));
411  m_indiP_timing.add(pcf::IndiElement("delta_aw"));
412  m_indiP_timing.add(pcf::IndiElement("delta_aw_jitter"));
413 
414  if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0)
415  {
416  #ifndef STDCAMERA_TEST_NOLOG
417  derivedT::template log<software_error>({__FILE__,__LINE__});
418  #endif
419  return -1;
420  }
421 
422  //Start the f.g. thread
423  if(derived().threadStart( m_fgThread, m_fgThreadInit, m_fgThreadID, m_fgThreadProp, m_fgThreadPrio, m_fgCpuset, "framegrabber", this, fgThreadStart) < 0)
424  {
425  derivedT::template log<software_error, -1>({__FILE__, __LINE__});
426  return -1;
427  }
428 
429  return 0;
430 
431 }
432 
433 template<class derivedT>
435 {
436  //do a join check to see if other threads have exited.
437  if(pthread_tryjoin_np(m_fgThread.native_handle(),0) == 0)
438  {
439  derivedT::template log<software_error>({__FILE__, __LINE__, "framegrabber thread has exited"});
440 
441  return -1;
442  }
443 
444  if( derived().state() == stateCodes::OPERATING && m_atimes.size() > 0 )
445  {
446  if(m_atimes.size() >= m_atimes.maxEntries())
447  {
448  cbIndexT refEntry = m_atimes.earliest();
449 
450  m_atimesD.resize(m_atimes.maxEntries()-1);
451  m_wtimesD.resize(m_wtimes.maxEntries()-1);
452  m_watimesD.resize(m_wtimes.maxEntries()-1);
453 
454  double a0 = m_atimes.at(refEntry, 0).tv_sec + ((double) m_atimes.at(refEntry, 0).tv_nsec)/1e9;
455  double w0 = m_wtimes.at(refEntry, 0).tv_sec + ((double) m_wtimes.at(refEntry, 0).tv_nsec)/1e9;
456  for(size_t n=1; n <= m_atimesD.size(); ++n)
457  {
458  double a = m_atimes.at(refEntry, n).tv_sec + ((double) m_atimes.at(refEntry, n).tv_nsec)/1e9;
459  double w = m_wtimes.at(refEntry, n).tv_sec + ((double) m_wtimes.at(refEntry, n).tv_nsec)/1e9;
460  m_atimesD[n-1] = a - a0;
461  m_wtimesD[n-1] = w - w0;
462  m_watimesD[n-1] = w - a;
463  a0 = a;
464  w0 = w;
465  }
466 
467  m_mna = mx::math::vectorMean(m_atimesD);
468  m_vara = mx::math::vectorVariance(m_atimesD, m_mna);
469 
470  m_mnw = mx::math::vectorMean(m_wtimesD);
471  m_varw = mx::math::vectorVariance(m_wtimesD, m_mnw);
472 
473  m_mnwa = mx::math::vectorMean(m_watimesD);
474  m_varwa = mx::math::vectorVariance(m_watimesD, m_mnwa);
475 
476  recordFGTimings();
477  }
478  else
479  {
480  m_mna = 0;
481  m_vara = 0;
482  m_mnw = 0;
483  m_varw = 0;
484  m_mnwa = 0;
485  m_varwa = 0;
486  }
487  }
488  else
489  {
490  m_mna = 0;
491  m_vara = 0;
492  m_mnw = 0;
493  m_varw = 0;
494  m_mnwa = 0;
495  m_varwa = 0;
496  }
497 
498  return 0;
499 
500 }
501 
502 template<class derivedT>
504 {
505  m_mna = 0;
506  m_vara = 0;
507  m_mnw = 0;
508  m_varw = 0;
509  m_mnwa = 0;
510  m_varwa = 0;
511 
512  m_width = 0;
513  m_height = 0;
514 
515  updateINDI();
516 
517  m_reconfig = true;
518 
519 
520  return 0;
521 }
522 
523 
524 template<class derivedT>
526 {
527  if(m_fgThread.joinable())
528  {
529  try
530  {
531  m_fgThread.join(); //this will throw if it was already joined
532  }
533  catch(...)
534  {
535  }
536  }
537 
538 
539 
540  return 0;
541 }
542 
543 
544 
545 template<class derivedT>
547 {
548  o->fgThreadExec();
549 }
550 
551 
552 template<class derivedT>
554 {
555  //Get the thread PID immediately so the caller can return.
556  m_fgThreadID = syscall(SYS_gettid);
557 
558  timespec writestart;
559 
560  //Wait fpr the thread starter to finish initializing this thread.
561  while(m_fgThreadInit == true && derived().shutdown() == 0)
562  {
563  sleep(1);
564  }
565 
566  uint32_t imsize[3] = {0,0,0};
567  std::string shmimName;
568 
569  while(derived().shutdown() == 0)
570  {
571  ///\todo this ought to wait until OPERATING, using READY as a sign of "not integrating"
572  while(!derived().shutdown() && (!( derived().state() == stateCodes::READY || derived().state() == stateCodes::OPERATING) || derived().powerState() <= 0 ) )
573  {
574  sleep(1);
575  }
576 
577  if(derived().shutdown()) continue;
578  else
579  {
580  //At the end of this, must have m_width, m_height, m_dataType set, and derived()->fps must be valid.
581  if(derived().configureAcquisition() < 0) continue;
582 
583  if(m_latencyCircBuffMaxLength == 0 || m_latencyCircBuffMaxTime == 0)
584  {
585  m_atimes.maxEntries(0);
586  m_wtimes.maxEntries(0);
587  }
588  else
589  {
590  //Set up the latency circ. buffs
591  cbIndexT cbSz = m_latencyCircBuffMaxTime * derived().fps();
592  if(cbSz > m_latencyCircBuffMaxLength) cbSz = m_latencyCircBuffMaxLength;
593  if(cbSz < 3) cbSz = 3; //Make variance meaningful
594  m_atimes.maxEntries(cbSz);
595  m_wtimes.maxEntries(cbSz);
596  }
597 
598  m_typeSize = ImageStreamIO_typesize(m_dataType);
599 
600 
601  //Here we resolve currentFlip somehow.
602  m_currentFlip = m_defaultFlip;
603  }
604 
605  /* Initialize ImageStreamIO
606  */
607  if(m_shmimName == "") m_shmimName = derived().configName();
608 
609  if(m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_shmimName != shmimName || m_imageStream == nullptr)
610  {
611  if(m_imageStream != nullptr)
612  {
613  ImageStreamIO_destroyIm(m_imageStream);
614  free(m_imageStream);
615  }
616 
617  m_imageStream = (IMAGE *) malloc(sizeof(IMAGE));
618 
619  imsize[0] = m_width;
620  imsize[1] = m_height;
621  imsize[2] = m_circBuffLength;
622  shmimName = m_shmimName;
623 
624  std::cerr << "Creating: " << m_shmimName << " " << m_width << " " << m_height << " " << m_circBuffLength << "\n";
625 
626  ImageStreamIO_createIm_gpu(m_imageStream, m_shmimName.c_str(), 3, imsize, m_dataType, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
627 
628  m_imageStream->md->cnt1 = m_circBuffLength - 1;
629  }
630  else
631  {
632  std::cerr << "Not creating . . .\n";
633  }
634 
635  //This completes the reconfiguration.
636  m_reconfig = false;
637 
638  if(derived().startAcquisition() < 0) continue;
639 
640  uint64_t next_cnt1 = 0;
641  char * next_dest = (char *) m_imageStream->array.raw;
642  timespec * next_wtimearr = &m_imageStream->writetimearray[0];
643  timespec * next_atimearr = &m_imageStream->atimearray[0];
644  uint64_t * next_cntarr = &m_imageStream->cntarray[0];
645 
646  //This is the main image grabbing loop.
647  while(!derived().shutdown() && !m_reconfig && derived().powerState() > 0)
648  {
649  //==================
650  //Get next image, process validity.
651  //====================
652  int isValid = derived().acquireAndCheckValid();
653  if(isValid != 0)
654  {
655  if( isValid < 0)
656  {
657  break;
658  }
659  else if( isValid > 0)
660  {
661  continue;
662  }
663  }
664 
665  //Ok, no timeout, so we process the image and publish it.
666  m_imageStream->md->write=1;
667 
668  //Set the time of last write
669  clock_gettime(CLOCK_REALTIME, &writestart);
670 
671  if(derived().loadImageIntoStream(next_dest) < 0)
672  {
673  break;
674  }
675 
676  //Set the time of last write
677  clock_gettime(CLOCK_REALTIME, &m_imageStream->md->writetime);
678 
679  //Set the image acquisition timestamp
680  m_imageStream->md->atime = m_currImageTimestamp;
681 
682  //Update cnt1
683  m_imageStream->md->cnt1 = next_cnt1;
684 
685  //Update cnt0
686  m_imageStream->md->cnt0++;
687 
688  *next_wtimearr = m_imageStream->md->writetime;
689  *next_atimearr = m_currImageTimestamp;
690  *next_cntarr = m_imageStream->md->cnt0;
691 
692  //And post
693  m_imageStream->md->write=0;
694  ImageStreamIO_sempost(m_imageStream,-1);
695 
696  //Update the latency circ. buffs
697  if(m_atimes.maxEntries() > 0)
698  {
699  m_atimes.nextEntry(m_imageStream->md->atime);
700  m_wtimes.nextEntry(m_imageStream->md->writetime);
701  }
702 
703  //Now we increment pointers outside the time-critical part of the loop.
704  next_cnt1 = m_imageStream->md->cnt1+1;
705  if(next_cnt1 >= m_circBuffLength) next_cnt1 = 0;
706 
707  next_dest = (char *) m_imageStream->array.raw + next_cnt1*m_width*m_height*m_typeSize;
708  next_wtimearr = &m_imageStream->writetimearray[next_cnt1];
709  next_atimearr = &m_imageStream->atimearray[next_cnt1];
710  next_cntarr = &m_imageStream->cntarray[next_cnt1];
711 
712  //Touch them to make sure we move
713  m_dummy_c = next_dest[0];
714  m_dummy_ts.tv_sec = next_wtimearr[0].tv_sec + next_atimearr[0].tv_sec;
715  m_dummy_cnt = next_cntarr[0];
716  }
717 
718  if(m_reconfig && !derived().shutdown())
719  {
720  derived().reconfig();
721  }
722 
723  } //outer loop, will exit if m_shutdown==true
724 
725  if(m_imageStream != nullptr)
726  {
727  ImageStreamIO_destroyIm( m_imageStream );
728  free(m_imageStream);
729  m_imageStream = nullptr;
730  }
731 }
732 
733 template<class derivedT>
735  void * src,
736  size_t width,
737  size_t height,
738  size_t szof
739  )
740 {
741  if(!derivedT::c_frameGrabber_flippable)
742  {
743  return memcpy(dest, src, width*height*szof);
744  }
745  else
746  {
747  switch(m_currentFlip)
748  {
749  case fgFlipNone:
750  return mx::improc::imcpy(dest, src, width, height, szof);
751  case fgFlipUD:
752  return mx::improc::imcpy_flipUD(dest, src, width, height, szof);
753  case fgFlipLR:
754  return mx::improc::imcpy_flipLR(dest, src, width, height, szof);
755  case fgFlipUDLR:
756  return mx::improc::imcpy_flipUDLR(dest, src, width, height, szof);
757  default:
758  return nullptr;
759  }
760  }
761 }
762 
763 
764 
765 template<class derivedT>
767 {
768  if( !derived().m_indiDriver ) return 0;
769 
770  indi::updateIfChanged(m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver);
771  indi::updateIfChanged(m_indiP_frameSize, "width", m_width, derived().m_indiDriver);
772  indi::updateIfChanged(m_indiP_frameSize, "height", m_height, derived().m_indiDriver);
773 
774  double fpsa = 0;
775  double fpsw = 0;
776  if(m_mna != 0 ) fpsa = 1.0/m_mna;
777  if(m_mnw != 0 ) fpsw = 1.0/m_mnw;
778 
779  indi::updateIfChanged<double>(m_indiP_timing, {"acq_fps","acq_jitter","write_fps","write_jitter","delta_aw","delta_aw_jitter"},
780  {fpsa, sqrt(m_vara), fpsw, sqrt(m_varw), m_mnwa, sqrt(m_varwa)},derived().m_indiDriver);
781 
782  return 0;
783 }
784 
785 template<class derivedT>
787 {
788  static double last_mna = 0;
789  static double last_vara = 0;
790 
791  static double last_mnw = 0;
792  static double last_varw = 0;
793 
794  static double last_mnwa = 0;
795  static double last_varwa = 0;
796 
797  if(force || m_mna != last_mna || m_vara != last_vara ||
798  m_mnw != last_mnw || m_varw != last_varw ||
799  m_mnwa != last_mnwa || m_varwa != last_varwa )
800  {
801  derived().template telem<telem_fgtimings>({m_mna, sqrt(m_vara), m_mnw, sqrt(m_varw), m_mnwa, sqrt(m_varwa)});
802 
803  last_mna = m_mna;
804  last_vara = m_vara;
805  last_mnw = m_mnw;
806  last_varw = m_varw;
807  last_mnwa = m_mnwa;
808  last_varwa = m_varwa;
809  }
810 
811  return 0;
812 
813 }
814 
815 } //namespace dev
816 } //namespace app
817 } //namespace MagAOX
818 #endif
uint16_t m_latencyCircBuffMaxLength
Maximum length of the latency measurement circular buffers.
int m_xbinning
The x-binning according to the framegrabber.
timespec m_currImageTimestamp
The timestamp of the current image.
uint32_t m_width
The width of the image, once deinterlaced etc.
int appShutdown()
Shuts down the framegrabber thread.
mx::sigproc::circularBufferIndex< timespec, cbIndexT > m_wtimes
void * loadImageIntoStreamCopy(void *dest, void *src, size_t width, size_t height, size_t szof)
int recordFGTimings(bool force=false)
pcf::IndiProperty m_fgThreadProp
The property to hold the f.g. thread details.
void fgThreadExec()
Execute framegrabbing.
bool m_fgThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
std::string m_fgCpuset
The cpuset to assign the framegrabber thread to. Not used if empty, the default.
static void fgThreadStart(frameGrabber *o)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls fgThreadExec.
int m_ybinning
The y-binning according to the framegrabber.
std::vector< double > m_watimesD
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
int updateINDI()
Update the INDI properties for this device controller.
std::vector< double > m_wtimesD
int onPowerOff()
On power off, sets m_reconfig to true.
pcf::IndiProperty m_indiP_shmimName
Property used to report the shmim buffer name.
std::string m_shmimName
The name of the shared memory image, is used in /tmp/<shmimName>.im.shm. Derived classes should set a...
uint8_t m_dataType
The ImageStreamIO type code.
bool m_reconfig
Flag to set if a camera reconfiguration requires a framegrabber reset.
std::thread m_fgThread
A separate thread for the actual framegrabbings.
pid_t m_fgThreadID
The ID of the framegrabber thread.
int appLogic()
Checks the framegrabber thread.
int m_fgThreadPrio
Priority of the framegrabber thread, should normally be > 00.
mx::sigproc::circularBufferIndex< timespec, cbIndexT > m_atimes
std::vector< double > m_atimesD
IMAGE * m_imageStream
The ImageStreamIO shared memory buffer.
pcf::IndiProperty m_indiP_frameSize
Property used to report the current frame size.
int appStartup()
Startup function.
pcf::IndiProperty m_indiP_timing
float m_latencyCircBuffMaxTime
Maximum time of the latency meaurement circular buffers.
uint32_t m_circBuffLength
Length of the circular buffer, in frames.
uint32_t m_height
The height of the image, once deinterlaced etc.
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
@ READY
The device is ready for operation, but is not operating.
Definition: stateCodes.hpp:51
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
Definition: dm.hpp:24
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.
Definition: logPriority.hpp:40