Libgypsy Tutorial

By Iain Holmes <iain@gnome.org>

Gypsy is made up of two seperate parts, gypsy-daemon and Libgypsy. The former is a daemon that runs in the background, connects to the GPS devices when told to by a client and parses the GPS data into a format that clients can easily deal with.

Libgypsy is a wrapper library to the Gypsy D-Bus interface providing GObjects that emit signals as the information they represent changes. The advantage of having many objects rather than one large object is that if a program does not care about, for example, the satellite information then it will not be woken up when Gypsy emits a satellite change signal. This allows programs to sleep more often lowering their power consumption.

Using Libgypsy in a program

Libgypsy provides a Pkgconfig file so that checking for libgypsy and getting the correct compiler and linker flags is simple. The following lines added to a program's configure.ac file will set it all up

PKG_CHECK_MODULES(GYPSY, [gypsy]) AC_SUBST(GYPSY_CFLAGS) AC_SUBST(GYPSY_LIBS)

The variables GYPSY_CFLAGS and GYPSY_LIBS then need to be added to the program's Makefiles (or Makefile.am if the program uses automake) to add them in the right place.

Libgypsy's header files are all stored in the gypsy subdirectory so if the program needs to use the GypsyDevice object, it would be included like so:

#include <gypsy/gypsy-device.h>

The same idea applies for all the other header files.

Creating a Simple GPS program with Libgypsy

When using Libgypsy the first thing a program needs to do is to tell the gypsy-daemon which device to connect to. This is done with the GypsyControl object.

GypsyControl is an object that controls the gypsy-daemon, telling it to create or destroy connections to GPS devices. GypsyControl is a singleton object, meaning that there can only be one per program. This object is obtained with the function gypsy_control_get_default. This object is referenced by every call to gypsy_control_get_default and so the program should call g_object_unref on it when the program is finished with it so that gypsy-daemon knows when it is able to clean up.

After obtaining a GypsyControl object, gypsy-daemon needs to be told what GPS unit the program wishes to access. Gypsy can refer to GPS devices by both a device path (like /dev/gps) or by Bluetooth address (such as 00:11:22:33:44). This allows Bluetooth devices to be connected to without having to set up the rfcomm connection first. To do this, the program uses the function gypsy_control_create. This function takes three parameters, the GypsyControl object, the device address and a pointer to a GError. It returns the D-Bus path to the GPS object. This path is important as it is how the program knows how to access the various interfaces that gypsy-daemon provides.

The simple GPS program now looks like this

int main (int argc, char **argv) { GypsyControl *control; GError *error = NULL; char *path; /* Libgypsy uses GObject, so the type system needs to be started up before any Gypsy calls */ g_type_init (); /* Retrieve the default control */ control = gypsy_control_get_default (); /* Tell gypsy-daemon to create a GPS object The GPS to use is passed in as a command line argument */ path = gypsy_control_create (control, argv[1], &error); if (path == NULL) { g_warning ("Error creating client for %s: %s, argv[1], error->message); return 0; } }

Gypsy now knows what GPS is should be listening to, but the program needs to start listening as well. Remember that programs using Gypsy only get woken up when the signals that they have expressed an interest in. Creating objects is the way to show D-Bus which signals the program is interested in. Currently the objects that exist are

A simple GPS just needs to know the location, so a GypsyPosition object is created using the path that gypsy_control_create returned. All the objects are created in this way. The simple GPS program now becomes

. . . GypsyPosition *position; . . . position = gypsy_position_new (path); g_signal_connect (position, "position-changed", G_CALLBACK (position_changed), NULL); . . .

Now, whenever gypsy-daemon emits a PositionChange signal, the position_changed callback will be called. This callback looks like this:

static void position_changed (GypsyPosition *position, GypsyPositionFields fields_set, int timestamp, double latitude, double longitude, double altitude, gpointer userdata) { g_print ("%d: %2f, %2f (%1fm)\n", timestamp, (fields_set & GYPSY_POSITION_FIELDS_LATITUDE) ? latitude :-1.0, (fields_set & GYPSY_POSITION_FIELDS_LONGITUDE) ? longitude :-1.0, (fields_set & GYPSY_POSITION_FIELDS_ALTITUDE) ? altitude : -1.0); }

The fields_set parameter is a bitfield which tells the callback which of the following latitude, longitude and altitude parameters contain valid data. This allows the callback to only print out valid locations by checking which bits are set.

Finally the program just needs to be able to tell gypsy-daemon that it should start the GPS and retrieve a fix. To do this a GypsyDevice is created. GypsyDevice is an object that contains methods and signals related to the actual physical device; being able to start or stop it, whether or not it is connected and the fix status. To start the device:

. . . GypsyDevice *device; . . . device = gypsy_device_new (path); gypsy_device_start (device, &error); if (error != NULL) { g_warning ("Error starting %s: %s", argv[1], error->message); return 0; } . . .

As Libgypsy uses GObject it needs to have a mainloop running so that signals can be processed, so it needs this added before it is complete:

. . . GMainLoop *mainloop; . . . mainloop = g_main_loop_new (NULL, FALSE); g_main_loop_run (mainloop); . . .

Obviously if Libgypsy is being used inside a GTK+ program then it will have a mainloop and the GObject type system will be initialised already.