Michael Sheldon's Mobile Stuff

Michael Sheldon (mike at mikeasoft dot com)

October 16, 2010

Libre Droid 1.4 Released
Mike @ 8:58 pm

I’ve just released a new version of Libre Droid onto the Android Market (also downloadable here: http://mikeasoft.com/~mike/libredroid-1.4.apk). The main changes in this release are:

  • Android 2.2 (Froyo) support – Previous versions didn’t work under Froyo due to the new streaming framework (libstagefright) not supporting HTTP redirection, so resolving the final URL is now handled by Libre Droid itself.
  • Ability to add custom tag stations – You’re no longer restricted to the preset tag stations so if you want to listen to female vocals, finger picked guitar, monkeys or anything else our music might be tagged with then you can create a dedicated station for it.
  • Support for playing a user’s loved station – All the music you’ve ever loved in one easy station.
  • Support for the community loved station – The Libre.fm community’s favourite music, this is a selection of all the music that’s ever been loved by any of our users with the most popular tracks playing most frequently.
  • New artwork.
  • Plus a few miscellaneous bug fixes.

New menu page in Libre Droid

Libre Droid playing some music

To access it directly on your phone either scan the QR code below or click on it from within your phone’s browser. Alternatively you can simply search for “Libre Droid” in the Android Market.

QR code for Libre Droid


September 24, 2010

Local map rendering and route finding with libchamplain, Spatialite and Open Street Map
Mike @ 3:07 pm

This tutorial takes you through the steps necessary to build a simple application which is capable of displaying data from OpenStreetMap and find driving routes between two locations without the need for any network services.

The final application will look something like this:

Final application demonstrating local render and routing

And can even be used on the Nokia N900 mobile phone (running Maemo):

Local map rendering and routing on the Nokia n900 mobile phone

Preparation

The libraries you’ll need to install for this are:

  • Memphis – A map renderer. Version 0.2.1 or later.
  • libchamplain – Provides clutter based mapping widgets. You’ll need version 0.7.1 or later for memphis support, when compiling add –enable-memphis to the ./configure parameters.
  • Spatialite – Provides OpenGIS compatible routing (and more) on top of SQLite. This needs to be version 2.4rc3 or later. You’ll also need the spatialite-tools package for importing OSM data

Next we’ll need to acquire some OSM data to work with, for this example we’ll be using a small area around Nantwich in the UK, which can be download here. Data for entire countries can be downloaded from CloudMade, or data from a smaller specific area can be obtained from the OpenStreetMap API.

Displaying the map

#include <gtk/gtk.h>
#include <champlain/champlain.h>
#include <champlain/champlain-memphis-renderer.h>
#include <champlain-gtk/champlain-gtk.h>
#include <clutter-gtk/clutter-gtk.h>

#define MAP "nantwich.osm"
#define RULES "default-rules.xml"

static GtkWidget *window;
static ChamplainMapSource *tile_source = NULL;
static ChamplainMemoryCache *memory_cache = NULL;
static ChamplainView *champlain_view;


static void on_destroy(GtkWidget *widget, gpointer data) {
        gtk_main_quit();
}


static void zoom_to_map_data(ChamplainView *view) {
        ChamplainMemphisRenderer *renderer;
        ChamplainBoundingBox *bbox;
        gdouble lat, lon;

        /* Fetch a reference to the memphis renderer */
        renderer = CHAMPLAIN_MEMPHIS_RENDERER(
                       champlain_map_source_get_renderer(CHAMPLAIN_MAP_SOURCE(tile_source)));
        /* Find what section of the world it covers */
        g_object_get(G_OBJECT(renderer), "bounding-box", &bbox, NULL);
        /* Find the centre of that region */
        champlain_bounding_box_get_center(bbox, &lat, &lon);

        /* Zoom in on that position */
        champlain_view_center_on(CHAMPLAIN_VIEW(view), lat, lon);
        champlain_view_set_zoom_level(CHAMPLAIN_VIEW(view), 14);
}


int main(int argc, char *argv[]) {
        GtkWidget *widget, *vbox, *bbox, *button, *viewport, *label;
        ChamplainMapSource *source;
        ChamplainRenderer *renderer;
        ChamplainMapSourceChain *source_chain;
        ChamplainMapSource *src;
        ChamplainRenderer *image_renderer;
        guint tile_size;

        /* Initialize libraries */
        g_thread_init(NULL);
        gtk_clutter_init(&argc, &argv);

        /* Create a GTK window */
        window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title(GTK_WINDOW(window), "Local rendering and routing tutorial");
        g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(on_destroy), NULL);

        /* Create an embedded champlain GTK widget */
        widget = gtk_champlain_embed_new();
        /* Get the ChamplainView contained within the GTK widget */
        champlain_view = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(widget));

        /* Setup memphis as our tile source. Normally champlain would download tile images
         * from one of a number of online services, by using memphis we can render all
         * the tiles locally without the need for a network connection. */
        ChamplainMapSourceFactory *factory = champlain_map_source_factory_dup_default();
        source = champlain_map_source_factory_create(factory, "memphis-local");
        renderer = champlain_map_source_get_renderer(CHAMPLAIN_MAP_SOURCE(source));
        champlain_memphis_renderer_load_rules(CHAMPLAIN_MEMPHIS_RENDERER(renderer), RULES);
        champlain_file_tile_source_load_map_data(CHAMPLAIN_FILE_TILE_SOURCE(source), MAP);
        tile_source = CHAMPLAIN_MAP_SOURCE(source);
        source_chain = champlain_map_source_chain_new();
        tile_size = champlain_map_source_get_tile_size(tile_source);
        src = champlain_map_source_factory_create_error_source(factory, tile_size);
        champlain_map_source_chain_push(source_chain, src);
        champlain_map_source_chain_push(source_chain, tile_source);

        /* Use a memory cache so that tiles don't have to be re-rendered everytime they're
         * displayed. Another option would be to use a file cache allowing the tiles to be
         * saved across multiple runs of the program (but at the expense of disk space). */
        image_renderer = CHAMPLAIN_RENDERER(champlain_image_renderer_new());
        /* We store up to 200 tiles in the cache */
        memory_cache = champlain_memory_cache_new_full(200, image_renderer); 
        champlain_map_source_chain_push(source_chain, CHAMPLAIN_MAP_SOURCE(memory_cache));
        g_object_set(G_OBJECT(champlain_view), "map-source", source_chain, NULL);

        /* Request a minimum size of 400x300 */
        gtk_widget_set_size_request(widget, 400, 300);

        /* Add our embedded champlain widget to the window */
        gtk_container_add(GTK_CONTAINER(window), widget);

        /* Display the window */
        gtk_widget_show_all(window);

        /* Find our OSM data on the map and show it */
        zoom_to_map_data(champlain_view);

        /* Start the GTK main loop */
        gtk_main();

        return 0;
}

