Calculate time delay with Cross Correlation

Dependencies:   mbed DHT

medeltest.cpp

Committer:
reritter
Date:
2017-05-11
Revision:
8:21e74746a067
Parent:
7:99d5e92152e0

File content as of revision 8:21e74746a067:

#include "mbed.h"
#include <iostream>     /*  cout */
//#include <stdio.h>      /* printf */
#include <math.h>       /* sin */
#include <vector>
#include <stdlib.h>     /* abs */
#include <stdio.h>
#include <AnalogIn.h>
#include <stdint.h>
#include <DHT.h>
#include<sstream>

//using namespace std;

/* DEBUG FUNCTION
// ersätter Debug(xyz) med xyz , där xyz är din kod
//För att aktivera:
#define Debug(xyz) xyz

//För att "stänga av":
#define Debug(xyz)

//I din kod, skriv din debug kod liknande så här:
Debug( std::cout << "My text: " << myVariable << std::endl; );

*/

#define Debug(x) x
#define DebugPrintState(y) y
#define DebugArcSin(z) z
#define DebugPrintDelay(q) q


//----------VARIABLES HERE
int dataLength = 1000;
int captureLength = 50;
double temp = 22;
double hum = 10;
double micDist = 0.15; //meters
double threshold_1 = 0; //value when going to active mode channel 1  //old hardcoded value = 330
double threshold_2 = 0; //value when going to active mode channel 2 //old hardcoded value = 200
double threshold_adjust = 100;  //used to adjust threshold, + for less sensitivity, - for increased sensitivity
bool calibratedStatus = false;      //flag to make sure Nuclueo only calibrated once for background noise
bool checkTemp = false;         //flag - true to checktemp, false to use predefined values
int positionOfMaxVal_1;
int positionOfMaxVal_2;
const double PI = 3.14159265358979323846;
double timedelay = 0;

// State machine
int    STATE;
//const int NONE     = -1;
const int IDLE  = 0;
const int CALIBRATE = 1;
const int TESTNEW = 2;
const int CALC  = 3;
const int CALC_ERROR = 4;
const int SEND   = 5;
//const int WAIT     = 9;

//dataLength behövs kanske inte, vector klassen kan växa med behov
std::vector<double> channel_1(dataLength);
std::vector<double> channel_2(dataLength);
std::vector<int> timestamps_1(dataLength);
std::vector<int> timestamps_2(dataLength);
std::vector<double> capture_1(captureLength);
std::vector<double> capture_2(captureLength);
std::vector<double> capturestamps_1(captureLength);
std::vector<double> capturestamps_2(captureLength);

int positiontest = 0;
int test = 9;
std::vector<double> delaytest(test);


AnalogIn mic1(A0);
AnalogIn mic2(A1);
AnalogIn mic3(A2);
DHT sensor(A3, DHT11);

//TIMER
Timer t;

//led can be used for status
DigitalOut led1(LED1);


//----------FUNCTIONS HERE
//Calculating distance between sound and camera
double calcDist(double t, double v)
{
    double s = t*v;
    return s;
}

//Calculating angle in radians, D distance between mic1 and mic2
double calcAng(double s, double D)
{
    return asin(s/D) + PI/2;
}

//Assuming the input value is temp as a number in degrees celcius and humidity as procent
double calcSoundSpeed(double temp, double hum)
{
    //Calculations were done in Matlab
    double speed = 331.1190 + 0.6016*temp + 0.0126*hum;
    return speed;
}

//translate angle to number for camera
string convertAngToCamNbr(double ang)
{
    ang = ang*(180 / PI) + 45;  //radianer till grader
    double angValues = 270;
    int stepValues = 50000;
    string tiltNumber = " 18000";   //hårdkodat Camera Pan värde

    double oneAng = stepValues/angValues;
    double cameraAngNumber = ang*oneAng;
    int panInt = (int)(cameraAngNumber);  //double to int
    //int to string
    string panNumber;
    ostringstream convert;
    convert << panInt;
    panNumber = convert.str();

    string send = panNumber + tiltNumber;
    return send;
}


