annotate core/ClockSync.cpp @ 151:e9c9404e3d1f ClockSync

Pff partially working. No PID. When setting the audio clock on the bbb to 44098 the master and slave clock keep diverging instead of converging ...
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 22 Sep 2015 04:10:07 +0100
parents 134bff10e561
children 8f98b32d0e23
rev   line source
giuliomoro@135 1 #include "ClockSync.h"
giuliomoro@135 2
giuliomoro@135 3 void ClockSync::setVirtualClock(VirtualClock &aVirtualClock){
giuliomoro@135 4 virtualClock=&aVirtualClock;
giuliomoro@135 5 }
giuliomoro@135 6 void ClockSync::init(bool thisIsSlave, int aPort, VirtualClock &aVirtualClock){
giuliomoro@135 7 setVirtualClock(aVirtualClock);
giuliomoro@135 8 slave=thisIsSlave;
giuliomoro@135 9 setPort(aPort);
giuliomoro@135 10 // isSlave() ? client.setServer("127.0.0.1") : client.setServer("127.0.0.1");
giuliomoro@135 11 isSlave() ? client.setServer("192.168.7.1") : client.setServer("192.168.7.2");
giuliomoro@135 12 bufferLength=kSyncMessageLength;
giuliomoro@141 13 resetTs();
giuliomoro@141 14 receiveLoopSleepUs=100;
giuliomoro@141 15 receiveLoopTimeout=1e5;
giuliomoro@141 16 movingAverage.setLength(31);
giuliomoro@141 17 expectedClockSyncType=isSlave() ? kSync : kNone;
giuliomoro@141 18 }
giuliomoro@141 19 void ClockSync::resetTs(){
giuliomoro@135 20 T1=-1;
giuliomoro@135 21 T1p=-1;
giuliomoro@135 22 T2=-1;
giuliomoro@135 23 T2p=-1;
giuliomoro@141 24 }
giuliomoro@141 25 bool ClockSync::areTsValid(){
giuliomoro@141 26 return T1>0 && T1p>0 && T2>0 && T2p>0;
giuliomoro@135 27 }
giuliomoro@135 28 ClockSync::ClockSync(bool thisIsSlave, int aPort, VirtualClock &aVirtualClock){
giuliomoro@135 29 init(thisIsSlave, aPort, aVirtualClock);
giuliomoro@135 30 }
giuliomoro@135 31 void* ClockSync::getBuffer(){
giuliomoro@135 32 return buffer;
giuliomoro@135 33 }
giuliomoro@135 34 bool ClockSync::isSlave(){
giuliomoro@135 35 return slave;
giuliomoro@135 36 }
giuliomoro@135 37 bool ClockSync::isMaster(){
giuliomoro@135 38 return !slave;
giuliomoro@135 39 }
giuliomoro@135 40 int ClockSync::getType(){
giuliomoro@135 41 return ((int*)buffer)[0];
giuliomoro@135 42 }
giuliomoro@135 43 myClock_t ClockSync::getTimestamp(){
giuliomoro@135 44 return *((myClock_t*)((char*)buffer+sizeof(int)));
giuliomoro@135 45 }
giuliomoro@135 46 void ClockSync::setType(int clockSyncType){
giuliomoro@135 47 ((int*)buffer)[0]=clockSyncType;
giuliomoro@135 48 }
giuliomoro@135 49 void ClockSync::setTimestamp(myClock_t timestamp){
giuliomoro@139 50 // printf("setting timestamp: %lld\n", timestamp);
giuliomoro@135 51 ((myClock_t*)((char*)buffer+sizeof(int)))[0]=timestamp;
giuliomoro@135 52 }
giuliomoro@135 53 void ClockSync::print(){
giuliomoro@135 54 //printf("type: %d, timestamp: %lld\n",*((int*)buffer),*((myClock_t*)(((int*)buffer)+1)));
giuliomoro@135 55 }
giuliomoro@135 56 void ClockSync::setPort(int aPort){
giuliomoro@135 57 if(aPort>0){
giuliomoro@135 58 int inPort=isSlave() ? aPort : aPort+1;
giuliomoro@135 59 int outPort=isSlave() ? aPort+1: aPort;
giuliomoro@135 60 server.bindToPort(inPort);
giuliomoro@135 61 client.setPort(outPort);
giuliomoro@135 62 //printf("Receiving on port %d\n", inPort);
giuliomoro@135 63 //printf("Sending to port %d\n", outPort);
giuliomoro@135 64 }
giuliomoro@135 65 }
giuliomoro@135 66 /**
giuliomoro@135 67 * sends a clockSync without blocking, checks results and returns the timestamp
giuliomoro@135 68 * immediately after the clockSync has been sent or -1 if there was an error or timeout expired.
giuliomoro@135 69 */
giuliomoro@135 70 myClock_t ClockSync::send(){
giuliomoro@135 71 // print();
giuliomoro@135 72 int ret;
giuliomoro@149 73 ret=client.waitUntilReady(false, isSlave() ? 110 : 5);
giuliomoro@135 74 if(ret<=0){ //don't retry
giuliomoro@135 75 return -1;
giuliomoro@135 76 }
giuliomoro@135 77 ret=client.send(buffer, bufferLength);
giuliomoro@135 78 myClock_t timestamp=(myClock_t)virtualClock->getNow();
giuliomoro@135 79 if(ret<0){
giuliomoro@135 80 //if there was an error while sending, don't retry
giuliomoro@135 81 return -1;
giuliomoro@135 82 }
giuliomoro@135 83 return timestamp; //get the accurate time *after* you sent the sync clockSync
giuliomoro@135 84 }
giuliomoro@135 85 /**
giuliomoro@135 86 * receives a clockSync without blocking, checks results and returns the timestamp
giuliomoro@135 87 * immediately after the clockSync has been received, or -1 if there was an error
giuliomoro@135 88 * or 0 if timeout expired.
giuliomoro@135 89 */
giuliomoro@135 90 myClock_t ClockSync::receive(){
giuliomoro@135 91 int ret;
giuliomoro@135 92 ret=server.waitUntilReady(true, 0);
giuliomoro@135 93 if(ret<=0){ //don't retry
giuliomoro@135 94 return 0;
giuliomoro@135 95 }
giuliomoro@135 96 ret=server.read(buffer, bufferLength, false);
giuliomoro@135 97 myClock_t timestamp=(myClock_t)virtualClock->getNow();
giuliomoro@135 98 if(timestamp==0){
giuliomoro@135 99 //printf("The virtualClock period is <=0\n");
giuliomoro@135 100 return -3;
giuliomoro@135 101 }
giuliomoro@135 102 if(ret==-1){
giuliomoro@135 103 //if there was an error while receiving, don't retry
giuliomoro@135 104 return -1;
giuliomoro@135 105 }
giuliomoro@135 106 if(ret!=bufferLength){
giuliomoro@135 107 //printf("Received a clockSync of the wrong size: %d\n", ret);
giuliomoro@135 108 return -2;
giuliomoro@135 109 }
giuliomoro@135 110 // print();
giuliomoro@135 111 return timestamp; //get the accurate time *after* you sent the sync clockSync
giuliomoro@135 112 }
giuliomoro@135 113
giuliomoro@135 114 int ClockSync::masterSendSync(){
giuliomoro@135 115 //let's send a sync clockSync!
giuliomoro@135 116 //printf("Sending a sync clockSync\n");
giuliomoro@135 117 setType(kSync);
giuliomoro@135 118 setTimestamp(-1);//do not care about sending the timestamp, a more accurate one will be sent in the follow up
giuliomoro@135 119 localTimestamp=send();
giuliomoro@135 120 if(localTimestamp<0){
giuliomoro@135 121 //printf("Could not send sync clockSync\n");
giuliomoro@135 122 return -1; //error, don't retry, start over
giuliomoro@135 123 }
giuliomoro@135 124 //let's send a followUp
giuliomoro@135 125 //printf("sent a sync clockSync\n");
giuliomoro@135 126 setType(kFollowUp);
giuliomoro@135 127 setTimestamp(localTimestamp);
giuliomoro@135 128 if(localTimestamp<0){
giuliomoro@135 129 //printf("Could not send followup clockSync\n");
giuliomoro@135 130 return -2; //error, don't retry, start over
giuliomoro@135 131 }
giuliomoro@135 132 int ret=send();
giuliomoro@135 133 if(ret<0){
giuliomoro@135 134 //printf("Error while sending followup\n");
giuliomoro@135 135 return -3;
giuliomoro@135 136 }
giuliomoro@135 137 //printf("sent a followUp clockSync\n");
giuliomoro@135 138 expectedClockSyncType=kDelayReq;
giuliomoro@135 139 return 1;
giuliomoro@135 140 }
giuliomoro@135 141 #ifdef USE_JUCE
giuliomoro@135 142 #define NOTGSHOULDSTOP 1
giuliomoro@135 143 #else
giuliomoro@135 144 extern bool gShouldStop;
giuliomoro@135 145 #define NOTGSHOULDSTOP (!gShouldStop)
giuliomoro@135 146 #endif /* USE_JUCE */
giuliomoro@135 147 int ClockSync::receiveLoop(){
giuliomoro@135 148 int receiveLoopElapsed=0;
giuliomoro@135 149 while( NOTGSHOULDSTOP && (isSlave() || (receiveLoopElapsed<receiveLoopTimeout))){ //when slave, does not timeout!
giuliomoro@135 150 receiveLoopElapsed+=receiveLoopSleepUs;
giuliomoro@135 151 usleep(receiveLoopSleepUs); //how often to check for new clockSyncs;
giuliomoro@135 152 // //printf("waiting for clockSyncs\n");
giuliomoro@135 153 localTimestamp=receive();
giuliomoro@135 154 if(localTimestamp<=0){
giuliomoro@135 155 if(localTimestamp==0){
giuliomoro@139 156 // printf("Socket not ready to be read: %lld\n", localTimestamp);
giuliomoro@135 157 }
giuliomoro@135 158 else if(localTimestamp==-1){
giuliomoro@139 159 printf("Error while receiving: %lld\n", localTimestamp);
giuliomoro@135 160 }
giuliomoro@135 161 else if(localTimestamp==-2){
giuliomoro@139 162 printf("Wrong size of the received clockSync: %lld\n", localTimestamp);
giuliomoro@135 163 }
giuliomoro@135 164 continue ; //keep waiting
giuliomoro@135 165 }
giuliomoro@135 166 clockSyncType=getType();
giuliomoro@135 167 clockSyncTimestamp=getTimestamp();
giuliomoro@135 168 if(clockSyncType!=expectedClockSyncType){
giuliomoro@135 169 //printf("Wrong clockSync type: %d, expected: %d\n",clockSyncType, expectedClockSyncType);
giuliomoro@135 170 return -2; //start over
giuliomoro@135 171 }
giuliomoro@139 172 // printf("Received clockSync type: %d, clockSyncTimestamp: %lld\n", clockSyncType, clockSyncTimestamp);
giuliomoro@135 173 if(isSlave()==true){
giuliomoro@135 174 int ret=slaveHandleMessage();
giuliomoro@135 175 if(ret==1 && clockSyncType==kDelayResp){ //we are done, end of a cycle!
giuliomoro@135 176 return 1;
giuliomoro@135 177 } else if (ret!=1) {
giuliomoro@135 178 return -1; //
giuliomoro@135 179 } else {
giuliomoro@135 180 continue;
giuliomoro@135 181 }
giuliomoro@135 182 }
giuliomoro@135 183 if(isMaster()==true){ //this is master
giuliomoro@135 184 int ret=masterHandleMessage();
giuliomoro@135 185 if(ret==1 && clockSyncType==kDelayReq){ //we are done, end of a cycle!
giuliomoro@135 186 return 1;
giuliomoro@135 187 } else {
giuliomoro@135 188 return -2; //we are done but something was wrong
giuliomoro@135 189 }
giuliomoro@135 190 }
giuliomoro@135 191 }
giuliomoro@135 192 //printf("Receive loop timeout\n");
giuliomoro@135 193 return -1;
giuliomoro@135 194 }
giuliomoro@135 195
giuliomoro@135 196 int ClockSync::slaveHandleMessage(){
giuliomoro@135 197 switch(clockSyncType){
giuliomoro@135 198 case kSync: //the clockSync timestamp is meaningless, the localTimestamp is when kSync was received
giuliomoro@141 199 resetTs();
giuliomoro@135 200 T1p=localTimestamp;
giuliomoro@135 201 expectedClockSyncType=kFollowUp;
giuliomoro@135 202 break;
giuliomoro@135 203 case kFollowUp: //the clockSyncTimestamp is the time when kSync was sent, the localTimestamp is meaningless
giuliomoro@135 204 T1=clockSyncTimestamp;
giuliomoro@135 205 //send delayReq
giuliomoro@135 206 setType(kDelayReq);
giuliomoro@135 207 setTimestamp(-1);
giuliomoro@135 208 T2=send();
giuliomoro@135 209 if(T2<0){
giuliomoro@135 210 //printf("Error while sending delayReq\n");
giuliomoro@135 211 return -1;
giuliomoro@135 212 }
giuliomoro@135 213 expectedClockSyncType=kDelayResp;
giuliomoro@135 214 break;
giuliomoro@135 215 case kDelayResp: {//the clockSyncTimestamp is the instant when the master received the kDelayResp clockSync, the localTimestamp is meaningless
giuliomoro@135 216 T2p=clockSyncTimestamp;
giuliomoro@135 217 //TODO: evaluate things
giuliomoro@135 218 double offset=(T1p-T1-T2p+T2)/2.0d;
giuliomoro@141 219 if(areTsValid()){
giuliomoro@141 220 processOffset(offset);
giuliomoro@141 221
giuliomoro@141 222 /*
giuliomoro@141 223 static int calls=0;
giuliomoro@141 224 static double referenceOffset=0;
giuliomoro@141 225
giuliomoro@141 226 if(calls<100){ // start by averaging everything
giuliomoro@141 227 movingAverage.add(offset);
giuliomoro@141 228 } else { //once we get an estimate, start discarding outliers
giuliomoro@141 229 float maxOffsetDeviation=20;
giuliomoro@141 230 float deviation=fabsf(movingAverage.getAverage()-offset);
giuliomoro@141 231 if(deviation<maxOffsetDeviation){
giuliomoro@141 232 movingAverage.add(offset);
giuliomoro@141 233 printf("w(end+1)=%4.1f;\n", movingAverage.getAverage());
giuliomoro@141 234 } else {
giuliomoro@141 235 // printf("Out of range: %f \n", deviation);
giuliomoro@141 236 }
giuliomoro@141 237 }
giuliomoro@141 238 printf("offset(end+1)=%f;\n", offset);
giuliomoro@141 239 if (calls==100){
giuliomoro@141 240 // printf("Update reference\n");
giuliomoro@141 241 referenceOffset=movingAverage.getAverage();
giuliomoro@141 242 } else if (calls==200){
giuliomoro@141 243 calls=99;
giuliomoro@141 244 }
giuliomoro@141 245 calls++;
giuliomoro@141 246 */
giuliomoro@141 247
giuliomoro@141 248 // printf("%lld, %lld, %lld, %lld \n", T1, T1p, T2, T2p);
giuliomoro@141 249 // if(T2-T1p<0){
giuliomoro@141 250 // printf("Negative: %lld, %lld, %lld, %lld \n", T1, T1p, T2, T2p);
giuliomoro@141 251 // }
giuliomoro@141 252 } else {
giuliomoro@141 253 printf("InvalidTs:\n %lld, %lld, %lld, %lld \n", T1, T1p, T2, T2p);
giuliomoro@141 254 }
giuliomoro@135 255 expectedClockSyncType=kSync; //end of the cycle, wait for next sync.
giuliomoro@135 256 break;
giuliomoro@135 257 }
giuliomoro@135 258 default:
giuliomoro@135 259 //printf("Unexpected message type\n"); // we should never get here
giuliomoro@135 260 return -1;
giuliomoro@135 261 }
giuliomoro@135 262 return 1;
giuliomoro@135 263 }
giuliomoro@141 264 void ClockSync::processOffset(double offset){
giuliomoro@141 265 static int calls=0;
giuliomoro@141 266 // TODO: change the flow control below so that it can happen multiple times
giuliomoro@141 267 //(base it upon the length of movingAverage rather than the number of calls)
giuliomoro@151 268 if(calls<30){
giuliomoro@151 269 // wait for the VirtualClock to stabilize and produce meaningful outputs
giuliomoro@151 270 } else if(calls<50) { //get an initial guess
giuliomoro@141 271 movingAverage.add(offset);
giuliomoro@141 272 // printf("-----------OFFSET IS : %04.4f samples, average: %04.4f samples\n",
giuliomoro@141 273 // offset, movingAverage.getAverage());
giuliomoro@151 274 } else if (calls==50){ //then compensate for initial offset
giuliomoro@151 275 // printf("compensating for offset: %f\n", movingAverage.getAverage());
giuliomoro@141 276 virtualClock->addOffset(movingAverage.getAverage());
giuliomoro@141 277 movingAverage.reset();
giuliomoro@151 278 } else if (calls>50){ //use IIR filter from now on
giuliomoro@141 279 //filter coefficients obtained from Matlab : [B,A]=butter(2,0.005);
giuliomoro@151 280 static IirFilter iir(1);
giuliomoro@151 281 static bool init = false;
giuliomoro@151 282 if(init == false){
giuliomoro@151 283 // double coeffs[5]={0.00554271721028068, 0.0110854344205614, 0.00554271721028068,
giuliomoro@151 284 // -1.77863177782459, 0.800802646665708};
giuliomoro@151 285 double coeffs[5]={0.0200833655642113, 0.0401667311284225, 0.0200833655642113,
giuliomoro@151 286 -1.56101807580072, 0.641351538057563};
giuliomoro@151 287 iir.setCoefficients(coeffs);
giuliomoro@151 288 init = true;
giuliomoro@151 289 }
giuliomoro@151 290 float expectedOffsetDifferenceMean = 0.2432;
giuliomoro@151 291 float expectedOffsetDifferenceStd = 6.487;
giuliomoro@151 292 float nlOffset;
giuliomoro@151 293 static float pastOffset = 0;
giuliomoro@151 294 float offsetIncrement = offset-pastOffset;
giuliomoro@151 295 if ( fabsf(offsetIncrement) > 1*expectedOffsetDifferenceStd && calls>51){
giuliomoro@151 296 // non-linear filtering: remove outliers
giuliomoro@151 297 nlOffset = pastOffset;
giuliomoro@151 298 // printf("%f %f 0,0,0,0,0,0\n", offset,pastOffset);
giuliomoro@151 299 } else {
giuliomoro@151 300 nlOffset=offset;
giuliomoro@151 301 }
giuliomoro@151 302 pastOffset=nlOffset;
giuliomoro@151 303 float iirOffset = iir.process(nlOffset);
giuliomoro@141 304 static float maxOffset=0;
giuliomoro@151 305 static float pastIirOffset=0;
giuliomoro@151 306 if( calls > 0 ) {
giuliomoro@151 307 maxOffset=fabsf(iirOffset) > fabsf(maxOffset) ? iirOffset : maxOffset;
giuliomoro@151 308 pid.setError(iirOffset);
giuliomoro@151 309 float correction=pid.getOutput()*0.0001;
giuliomoro@149 310 static float oldSamplingRate=44100;
giuliomoro@151 311 printf("%10.3f, %10.3f, %10.3f, %10.3f, %10.3f, %10.3f\n",
giuliomoro@151 312 offset, nlOffset, iirOffset, iirOffset - pastIirOffset, correction, oldSamplingRate); //unfiltered, filtered
giuliomoro@151 313 pastIirOffset = iirOffset;
giuliomoro@151 314 float targetSamplingRate;
giuliomoro@151 315 static int direction=1;
giuliomoro@151 316 float thresholdL = -1;
giuliomoro@151 317 // targetSamplingRate = oldSamplingRate - correction;
giuliomoro@151 318 float thresholdH = 3;
giuliomoro@151 319
giuliomoro@151 320 targetSamplingRate = 44100;
giuliomoro@151 321 if(
giuliomoro@151 322 (iirOffset > thresholdH && direction == 1) ||
giuliomoro@151 323 (iirOffset > thresholdL && direction == -1)
giuliomoro@151 324 ){
giuliomoro@151 325 targetSamplingRate = 44097;
giuliomoro@151 326 direction = -1;
giuliomoro@151 327 }
giuliomoro@151 328 else if (
giuliomoro@151 329 (iirOffset < -thresholdH && direction == -1) ||
giuliomoro@151 330 (iirOffset < -thresholdL && direction == 1)
giuliomoro@151 331 )
giuliomoro@151 332 {
giuliomoro@151 333 iirOffset = 44102;
giuliomoro@151 334 direction = 1;
giuliomoro@151 335 }
giuliomoro@151 336 #ifndef USE_JUCE
giuliomoro@151 337 if(oldSamplingRate != targetSamplingRate){
giuliomoro@151 338 gAudioCodec->setAudioSamplingRate(targetSamplingRate);
giuliomoro@151 339 }
giuliomoro@151 340 #endif
giuliomoro@149 341 oldSamplingRate=targetSamplingRate;
giuliomoro@141 342 }
giuliomoro@141 343 }
giuliomoro@141 344 calls++;
giuliomoro@141 345 }
giuliomoro@135 346 int ClockSync::masterHandleMessage(){
giuliomoro@135 347 switch(clockSyncType){
giuliomoro@135 348 case kDelayReq:
giuliomoro@135 349 //send kDelayResp
giuliomoro@135 350 setType(kDelayResp);
giuliomoro@135 351 setTimestamp(localTimestamp);
giuliomoro@135 352 send();
giuliomoro@135 353 expectedClockSyncType=kNone;
giuliomoro@141 354 return 1;
giuliomoro@135 355 break;
giuliomoro@141 356 default:
giuliomoro@141 357 return -1;
giuliomoro@135 358 }
giuliomoro@135 359 }
giuliomoro@135 360
giuliomoro@135 361 int ClockSync::sendReceiveLoop(){
giuliomoro@135 362 if(isSlave()==true){
giuliomoro@135 363 //printf("Waiting for a sync clockSync\n");
giuliomoro@135 364 } else { //if this is master
giuliomoro@151 365 usleep(30000); //this times (roughly) how often sync clockSyncs are being sent.
giuliomoro@135 366 int ret=masterSendSync();
giuliomoro@135 367 if(ret<=0)
giuliomoro@135 368 return -1;
giuliomoro@135 369 }
giuliomoro@135 370 int ret=receiveLoop();
giuliomoro@135 371 if(ret<=0)
giuliomoro@135 372 return -2;
giuliomoro@135 373 return 1;
giuliomoro@135 374 }
giuliomoro@135 375
giuliomoro@135 376