annotate core/ClockSync.cpp @ 152:8f98b32d0e23 ClockSync

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