From a19de37552904b4ee85dcb509fa48742cb5a6537 Mon Sep 17 00:00:00 2001 From: Ankit Date: Sun, 14 Jun 2015 15:08:38 +0530 Subject: [PATCH] Create gclue_location_create_from_gga This patch creates gclue_location_create_from_gga function inside GClueLocation which will be able to create a GClueLocation object out of a NMEA GGA sentence. This function works with the help of functions: get_accuracy_from_hdop, parse_coordinate_string and parse_altitude_string which are moved from gclue-modem-gps.c. https://bugs.freedesktop.org/show_bug.cgi?id=90974 --- src/gclue-location.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/gclue-location.h | 7 +++ src/gclue-modem-gps.c | 118 +++---------------------------------------- 3 files changed, 150 insertions(+), 111 deletions(-) diff --git a/src/gclue-location.c b/src/gclue-location.c index 338d486..9fb3c4a 100644 --- a/src/gclue-location.c +++ b/src/gclue-location.c @@ -203,6 +203,142 @@ gclue_location_new_with_description (gdouble latitude, NULL); } +static gdouble +get_accuracy_from_hdop (gdouble hdop) +{ + /* FIXME: These are really just rough estimates based on: + * http://en.wikipedia.org/wiki/Dilution_of_precision_%28GPS%29#Meaning_of_DOP_Values + */ + if (hdop <= 1) + return 0; + else if (hdop <= 2) + return 1; + else if (hdop <= 5) + return 3; + else if (hdop <= 10) + return 50; + else if (hdop <= 20) + return 100; + else + return 300; +} + +static gdouble +parse_coordinate_string (const char *coordinate, + const char *direction) +{ + gdouble minutes, degrees, out; + gchar *degrees_str; + gchar *dot_str; + gint dot_offset; + + if (coordinate[0] == '\0' || + direction[0] == '\0' || + direction[0] == '\0') + return INVALID_COORDINATE; + + if (direction[0] != 'N' && + direction[0] != 'S' && + direction[0] != 'E' && + direction[0] != 'W') { + g_warning ("Unknown direction '%s' for coordinates, ignoring..", + direction); + return INVALID_COORDINATE; + } + + dot_str = g_strstr_len (coordinate, 6, "."); + if (dot_str == NULL) + return INVALID_COORDINATE; + dot_offset = dot_str - coordinate; + + degrees_str = g_strndup (coordinate, dot_offset - 2); + degrees = g_ascii_strtod (degrees_str, NULL); + g_free (degrees_str); + + minutes = g_ascii_strtod (coordinate + dot_offset - 2, NULL); + + /* Include the minutes as part of the degrees */ + out = degrees + (minutes / 60.0); + + if (direction[0] == 'S' || direction[0] == 'W') + out = 0 - out; + + return out; +} + +static gdouble +parse_altitude_string (const char *altitude, + const char *unit) +{ + if (altitude[0] == '\0' || unit[0] == '\0') + return GEOCODE_LOCATION_ALTITUDE_UNKNOWN; + + if (unit[0] != 'M') { + g_warning ("Unknown unit '%s' for altitude, ignoring..", + unit); + + return GEOCODE_LOCATION_ALTITUDE_UNKNOWN; + } + + return g_ascii_strtod (altitude, NULL); +} + +/** + * gclue_location_create_from_gga: + * @gga NMEA GGA sentence + * @error a GIOErrorEnum, it's value is set to G_IO_ERROR_INVALID_ARGUMENT + * whenever the gga sentence can't be parsed properly. + * + * Creates a new #GClueLocation object from a GGA sentence. + * + * Returns: a new #GClueLocation object. Use g_object_unref() when done. + **/ +GClueLocation * +gclue_location_create_from_gga (const char *gga, GError **error) +{ + GClueLocation *location = NULL; + gdouble latitude, longitude, accuracy, altitude; + gdouble hdop; /* Horizontal Dilution Of Precision */ + char **parts; + + parts = g_strsplit (gga, ",", -1); + if (g_strv_length (parts) < 14) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid NMEA GGA sentence"); + goto out; + } + + /* For syntax of GGA senentences: + * http://www.gpsinformation.org/dale/nmea.htm#GGA + */ + latitude = parse_coordinate_string (parts[2], parts[3]); + longitude = parse_coordinate_string (parts[4], parts[5]); + if (latitude == INVALID_COORDINATE || longitude == INVALID_COORDINATE) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "Invalid NMEA GGA sentence"); + goto out; + } + + altitude = parse_altitude_string (parts[9], parts[10]); + if (altitude == GEOCODE_LOCATION_ALTITUDE_UNKNOWN) + goto out; + + hdop = g_ascii_strtod (parts[8], NULL); + accuracy = get_accuracy_from_hdop (hdop); + + location = gclue_location_new (latitude, longitude, accuracy); + if (altitude != GEOCODE_LOCATION_ALTITUDE_UNKNOWN) + g_object_set (location, "altitude", altitude, NULL); + +out: + g_strfreev (parts); + return location; +} + /** * gclue_location_get_speed: * @location: a #GClueLocation diff --git a/src/gclue-location.h b/src/gclue-location.h index 7cd0e7f..86d738f 100644 --- a/src/gclue-location.h +++ b/src/gclue-location.h @@ -27,6 +27,7 @@ #define GCLUE_LOCATION_H #include +#include #include "geocode-glib/geocode-location.h" G_BEGIN_DECLS @@ -38,6 +39,8 @@ G_BEGIN_DECLS #define GCLUE_IS_LOCATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GCLUE_TYPE_LOCATION)) #define GCLUE_LOCATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GCLUE_TYPE_LOCATION, GClueLocationClass)) +#define INVALID_COORDINATE -G_MAXDOUBLE + typedef struct _GClueLocation GClueLocation; typedef struct _GClueLocationClass GClueLocationClass; typedef struct _GClueLocationPrivate GClueLocationPrivate; @@ -82,6 +85,10 @@ GClueLocation *gclue_location_new_with_description gdouble accuracy, const char *description); +GClueLocation *gclue_location_create_from_gga + (const char *gga, + GError **error); + void gclue_location_set_speed (GClueLocation *loc, gdouble speed); diff --git a/src/gclue-modem-gps.c b/src/gclue-modem-gps.c index 36c8b74..6362be6 100644 --- a/src/gclue-modem-gps.c +++ b/src/gclue-modem-gps.c @@ -187,128 +187,24 @@ gclue_modem_gps_get_singleton (void) return source; } -static gdouble -get_accuracy_from_hdop (gdouble hdop) -{ - /* FIXME: These are really just rough estimates based on: - * http://en.wikipedia.org/wiki/Dilution_of_precision_%28GPS%29#Meaning_of_DOP_Values - */ - if (hdop <= 1) - return 0; - else if (hdop <= 2) - return 1; - else if (hdop <= 5) - return 3; - else if (hdop <= 10) - return 50; - else if (hdop <= 20) - return 100; - else - return 300; -} - -#define INVALID_COORDINATE -G_MAXDOUBLE - -static gdouble -parse_coordinate_string (const char *coordinate, - const char *direction) -{ - gdouble minutes, degrees, out; - gchar *degrees_str; - gchar *dot_str; - gint dot_offset; - - if (coordinate[0] == '\0' || - direction[0] == '\0' || - direction[0] == '\0') - return INVALID_COORDINATE; - - if (direction[0] != 'N' && - direction[0] != 'S' && - direction[0] != 'E' && - direction[0] != 'W') { - g_warning ("Unknown direction '%s' for coordinates, ignoring..", - direction); - return INVALID_COORDINATE; - } - - dot_str = g_strstr_len (coordinate, 6, "."); - if (dot_str == NULL) - return INVALID_COORDINATE; - dot_offset = dot_str - coordinate; - - degrees_str = g_strndup (coordinate, dot_offset - 2); - degrees = g_ascii_strtod (degrees_str, NULL); - g_free (degrees_str); - - minutes = g_ascii_strtod (coordinate + dot_offset - 2, NULL); - - /* Include the minutes as part of the degrees */ - out = degrees + (minutes / 60.0); - - if (direction[0] == 'S' || direction[0] == 'W') - out = 0 - out; - - return out; -} - -static gdouble -parse_altitude_string (const char *altitude, - const char *unit) -{ - if (altitude[0] == '\0' || unit[0] == '\0') - return GEOCODE_LOCATION_ALTITUDE_UNKNOWN; - - if (unit[0] != 'M') { - g_warning ("Unknown unit '%s' for altitude, ignoring..", - unit); - - return GEOCODE_LOCATION_ALTITUDE_UNKNOWN; - } - - return g_ascii_strtod (altitude, NULL); -} - static void on_fix_gps (GClueModem *modem, const char *gga, gpointer user_data) { - GClueModemGPS *source = GCLUE_MODEM_GPS (user_data); + GClueLocationSource *source = GCLUE_LOCATION_SOURCE (user_data); GClueLocation *location; - gdouble latitude, longitude, accuracy, altitude; - gdouble hdop; /* Horizontal Dilution Of Precision */ - char **parts; + GError *error = NULL; - parts = g_strsplit (gga, ",", -1); - if (g_strv_length (parts) < 14 ) { - g_warning ("Failed to parse NMEA GGA sentence:\n%s", gga); + location = gclue_location_create_from_gga (gga, &error); - goto out; + if (error != NULL) { + g_warning ("Error: %s", error->message); + g_clear_error (&error); } - /* For sentax of GGA senentences: - * http://www.gpsinformation.org/dale/nmea.htm#GGA - */ - latitude = parse_coordinate_string (parts[2], parts[3]); - longitude = parse_coordinate_string (parts[4], parts[5]); - if (latitude == INVALID_COORDINATE || longitude == INVALID_COORDINATE) - goto out; - - altitude = parse_altitude_string (parts[9], parts[10]); - if (altitude == GEOCODE_LOCATION_ALTITUDE_UNKNOWN) - goto out; - - hdop = g_ascii_strtod (parts[8], NULL); - accuracy = get_accuracy_from_hdop (hdop); - - location = gclue_location_new (latitude, longitude, accuracy); - if (altitude != GEOCODE_LOCATION_ALTITUDE_UNKNOWN) - g_object_set (location, "altitude", altitude, NULL); - gclue_location_source_set_location (GCLUE_LOCATION_SOURCE (source), + gclue_location_source_set_location (source, location); -out: - g_strfreev (parts); } static gboolean -- 2.1.0