To compile this example (assuming you’ve saved it to ‘champlain-routing.c’) run:

gcc `pkg-config --cflags --libs spatialite champlain-0.8 champlain-gtk-0.8 champlain-memphis-0.8` champlain-routing.c -o champlain-routing

This is a fairly standard champlain program except it uses the memphis renderer to create tile images locally instead of fetching them from an online tile server. In addition to nantwich.osm you’ll also need default-rules.xml, which defines the appearance of the map.

The resulting program will give us a small window that displays our map data without the need for an internet connection:

Displaying the map via libchamplain

Creating routing data

Before we can performing any routing tasks we first need to import our OSM data into spatialite and create a routing network from it.

Importing OSM data into spatialite

spatialite_osm -o nantwich.osm -d nantwich.sqlite -T roads -m

Generating a routing table

spatialite_network -d nantwich.sqlite -T roads -g geometry -c cost -t node_to -f node_from -n name --oneway-fromto oneway_fromto --oneway-tofrom oneway_tofrom -o roads_net_data

spatialite nantwich.sqlite 'CREATE VIRTUAL TABLE "roads_net" USING VirtualNetwork("roads_net_data")'.

Automatic import

I’ve joined these stages together into a small script, available here: populate_spatialite.sh. It takes two parameters, the first being the OSM data to import and the second is the spatialite database to be created:

./populate_spatialite.sh nantwich.osm nantwich.sqlite

Finding and drawing a route

First we define a couple of new global variables for storing references to our route’s polygon, our Spatialite database, and our database’s filename:

#define SPATIAL "nantwich.sqlite"

ChamplainPolygon *route_polygon;
sqlite3 *handle;

Then we initialize Spatialite within our main setup:

int main(int argc, char *argv[]) {
        ...
        g_thread_init(NULL);
        gtk_clutter_init(&argc, &argv);
        spatialite_init(0);
        sqlite3_open_v2(SPATIAL, &handle, SQLITE_OPEN_READONLY, NULL);
        ...
}

Finally we can create a function for drawing the routes between two OSM nodes:

/* Takes two OSM node IDs representing the desired
 * start and end locations and draws the route between
 * them on the map. */
