Leuven Orbital

Leuven Ring GPS

Leuven Ring GPS trace

With Iain Sinclair’s rather more epic journey in my mind as well as a book that I last read 22 years ago (thanks Dave), I decided to do what Riddley Walker (Russel Hoban 1980) would call a Fool’s Circel 9wys which is to say a circular trip. The impulse for this was purely cartographic, noticing that Leuven has a rather nicely round ring road, a fact I am keenly aware of because I am here this week, working on Crossing Paths, our new project, which takes off where A Day in the Life left off and will be premiered as part of the Artefact festival in Stuk next year.

Getting the volunteers data back from the GPSs they’ve been going around with, I like to think of Leuven like a clock face. Most of the activity (the volunteers of Stuk are mostly students) seems to take place between 2 and 6 o’clock. Perhaps that’s also temporarily true. We’ll find out.

Bike Loaned from Stuk

Trusty steed

Doing something that would get me thrown out of any psychogeographic association (I’d be in good company there), I set off on a bike loaned from Stuk and starting at 6 o’clock, cycled through a grey windy afternoon. I thoroughly recommend this to anyone who wants to practice psychogeography in Leuven. For one thing, some of the route seems to be illegal if not uncomfortable in the extreme by foot but perfectly doable by bike. This is because the ring road pretty soon (about 8 o’clock) took on the quality of an urban freeway / motorway, the only thing separating me from the intermittently thundering lorries being a painted white stripe between the hard shoulder and the pressed metal crash barriers.

Artois Motorway

Cycling on the freeway. Artois factory in distance

Good old Belgium I thought, passing the Stella Artois factory on a stretch of flyover, it might not be scenic cycling, but at least it is possible at all.

Trying to resist the pull of the roads leading into the city, (what’s the reverse of centripetal force?), I navigated by trying to follow the bleaker, industrial choice, usually to my left which only once tripped me up as at about 2 o’clock, approaching the train station, the ring road changes character completely and transforms into a shopping street leading into town. I was able to adjust, however, and found myself on a different character of urban freeway, much more embedded into the fabric of the city.

City Ring Blurred

A blurry Fool's Circel 9wys

I have to say, fun was had by all but because I was alone, that’s not saying too much.

Posted in Diary, GPS | Tagged , , , , , , , | 7 Comments

Monument. Skin. Excavation (Workshop with Martin Howse at HKW Berlin)

Another great workshop with Martin. It was part of Trampoline’s exhibition we are part of at the Haus der Kulturen der Welt.
I was able to hook up a hard drive read/write head that Martin gave to me after his, Jonnie and Ryan’s recrystallisation workshop and get some nice sounds out of passing it over my laptop.

Hard Drive Coil pickup on laptop

Hard Drive Coil pickup on laptop


This is what it sounded like:
HardDriveCoilLaptop by planbperformance
I also augmented the coil I made with the screwdriver I found in the military air field in Rendlesham Forest on the Psychogeophysics Summit this summer with the piezo and pin that Martin was suggesting we construct as part of the workshop. When we had made our probes and went out to a place Martin had discovered near the Haus der Kulturen der Welt where there is a semi-circle of empty podia.
At the empty podium

At the empty podium


I made some recordings with the Rendlesham coil with piezo and did some dowsing with the brass rods.
Rendlesham coil with piezo and portable amp

Rendlesham coil with piezo and portable amp

Posted in Diary | Tagged , , , , | Comments Off on Monument. Skin. Excavation (Workshop with Martin Howse at HKW Berlin)

Initial results from Crossing Paths in Leuven

crossing paths sky

Crossing Paths


We are very excited to be working with Stuk in Leuven, BE on a project that picks up where A Day in the Life, the Walkers of Birmingham left off.
We’ve just received some data from some initial trials with the volunteers and staff of Stuk – here are the results in Qgis
Leuven Map November 2011 1:60000

Leuven Map November 2011 1:60000


Leuven Map November 2011 1:20000

Leuven Map November 2011 1:20000


Here are some results from the files downloaded and rendered with gpx2png (more about this great script here)
Tracks downloaded on 10 Nov from GPS 7

Tracks downloaded on 10 Nov from GPS 7


Tracks downloaded on 17 Nov from GPS 2

Tracks downloaded on 17 Nov from GPS 2


Tracks downloaded on 17 Nov from GPS 9

Tracks downloaded on 17 Nov from GPS 9

Posted in Diary, GPS, Linux, Walking | Tagged , , , , | Comments Off on Initial results from Crossing Paths in Leuven

A visit to the primary triangulation point of the Prussian spatial reference system

