/* Compile with:  gcc -Wall -o gps -lm gps.c nmea.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <inttypes.h>
#include <math.h>

#include "nmea.h"

#define BUFSIZE 4000
#define BAUDRATE B115200
#define UART "/dev/ttyACM0"

/* Speichern der GPS-Daten */
struct location
  {
  double latitude;    /* Breitengrad */
  double longitude;   /* Laengengrad */
  double speed;       /* Geschwindigkeit */
  double altitude;    /* Hoehe ueber NN */
  double course;      /* Marschrichtung */
  };
  
  
/* 
 * Oeffen der seriellen Schnittstelle fuer den GPS-USB-Empfaenger
 */
int UART_Init()
  {
  int FD;
  struct termios options = {};

  FD = open(UART, O_RDONLY | O_NOCTTY);
  if (FD < 0)
    {
    printf("Fehler beim Oeffnen von %s\n", UART);
    exit(-1);
    }
  memset(&options, 0, sizeof(options));
  options.c_cflag &= ~PARENB;         /* kein Paritybit */
  options.c_cflag &= ~CSTOPB;         /* 1 Stoppbit */
  options.c_cflag &= ~CSIZE;          /* 8 Datenbits */
  options.c_cflag = CS8 | CLOCAL | CREAD;
  options.c_iflag = IGNPAR;
  options.c_oflag = 0;
  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  options.c_cc[VTIME] = 10;
  options.c_cc[VMIN] = 1;
  cfsetspeed(&options, BAUDRATE);
  tcflush(FD, TCIFLUSH);
  tcsetattr(FD, TCSAFLUSH, &options);
  return FD;
  }

/*
 * Daten ueber Laenge, Breite, Geschwindigkeit, Richtung, Datum und Uhrzeit
 * vom GPS-Empfaenger einsammeln. Die Uhrzeit (Format: hhmmss.sss) wird als
 * Dezimalzahl (double) gespeichert, das Datum (Format: ddmmyy) als Long Integer.
 */
void gps_location(int FD, struct location *coord, double *time, long int *date)
  {
  char buffer[BUFSIZE];
  int Laenge, Anz, Fin;
  char Zeichen;
  unsigned char status = N_EMPTY;
  struct gpgga gpgga;
  struct gprmc gprmc;

  /* Schleife, bis jeweils ein GPGGA- und ein GPRMC-Record gelesen wurden. */
  while(status != N_COMPLETE)
    {
    Laenge = 0;
    Fin = 0;

    /* eine Zeile einlesen */
    while(! Fin)
      {
      Anz = read(FD, &Zeichen, 1);
      if (Anz > 0)
        {
        if(Zeichen == '\n')
          {
          buffer[Laenge] = '\0';
          Fin = 1;
          }
        else if(Zeichen != '\r')
          {
          buffer[Laenge] = Zeichen;
          Laenge++;
          }
        }
      }
    /* Abhaengig vom Record-Typ die Daten einsammeln */
    switch (nmea_get_message_type(buffer))
      {
      case N_GPGGA:
        nmea_parse_gpgga(buffer, &gpgga);
        gps_deg(&(gpgga.latitude), gpgga.lat, &(gpgga.longitude), gpgga.lon);
        coord->latitude = gpgga.latitude;
        coord->longitude = gpgga.longitude;
        coord->altitude = gpgga.altitude;
        if (status == N_GPRMC)
          status = N_COMPLETE;
        else
          status |= N_GPGGA;
        break;
      case N_GPRMC:
        nmea_parse_gprmc(buffer, &gprmc);
        coord->speed = gprmc.speed;
        coord->course = gprmc.course;
        *date = gprmc.date;
        *time = gprmc.time;
        if (status == N_GPGGA)
          status = N_COMPLETE;
        else
          status |= N_GPRMC;
        break;
      }
    }
  }


/*
 * Datumsangabe (gespeichert als long int, Format: ddmmyy) in String umwandeln
 */ 
void gps_date2str(long int date, char *datestr)
  {
  char buf[10];
  /* date: ttmmyy */

  buf[0] = '\0';
  sprintf(datestr,"%02ld", ((date/10000) % 100)); /* Tag */
  strcat(datestr, ".");
  sprintf(buf,"%02ld", ((date/100) % 100));       /* Monat */
  strcat(datestr, buf);
  strcat(datestr, ".");
  sprintf(buf,"%02ld", (date % 100));             /* Jahr */
  strcat(datestr, "20");
  strcat(datestr, buf);
  }


/*
 * Zeitangabe (gespeichert als double, Format: hhmmss.sss) in String umwandeln
 * (ohne Nachkommastellen der Sekunden)
 */ 
void gps_time2str(double time, char *timestr)
  {
  char buf[10];
  long int ttt;
 
  ttt = (long) time;
  buf[0] = '\0';
  sprintf(timestr,"%02ld", (1 + (ttt/10000) % 100)); /* Stunde (UTC+1 -> MESZ) */
  strcat(timestr, ":");
  sprintf(buf,"%02ld", ((ttt/100) % 100));       /* Minute */
  strcat(timestr, buf);
  strcat(timestr, ":");
  sprintf(buf,"%02ld", (ttt % 100));             /* Sekunde */
  strcat(timestr, buf);
  }


int main(int argc, char** argv)
  {
  int FD;                       /* Filehandle fuer serielle Schnittstelle */
  struct location coordinates;  /* Datensammler füer GPS-Standort */
  long int date;                /* Datum */ 
  double time;                  /* Uhrzeit */
  char buffer[100];             /* Puffer */

  /* Schnittstelle oeffnen */
  FD = UART_Init();

  /* Standort ermitteln */ 
  gps_location(FD, &coordinates, &time, &date);
  
  /* Kontroll-Ausgabe */
  printf("Breitengrad:      %lf\n", coordinates.latitude);
  printf("Laengengrad:      %lf\n", coordinates.longitude);
  printf("Hoehe ueber NN:   %lf\n", coordinates.altitude);
  printf("Geschwindigkeit:  %lf Richtung: %lf\n", 
                 coordinates.speed, coordinates.course);
  printf("Uhrzeit (double): %lf Datum (long): %ld\n", time, date);

  /* Uhrzeit/Datum in String umwandeln */
  gps_date2str(date, buffer);
  printf("Datum (String):   %s\n", buffer);

  gps_time2str(time, buffer);
  printf("Uhrzeit (String): %s\n", buffer);

  close (FD);
  return 0;
  }
