annotate core/ClockSync.cpp @ 141:44d07fa9bd03 ClockSync

Ultra-basic feedback for clock sync works^CIssues: response time of the IIR filter is too slow, requires PID and better filtering algorithm.
author Giulio Moro <giuliomoro@yahoo.it>
date Mon, 14 Sep 2015 15:42:11 +0100
parents 4e2dd3eb1d28
children 55c1e591cb2e
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@135 73 ret=client.waitUntilReady(false, 0);
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 #include <I2c_Codec.h>
giuliomoro@141 265 extern I2c_Codec* gAudioCodec;
giuliomoro@141 266 void ClockSync::processOffset(double offset){
giuliomoro@141 267 static int calls=0;
giuliomoro@141 268 // TODO: change the flow control below so that it can happen multiple times
giuliomoro@141 269 //(base it upon the length of movingAverage rather than the number of calls)
giuliomoro@141 270 if(calls<10) { //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@141 274 } else if (calls==10){ //then compensate for initial offset
giuliomoro@141 275 // printf("compensating for offset: %f\n", offset);
giuliomoro@141 276 virtualClock->addOffset(movingAverage.getAverage());
giuliomoro@141 277 movingAverage.reset();
giuliomoro@141 278 } else if (calls>=10){ //use IIR filter from now on
giuliomoro@141 279 //filter coefficients obtained from Matlab : [B,A]=butter(2,0.005);
giuliomoro@141 280 // static float B[3]={6.10061787580662e-05, 0.000122012357516132, 6.10061787580662e-05};
giuliomoro@141 281 // static float A[3]={1, -1.97778648377676, 0.978030508491796};
giuliomoro@141 282 static float B[3]={6.10061787580662e-05, 0.000122012357516132, 6.10061787580662e-05};
giuliomoro@141 283 static float A[3]={1, -1.97778648377676, 0.978030508491796};
giuliomoro@141 284 static float pastOut[3]={0,0,0};
giuliomoro@141 285 static float pastIn[3]={0,0,0};
giuliomoro@141 286 float in=offset;
giuliomoro@141 287 float out= -pastOut[1]*A[1] -pastOut[2]*A[2] +in*B[0] +pastIn[1]*B[1] +pastIn[2]*B[2];
giuliomoro@141 288 pastOut[2]=pastOut[1];
giuliomoro@141 289 pastOut[1]=out;
giuliomoro@141 290 pastIn[2]=pastIn[1];
giuliomoro@141 291 pastIn[1]=in;
giuliomoro@141 292 offset=out;
giuliomoro@141 293 static float maxOffset=0;
giuliomoro@141 294 maxOffset=fabsf(offset) > fabsf(maxOffset) ? offset : maxOffset;
giuliomoro@141 295 printf("%10.3f, %10.3f, %10.3f, %10.3f\n", in, offset, offset-pastOut[2], maxOffset); //unfiltered, filtered
giuliomoro@141 296 if(fabsf(offset)>10 && calls>30){
giuliomoro@141 297 calls=11;
giuliomoro@141 298 //TODO: correct for offset
giuliomoro@141 299 float targetSamplingRate=offset>0 ? 44097 : 44103;
giuliomoro@141 300 gAudioCodec->setAudioSamplingRate(targetSamplingRate);
giuliomoro@141 301 // pastOut[1]=pastOut[2]=pastIn[1]=pastIn[2]=offset;
giuliomoro@141 302 printf("------setAudioSmplingRate to %f\n", targetSamplingRate);
giuliomoro@141 303 }
giuliomoro@141 304 }
giuliomoro@141 305 calls++;
giuliomoro@141 306 }
giuliomoro@135 307 int ClockSync::masterHandleMessage(){
giuliomoro@135 308 switch(clockSyncType){
giuliomoro@135 309 case kDelayReq:
giuliomoro@135 310 //send kDelayResp
giuliomoro@135 311 setType(kDelayResp);
giuliomoro@135 312 setTimestamp(localTimestamp);
giuliomoro@135 313 send();
giuliomoro@135 314 expectedClockSyncType=kNone;
giuliomoro@141 315 return 1;
giuliomoro@135 316 break;
giuliomoro@141 317 default:
giuliomoro@141 318 return -1;
giuliomoro@135 319 }
giuliomoro@135 320 }
giuliomoro@135 321
giuliomoro@135 322 int ClockSync::sendReceiveLoop(){
giuliomoro@135 323 if(isSlave()==true){
giuliomoro@135 324 //printf("Waiting for a sync clockSync\n");
giuliomoro@135 325 } else { //if this is master
giuliomoro@135 326 usleep(100000); //this times (roughly) how often sync clockSyncs are being sent.
giuliomoro@135 327 int ret=masterSendSync();
giuliomoro@135 328 if(ret<=0)
giuliomoro@135 329 return -1;
giuliomoro@135 330 }
giuliomoro@135 331 int ret=receiveLoop();
giuliomoro@135 332 if(ret<=0)
giuliomoro@135 333 return -2;
giuliomoro@135 334 return 1;
giuliomoro@135 335 }
giuliomoro@135 336
giuliomoro@135 337