Real time Moving Average.

Hi,

I’m trying to get a real time moving average working for a 9 different Arrays. I’m having trouble with ‘pushing’ the new value into the array while popping the last value of the array out. My first though was to do it like this:

for (int i=0; i < 9 ; i++)
    { 
      switch (i)
      {
      case 0:
         digitalWrite(SENSOR0, LOW);
         break;
      case 1:
         digitalWrite(SENSOR1, LOW);
         break;
      case 2:
         digitalWrite(SENSOR2, LOW);
         break;
      case 3:
         digitalWrite(SENSOR3, LOW);
         break;
      case 4:
         digitalWrite(SENSOR4, LOW);
         break;
      case 5:
         digitalWrite(SENSOR5, LOW);
         break;
      case 6:
         digitalWrite(SENSOR6, LOW);
         break;
      case 7:
         digitalWrite(SENSOR7, LOW);
         break;
      case 8:
         digitalWrite(SENSOR8, LOW);
         break; 
      }
      
      pressure_msb = read_register(PRESSURE);
      pressure_msb &= B00000111;
      pressure_lsb = read_register16(PRESSURE_LSB);
      pressure_raw = UBLB19(pressure_msb, pressure_lsb);
      pressure_float = float(pressure_raw) / 4.0;
      //printDouble(pressure_float);   
  if(i == 0){
      pArray0[3] = pArray0[2];
      pArray0[2] = pArray0[1];
      pArray0[1] = pArray0[0];
      pArray0[0] = pressure_float;
  }
  if(i == 1){
      pArray1[3] = pArray1[2];
      pArray1[2] = pArray1[1];
      pArray1[1] = pArray1[0];
      pArray1[0] = pressure_float;
  }
  if(i == 2){
      pArray2[3] = pArray2[2];
      pArray2[2] = pArray2[1];
      pArray2[1] = pArray2[0];
      pArray2[0] = pressure_float; 
  }
  if(i == 3){
      pArray3[3] = pArray3[2];
      pArray3[2] = pArray3[1];
      pArray3[1] = pArray3[0];
      pArray3[0] = pressure_float;
  }
  if(i == 4){
      pArray4[3] = pArray4[2];
      pArray4[2] = pArray4[1];
      pArray4[1] = pArray4[0];
      pArray4[0] = pressure_float;
  }
  if(i == 5){
      pArray5[3] = pArray5[2];
      pArray5[2] = pArray5[1];
      pArray5[1] = pArray5[0];
      pArray5[0] = pressure_float;
  }
  if(i == 6){
      pArray6[3] = pArray6[2];
      pArray6[2] = pArray6[1];
      pArray6[1] = pArray6[0];
      pArray6[0] = pressure_float;
  }
  if(i == 7){
      pArray7[3] = pArray7[2];
      pArray7[2] = pArray7[1];
      pArray7[1] = pArray7[0];
      pArray7[0] = pressure_float;
  }
  if(i == 8){
      pArray8[3] = pArray8[2];
      pArray8[2] = pArray8[1];
      pArray8[1] = pArray8[0];
      pArray8[0] = pressure_float;
  }

But, It seems to be getting values confused … ie; pArray0 will somehow pickup values from pArray1 and pArray1 somehow gets values from pArray2.

Here is where the Arrays are Populated:

 for(int j=0; j < 4; j++)
  {
    for (int i=0; i < 9 ; i++)
    {
      switch (i)
      {
      case 0:
           digitalWrite(SENSOR0, LOW);
           break;
      case 1:
         digitalWrite(SENSOR1, LOW);
         break;
      case 2:
         digitalWrite(SENSOR2, LOW);
         break;
      case 3:
         digitalWrite(SENSOR3, LOW);
         break;
      case 4:
         digitalWrite(SENSOR4, LOW);
         break;
      case 5:
         digitalWrite(SENSOR5, LOW);
         break;
      case 6:
         digitalWrite(SENSOR6, LOW);
         break;
      case 7:
         digitalWrite(SENSOR7, LOW);
         break;
      case 8:
         digitalWrite(SENSOR8, LOW);
         break; 
      }
      
      pressure_msb = read_register(PRESSURE);
      pressure_msb &= B00000111;
      pressure_lsb = read_register16(PRESSURE_LSB);
      pressure_raw = UBLB19(pressure_msb, pressure_lsb);
      pressure_float = float(pressure_raw) / 4.0;
      printDouble(pressure_float); 
      ClearCSBs();
      
      Serial.print(",");
      delay(50);
     if(j == 0)
      {
        if(i == 0){
          pArray0[0] = pressure_float;
        }
        if(i == 1){
          pArray1[0] = pressure_float;
        }
        if(i == 2){
          pArray2[0] = pressure_float;
        }
        if(i == 3){
          pArray3[0] = pressure_float;
        }
        if(i == 4){
          pArray4[0] = pressure_float;
        }
        if(i == 5){
          pArray5[0] = pressure_float;
        }
        if(i == 6){
          pArray6[0] = pressure_float;
        }
        if(i == 7){
          pArray7[0] = pressure_float;
        }
        if(i == 8){
          pArray8[0] = pressure_float;
        }
      }
   if(j == 1)
      {
        if(i == 0){
          pArray0[1] = pressure_float;
        }
        if(i == 1){
          pArray1[1] = pressure_float;
        }
        if(i == 2){
          pArray2[1] = pressure_float;
        }
        if(i == 3){
          pArray3[1] = pressure_float;
        }
        if(i == 4){
          pArray4[1] = pressure_float;
        }
        if(i == 5){
          pArray5[1] = pressure_float;
        }
        if(i == 6){
          pArray6[1] = pressure_float;
        }
        if(i == 7){
          pArray7[1] = pressure_float;
        }
        if(i == 8){
          pArray8[1] = pressure_float;
        }
      } 
    if(j == 2)
      {
        if(i == 0){
          pArray0[2] = pressure_float;
        }
        if(i == 1){
          pArray1[2] = pressure_float;
        }
        if(i == 2){
          pArray2[2] = pressure_float;
        }
        if(i == 3){
          pArray3[2] = pressure_float;
        }
        if(i == 4){
          pArray4[2] = pressure_float;
        }
        if(i == 5){
          pArray5[2] = pressure_float;
        }
        if(i == 6){
          pArray6[2] = pressure_float;
        }
        if(i == 7){
          pArray7[2] = pressure_float;
        }
        if(i == 8){
          pArray8[2] = pressure_float;
        }
      }
   [glow] if(j == 3)
      {
        if(i == 0){
          pArray0[3] = pressure_float;
        }
        if(i == 1){
          pArray1[3] = pressure_float;
        }
        if(i == 2){
          pArray2[3] = 222222222;//pressure_float;
        }
        if(i == 3){
          pArray3[3] = pressure_float;
        }
        if(i == 4){
          pArray4[3] = pressure_float;
        }
        if(i == 5){
          pArray5[3] = pressure_float;
        }
        if(i == 6){
          pArray6[3] = pressure_float;
        }
        if(i == 7){
          pArray7[3] = pressure_float;
        }
        if(i == 8){
          pArray8[3] = pressure_float;
        }
      } [/glow]
        
    }

From further testing it seems that this holds true:

pArray(x)[0] = pArray(x-1)[3]

Where x is my Arrays 1-9. This is from tracing each of the arrays after they have been filled.

Heres my printout while trying to debug:

pArray0 - [0] = 101015,[1] = 101015,[2] = 101017,[3] = 101014
pArray1 - [0] = 101014,[1] = 101275,[2] = 101279,[3] = 101279
pArray2 - [0] = 101279,[1] = 101033,[2] = 101030,[3] = 222222
pArray3 - [0] = 222222,[1] = 100948,[2] = 100947,[3] = 100946
pArray4 - [0] = 100946,[1] = 101071,[2] = 101071,[3] = 101071
pArray5 - [0] = 101071,[1] = 101266,[2] = 101263,[3] = 101263
pArray6 - [0] = 101263,[1] = 101252,[2] = 101250,[3] = 101249
pArray7 - [0] = 101249,[1] = 101104,[2] = 101103,[3] = 101105

I don’t see how the last section ( j = 3 ) could be changing two values with only one line of code.
Any help would be great :slight_smile: Maybe there is a much simpler way to do this.

Furthermore…

 if(i == 1){
          Serial.print("\n");
          Serial.print(pArray2[0]);
          Serial.print("\n");
          pArray1[3] = 222222//pressure_float;
          Serial.print(pArray2[0]);
          Serial.print("\n");
        }

Changed the code to include the section above as a test, and indeed… it changes pArray2[0] to 222222… HOW! :o

Youch! My head is exploding just looking at your code.

Consider using a C++ class to implement a "filtering" object. I think that will clean up the code quite a bit. There's a demonstration of this class in use to filter accelerometer readings at the bottom of our Gadget Shield page.

You would instantiate one filter object for each sensor:

Filter sensor0;
Filter sensor1;
// .etc.  ...or use arrays of Filter objects if you're comfortable

Every time you get a reading you call the input() method (which you should modify from uint8_t to float):

sensor0.input(pressure_float);

You can then read the filtered result using the result() method (again, change the return type to float):

Serial.println(sensor0.result());

You can change the amount of filtering by changing the m_alpha member of the object (in the range 0 to 1):

sensor0.m_alpha = 0.9; // The default
sensor0.m_alpha = 0.95; // Stronger filtering

The m_threshold member is there to set a lower "noise floor". Anything below this value is returned as 0. You can set it to a really big negative number if you want to ignore this functionality.

sensor0.m_threshold = 0; // Anything below 0 returns as 0

-- Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Hey RC,

Tell me about it, my head wants to explode as well :)

I think this is the demonstration you mentioned:

http://ruggedcircuits.com/AS010/accelerometer.pde

I'm honestly not exactly sure what the purpose of filtering is though :-? I'm very new to this type of programing, most of my previous work has been projects not involving computer chips / sensors etc.

That isn't all of the code, I can post it if you would like. The pressure sensors read very well, and display correctly. I'm just having trouble handling / manipulating

I'm honestly not exactly sure what the purpose of filtering is though

Your moving-average code is exactly that: a type of filter (and, mathematically, not a very good one). The code I pointed you to has the same effect, it smoothes out variations in the signal in the same way that a moving average does but with less effort. With a moving-average filter you control the amount of smoothing by averaging over more points or fewer points. With the Filter class you simply change the value of m_alpha.

-- Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Your moving-average code is exactly that: a type of filter (and, mathematically, not a very good one). The code I pointed you to has the same effect, it smoothes out variations in the signal in the same way that a moving average does but with less effort. With a moving-average filter you control the amount of smoothing by averaging over more points or fewer points. With the Filter class you simply change the value of m_alpha.

Ah, thats great! :D

I've been working on implementing the class / functionality this morning. I'm running into a few problems though.

You said to " instantiate one filter object for each sensor" using

Filter sensor0;

but I'm getting the follow error when attempting to add that section of code in

error: expected constructor, destructor, or type conversion before numeric constan

Thanks again!

I'm thinking the problem might be somewhere else. Perhaps try posting the whole code (or just enough code to make the error happen).

-- Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Sure

Heres what I was trying when I got the error ( This is after removing my sad attempt at moving average :stuck_out_tongue: )

// define spi bus pins
//#define SLAVESELECT 10
#define SPICLOCK 13
#define DATAOUT 11      //MOSI
#define DATAIN 12       //MISO

#define SENSOR0 2
#define SENSOR1 3
#define SENSOR2 4
#define SENSOR3 5
#define SENSOR4 6
#define SENSOR5 7
#define SENSOR6 8
#define SENSOR7 9
#define SENSOR8 10
[glow]Filter SENSOR0;[/glow]


#define UBLB(a,b)  ( ( (a) << 8) | (b) )
#define UBLB19(a,b) ( ( (a) << 16 ) | (b) )

//Addresses
#define REVID 0x00      //ASIC Revision Number
#define OPSTATUS 0x04   //Operation Status
#define STATUS 0x07     //ASIC Status
#define START 0x0A      //Constant Readings
#define PRESSURE 0x1F   //Pressure 3 MSB
#define PRESSURE_LSB 0x20 //Pressure 16 LSB
//#define TEMP 0x21       //16 bit temp

char rev_in_byte;          
int temp_in;
unsigned long pressure_lsb;
unsigned long pressure_msb;
unsigned long temp_pressure;
unsigned long pressure_raw;
float pressure_float;

void setup()
{
  byte clr;
  
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK,OUTPUT);
  pinMode(SENSOR0,OUTPUT);
  pinMode(SENSOR1,OUTPUT);
  pinMode(SENSOR2,OUTPUT);
  pinMode(SENSOR3,OUTPUT);
  pinMode(SENSOR4,OUTPUT);
  pinMode(SENSOR5,OUTPUT);
  pinMode(SENSOR6,OUTPUT);
  pinMode(SENSOR7,OUTPUT);
  pinMode(SENSOR8,OUTPUT);
  ClearCSBs();
  
  SPCR = B01010011; //MPIE=0, SPE=1 (on), DORD=0 (MSB first), MSTR=1 (master), CPOL=0 (clock idle when low), CPHA=0 (samples MOSI on rising edge), SPR1=0 & SPR0=0 (500kHz)
  clr=SPSR;
  clr=SPDR;
  delay(10);
  Serial.begin(9600);
  delay(500);

  digitalWrite(SENSOR0, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
    
  digitalWrite(SENSOR1, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR2, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR3, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);

  digitalWrite(SENSOR4, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR5, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR6, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR7, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
  digitalWrite(SENSOR8, LOW);
  write_register(0x03,0x0A);
  ClearCSBs();
  delay(100);
  
}



void loop()
{
  for (int i=0; i < 9 ; i++)
  {
    switch (i)
    {
    case 0:
       digitalWrite(SENSOR0, LOW);
       break;
    case 1:
       digitalWrite(SENSOR1, LOW);
       break;
    case 2:
       digitalWrite(SENSOR2, LOW);
       break;
    case 3:
       digitalWrite(SENSOR3, LOW);
       break;
    case 4:
       digitalWrite(SENSOR4, LOW);
       break;
    case 5:
       digitalWrite(SENSOR5, LOW);
       break;
    case 6:
       digitalWrite(SENSOR6, LOW);
       break;
    case 7:
       digitalWrite(SENSOR7, LOW);
       break;
    case 8:
       digitalWrite(SENSOR8, LOW);
       break;
    }
    
    pressure_msb = read_register(PRESSURE);
    pressure_msb &= B00000111;
    pressure_lsb = read_register16(PRESSURE_LSB);
    pressure_raw = UBLB19(pressure_msb, pressure_lsb);
    pressure_float = float(pressure_raw) / 4.0;
    printDouble(pressure_float);    
    ClearCSBs();
    
    Serial.print(",");
    delay(50);
  } 
  //prints out an 'enter' 
  Serial.print("\n");
  delay(500);
  //rev_in_byte = read_register(REVID);
}



char spi_transfer(volatile char data)
{
  SPDR = data;                    // Start the transmission
  while (!(SPSR & (1<<SPIF)))     // Wait for the end of the transmission
  {
  };
  return SPDR;                    // return the received byte
}

char read_register(char register_name)
{
    char in_byte;
    register_name <<= 2;
    register_name &= B11111100; //Read command

    //digitalWrite(SLAVESELECT,LOW); //Select SPI Device
    spi_transfer(register_name); //Write byte to device
    in_byte = spi_transfer(0x00); //Send nothing, but we should get back the register value
    //digitalWrite(SLAVESELECT,HIGH);
    delay(10);
    return(in_byte);
}



unsigned long read_register16(char register_name)
{
    unsigned char in_byte1;
    unsigned char in_byte2;
    unsigned long in_word;

    register_name <<= 2;
    register_name &= B11111100; //Read command

    //digitalWrite(SLAVESELECT,LOW); //Select SPI Device
    spi_transfer(register_name); //Write byte to device
    in_byte1 = spi_transfer(0x00);    
    in_byte2 = spi_transfer(0x00);
    //digitalWrite(SLAVESELECT,HIGH);
    in_word = UBLB(in_byte1,in_byte2);
    return(in_word);
}

void write_register(char register_name, char register_value)
{
    register_name <<= 2;
    register_name |= B00000010; //Write command

    //digitalWrite(SLAVESELECT,LOW); //Select SPI device
    spi_transfer(register_name); //Send register location
    spi_transfer(register_value); //Send value to record into register
    //digitalWrite(SLAVESELECT,HIGH);
} 

void printDouble( float val)
{
//Currently prints to two decimal places.  Precision parameter is ignored.

    Serial.print (long(val),DEC);  //prints the int part
    Serial.print("."); // print the decimal point
    unsigned long frac;
    float frac_float;
    if(val >= 0)
    {
      frac_float = val - long(val);
      frac_float *= 100;
      frac = long(frac_float);
    }
    else
    {
      frac_float = long(val) - val;
      frac_float *= 100;
      frac = long(frac_float);
    }
    Serial.print(frac,DEC) ;
}


void ClearCSBs()
{
  digitalWrite(SENSOR0, HIGH);
  digitalWrite(SENSOR1, HIGH);
  digitalWrite(SENSOR2, HIGH);
  digitalWrite(SENSOR3, HIGH);
  digitalWrite(SENSOR4, HIGH);
  digitalWrite(SENSOR5, HIGH);
  digitalWrite(SENSOR6, HIGH);
  digitalWrite(SENSOR7, HIGH);
  digitalWrite(SENSOR8, HIGH);
}

class Filter
{
  public:
    Filter(double alpha=0.90, double lowThreshold=16, double diffMargin=2, double scale=1.00) {
      m_alpha = alpha;
      m_state = 0.0;
      m_lastState = 0.0;
      m_newval = 0;
      m_diffmargin = diffMargin;
      m_threshold = lowThreshold;
      m_scale = scale;
    }

    void input(uint8_t sample) {
      m_state = m_alpha*m_state + (1-m_alpha)*sample;
      if (fabs(m_state - m_lastState) >= m_diffmargin) {
        m_newval = 1;
      }
    }

    void reset(void)
    {
      m_state = 0;
      m_newval = 0;
    }

    uint8_t result(void) {
      if (m_state < m_threshold) return 0;
      m_lastState = m_state;
      return (uint8_t) (m_scale*m_state + 0.5);
    }

    uint8_t debugresult(void) {
      if (m_state < m_threshold) return 0;
      //m_lastState = m_state;
      return (uint8_t) (m_scale*m_state + 0.5);
    }

    uint8_t isnew(void) {
      if (m_newval) {
        m_newval = 0;
        return 1;
      } else {
        return 0;
      }
    }

    double m_alpha, m_state, m_lastState, m_diffmargin, m_threshold, m_scale;
    uint8_t m_newval;
};

Which may have been causing problems because of the conflict in names. But even after changing it to sensor0 ( no caps ) I would get “error: ‘Filter’ does not name a type”

I just tried this instead of the highlighted line above, it uploads and I had missed it the first few times looking through the sample code.

static Filter Sensor0,Sensor1,Sensor2,Sensor3,Sensor4,Sensor5,Sensor6,Sensor7,Sensor8;

Look good? :slight_smile:

Also, looking at the

 uint8_t result(void) {
      if (m_state < m_threshold) return 0;
      m_lastState = m_state;
      return (uint8_t) (m_scale*m_state + 0.5);
    }

I’m not sure how to switch to return float as you mentioned in your original post

EDIT: Ohhhh is uint8_t a type? LOL so I just change them to float?

So far so good!

Don’t forget to change the types in the Filter class from uint8_t to float for your purposes.


Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

float result(void) {
      if (m_state < m_threshold) return 0;
      m_lastState = m_state;
      return (float) (m_scale*m_state);
    }


Check out our new shield: http://www.ruggedcircuits.com/html/gadget_shield.html

Hahahah, I was thinking that uint8_t was some sort of function name! Not a type ::slight_smile:

Thanks for your help RC!

There was a really good discussion about this sort of filtering on the PICList recently (not at all PIC-specific!) http://old.nabble.com/looking-for-a-formula-definition---naming-td29399056.html#a29399056