Commit 2602ae73 authored by Jussi Lindgren's avatar Jussi Lindgren

Merge branch 'wip-jlindgre-fix-generator-timing' of...

Merge branch 'wip-jlindgre-fix-generator-timing' of gitlab.inria.fr:openvibe/extras into wip-all-openvibe20-base
parents 697dc7b3 60128b95
......@@ -196,6 +196,7 @@ namespace OpenViBEAcquisitionServer
* If \c f64Impedance is any other negative value, then the impedance is considered as "unknown"
*/
virtual OpenViBE::boolean updateImpedance(const OpenViBE::uint32 ui32ChannelIndex, const OpenViBE::float64 f64Impedance)=0;
};
/**
......
......@@ -20,8 +20,7 @@ CDriverGenericOscillator::CDriverGenericOscillator(IDriverContext& rDriverContex
,m_oSettings("AcquisitionServer_Driver_GenericOscillator", m_rDriverContext.getConfigurationManager())
,m_pCallback(NULL)
,m_ui32SampleCountPerSentBlock(0)
,m_pSample(NULL)
,m_ui32TotalSampleCount(0)
,m_ui64TotalSampleCount(0)
,m_ui64StartTime(0)
,m_bSendPeriodicStimulations(false)
{
......@@ -76,12 +75,7 @@ boolean CDriverGenericOscillator::initialize(
}
}
m_pSample=new float32[m_oHeader.getChannelCount()*ui32SampleCountPerSentBlock];
if(!m_pSample)
  • You are not garenteed to have your memory allocated with resize. A check should still happen afterward.

  • I see numerous resizes in the sdk certifiable perimeter without any checks. I recommend fixing these first. Wontfix in extras for now.

    Edited by Jussi Lindgren
Please register or sign in to reply
{
m_rDriverContext.getLogManager() << LogLevel_Error << "GenericOscillator: Memory allocation error\n";
return false;
}
m_vSample.resize(m_oHeader.getChannelCount()*ui32SampleCountPerSentBlock);
m_pCallback=&rCallback;
m_ui32SampleCountPerSentBlock=ui32SampleCountPerSentBlock;
......@@ -96,8 +90,8 @@ boolean CDriverGenericOscillator::start(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(m_rDriverContext.isStarted()) { return false; }
m_ui32TotalSampleCount=0;
m_ui64StartTime=System::Time::zgetTime();
m_ui64TotalSampleCount=0;
m_ui64StartTime = System::Time::zgetTime();
return true;
}
......@@ -108,7 +102,7 @@ boolean CDriverGenericOscillator::loop(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(m_rDriverContext.isStarted())
if (m_rDriverContext.isStarted())
{
// Generate the contents we want to send next
CStimulationSet l_oStimulationSet;
......@@ -120,40 +114,37 @@ boolean CDriverGenericOscillator::loop(void)
l_oStimulationSet.setStimulationDuration(0, 0);
}
for(uint32 j=0; j<m_oHeader.getChannelCount(); j++)
const uint64 l_ui64Elapsed = System::Time::zgetTime() - m_ui64StartTime;
const uint64 l_ui64SamplesNeededSoFar = ITimeArithmetics::timeToSampleCount(m_oHeader.getSamplingFrequency(), l_ui64Elapsed);
if (l_ui64SamplesNeededSoFar <= m_ui64TotalSampleCount)
{
for(uint32 i=0; i<m_ui32SampleCountPerSentBlock; i++)
{
#if 1
const float64 l_f64Value=
::sin(((i+m_ui32TotalSampleCount)*(j+1)*12.3)/m_oHeader.getSamplingFrequency())+
::sin(((i+m_ui32TotalSampleCount)*(j+1)* 4.5)/m_oHeader.getSamplingFrequency())+
::sin(((i+m_ui32TotalSampleCount)*(j+1)*67.8)/m_oHeader.getSamplingFrequency());
m_pSample[j*m_ui32SampleCountPerSentBlock+i]=(float32)l_f64Value;
#else
m_pSample[j*m_ui32SampleCountPerSentBlock+i]=j;
#endif
}
// Too early
return true;
}
// If we're early, sleep before sending. Otherwise, push the chunk out immediately
const uint64 l_ui64CurrentTime = System::Time::zgetTime() - m_ui64StartTime;
const uint64 l_ui64NextTime = ITimeArithmetics::sampleCountToTime(m_oHeader.getSamplingFrequency(), m_ui32TotalSampleCount+m_ui32SampleCountPerSentBlock);
if(l_ui64NextTime>l_ui64CurrentTime)
const uint32 l_ui32RemainingSamples = static_cast<uint32>(l_ui64SamplesNeededSoFar - m_ui64TotalSampleCount);
if (l_ui32RemainingSamples * m_oHeader.getChannelCount() > m_vSample.size())
{
const uint64 l_ui64SleepTime = l_ui64NextTime - l_ui64CurrentTime;
System::Time::zsleep(l_ui64SleepTime);
m_vSample.resize(l_ui32RemainingSamples * m_oHeader.getChannelCount());
}
#ifdef TIMINGDEBUG
m_rDriverContext.getLogManager() << LogLevel_Info << "At " << ITimeArithmetics::timeToSeconds(l_ui64CurrentTime)*1000 << "ms filled for "
<< ITimeArithmetics::timeToSeconds(l_ui64NextTime)*1000 << "ms -> nSamples = " << m_ui32TotalSampleCount + m_ui32SampleCountPerSentBlock << "\n";
#endif
// std::cout << "At " << ITimeArithmetics::timeToSeconds(l_ui64Elapsed) * 1000 << "ms, remaining " << l_ui32RemainingSamples << " samples\n";
for (uint32 i = 0; i<l_ui32RemainingSamples; i++)
{
for (uint32 j = 0; j<m_oHeader.getChannelCount(); j++)
{
const float64 l_f64Value=
::sin((m_ui64TotalSampleCount*(j+1)*12.3)/m_oHeader.getSamplingFrequency())+
::sin((m_ui64TotalSampleCount*(j+1)* 4.5)/m_oHeader.getSamplingFrequency())+
::sin((m_ui64TotalSampleCount*(j+1)*67.8)/m_oHeader.getSamplingFrequency());
m_vSample[j*l_ui32RemainingSamples + i] = (float32)l_f64Value;
}
m_ui64TotalSampleCount++;
}
m_ui32TotalSampleCount+=m_ui32SampleCountPerSentBlock;
m_pCallback->setSamples(m_pSample);
m_pCallback->setSamples(&m_vSample[0], l_ui32RemainingSamples);
m_pCallback->setStimulationSet(l_oStimulationSet);
m_rDriverContext.correctDriftSampleCount(m_rDriverContext.getSuggestedDriftCorrectionSampleCount());
}
else
{
......@@ -185,8 +176,6 @@ boolean CDriverGenericOscillator::uninitialize(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(m_rDriverContext.isStarted()) { return false; }
delete [] m_pSample;
m_pSample=NULL;
m_pCallback=NULL;
return true;
......
......@@ -42,9 +42,10 @@ namespace OpenViBEAcquisitionServer
OpenViBEAcquisitionServer::CHeader m_oHeader;
OpenViBE::uint32 m_ui32SampleCountPerSentBlock;
OpenViBE::float32* m_pSample;
std::vector<OpenViBE::float32> m_vSample;
OpenViBE::uint32 m_ui64TotalSampleCount;
OpenViBE::uint32 m_ui32TotalSampleCount;
OpenViBE::uint64 m_ui64StartTime;
private:
......
......@@ -18,9 +18,9 @@ using namespace OpenViBE::Kernel;
CDriverGenericSawTooth::CDriverGenericSawTooth(IDriverContext& rDriverContext)
:IDriver(rDriverContext)
,m_pCallback(NULL)
,m_ui32SampleCountPerSentBlock(0)
,m_pSample(NULL)
,m_ui32TotalSampleCount(0)
,m_ui32ExternalBlockSize(0)
,m_vSample(NULL)
,m_ui64TotalSampleCount(0)
,m_ui64StartTime(0)
{
m_rDriverContext.getLogManager() << LogLevel_Trace << "CDriverGenericSawTooth::CDriverGenericSawTooth\n";
......@@ -62,15 +62,12 @@ boolean CDriverGenericSawTooth::initialize(
return false;
}
m_pSample=new float32[m_oHeader.getChannelCount()*ui32SampleCountPerSentBlock];
if(!m_pSample)
{
m_rDriverContext.getLogManager() << LogLevel_Error << "Sawtooth: Memory allocation error\n";
return false;
}
m_vSample.resize(m_oHeader.getChannelCount()*ui32SampleCountPerSentBlock);
m_pCallback=&rCallback;
m_ui32SampleCountPerSentBlock=ui32SampleCountPerSentBlock;
m_ui32ExternalBlockSize=ui32SampleCountPerSentBlock;
return true;
}
......@@ -82,8 +79,8 @@ boolean CDriverGenericSawTooth::start(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(m_rDriverContext.isStarted()) { return false; }
m_ui32TotalSampleCount=0;
m_ui64StartTime=System::Time::zgetTime();
m_ui64TotalSampleCount=0;
m_ui64StartTime = System::Time::zgetTime();
return true;
}
......@@ -97,36 +94,35 @@ boolean CDriverGenericSawTooth::loop(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(!m_rDriverContext.isStarted()) { return true; }
// Generate the data
for(uint32 j=0; j<m_oHeader.getChannelCount(); j++)
// Find out how many samples to send
const uint64 l_ui64Elapsed = System::Time::zgetTime() - m_ui64StartTime;
const uint64 l_ui64SamplesNeededSoFar = ITimeArithmetics::timeToSampleCount(m_oHeader.getSamplingFrequency(), l_ui64Elapsed);
if (l_ui64SamplesNeededSoFar <= m_ui64TotalSampleCount)
{
for(uint32 i=0; i<m_ui32SampleCountPerSentBlock; i++)
{
m_pSample[j*m_ui32SampleCountPerSentBlock+i]=float32(i)/(m_ui32SampleCountPerSentBlock-1);
}
// Too early
return true;
}
// If we're early, sleep before sending. Otherwise, push the chunk out immediately
const uint64 l_ui64CurrentTime = System::Time::zgetTime() - m_ui64StartTime;
const uint64 l_ui64NextTime = ITimeArithmetics::sampleCountToTime(m_oHeader.getSamplingFrequency(), m_ui32TotalSampleCount+m_ui32SampleCountPerSentBlock);
if(l_ui64NextTime>l_ui64CurrentTime)
const uint32 l_ui32RemainingSamples = static_cast<uint32>(l_ui64SamplesNeededSoFar - m_ui64TotalSampleCount);
if (l_ui32RemainingSamples * m_oHeader.getChannelCount() > m_vSample.size())
{
const uint64 l_ui64SleepTime = l_ui64NextTime - l_ui64CurrentTime;
System::Time::zsleep(l_ui64SleepTime);
m_vSample.resize(l_ui32RemainingSamples * m_oHeader.getChannelCount());
}
#ifdef TIMINGDEBUG
m_rDriverContext.getLogManager() << LogLevel_Info << "At " << ITimeArithmetics::timeToSeconds(l_ui64CurrentTime)*1000 << "ms filling for "
<< ITimeArithmetics::timeToSeconds(l_ui64NextTime)*1000 << "ms -> nSamples = " << m_ui32TotalSampleCount + m_ui32SampleCountPerSentBlock << "\n";
#endif
// Generate the data
// The result should be a linear ramp between [0,1] for each block sent *out* by the acquisition server
for(uint32 i=0; i<l_ui32RemainingSamples; i++)
{
for (uint32 j = 0; j<m_oHeader.getChannelCount(); j++)
{
m_vSample[j*l_ui32RemainingSamples + i] = float32(m_ui64TotalSampleCount % m_ui32ExternalBlockSize) / (m_ui32ExternalBlockSize - 1);
}
m_ui64TotalSampleCount++;
}
m_pCallback->setSamples(m_pSample);
m_pCallback->setSamples(&m_vSample[0],l_ui32RemainingSamples);
m_rDriverContext.correctDriftSampleCount(m_rDriverContext.getSuggestedDriftCorrectionSampleCount());
m_ui32TotalSampleCount+=m_ui32SampleCountPerSentBlock;
return true;
}
......@@ -146,8 +142,6 @@ boolean CDriverGenericSawTooth::uninitialize(void)
if(!m_rDriverContext.isConnected()) { return false; }
if(m_rDriverContext.isStarted()) { return false; }
delete [] m_pSample;
m_pSample=NULL;
m_pCallback=NULL;
return true;
......
......@@ -36,10 +36,11 @@ namespace OpenViBEAcquisitionServer
OpenViBEAcquisitionServer::IDriverCallback* m_pCallback;
OpenViBEAcquisitionServer::CHeader m_oHeader;
OpenViBE::uint32 m_ui32SampleCountPerSentBlock;
OpenViBE::float32* m_pSample;
OpenViBE::uint32 m_ui32ExternalBlockSize;
std::vector<OpenViBE::float32> m_vSample;
OpenViBE::uint64 m_ui64TotalSampleCount;
OpenViBE::uint32 m_ui32TotalSampleCount;
OpenViBE::uint64 m_ui64StartTime;
};
};
......
......@@ -292,7 +292,7 @@ boolean CDriverLabStreamingLayer::initialize(
float64 l_f64CurrentCaptureTime = l_f64StartCaptureTime;
while(l_f64CurrentCaptureTime != 0.0 && l_f64CurrentCaptureTime-l_f64StartCaptureTime < g_LSL_samplingRateEstimatationDuration)
{
float l_fPulledSampleTime = l_oTmpInlet.pull_sample(m_pBuffer, m_oHeader.getChannelCount(), g_LSL_readTimeOut);
double l_fPulledSampleTime = l_oTmpInlet.pull_sample(m_pBuffer, m_oHeader.getChannelCount(), g_LSL_readTimeOut);
if (l_fPulledSampleTime != 0)
{
l_ui32SampleCount++;
......@@ -468,6 +468,7 @@ boolean CDriverLabStreamingLayer::loop(void)
if(m_bLimitSpeed)
{
// If we were faster than what the AS expects, sleep.
// n.b. This sleep may not be accurate on Windows (it may oversleep)
const uint64 l_ui64TimeNow = System::Time::zgetTime() - m_ui64StartTime;
const uint64 l_ui64TimeLimitForBuffer = ITimeArithmetics::sampleCountToTime(m_oHeader.getSamplingFrequency(), m_ui64SampleCount+m_ui32SampleCountPerSentBlock);
......
......@@ -244,6 +244,7 @@ boolean CDriverNeuroServoHid::loop(void)
if (m_ui64SendBlockRequiredTime>l_ui64ElapsedTimeSinceLastSentBlock)
{
// If we're early, sleep before sending. This code regulate the data drift
// @fixme the code should not rely on zsleep() as its time precision can not be guaranteed; it can oversleep esp. on Windows + boost 1.58
const uint64 l_ui64SleepTime = m_ui64SendBlockRequiredTime - l_ui64ElapsedTimeSinceLastSentBlock;
System::Time::zsleep((uint64)(l_ui64SleepTime + m_i64DriftAutoCorrectionDelay));
}
......
......@@ -124,6 +124,11 @@ namespace OpenViBEAcquisitionServer
return m_rAcquisitionServer.updateImpedance(ui32ChannelIndex, f64Impedance);
}
virtual uint64 getStartTime(void) const
{
return m_rAcquisitionServer.m_ui64StartTime;
}
protected:
const IKernelContext& m_rKernelContext;
......@@ -239,6 +244,8 @@ CAcquisitionServer::CAcquisitionServer(const IKernelContext& rKernelContext)
,m_bReplacementInProgress(false)
,m_bInitialized(false)
,m_bStarted(false)
,m_ui64StartTime(0)
,m_ui64LastDeliveryTime(0)
,m_oDriftCorrection(rKernelContext)
{
m_pDriverContext=new CDriverContext(rKernelContext, *this);
......@@ -636,7 +643,6 @@ boolean CAcquisitionServer::connect(IDriver& rDriver, IHeader& rHeaderCopy, uint
if(!m_pDriver->initialize(m_ui32SampleCountPerSentBlock, *this))
{
m_rKernelContext.getLogManager() << LogLevel_Error << "Connection failed...\n";
m_ui64StartTime=System::Time::zgetTime();
return false;
}
......@@ -823,16 +829,13 @@ boolean CAcquisitionServer::start(void)
// Starts driver
if(!m_pDriver->start())
{
m_ui64StartTime=System::Time::zgetTime();
m_ui64LastDeliveryTime=m_ui64StartTime;
m_rKernelContext.getLogManager() << LogLevel_Error << "Starting failed !\n";
return false;
}
// m_pDriverContext->onStart(*m_pDriver->getHeader());
m_ui64StartTime=System::Time::zgetTime();
m_ui64LastDeliveryTime=m_ui64StartTime;
m_ui64StartTime = System::Time::zgetTime();
m_ui64LastDeliveryTime = m_ui64StartTime;
m_oDriftCorrection.start(m_ui32SamplingFrequency, m_ui64StartTime);
......@@ -1010,6 +1013,7 @@ void CAcquisitionServer::setSamples(const float32* pSample, const uint32 ui32Sam
m_vPendingBuffer.push_back(m_vSwapBuffer);
}
}
m_ui64LastSampleCount=m_ui64SampleCount;
m_ui64SampleCount+=ui32SampleCount*m_ui64OverSamplingFactor;
......
......@@ -226,6 +226,8 @@ boolean CDriftCorrection::estimateDrift(const uint64 ui64NewSamples)
const uint64 l_ui64CurrentTime = System::Time::zgetTime();
// m_rKernelContext.getLogManager() << LogLevel_Info << "Drift measured at " << ITimeArithmetics::timeToSeconds(l_ui64CurrentTime - m_ui64StartTime) * 1000 << "ms.\n";
const float64 l_f64Jitter = computeJitter(l_ui64CurrentTime);
m_vJitterEstimate.push_back(l_f64Jitter);
......
......@@ -116,7 +116,9 @@ int main(int argc, char ** argv)
//SetThreadAffinityMask(hProcess, threadMask);
// Set the clock interval to 1ms (default on Win7: 15ms). This is needed to get under 15ms accurate sleeps,
// and the precision of all the non-QPC clocks.
// and improves the precision of non-QPC clocks. Note that since boost 1.58, the sleeps no longer seem
// to be 1ms accurate on Windows (as they seemed to be on 1.55), and sleep can oversleep even 10ms even with
// timeBeginPeriod(1) called. @todo in the future, make sure nothing relies on sleep accuracy in openvibe
timeBeginPeriod(1);
// Since AS is just sleeping when its not acquiring, a high priority should not be a problem.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment