summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoriain <iain@linux.intel.com>2011-08-12 14:10:40 +0100
committeriain <iain@linux.intel.com>2011-08-12 14:10:40 +0100
commit989a6cab8b272b094e1f30f471fe13d33cf6a0b6 (patch)
tree28f303ab7bc01693aaf0a2ffbaf60afa5fe130ef /src
parent3f587f43c8ea06d42148dd0be84bc3ad92838069 (diff)
Add some parser objects
The parsers in GypsyClient need separated out into separate objects so that other protocols can be added easily. The NMEA parser was created from the code in gypsy-client.c and the Garmin code is based on the garmin->nmea conversion code in gypsy-client.c and nmea-gen.c
Diffstat (limited to 'src')
-rw-r--r--src/gypsy-garmin-parser.c372
-rw-r--r--src/gypsy-garmin-parser.h52
-rw-r--r--src/gypsy-nmea-parser.c179
-rw-r--r--src/gypsy-nmea-parser.h51
-rw-r--r--src/gypsy-parser.c140
-rw-r--r--src/gypsy-parser.h57
6 files changed, 851 insertions, 0 deletions
diff --git a/src/gypsy-garmin-parser.c b/src/gypsy-garmin-parser.c
new file mode 100644
index 0000000..9990ff8
--- /dev/null
+++ b/src/gypsy-garmin-parser.c
@@ -0,0 +1,372 @@
+/*
+ * Gypsy
+ *
+ * A simple to use and understand GPSD replacement
+ * that uses D-Bus, GLib and memory allocations
+ *
+ * Author: Iain Holmes <iain@sleepfive.com>
+ * Copyright (C) 2011
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* Includes code from Garmin Protocol to NMEA 0183 converter,
+ license as below */
+/*
+ Garmin protocol to NMEA 0183 converter
+ Copyright (C) 2004 Manuel Kasper <mk@neon1.net>.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+*/
+#include <math.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "gypsy-garmin-parser.h"
+#include "garmin.h"
+
+#define KNOTS_TO_KMH 1.852
+#define rad2deg(x) ((x) * 180.0 / G_PI)
+#define READ_BUFFER_SIZE 1024
+
+struct _GypsyGarminParserPrivate {
+ guchar sentence[READ_BUFFER_SIZE];
+ gsize bytes_in_buffer;
+
+ GDate *epoch;
+ GDate *date;
+
+ double lastcourse;
+};
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GYPSY_TYPE_GARMIN_PARSER, GypsyGarminParserPrivate))
+G_DEFINE_TYPE (GypsyGarminParser, gypsy_garmin_parser, GYPSY_TYPE_PARSER);
+
+static void
+gypsy_garmin_parser_finalize (GObject *object)
+{
+ GypsyGarminParser *self = (GypsyGarminParser *) object;
+ GypsyGarminParserPrivate *priv = self->priv;
+
+ g_date_free (priv->epoch);
+ g_date_free (priv->date);
+
+ G_OBJECT_CLASS (gypsy_garmin_parser_parent_class)->finalize (object);
+}
+
+static void
+gypsy_garmin_parser_dispose (GObject *object)
+{
+#if 0
+ GypsyGarminParser *self = (GypsyGarminParser *) object;
+#endif
+ G_OBJECT_CLASS (gypsy_garmin_parser_parent_class)->dispose (object);
+}
+
+#define SECONDS_PER_WEEK 604800
+#define SECONDS_PER_DAY 86400
+#define DAYS_PER_WEEK 7
+
+static int
+calculate_utc (GypsyGarminParser *parser,
+ D800_Pvt_Data_Type *pvt)
+{
+ GypsyGarminParserPrivate *priv = parser->priv;
+ int tmp = 0, dtmp = 0;
+ unsigned long w, x, a, b, c, d, e, f;
+ unsigned long day, month, year;
+ unsigned long jd;
+ int datestamp, days_since_epoch;
+ int timestamp;
+
+ /*
+ UTC time of position fix
+ Reminder:
+ pvt->tow = seconds (including fractions) since the start of the week.
+ pvt->wn_days = days since 31-DEC-1989 for the start of the current week
+ (neither is adjusted for leap seconds)
+ pvt->leap_scnds = leap second adjustment required.
+ */
+
+ /*
+ The receivers can (and do) return times like 86299.999999 instead
+ of 86300.0 Rounding is required to get the correct time.
+ Just capture wn_days for now.
+ */
+
+ tmp = rint (pvt->tow);
+ dtmp = pvt->wn_days;
+
+ /*
+ If the result is 604800, it's really the first sample
+ of the new week, so zero out tmp and increment dtmp
+ by a week ( 7 days ).
+ */
+
+ if (tmp >= SECONDS_PER_WEEK) {
+ dtmp += DAYS_PER_WEEK;
+ tmp = 0;
+ }
+
+ /*
+ At this point we have tmp = seconds since the start
+ of the week, and dtmp = the first day of the week.
+ We now need to correct for leap seconds. This may actually
+ result in reversing the previous adjustment but the code
+ required to combine the two operations wouldn't be clear.
+ */
+ tmp -= pvt->leap_scnds;
+ if (tmp < 0) {
+ tmp += SECONDS_PER_WEEK;
+ dtmp -= DAYS_PER_WEEK;
+ }
+
+ /*
+ Now we have tmp = seconds since the start if the week,
+ and dtmp = the first day of the week, all corrected for
+ rounding and leap seconds.
+
+ We now convert dtmp to today's day number
+ */
+ dtmp += (tmp / SECONDS_PER_DAY);
+
+ /* Garmin format: number of days since December 31, 1989 */
+ jd = dtmp + 2447892;
+
+ w = (unsigned long)((jd - 1867216.25)/36524.25);
+ x = w/4;
+ a = jd + 1 + w - x;
+ b = a + 1524;
+ c = (unsigned long)((b - 122.1)/365.25);
+ d = (unsigned long)(365.25 * c);
+ e = (unsigned long)((b-d)/30.6001);
+ f = (unsigned long)(30.6001 * e);
+
+ day = b - d - f;
+ month = e - 1;
+ if (month > 12)
+ month -= 12;
+ year = c - 4716;
+ if (month == 1 || month == 2)
+ year++;
+
+ g_date_set_dmy (priv->date, day, month, year);
+ days_since_epoch = g_date_days_between (priv->epoch, priv->date);
+ datestamp = days_since_epoch * SECONDS_PER_DAY;
+
+ /* Convert tmp to seconds since midnight */
+ tmp %= SECONDS_PER_DAY;
+ timestamp = datestamp + tmp;
+
+ return timestamp;
+}
+
+/* NB: Speed and course over ground are calculated from
+ the north/east velocity and may not be accurate */
+static void
+calculate_speed_course (GypsyGarminParser *parser,
+ D800_Pvt_Data_Type *pvt,
+ double *speed,
+ double *course)
+{
+ GypsyGarminParserPrivate *priv = parser->priv;
+
+ *speed = sqrt (pvt->east * pvt->east + pvt->north * pvt->north) * 3.6 / KNOTS_TO_KMH;
+
+ if (*speed < 1.0) {
+ if (priv->lastcourse >= 0) {
+ *course = priv->lastcourse;
+ } else {
+ *course = 0; /* Too slow to determine course */
+ }
+ } else {
+ *course = atan2 (pvt->east, pvt->north);
+ if (*course < 0) {
+ *course += 2 * G_PI;
+ }
+ *course = rad2deg (*course);
+ priv->lastcourse = *course;
+ }
+}
+
+static gboolean
+gypsy_garmin_parser_received_data (GypsyParser *parser,
+ const guchar *data,
+ gsize length,
+ GError **error)
+{
+ GypsyGarminParser *garmin = GYPSY_GARMIN_PARSER (parser);
+ GypsyGarminParserPrivate *priv = garmin->priv;
+ GypsyClient *client;
+ G_Packet_t *pGpkt;
+ int pktlen;
+
+ client = gypsy_parser_get_client (parser);
+
+ memcpy (priv->sentence + priv->bytes_in_buffer, data, length);
+ priv->bytes_in_buffer += length;
+
+ pGpkt = (G_Packet_t*) priv->sentence;
+ pktlen = GARMIN_HEADER_SIZE + pGpkt->mDataSize;
+ while ((priv->bytes_in_buffer >= GARMIN_HEADER_SIZE) &&
+ (priv->bytes_in_buffer >= pktlen)) {
+ double speed, course;
+
+ /*g_debug("PacketId: %d pktlen = %d",
+ pGpkt->mPacketId, pktlen);*/
+
+ if (pGpkt->mPacketId == Pid_Pvt_Data) {
+ D800_Pvt_Data_Type *pvt;
+ int fixtype;
+
+ pvt = (D800_Pvt_Data_Type *) pGpkt->mData;
+
+ gypsy_client_set_timestamp (client, calculate_utc (garmin, pvt));
+
+ switch (pvt->fix) {
+ case 0:
+ case 1:
+ fixtype = FIX_NONE;
+ break;
+
+ case 2:
+ case 4:
+ fixtype = FIX_2D;
+ break;
+
+ case 3:
+ case 5:
+ fixtype = FIX_3D;
+ break;
+
+ default:
+ fixtype = FIX_INVALID;
+ break;
+ }
+ gypsy_client_set_fix_type (client, fixtype, FALSE);
+ gypsy_client_set_position (client,
+ POSITION_LATITUDE |
+ POSITION_LONGITUDE |
+ POSITION_ALTITUDE,
+ pvt->lat, pvt->lon, pvt->alt);
+
+ calculate_speed_course (garmin, pvt, &speed, &course);
+ gypsy_client_set_course (client,
+ COURSE_SPEED |
+ COURSE_DIRECTION,
+ speed, course, 0.0);
+ } else if (pGpkt->mPacketId == Pid_SatData_Record) {
+ int i;
+ cpo_sat_data *sat;
+
+ sat = (cpo_sat_data *)pGpkt->mData;
+
+ gypsy_client_clear_satellites (client);
+ for (i = 0; i < SAT_MAX_COUNT; i++) {
+ if (((sat[i].status & SAT_STATUS_MASK) == SAT_STATUS_GOOD) &&
+ (sat[i].svid <= MAX_SAT_SVID)) {
+ /* FIXME: I think this is only passing in_use satellites to
+ Gypsy, do we want to pass SAT_STATUS_BAD satellites as well
+ with in_use = FALSE? */
+ gypsy_client_add_satellite (client, sat[i].svid, TRUE,
+ sat[i].elev, sat[i].azmth,
+ sat[i].snr);
+ }
+ }
+
+ gypsy_client_set_satellites (client);
+ } else {
+ g_debug ("Untranslated PacketId = %d", pGpkt->mPacketId);
+ }
+
+ /* now that we're done with this packet,
+ move any remaining data up to the
+ beginning of the buffer */
+ memmove (priv->sentence, priv->sentence + pktlen,
+ priv->bytes_in_buffer - pktlen);
+ priv->bytes_in_buffer -= pktlen;
+ }
+
+ return TRUE;
+}
+
+static gsize
+gypsy_garmin_parser_get_space_in_buffer (GypsyParser *parser)
+{
+ GypsyGarminParser *garmin = GYPSY_GARMIN_PARSER (parser);
+ GypsyGarminParserPrivate *priv = garmin->priv;
+
+ return READ_BUFFER_SIZE - priv->bytes_in_buffer;
+}
+
+static void
+gypsy_garmin_parser_class_init (GypsyGarminParserClass *klass)
+{
+ GObjectClass *o_class = (GObjectClass *) klass;
+ GypsyParserClass *p_class = (GypsyParserClass *) klass;
+
+ o_class->dispose = gypsy_garmin_parser_dispose;
+ o_class->finalize = gypsy_garmin_parser_finalize;
+
+ p_class->received_data = gypsy_garmin_parser_received_data;
+ p_class->get_space_in_buffer = gypsy_garmin_parser_get_space_in_buffer;
+
+ g_type_class_add_private (klass, sizeof (GypsyGarminParserPrivate));
+}
+
+static void
+gypsy_garmin_parser_init (GypsyGarminParser *self)
+{
+ GypsyGarminParserPrivate *priv = GET_PRIVATE (self);
+
+ self->priv = priv;
+
+ priv->lastcourse = -1;
+
+ priv->epoch = g_date_new_dmy (1, 1, 1970);
+ priv->date = g_date_new ();
+}
+
+GypsyParser *
+gypsy_garmin_parser_new (GypsyClient *client)
+{
+ g_return_val_if_fail (GYPSY_IS_CLIENT (client), NULL);
+
+ return (GypsyParser *) g_object_new (GYPSY_TYPE_GARMIN_PARSER,
+ "client", client,
+ NULL);
+}
diff --git a/src/gypsy-garmin-parser.h b/src/gypsy-garmin-parser.h
new file mode 100644
index 0000000..a4db69b
--- /dev/null
+++ b/src/gypsy-garmin-parser.h
@@ -0,0 +1,52 @@
+#ifndef __GYPSY_GARMIN_PARSER_H__
+#define __GYPSY_GARMIN_PARSER_H__
+
+#include <gypsy-parser.h>
+#include <gypsy-client.h>
+
+
+G_BEGIN_DECLS
+
+#define GYPSY_TYPE_GARMIN_PARSER \
+ (gypsy_garmin_parser_get_type())
+#define GYPSY_GARMIN_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GYPSY_TYPE_GARMIN_PARSER, \
+ GypsyGarminParser))
+#define GYPSY_GARMIN_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ GYPSY_TYPE_GARMIN_PARSER, \
+ GypsyGarminParserClass))
+#define GYPSY_IS_GARMIN_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GYPSY_TYPE_GARMIN_PARSER))
+#define GYPSY_IS_GARMIN_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ GYPSY_TYPE_GARMIN_PARSER))
+#define GYPSY_GARMIN_PARSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GYPSY_TYPE_GARMIN_PARSER, \
+ GypsyGarminParserClass))
+
+typedef struct _GypsyGarminParserPrivate GypsyGarminParserPrivate;
+typedef struct _GypsyGarminParser GypsyGarminParser;
+typedef struct _GypsyGarminParserClass GypsyGarminParserClass;
+
+struct _GypsyGarminParser
+{
+ GypsyParser parent;
+
+ GypsyGarminParserPrivate *priv;
+};
+
+struct _GypsyGarminParserClass
+{
+ GypsyParserClass parent_class;
+};
+
+GType gypsy_garmin_parser_get_type (void) G_GNUC_CONST;
+GypsyParser *gypsy_garmin_parser_new (GypsyClient *client);
+
+G_END_DECLS
+
+#endif /* __GYPSY_GARMIN_PARSER_H__ */
diff --git a/src/gypsy-nmea-parser.c b/src/gypsy-nmea-parser.c
new file mode 100644
index 0000000..561a5db
--- /dev/null
+++ b/src/gypsy-nmea-parser.c
@@ -0,0 +1,179 @@
+/*
+ * Gypsy
+ *
+ * A simple to use and understand GPSD replacement
+ * that uses D-Bus, GLib and memory allocations
+ *
+ * Author: Iain Holmes <iain@sleepfive.com>
+ * Copyright (C) 2011
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "nmea-parser.h"
+#include "gypsy-nmea-parser.h"
+
+#define READ_BUFFER_SIZE 1024
+
+struct _GypsyNmeaParserPrivate {
+ NMEAParseContext *ctxt;
+
+ char sentence[READ_BUFFER_SIZE + 1]; /* This is for building
+ the NMEA sentence */
+ gsize chars_in_buffer; /* How many characters are in the buffer */
+};
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GYPSY_TYPE_NMEA_PARSER, GypsyNmeaParserPrivate))
+G_DEFINE_TYPE (GypsyNmeaParser, gypsy_nmea_parser, GYPSY_TYPE_PARSER);
+
+static void
+gypsy_nmea_parser_finalize (GObject *object)
+{
+ GypsyNmeaParser *self = (GypsyNmeaParser *) object;
+ GypsyNmeaParserPrivate *priv = self->priv;
+
+ if (priv->ctxt) {
+ nmea_parse_context_free (priv->ctxt);
+ priv->ctxt = NULL;
+ }
+
+ G_OBJECT_CLASS (gypsy_nmea_parser_parent_class)->finalize (object);
+}
+
+static void
+gypsy_nmea_parser_dispose (GObject *object)
+{
+#if 0
+ GypsyNmeaParser *self = (GypsyNmeaParser *) object;
+#endif
+ G_OBJECT_CLASS (gypsy_nmea_parser_parent_class)->dispose (object);
+}
+
+static GObject *
+gypsy_nmea_parser_constructor (GType type,
+ guint n_params,
+ GObjectConstructParam *params)
+{
+ GypsyNmeaParser *parser;
+ GypsyNmeaParserPrivate *priv;
+ GypsyClient *client;
+ GObject *object;
+
+ object = G_OBJECT_CLASS (gypsy_nmea_parser_parent_class)->constructor
+ (type, n_params, params);
+
+ parser = GYPSY_NMEA_PARSER (object);
+ priv = parser->priv;
+
+ g_object_get (object,
+ "client", &client,
+ NULL);
+
+ priv->ctxt = nmea_parse_context_new (client);
+
+ return object;
+}
+
+static gboolean
+gypsy_nmea_parser_received_data (GypsyParser *parser,
+ const guchar *data,
+ gsize length,
+ GError **error)
+{
+ GypsyNmeaParser *nmea = GYPSY_NMEA_PARSER (parser);
+ GypsyNmeaParserPrivate *priv = nmea->priv;
+ char *eos = NULL;
+
+ memcpy (priv->sentence + priv->chars_in_buffer, data, length);
+ priv->chars_in_buffer += length;
+
+ /* Append a '\0' to the data so we never run off the end.
+ The '\0' will be overwritten by the next call to received_data */
+ *(priv->sentence + priv->chars_in_buffer) = '\0';
+
+ /* NMEA sentences end with <CR><LF>,
+ so find the <CR> at the end of each sentence */
+ while ((eos = strchr (priv->sentence, '\r'))) {
+ int sentence_length;
+ /* Account for <LF> */
+ sentence_length = (eos - priv->sentence) + 2;
+ if (sentence_length > 1) {
+ /* terminate the string at the <CR> */
+ *eos = '\0';
+
+ g_debug ("NMEA sentence: %s", priv->sentence);
+ if (nmea_parse_sentence (priv->ctxt, priv->sentence, NULL) == FALSE) {
+ g_debug ("Invalid sentence: %s", priv->sentence);
+ }
+ }
+
+ if (sentence_length > 0) {
+ /* Remove the sentence from the buffer and
+ move the rest up including terminating 0 */
+ memmove (priv->sentence, eos + 2,
+ (priv->chars_in_buffer - sentence_length) + 1);
+ priv->chars_in_buffer -= sentence_length;
+ }
+ }
+
+ return TRUE;
+}
+
+static gsize
+gypsy_nmea_parser_get_space_in_buffer (GypsyParser *parser)
+{
+ GypsyNmeaParser *nmea = GYPSY_NMEA_PARSER (parser);
+ GypsyNmeaParserPrivate *priv = nmea->priv;
+
+ return READ_BUFFER_SIZE - priv->chars_in_buffer;
+}
+
+static void
+gypsy_nmea_parser_class_init (GypsyNmeaParserClass *klass)
+{
+ GObjectClass *o_class = (GObjectClass *) klass;
+ GypsyParserClass *p_class = (GypsyParserClass *) klass;
+
+ o_class->dispose = gypsy_nmea_parser_dispose;
+ o_class->finalize = gypsy_nmea_parser_finalize;
+ o_class->constructor = gypsy_nmea_parser_constructor;
+
+ p_class->received_data = gypsy_nmea_parser_received_data;
+ p_class->get_space_in_buffer = gypsy_nmea_parser_get_space_in_buffer;
+
+ g_type_class_add_private (klass, sizeof (GypsyNmeaParserPrivate));
+}
+
+static void
+gypsy_nmea_parser_init (GypsyNmeaParser *self)
+{
+ GypsyNmeaParserPrivate *priv = GET_PRIVATE (self);
+
+ self->priv = priv;
+}
+
+GypsyParser *
+gypsy_nmea_parser_new (GypsyClient *client)
+{
+ g_return_val_if_fail (GYPSY_IS_CLIENT (client), NULL);
+
+ return (GypsyParser *) g_object_new (GYPSY_TYPE_NMEA_PARSER,
+ "client", client,
+ NULL);
+}
diff --git a/src/gypsy-nmea-parser.h b/src/gypsy-nmea-parser.h
new file mode 100644
index 0000000..e729f28
--- /dev/null
+++ b/src/gypsy-nmea-parser.h
@@ -0,0 +1,51 @@
+#ifndef __GYPSY_NMEA_PARSER_H__
+#define __GYPSY_NMEA_PARSER_H__
+
+#include <gypsy-parser.h>
+#include <gypsy-client.h>
+
+G_BEGIN_DECLS
+
+#define GYPSY_TYPE_NMEA_PARSER \
+ (gypsy_nmea_parser_get_type())
+#define GYPSY_NMEA_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GYPSY_TYPE_NMEA_PARSER, \
+ GypsyNmeaParser))
+#define GYPSY_NMEA_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ GYPSY_TYPE_NMEA_PARSER, \
+ GypsyNmeaParserClass))
+#define GYPSY_IS_NMEA_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GYPSY_TYPE_NMEA_PARSER))
+#define GYPSY_IS_NMEA_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ GYPSY_TYPE_NMEA_PARSER))
+#define GYPSY_NMEA_PARSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GYPSY_TYPE_NMEA_PARSER, \
+ GypsyNmeaParserClass))
+
+typedef struct _GypsyNmeaParserPrivate GypsyNmeaParserPrivate;
+typedef struct _GypsyNmeaParser GypsyNmeaParser;
+typedef struct _GypsyNmeaParserClass GypsyNmeaParserClass;
+
+struct _GypsyNmeaParser
+{
+ GypsyParser parent;
+
+ GypsyNmeaParserPrivate *priv;
+};
+
+struct _GypsyNmeaParserClass
+{
+ GypsyParserClass parent_class;
+};
+
+GType gypsy_nmea_parser_get_type (void) G_GNUC_CONST;
+GypsyParser *gypsy_nmea_parser_new (GypsyClient *client);
+
+G_END_DECLS
+
+#endif /* __GYPSY_NMEA_PARSER_H__ */
diff --git a/src/gypsy-parser.c b/src/gypsy-parser.c
new file mode 100644
index 0000000..4695de7
--- /dev/null
+++ b/src/gypsy-parser.c
@@ -0,0 +1,140 @@
+#include "gypsy-parser.h"
+
+enum {
+ PROP_0,
+ PROP_CLIENT,
+};
+
+struct _GypsyParserPrivate {
+ GypsyClient *client;
+};
+
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GYPSY_TYPE_PARSER, GypsyParserPrivate))
+G_DEFINE_TYPE (GypsyParser, gypsy_parser, G_TYPE_OBJECT);
+
+static void
+gypsy_parser_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (gypsy_parser_parent_class)->finalize (object);
+}
+
+static void
+gypsy_parser_dispose (GObject *object)
+{
+ GypsyParser *self = (GypsyParser *) object;
+ GypsyParserPrivate *priv = self->priv;
+
+ if (priv->client) {
+ g_object_unref (priv->client);
+ priv->client = NULL;
+ }
+
+ G_OBJECT_CLASS (gypsy_parser_parent_class)->dispose (object);
+}
+
+static void
+gypsy_parser_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GypsyParser *self = (GypsyParser *) object;
+ GypsyParserPrivate *priv = self->priv;
+
+ switch (prop_id) {
+ case PROP_CLIENT:
+ priv->client = g_value_dup_object (value);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gypsy_parser_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GypsyParser *self = (GypsyParser *) object;
+ GypsyParserPrivate *priv = self->priv;
+
+ switch (prop_id) {
+ case PROP_CLIENT:
+ g_value_set_object (value, priv->client);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void
+gypsy_parser_class_init (GypsyParserClass *klass)
+{
+ GObjectClass *o_class = (GObjectClass *) klass;
+ GParamSpec *pspec;
+
+ o_class->dispose = gypsy_parser_dispose;
+ o_class->finalize = gypsy_parser_finalize;
+ o_class->set_property = gypsy_parser_set_property;
+ o_class->get_property = gypsy_parser_get_property;
+
+ g_type_class_add_private (klass, sizeof (GypsyParserPrivate));
+
+ pspec = g_param_spec_object ("client", "Client",
+ "The GypsyClient object this parser belongs to",
+ GYPSY_TYPE_CLIENT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (o_class, PROP_CLIENT, pspec);
+}
+
+static void
+gypsy_parser_init (GypsyParser *self)
+{
+ GypsyParserPrivate *priv = GET_PRIVATE (self);
+
+ self->priv = priv;
+}
+
+gboolean
+gypsy_parser_received_data (GypsyParser *parser,
+ const guchar *data,
+ guint length,
+ GError **error)
+{
+ GypsyParserClass *klass = GYPSY_PARSER_GET_CLASS (parser);
+
+ if (klass->received_data == NULL) {
+ g_error ("%s does not implement received_data",
+ G_OBJECT_TYPE_NAME (parser));
+ return FALSE;
+ }
+
+ return klass->received_data (parser, data, length, error);
+}
+
+guint
+gypsy_parser_get_space_in_buffer (GypsyParser *parser)
+{
+ GypsyParserClass *klass = GYPSY_PARSER_GET_CLASS (parser);
+
+ if (klass->get_space_in_buffer == NULL) {
+ g_error ("%s does not implement get_space_in_buffer",
+ G_OBJECT_TYPE_NAME (parser));
+ return FALSE;
+ }
+
+ return klass->get_space_in_buffer (parser);
+}
+
+GypsyClient *
+gypsy_parser_get_client (GypsyParser *parser)
+{
+ g_return_val_if_fail (GYPSY_IS_PARSER (parser), NULL);
+
+ return parser->priv->client;
+}
diff --git a/src/gypsy-parser.h b/src/gypsy-parser.h
new file mode 100644
index 0000000..2c7f58e
--- /dev/null
+++ b/src/gypsy-parser.h
@@ -0,0 +1,57 @@
+#ifndef __GYPSY_PARSER_H__
+#define __GYPSY_PARSER_H__
+
+#include <glib-object.h>
+#include <gypsy-client.h>
+
+G_BEGIN_DECLS
+
+#define GYPSY_TYPE_PARSER \
+ (gypsy_parser_get_type())
+#define GYPSY_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ GYPSY_TYPE_PARSER, \
+ GypsyParser))
+#define GYPSY_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ GYPSY_TYPE_PARSER, \
+ GypsyParserClass))
+#define GYPSY_IS_PARSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ GYPSY_TYPE_PARSER))
+#define GYPSY_IS_PARSER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ GYPSY_TYPE_PARSER))
+#define GYPSY_PARSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ GYPSY_TYPE_PARSER, \
+ GypsyParserClass))
+
+typedef struct _GypsyParserPrivate GypsyParserPrivate;
+typedef struct _GypsyParser GypsyParser;
+typedef struct _GypsyParserClass GypsyParserClass;
+
+struct _GypsyParser
+{
+ GObject parent;
+
+ GypsyParserPrivate *priv;
+};
+
+struct _GypsyParserClass
+{
+ GObjectClass parent_class;
+
+ gboolean (*received_data) (GypsyParser *parser,
+ const guchar *data,
+ gsize length,
+ GError **error);
+ gsize (*get_space_in_buffer) (GypsyParser *parser);
+};
+
+GType gypsy_parser_get_type (void) G_GNUC_CONST;
+GypsyClient *gypsy_parser_get_client (GypsyParser *parser);
+
+G_END_DECLS
+
+#endif /* __GYPSY_PARSER_H__ */