The stairs to the Rauenberg Trig Point park on Marienhöhe
I arranged to meet Martin at Attilastrasse and was half an hour late, much to my shame. The strange park that the TP is in is called Marienhöhe. Martin puts it well. “I can remember previously dreaming of several features of this unusual place and of its act of description here; the terracing, as if in a hall but obviously an exterior location. …”
TP Rauenberg
Reading about Marienhöhe on de.wikipedia.org, it seems it was the site of a sand and gravel quarry, then a rubbish dump which was augmented with rubble from the Second World War and then grassed over and made into a park with a toboggan run and open air theatre.
Rauenberg stone
One of the curious things about this disorientating park was that the Triangulation Point itself was tucked to the side of a large circular clearing made with stone on the summit of the park, under a tree. The focus of the circular clearing was this large stone above with the curious inscription, which, unlike the TP point itself, lacked any further information or interpretation. The wikipedia article doesn’t mention it. It is as if it is not something anyone is proud of.

Denen –
Die Nicht zurückkehrten
Die Ihre Heimat verloren
Die noch in Knechtschaft leben

To those –
Who have not returned
Who have lost their homeland
Who still live in servitude
(my quick translation)

In any case, although Martin was successful with his high and low frequency and entropy logging, I made a big mistake by not realising that it wasn’t enough to have the Garmin Forerunner on, I had to press ‘Start’. I had planned to make some nice heart rate/elevation maps like here. I did get my usual GPS data, however but I was very disappointed.
Rauenberg GPS trace

Posted in Diary, GPS, Walking | Tagged , , , , | Comments Off on A visit to the primary triangulation point of the Prussian spatial reference system

Garmin Forerunner to GPX with heartrate as elevation (altitude)

Cycling Back Home

GPX track in Google Earth with heart rate as elevation/altitude (track height)