static void draw_route(int from, int to) { 
        ClutterColor color = { 0x00, 0x11, 0x33, 0x99 };
        int ret, n_rows, n_columns, i, points;
        float lat, lon;
        char sql[256];
        char **results;
        char *err_msg = NULL;

        /* Remove any previously displayed route */
        if(route_polygon != NULL) {
                champlain_view_remove_polygon(champlain_view, route_polygon);
        }

        /* Find out how many points there are in the route */
        sprintf(sql, "SELECT NumPoints(Geometry) FROM Roads_net "
           "WHERE nodeFrom = %d AND nodeTo = %d LIMIT 1", from, to);
        ret = sqlite3_get_table(handle, sql, &results, &n_rows, &n_columns, &err_msg);
        if(ret != SQLITE_OK) {
                printf("SQL error: %s\n", err_msg);
                sqlite3_free(err_msg);
                return;
        }

        /* Check to see if we could find a route */
        if (n_rows == 0 || results[1] == 0) {
                printf("No route for %d -> %d\n", from, to);
                return;
        }

        points = atoi(results[1]);

        /* Create a new champlain polygon */
        route_polygon = champlain_polygon_new();

        /* Add each point in the route to the polygon */
        for (i = 1; i < = points; i++) {
                sprintf(sql, "SELECT X(PointN(Geometry, %d)), Y(PointN(Geometry, %d)) "
                   "FROM Roads_net WHERE nodeFrom = %d and nodeTo = %d LIMIT 1", i, i, from, to);
                ret = sqlite3_get_table(handle, sql, &results, &n_rows, &n_columns, &err_msg);                  
                if(ret != SQLITE_OK) {                                          
                        printf("SQL error: %s\n", err_msg);
                        sqlite3_free(err_msg);                                                                          
                        return;                                                                                 
                }
                sscanf(results[2], "%f", &lon);
                sscanf(results[3], "%f", &lat);
                champlain_polygon_append_point(route_polygon, lat, lon);
        }

        /* Set the polygon's display properties */
        champlain_polygon_set_stroke_color(route_polygon, &color);
        champlain_polygon_set_stroke_width(route_polygon, 12.0);

        /* Show the polygon on the map */
        champlain_view_add_polygon(champlain_view, route_polygon);
}

To see what this does we can then try calling draw_route towards the end of our main function:

int main(int argc, char *argv[]) {
        ...
        /* Find our OSM data on the map and show it */
        zoom_to_map_data(champlain_view);

        /* Draw an example route */
        draw_route(291898931, 263396519);

        /* Start the GTK main loop */
        gtk_main();
        ...
}

The main part of this likely to need further explanation are the SQL queries being used. To find a route between locations a query can be performed on the Roads_Net virtual table, to see the sort of results this generates we can load the database into spatialite directly and try some queries.

spatialite nantwich.sqlite

This starts an sqlite session with the spatialite extensions already loaded, so we can just type out our query directly and see the results:

spatialite> SELECT * FROM Roads_Net WHERE NodeFrom = 291898931 AND NodeTo = 263694833;

Algorithm ArcRowid NodeFrom NodeTo Cost Geometry Name
Dijkstra 291898931 263694833 44.5477589762393
Dijkstra 625 291898931 30091396 9.26106988426742 Hawksey Drive
Dijkstra 863 30091396 291797647 8.05233487984465 Peter Destapleigh Way
Dijkstra 864 291797647 30091398 2.10526089940676 Peter Destapleigh Way
Dijkstra 865 30091398 30091397 2.57141142336182 Peter Destapleigh Way
Dijkstra 1100 30091397 30091385 10.1017580189628 Audlem Road
Dijkstra 1101 30091385 291797619 1.21093075722029 Audlem Road
Dijkstra 1102 291797619 30091384 2.78122691192808 Audlem Road
Dijkstra 1994 30091384 263694833 8.46376620124748 Wellington Road

The first row gives us details about the entire route, each following row is a single step along that route including the name of the road we’re travelling on at the time. You’ll notice that the Geometry column appears to be empty, this column is actually accessed through a number of different functions, to get a plain text representation of the geometry for our route we can do:

spatialite> SELECT AsText(Geometry) FROM Roads_Net WHERE NodeFrom = 291898931 AND NodeTo = 263694833 LIMIT 1;
LINESTRING(-2.509696 53.058716, -2.509586 53.058025, -2.512386 53.057934, -2.512593 53.05792, -2.513372 53.057853, -2.514185 53.057543, -2.514511 53.057525, -2.515953 53.057457, -2.51673 53.057505, -2.517495 53.058079, -2.517746 53.058306, -2.518433 53.058776, -2.519147 53.059387, -2.519725 53.06049)

