Sunday, March 3, 2013

Comparing stored data

So last time when we left off we had brought serial data into supercollider from an arduino and then stored it in an ever evolving array.  Also, we noted that when that array had a zero in its last index, it was in alignment with the analog sensor placement--meaning index 0 represented the value found at analog sensor 0.

Here is the SC code for that:


(

SerialPort.listDevices;
~myPort = SerialPort("/dev/tty.usbserial-A600dSr9", baudrate: 9600, crtscts: true);
~myPort.close
SerialPort.closeAll;

~serialReceive.play;
~serialReceive.stop;

~pieceBegins = thisThread.seconds; //assign the start of the piece


~serialReceive = Task({  
       var serialArraySize = 7; 
       var serialArray = Array.newClear(serialArraySize); 
                inf.do({
                       if(~myPort.notNil, { 
                           ~incData = ~myPort.next; 
                          if(~incData.notNil, { 
                              ~incData.postln;
                              serialArray = serialArray.shift(-1); 
                              serialArray[serialArraySize - 1] = ~incData;  
                              if(serialArray[serialArraySize - 1]==0, {   
                                  serialArray.postln; 
                         });
                   });
           });
      0.1.wait;
     });
});

)


The next step is to store completed serial arrays in order to compare data from  consecutive arrays.  When I began working with these sensors I noticed that they can sometimes be quite sensitive and so I thought I would compare the average of three initial sensor values to the average of the subsequent three values in order to determine whether or not there was a breath.  The biggest problem with this approach was that it introduced a bit of a delay into the system so that breaths were not recognized in real time.  And so while I am still storing values from the six 'most recent' arrays, I am no longer taking the time to find the average values of the sensors for comparison.

Storing arrays in a 2D array:

The first step in storing arrays is to capture the relevant data in new array.  Here I fill a new array called ~newVal with the six values from the serialArray, and then post it:


~newValArray.fill(6, { arg i; serialArray[i]});
 "newVal is ".postln;
 ~newVal.postln;

In order to avoid nil related errors, first I check to make sure that the serialArray is notNil, so the complete code looks like this:

if((serialArray[0]).notNil && (serialArray[1]).notNil && (serialArray[2]).notNil && (serialArray[3]).notNil, { 


~newValArray.fill(6, { arg i; serialArray[i]});
 "newVal is ".postln;
 ~newVal.postln;


});

The next step is to store the next array that comes in.  In order to do this I essentially made a bucket brigade which passes the ~newVal array up the line until it eventually becomes ~oldVal5.  In the meantime, new arrays are filling up the chain.  So it ends up looking like this:


~curTime = thisThread.seconds - ~pieceBegins

