400 config( m_fgThreadPrio,
"framegrabber.threadPrio" );
401 config( m_fgCpuset,
"framegrabber.cpuset" );
402 if( m_shmimName ==
"" )
404 m_shmimName = derived().configName();
407 config( m_shmimName,
"framegrabber.shmimName" );
409 if( m_shmimName ==
"" )
411 return derivedT::template log<software_critical, -1>( {
"framegrabber.shmimName can't be empty" } );
414 config( m_circBuffLength,
"framegrabber.circBuffLength" );
416 if( m_circBuffLength < 1 )
418 m_circBuffLength = 1;
419 derivedT::template log<text_log>(
"circBuffLength set to 1" );
422 config( m_latencyCircBuffMaxTime,
"framegrabber.latencyTime" );
423 if( m_latencyCircBuffMaxTime < 0 )
425 m_latencyCircBuffMaxTime = 0;
426 derivedT::template log<text_log>(
"latencyTime set to 0 (off)" );
429 config( m_latencyCircBuffMaxLength,
"framegrabber.latencySize" );
431 if( derivedT::c_frameGrabber_flippable )
433 std::string flip =
"flipNone";
435 config( flip,
"framegrabber.defaultFlip" );
437 if( flip ==
"flipNone" )
439 m_defaultFlip = fgFlipNone;
441 else if( flip ==
"flipUD" )
443 m_defaultFlip = fgFlipUD;
445 else if( flip ==
"flipLR" )
447 m_defaultFlip = fgFlipLR;
449 else if( flip ==
"flipUDLR" )
451 m_defaultFlip = fgFlipUDLR;
455 derivedT::template log<text_log>( { std::string(
"invalid framegrabber flip"
456 "specification (" ) +
457 flip +
"), setting flipNone" },
458 logPrio::LOG_ERROR );
460 m_defaultFlip = fgFlipNone;
471 m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
472 m_indiP_shmimName.setDevice( derived().configName() );
473 m_indiP_shmimName.setName(
"fg_shmimName" );
474 m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
475 m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
476 m_indiP_shmimName.add( pcf::IndiElement(
"name" ) );
477 m_indiP_shmimName[
"name"] = m_shmimName;
479 if( derived().registerIndiPropertyNew( m_indiP_shmimName,
nullptr ) < 0 )
481#ifndef FRAMEGRABBER_TEST_NOLOG
482 derivedT::template log<software_error>( { std::source_location::current() } );
489 m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
490 m_indiP_frameSize.setDevice( derived().configName() );
491 m_indiP_frameSize.setName(
"fg_frameSize" );
492 m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
493 m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
494 m_indiP_frameSize.add( pcf::IndiElement(
"width" ) );
495 m_indiP_frameSize[
"width"] = 0;
496 m_indiP_frameSize.add( pcf::IndiElement(
"height" ) );
497 m_indiP_frameSize[
"height"] = 0;
498 m_indiP_frameSize.add( pcf::IndiElement(
"depth" ) );
499 m_indiP_frameSize[
"depth"] = 0;
501 if( derived().registerIndiPropertyNew( m_indiP_frameSize,
nullptr ) < 0 )
503#ifndef FRAMEGRABBER_TEST_NOLOG
504 derivedT::template log<software_error>( { std::source_location::current() } );
511 derived().createROIndiNumber( m_indiP_timing,
"fg_timing" );
512 m_indiP_timing.add( pcf::IndiElement(
"acq_fps" ) );
513 m_indiP_timing.add( pcf::IndiElement(
"acq_min" ) );
514 m_indiP_timing.add( pcf::IndiElement(
"acq_max" ) );
515 m_indiP_timing.add( pcf::IndiElement(
"acq_jitter" ) );
516 m_indiP_timing.add( pcf::IndiElement(
"write_fps" ) );
517 m_indiP_timing.add( pcf::IndiElement(
"write_min" ) );
518 m_indiP_timing.add( pcf::IndiElement(
"write_max" ) );
519 m_indiP_timing.add( pcf::IndiElement(
"write_jitter" ) );
520 m_indiP_timing.add( pcf::IndiElement(
"delta_aw" ) );
521 m_indiP_timing.add( pcf::IndiElement(
"delta_aw_jitter" ) );
523 if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0 )
525#ifndef FRAMEGRABBER_TEST_NOLOG
526 derivedT::template log<software_error>( { std::source_location::current() } );
533 if( derived().threadStart( m_fgThread,
541 fgThreadStart ) < 0 )
543 derivedT::template log<software_error, -1>( { std::source_location::current() } );
554 if( pthread_tryjoin_np( m_fgThread.native_handle(), 0 ) == 0 )
556 derivedT::template log<software_error>( {
"framegrabber thread has exited" } );
565 if( m_atimes.size() >= m_atimes.maxEntries() )
567 cbIndexT latTime = m_latencyCircBuffMaxTime * m_cbFPS;
568 if( latTime >= m_atimes.maxEntries() )
570 latTime = m_atimes.maxEntries() - 1;
573 m_atimesD.resize( latTime - 1 );
574 m_wtimesD.resize( latTime - 1 );
575 m_watimesD.resize( latTime - 1 );
577 cbIndexT refEntry = m_atimes.latest();
579 if( refEntry >= latTime )
585 refEntry = m_atimes.maxEntries() + refEntry - latTime;
588 timespec ts = m_atimes.at( refEntry, 0 );
589 double a0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
591 ts = m_wtimes.at( refEntry, 0 );
592 double w0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
599 for(
size_t n = 1; n <= m_atimesD.size(); ++n )
601 ts = m_atimes.at( refEntry, n );
603 double a = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
605 ts = m_wtimes.at( refEntry, n );
607 double w = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
609 m_atimesD[n - 1] =
a - a0;
610 m_wtimesD[n - 1] = w - w0;
611 m_watimesD[n - 1] = w -
a;
615 if( m_atimesD[n - 1] < mina )
617 mina = m_atimesD[n - 1];
620 if( m_atimesD[n - 1] > maxa )
622 maxa = m_atimesD[n - 1];
625 if( m_wtimesD[n - 1] < minw )
627 minw = m_wtimesD[n - 1];
630 if( m_wtimesD[n - 1] > maxw )
632 maxw = m_wtimesD[n - 1];
635 if( m_wtimesD[n - 1] < 0 )
637 std::cerr <<
"negative wtime: " << m_wtimesD[n - 1] <<
' ' << n <<
' ' << m_atimesD.size()
638 <<
' ' << refEntry <<
' ' << m_atimes.maxEntries() <<
' ' << latTime <<
'\n';
640 return derivedT::template log<software_error, 0>( {
"negative write time. latency "
641 "circ buff is not long enought" } );
645 m_mna = mx::math::vectorMean( m_atimesD );
646 m_vara = mx::math::vectorVariance( m_atimesD, m_mna );
650 m_mnw = mx::math::vectorMean( m_wtimesD );
651 m_varw = mx::math::vectorVariance( m_wtimesD, m_mnw );
655 m_mnwa = mx::math::vectorMean( m_watimesD );
656 m_varwa = mx::math::vectorVariance( m_watimesD, m_mnwa );
688 catch(
const std::exception &e )
690 std::cerr << e.what() <<
'\n';
783 m_fgThreadID = syscall( SYS_gettid );
788 while( m_fgThreadInit ==
true && derived().shutdown() == 0 )
793 uint32_t imsize[3] = { 0, 0, 0 };
795 std::string shmimName;
797 static bool logged_wrong_size =
false;
799 while( derived().shutdown() == 0 )
802 while( !derived().shutdown() &&
804 derived().powerState() <= 0 ) )
809 if( derived().shutdown() )
815 if( derived().configureAcquisition() != 0 )
821 m_typeSize = ImageStreamIO_typesize( m_dataType );
823 if( configCircBuffs() < 0 )
825 derivedT::template log<software_error>( {
"error configuring latency circ. buffs" } );
832 if( m_imageStream !=
nullptr )
834 imsize[0] = m_imageStream->md[0].size[0];
836 if( m_imageStream->md[0].naxis > 1 )
838 imsize[1] = m_imageStream->md[0].size[1];
841 if( m_imageStream->md[0].naxis > 2 )
843 imsize[2] = m_imageStream->md[0].size[2];
853 if( m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_imageStream ==
nullptr )
855 if( m_imageStream !=
nullptr && m_ownShmim )
857 ImageStreamIO_destroyIm( m_imageStream );
858 free( m_imageStream );
860 else if( m_imageStream !=
nullptr && !m_ownShmim )
862 if( !logged_wrong_size )
864 derivedT::template log<text_log>(
865 std::format(
"image stream {} is not expected size", m_shmimName ), logPrio::LOG_WARNING );
866 logged_wrong_size =
true;
873 logged_wrong_size =
false;
875 m_imageStream =
reinterpret_cast<IMAGE *
>( malloc(
sizeof( IMAGE ) ) );
878 imsize[1] = m_height;
879 imsize[2] = m_circBuffLength;
881 std::cerr <<
"Creating: " << m_shmimName <<
" " << m_width <<
" " << m_height <<
" " << m_circBuffLength
884 ImageStreamIO_createIm_gpu( m_imageStream,
893 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
896 m_imageStream->md->cnt1 = m_circBuffLength - 1;
904 if( derived().startAcquisition() < 0 )
909 uint64_t next_cnt1 = 0;
910 char *next_dest =
reinterpret_cast<char *
>( m_imageStream->array.raw );
912 timespec *next_wtimearr =
nullptr;
913 timespec *next_atimearr =
nullptr;
914 uint64_t *next_cntarr =
nullptr;
918 next_wtimearr = &m_imageStream->writetimearray[0];
919 next_atimearr = &m_imageStream->atimearray[0];
920 next_cntarr = &m_imageStream->cntarray[0];
924 while( !derived().shutdown() && !m_reconfig && derived().powerState() > 0 )
929 int isValid = derived().acquireAndCheckValid();
943 m_imageStream->md->write = 1;
945 if( derived().loadImageIntoStream( next_dest ) < 0 )
952 if( clock_gettime( CLOCK_REALTIME, &m_imageStream->md->writetime ) < 0 )
954 derivedT::template log<software_critical>( { errno,
"clock_gettime" } );
958 m_imageStream->md->atime = m_currImageTimestamp;
961 m_imageStream->md->cnt1 = next_cnt1;
964 m_imageStream->md->cnt0++;
968 *next_wtimearr = m_imageStream->md->writetime;
969 *next_atimearr = m_currImageTimestamp;
970 *next_cntarr = m_imageStream->md->cnt0;
974 m_imageStream->md->write = 0;
975 ImageStreamIO_sempost( m_imageStream, -1 );
978 if( m_atimes.maxEntries() > 0 )
980 m_atimes.nextEntry( m_imageStream->md->atime );
981 m_wtimes.nextEntry( m_imageStream->md->writetime );
985 next_cnt1 = m_imageStream->md->cnt1 + 1;
986 if( next_cnt1 >= m_circBuffLength )
992 reinterpret_cast<char *
>( m_imageStream->array.raw ) + next_cnt1 * m_width * m_height * m_typeSize;
996 next_wtimearr = &m_imageStream->writetimearray[next_cnt1];
997 next_atimearr = &m_imageStream->atimearray[next_cnt1];
998 next_cntarr = &m_imageStream->cntarray[next_cnt1];
1002 m_dummy_c = next_dest[0];
1006 m_dummy_ts.tv_sec = next_wtimearr[0].tv_sec + next_atimearr[0].tv_sec;
1007 m_dummy_cnt = next_cntarr[0];
1010 if( m_cbFPS != derived().fps() )
1012 std::cerr <<
"configuring due to mismatch m_cbFPS\n";
1013 if( configCircBuffs() < 0 )
1015 derivedT::template log<software_error>( {
"error configuring latency circ. buffs" } );
1020 if( m_reconfig && !derived().shutdown() )
1022 derived().reconfig();
1027 if( m_imageStream !=
nullptr )
1031 ImageStreamIO_destroyIm( m_imageStream );
1035 ImageStreamIO_closeIm( m_imageStream );
1038 free( m_imageStream );
1039 m_imageStream =
nullptr;
1046 if( !derivedT::c_frameGrabber_flippable )
1048 return memcpy( dest, src, width * height * szof );
1052 switch( m_defaultFlip )
1055 return mx::improc::imcpy( dest, src, width, height, szof );
1057 return mx::improc::imcpy_flipUD( dest, src, width, height, szof );
1059 return mx::improc::imcpy_flipLR( dest, src, width, height, szof );
1061 return mx::improc::imcpy_flipUDLR( dest, src, width, height, szof );
1071 static bool logged =
false;
1073 if( m_imageStream !=
nullptr )
1075 ImageStreamIO_closeIm( m_imageStream );
1076 free( m_imageStream );
1077 m_imageStream =
nullptr;
1083 char SM_fname[1024];
1084 ImageStreamIO_filename( SM_fname,
sizeof( SM_fname ), m_shmimName.c_str() );
1085 SM_fd = open( SM_fname, O_RDWR );
1091 derivedT::template log<text_log>(
"ImageStream " + m_shmimName +
" not found (yet). Retrying . . .",
1092 logPrio::LOG_NOTICE );
1103 m_imageStream =
reinterpret_cast<IMAGE *
>( malloc(
sizeof( IMAGE ) ) );
1105 if( ImageStreamIO_openIm( m_imageStream, m_shmimName.c_str() ) == 0 )
1107 if( m_imageStream->md[0].sem < SEMAPHORE_MAXVAL )
1109 ImageStreamIO_closeIm( m_imageStream );
1110 free( m_imageStream );
1111 m_imageStream =
nullptr;
1117 char SM_fname[1024];
1118 ImageStreamIO_filename( SM_fname,
sizeof( SM_fname ), m_shmimName.c_str() );
1122 int rv = stat( SM_fname, &buffer );
1126 derivedT::template log<software_critical>( { errno,
1127 "Could not get inode for " + m_shmimName +
1128 ". Source process will need to be restarted." } );
1130 ImageStreamIO_closeIm( m_imageStream );
1132 free( m_imageStream );
1134 m_imageStream =
nullptr;
1136 derived().m_shutdown =
true;
1141 m_inode = buffer.st_ino;
1143 m_width = m_imageStream->md->size[0];
1145 if( m_imageStream->md->naxis == 2 )
1147 m_height = m_imageStream->md->size[1];
1148 m_circBuffLength = 1;
1150 else if( m_imageStream->md->naxis == 3 )
1152 m_height = m_imageStream->md->size[1];
1153 m_circBuffLength = m_imageStream->md->size[2];
1158 m_circBuffLength = 1;
1161 m_dataType = m_imageStream->md->datatype;
1162 m_typeSize = ImageStreamIO_typesize( m_dataType );
1169 free( m_imageStream );
1170 m_imageStream =
nullptr;
1179 if( !derived().m_indiDriver )
1194 indi::updateIfChanged<double>(
1205 "delta_aw_jitter" },
1206 { fpsa, m_mina, m_maxa, sqrt( m_vara ), fpsw, m_minw, m_maxw, sqrt( m_varw ), m_mnwa, sqrt( m_varwa ) },
1207 derived().m_indiDriver );