diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-02 15:08:46 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-10-04 10:17:05 +0200 |
commit | b6d628b3a10bf183b918c7afe89a5b260eb87760 (patch) | |
tree | 8d87e92f2d8b7da99e6a0e41e49a6aa0ebbf55bd /libmm-glib/mm-location-gps-nmea.c | |
parent | 624fdb6ab4544a82774e9805332cc242c1068b0f (diff) |
build: merge libmm-common into libmm-glib
It's pointless to have libmm-common around, just merge it into libmm-glib and
make ModemManager depend on libmm-glib directly. At the end, the non-common
stuff in libmm-glib is really minimal.
Diffstat (limited to 'libmm-glib/mm-location-gps-nmea.c')
-rw-r--r-- | libmm-glib/mm-location-gps-nmea.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/libmm-glib/mm-location-gps-nmea.c b/libmm-glib/mm-location-gps-nmea.c new file mode 100644 index 00000000..de6478dc --- /dev/null +++ b/libmm-glib/mm-location-gps-nmea.c @@ -0,0 +1,248 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2012 Lanedo GmbH <aleksander@lanedo.com> + */ + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#include "mm-common-helpers.h" +#include "mm-errors-types.h" +#include "mm-location-gps-nmea.h" + +G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT); + +struct _MMLocationGpsNmeaPrivate { + GHashTable *traces; + GRegex *sequence_regex; +}; + +/*****************************************************************************/ + +static gboolean +check_append_or_replace (MMLocationGpsNmea *self, + const gchar *trace) +{ + /* By default, replace */ + gboolean append_or_replace = FALSE; + GMatchInfo *match_info = NULL; + + if (G_UNLIKELY (!self->priv->sequence_regex)) + self->priv->sequence_regex = g_regex_new ("\\$GPGSV,(\\d),(\\d).*", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, + NULL); + + if (g_regex_match (self->priv->sequence_regex, trace, 0, &match_info)) { + guint index; + + /* If we don't have the first element of a sequence, append */ + if (mm_get_uint_from_match_info (match_info, 2, &index) && index != 1) + append_or_replace = TRUE; + } + g_match_info_free (match_info); + + return append_or_replace; +} + +static gboolean +location_gps_nmea_take_trace (MMLocationGpsNmea *self, + gchar *trace) +{ + gchar *i; + gchar *trace_type; + + i = strchr (trace, ','); + if (!i || i == trace) + return FALSE; + + trace_type = g_malloc (i - trace + 1); + memcpy (trace_type, trace, i - trace); + trace_type[i - trace] = '\0'; + + /* Some traces are part of a SEQUENCE; so we need to decide whether we + * completely replace the previous trace, or we append the new one to + * the already existing list */ + if (check_append_or_replace (self, trace)) { + /* Append */ + const gchar *previous; + + previous = g_hash_table_lookup (self->priv->traces, trace_type); + if (previous) { + gchar *sequence; + + /* Skip the trace if we already have it there */ + if (strstr (previous, trace)) + return TRUE; + + sequence = g_strdup_printf ("%s%s%s", + previous, + g_str_has_suffix (previous, "\r\n") ? "" : "\r\n", + trace); + g_free (trace); + trace = sequence; + } + } + + g_hash_table_replace (self->priv->traces, + trace_type, + trace); + return TRUE; +} + +gboolean +mm_location_gps_nmea_add_trace (MMLocationGpsNmea *self, + const gchar *trace) +{ + return location_gps_nmea_take_trace (self, g_strdup (trace)); +} + +/*****************************************************************************/ + +const gchar * +mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self, + const gchar *trace_type) +{ + return (const gchar *)g_hash_table_lookup (self->priv->traces, trace_type); +} + +/*****************************************************************************/ + +static void +build_full_foreach (const gchar *trace_type, + const gchar *trace, + GString **built) +{ + if ((*built)->len == 0 || g_str_has_suffix ((*built)->str, "\r\n")) + g_string_append (*built, trace); + else + g_string_append_printf (*built, "\r\n%s", trace); +} + +gchar * +mm_location_gps_nmea_build_full (MMLocationGpsNmea *self) +{ + GString *built; + + built = g_string_new (""); + g_hash_table_foreach (self->priv->traces, + (GHFunc)build_full_foreach, + &built); + return g_string_free (built, FALSE); +} + +/*****************************************************************************/ + +GVariant * +mm_location_gps_nmea_get_string_variant (MMLocationGpsNmea *self) +{ + GVariant *variant = NULL; + gchar *built; + + g_return_val_if_fail (MM_IS_LOCATION_GPS_NMEA (self), NULL); + + built = mm_location_gps_nmea_build_full (self); + variant = g_variant_new_string (built); + g_free (built); + + return variant; +} + +/*****************************************************************************/ + +MMLocationGpsNmea * +mm_location_gps_nmea_new_from_string_variant (GVariant *string, + GError **error) +{ + MMLocationGpsNmea *self = NULL; + gchar **split; + guint i; + + if (!g_variant_is_of_type (string, G_VARIANT_TYPE_STRING)) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot create GPS NMEA location from string: " + "invalid variant type received"); + return NULL; + } + + split = g_strsplit (g_variant_get_string (string, NULL), "\r\n", -1); + if (!split) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Invalid GPS NMEA location string: '%s'", + g_variant_get_string (string, NULL)); + return NULL; + } + + /* Create new location object */ + self = mm_location_gps_nmea_new (); + + for (i = 0; split[i]; i++) { + if (!location_gps_nmea_take_trace (self, split[i])) + g_free (split[i]); + } + + /* Note that the strings in the array of strings were already taken + * or freed */ + g_free (split); + + return self; +} + +/*****************************************************************************/ + +MMLocationGpsNmea * +mm_location_gps_nmea_new (void) +{ + return (MM_LOCATION_GPS_NMEA ( + g_object_new (MM_TYPE_LOCATION_GPS_NMEA, NULL))); +} + +static void +mm_location_gps_nmea_init (MMLocationGpsNmea *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_LOCATION_GPS_NMEA, + MMLocationGpsNmeaPrivate); + + self->priv->traces = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); +} + +static void +finalize (GObject *object) +{ + MMLocationGpsNmea *self = MM_LOCATION_GPS_NMEA (object); + + g_hash_table_destroy (self->priv->traces); + if (self->priv->sequence_regex) + g_regex_unref (self->priv->sequence_regex); + + G_OBJECT_CLASS (mm_location_gps_nmea_parent_class)->finalize (object); +} + +static void +mm_location_gps_nmea_class_init (MMLocationGpsNmeaClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMLocationGpsNmeaPrivate)); + + object_class->finalize = finalize; +} |