//calc time delay by finding peak values in 2 vectors
//channel = 1 or 2
int FindPeak(int channel)
{
    std::vector<double> channel_curr(captureLength); //temporary vector with channel voltage values

    //if channel 1 then set current channel to channel 1
    if (channel == 1) {
        channel_curr = capture_1;
    } else channel_curr = capture_2;

    //reset max value & sum value
    double valueMax = 0;

    //reset array position
    int positionOfMaxVal = 0;

    //find largest value & mark that position in vectors
    for (int position = 0; position < channel_curr.size(); position++) {
        double val = abs(channel_curr[position]);
        if (val > valueMax ) {
            valueMax = val;
            positionOfMaxVal = position;
        }
    }
    return positionOfMaxVal;
}

/*Crosscorrelation code
/*TDEVector* TDE::CrossCorrelation()
{
    TDEVector* res = new TDEVector(
        2 * m_maxDelay + 1, {0, CalcZero});

    for (DelayType = -m_maxDelay; delay <= m_maxDelay; delay++)
    {
        CalcType sum = 0;
        for (size_t pos = 0; pos < m_datalength; pos++)
        {
            sum += m_channel0[pos] 
                 * m_channel1[pos + delay + m_maxDelay];
        }
        res->at(delay + m_maxDelay).delay = delay;
        res->at(delay + m_maxDelay).value = sum;
    }
    return res;
}*/




double FindTimeDelay(int positionOfMaxVal_1, int positionOfMaxVal_2)
{
    double timemax_1 = capturestamps_1[positionOfMaxVal_1];
    double timemax_2 = capturestamps_2[positionOfMaxVal_2];
    double delay = timemax_1 - timemax_2;
    return delay; //if negative near microphone 1, if positive near micropnone 2
}


//get voltage value which represents audio amplitude from microphone
double getAudioValue(AnalogIn micX)
{
    return 1000*micX.read();
}


bool overThreshold(double micValue_1, double micValue_2)
{
    if ((micValue_1 > threshold_1) || (micValue_2 > threshold_2)) {
        return true;
    } else return false;
}

//true if voltage value in microphone is above the current threshold value
bool calibrateThreshold(double micValue, double currentThreshold)
{
    if ( micValue > currentThreshold ) {
        return true;
    } else return false;
}


