SOFT261_CW

Dependencies:   X_NUCLEO_IKS01A1 mbed

main.cpp

Committer:
jbindin
Date:
2018-08-15
Revision:
0:d9a14a63c3ed

File content as of revision 0:d9a14a63c3ed:

/*
This programme reads an accelerometer every 1/10 of a second.
The data is then stored in a ring buffer, for each sample the previous sample
is subtraced away from the current sample and a moving average of the past 10
samples are taken.

this moving average is then passed to a struct which is then passed to a message
queue. thread1 passes a signal to thread2 so it can process the values from the 
struct. 

thread2 works out the mean of the past 10 values and outputs it serially to a 
console. This works out as one output per second (althouch not exact).

the project was made with MBEDs online compiler
*/

// imports
#include "mbed.h"
#include "rtos.h"
#include "x_nucleo_iks01a1.h"

//to pass signal from threads (easier to read)
#define READ 1

//to show maximum ammount of samples stored
#define COUNTMAX 10

//declarations
Serial pc(USBTX, USBRX);
Thread* thread1;
Thread* thread2;

//struct to store x, y and z values from the accelerometer.
typedef struct {
    float    xVal;
    float    yVal;
    float    zVal;
} accMessage_t;

//declarations of additional functions used.
float movingAverage(float newSample, float previousSample, float rBuffer[]);
float average(float values[10]);

//used to collect samples from the expantion boards sensors
static X_NUCLEO_IKS01A1 *expBoard = X_NUCLEO_IKS01A1::Instance(D14, D15);
static MotionSensor *accelerometer = expBoard->GetAccelerometer();

//array to store x,y,z values in thread1
int accVals[3];

//used to store samples
float valuesX[COUNTMAX];
float valuesY[COUNTMAX];
float valuesZ[COUNTMAX];

//flag to discard the first output. the array is not populated yet so this output
//is not as accurate
bool arrayPopulated = false;

//queue and memory pool to create structs and pass messages efficiently
Queue<accMessage_t, 20> que;
MemoryPool<accMessage_t, 20> memPool;

/*this function is used by thread1, it samples values, passes them to the
movingAverage function, uses the memory pool to store them as structs 
the structs are passed to a queue and sets a message to thread2 to allow it to
be active*/
void producer()
{
    //collects data as previous values to make average more accurate
    accelerometer->get_x_axes(accVals);
    float previousX = accVals[0];
    float previousY = accVals[1];
    float previousZ = accVals[2];
    
    //thread loop involves a 10ms delay
    while(1) {
        Thread::wait(100);
        //sample data from accelerometer
        accelerometer->get_x_axes(accVals);

        float newAverageX = movingAverage((float)accVals[0], (float)previousX, valuesX);
        float newAverageY = movingAverage((float)accVals[1], (float)previousY, valuesY);
        float newAverageZ = movingAverage((float)accVals[2], (float)previousZ, valuesZ);
        previousX = accVals[0];
        previousY = accVals[1];
        previousZ = accVals[2];
        
            //adds values to struct
            accMessage_t *message = memPool.alloc();
            message->xVal = newAverageX;
            message->yVal = newAverageY;
            message->zVal = newAverageZ;
            
            //passes structs to queue and sets thread2s signal
            que.put(message);
            thread2->signal_set(READ);
    }
}

/*
function is used by thread2. it gets structs to the queue. Every 10 stucts
recieved calls the average function which returns the mean of the past 10 values.
this is done for the x,y and z axes.
the average is outputted although the first output is discarded to ensure the
first set of data is populated.
*/
void consumer()
{
    int count = 0;
    //stores structs data in arrays so the values can be averaged later
    float valuesReadX[10];
    float valuesReadY[10];
    float valuesReadZ[10];
    
    while(1) 
    {
        //waits for thread1
        Thread::signal_wait(READ);
        
        osEvent evt = que.get();
        if(evt.status == osEventMessage) 
        {
            accMessage_t *message = (accMessage_t*)evt.value.p;
            valuesReadX[count] = message->xVal;
            valuesReadY[count] = message->yVal;
            valuesReadZ[count] = message->zVal;
            
            memPool.free(message);
            
            if(count >= 9)
            {
                if(arrayPopulated == true){
                    
                //output averages seperated by commas to ensure data can be
                //easily read and processed
                pc.printf("%.2f,\t %.2f,\t %.2f\n\r", average(valuesReadX), average(valuesReadY), average(valuesReadZ));
                }
                count = 0;
                arrayPopulated = true;
            }
        }
        count++;
    }
}

//main function
main()
{
    //attaching thereads to their functions
    thread1 = new Thread(&producer);
    thread2 = new Thread(&consumer);

    while(1) {
        wait(50000);
    }
}
/*
calculats the moving average for the past 10 values. it also is responsible 
for keeping the array ring buffered and subtracting the previous sample from 
the current. 
*/
float movingAverage(float newSample, float previousSample, float rBuffer[])
{
    float ave = 0.0;
    
    // to calculate the change in acceleration
    newSample = newSample - previousSample;

    //move up every value in the array
    for(int i = (COUNTMAX-1); i > 0; i--) {
        rBuffer[i] = rBuffer[i-1];
    }
    
    //add new value
    rBuffer[0] = newSample;

    //summing every value in the array
    for(int i = 0; i < COUNTMAX; i++) {
        ave = ave + rBuffer[i];
    }
    
    //divide the sum by 10 to work out the mean
    ave = ave / (float)COUNTMAX;
    
    //return the mean value
    return ave;
}

/*
used to work out the final average that is to be ouputted every second
*/
float average(float values[10])
{
    float sum = 0;
    float result = 0;
    for(int i = 0; i < 10; i++)
    {
        sum = values[i] + sum;
    }
    result = sum / 10;
    
    //returns the final mean value
    return result;
    
}


// please note an attempt to use a Ticker as an interupt was used to make the 
// samples more accurate however the command "accelerometer->get_x_axes(accVals);"
// only returned 0 so two threads were used insted.