# HG changeset patch # User Giulio Moro # Date 1442241731 -3600 # Node ID 44d07fa9bd03bbc975087b70297fdffc27a86cca # Parent 5edc6d0713ef26271897cc3bc387683ea353f37a Ultra-basic feedback for clock sync works^CIssues: response time of the IIR filter is too slow, requires PID and better filtering algorithm. diff -r 5edc6d0713ef -r 44d07fa9bd03 core/ClockSync.cpp --- a/core/ClockSync.cpp Mon Sep 14 14:57:54 2015 +0100 +++ b/core/ClockSync.cpp Mon Sep 14 15:42:11 2015 +0100 @@ -10,14 +10,20 @@ // isSlave() ? client.setServer("127.0.0.1") : client.setServer("127.0.0.1"); isSlave() ? client.setServer("192.168.7.1") : client.setServer("192.168.7.2"); bufferLength=kSyncMessageLength; + resetTs(); + receiveLoopSleepUs=100; + receiveLoopTimeout=1e5; + movingAverage.setLength(31); + expectedClockSyncType=isSlave() ? kSync : kNone; +} +void ClockSync::resetTs(){ T1=-1; T1p=-1; T2=-1; T2p=-1; - receiveLoopSleepUs=100; - receiveLoopTimeout=1e5; - movingAverage.setLength(31); - expectedClockSyncType=isSlave() ? kSync : kNone; +} +bool ClockSync::areTsValid(){ + return T1>0 && T1p>0 && T2>0 && T2p>0; } ClockSync::ClockSync(bool thisIsSlave, int aPort, VirtualClock &aVirtualClock){ init(thisIsSlave, aPort, aVirtualClock); @@ -190,6 +196,7 @@ int ClockSync::slaveHandleMessage(){ switch(clockSyncType){ case kSync: //the clockSync timestamp is meaningless, the localTimestamp is when kSync was received + resetTs(); T1p=localTimestamp; expectedClockSyncType=kFollowUp; break; @@ -209,8 +216,42 @@ T2p=clockSyncTimestamp; //TODO: evaluate things double offset=(T1p-T1-T2p+T2)/2.0d; - printf("-----------OFFSET IS : %04.4f seconds, average: %04.4f seconds\n", - offset/44100.f, movingAverage.add(offset)/44100.f); + if(areTsValid()){ + processOffset(offset); + + /* + static int calls=0; + static double referenceOffset=0; + + if(calls<100){ // start by averaging everything + movingAverage.add(offset); + } else { //once we get an estimate, start discarding outliers + float maxOffsetDeviation=20; + float deviation=fabsf(movingAverage.getAverage()-offset); + if(deviation +extern I2c_Codec* gAudioCodec; +void ClockSync::processOffset(double offset){ + static int calls=0; + // TODO: change the flow control below so that it can happen multiple times + //(base it upon the length of movingAverage rather than the number of calls) + if(calls<10) { //get an initial guess + movingAverage.add(offset); +// printf("-----------OFFSET IS : %04.4f samples, average: %04.4f samples\n", +// offset, movingAverage.getAverage()); + } else if (calls==10){ //then compensate for initial offset +// printf("compensating for offset: %f\n", offset); + virtualClock->addOffset(movingAverage.getAverage()); + movingAverage.reset(); + } else if (calls>=10){ //use IIR filter from now on + //filter coefficients obtained from Matlab : [B,A]=butter(2,0.005); +// static float B[3]={6.10061787580662e-05, 0.000122012357516132, 6.10061787580662e-05}; +// static float A[3]={1, -1.97778648377676, 0.978030508491796}; + static float B[3]={6.10061787580662e-05, 0.000122012357516132, 6.10061787580662e-05}; + static float A[3]={1, -1.97778648377676, 0.978030508491796}; + static float pastOut[3]={0,0,0}; + static float pastIn[3]={0,0,0}; + float in=offset; + float out= -pastOut[1]*A[1] -pastOut[2]*A[2] +in*B[0] +pastIn[1]*B[1] +pastIn[2]*B[2]; + pastOut[2]=pastOut[1]; + pastOut[1]=out; + pastIn[2]=pastIn[1]; + pastIn[1]=in; + offset=out; + static float maxOffset=0; + maxOffset=fabsf(offset) > fabsf(maxOffset) ? offset : maxOffset; + printf("%10.3f, %10.3f, %10.3f, %10.3f\n", in, offset, offset-pastOut[2], maxOffset); //unfiltered, filtered + if(fabsf(offset)>10 && calls>30){ + calls=11; + //TODO: correct for offset + float targetSamplingRate=offset>0 ? 44097 : 44103; + gAudioCodec->setAudioSamplingRate(targetSamplingRate); +// pastOut[1]=pastOut[2]=pastIn[1]=pastIn[2]=offset; + printf("------setAudioSmplingRate to %f\n", targetSamplingRate); + } + } + calls++; +} int ClockSync::masterHandleMessage(){ switch(clockSyncType){ case kDelayReq: - //TODO: do something with it //send kDelayResp setType(kDelayResp); setTimestamp(localTimestamp); send(); expectedClockSyncType=kNone; + return 1; break; + default: + return -1; } } diff -r 5edc6d0713ef -r 44d07fa9bd03 core/ClockSyncThread.cpp --- a/core/ClockSyncThread.cpp Mon Sep 14 14:57:54 2015 +0100 +++ b/core/ClockSyncThread.cpp Mon Sep 14 15:42:11 2015 +0100 @@ -39,9 +39,7 @@ #ifdef USE_JUCE #else void ClockSyncThread::startThread(){ - printf("starting\n"); BeagleRT_scheduleAuxiliaryTask(clockSyncTask); - printf("started\n"); } void ClockSyncThread::stopThread(){ threadIsExiting=true; @@ -56,11 +54,13 @@ }; void ClockSyncThread::run(){ + printf("var=["); while(!threadShouldExit()){ clockSync.sendReceiveLoop(); // double now=virtualClock->getNow(); // printf("th(end+1)=%f;\n", now); // printf("act(end+1)=%lld;\n", Clock::getTimeUs()); } - printf("Thread is not running \n"); + printf("];\n"); +// printf("Thread is not running \n"); } diff -r 5edc6d0713ef -r 44d07fa9bd03 core/VirtualClock.cpp --- a/core/VirtualClock.cpp Mon Sep 14 14:57:54 2015 +0100 +++ b/core/VirtualClock.cpp Mon Sep 14 15:42:11 2015 +0100 @@ -1,8 +1,12 @@ #include "VirtualClock.h" void VirtualClock::init(){ firstRun=true; - movingAverage.setLength(31); //TODO: a better filtering algorithm ( Did you say Kalman?) + movingAverage.setLength(101); //TODO: a better filtering algorithm ( Did you say Kalman?) period=-1; + elapsedPeriods=0; + startTime=0; + startTimeOffset=0; + elapsedPeriodsOffset=0; } VirtualClock::VirtualClock(){ @@ -11,13 +15,29 @@ void VirtualClock::sync(){ sync(1); } -void VirtualClock::sync(double count){ +void VirtualClock::sync(double numPeriods){ myClock_t currentTime=Clock::getTimeUs(); + static int calls=0; + elapsedPeriods+=numPeriods; + if(calls==50){ //TODO: this is dangerous as the clock might jump suddenly if currentTime is not precise + calls=0; + startTimeOffset=startTime; + startTime=currentTime; + elapsedPeriodsOffset=elapsedPeriods; + } + calls++; if(firstRun==true){ firstRun=false; startTime=currentTime; } else { - period=movingAverage.add((currentTime-lastSync)/count); //TODO: replace with Kalman filter + double newPeriod=(currentTime-lastSync)/numPeriods; + double expectedPeriod=22.67; + double maxPeriodDeviation=10; + if(fabs(newPeriod-expectedPeriod) +#endif /* USE_JUCE */ class VirtualClock{ private: myClock_t startTime; + myClock_t startTimeOffset; myClock_t lastSync; bool firstRun; + double elapsedPeriods; + double elapsedPeriodsOffset; double period; MovingAverage movingAverage; public: @@ -21,7 +29,7 @@ /** Call this method asynchronously, passing a number of equally spaced events that have elapsed since the last call. */ - void sync(double count); + void sync(double numPeriods); /** Get the current time according to the VirtualClock. @@ -34,6 +42,12 @@ Get the length of the period (difference between calls to sync() after various filtering operations) */ double getPeriod(); +/** + * Add an offset to the number of elapsed periods. + * + * Add an offset to the number of elapsed periods. It also compensates for the corresponding time offset. + */ + void addOffset(double periodOffset); }; #endif /* VIRTUAL_CLOCK_H_INCLUDED */ diff -r 5edc6d0713ef -r 44d07fa9bd03 projects/scope/render.cpp --- a/projects/scope/render.cpp Mon Sep 14 14:57:54 2015 +0100 +++ b/projects/scope/render.cpp Mon Sep 14 15:42:11 2015 +0100 @@ -26,6 +26,19 @@ extern I2c_Codec* gAudioCodec; VirtualClock virtualClock; ClockSyncThread clockSyncThread; +AuxiliaryTask testTime; +void testTimeFunction(){ + rt_printf("time=["); + while(!gShouldStop){ + rt_task_sleep(50000*1e3); + rt_printf("%f, ", virtualClock.getNow()); + rt_printf("%f, ", virtualClock.getPeriod()); + rt_task_sleep(20000); + rt_printf("%f,", virtualClock.getNow()); + rt_printf("%f\n", virtualClock.getPeriod()); + } + rt_printf("];"); +} bool setup(BeagleRTContext *context, void *userData) { // receiveAudio0.init(10000, context->audioFrames, 0); @@ -35,7 +48,7 @@ // scope.setPort(0, 9999); // scope.setPort(1, 10000); // networkSend.setup(context->audioSampleRate, context->audioFrames, 0, 9999, "192.168.7.1"); - clockSynchronizer.setup(); +// clockSynchronizer.setup(); virtualClock.init(); clockSyncThread.init(true, 5000, virtualClock); //start as slave gInverseSampleRate = 1.0/context->audioSampleRate; @@ -46,6 +59,7 @@ gFrequency1 = 200.0; gFrequency2 = 201.0; +// testTime=BeagleRT_createAuxiliaryTask(testTimeFunction, 80, "testTimeTask"); return true; } @@ -58,8 +72,10 @@ { virtualClock.sync(context->audioFrames); static int count=0; - if(count==0) - clockSyncThread.startThread(); + if(count==0){ +// BeagleRT_scheduleAuxiliaryTask(testTime); + clockSyncThread.startThread(); //make sure you uncomment .init in setup() + } static float phase=0; float phaseInc=200.0/44100.0*2*M_PI; // rt_printf("phaseInc: %f, phase: %f\n",phaseInc,phase);