changeset 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 fb9c28a4676b
children 0ee6eebb567a
files core/Scope.cpp include/Scope.h
diffstat 2 files changed, 368 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/Scope.cpp	Tue May 17 16:17:48 2016 +0100
@@ -0,0 +1,282 @@
+/***** Scope.cpp *****/
+#include <Scope.h>
+
+Scope::Scope():connected(0), triggerPrimed(false), started(false){}
+
+// static aux task functions
+void Scope::triggerTask(void *ptr){
+    Scope *instance = (Scope*)ptr;
+    if (instance->started)
+        instance->doTrigger();
+}
+void Scope::sendBufferTask(void *ptr){
+    Scope *instance = (Scope*)ptr;
+    instance->sendBuffer();
+}
+
+void Scope::setup(unsigned int _numChannels, float _sampleRate){
+    
+    numChannels = _numChannels;
+    sampleRate = _sampleRate;
+    
+    // setup the OSC server && client
+    // used for sending / recieving settings
+    // oscClient has it's send task turned off, as we only need to send one handshake message
+    oscServer.setup(OSC_RECIEVE_PORT);
+    oscClient.setup(OSC_SEND_PORT, "127.0.0.1", false);
+
+    // setup the udp socket
+    // used for sending raw frame data
+    socket.setServer("127.0.0.1");
+	socket.setPort(SCOPE_UDP_PORT);
+
+	// setup the auxiliary tasks
+	scopeTriggerTask = BeagleRT_createAuxiliaryTask(Scope::triggerTask, BEAGLERT_AUDIO_PRIORITY-2, "scopeTriggerTask", this, true);
+	scopeSendBufferTask = BeagleRT_createAuxiliaryTask(Scope::sendBufferTask, BEAGLERT_AUDIO_PRIORITY-1, "scopeSendBufferTask", this);
+
+    // send an OSC message to address /scope-setup
+    // then wait 1 second for a reply on /scope-setup-reply 
+    bool handshakeRecieved = false;
+    oscClient.sendMessageNow(oscClient.newMessage.to("/scope-setup").add((int)numChannels).add(sampleRate).end());
+    oscServer.recieveMessageNow(1000);
+    while (oscServer.messageWaiting()){
+        if (handshakeRecieved){
+            parseMessage(oscServer.popMessage());
+        } else if (oscServer.popMessage().match("/scope-setup-reply")){
+            handshakeRecieved = true;
+        }
+    }
+    
+    if (handshakeRecieved && connected)
+        start();
+    
+}
+
+void Scope::start(){
+
+    // resize the buffers
+    channelWidth = frameWidth * FRAMES_STORED;
+    buffer.resize(numChannels*channelWidth);
+    outBuffer.resize(numChannels*frameWidth);
+
+    // reset the pointers
+    writePointer = 0;
+    readPointer = 0;
+    triggerPointer = 0;
+
+    // reset the trigger
+    triggerPrimed = true;
+    triggerCollecting = false;
+    triggerWaiting = false;
+    triggerCount = 0;
+    downSampleCount = 1;
+    autoTriggerCount = 0;
+    customTriggered = false;
+
+    started = true;
+}
+
+void Scope::stop(){
+    started = false;
+}
+
+void Scope::log(float chn1, ...){
+
+    // check for any recieved OSC messages
+    while (oscServer.messageWaiting()){
+        parseMessage(oscServer.popMessage());
+    }
+    
+    if (!started) return;
+    
+    if (downSampling > 1){
+        if (downSampleCount < downSampling){
+            downSampleCount++;
+            return;
+        }
+        downSampleCount = 1;
+    }
+    
+    va_list args;
+    va_start (args, chn1);
+    
+    int startingWritePointer = writePointer;
+  
+    // save the logged samples into the buffer
+    buffer[writePointer] = chn1;
+
+    for (int i=1; i<numChannels; i++) {
+        // iterate over the function arguments, store them in the relevant part of the buffer
+        // channels are stored sequentially in the buffer i.e [[channel1], [channel2], etc...]
+        buffer[i*channelWidth + writePointer] = (float)va_arg(args, double);
+    }
+
+    writePointer = (writePointer+1)%channelWidth;
+    
+    // if upSampling > 1, save repeated samples into the buffer
+    for (int j=1; j<upSampling; j++){
+        
+        buffer[writePointer] = buffer[startingWritePointer];
+    
+        for (int i=1; i<numChannels; i++) {
+            buffer[i*channelWidth + writePointer] = buffer[i*channelWidth + startingWritePointer];
+        }
+    
+        writePointer = (writePointer+1)%channelWidth;
+        
+    }
+    
+    va_end (args);
+    
+}
+
+bool Scope::trigger(){
+    if (triggerMode == 2 && !customTriggered && triggerPrimed && started){
+        customTriggerPointer = (writePointer-xOffset+channelWidth)%channelWidth;
+        customTriggered = true;
+        return true;
+    }
+    return false;
+}
+
+void Scope::scheduleSendBufferTask(){
+    BeagleRT_scheduleAuxiliaryTask(scopeSendBufferTask);
+}
+
+bool Scope::triggered(){
+    if (triggerMode == 0 || triggerMode == 1){  // normal or auto trigger
+        return (!triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] < triggerLevel // positive trigger direction
+                && buffer[channelWidth*triggerChannel+readPointer] >= triggerLevel) || 
+                (triggerDir && buffer[channelWidth*triggerChannel+((readPointer-1+channelWidth)%channelWidth)] > triggerLevel // negative trigger direciton
+                && buffer[channelWidth*triggerChannel+readPointer] <= triggerLevel);
+    } else if (triggerMode == 2){   // custom trigger
+        return (customTriggered && readPointer == customTriggerPointer);
+    }
+    return false;
+}
+
+void Scope::doTrigger(){
+    // iterate over the samples between the read and write pointers and check for / deal with triggers
+    while (readPointer != writePointer){
+        
+        // if we are currently listening for a trigger
+        if (triggerPrimed){
+            
+            // if we crossed the trigger threshold
+            if (triggered()){
+                
+                // stop listening for a trigger
+                triggerPrimed = false;
+                triggerCollecting = true;
+                
+                // save the readpointer at the trigger point
+                triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
+                
+                triggerCount = frameWidth/2 - xOffset;
+                autoTriggerCount = 0;
+                
+            } else {
+                // auto triggering
+                if (triggerMode == 0 && (autoTriggerCount++ > (frameWidth+holdOff))){
+                    // it's been a whole frameWidth since we've found a trigger, so auto-trigger anyway
+                    triggerPrimed = false;
+                    triggerCollecting = true;
+                    
+                    // save the readpointer at the trigger point
+                    triggerPointer = (readPointer-xOffset+channelWidth)%channelWidth;
+                    
+                    triggerCount = frameWidth/2 - xOffset;
+                    autoTriggerCount = 0;
+                }
+            }
+            
+        } else if (triggerCollecting){
+            
+            // a trigger has been detected, and we are collecting the second half of the triggered frame
+            if (--triggerCount > 0){
+                
+            } else {
+                triggerCollecting = false;
+                triggerWaiting = true;
+                triggerCount = frameWidth/2 + holdOffSamples;
+                
+                // copy the previous to next frameWidth/2 samples into the outBuffer
+                int startptr = (triggerPointer-frameWidth/2 + channelWidth)%channelWidth;
+                int endptr = (triggerPointer+frameWidth/2 + channelWidth)%channelWidth;
+                
+                if (endptr > startptr){
+                    for (int i=0; i<numChannels; i++){
+                        std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*i+endptr], outBuffer.begin()+(i*frameWidth));
+                    }
+                } else {
+                    for (int i=0; i<numChannels; i++){
+                        std::copy(&buffer[channelWidth*i+startptr], &buffer[channelWidth*(i+1)], outBuffer.begin()+(i*frameWidth));
+                        std::copy(&buffer[channelWidth*i], &buffer[channelWidth*i+endptr], outBuffer.begin()+((i+1)*frameWidth-endptr));
+                    }
+                }
+                
+                // the whole frame has been saved in outBuffer, so send it
+                scheduleSendBufferTask();
+                
+            }
+            
+        } else if (triggerWaiting){
+            
+            // a trigger has completed, so wait half a framewidth before looking for another
+            if (--triggerCount > 0){
+                
+            } else {
+                triggerWaiting = false;
+                triggerPrimed = true;
+                customTriggered = false;
+            }
+            
+        }
+        
+        // increment the read pointer
+        readPointer = (readPointer+1)%channelWidth;
+    }
+
+}
+
+void Scope::sendBuffer(){
+    socket.send(&(outBuffer[0]), outBuffer.size()*sizeof(float));
+}
+
+void Scope::parseMessage(oscpkt::Message msg){
+    if (msg.partialMatch("/scope-settings/")){
+        int intArg;
+        float floatArg;
+        if (msg.match("/scope-settings/connected").popInt32(intArg).isOkNoMoreArgs()){
+            if (connected == 0 && intArg == 1){
+                start();
+            } else if (connected == 1 && intArg == 0){
+                stop();
+            }
+            connected = intArg;
+        } else if (msg.match("/scope-settings/frameWidth").popInt32(intArg).isOkNoMoreArgs()){
+            stop();
+            frameWidth = intArg;
+            start();
+        } else if (msg.match("/scope-settings/triggerMode").popInt32(intArg).isOkNoMoreArgs()){
+            triggerMode = intArg;
+        } else if (msg.match("/scope-settings/triggerChannel").popInt32(intArg).isOkNoMoreArgs()){
+            triggerChannel = intArg;
+        } else if (msg.match("/scope-settings/triggerDir").popInt32(intArg).isOkNoMoreArgs()){
+            triggerDir = intArg;
+        } else if (msg.match("/scope-settings/triggerLevel").popFloat(floatArg).isOkNoMoreArgs()){
+            triggerLevel = floatArg;
+        } else if (msg.match("/scope-settings/xOffset").popInt32(intArg).isOkNoMoreArgs()){
+            xOffset = intArg;
+        } else if (msg.match("/scope-settings/upSampling").popInt32(intArg).isOkNoMoreArgs()){
+            upSampling = intArg;
+            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
+        } else if (msg.match("/scope-settings/downSampling").popInt32(intArg).isOkNoMoreArgs()){
+            downSampling = intArg;
+            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
+        } else if (msg.match("/scope-settings/holdOff").popFloat(floatArg).isOkNoMoreArgs()){
+            holdOff = floatArg;
+            holdOffSamples = (int)(sampleRate*0.001*holdOff*upSampling/downSampling);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/Scope.h	Tue May 17 16:17:48 2016 +0100
@@ -0,0 +1,86 @@
+/***** Scope.h *****/
+#ifndef __Scope_H_INCLUDED__
+#define __Scope_H_INCLUDED__ 
+
+#include <OSCServer.h>
+#include <OSCClient.h>
+#include <stdarg.h>
+
+#define OSC_RECIEVE_PORT 8675
+#define OSC_SEND_PORT 8676
+#define SCOPE_UDP_PORT 8677
+
+#define FRAMES_STORED 2
+
+class Scope{
+    public:
+        Scope();
+        
+        void setup(unsigned int numChannels, float sampleRate);
+        void log(float chn1, ...);
+        bool trigger();
+        
+    private:
+        OSCServer oscServer;
+        OSCClient oscClient;
+        UdpClient socket;
+        
+        void parseMessage(oscpkt::Message);
+        void start();
+        void stop();
+        void doTrigger();
+        void triggerNormal();
+        void triggerAuto();
+        void scheduleSendBufferTask();
+        void sendBuffer();
+        void customTrigger();
+        bool triggered();
+        
+        // settings
+        int numChannels;
+        float sampleRate;
+        int connected;
+        int frameWidth;
+        int triggerMode;
+        int triggerChannel;
+        int triggerDir;
+        float triggerLevel;
+        int xOffset;
+        int upSampling;
+        int downSampling;
+        float holdOff;
+        
+        int channelWidth;
+        int downSampleCount;
+        int holdOffSamples;
+        
+        // buffers
+        std::vector<float> buffer;
+        std::vector<float> outBuffer;
+        
+        // pointers
+        int writePointer;
+        int readPointer;
+        int triggerPointer;
+        int customTriggerPointer;
+        
+        // trigger status
+        bool triggerPrimed;
+        bool triggerCollecting;
+        bool triggerWaiting;
+        bool triggering;
+        int triggerCount;
+        int autoTriggerCount;
+        bool started;
+        bool customTriggered;
+        
+        // aux tasks
+        AuxiliaryTask scopeTriggerTask;
+        static void triggerTask(void*);
+        
+        AuxiliaryTask scopeSendBufferTask;
+        static void sendBufferTask(void*);
+        
+};
+
+#endif
\ No newline at end of file