It seems like I’ve been working on this for ages but I finally managed to get some kind of output from the Garmin Forerunner 301 that Miles Chalcraft kindly leant me.

  1. Grab GarminTools from here.
  2. Plug in the Forerunner and invoke garmin_save_runs
  3. Convert the binary *.gmn file and redirect it to a funky xml file that needs fixing with garmin_dump [input file.gmn] > [output_file.xml]
  4. #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    #
    # A script to mend the broken xml that garmintools generates and
    # forces the sax parser to choke on a fatal error
    #
    # Problem 1: the whole file needs wrapping in a tag, so I wrap it with
    # 
    #
    # Problem 2: There seems to be some dodgy encoding going on, probably
    # with reading straight from the garmin device, so I just loose the
    # offending line which starts 
    #
    # Daniel Belasco Rogers 2011
    
    
    from sys import exit
    import os
    from shutil import copy
    from optparse import OptionParser
    
    # frivolous variables for stout formatting
    rockdots = "+"
    dotnum = 50
    
    
    def fileBackup(filename):
        """
        save a copy of the file as a backup appending ~ to the filename
        """
        copy(filename, filename + "~")
        print
        print rockdots * dotnum
        print "Wrote backup '%s'" % (filename + "~")
        print rockdots * dotnum
        print
        return
    
    
    def insertTopBottom(filename):
        """
        open the file, read its contents, check for the xml tag and if not
        there, call the backup routine and then insert the opening and
        closing xml tags at the beginning and end of the file
        """
        with open(filename, "r+") as f:
            # read everything in the file into a list of lines
            old = f.readlines()
            # check if this file has already been altered
            if old[0].startswith("< ?xml"):
                print
                print rockdots * dotnum
                print """The file you specified has already seems
    to have been altered
    
    This is not an error
    Script ends here"""
                print rockdots * dotnum
                print
                return
            fileBackup(filename)
            # rewind the file to the beginning
            f.seek(0)
            fileheader = """
    
    """
            f.write(fileheader)
            for line in old:
                # don't copy the dodgy encoding line in the output file
                if not line.startswith(" ")
            print rockdots * dotnum
            print """Re-written the xml file inserting proper headers and
    deleting bad encoding
    Script ends sucessfully here"""
            print rockdots * dotnum
            print
    
    
    def main():
        """
        parse the arguments passed, check for file and call the functions
        that create backup and inserting xml tags
        """
        usage = "usage: %prog /path/to/xml/file.xml"
        parser = OptionParser(usage, version="%prog 1.0")
        (options, args) = parser.parse_args()
        if len(args) != 1:
            parser.error("specify a file path")
        filename = args[0]
        insertTopBottom(filename)
    
    
    if __name__ == '__main__':
        exit(main())
    
  5. Invoke it like python repairForerunnerXML.py path/to/broken/xml.xml This will save the original file as a backup with a tilde extension, should you ever need it again and wrap the file in an xml tag and remove bad encoding lines
  6. #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    #
    # ForerunnerXML2GPX
    #
    #
    # Converts output of garmintools xml from Garmin Forerunner to GPX
    #
    # Replaces elevation (ele) with heartrate (hr)
    #
    # At the moment, the namespace writing at the top of the file is a
    # hack. It seems this is a problem with lxml but could do with further
    # investigation
    #
    # Script now conflates track and track segment tags: each segment is
    # now inside a new track - this is to make Google Earth more compliant
    #
    # Copyright 2011 Daniel Belasco Rogers
    # 
    
    import sys
    import os.path
    from optparse import OptionParser
    import datetime
    try:
        from lxml import etree
    except ImportError:
        print '*' * 48
        print 'This script needs the python module lxml to work'
        print 'lxml is not currently installed'
        print 'You can get it by typing:'
        print 'sudo apt-get install python-lxml'
        print '*' * 48
        sys.exit(2)
    
    
    def convertTimeString(string):
        '''
        take the time string and return a datetime object for
        time delta comparison
        '''
        string = string.replace('T', ' ')
        string = string.replace('-', ' ')
        string = string.replace(':', ' ')
        # Knock off the Z if it's there
        if string[-1] == 'Z':
            string = string[:-1]
        findplus = string.find('+')
        if findplus <> -1:
            # just ignore the tzinfo for now - it doesn't matter for the
            # delta calculation
            string = string[:findplus]
        strlist = string.split()
        return datetime.datetime(int(strlist[0]),
                                 int(strlist[1]),
                                 int(strlist[2]),
                                 int(strlist[3]),
                                 int(strlist[4]),
                                 int(strlist[5]))
    
    
    def main():
        usage = "usage: %prog /path/to/xml/file.gpx"
        optparse = OptionParser(usage, version="%prog 0.1")
        (options, args) = optparse.parse_args()
        if len(args) != 1:
            optparse.error("""
    
    Please define input Forerunner XML file
    """)
        filename = args[0]
        if not(os.path.isfile(filename)):
            print '*' * 48
            print "input file %s does not exist" % filename
            print '*' * 48
            exit(1)
    
        parser = etree.XMLParser(remove_blank_text=True)
        doc = etree.parse(filename, parser)
        inroot = doc.getroot()
        # the namespace hack
        outroot = etree.Element('gpx',
                                attrib={"creator": "ForerunnerXML2GPX.py",
                                        "version": "1.0",
                                        "xmlns": "https://www.topografix.com/GPX/1/0"})
        # this is the bit that doesn't work about namespace at the moment
        #etree.register_namespace("", "https://www.topografix.com/GPX/1/0")
        #outroot = etree.Element('{https://www.topografix.com/GPX/1/0}gpx',
        #                    attrib={'creator': 'gpx-split', "version": "1.0"})
        timed1 = None
        newTrkseg = True
        #trk = etree.SubElement(outroot, 'trk')
        for element in inroot.iter('point'):
            time = element.get('time')
            lat = element.get('lat')
            lon = element.get('lon')
            hr = element.get('hr')
            # start writing new nodes
            # work out if you need to start a new trk element by finding
            # out time delta between previous point and current
            timed2 = convertTimeString(time)
            if timed1 is None: # the first time
                timed1 = timed2
            timegap = (timed2 - timed1)
            if timegap > datetime.timedelta(hours=1):
                newTrkseg = True
            if lat is not None:
                if newTrkseg:
                    trk = etree.SubElement(outroot, 'trk')
                    name = etree.SubElement(trk, 'name')
                    name.text = time
                    trkseg = etree.SubElement(trk, 'trkseg')
                    newTrkseg = False
                trkpt = etree.SubElement(trkseg, 'trkpt')
                trkpt.set('lat', lat)
                trkpt.set('lon', lon)
                ele = etree.SubElement(trkpt, 'ele')
                if hr is None:
                    hr = '0'
                ele.text = hr
                tagtime = etree.SubElement(trkpt, 'time')
                tagtime.text = time
            else:
                newTrkseg = True
            timed1 = convertTimeString(time)
    
        newfilename = os.path.splitext(filename)[0] + '.gpx'
        # why doesn't this always work?
    #     if (os.path.isfile(filename)):
    #         print """
    # file
    # %s
    # already exists, please check and try again
    # """ % newfilename
    #         sys.exit(2)
        print 'writing new file %s' % newfilename
        with open(newfilename, 'w') as f:
            f.write(etree.tostring(outroot,
                           encoding="UTF-8",
                           xml_declaration=True,
                           pretty_print=True))
    
    
    if __name__ == '__main__':
        sys.exit(main())
    
  7. Invoke thusly: python ForerunnerXML2GPX.py path/to/fixed/xml.xml This produces a GPX file with the same name as the xml file. It writes heart rate to the tag, effectively mapping heart rate onto altitude for an interesting plot. When the Forerunner hasn’t recorded heart rate, only position, the elevation is set to 0. If the Forerunner has only captured heart rate and no position data, it is skipped (obviously)
  8. Open GPX file in Google Earth and play with the settings (won’t double up their copious help here)
Posted in Code, GPS, Python, Software, Walking | Tagged , , , , , , , | Comments Off on Garmin Forerunner to GPX with heartrate as elevation (altitude)