344 config(m_fgThreadPrio,
"framegrabber.threadPrio");
345 config(m_fgCpuset,
"framegrabber.cpuset");
346 if(m_shmimName ==
"") m_shmimName = derived().configName();
347 config(m_shmimName,
"framegrabber.shmimName");
349 config(m_circBuffLength,
"framegrabber.circBuffLength");
351 if(m_circBuffLength < 1)
353 m_circBuffLength = 1;
354 derivedT::template log<text_log>(
"circBuffLength set to 1");
357 config(m_latencyCircBuffMaxTime,
"framegrabber.latencyTime");
358 if(m_latencyCircBuffMaxTime < 0)
360 m_latencyCircBuffMaxTime = 0;
361 derivedT::template log<text_log>(
"latencyTime set to 0 (off)");
364 config(m_latencyCircBuffMaxLength,
"framegrabber.latencySize");
367 if(derivedT::c_frameGrabber_flippable)
369 std::string flip =
"flipNone";
370 config(flip,
"framegrabber.defaultFlip");
371 if(flip ==
"flipNone")
373 m_defaultFlip = fgFlipNone;
375 else if(flip ==
"flipUD")
377 m_defaultFlip = fgFlipUD;
379 else if(flip ==
"flipLR")
381 m_defaultFlip = fgFlipLR;
383 else if(flip ==
"flipUDLR")
385 m_defaultFlip = fgFlipUDLR;
389 derivedT::template log<text_log>({std::string(
"invalid framegrabber flip specification (") + flip +
"), setting flipNone"}, logPrio::LOG_ERROR);
390 m_defaultFlip = fgFlipNone;
402 m_indiP_shmimName = pcf::IndiProperty(pcf::IndiProperty::Text);
403 m_indiP_shmimName.setDevice(derived().configName());
404 m_indiP_shmimName.setName(
"fg_shmimName");
405 m_indiP_shmimName.setPerm(pcf::IndiProperty::ReadOnly);
406 m_indiP_shmimName.setState(pcf::IndiProperty::Idle);
407 m_indiP_shmimName.add(pcf::IndiElement(
"name"));
408 m_indiP_shmimName[
"name"] = m_shmimName;
410 if( derived().registerIndiPropertyNew( m_indiP_shmimName,
nullptr) < 0)
412 #ifndef FRAMEGRABBER_TEST_NOLOG
413 derivedT::template log<software_error>({__FILE__,__LINE__});
419 m_indiP_frameSize = pcf::IndiProperty(pcf::IndiProperty::Number);
420 m_indiP_frameSize.setDevice(derived().configName());
421 m_indiP_frameSize.setName(
"fg_frameSize");
422 m_indiP_frameSize.setPerm(pcf::IndiProperty::ReadOnly);
423 m_indiP_frameSize.setState(pcf::IndiProperty::Idle);
424 m_indiP_frameSize.add(pcf::IndiElement(
"width"));
425 m_indiP_frameSize[
"width"] = 0;
426 m_indiP_frameSize.add(pcf::IndiElement(
"height"));
427 m_indiP_frameSize[
"height"] = 0;
429 if( derived().registerIndiPropertyNew( m_indiP_frameSize,
nullptr) < 0)
431 #ifndef FRAMEGRABBER_TEST_NOLOG
432 derivedT::template log<software_error>({__FILE__,__LINE__});
438 derived().createROIndiNumber( m_indiP_timing,
"fg_timing");
439 m_indiP_timing.add(pcf::IndiElement(
"acq_fps"));
440 m_indiP_timing.add(pcf::IndiElement(
"acq_min"));
441 m_indiP_timing.add(pcf::IndiElement(
"acq_max"));
442 m_indiP_timing.add(pcf::IndiElement(
"acq_jitter"));
443 m_indiP_timing.add(pcf::IndiElement(
"write_fps"));
444 m_indiP_timing.add(pcf::IndiElement(
"write_min"));
445 m_indiP_timing.add(pcf::IndiElement(
"write_max"));
446 m_indiP_timing.add(pcf::IndiElement(
"write_jitter"));
447 m_indiP_timing.add(pcf::IndiElement(
"delta_aw"));
448 m_indiP_timing.add(pcf::IndiElement(
"delta_aw_jitter"));
450 if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0)
452 #ifndef STDCAMERA_TEST_NOLOG
453 derivedT::template log<software_error>({__FILE__,__LINE__});
459 if(derived().threadStart( m_fgThread, m_fgThreadInit, m_fgThreadID, m_fgThreadProp, m_fgThreadPrio, m_fgCpuset,
"framegrabber",
this, fgThreadStart) < 0)
461 derivedT::template log<software_error, -1>({__FILE__, __LINE__});
473 if(pthread_tryjoin_np(m_fgThread.native_handle(),0) == 0)
475 derivedT::template log<software_error>({__FILE__, __LINE__,
"framegrabber thread has exited"});
482 if(m_atimes.size() >= m_atimes.maxEntries() )
485 cbIndexT latTime = m_latencyCircBuffMaxTime*derived().fps();
486 if(latTime > m_atimes.maxEntries())
488 latTime = m_atimes.maxEntries();
491 m_atimesD.resize(latTime-1);
492 m_wtimesD.resize(latTime-1);
493 m_watimesD.resize(latTime-1);
495 cbIndexT refEntry = m_atimes.latest();
497 if(refEntry >= latTime)
503 refEntry = m_atimes.maxEntries() + refEntry - latTime;
506 timespec ts = m_atimes.at(refEntry, 0);
507 double a0 = ts.tv_sec + ((double) ts.tv_nsec)/1e9;
509 ts = m_wtimes.at(refEntry, 0);
510 double w0 = ts.tv_sec + ((double) ts.tv_nsec)/1e9;
517 for(
size_t n=1; n <= m_atimesD.size(); ++n)
519 ts = m_atimes.at(refEntry, n);
520 double a = ts.tv_sec + ((double) ts.tv_nsec)/1e9;
522 ts = m_wtimes.at(refEntry, n);
523 double w = ts.tv_sec + ((double) ts.tv_nsec)/1e9;
525 m_atimesD[n-1] = a - a0;
526 m_wtimesD[n-1] = w - w0;
527 m_watimesD[n-1] = w - a;
531 if(m_atimesD[n-1] < mina)
533 mina = m_atimesD[n-1];
536 if(m_atimesD[n-1] > maxa)
538 maxa = m_atimesD[n-1];
541 if(m_wtimesD[n-1] < minw)
543 minw = m_wtimesD[n-1];
546 if(m_wtimesD[n-1] > maxw)
548 maxw = m_wtimesD[n-1];
551 if(m_wtimesD[n-1] < 0)
553 std::cerr <<
"negative wtime: " << m_wtimesD[n-1] <<
' ' << n <<
' ' << m_atimesD.size() <<
' ' << refEntry <<
'\n';
554 return derivedT::template log<software_error,-1>({__FILE__, __LINE__,
"negative write time. latency circ buff is not long enought"});
558 m_mna = mx::math::vectorMean(m_atimesD);
559 m_vara = mx::math::vectorVariance(m_atimesD, m_mna);
563 m_mnw = mx::math::vectorMean(m_wtimesD);
564 m_varw = mx::math::vectorVariance(m_wtimesD, m_mnw);
568 m_mnwa = mx::math::vectorMean(m_watimesD);
569 m_varwa = mx::math::vectorVariance(m_watimesD, m_mnwa);
663 m_fgThreadID = syscall(SYS_gettid);
668 while(m_fgThreadInit ==
true && derived().shutdown() == 0)
673 uint32_t imsize[3] = {0,0,0};
674 std::string shmimName;
676 while(derived().shutdown() == 0)
684 if(derived().shutdown())
691 if(derived().configureAcquisition() < 0)
continue;
693 if(m_latencyCircBuffMaxLength == 0 || m_latencyCircBuffMaxTime == 0 || derived().fps() <= 0)
695 m_atimes.maxEntries(0);
696 m_wtimes.maxEntries(0);
701 cbIndexT cbSz = 2*m_latencyCircBuffMaxTime * derived().fps();
702 if(cbSz > m_latencyCircBuffMaxLength) cbSz = m_latencyCircBuffMaxLength;
703 if(cbSz < 3) cbSz = 3;
705 m_atimes.maxEntries(cbSz);
706 m_wtimes.maxEntries(cbSz);
709 m_typeSize = ImageStreamIO_typesize(m_dataType);
713 m_currentFlip = m_defaultFlip;
718 if(m_shmimName ==
"") m_shmimName = derived().configName();
720 if(m_width != imsize[0] || m_height != imsize[1] ||
static_cast<uint32_t
>(m_circBuffLength) != imsize[2] || m_shmimName != shmimName || m_imageStream ==
nullptr)
722 if(m_imageStream !=
nullptr)
724 ImageStreamIO_destroyIm(m_imageStream);
728 m_imageStream =
reinterpret_cast<IMAGE *
>(malloc(
sizeof(IMAGE)));
731 imsize[1] = m_height;
732 imsize[2] = m_circBuffLength;
733 shmimName = m_shmimName;
735 std::cerr <<
"Creating: " << m_shmimName <<
" " << m_width <<
" " << m_height <<
" " << m_circBuffLength <<
"\n";
737 ImageStreamIO_createIm_gpu(m_imageStream, m_shmimName.c_str(), 3, imsize, m_dataType, -1, 1, IMAGE_NB_SEMAPHORE, 0, CIRCULAR_BUFFER | ZAXIS_TEMPORAL, 0);
739 m_imageStream->md->cnt1 = m_circBuffLength - 1;
745 if(derived().startAcquisition() < 0)
continue;
747 uint64_t next_cnt1 = 0;
748 char * next_dest =
reinterpret_cast<char *
>(m_imageStream->array.raw);
749 timespec * next_wtimearr = &m_imageStream->writetimearray[0];
750 timespec * next_atimearr = &m_imageStream->atimearray[0];
751 uint64_t * next_cntarr = &m_imageStream->cntarray[0];
754 while(!derived().shutdown() && !m_reconfig && derived().powerState() > 0)
759 int isValid = derived().acquireAndCheckValid();
773 m_imageStream->md->write=1;
778 if(derived().loadImageIntoStream(next_dest) < 0)
785 if(clock_gettime(CLOCK_REALTIME, &m_imageStream->md->writetime) < 0)
787 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
791 m_imageStream->md->atime = m_currImageTimestamp;
794 m_imageStream->md->cnt1 = next_cnt1;
797 m_imageStream->md->cnt0++;
799 *next_wtimearr = m_imageStream->md->writetime;
800 *next_atimearr = m_currImageTimestamp;
801 *next_cntarr = m_imageStream->md->cnt0;
804 m_imageStream->md->write=0;
805 ImageStreamIO_sempost(m_imageStream,-1);
808 if(m_atimes.maxEntries() > 0)
810 m_atimes.nextEntry(m_imageStream->md->atime);
811 m_wtimes.nextEntry(m_imageStream->md->writetime);
815 next_cnt1 = m_imageStream->md->cnt1+1;
816 if(next_cnt1 >= m_circBuffLength) next_cnt1 = 0;
818 next_dest =
reinterpret_cast<char *
>(m_imageStream->array.raw) + next_cnt1*m_width*m_height*m_typeSize;
819 next_wtimearr = &m_imageStream->writetimearray[next_cnt1];
820 next_atimearr = &m_imageStream->atimearray[next_cnt1];
821 next_cntarr = &m_imageStream->cntarray[next_cnt1];
824 m_dummy_c = next_dest[0];
825 m_dummy_ts.tv_sec = next_wtimearr[0].tv_sec + next_atimearr[0].tv_sec;
826 m_dummy_cnt = next_cntarr[0];
829 if(m_reconfig && !derived().shutdown())
831 derived().reconfig();
836 if(m_imageStream !=
nullptr)
838 ImageStreamIO_destroyIm( m_imageStream );
840 m_imageStream =
nullptr;
852 if(!derivedT::c_frameGrabber_flippable)
854 return memcpy(dest, src, width*height*szof);
858 switch(m_currentFlip)
861 return mx::improc::imcpy(dest, src, width, height, szof);
863 return mx::improc::imcpy_flipUD(dest, src, width, height, szof);
865 return mx::improc::imcpy_flipLR(dest, src, width, height, szof);
867 return mx::improc::imcpy_flipUDLR(dest, src, width, height, szof);
879 if( !derived().m_indiDriver )
return 0;
887 if(m_mna != 0 ) fpsa = 1.0/m_mna;
888 if(m_mnw != 0 ) fpsw = 1.0/m_mnw;
890 indi::updateIfChanged<double>(m_indiP_timing, {
"acq_fps",
"acq_min",
"acq_max",
"acq_jitter",
"write_fps",
"write_min",
"write_max",
"write_jitter",
"delta_aw",
"delta_aw_jitter"},
891 {fpsa, m_mina, m_maxa, sqrt(m_vara), fpsw, m_minw, m_maxw, sqrt(m_varw), m_mnwa, sqrt(m_varwa)},derived().m_indiDriver);