Or to get the X, Y position for a single point along that line we can use the X(), Y() and PointN() functions as we do in our draw_route function. So to find the position of the 4th point we could do:

spatialite> SELECT X(PointN(Geometry, 4)), Y(PointN(Geometry, 4)) FROM Roads_Net WHERE NodeFrom = 291898931 AND NodeTo = 263694833 LIMIT 1;
-2.5125926|53.0579196

From here the program could be easily extended to look up OSM IDs based on their street names (stored in the roads table), to find the nearest OSM ID to a GPS position or to display text based driving instructions, all with just a few SQL queries.

Complete listings

The full source code and data for this tutorial can be downloaded here: champlain-routing.tar.gz. The complete program includes some additional enhancements such as the ability to request routes based on street names (in a simple, but non-robust manner) and zoom level controls.

If you create anything based on this tutorial I’d be very interested to hear about it :).


June 18, 2010

Jokosher on the Nokia n900
Mike @ 1:05 am

I’ve had another stab at getting Jokosher running on the Nokia n900 and I’m getting much closer to something actually usable now, as this screenshot attests:

Jokosher on the Nokia n900

There’s still a number of issues that need resolving before it’s really ready for use (most notably some playback/recording problems and some dialog boxes that are too large for the screen), but it’s getting there. When it’s working fully it could make the n900 a very useful device for portable podcasting, allowing users to record, edit, mix, encode and upload their roaming shows with nothing more than their phone.


June 17, 2010

GStreamer OpenCV plugins on the Nokia n900
Mike @ 3:49 pm

A while back I wrote a few GStreamer plugins that expose OpenCV functionality as GStreamer elements (source code), I haven’t had much time to work on these recently myself, but thankfully a number of other folks have started contributing now. Yesterday Daniil Ivanov kindly packaged gst-opencv for the maemo extras-devel repository, and the n900 performs surprisingly well considering how CPU intensive many of the vision operations performed are.

This first video shows edge detection being performed from the n900’s main camera (whilst simultaneously being encoded):

Example gst-launch line: gst-launch v4l2camsrc device=/dev/video0 ! video/x-raw-yuv,width=480,height=272 ! videorate ! video/x-raw-yuv,framerate=12/1 ! ffmpegcolorspace ! edgedetect ! ffmpegcolorspace ! xvimagesink

This second video shows the faceblur element in action, it detects any faces in the current scene and blurs them out, the frame rate and resolution on this one had to be reduced somewhat due to the complexity of the operation, it looks clearer when performed directly to an xvimagesink rather than attempting to encode at the same time.

Example gst-launch line: gst-launch v4l2camsrc device=/dev/video0 ! video/x-raw-yuv,width=240,height=136 ! videorate ! video/x-raw-yuv,width=240,height=136,framerate=6/1 ! videoscale ! video/x-raw-yuv,width=120,height=68 ! ffmpegcolorspace ! faceblur profile=/home/user/haarcascade_frontalface_default.xml ! ffmpegcolorspace ! xvimagesink

For some more examples of the gst-opencv plugins in action on a normal desktop machine take a look at thiagoss’ blog post and a couple of videos by Alexandre Poltorak (edge detection and face blurring).


August 5, 2009

Libre Droid
Mike @ 7:57 pm

Libre.fm

For the past few months I’ve been working on the Libre.fm music service. It provides Last.fm compatible APIs, allowing you to submit your listening habits and to stream creative commons music.

Over the past week I’ve put together an Android application called Libre Droid, allowing people to stream music from libre.fm directly to their Android mobile phones. Here’s a short video of it in action:

It’s now available for download (for free) from the Android marketplace, scan the image below with your phone (or click it in your phone’s browser) to download it:

Alternatively if you don’t have access to the marketplace (e.g. if you’re using Android on an unofficial platform like the OpenMoko phones) you can download the package directly from: http://mikeasoft.com/~mike/libredroid-1.2.apk


October 12, 2008

FBReader now working on the OpenMoko FreeRunner
Mike @ 7:09 pm

