Lagom medelvärdesbildning

I mitt arbete springer man på löpande* medelvärden titt som tätt, och de är oftast ganska enkla historier men som kan kräva mycket minne ifall en stor data mängd skall användas (dvs. medelvärdet skall bildas över en längre tid). Detta kan ställa till problem i inbyggda system där det är snålt med minne. Jag har därför designat ett litet nätt objekt för medelvärden som man kan byta uppdateringshastighet mot minneskrav utan att tappa sampel-frekvens. Systemet är jag dock inte först med utan det har till exempel använts av en kamrat som var på Alfvén-laboratoriet samtidigt som mig och det var i ärlighetens namn hon som fick mig i dessa tankegångar.

Systemet har en buffer med delmedelvärden och en buffer med sampels. Mätvärden läggs in i sampel-bufferten och när den är full medelvärdesbildas dessa och läggs in i ett element i delmedelvärdes-buffern. när man sedan vill ha medelvärdet för samtliga sampel tas ett medelvärde av de befintliga delmedelvärdena.

Minneskravet för medelvärde på de senaste 300 mätvärden går från 300 ord** till 30 + 10 ord ifall vi kan nöja oss med uppdateringar var 10:e sample.

#include <stdio.h>
#include <stdlib.h>

#define MEAN_FULL 0x08

typedef struct meanStruct {
  signed short *s;  // Sample-array
  signed short *m;  // array of sub-mean-values
  unsigned char sLen; // Number of samples before a new sub-mean is generated
  unsigned char mLen; // total number of sub-means
  unsigned char sPtr; // pointer to current sample
  unsigned char mPtr; // pointer to current mean
  unsigned char status;
} mean_t;

void meanInit(mean_t *mean, signed short *meanArr, unsigned char maLen, signed short *sampleArr, unsigned char saLen)
{
  mean->mLen = maLen;
  mean->sLen = saLen;

  mean->mPtr = 0;
  mean->sPtr = 0;

  mean->m = meanArr;
  mean->s = sampleArr;

  mean->status = 0x00;
}

void meanAddSample(mean_t *mean, signed short sample)
{
  unsigned char index;
  signed long subMean = 0;

  mean->s[mean->sPtr] = sample;

  mean->sPtr++;

  if (mean->sPtr >= mean->sLen)
  {
    mean->sPtr = 0; // reset sample pointer

    // Calculate sub-mean
    for (index = 0; index < mean->sLen; index++)
    {
      subMean += (signed long)mean->s[index];
    }
    subMean /= (signed long) mean->sLen;

    printf("mean->m[%d] = %d\n", mean->mPtr, subMean);
    mean->m[mean->mPtr] = subMean; //update subMean-array

    mean->mPtr++;
    if (mean->mPtr >= mean->mLen)
    {
      mean->status |= MEAN_FULL;
      mean->mPtr = 0;
    }
  }
}

signed short meanGet(mean_t mean)
{
  unsigned char index, stop;
  signed short sum = 0;

  if (mean.status & MEAN_FULL)
    stop = mean.mLen;
  else
    stop = mean.mPtr;
  // get mean-value from sub-means

  for (index = 0; index < stop; index++)
    sum += mean.m[index];
  if(stop > 0)
    return sum / stop;
  else
    return 0x8000; // UNDEFINED

}

int main(int argc, char *argv[])
{
  mean_t test;
  signed short means[3], samples[10], index;
  signed long compare = 0;
  meanInit(&test, means, 3, samples, 10);

  for(index = 0; index < 30; index++)
  {
    compare += index * 2;
    addSample(&test, (index * 2));
  }
  compare /= 300;
  printf("Orig: %d Test: %d\n", compare, getMean(test));

  system("PAUSE");
  return 0;
}

syntax highlighted by Code2HTML, v. 0.9.1

main()-funktionen ovan initialiserar ett medelvärdesobjekt och fyller det med data och testar det genom att jämföra medelvärdet med ett ”normalt” beräknat medelvärde.

Man borde kunna generalisera metoden ytterligare genom att lägga in ett läge som fyller ut sample buffern med Älsta-medlet / sample-bufferstorleken och räknar ut ett medel på detta som sedan ersätter det älsta medlet. Detta höjer uppdateringshastigheten om man gör lite avkall på noggranheten eftersom det älsta medlet kommer fungera som ett IIR-baserat medel istället för ett FIR-baserat (som de övriga medelvärdena använder).

Eftersom jag inte är hundra på allt detta är kommentarer, tillrättningar, förslag och buggrapporter hemskt välkomna! Förövrigt är det nog dags att jag hittar nått ställe att lägga dessa kodsnuttar, de börjar bli ett antal nu.

* löpande innebär att de x senaste mätningarna skall användas för att bilda medelvärdet

** ord kan vara 8, 16,32 eller 64 bitar stort beroende på val. I den här implementeringen är orden 16 byte stora

Etiketter: ,

Kommentera

Fyll i dina uppgifter nedan eller klicka på en ikon för att logga in:

WordPress.com Logo

Du kommenterar med ditt WordPress.com-konto. Logga ut / Ändra )

Twitter-bild

Du kommenterar med ditt Twitter-konto. Logga ut / Ändra )

Facebook-foto

Du kommenterar med ditt Facebook-konto. Logga ut / Ändra )

Google+ photo

Du kommenterar med ditt Google+-konto. Logga ut / Ändra )

Ansluter till %s


%d bloggare gillar detta: