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