giuliomoro@132
|
1 /*
|
giuliomoro@132
|
2 * ClockSynchronizer.cpp
|
giuliomoro@132
|
3 *
|
giuliomoro@132
|
4 * Created on: 26 Aug 2015
|
giuliomoro@132
|
5 * Author: giulio
|
giuliomoro@132
|
6 */
|
giuliomoro@132
|
7
|
giuliomoro@132
|
8 #include "ClockSynchronizer.h"
|
giuliomoro@132
|
9
|
giuliomoro@132
|
10 // declare static members
|
giuliomoro@132
|
11 float ClockSynchronizer::targetSamplingRate;
|
giuliomoro@132
|
12 float ClockSynchronizer::currentSamplingRate;
|
giuliomoro@132
|
13 bool ClockSynchronizer::threadRunning;
|
giuliomoro@132
|
14 int ClockSynchronizer::threadWasRunning;
|
giuliomoro@132
|
15 bool ClockSynchronizer::staticConstructed=false;
|
giuliomoro@132
|
16 AuxiliaryTask ClockSynchronizer::setClockTask;
|
giuliomoro@132
|
17
|
giuliomoro@132
|
18 void ClockSynchronizer::staticConstructor(){
|
giuliomoro@132
|
19 int priority=90;
|
giuliomoro@132
|
20 setClockTask=BeagleRT_createAuxiliaryTask(&ClockSynchronizer::setClock, priority, "setClockTask");
|
giuliomoro@132
|
21 threadRunning=false;
|
giuliomoro@132
|
22 }
|
giuliomoro@132
|
23
|
giuliomoro@132
|
24 void ClockSynchronizer::setClock(){
|
giuliomoro@133
|
25 // rt_printf("Setting clock to %f\n",targetSamplingRate);
|
giuliomoro@132
|
26 threadRunning=true;
|
giuliomoro@132
|
27 //I2C magic
|
giuliomoro@132
|
28 gAudioCodec->setAudioSamplingRate(targetSamplingRate);
|
giuliomoro@132
|
29 threadRunning=false;
|
giuliomoro@133
|
30 threadWasRunning=1;
|
giuliomoro@133
|
31 // rt_printf("Exiting thread\n");
|
giuliomoro@132
|
32 };
|
giuliomoro@132
|
33
|
giuliomoro@132
|
34 ClockSynchronizer::ClockSynchronizer() {
|
giuliomoro@132
|
35 reset();
|
giuliomoro@132
|
36 }
|
giuliomoro@132
|
37
|
giuliomoro@132
|
38 ClockSynchronizer::~ClockSynchronizer(){};
|
giuliomoro@132
|
39
|
giuliomoro@132
|
40 void ClockSynchronizer::setup(){
|
giuliomoro@132
|
41 if(staticConstructed==false) //This should be called in the constructor, but because of the current limitations of
|
giuliomoro@132
|
42 // BeagleRT, it is here and setup() needs to be called in BeagleRT setup();
|
giuliomoro@132
|
43 staticConstructor();
|
giuliomoro@132
|
44 }
|
giuliomoro@132
|
45 void ClockSynchronizer::reset(){
|
giuliomoro@132
|
46 localCounter=0;
|
giuliomoro@132
|
47 remoteCounter=0;
|
giuliomoro@132
|
48 lastTime=0;
|
giuliomoro@132
|
49 localOffset=-1;
|
giuliomoro@132
|
50 remoteOffset=-1;
|
giuliomoro@132
|
51 timeOffset=-1;
|
giuliomoro@132
|
52 currentSamplingRate=44100;
|
giuliomoro@132
|
53 }
|
giuliomoro@132
|
54
|
giuliomoro@132
|
55 void ClockSynchronizer::update(int aLocalCounter, int aRemoteCounter, RTIME aLastTime){
|
giuliomoro@132
|
56 if(threadRunning==true){ //do nothing if clock is being adjusted
|
giuliomoro@132
|
57 rt_printf("do nothing if clock is being adjusted\n");
|
giuliomoro@132
|
58 return;
|
giuliomoro@132
|
59 }
|
giuliomoro@132
|
60 if(threadWasRunning > 0){ //reset variables after clock has been adjusted
|
giuliomoro@132
|
61 threadWasRunning--; // wait a few calls to make sure the clock stabilizes
|
giuliomoro@132
|
62 rt_printf("wait a few calls to make sure the clock stabilizes\n");
|
giuliomoro@132
|
63 return;
|
giuliomoro@132
|
64 }/*
|
giuliomoro@132
|
65 if(threadWasRunning==1){
|
giuliomoro@132
|
66 threadWasRunning=0;
|
giuliomoro@132
|
67 // reset offsets after a correction
|
giuliomoro@132
|
68 localOffset=aLocalCounter;
|
giuliomoro@132
|
69 remoteOffset=aRemoteCounter;
|
giuliomoro@132
|
70 timeOffset=aLastTime;
|
giuliomoro@132
|
71 rt_printf("reset variables after clock has been adjusted\n");
|
giuliomoro@132
|
72 return;
|
giuliomoro@132
|
73 }*/
|
giuliomoro@132
|
74 if (localOffset<=0 || remoteOffset<=0){ // probably this is the first run
|
giuliomoro@132
|
75 localOffset=aLocalCounter;
|
giuliomoro@132
|
76 remoteOffset=aRemoteCounter;
|
giuliomoro@132
|
77 timeOffset=aLastTime;
|
giuliomoro@132
|
78 rt_printf("First run of update(), localOffset: %d, remoteOffset: %d, timeOffset: %llu\n",
|
giuliomoro@132
|
79 localOffset, remoteOffset, timeOffset);
|
giuliomoro@132
|
80 return;
|
giuliomoro@132
|
81 }
|
giuliomoro@132
|
82 localCounter=aLocalCounter-localOffset;
|
giuliomoro@132
|
83 remoteCounter=aRemoteCounter-remoteOffset;
|
giuliomoro@132
|
84 lastTime=aLastTime-timeOffset;
|
giuliomoro@132
|
85 if (localCounter<=0 || remoteCounter<=0 || timeOffset<=0) {// did anything wrong happened?
|
giuliomoro@132
|
86 rt_printf("fourth\n");
|
giuliomoro@132
|
87 return;
|
giuliomoro@132
|
88 }
|
giuliomoro@132
|
89 // TODO: make sure we do not get actually adjust the clock too often (e.g.: limits on the timestamp)
|
giuliomoro@132
|
90 // Should keep track of last time a change has been made, so that when a large compensation is needed
|
giuliomoro@132
|
91 // (e.g.: as the program has just started), adjustClock() is called more frequently
|
giuliomoro@132
|
92 // but gets called less often later.
|
giuliomoro@132
|
93 // Should also try to avoid drastic correction after a while that the program
|
giuliomoro@132
|
94 // has started, so to avoid glitches. But this can maybe be handled by the thread itself.
|
giuliomoro@132
|
95 // TODO: should keep a log and maybe compensate for (and prevent) overshoot according to previous changes
|
giuliomoro@133
|
96 static RTIME lastlastTime=0;
|
giuliomoro@133
|
97 // rt_printf("interval: %f, \n",(double)(remoteCounter*300.0)/lastTime*1000000000.0);
|
giuliomoro@133
|
98 lastlastTime=lastTime;
|
giuliomoro@132
|
99 currentSamplingRate=gAudioCodec->getAudioSamplingRate();
|
giuliomoro@132
|
100 float T=1/currentSamplingRate;
|
giuliomoro@132
|
101 int elapsedSamples=remoteCounter*(NetworkBuffer::bufferLength-NetworkBuffer::headerLength);
|
giuliomoro@132
|
102 double expectedTimeUs=T*elapsedSamples*1000000;
|
giuliomoro@133
|
103 double actualTimeS=lastTime/1000000000.0;
|
giuliomoro@132
|
104 double actualTimeUs=lastTime/1000.0;
|
giuliomoro@133
|
105 static float averageBuffer[101]={0};
|
giuliomoro@133
|
106 static int averageBufferPointer=0;
|
giuliomoro@133
|
107 static float average=0;
|
giuliomoro@133
|
108 static int bufferFull=0;
|
giuliomoro@133
|
109 average-=averageBuffer[averageBufferPointer];
|
giuliomoro@133
|
110 averageBuffer[averageBufferPointer]=elapsedSamples/actualTimeS;
|
giuliomoro@133
|
111 average+=averageBuffer[averageBufferPointer];
|
giuliomoro@133
|
112 averageBufferPointer++;
|
giuliomoro@133
|
113 if(averageBufferPointer==101){
|
giuliomoro@133
|
114 averageBufferPointer=0;
|
giuliomoro@133
|
115 bufferFull++;
|
giuliomoro@133
|
116 }
|
giuliomoro@133
|
117
|
giuliomoro@132
|
118 // rt_printf("Fs: %f, expectedTimeUs: %f, lastTime: %ul\n", expectedTimeUs, lastTime);
|
giuliomoro@133
|
119 // rt_printf("Fs: %.1f, actualTimeS: %4.6f, targetFs_ %.3f\n",
|
giuliomoro@133
|
120 if((averageBufferPointer&3)==0){
|
giuliomoro@133
|
121 static float oldTargetSamplingRate=0;
|
giuliomoro@133
|
122 targetSamplingRate=average/101;
|
giuliomoro@133
|
123 if(bufferFull>=3 && fabsf(targetSamplingRate-oldTargetSamplingRate) < 1){
|
giuliomoro@133
|
124 rt_printf("%.1f, %4.6f, %.3f;\n",
|
giuliomoro@133
|
125 gAudioCodec->getAudioSamplingRate(), actualTimeS, targetSamplingRate, targetSamplingRate-oldTargetSamplingRate);
|
giuliomoro@133
|
126 adjustClock();
|
giuliomoro@133
|
127 }
|
giuliomoro@133
|
128 oldTargetSamplingRate=targetSamplingRate;
|
giuliomoro@133
|
129 }
|
giuliomoro@132
|
130 }
|
giuliomoro@132
|
131
|
giuliomoro@132
|
132 void ClockSynchronizer::adjustClock(){
|
giuliomoro@133
|
133 if(fabsf(currentSamplingRate-targetSamplingRate)>0.7){ //TODO: actually check that the difference is less than the quantization error in the PLL
|
giuliomoro@132
|
134 BeagleRT_scheduleAuxiliaryTask(setClockTask);
|
giuliomoro@132
|
135 }
|
giuliomoro@132
|
136 }
|