I’ve just spent the day hacking on FBReader to make it work correctly under OpenMoko (OM2008.*). Until now it’s been pretty much unusable due to the GPE version of FBReader expecting you to be using a device that has some physical buttons which then get bound to vital functions like turning the page. The changes I’ve made are as follows:

  • Add scroll forward/backward buttons to the toolbar
  • Add fullscreen mode button to the toolbar (doesn’t have an icon at the moment, it’s the third button from the right)
  • Change fullscreen mode so that it doesn’t hide the toolbar (otherwise there’s no way to get back from fullscreen mode)
  • Switch to using the much prettier blue tango icons
  • Make the line separation larger so the text doesn’t overlap
  • Reduce the font size
  • Change the default colours to match openmoko’s colour scheme better (and so it’s a little easier on the eyes)

And most importantly…

  • Make it so that tapping the sides of the screen turns the book’s pages (left = backwards, right = forwards)

Here’s a screenshot of what it used to look like:

And what it looks like with my patches:

To install it simply run:

opkg install http://mikeasoft.com/~mike/openmoko/enca_1.9-r3_armv4t.ipk http://mikeasoft.com/~mike/openmoko/fbreader_0.8.2a-r7+elleopatches_om-gta02.ipk

For those interested the patch can also be downloaded from http://mikeasoft.com/~mike/openmoko/fbreader-openmoko.patch.

Bonus points for anyone who knows what book I’m testing it with in the screenshots (without googling) ;).


August 16, 2008

Jokosher running on the Neo FreeRunner
Mike @ 11:38 pm

Just for a bit of fun I thought I’d see how well Jokosher runs on the Neo FreeRunner mobile phone. It actually seems to be almost usable, I might see about finally looking at bug #228035 (making Jokosher more usable on small screen devices) to make it a bit easier to use.

Jokosher on the Neo FreeRunner

Once some of the other more important tasks are completed I might also go back and take a look at implementing the Jokosher remote that we’ve had planned for a few years.


July 1, 2007

OpenMoko on a Treo 650
Mike @ 2:40 pm

Following on from yesterday’s post I now have have OpenMoko running on my Treo.

OpenMoko on a Treo 650

When I next have some time to spare I’ll have a go at hacking support for the Treo’s GSM chip into OpenMoko’s gsmd, then it’ll be possible to make phone calls through OpenMoko’s phone application :).


June 30, 2007

Im in ur Treo, Linuxin’ ur phonez…
Mike @ 7:42 pm

With much help from the chaps in #hackndev I managed to get Linux installed on my Treo 650:

Linux Treo 650

You can see a video of it booting and running a few applications here:

http://elleo.blip.tv/file/get/Elleo-LinuxOnATreo650996.ogg (Ogg Theora)

For anyone else wanting to do this, the steps are as follows:

  1. Download and unpack P3t3’s Treo image.
  2. Overwrite the zImage with my version. This is a more up-to-date kernel and has the initramfs built in to the kernel image (because of some buggyness in the MMC driver loading an initrd image from an SD card can be unreliable).
  3. Copy AngstromOPIE_t650-v01.ext2, linux.boot.cfg and zImage to the root of your SD card (make sure you unmount it cleanly after you’ve finished).
  4. Download P3t3’s Treo version of cocoboot and install it to your Treo.
  5. Run Cocoboot on your Treo, replace the line saying “init=/linuxrc” with “mem=32M”, then hit “Boot”.
  6. Enjoy the geeky goodness :).

For people asking about the 680, I don’t own one but you could try using the latest zImage and cocoboot version from: http://www.palmlinux.cz/p3t3/treo680/.

It’s still not amazingly fast, but adding the swap makes it just about usable (see update), and there’s still a lot of work needs doing before it can actually be used as a phone. I believe P3t3 has worked out how to switch the GSM chip in to AT mode (PalmOS uses some odd binary mode for talking to the chip), but I don’t think things have gone much further than that yet.

To get back in to PalmOS just hit the reset button. It shouldn’t touch any of your files in any way, but it might be a good idea to backup first just in case ;).

Update: The speed can be increased greatly by passing the parameter mem=32M in Cocoboot, for some reason without this the kernel only makes use of about 16MB of RAM.

Update 2: Fixed dead links, added links for Treo 680, removed swap info (not needed with the mem=32M parameter).


July 3, 2006

Jokosher On The Nokia 770
Mike @ 6:11 pm

With all the excitement streaming from Jono and Aq on the subject of things to do with the Nokia 770 that was kindly donated to the Jokosher project at GUADEC, I thought I’d have a play around with the Maemo SDK (Maemo is the platform upon which the Nokia 770 operates).

After a little bit of fiddling I managed to get Jokosher up and running:

Jokosher 770

Jokosher 770 - 2

It’s not really in a usable state, I had to strip out all the Cairo stuff due to me not having the python cairo bindings installed, but at least it roughly runs :).


« Previous Page

Powered by WordPress