// main() runs in its own thread in the OS
int main()
{
    for(int i = 0; i < test; i++) {
        delaytest[i] = -420 + i*105;
    }
    t.start(); // start timer

    //while (true) {
    led1 = !led1;
    wait(0.5);


    //STATE MACHINE
    STATE = IDLE;
    //int counter = 0;
    while (true) {
        switch (STATE) {
            case IDLE: //always start here
                DebugPrintState( std::cout << "Nucleo state is IDLE: " << std::endl; );
                Debug( wait(0.5); );
                if (!calibratedStatus)  STATE = CALIBRATE;
                else STATE = TESTNEW;
                break;

            case CALIBRATE:
                DebugPrintState( std::cout << "Nucleo state is CALIBRATE: " << std::endl; );
                Debug( wait(1); );
                //listen for X seconds to background noise, to set accurate threshold value
                // This should be done only once when rebooting Nucleo
                int startTime = t.read_us();
                int offsetTime = 3000; //microseconds
                int blinkTime = 500; //microseconds
                while (t.read_us() < (startTime + offsetTime) ) {
                    double micValue_1 = getAudioValue(mic1);
                    if ( calibrateThreshold(micValue_1, threshold_1) ) {
                        threshold_1 = micValue_1;       //threshold value updated
                    }
                    double micValue_2 = getAudioValue(mic2);
                    if ( calibrateThreshold(micValue_2, threshold_2) ) {
                        threshold_2 = micValue_2;       //threshold value updated
                    }
                    //make LED blink every 500 ms
                    if ( t.read_us() > (startTime + blinkTime) ) {
                        led1 = !led1;
                        blinkTime = blinkTime + 500;
                    }
                }
                threshold_1 = threshold_1 + threshold_adjust;
                threshold_2 = threshold_2 + threshold_adjust;

                //Calibrate temp and hum
                if(checkTemp){
                    bool done = false;
                    while(!done) {
                        if(sensor.readData() == 0) {
                            temp = sensor.ReadTemperature(CELCIUS);
                            hum = sensor.ReadHumidity();
                            DebugPrintState(std::cout << "Temp: " << temp << "Degrees Celcius" <<std::endl; );
                            DebugPrintState(std::cout << "Hum: " << temp << "%" <<std::endl; );
                            done = true;
                        }
                    }
                }

                calibratedStatus = true;
                STATE = TESTNEW; //next state
                break;

            case TESTNEW:
                DebugPrintState( std::cout << "Nucleo state is TESTNEW: " << std::endl; );
                
                double avgTimedelay = 0;
                int counter = 0;
                while(counter<500){
                    int i = 0;
                    bool quit = false;
                    while(!quit) {
                        channel_1[i] = getAudioValue(mic1);
                        timestamps_1[i] = t.read_us();
                        channel_2[i] = getAudioValue(mic2);
                        timestamps_2[i] = t.read_us();
                        if(overThreshold(channel_1[i], channel_2[i]) == true) {
                            capture_1[0] = channel_1[i];
                            capturestamps_1[0] = timestamps_1[i];
                            capture_2[0] = channel_2[i];
                            capturestamps_2[0] = timestamps_2[i];
                            for(int j = 1; j < captureLength; j++) {
                                capture_1[j] = getAudioValue(mic1);
                                capturestamps_1[j] = t.read_us();
                                capture_2[j] = getAudioValue(mic2);
                                capturestamps_2[j] = t.read_us();
                            }
                            quit = true;
                        }
                        if(i < dataLength) {
                            i++;
                        } else {
                            i = 0;
                        }
                    }
                
                
                    int positionOfMaxVal_1 = FindPeak(1);
                    int positionOfMaxVal_2 = FindPeak(2);
                    avgTimedelay =  avgTimedelay + FindTimeDelay(positionOfMaxVal_1, positionOfMaxVal_2); //microseceonds
                    counter++;
                }
                timedelay = avgTimedelay/500;
                DebugPrintDelay(std::cout<<"avgTimedelay is: "<<timedelay<<std::endl;);
                STATE = CALC;
                break;

           
            case CALC:
                DebugPrintState( std::cout << "Nucleo state is CALC: " << std::endl; );
                //Debug( wait(0.5); );
                
                //int positionOfMaxVal_1 = FindPeak(1);
                //int positionOfMaxVal_2 = FindPeak(2);
                //run functions
                //double timedelay = FindTimeDelay(positionOfMaxVal_1, positionOfMaxVal_2); //microseceonds
                if(abs(timedelay/1000000) > micDist/calcSoundSpeed(temp, hum)){         //now timedelay in sec
                    STATE = CALC_ERROR;
                    break;
                }
                double speed = calcSoundSpeed(temp, hum); //meters per second
                double distance = calcDist(timedelay/1000000, speed); //input converted to meters
                double angle = calcAng((double)distance, micDist); //0,15m = 15cm = 150mm, double type cast because of asin function in angle calculation
                //go to state SEND if no calc_error

                Debug(
                    //std::cout << "max position for channel 1: " << positionOfMaxVal_1+1 << std::endl;
                    //std::cout << "max position for channel 2: " << positionOfMaxVal_2+1 << std::endl;
                    std::cout << "run FindPeak, delay is: " << timedelay << "microseconds" << std::endl;
                    std::cout << "run calcDist, delta s is: " << distance << "  millimeters" << std::endl;
                    std::cout << "run calcAngle, angle is: " << angle << " radians" << std::endl;
                    std::cout << "run calcAngle, angle is: " << angle*(180 / PI) << " degrees" << std::endl;
                    std::cout << "run convertAngToCamNbr, coordinates: "<< convertAngToCamNbr(angle)<<std::endl; //return "panNumber tiltNumber";
                );
                if (angle > (3 * PI )/2 || angle < 0 ) {       //vinkel larger than 270 eller minde än noll
                    STATE = CALC_ERROR;
                } else {
                    STATE = SEND;
                }
                break;

            case CALC_ERROR:
                DebugPrintState( std::cout << "Nucleo state is CALC_ERROR: " << std::endl; );
                Debug( wait(0.5); );
                //error message
                std::cout << "Error. angle not within limits 0 -270 degrees" << std::endl;
                //nollställ vektorer, , stoppa klockan , osv
                STATE = TESTNEW;
                break;

            case SEND:
                DebugPrintState( std::cout << "Nucleo state is SEND: " << std::endl; );
                Debug( wait(0.5); );
                // send coordinates to serial port to camera
                std::cout<<convertAngToCamNbr(angle)<<std::endl; //return "panNumber tiltNumber";
                Debug( wait(0.5); );
                STATE = IDLE;
                wait(5);
                break;
        }
    }
}