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