9 #ifndef frameGrabber_hpp
10 #define frameGrabber_hpp
12 #include <sys/syscall.h>
14 #include <mx/sigproc/circularBuffer.hpp>
15 #include <mx/math/vectorUtils.hpp>
16 #include <mx/improc/imageUtils.hpp>
18 #include <ImageStreamIO/ImageStruct.h>
19 #include <ImageStreamIO/ImageStreamIO.h>
21 #include "../../common/paths.hpp"
77 template<
class derivedT>
122 mx::sigproc::circularBufferIndex<timespec, cbIndexT>
m_atimes;
123 mx::sigproc::circularBufferIndex<timespec, cbIndexT>
m_wtimes;
285 return *
static_cast<derivedT *
>(
this);
289 template<
class derivedT>
292 config.add(
"framegrabber.threadPrio",
"",
"framegrabber.threadPrio", argType::Required,
"framegrabber",
"threadPrio",
false,
"int",
"The real-time priority of the framegrabber thread.");
294 config.add(
"framegrabber.cpuset",
"",
"framegrabber.cpuset", argType::Required,
"framegrabber",
"cpuset",
false,
"string",
"The cpuset to assign the framegrabber thread to.");
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.");
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.");
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.");
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.");
305 if(derivedT::c_frameGrabber_flippable)
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.");
311 template<
class derivedT>
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");
319 config(m_circBuffLength,
"framegrabber.circBuffLength");
321 if(m_circBuffLength < 1)
323 m_circBuffLength = 1;
324 derivedT::template log<text_log>(
"circBuffLength set to 1");
327 config(m_latencyCircBuffMaxTime,
"framegrabber.latencyTime");
328 if(m_latencyCircBuffMaxTime < 0)
330 m_latencyCircBuffMaxTime = 0;
331 derivedT::template log<text_log>(
"latencyTime set to 0 (off)");
334 config(m_latencyCircBuffMaxLength,
"framegrabber.latencySize");
337 if(derivedT::c_frameGrabber_flippable)
339 std::string flip =
"flipNone";
340 config(flip,
"framegrabber.defaultFlip");
341 if(flip ==
"flipNone")
343 m_defaultFlip = fgFlipNone;
345 else if(flip ==
"flipUD")
347 m_defaultFlip = fgFlipUD;
349 else if(flip ==
"flipLR")
351 m_defaultFlip = fgFlipLR;
353 else if(flip ==
"flipUDLR")
355 m_defaultFlip = fgFlipUDLR;
359 derivedT::template log<text_log>({std::string(
"invalid framegrabber flip specification (") + flip +
"), setting flipNone"},
logPrio::LOG_ERROR);
360 m_defaultFlip = fgFlipNone;
366 template<
class derivedT>
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;
378 if( derived().registerIndiPropertyNew( m_indiP_shmimName,
nullptr) < 0)
380 #ifndef FRAMEGRABBER_TEST_NOLOG
381 derivedT::template log<software_error>({__FILE__,__LINE__});
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;
397 if( derived().registerIndiPropertyNew( m_indiP_frameSize,
nullptr) < 0)
399 #ifndef FRAMEGRABBER_TEST_NOLOG
400 derivedT::template log<software_error>({__FILE__,__LINE__});
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"));
414 if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0)
416 #ifndef STDCAMERA_TEST_NOLOG
417 derivedT::template log<software_error>({__FILE__,__LINE__});
423 if(derived().threadStart( m_fgThread, m_fgThreadInit, m_fgThreadID, m_fgThreadProp, m_fgThreadPrio, m_fgCpuset,
"framegrabber",
this, fgThreadStart) < 0)
425 derivedT::template
log<software_error, -1>({__FILE__, __LINE__});
433 template<
class derivedT>
437 if(pthread_tryjoin_np(m_fgThread.native_handle(),0) == 0)
439 derivedT::template log<software_error>({__FILE__, __LINE__,
"framegrabber thread has exited"});
446 if(m_atimes.size() >= m_atimes.maxEntries())
448 cbIndexT refEntry = m_atimes.earliest();
450 m_atimesD.resize(m_atimes.maxEntries()-1);
451 m_wtimesD.resize(m_wtimes.maxEntries()-1);
452 m_watimesD.resize(m_wtimes.maxEntries()-1);
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)
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;
467 m_mna = mx::math::vectorMean(m_atimesD);
468 m_vara = mx::math::vectorVariance(m_atimesD, m_mna);
470 m_mnw = mx::math::vectorMean(m_wtimesD);
471 m_varw = mx::math::vectorVariance(m_wtimesD, m_mnw);
473 m_mnwa = mx::math::vectorMean(m_watimesD);
474 m_varwa = mx::math::vectorVariance(m_watimesD, m_mnwa);
502 template<
class derivedT>
524 template<
class derivedT>
527 if(m_fgThread.joinable())
545 template<
class derivedT>
552 template<
class derivedT>
556 m_fgThreadID = syscall(SYS_gettid);
561 while(m_fgThreadInit ==
true && derived().shutdown() == 0)
566 uint32_t imsize[3] = {0,0,0};
567 std::string shmimName;
569 while(derived().shutdown() == 0)
577 if(derived().shutdown())
continue;
581 if(derived().configureAcquisition() < 0)
continue;
583 if(m_latencyCircBuffMaxLength == 0 || m_latencyCircBuffMaxTime == 0)
585 m_atimes.maxEntries(0);
586 m_wtimes.maxEntries(0);
591 cbIndexT cbSz = m_latencyCircBuffMaxTime * derived().fps();
592 if(cbSz > m_latencyCircBuffMaxLength) cbSz = m_latencyCircBuffMaxLength;
593 if(cbSz < 3) cbSz = 3;
594 m_atimes.maxEntries(cbSz);
595 m_wtimes.maxEntries(cbSz);
598 m_typeSize = ImageStreamIO_typesize(m_dataType);
602 m_currentFlip = m_defaultFlip;
607 if(m_shmimName ==
"") m_shmimName = derived().configName();
609 if(m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_shmimName != shmimName || m_imageStream ==
nullptr)
611 if(m_imageStream !=
nullptr)
613 ImageStreamIO_destroyIm(m_imageStream);
617 m_imageStream = (IMAGE *) malloc(
sizeof(IMAGE));
620 imsize[1] = m_height;
621 imsize[2] = m_circBuffLength;
622 shmimName = m_shmimName;
624 std::cerr <<
"Creating: " << m_shmimName <<
" " << m_width <<
" " << m_height <<
" " << m_circBuffLength <<
"\n";
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);
628 m_imageStream->md->cnt1 = m_circBuffLength - 1;
638 if(derived().startAcquisition() < 0)
continue;
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];
647 while(!derived().shutdown() && !m_reconfig && derived().powerState() > 0)
652 int isValid = derived().acquireAndCheckValid();
659 else if( isValid > 0)
666 m_imageStream->md->write=1;
669 clock_gettime(CLOCK_REALTIME, &writestart);
671 if(derived().loadImageIntoStream(next_dest) < 0)
677 clock_gettime(CLOCK_REALTIME, &m_imageStream->md->writetime);
680 m_imageStream->md->atime = m_currImageTimestamp;
683 m_imageStream->md->cnt1 = next_cnt1;
686 m_imageStream->md->cnt0++;
688 *next_wtimearr = m_imageStream->md->writetime;
689 *next_atimearr = m_currImageTimestamp;
690 *next_cntarr = m_imageStream->md->cnt0;
693 m_imageStream->md->write=0;
694 ImageStreamIO_sempost(m_imageStream,-1);
697 if(m_atimes.maxEntries() > 0)
699 m_atimes.nextEntry(m_imageStream->md->atime);
700 m_wtimes.nextEntry(m_imageStream->md->writetime);
704 next_cnt1 = m_imageStream->md->cnt1+1;
705 if(next_cnt1 >= m_circBuffLength) next_cnt1 = 0;
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];
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];
718 if(m_reconfig && !derived().shutdown())
720 derived().reconfig();
725 if(m_imageStream !=
nullptr)
727 ImageStreamIO_destroyIm( m_imageStream );
729 m_imageStream =
nullptr;
733 template<
class derivedT>
741 if(!derivedT::c_frameGrabber_flippable)
743 return memcpy(dest, src,
width*height*szof);
747 switch(m_currentFlip)
750 return mx::improc::imcpy(dest, src,
width, height, szof);
752 return mx::improc::imcpy_flipUD(dest, src,
width, height, szof);
754 return mx::improc::imcpy_flipLR(dest, src,
width, height, szof);
756 return mx::improc::imcpy_flipUDLR(dest, src,
width, height, szof);
765 template<
class derivedT>
768 if( !derived().m_indiDriver )
return 0;
776 if(m_mna != 0 ) fpsa = 1.0/m_mna;
777 if(m_mnw != 0 ) fpsw = 1.0/m_mnw;
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);
785 template<
class derivedT>
788 static double last_mna = 0;
789 static double last_vara = 0;
791 static double last_mnw = 0;
792 static double last_varw = 0;
794 static double last_mnwa = 0;
795 static double last_varwa = 0;
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 )
801 derived().template telem<telem_fgtimings>({m_mna, sqrt(m_vara), m_mnw, sqrt(m_varw), m_mnwa, sqrt(m_varwa)});
808 last_varwa = m_varwa;
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.
@ 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.
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.