439 config( m_fgThreadPrio,
"framegrabber.threadPrio" );
440 config( m_fgCpuset,
"framegrabber.cpuset" );
441 if( m_shmimName ==
"" )
443 m_shmimName = derived().configName();
446 config( m_shmimName,
"framegrabber.shmimName" );
448 if( m_shmimName ==
"" )
450 return derivedT::template log<software_critical, -1>( {
"framegrabber.shmimName can't be empty" } );
453 config( m_circBuffLength,
"framegrabber.circBuffLength" );
455 if( m_circBuffLength < 1 )
457 m_circBuffLength = 1;
458 derivedT::template log<text_log>(
"circBuffLength set to 1" );
461 config( m_latencyCircBuffMaxTime,
"framegrabber.latencyTime" );
462 if( m_latencyCircBuffMaxTime < 0 )
464 m_latencyCircBuffMaxTime = 0;
465 derivedT::template log<text_log>(
"latencyTime set to 0 (off)" );
468 config( m_latencyCircBuffMaxLength,
"framegrabber.latencySize" );
470 if( derivedT::c_frameGrabber_flippable )
472 std::string flip =
"flipNone";
474 config( flip,
"framegrabber.defaultFlip" );
476 if( flip ==
"flipNone" )
478 m_defaultFlip = fgFlipNone;
480 else if( flip ==
"flipUD" )
482 m_defaultFlip = fgFlipUD;
484 else if( flip ==
"flipLR" )
486 m_defaultFlip = fgFlipLR;
488 else if( flip ==
"flipUDLR" )
490 m_defaultFlip = fgFlipUDLR;
494 derivedT::template log<text_log>( { std::string(
"invalid framegrabber flip"
495 "specification (" ) +
496 flip +
"), setting flipNone" },
497 logPrio::LOG_ERROR );
499 m_defaultFlip = fgFlipNone;
510 m_indiP_shmimName = pcf::IndiProperty( pcf::IndiProperty::Text );
511 m_indiP_shmimName.setDevice( derived().configName() );
512 m_indiP_shmimName.setName(
"fg_shmimName" );
513 m_indiP_shmimName.setPerm( pcf::IndiProperty::ReadOnly );
514 m_indiP_shmimName.setState( pcf::IndiProperty::Idle );
515 m_indiP_shmimName.add( pcf::IndiElement(
"name" ) );
516 m_indiP_shmimName[
"name"] = m_shmimName;
518 if( derived().registerIndiPropertyNew( m_indiP_shmimName,
nullptr ) < 0 )
520#ifndef FRAMEGRABBER_TEST_NOLOG
521 derivedT::template log<software_error>( { std::source_location::current() } );
528 m_indiP_frameSize = pcf::IndiProperty( pcf::IndiProperty::Number );
529 m_indiP_frameSize.setDevice( derived().configName() );
530 m_indiP_frameSize.setName(
"fg_frameSize" );
531 m_indiP_frameSize.setPerm( pcf::IndiProperty::ReadOnly );
532 m_indiP_frameSize.setState( pcf::IndiProperty::Idle );
533 m_indiP_frameSize.add( pcf::IndiElement(
"width" ) );
534 m_indiP_frameSize[
"width"] = 0;
535 m_indiP_frameSize.add( pcf::IndiElement(
"height" ) );
536 m_indiP_frameSize[
"height"] = 0;
537 m_indiP_frameSize.add( pcf::IndiElement(
"depth" ) );
538 m_indiP_frameSize[
"depth"] = 0;
540 if( derived().registerIndiPropertyNew( m_indiP_frameSize,
nullptr ) < 0 )
542#ifndef FRAMEGRABBER_TEST_NOLOG
543 derivedT::template log<software_error>( { std::source_location::current() } );
550 derived().createROIndiNumber( m_indiP_timing,
"fg_timing" );
551 m_indiP_timing.add( pcf::IndiElement(
"acq_fps" ) );
552 m_indiP_timing.add( pcf::IndiElement(
"acq_min" ) );
553 m_indiP_timing.add( pcf::IndiElement(
"acq_max" ) );
554 m_indiP_timing.add( pcf::IndiElement(
"acq_jitter" ) );
555 m_indiP_timing.add( pcf::IndiElement(
"write_fps" ) );
556 m_indiP_timing.add( pcf::IndiElement(
"write_min" ) );
557 m_indiP_timing.add( pcf::IndiElement(
"write_max" ) );
558 m_indiP_timing.add( pcf::IndiElement(
"write_jitter" ) );
559 m_indiP_timing.add( pcf::IndiElement(
"delta_aw" ) );
560 m_indiP_timing.add( pcf::IndiElement(
"delta_aw_jitter" ) );
562 if( derived().registerIndiPropertyReadOnly( m_indiP_timing ) < 0 )
564#ifndef FRAMEGRABBER_TEST_NOLOG
565 derivedT::template log<software_error>( { std::source_location::current() } );
572 if( derived().threadStart( m_fgThread,
580 fgThreadStart ) < 0 )
582 derivedT::template log<software_error, -1>( { std::source_location::current() } );
593 if( pthread_tryjoin_np( m_fgThread.native_handle(), 0 ) == 0 )
595 derivedT::template log<software_error>( {
"framegrabber thread has exited" } );
604 cbIndexT latTime = m_latencyCircBuffMaxTime * m_cbFPS;
605 if( latTime >= m_atimes.maxEntries() )
607 latTime = m_atimes.maxEntries() - 1;
610 cbIndexT usedEntries = std::min<cbIndexT>( latTime, m_atimes.size() );
612 if( usedEntries >= 2 )
614 m_atimesD.resize( usedEntries - 1 );
615 m_wtimesD.resize( usedEntries - 1 );
616 m_watimesD.resize( usedEntries - 1 );
618 cbIndexT refEntry = m_atimes.latest();
620 if( refEntry >= usedEntries )
622 refEntry -= usedEntries;
626 refEntry = m_atimes.maxEntries() + refEntry - usedEntries;
629 timespec ts = m_atimes.at( refEntry, 0 );
630 double a0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
632 ts = m_wtimes.at( refEntry, 0 );
633 double w0 = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
640 for(
size_t n = 1; n <= m_atimesD.size(); ++n )
642 ts = m_atimes.at( refEntry, n );
644 double a = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
646 ts = m_wtimes.at( refEntry, n );
648 double w = ts.tv_sec + ( (double)ts.tv_nsec ) / 1e9;
650 m_atimesD[n - 1] =
a - a0;
651 m_wtimesD[n - 1] = w - w0;
652 m_watimesD[n - 1] = w -
a;
656 if( m_atimesD[n - 1] < mina )
658 mina = m_atimesD[n - 1];
661 if( m_atimesD[n - 1] > maxa )
663 maxa = m_atimesD[n - 1];
666 if( m_wtimesD[n - 1] < minw )
668 minw = m_wtimesD[n - 1];
671 if( m_wtimesD[n - 1] > maxw )
673 maxw = m_wtimesD[n - 1];
676 if( m_wtimesD[n - 1] < 0 )
678 std::cerr <<
"negative wtime: " << m_wtimesD[n - 1] <<
' ' << n <<
' ' << m_atimesD.size()
679 <<
' ' << refEntry <<
' ' << m_atimes.maxEntries() <<
' ' << latTime <<
'\n';
681 return derivedT::template log<software_error, 0>( {
"negative write time. latency "
682 "circ buff is not long enought" } );
686 m_mna = mx::math::vectorMean( m_atimesD );
687 m_vara = mx::math::vectorVariance( m_atimesD, m_mna );
691 m_mnw = mx::math::vectorMean( m_wtimesD );
692 m_varw = mx::math::vectorVariance( m_wtimesD, m_mnw );
696 m_mnwa = mx::math::vectorMean( m_watimesD );
697 m_varwa = mx::math::vectorVariance( m_watimesD, m_mnwa );
729 catch(
const std::exception &e )
731 std::cerr << e.what() <<
'\n';
824 m_fgThreadID = syscall( SYS_gettid );
829 while( m_fgThreadInit ==
true && derived().shutdown() == 0 )
834 uint32_t imsize[3] = { 0, 0, 0 };
836 std::string shmimName;
838 static bool logged_wrong_size =
false;
840 while( derived().shutdown() == 0 )
843 while( !derived().shutdown() &&
845 derived().powerState() <= 0 ) )
850 if( derived().shutdown() )
856 if( derived().configureAcquisition() != 0 )
862 m_typeSize = ImageStreamIO_typesize( m_dataType );
864 if( configCircBuffs() < 0 )
866 derivedT::template log<software_error>( {
"error configuring latency circ. buffs" } );
873 if( m_imageStream !=
nullptr )
875 imsize[0] = m_imageStream->md[0].size[0];
877 if( m_imageStream->md[0].naxis > 1 )
879 imsize[1] = m_imageStream->md[0].size[1];
882 if( m_imageStream->md[0].naxis > 2 )
884 imsize[2] = m_imageStream->md[0].size[2];
894 if( m_width != imsize[0] || m_height != imsize[1] || m_circBuffLength != imsize[2] || m_imageStream ==
nullptr )
896 if( m_imageStream !=
nullptr && m_ownShmim )
898 ImageStreamIO_destroyIm( m_imageStream );
899 free( m_imageStream );
901 else if( m_imageStream !=
nullptr && !m_ownShmim )
903 if( !logged_wrong_size )
905 derivedT::template log<text_log>(
906 std::format(
"image stream {} is not expected size", m_shmimName ), logPrio::LOG_WARNING );
907 logged_wrong_size =
true;
914 logged_wrong_size =
false;
916 m_imageStream =
reinterpret_cast<IMAGE *
>( malloc(
sizeof( IMAGE ) ) );
919 imsize[1] = m_height;
920 imsize[2] = m_circBuffLength;
922 std::cerr <<
"Creating: " << m_shmimName <<
" " << m_width <<
" " << m_height <<
" " << m_circBuffLength
925 ImageStreamIO_createIm_gpu( m_imageStream,
934 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
937 m_imageStream->md->cnt1 = m_circBuffLength - 1;
945 if( derived().startAcquisition() < 0 )
950 uint64_t next_cnt1 = 0;
951 char *next_dest =
reinterpret_cast<char *
>( m_imageStream->array.raw );
953 timespec *next_wtimearr =
nullptr;
954 timespec *next_atimearr =
nullptr;
955 uint64_t *next_cntarr =
nullptr;
959 next_wtimearr = &m_imageStream->writetimearray[0];
960 next_atimearr = &m_imageStream->atimearray[0];
961 next_cntarr = &m_imageStream->cntarray[0];
965 while( !derived().shutdown() && !m_reconfig && derived().powerState() > 0 )
970 int isValid = derived().acquireAndCheckValid();
984 m_imageStream->md->write = 1;
986 if( derived().loadImageIntoStream( next_dest ) < 0 )
993 if( clock_gettime( CLOCK_REALTIME, &m_imageStream->md->writetime ) < 0 )
995 derivedT::template log<software_critical>( { errno,
"clock_gettime" } );
999 m_imageStream->md->atime = m_currImageTimestamp;
1002 m_imageStream->md->cnt1 = next_cnt1;
1005 m_imageStream->md->cnt0++;
1009 *next_wtimearr = m_imageStream->md->writetime;
1010 *next_atimearr = m_currImageTimestamp;
1011 *next_cntarr = m_imageStream->md->cnt0;
1015 m_imageStream->md->write = 0;
1016 ImageStreamIO_sempost( m_imageStream, -1 );
1018 if( postPublishHook( derived(), m_imageStream, 0 ) < 0 )
1024 if( m_atimes.maxEntries() > 0 )
1026 m_atimes.nextEntry( m_imageStream->md->atime );
1027 m_wtimes.nextEntry( m_imageStream->md->writetime );
1031 next_cnt1 = m_imageStream->md->cnt1 + 1;
1032 if( next_cnt1 >= m_circBuffLength )
1038 reinterpret_cast<char *
>( m_imageStream->array.raw ) + next_cnt1 * m_width * m_height * m_typeSize;
1042 next_wtimearr = &m_imageStream->writetimearray[next_cnt1];
1043 next_atimearr = &m_imageStream->atimearray[next_cnt1];
1044 next_cntarr = &m_imageStream->cntarray[next_cnt1];
1048 m_dummy_c = next_dest[0];
1052 m_dummy_ts.tv_sec = next_wtimearr[0].tv_sec + next_atimearr[0].tv_sec;
1053 m_dummy_cnt = next_cntarr[0];
1056 if( m_cbFPS != derived().fps() )
1058 std::cerr <<
"configuring due to mismatch m_cbFPS\n";
1059 if( configCircBuffs() < 0 )
1061 derivedT::template log<software_error>( {
"error configuring latency circ. buffs" } );
1066 if( m_reconfig && !derived().shutdown() )
1068 derived().reconfig();
1073 if( m_imageStream !=
nullptr )
1077 ImageStreamIO_destroyIm( m_imageStream );
1081 ImageStreamIO_closeIm( m_imageStream );
1084 free( m_imageStream );
1085 m_imageStream =
nullptr;
1092 if( !derivedT::c_frameGrabber_flippable )
1094 return memcpy( dest, src, width * height * szof );
1098 switch( m_defaultFlip )
1101 return mx::improc::imcpy( dest, src, width, height, szof );
1103 return mx::improc::imcpy_flipUD( dest, src, width, height, szof );
1105 return mx::improc::imcpy_flipLR( dest, src, width, height, szof );
1107 return mx::improc::imcpy_flipUDLR( dest, src, width, height, szof );
1117 static bool logged =
false;
1119 if( m_imageStream !=
nullptr )
1121 ImageStreamIO_closeIm( m_imageStream );
1122 free( m_imageStream );
1123 m_imageStream =
nullptr;
1129 char SM_fname[1024];
1130 ImageStreamIO_filename( SM_fname,
sizeof( SM_fname ), m_shmimName.c_str() );
1131 SM_fd = open( SM_fname, O_RDWR );
1137 derivedT::template log<text_log>(
"ImageStream " + m_shmimName +
" not found (yet). Retrying . . .",
1138 logPrio::LOG_NOTICE );
1149 m_imageStream =
reinterpret_cast<IMAGE *
>( malloc(
sizeof( IMAGE ) ) );
1151 if( ImageStreamIO_openIm( m_imageStream, m_shmimName.c_str() ) == 0 )
1153 if( m_imageStream->md[0].sem < SEMAPHORE_MAXVAL )
1155 ImageStreamIO_closeIm( m_imageStream );
1156 free( m_imageStream );
1157 m_imageStream =
nullptr;
1163 char SM_fname[1024];
1164 ImageStreamIO_filename( SM_fname,
sizeof( SM_fname ), m_shmimName.c_str() );
1168 int rv = stat( SM_fname, &buffer );
1172 derivedT::template log<software_critical>(
1174 "Could not get inode for " + m_shmimName +
". Source process will need to be restarted." } );
1176 ImageStreamIO_closeIm( m_imageStream );
1178 free( m_imageStream );
1180 m_imageStream =
nullptr;
1182 derived().m_shutdown =
true;
1187 m_inode = buffer.st_ino;
1189 m_width = m_imageStream->md->size[0];
1191 if( m_imageStream->md->naxis == 2 )
1193 m_height = m_imageStream->md->size[1];
1194 m_circBuffLength = 1;
1196 else if( m_imageStream->md->naxis == 3 )
1198 m_height = m_imageStream->md->size[1];
1199 m_circBuffLength = m_imageStream->md->size[2];
1204 m_circBuffLength = 1;
1207 m_dataType = m_imageStream->md->datatype;
1208 m_typeSize = ImageStreamIO_typesize( m_dataType );
1215 free( m_imageStream );
1216 m_imageStream =
nullptr;
1225 if( !derived().m_indiDriver )
1240 indi::updateIfChanged<double>(
1251 "delta_aw_jitter" },
1252 { fpsa, m_mina, m_maxa, sqrt( m_vara ), fpsw, m_minw, m_maxw, sqrt( m_varw ), m_mnwa, sqrt( m_varwa ) },
1253 derived().m_indiDriver );