template< Aftr::SensorDatumConcept T > Aftr::SensorDatumTraversal::SensorDatumTraversal() { } template< Aftr::SensorDatumConcept T > Aftr::SensorDatumTraversal::SensorDatumTraversal( std::vector&& dat ) : dat( std::move(dat) ) { } template< Aftr::SensorDatumConcept T > Aftr::SensorDatumTraversal& Aftr::SensorDatumTraversal::operator=( SensorDatumTraversal&& toMove ) noexcept { this->dat = std::move( toMove.dat ); this->startTime = toMove.startTime; ////this->timeDurationAtFirstDatum = toMove.timeDurationAtFirstDatum; this->timeAtFirstDatum = toMove.timeAtFirstDatum; this->curIdx = toMove.curIdx; this->timeMultiplier = toMove.timeMultiplier; return *this; } template< Aftr::SensorDatumConcept T > Aftr::SensorDatumTraversal::~SensorDatumTraversal() { } template< Aftr::SensorDatumConcept T > std::chrono::utc_clock::time_point Aftr::SensorDatumTraversal::initPlayback() { return this->initPlayback( std::chrono::utc_clock::now() ); } template< Aftr::SensorDatumConcept T > std::chrono::utc_clock::time_point Aftr::SensorDatumTraversal::initPlayback( std::chrono::utc_clock::time_point const& initializationTimePoint ) { ////this->timeDurationAtFirstDatum = std::chrono::duration< std::chrono::system_clock::rep, std::chrono::system_clock::period >( 0 ); this->startTime = initializationTimePoint; this->curIdx = 0; //auto rawDatumTimeDurationUSec = std::chrono::duration_cast( ptr( dat[curIdx] )->getDuration() ).count(); if( this->dat.size() > 0 ) this->timeAtFirstDatum = this->dat[0].getTime(); return startTime; } template< Aftr::SensorDatumConcept T > void Aftr::SensorDatumTraversal::setTimeMultiplier( double multiplier ) { this->timeMultiplier = multiplier; this->setPlaybackLocationIdx( this->curIdx, std::chrono::utc_clock::now() ); } template< Aftr::SensorDatumConcept T > double Aftr::SensorDatumTraversal::getTimeMultiplier() { return this->timeMultiplier; } template< Aftr::SensorDatumConcept T > double Aftr::SensorDatumTraversal::getPlaybackLocation() const { double t = static_cast( this->curIdx ) / static_cast( this->dat.size() ); return t; } template< Aftr::SensorDatumConcept T > size_t Aftr::SensorDatumTraversal::getPlaybackLocationIdx() const { return this->curIdx; } template< Aftr::SensorDatumConcept T > void Aftr::SensorDatumTraversal::setPlaybackLocation( double parametricProgressThruData, std::chrono::utc_clock::time_point const& timeNow ) { if( parametricProgressThruData < 0 || parametricProgressThruData > 1.0 ) { fmt::print( "WARNING: {:s}: cannot set parametric playback location outside domain [0,1]\n", AFTR_FILE_LINE_STR ); return; } double dIdx = static_cast( this->dat.size() ) * parametricProgressThruData; this->curIdx = static_cast( dIdx ); if( curIdx >= this->dat.size() ) curIdx = this->dat.size() - 1; this->setPlaybackLocationIdx( this->curIdx, timeNow ); } template< Aftr::SensorDatumConcept T > void Aftr::SensorDatumTraversal::setPlaybackLocationIdx( size_t datumIndex, std::chrono::utc_clock::time_point const& timeNow ) { if( this->dat.size() == 0 ) return; if( datumIndex > this->dat.size()-1 ) { fmt::print( "WARNING: {:s}: cannot set playback location index beyond last datum...\n" " user tried to set curIdx to {:d}, but max valid value is ", AFTR_FILE_LINE_STR, datumIndex, this->dat.size() - 1 ); // std::cout << "WARNING: " << AFTR_FILE_LINE_STR << " cannot set playback location index beyond last datum...\n" // << " user tried to set curIdx to " << datumIndex << ", but max valid value is " // << this->dat.size()-1 << "... Ignoring this invocation...\n"; return; } this->curIdx = datumIndex; //calculate virtual clock time of curIdx given timeMultiplier ////std::chrono::duration< int64_t, std::micro > vDurCurIdx = std::chrono::duration_cast( ptr( this->dat[this->curIdx] )->getDuration() ); auto t_curIdx = ptr( this->dat[this->curIdx] )->getTime(); //timestamp of this current datum //adjust for constant time offset in log file (opposite of what we do in getNextNearestDatum (subtract & divide inside this method versus multiply then add in other method) ////vDurCurIdx -= std::chrono::duration_cast( this->timeAtFirstDatum ); auto delta_t = t_curIdx - this->timeAtFirstDatum; //duration from beginning of data set to this current datum //convert vDurCurIdx to a double so it can be divided by timeMultiplier w/o integer truncation ////double vDurCurIdxDbl = static_cast( std::chrono::duration_cast( vDurCurIdx ).count() ); double vDurCurIdxDbl = static_cast(std::chrono::duration_cast( delta_t ).count()); //divide by timeMultiplier if( !Aftr::isFloatZero( this->timeMultiplier, 0.000001 ) ) vDurCurIdxDbl /= this->timeMultiplier; //convert back to a chrono::duration from a double ////vDurCurIdx = std::chrono::duration< int64_t, std::micro >( static_cast( vDurCurIdxDbl ) ); delta_t = std::chrono::duration< int64_t, std::micro >( static_cast(vDurCurIdxDbl) ); //time-multiplier adjusted duration ////auto timePt = timeNow - vDurCurIdx; auto timePt = timeNow - delta_t; auto diff = timePt - this->startTime; this->startTime += diff; } template< Aftr::SensorDatumConcept T > const T& Aftr::SensorDatumTraversal::getNextDatum() noexcept { size_t DAT_SIZE = dat.size(); if( DAT_SIZE == 0 ) { fmt::print( "WARNING: {:s}: SensorDatumTraversal has a data set of size 0...\n", AFTR_FILE_LINE_STR ); // std::cout << "WARNING: " << AFTR_FILE_LINE_STR << " SensorDatumTraversal has a data set of size 0...\n"; static T dummy; return dummy; } if( this->timeMultiplier >= 0 ) // we are marching forwards in time if( curIdx < (DAT_SIZE - 1) ) ++curIdx; else //we are marching backwards in time if( curIdx > 0 ) --this->curIdx; return this->dat[this->curIdx]; } template< Aftr::SensorDatumConcept T > const T& Aftr::SensorDatumTraversal::getNextNearestDatum( bool interpolate ) { size_t DAT_SIZE = dat.size(); if( DAT_SIZE == 0 ) { fmt::print( "WARNING: {:s}: SensorDatumTraversal has a data set of size 0...\n", AFTR_FILE_LINE_STR ); //std::cout << "WARNING: " << AFTR_FILE_LINE_STR << " SensorDatumTraversal has a data set of size 0...\n"; static T dummy; return dummy; } //duration stores the wall clock time elapsed since the last call to initPlayback. //Since we assume we want to traverse the data at wall time (real world time), this is an accurate indicator //or wall time. auto durationAlongDataSet = std::chrono::utc_clock::now() - this->startTime; int64_t rawDurationUSec = std::chrono::duration_cast( durationAlongDataSet ).count(); //std::cout << rawDurationUSec << "usec has elapsed...\n"; rawDurationUSec = static_cast( static_cast( rawDurationUSec ) * this->timeMultiplier ); //scale by time multiplier //std::cout << rawDurationUSec << "usec has elapsed after multiplier...\n"; //now that we've scaled the rawDurationUSec by timeMultiplier, we have to convert it back to a duration and add in starting offset time and timewarp time durationAlongDataSet = std::chrono::duration< int64_t, std::micro >( rawDurationUSec ); ////durationAlongDataSet += this->timeDurationAtFirstDatum; //account for non-zero start time (that is positive) as well as for a call to setPlaybackLocation auto timeAlongDataSet = durationAlongDataSet + this->timeAtFirstDatum; ////rawDurationUSec = std::chrono::duration_cast( durationAlongDataSet ).count(); ////auto rawDatumTimeDurationUSec = std::chrono::duration_cast( ptr( dat[curIdx] )->getDuration() ).count(); auto timeAtThisDatum = ptr( this->dat[this->curIdx] )->getTime(); //we ought to index into the data such that we find the time closest to "duration" assuming the log file's time begins at zero if( this->timeMultiplier >= 0 ) // we are marching forwards in time { ////while( rawDatumTimeDurationUSec < rawDurationUSec ) while( timeAtThisDatum < timeAlongDataSet ) { if( curIdx < (DAT_SIZE - 1) ) ++curIdx; ////rawDatumTimeDurationUSec = std::chrono::duration_cast( ptr( dat[curIdx] )->getDuration() ).count(); timeAtThisDatum = ptr( this->dat[this->curIdx] )->getTime(); if( curIdx == (DAT_SIZE - 1) ) break; //we've reached the last data point, so we return the most recent point we have } if( interpolate ) { //interpolate the data in between curIdx and curIdx-1, by parametric value //interp( curIdx, curIdx-1, t ) ////auto upper = std::chrono::duration_cast( ptr( dat[curIdx] )->getDuration() ).count(); ////auto lower = std::chrono::duration_cast( ptr( dat[curIdx-1] )->getDuration() ).count(); auto upperTime = ptr( dat[curIdx] )->getTime(); auto lowerTime = ptr( dat[curIdx - 1] )->getTime(); ////auto domain = upper - lower; auto domainDur = upperTime - lowerTime; ////auto deltaT = rawDurationUSec - lower; auto deltaT = timeAlongDataSet - lowerTime; ////double t = static_cast( deltaT ) / static_cast( domain ); double t = static_cast( deltaT.count() ) / static_cast( domainDur.count() ); //interp( lower, upper, t ); //NOT YET TESTED THE INTERPOLATION BLOCK } } else //we are marching backwards in time - THIS HAS NOT YET BEEN TESTED - SLN { //swap the sign on rawDurationUSec, swap less-than to greater-than, and decrement curIdx //std::cout << "BACKWARDS IN TIME...\n"; ////while( rawDatumTimeDurationUSec > rawDurationUSec ) while( timeAtThisDatum > timeAlongDataSet ) { if( curIdx > 0 ) --curIdx; ////rawDatumTimeDurationUSec = std::chrono::duration_cast( ptr( dat[curIdx] )->getDuration() ).count(); timeAtThisDatum = ptr( this->dat[this->curIdx] )->getTime(); if( curIdx == 0 ) break; //we've reached the earliest data point, so we return the earliest point we have } if( interpolate ) { //interpolate the data in between curIdx and curIdx+1 //interp( curIdx, curIdx+1, t ) } } //std::cout << "idx: " << curIdx << "\n"; return dat[curIdx]; } /// aaaaaaaaabbbbbbbbbbbcccccccc /// ^ ^ ^ /// | | Data that is after the elements we don't care about (should be trimmed) /// | Data we want to capture (create new Dataset of only the b's) - pred first evaluates to true at this element /// Data that is before the element we don't want (this should be trimmed) template< Aftr::SensorDatumConcept T > Aftr::SensorDatumTraversal Aftr::SensorDatumTraversal::trimDataSetTo( std::function pred, int length, Aftr::SensorDatumTraversal&& dat ) { // // aaaaaaaaabbbbbbbbbbbcccccccc // ^ ^ ^ // | | Data that is after the elements we don't care about (should be trimmed) // | Data we want to capture (create new Dataset of only the b's) - pred first evaluates to true at this element // Data that is before the element we don't want (this should be trimmed) //std::function pred = [] () { return false; } auto & d = dat.getData(); auto startIt = std::find_if( d.begin(), d.end(), pred ); if( startIt == d.end() ) { fmt::print( "{:s}: Trimmed to size zero...\n", AFTR_FILE_LINE_STR ); //std::cout << AFTR_FILE_LINE_STR << ": Trimmed to size zero...\n"; dat.dat.clear(); dat.curIdx = 0; //return std::move(dat); } auto numSelected = std::distance( startIt, d.end() ); if( numSelected >= static_cast(length ) ) { //we can return startId to startIt + length as a new AftrSensorDataTarversal dat.dat = std::vector( startIt, startIt + length ); return std::move(dat); } else { fmt::print( "ERROR: {:s}: max selectable given start pulse is only {:d}" " datums; however, user wanted to trim {:d} datums. Already removed {:d}" " datums from the beginning... Total input size is {:d}\n", AFTR_FILE_LINE_STR, numSelected, length, std::distance( d.begin(), startIt ), d.size() ); std::abort(); } return Aftr::SensorDatumTraversal{}; }