title logo
title right side
26.09.2025 16:36:48 Lokal:
Serverstatus: (4.36/4.05/4.0153.69/1060.3 GByte  
Aussentemperatur : 11.9 °C  Luftdruck : 1020.30 hPas/mbar
Impressions: 8 
Visits heute: 148 

  Aktuelle Seite: GPS NMEA stream parser

Home  

GPS-Visualisierer für das Terminal

Dieses C-Programm ist ein schlankes und effizientes Werkzeug, um GPS-Daten direkt im Terminal zu visualisieren. Es liest und parst GPS-Informationen im gängigen NMEA-Format von der Standardeingabe und stellt sie mithilfe der ncurses-Bibliothek in einer interaktiven, textbasierten Benutzeroberfläche dar.

Hauptfunktionen auf einen Blick

  • Echtzeit-Anzeige: Das Programm verarbeitet kontinuierlich eingehende GPS-Daten und aktualisiert die Anzeige nur dann, wenn sich die Werte ändern.

  • Wichtige GPS-Informationen: Ein Hauptfenster zeigt alle relevanten Details an, darunter:

    • Position: Breitengrad und Längengrad.

    • Höhe: Die aktuelle Höhe über dem Meeresspiegel.

    • Geschwindigkeit: Angezeigt in km/h und Knoten.

    • Zeit & Datum: UTC-Zeit und -Datum.

    • Genauigkeit: Schätzung der Positionsgenauigkeit in Metern.

  • Satelliten-Übersicht: Ein separates Fenster listet alle sichtbaren Satelliten auf. Du siehst ihre PRN-Nummer, Höhe, Azimut und das Signal-Rausch-Verhältnis (SNR).

  • NMEA-Parsing: Die Software kann gängige NMEA-Sätze wie $GPGGA, $GPRMC, $GPGSA und $GPGSV interpretieren.

Dieses Tool eignet sich ideal, um die Daten von einem seriellen GPS-Empfänger direkt und übersichtlich auf einem Linux-System anzuzeigen.

Quellcode:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <ncurses.h>

// Struktur zum Speichern der GPS-Daten
typedef struct {
    double latitude;
    double longitude;
    double altitude;
    double speed_knots;
    int fix_quality;
    int satellites;
    char utc_time[11];
    char utc_date[7];
    char fix_mode[2];
    int fix_type;
    double pdop;
    double hdop;
    double vdop;
    int visible_satellites;
    int satellite_prn[32];
    int satellite_elevation[32];
    int satellite_azimuth[32];
    int satellite_snr[32];
    double accuracy_meters; // Neues Feld für die Genauigkeit in Metern
} GpsData;

// Deklarationen
void parse_gpgga(const char *nmea, GpsData *data);
void parse_gprmc(const char *nmea, GpsData *data);
void parse_gpgll(const char *nmea, GpsData *data);
void parse_gpgsa(const char *nmea, GpsData *data);
void parse_gpgsv(const char *nmea, GpsData *data);
void draw_ui(WINDOW *main_win, WINDOW *sat_win, const GpsData *data);
int data_changed(const GpsData *current, const GpsData *last);
void read_and_parse(GpsData *data, WINDOW *main_win, WINDOW *sat_win);

// Funktion, um zwei GpsData-Strukturen zu vergleichen
int data_changed(const GpsData *current, const GpsData *last) {
    if (current->latitude != last->latitude ||
        current->longitude != last->longitude ||
        current->altitude != last->altitude ||
        current->speed_knots != last->speed_knots ||
        current->fix_quality != last->fix_quality ||
        current->fix_type != last->fix_type ||
        current->satellites != last->satellites ||
        current->visible_satellites != last->visible_satellites ||
        strcmp(current->utc_time, last->utc_time) != 0 ||
        strcmp(current->utc_date, last->utc_date) != 0 ||
        current->hdop != last->hdop ||
        current->accuracy_meters != last->accuracy_meters) { // Neuer Vergleich
        return 1;
    }

    for (int i = 0; i < current->visible_satellites && i < 32; i++) {
        if (current->satellite_prn[i] != last->satellite_prn[i] ||
            current->satellite_snr[i] != last->satellite_snr[i]) {
            return 1;
        }
    }

    return 0;
}

// Funktion zum Initialisieren und Zeichnen der ncurses-Oberfläche
void draw_ui(WINDOW *main_win, WINDOW *sat_win, const GpsData *data) {
    wclear(main_win);
    wclear(sat_win);

    mvwprintw(main_win, 1, 2, "GPS Daten");
    mvwprintw(main_win, 3, 4, "Position:      Breitengrad: %.6f, Laengengrad: %.6f", data->latitude, data->longitude);
    mvwprintw(main_win, 4, 4, "Hoehe:         %.2f m", data->altitude);
    mvwprintw(main_win, 5, 4, "Geschw.:       %.1f km/h (%.2f Knoten)", data->speed_knots*1.852, data->speed_knots);
    mvwprintw(main_win, 6, 4, "Datum (UTC):   %.2s.%.2s.20%.2s", data->utc_date, data->utc_date + 2, data->utc_date + 4);
    mvwprintw(main_win, 7, 4, "Uhrzeit (UTC): %.2s:%.2s:%.2s", data->utc_time, data->utc_time + 2, data->utc_time + 4);
    mvwprintw(main_win, 8, 4, "Fix-Qualitaet: %d", data->fix_quality);
    mvwprintw(main_win, 9, 4, "Fix-Typ:       %s", (data->fix_type == 1) ? "Kein Fix" : (data->fix_type == 2) ? "2D Fix" : "3D Fix");
    mvwprintw(main_win, 10, 4, "Satelliten:    %d (verwendet), %d (sichtbar)", data->satellites, data->visible_satellites);
    mvwprintw(main_win, 11, 4, "HDOP:          %.2f", data->hdop);
    mvwprintw(main_win, 12, 4, "Genauigkeit:   ~%.2f m", data->accuracy_meters); // Neue Ausgabe

    box(main_win, 0, 0);
    wrefresh(main_win);

    mvwprintw(sat_win, 1, 2, "Sichtbare Satelliten");
    mvwprintw(sat_win, 3, 2, "PRNtHoehetAzimuttSNR");
    mvwprintw(sat_win, 4, 2, "-----------------------------------");

    for (int i = 0; i < data->visible_satellites && i < 32; i++) {
        mvwprintw(sat_win, 5 + i, 2, "%dt%dt%dt%d",
                  data->satellite_prn[i],
                  data->satellite_elevation[i],
                  data->satellite_azimuth[i],
                  data->satellite_snr[i]);
    }

    box(sat_win, 0, 0);
    wrefresh(sat_win);
}

// ... (Hier folgen die Parser-Funktionen, wie im vorherigen Beispiel) ...
void parse_gpgga(const char *nmea_sentence, GpsData *data) {
    char s_latitude[15], s_longitude[15], s_altitude[15];
    int fix_quality, satellites;
    double latitude, longitude, altitude;
    if (sscanf(nmea_sentence, "$GPGGA,%*[^,],%[^,],%*[^,],%[^,],%*[^,],%d,%d,%*[^,],%[^,]",
               s_latitude, s_longitude, &fix_quality, &satellites, s_altitude) == 5) {
        double lat_deg = strtod(s_latitude, NULL) / 100.0;
        double lat_min = fmod(lat_deg, 1.0) * 100.0 / 60.0;
        latitude = floor(lat_deg) + lat_min;
        if (strstr(nmea_sentence, ",S,") != NULL) latitude = -latitude;
        double lon_deg = strtod(s_longitude, NULL) / 100.0;
        double lon_min = fmod(lon_deg, 1.0) * 100.0 / 60.0;
        longitude = floor(lon_deg) + lon_min;
        if (strstr(nmea_sentence, ",W,") != NULL) longitude = -longitude;
        altitude = strtod(s_altitude, NULL);
        data->latitude = latitude;
        data->longitude = longitude;
        data->altitude = altitude;
        data->fix_quality = fix_quality;
        data->satellites = satellites;
    }
}

void parse_gprmc(const char *nmea_sentence, GpsData *data) {
    char s_speed[15], s_date[7], s_time[11], status;
    double speed;
    if (sscanf(nmea_sentence, "$GPRMC,%[^,],%c,%*[^,],%*[^,],%*[^,],%*[^,],%[^,],%*[^,],%[^,]",
               s_time, &status, s_speed, s_date) == 4) {
         if (status == 'A') {
            speed = strtod(s_speed, NULL);
            data->speed_knots = speed;
            strcpy(data->utc_time, s_time);
            strcpy(data->utc_date, s_date);
        }
    }
}

void parse_gpgll(const char *nmea_sentence, GpsData *data) {
    char s_latitude[15], s_longitude[15], s_time[11], status;
    double latitude, longitude;
    if (sscanf(nmea_sentence, "$GPGLL,%[^,],%*[^,],%[^,],%*[^,],%[^,],%c",
               s_latitude, s_longitude, s_time, &status) == 4) {
        if (status == 'A') {
            double lat_deg = strtod(s_latitude, NULL) / 100.0;
            double lat_min = fmod(lat_deg, 1.0) * 100.0 / 60.0;
            latitude = floor(lat_deg) + lat_min;
            if (strstr(nmea_sentence, ",S,") != NULL) latitude = -latitude;
            double lon_deg = strtod(s_longitude, NULL) / 100.0;
            double lon_min = fmod(lon_deg, 1.0) * 100.0 / 60.0;
            longitude = floor(lon_deg) + lon_min;
            if (strstr(nmea_sentence, ",W,") != NULL) longitude = -longitude;
            data->latitude = latitude;
            data->longitude = longitude;
        }
        strcpy(data->utc_time, s_time);
    }
}

void parse_gpgsa(const char *nmea_sentence, GpsData *data) {
    char s_pdop[10], s_hdop[10], s_vdop[10];
    int fix_type;
    char fix_mode[2];
    if (sscanf(nmea_sentence, "$GPGSA,%c,%d,%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%*[^,],%[^,],%[^,],%[^,]",
               fix_mode, &fix_type, s_pdop, s_hdop, s_vdop) == 5) {
        strcpy(data->fix_mode, fix_mode);
        data->fix_type = fix_type;
        data->pdop = strtod(s_pdop, NULL);
        data->hdop = strtod(s_hdop, NULL);
        data->vdop = strtod(s_vdop, NULL);
        // Berechnung der Genauigkeit in Metern
        if (data->hdop > 0) {
            data->accuracy_meters = data->hdop * 5.0;
        } else {
            data->accuracy_meters = 0.0;
        }
    }
}

void parse_gpgsv(const char *nmea_sentence, GpsData *data) {
    int total_sentences, sentence_number, visible_satellites;
    char *token;
    char temp_line[1024];

    strcpy(temp_line, nmea_sentence);

    token = strtok(temp_line, ",");
    if (token == NULL) return;
    total_sentences = atoi(strtok(NULL, ","));
    sentence_number = atoi(strtok(NULL, ","));
    visible_satellites = atoi(strtok(NULL, ","));

    if (sentence_number == 1) {
        data->visible_satellites = visible_satellites;
        memset(data->satellite_prn, 0, sizeof(data->satellite_prn));
        memset(data->satellite_elevation, 0, sizeof(data->satellite_elevation));
        memset(data->satellite_azimuth, 0, sizeof(data->satellite_azimuth));
        memset(data->satellite_snr, 0, sizeof(data->satellite_snr));
    }

    for (int i = 0; i < 4; i++) {
        int index = (sentence_number - 1) * 4 + i;
        if (index >= 32) break;

        char *prn_str = strtok(NULL, ",");
        char *elev_str = strtok(NULL, ",");
        char *azim_str = strtok(NULL, ",");
        char *snr_str = strtok(NULL, "*");

        if (prn_str && elev_str && azim_str && snr_str) {
            data->satellite_prn[index] = atoi(prn_str);
            data->satellite_elevation[index] = atoi(elev_str);
            data->satellite_azimuth[index] = atoi(azim_str);
            data->satellite_snr[index] = atoi(snr_str);
        }
    }
}

void read_and_parse(GpsData *data, WINDOW *main_win, WINDOW *sat_win) {
    char line[1024];
    GpsData last_gps_data = {0}; 
    while (fgets(line, sizeof(line), stdin) != NULL) {
        line[strcspn(line, "n")] = 0;
        if (strstr(line, "$GPGGA") == line) {
            parse_gpgga(line, data);
        } else if (strstr(line, "$GPRMC") == line) {
            parse_gprmc(line, data);
        } else if (strstr(line, "$GPGLL") == line) {
            parse_gpgll(line, data);
        } else if (strstr(line, "$GPGSA") == line) {
            parse_gpgsa(line, data);
        } else if (strstr(line, "$GPGSV") == line) {
            parse_gpgsv(line, data);
        }
        if (data_changed(data, &last_gps_data)) {
            draw_ui(main_win, sat_win, data);
            doupdate();
            last_gps_data = *data;
        }
    }
}

int main(void) {
    GpsData my_gps_data;
    memset(&my_gps_data, 0, sizeof(my_gps_data));
    initscr();
    cbreak();
    noecho();
    curs_set(0); 
    int main_win_height = 15, main_win_width = 80;
    int sat_win_height = 20, sat_win_width = 80;
    WINDOW *main_win = newwin(main_win_height, main_win_width, 0, 0);
    WINDOW *sat_win = newwin(sat_win_height, sat_win_width, main_win_height, 0);
    mvwprintw(main_win, 1, 2, "Warte auf NMEA-Daten...");
    mvwprintw(main_win, 2, 2, "Zum Beenden Strg+C druecken.");
    box(main_win, 0, 0);
    wrefresh(main_win);
    read_and_parse(&my_gps_data, main_win, sat_win);
    delwin(main_win);
    delwin(sat_win);
    endwin();
    return 0;
}

Makefile:

CC = gcc
CFLAGS = -Wall -Wextra
LDFLAGS = -lm -lncurses
#gcc -o gps_decoder gps_decoder.c -lgps
TARGET = gps-nmea-decode
SRC = gps-nmea-decode.c

all: $(TARGET)

$(TARGET): $(SRC)
        $(CC) $(CFLAGS) $(SRC) -o $(TARGET) $(LDFLAGS)

clean:
        rm -f $(TARGET)

Lizenz

GPL V3 oder neuer

Download

Format tar.xz LINK
Format zip LINK