if(~curTime > 5.0, { 

     ~oldVal5 = Array.newClear(6);  
     ~oldVal5 = ~oldVal4;

     ~oldVal4 = Array.newClear(6);
     ~oldVal4 = ~oldVal3;

     ~oldVal3 = Array.newClear(6);
     ~oldVal3 = ~oldVal2;

     ~oldVal2 = Array.newClear(6);
     ~oldVal2 = ~oldVal1;

     ~oldVal1 = Array.newClear(6);
     ~oldVal1 = ~oldVal

     if(~newVal.notNil, {
         ~oldVal = Array.newClear(6);
         ~oldVal = ~newVal
       });

     if((serialArray[0]).notNil 
        && (serialArray[1]).notNil 
        && (serialArray[2]).notNil 
        && (serialArray[3]).notNil, { 

           ~newVal= Array.fill(6, { arg i; serialArray[i]});
           "newVal is ".postln;
           ~newVal.postln;
});


Note that it's a bit counterintuitive...everything moves from the bottom newVal up to the top oldVal5.

Finally,  all of these values are stored in a 2D array:


~array = [~oldVal5, ~oldVal4, ~oldVal3, ~oldVal2, ~oldVal1, ~oldVal, ~newVal];

Again, the main purpose of storing so many arrays (actually 7, total) was so that I could compare the average sensor values to avoid reading in a lot of 'noise'.  After I discovered that this was creating an awkward delay, instead of rewriting all of the code that dealt with the averages, I put a temporary bandaid on the situation by swapping out the arrays in my 2D array:


~array = [~oldVal, ~oldVal, ~oldVal, ~newVal, ~newVal, ~newVal, ~newVal];

This way when I go to take the 'averages' I can do it right away because the arrays stored are really only the most recent arrays.  

Here I get the 'averages' (again, will most likely be getting rid of this step):

//gets averages of 3 values for each sensor, and rounds

//get the average of the first sensor value in the OLDEST three arrays
if((~array[0][0]).notNil 
   && (~array[1][0]).notNil 
   && (~array[2][0]).notNil, { //avoid nil-related errors
           ~sensorOLD0
                 [~array[0][0], ~array[1][0], ~array[2][0]].mean; 
                 //round that number to whole number
                  ~sensorOLD0 = ~sensorOLD0.round; 
                 //and post it
                  ("sensorOLD0 is " ++ ~sensorOLD0).postln;
});

//and again...
if((~array[0][1]).notNil && (~array[1][1]).notNil && (~array[2][1]).notNil, {
~sensorOLD1 = [~array[0][1], ~array[1][1], ~array[2][1]].mean; 
~sensorOLD1 = ~sensorOLD1.round; 
("sensorOLD1 is " ++ ~sensorOLD1).postln;
});

if((~array[0][2]).notNil && (~array[1][2]).notNil && (~array[2][2]).notNil, {
~sensorOLD2 = [~array[0][2], ~array[1][2], ~array[2][2]].mean; 
~sensorOLD2 = ~sensorOLD2.round; 
//("sensorOLD2 is " ++ ~sensorOLD2).postln;
});

if((~array[0][3]).notNil && (~array[1][3]).notNil && (~array[2][3]).notNil, {
~sensorOLD3 = [~array[0][3], ~array[1][3], ~array[2][3]].mean;
~sensorOLD3 = ~sensorOLD3.round; 
//("sensorOLD3 is " ++ ~sensorOLD3).postln;
});

if((~array[0][4]).notNil && (~array[1][4]).notNil && (~array[2][4]).notNil, {
~sensorOLD4 = [~array[0][4], ~array[1][4], ~array[2][4]].mean; 
~sensorOLD4 = ~sensorOLD4.round; 
//("sensorOLD4 is " ++ ~sensorOLD4).postln;
});

if((~array[0][5]).notNil && (~array[1][5]).notNil && (~array[2][5]).notNil, {
~sensorOLD5 = [~array[0][5], ~array[1][5], ~array[2][5]].mean; 
~sensorOLD5 = ~sensorOLD5.round; 
//("sensorOLD5 is " ++ ~sensorOLD5).postln;
});



 //get the average of the first sensor value in the NEWEST three arrays
if((~array[3][0]).notNil 
      && (~array[4][0]).notNil 
      && (~array[5][0]).notNil, { 
             ~sensorNEW0 = [~array[3][0], ~array[4][0], ~array[5][0]].mean;
             //round that number to whole number
             ~sensorNEW0 = ~sensorNEW0.round;
             //and post it
             ("sensorNEW0 is " ++ ~sensorNEW0).postln;
});

//...and again
if((~array[3][1]).notNil && (~array[4][1]).notNil && (~array[5][1]).notNil, {
~sensorNEW1 = [~array[3][1], ~array[4][1], ~array[5][1]].mean;
~sensorNEW1 = ~sensorNEW1.round;
//("sensorNEW1 is " ++ ~sensorNEW1).postln;
});

if((~array[3][2]).notNil && (~array[4][2]).notNil && (~array[5][2]).notNil, {
~sensorNEW2 = [~array[3][2], ~array[4][2], ~array[5][2]].mean;
~sensorNEW2 = ~sensorNEW2.round;
//("sensorNEW2 is " ++ ~sensorNEW2).postln;
});

if((~array[3][3]).notNil && (~array[4][3]).notNil && (~array[5][3]).notNil, {
~sensorNEW3 = [~array[3][3], ~array[4][3], ~array[5][3]].mean;
~sensorNEW3 = ~sensorNEW3.round;
//("sensorNEW3 is " ++ ~sensorNEW3).postln;
});

if((~array[3][4]).notNil && (~array[4][4]).notNil && (~array[5][4]).notNil, {
~sensorNEW4 = [~array[3][4], ~array[4][4], ~array[5][4]].mean;
~sensorNEW4 = ~sensorNEW4.round;
//("sensorNEW4 is " ++ ~sensorNEW4).postln;
});

if((~array[3][5]).notNil && (~array[4][5]).notNil && (~array[5][5]).notNil, {
~sensorNEW5 = [~array[3][5], ~array[4][5], ~array[5][5]].mean;
~sensorNEW5 = ~sensorNEW5.round;
//("sensorNEW5 is " ++ ~sensorNEW5).postln;
});



I then take the information about the newest and oldest values and place that in a series of short, two-index arrays:

if((~sensorOLD0.notNil) && (~sensorNEW0.notNil), { //make an array of 2 values, for comparison's sake: the old (averaged & rounded) value and the new (averaged & rounded) value
~compareSensor0 = [~sensorOLD0, ~sensorNEW0];
//("compare sensor 0 = " ++ ~compareSensor0).postln;
});

if((~sensorOLD1.notNil) && (~sensorNEW1.notNil), {//do this for each stretch sensor
~compareSensor1 = [~sensorOLD1, ~sensorNEW1];
//("compare sensor 1 = " ++ ~compareSensor1).postln;
});

if((~sensorOLD2.notNil) && (~sensorNEW2.notNil), {
~compareSensor2 = [~sensorOLD2, ~sensorNEW2];
//("compare sensor 2 = " ++ ~compareSensor2).postln;
});

if((~sensorOLD3.notNil) && (~sensorNEW3.notNil), {
~compareSensor3 = [~sensorOLD3, ~sensorNEW3];
//("compare sensor 3 = " ++ ~compareSensor3).postln;
});

if((~sensorOLD4.notNil) && (~sensorNEW4.notNil), {
~compareSensor4 = [~sensorOLD4, ~sensorNEW4];
//("compare sensor 4 = " ++ ~compareSensor4).postln;
});

if((~sensorOLD5.notNil) && (~sensorNEW5.notNil), {
~compareSensor5 = [~sensorOLD5, ~sensorNEW5];
//("compare sensor 5 = " ++ ~compareSensor5).postln;
});


Finally, for each of these small arrays, I test to see whether the new value is greater than, less than or equal to the old value (indicating, respectively, an inspiration, expiration or no change.

if(~compareSensor0[0] < ~compareSensor0[1], { //if the old value is less than the new value
   ~count0 = 1; //return a 1 to indicate an inspiration
   //subtract the old value from the new value in the array to see depth:
   ~depthAtZeroInspiration = ~compareSensor0[1] - ~compareSensor0[0]; 
   //"the depth of the inspiration at sensor 0 is ".post;
   //~depthAtZeroInspiration.postln;

}, { 

//if the old value is NOT less than the new value, check to see if the old value is > the new value.

if(~compareSensor0[0] > ~compareSensor0[1], {//if the old value is greater than the new value
    ~count0 = -1;//return -1 to show an exhalation
    //depth of exhalation. Note that this will return a NEGATIVE NUMBER:
    ~depthAtZeroExhalation = ~compareSensor0[1] - ~compareSensor0[0]; 
    //"the depth of the exhalation at sensor 0 is ".post;
    //~depthAtZeroExhalation.postln;

}, {

//otherwise, if the sensors show no change, return a zero to indicate no 
      ~count0 = 0;
      ~depthAtZeroInspiration = 0; breath
      ~depthAtZeroExhalation = 0;
      //"depth at zero = 0".postln});

      }); 
});





As previously mentioned: the code immediately above is repeated for each ~compareSensor array, #0-5, and is part of an infinite loop reading in and testing these values.  Whether or not there is a breath is determined in a separate loop that considers the data coming in from all of the sensors, collectively.  I'll save that for my next post...

No comments:

Post a Comment