annotate core/Scope.cpp @ 272:733006bdbca2 prerelease

Added Scope.h and Scope.cpp
author Liam Donovan <l.b.donovan@qmul.ac.uk>
date Tue, 17 May 2016 16:17:48 +0100
parents
children e4392164b458
rev   line source
l@272 1 /***** Scope.cpp *****/
l@272 2 #include <Scope.h>
l@272 3
l@272 4 Scope::Scope():connected(0), triggerPrimed(false), started(false){}
l@272 5
l@272 6 // static aux task functions
l@272 7 void Scope::triggerTask(void *ptr){
l@272 8 Scope *instance = (Scope*)ptr;
l@272 9 if (instance->started)
l@272 10 instance->doTrigger();
l@272 11 }
l@272 12 void Scope::sendBufferTask(void *ptr){
l@272 13 Scope *instance = (Scope*)ptr;
l@272 14 instance->sendBuffer();
l@272 15 }
l@272 16
l@272 17 void Scope::setup(unsigned int _numChannels, float _sampleRate){
l@272 18
l@272 19 numChannels = _numChannels;
l@272 20 sampleRate = _sampleRate;
l@272 21
l@272 22 // setup the OSC server && client
l@272 23 // used for sending / recieving settings
l@272 24 // oscClient has it's send task turned off, as we only need to send one handshake message
l@272 25 oscServer.setup(OSC_RECIEVE_PORT);
l@272 26 oscClient.setup(OSC_SEND_PORT, "127.0.0.1", false);
l@272 27
l@272 28 // setup the udp socket
l@272 29 // used for sending raw frame data
l@272 30 socket.setServer("127.0.0.1");
l@272 31 socket.setPort(SCOPE_UDP_PORT);
l@272 32
l@272 33 // setup the auxiliary tasks
l@272 34 scopeTriggerTask = BeagleRT_createAuxiliaryTask(Scope::triggerTask, BEAGLERT_AUDIO_PRIORITY-2, "scopeTriggerTask", this, true);
l@272 35 scopeSendBufferTask = BeagleRT_createAuxiliaryTask(Scope::sendBufferTask, BEAGLERT_AUDIO_PRIORITY-1, "scopeSendBufferTask", this);
l@272 36
l@272 37 // send an OSC message to address /scope-setup
l@272 38 // then wait 1 second for a reply on /scope-setup-reply
l@272 39 bool handshakeRecieved = false;
l@272 40 oscClient.sendMessageNow(oscClient.newMessage.to("/scope-setup").add((int)numChannels).add(sampleRate).end());
l@272 41 oscServer.recieveMessageNow(1000);
l@272 42 while (oscServer.messageWaiting()){
l@272 43 if (handshakeRecieved){
l@272 44 parseMessage(oscServer.popMessage());
l@272 45 } else if (oscServer.popMessage().match("/scope-setup-reply")){
l@272 46 handshakeRecieved = true;
l@272 47 }
l@272 48 }
l@272 49
l@272 50 if (handshakeRecieved && connected)
l@272 51 start();
l@272 52
l@272 53 }
l@272 54
l@272 55 void Scope::start(){
l@272 56
l@272 57 // resize the buffers
l@272 58 channelWidth = frameWidth * FRAMES_STORED;
l@272 59 buffer.resize(numChannels*channelWidth);
l@272 60 outBuffer.resize(numChannels*frameWidth);
l@272 61
l@272 62 // reset the pointers
l@272 63 writePointer = 0;
l@272 64 readPointer = 0;
l@272 65 triggerPointer = 0;
l@272 66
l@272 67 // reset the trigger
l@272 68 triggerPrimed = true;
l@272 69 triggerCollecting = false;
l@272 70 triggerWaiting = false;
l@272 71 triggerCount = 0;
l@272 72 downSampleCount = 1;
l@272 73 autoTriggerCount = 0;
l@272 74 customTriggered = false;
l@272 75
l@272 76 started = true;
l@272 77 }
l@272 78
l@272 79 void Scope::stop(){
l@272 80 started = false;
l@272 81 }
l@272 82
l@272 83 void Scope::log(float chn1, ...){
l@272 84
l@272 85 // check for any recieved OSC messages
l@272 86 while (oscServer.messageWaiting()){
l@272 87 parseMessage(oscServer.popMessage());
l@272 88 }
l@272 89
l@272 90 if (!started) return;
l@272 91
l@272 92 if (downSampling > 1){
l@272 93 if (downSampleCount < downSampling){
l@272 94 downSampleCount++;
l@272 95 return;
l@272 96 }
l@272 97 downSampleCount = 1;
l@272 98 }
l@272 99
l@272 100 va_list args;
l@272 101 va_start (args, chn1);
l@272 102
l@272 103 int startingWritePointer = writePointer;
l@272 104
l@272 105 // save the logged samples into the buffer
l@272 106 buffer[writePointer] = chn1;
l@272 107
l@272 108 for (int i=1; i<numChannels; i++) {
l@272 109 // iterate over the function arguments, store them in the relevant part of the buffer
l@272 110 // channels are stored sequentially in the buffer i.e [[channel1], [channel2], etc...]
l@272 111 buffer[i*channelWidth + writePointer] = (float)va_arg(args, double);
l@272 112 }
l@272 113
l@272 114 writePointer = (writePointer+1)%channelWidth;
l@272 115
l@272 116 // if upSampling > 1, save repeated samples into the buffer
l@272 117 for (int j=1; j<upSampling; j++){
l@272 118
l@272 119 buffer[writePointer] = buffer[startingWritePointer];
l@272 120
l@272 121 for (int i=1; i<numChannels; i++) {
l@272 122 buffer[i*channelWidth + writePointer] = buffer[i*channelWidth + startingWritePointer];
l@272 123 }
l@272 124
l@272 125 writePointer = (writePointer+1)%channelWidth;
l@272 126
l@272 127 }
l@272 128
l@272 129 va_end (args);
l@272 130
l@272 131 }
l@272 132
l@272 133 bool Scope::trigger(){
l@272 134 if (triggerMode == 2 && !customTriggered && triggerPrimed && started){
l@272 135 customTriggerPointer = (writePointer-xOffset+channelWidth)%channelWidth;
l@272 136 customTriggered = true;
l@272 137 return true;
l@272 138 }
l@272 139 return false;
l@272 140 }
l@272 141
l@272 142 void Scope::scheduleSendBufferTask(){
l@272 143 BeagleRT_scheduleAuxiliaryTask(scopeSendBufferTask);
l@272 144 }
l@272 145
l@272 146 bool Scope::triggered(){
l@272 147 if (triggerMode == 0 || triggerMode == 1){ // normal or auto trigger
l@272 148 return (!triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] < triggerLevel // positive trigger direction
l@272 149 && buffer[channelWidth*triggerChannel+readPointer] >= triggerLevel) ||
l@272 150 (triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] > triggerLevel // negative trigger direciton
l@272 151 && buffer[channelWidth*triggerChannel+readPointer] <= triggerLevel);
l@272 152 } else if (triggerMode == 2){ // custom trigger
l@272 153 return (customTriggered && readPointer == customTriggerPointer);
l@272 154 }
l@272 155 return false;
l@272 156 }
l@272 157
l@272 158 void Scope::doTrigger(){
l@272 159 // iterate over the samples between the read and write pointers and check for / deal with triggers
l@272 160 while (readPointer != writePointer){
l@272 161
l@272 162 // if we are currently listening for a trigger
l@272 163 if (triggerPrimed){
l@272 164
l@272 165 // if we crossed the trigger threshold
l@272 166 if (triggered()){
l@272 167
l@272 168 // stop listening for a trigger
l@272 169 triggerPrimed = false;
l@272 170 triggerCollecting = true;
l@272 171
l@272 172 // save the readpointer at the trigger point
l@272 173 triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
l@272 174
l@272 175 triggerCount = frameWidth/2 - xOffset;
l@272 176 autoTriggerCount = 0;
l@272 177
l@272 178 } else {
l@272 179 // auto triggering
l@272 180 if (triggerMode == 0 && (autoTriggerCount++ > (frameWidth+holdOff))){
l@272 181 // it's been a whole frameWidth since we've found a trigger, so auto-trigger anyway
l@272 182 triggerPrimed = false;
l@272 183 triggerCollecting = true;
l@272 184
l@272 185 // save the readpointer at the trigger point
l@272 186 triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
l@272 187
l@272 188 triggerCount = frameWidth/2 - xOffset;
l@272 189 autoTriggerCount = 0;
l@272 190 }
l@272 191 }
l@272 192
l@272 193 } else if (triggerCollecting){
l@272 194
l@272 195 // a trigger has been detected, and we are collecting the second half of the triggered frame
l@272 196 if (--triggerCount > 0){
l@272 197
l@272 198 } else {
l@272 199 triggerCollecting = false;
l@272 200 triggerWaiting = true;
l@272 201 triggerCount = frameWidth/2 + holdOffSamples;
l@272 202
l@272 203 // copy the previous to next frameWidth/2 samples into the outBuffer
l@272 204 int startptr = (triggerPointer-frameWidth/2 + channelWidth)%channelWidth;
l@272 205 int endptr = (triggerPointer+frameWidth/2 + channelWidth)%channelWidth;
l@272 206
l@272 207 if (endptr > startptr){
l@272 208 for (int i=0; i<numChannels; i++){
l@272 209 std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*i+endptr], outBuffer.begin()+(i*frameWidth));
l@272 210 }
l@272 211 } else {
l@272 212 for (int i=0; i<numChannels; i++){
l@272 213 std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*(i+1)], outBuffer.begin()+(i*frameWidth));
l@272 214 std::copy(&buffer[channelWidth*i], &buffer[channelWidth*i+endptr], outBuffer.begin()+((i+1)*frameWidth-endptr));
l@272 215 }
l@272 216 }
l@272 217
l@272 218 // the whole frame has been saved in outBuffer, so send it
l@272 219 scheduleSendBufferTask();
l@272 220
l@272 221 }
l@272 222
l@272 223 } else if (triggerWaiting){
l@272 224
l@272 225 // a trigger has completed, so wait half a framewidth before looking for another
l@272 226 if (--triggerCount > 0){
l@272 227
l@272 228 } else {
l@272 229 triggerWaiting = false;
l@272 230 triggerPrimed = true;
l@272 231 customTriggered = false;
l@272 232 }
l@272 233
l@272 234 }
l@272 235
l@272 236 // increment the read pointer
l@272 237 readPointer = (readPointer+1)%channelWidth;
l@272 238 }
l@272 239
l@272 240 }
l@272 241
l@272 242 void Scope::sendBuffer(){
l@272 243 socket.send(&(outBuffer[0]), outBuffer.size()*sizeof(float));
l@272 244 }
l@272 245
l@272 246 void Scope::parseMessage(oscpkt::Message msg){
l@272 247 if (msg.partialMatch("/scope-settings/")){
l@272 248 int intArg;
l@272 249 float floatArg;
l@272 250 if (msg.match("/scope-settings/connected").popInt32(intArg).isOkNoMoreArgs()){
l@272 251 if (connected == 0 && intArg == 1){
l@272 252 start();
l@272 253 } else if (connected == 1 && intArg == 0){
l@272 254 stop();
l@272 255 }
l@272 256 connected = intArg;
l@272 257 } else if (msg.match("/scope-settings/frameWidth").popInt32(intArg).isOkNoMoreArgs()){
l@272 258 stop();
l@272 259 frameWidth = intArg;
l@272 260 start();
l@272 261 } else if (msg.match("/scope-settings/triggerMode").popInt32(intArg).isOkNoMoreArgs()){
l@272 262 triggerMode = intArg;
l@272 263 } else if (msg.match("/scope-settings/triggerChannel").popInt32(intArg).isOkNoMoreArgs()){
l@272 264 triggerChannel = intArg;
l@272 265 } else if (msg.match("/scope-settings/triggerDir").popInt32(intArg).isOkNoMoreArgs()){
l@272 266 triggerDir = intArg;
l@272 267 } else if (msg.match("/scope-settings/triggerLevel").popFloat(floatArg).isOkNoMoreArgs()){
l@272 268 triggerLevel = floatArg;
l@272 269 } else if (msg.match("/scope-settings/xOffset").popInt32(intArg).isOkNoMoreArgs()){
l@272 270 xOffset = intArg;
l@272 271 } else if (msg.match("/scope-settings/upSampling").popInt32(intArg).isOkNoMoreArgs()){
l@272 272 upSampling = intArg;
l@272 273 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
l@272 274 } else if (msg.match("/scope-settings/downSampling").popInt32(intArg).isOkNoMoreArgs()){
l@272 275 downSampling = intArg;
l@272 276 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
l@272 277 } else if (msg.match("/scope-settings/holdOff").popFloat(floatArg).isOkNoMoreArgs()){
l@272 278 holdOff = floatArg;
l@272 279 holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
l@272 280 }
l@272 281 }
l@272 282 }