Visits heute: 114
Aktuelle Seite: GPS NMEA stream parser
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 |