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"
100 template<
class derivedT>
145 mx::sigproc::circularBufferIndex<timespec, cbIndexT>
m_atimes;
146 mx::sigproc::circularBufferIndex<timespec, cbIndexT>
m_wtimes;
308 return *
static_cast<derivedT *
>(
this);
312 template<
class derivedT>
315 config.add(
"framegrabber.threadPrio",
"",
"framegrabber.threadPrio", argType::Required,
"framegrabber",
"threadPrio",
false,
"int",
"The real-time priority of the framegrabber thread.");
317 config.add(
"framegrabber.cpuset",
"",
"framegrabber.cpuset", argType::Required,
"framegrabber",
"cpuset",
false,
"string",
"The cpuset to assign the framegrabber thread to.");
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.");
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.");
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.");
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.");
328 if(derivedT::c_frameGrabber_flippable)
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.");
336 template<
class derivedT>
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");
344 config(m_circBuffLength,
"framegrabber.circBuffLength");
346 if(m_circBuffLength < 1)
348 m_circBuffLength = 1;
349 derivedT::template log<text_log>(
"circBuffLength set to 1");
352 config(m_latencyCircBuffMaxTime,
"framegrabber.latencyTime");
353 if(m_latencyCircBuffMaxTime < 0)
355 m_latencyCircBuffMaxTime = 0;
356 derivedT::template log<text_log>(
"latencyTime set to 0 (off)");
359 config(m_latencyCircBuffMaxLength,
"framegrabber.latencySize");
362 if(derivedT::c_frameGrabber_flippable)
364 std::string flip =
"flipNone";
365 config(flip,
"framegrabber.defaultFlip");
366 if(flip ==
"flipNone")
368 m_defaultFlip = fgFlipNone;
370 else if(flip ==
"flipUD")
372 m_defaultFlip = fgFlipUD;
374 else if(flip ==
"flipLR")
376 m_defaultFlip = fgFlipLR;
378 else if(flip ==
"flipUDLR")
380 m_defaultFlip = fgFlipUDLR;
384 derivedT::template log<text_log>({std::string(
"invalid framegrabber flip specification (") + flip +
"), setting flipNone"},
logPrio::LOG_ERROR);
385 m_defaultFlip = fgFlipNone;
393 template<
class derivedT>
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;
405 if( derived().registerIndiPropertyNew( m_indiP_shmimName,
nullptr) < 0)
407 #ifndef FRAMEGRABBER_TEST_NOLOG
408 derivedT::template log<software_error>({__FILE__,__LINE__});
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;
424 if( derived().registerIndiPropertyNew( m_indiP_frameSize,
nullptr) < 0)
426 #ifndef FRAMEGRABBER_TEST_NOLOG
427 derivedT::template log<software_error>({__FILE__,__LINE__});
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"));
441 if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0)
443 #ifndef STDCAMERA_TEST_NOLOG
444 derivedT::template log<software_error>({__FILE__,__LINE__});
450 if(derived().threadStart( m_fgThread, m_fgThreadInit, m_fgThreadID, m_fgThreadProp, m_fgThreadPrio, m_fgCpuset,
"framegrabber",
this, fgThreadStart) < 0)
452 derivedT::template
log<software_error, -1>({__FILE__, __LINE__});
460 template<
class derivedT>
464 if(pthread_tryjoin_np(m_fgThread.native_handle(),0) == 0)
466 derivedT::template log<software_error>({__FILE__, __LINE__,
"framegrabber thread has exited"});
473 if(m_atimes.size() >= m_atimes.maxEntries())
475 cbIndexT refEntry = m_atimes.earliest();
477 m_atimesD.resize(m_atimes.maxEntries()-1);
478 m_wtimesD.resize(m_wtimes.maxEntries()-1);
479 m_watimesD.resize(m_wtimes.maxEntries()-1);
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)
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;
494 m_mna = mx::math::vectorMean(m_atimesD);
495 m_vara = mx::math::vectorVariance(m_atimesD, m_mna);
497 m_mnw = mx::math::vectorMean(m_wtimesD);
498 m_varw = mx::math::vectorVariance(m_wtimesD, m_mnw);
500 m_mnwa = mx::math::vectorMean(m_watimesD);
501 m_varwa = mx::math::vectorVariance(m_watimesD, m_mnwa);
529 template<
class derivedT>
551 template<
class derivedT>
554 if(m_fgThread.joinable())
572 template<
class derivedT>
579 template<
class derivedT>
583 m_fgThreadID = syscall(SYS_gettid);
588 while(m_fgThreadInit ==
true && derived().shutdown() == 0)
593 uint32_t imsize[3] = {0,0,0};
594 std::string shmimName;
596 while(derived().shutdown() == 0)
604 if(derived().shutdown())
continue;
608 if(derived().configureAcquisition() < 0)
continue;
610 if(m_latencyCircBuffMaxLength == 0 || m_latencyCircBuffMaxTime == 0)
612 m_atimes.maxEntries(0);
613 m_wtimes.maxEntries(0);
618 cbIndexT cbSz = m_latencyCircBuffMaxTime * derived().fps();
619 if(cbSz > m_latencyCircBuffMaxLength) cbSz = m_latencyCircBuffMaxLength;
620 if(cbSz < 3) cbSz = 3;
621 m_atimes.maxEntries(cbSz);
622 m_wtimes.maxEntries(cbSz);
625 m_typeSize = ImageStreamIO_typesize(m_dataType);
629 m_currentFlip = m_defaultFlip;
634 if(m_shmimName ==
"") m_shmimName = derived().configName();
636 if(m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_shmimName != shmimName || m_imageStream ==
nullptr)
638 if(m_imageStream !=
nullptr)
640 ImageStreamIO_destroyIm(m_imageStream);
644 m_imageStream = (IMAGE *) malloc(
sizeof(IMAGE));
647 imsize[1] = m_height;
648 imsize[2] = m_circBuffLength;
649 shmimName = m_shmimName;
651 std::cerr <<
"Creating: " << m_shmimName <<
" " << m_width <<
" " << m_height <<
" " << m_circBuffLength <<
"\n";
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);
655 m_imageStream->md->cnt1 = m_circBuffLength - 1;
665 if(derived().startAcquisition() < 0)
continue;
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];
674 while(!derived().shutdown() && !m_reconfig && derived().powerState() > 0)
679 int isValid = derived().acquireAndCheckValid();
686 else if( isValid > 0)
693 m_imageStream->md->write=1;
696 clock_gettime(CLOCK_REALTIME, &writestart);
698 if(derived().loadImageIntoStream(next_dest) < 0)
704 clock_gettime(CLOCK_REALTIME, &m_imageStream->md->writetime);
707 m_imageStream->md->atime = m_currImageTimestamp;
710 m_imageStream->md->cnt1 = next_cnt1;
713 m_imageStream->md->cnt0++;
715 *next_wtimearr = m_imageStream->md->writetime;
716 *next_atimearr = m_currImageTimestamp;
717 *next_cntarr = m_imageStream->md->cnt0;
720 m_imageStream->md->write=0;
721 ImageStreamIO_sempost(m_imageStream,-1);
724 if(m_atimes.maxEntries() > 0)
726 m_atimes.nextEntry(m_imageStream->md->atime);
727 m_wtimes.nextEntry(m_imageStream->md->writetime);
731 next_cnt1 = m_imageStream->md->cnt1+1;
732 if(next_cnt1 >= m_circBuffLength) next_cnt1 = 0;
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];
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];
745 if(m_reconfig && !derived().shutdown())
747 derived().reconfig();
752 if(m_imageStream !=
nullptr)
754 ImageStreamIO_destroyIm( m_imageStream );
756 m_imageStream =
nullptr;
760 template<
class derivedT>
768 if(!derivedT::c_frameGrabber_flippable)
770 return memcpy(dest, src,
width*height*szof);
774 switch(m_currentFlip)
777 return mx::improc::imcpy(dest, src,
width, height, szof);
779 return mx::improc::imcpy_flipUD(dest, src,
width, height, szof);
781 return mx::improc::imcpy_flipLR(dest, src,
width, height, szof);
783 return mx::improc::imcpy_flipUDLR(dest, src,
width, height, szof);
792 template<
class derivedT>
795 if( !derived().m_indiDriver )
return 0;
803 if(m_mna != 0 ) fpsa = 1.0/m_mna;
804 if(m_mnw != 0 ) fpsw = 1.0/m_mnw;
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);
812 template<
class derivedT>
815 static double last_mna = 0;
816 static double last_vara = 0;
818 static double last_mnw = 0;
819 static double last_varw = 0;
821 static double last_mnwa = 0;
822 static double last_varwa = 0;
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 )
828 derived().template telem<telem_fgtimings>({m_mna, sqrt(m_vara), m_mnw, sqrt(m_varw), m_mnwa, sqrt(m_varwa)});
835 last_varwa = m_varwa;
846 #define FRAMEGRABBER_SETUP_CONFIG( cfig ) \
847 if(frameGrabberT::setupConfig(cfig) < 0) \
849 log<software_error>({__FILE__, __LINE__, "Error from frameGrabberT::setupConfig"}); \
858 #define FRAMEGRABBER_LOAD_CONFIG( cfig ) \
859 if(frameGrabberT::loadConfig(cfig) < 0) \
861 return log<software_error,-1>({__FILE__, __LINE__, "Error from frameGrabberT::loadConfig"}); \
865 #define FRAMEGRABBER_APP_STARTUP \
866 if(frameGrabberT::appStartup() < 0) \
868 return log<software_error,-1>({__FILE__, __LINE__, "Error from frameGrabberT::appStartup"}); \
872 #define FRAMEGRABBER_APP_LOGIC \
873 if(frameGrabberT::appLogic() < 0) \
875 return log<software_error,-1>({__FILE__, __LINE__, "Error from frameGrabberT::appLogic"}); \
879 #define FRAMEGRABBER_UPDATE_INDI \
880 if(frameGrabberT::updateINDI() < 0) \
882 return log<software_error,-1>({__FILE__, __LINE__, "Error from frameGrabberT::updateINDI"}); \
886 #define FRAMEGRABBER_APP_SHUTDOWN \
887 if(frameGrabberT::appShutdown() < 0) \
889 return log<software_error,-1>({__FILE__, __LINE__, "Error from frameGrabberT::appShutdown"}); \
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.
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.
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.
constexpr static logPrioT LOG_ERROR
An error has occured which the software will attempt to correct.