Progetto Tuzzi

Dependencies:   MTK3339 TextLCD mbed tsi_sensor

Fork of app_gps by EmbeddedArtists AB

main.cpp

Committer:
den90
Date:
2015-07-15
Revision:
12:45cdb2b65f79
Parent:
11:d9fb0fa0cbcd

File content as of revision 12:45cdb2b65f79:

#include "mbed.h"
#include "MTK3339.h"
#include "TextLCD.h"
#include "tsi_sensor.h"

#define ELEC0 9
#define ELEC1 10
#define LAT 0
#define LON 1

static int waitData = 0;

static MTK3339 gps(PTE22, PTE23);
static DigitalOut r_led(LED_RED);
static DigitalOut g_led(LED_GREEN);
static DigitalOut b_led(LED_BLUE);
static TSIAnalogSlider slider(ELEC0, ELEC1, 40);
static TextLCD lcd(PTE5, PTE3, PTE2, PTB11, PTB10, PTB9, TextLCD::LCD16x2);
static Serial pc(USBTX, USBRX);

const double PI = 3.141592654; // Pi greco
const double R  = 6371000.0;   // Raggio della Terra in metri
const int    N  = 100;         // Campioni da acquisire per determinare la posizione
const int SATS  = 5;           // Satelliti da fixare prima di acquisire la posizione

double initPos[2] = {.0, .0};  // Latitudine, Longitudine
double thrs       = 5.0;       // Soglia di allarme di default in metri
bool   set        = false;     // Posizione iniziale individuata

static void dataAvailable()
{
    waitData |= gps.getAvailableDataType();
}

static void setRGBColor(double r, double g, double b)
{
    r_led = 1 - r;
    g_led = 1 - g;
    b_led = 1 - b;
}

// Trasforma un angolo da gradi in radianti
static double toRadians(double deg)
{
    return deg * PI / 180.0;
}

// Calcola la distanza tra 2 punti in metri
static double dist(double lat1, double lon1, double lat2, double lon2)
{
    double dPhi, dLam, a, c;

    dPhi = toRadians(lat2 - lat1);
    dLam = toRadians(lon2 - lon1);
    a    = sin(dPhi/2) * sin(dPhi/2) +
           cos(toRadians(lat1)) * cos(toRadians(lat2)) * sin(dLam/2) * sin(dLam/2);
    c    = 2 * atan2(sqrt(a), sqrt(1-a));
    return R * c;
}

// Calcola la distanza tra punto iniziale e attuale in metri
static double distFromInit(double lat, double lon)
{
    return dist(initPos[LAT], initPos[LON], lat, lon);
}

// Calcola la distanza tra 2 punti in metri (approssimazione equirettangolare)
static double distThomas(double lat1, double lon1, double lat2, double lon2)
{
    double x = toRadians(lon2 - lon1) * cos(toRadians(lat1 + lat2)/2);
    double y = toRadians(lat2 - lat1);
    double d = sqrt(x*x + y*y) * R;
    return isnan(d) ? 0.0 : d;
}

// Calcola la distanza tra punto iniziale e attuale in metri (approssimazione equirettangolare)
static double distThomasFromInit(double lat, double lon)
{
    return distThomas(initPos[LAT], initPos[LON], lat, lon);
}

// Calcola la media di un vettore
static double mean(double* x, int n)
{
    double sum = 0;
    double   c = 0;
    for (int i=0; i<n; i++) {
        if (!isnan(x[i])) {
            sum += x[i];
            c++;
        }
    }
    return sum / c;
}

// Calcola la varianza di un vettore
static double var(double* x, int n)
{
    double x1[n];
    for (int i=0; i<n; i++) {
        x1[i] = x[i]*x[i];
    }
    return mean(x1,n) - (mean(x,n)*mean(x,n));
}

// Calcola la deviazione standard di un vettore
static double stdev(double* x, int n)
{
    return sqrt(var(x,n));
}

// Determina la posizione iniziale
static void setInitPos()
{
    double coords[2][N], lat, lon, meanLat, meanLon, stdevLat, stdevLon;
    int i = 0;

    while (!set && gps.gga.satellites>=SATS) {

        lat = gps.getLatitudeAsDegrees();
        lon = gps.getLongitudeAsDegrees();

        wait(1);

        if (!isnan(lat+lon)) {
            coords[LAT][i%N] = lat;
            coords[LON][i%N] = lon;

            lcd.cls();

            if (i >= 2*N) {
                meanLat  = mean(coords[LAT], N);
                meanLon  = mean(coords[LON], N);
                stdevLat = stdev(coords[LAT], N);
                stdevLon = stdev(coords[LON], N);

                lcd.printf("ACCURACY=%0.2fm\nTIME=%ds GPS=%d", distThomas(0, 0, 3*stdevLat, 3*stdevLon), i-2*N, gps.gga.satellites);
                if (distThomas(0, 0, 3*stdevLat, 3*stdevLon) < 2.0) {
                    initPos[LAT] = meanLat;
                    initPos[LON] = meanLon;
                    set = true;
                }
            } else {
                lcd.printf("Acquiring\nposition (%ds)", 2*N-i);
            }

            i++;
        }

    }
}

int main(void)
{
    double d;
    int c = 0;

    gps.start(&dataAvailable, MTK3339::NmeaGga);

    while (1) {

        while (waitData == 0);

        lcd.cls();

        if ((waitData & MTK3339::NmeaGga) != 0) {
            waitData &= ~(MTK3339::NmeaGga);

            if (gps.gga.satellites < SATS) {
                lcd.printf("Fixing\nsats (%d/%d)", gps.gga.satellites, SATS);
                setRGBColor(1, 1, 0);
                c = 0;
            } else if (!set) {
                // La prima volta prendi la posizione iniziale
                lcd.printf("Acquiring\nposition...");
                setInitPos();
            } else {
                // Prendi la soglia impostata dall'utente
                if (slider.readPercentage() != 0.0) {
                    thrs = floor(slider.readPercentage() * 13.0 + 3.0);
                }

                // Calcola la distanza tra punto iniziale e attuale
                d = distThomasFromInit(gps.getLatitudeAsDegrees(), gps.getLongitudeAsDegrees());

                // Verifica che la soglia sia superata
                if (d > thrs) {
                    if (++c >= 15) {
                        lcd.printf("THR=%0.0fm SATS=%d\nDIST=%2.1fm ALARM", thrs, gps.gga.satellites, d);
                        setRGBColor(1, 0, 0);
                    } else {
                        lcd.printf("THR=%0.0fm SATS=%d\nDIST=%2.1fm    OK", thrs, gps.gga.satellites, thrs);
                        setRGBColor(1, 0, 0);
                    }
                } else {
                    lcd.printf("THR=%0.0fm SATS=%d\nDIST=%2.1fm    OK", thrs, gps.gga.satellites, d);
                    setRGBColor(0, 1, 0);
                    c = 0;
                }
            }

        }
        waitData &= MTK3339::NmeaGga;
    }
}