Back from Vacation

As you may have surmised, I've been on vacation, and a glorious vacation it was. As always with the Boisverts, good food was plentiful. Even with a significantly more active lifestyle than my usual state of existence, I'm sure I gained weight. But as nice as it is, it's always nice to come home and return to a bit more structured schedule. For a full account (with a pile of pictures), I'll point you to Heather's site: The Boisvert Life.

While I was out, I did manage to get a bit of work done on my app for visualizing the data from my Edge. Not as much as I'd hoped, but more than I'd expected. I've got the MotionBased packet importer all built, along with a couple cleanup routines, and all feeding a database. I also set up a new site, www.gpsracr.com (check my way-hip missing 'e'!!!), to host the app. Currently there isn't much of interest, just a report from processing the import queue, but at least for me, having a home for an app all set up is a huge hurdle to cross. Infrastructure surrounding it is pretty sparse, just deploying as a SVN working copy, but it's adequate.

Still contemplating architecture for the app. Definitely going HTML instead of Flex now that a highly dynamic data entry interface isn't needed, though there will be a few Flex bits where appropriate (like charts). ColdSpring for sure. I'm debating between Fusebox 5.5 and my modified Fusebox 3 for the UI framework. I'm thinking that decision is actually a pretty minor one with the improvements in 5.5 that Sean has made, but I haven't dug in real deeply yet.

First task, which is of rather limited interest, is plotting cadence/speed curves to identify my relative gear ratios and more specifically, the overlap between my large and small chainrings. Most of my riding is pretty flat and I like having relatively large rings, but when I get into big climbs (like the friggin' cliff I went up Saturday) I need some lower gears. Pushing 45 rpm at 5 mph for 15 minutes sucks. So I want to quantify what I've got, and then extrapolate what effect different size rings would have. That's a relatively easy task, so I thought it's make a good first foray into actually using the data.

readGZippedText() UDF

I needed to read in some gzipped text files from disk, so I wrote a little UDF to do it for me, and thought I'd share.  It uses Java to do the heavy lifting under the hood:

<cffunction name="readGZippedText" output="false" returntype="string">
  <cfargument name="filename" type="string" required="true" />
  <cfset var data = "" />
  <cfset var stream = "" />
  <cfset var text = "" />
  <cfset var s = structNew() />
  <cffile action="readbinary"
    file="#filename#"
    variable="data" />
  <cfscript>
    stream =
      createObject("java", "java.io.BufferedReader").init(
        createObject("java", "java.io.InputStreamReader").init(
          createObject("java", "java.util.zip.GZIPInputStream").init(
            createObject("java", "java.io.ByteArrayInputStream").init(data)
          )
        )
      )
    ;
    text = createObject("java", "java.lang.StringBuffer").init();
    while (true) {
      s.line = stream.readLine();
      if (NOT structKeyExists(s, "line")) {
        break;
      }
      text.append(s.line).append(chr(10));
    }
  </cfscript>
  <cfreturn text />
</cffunction>

The code isn't very complex, just reads in the data with CFFILE, uses the native Java zip and IO stuff to get a text stream, and then reads that into a StringBuffer (for performance reasons) before returning it.

Schema Tool Update

Small update to the schema tool: it should now support MS SQL Server in addition to MySQL, courtesy of Joshua Frankamp. SQL Server doesn't have an unsigned int type and it requires a column list on INSERT statements. Based on his patch, I've fixed the syntax so that it'll work on both platforms without issue. I'd hope that it will work on pretty much any database (the SQL is very simple), but those are the only two that have been tested.

After a Day on the Edge

After a day of using my new Edge, a couple thoughts:

  • As expected, the distance/speed measurements are slightly low, because it assumes traveling in a straight line between checkpoints, so you lose a little bit of distance going around corners.  Compared with my "normal" computer that uses a wheel sensor, the error is about 1.5%, though I know the wheel sensor is reading slightly high because of tire compression.  So it's not too far off.
  • The heart rate monitor takes a bit to get itself inline with my heart.  The three times I've worn it, it starts out reading really high (235-240 bpm), and then after a little while figures out that it's really supposed to be in the 165 range and stays there for the remainder of the ride.  Have to experiment with some different positions and see if I can get it to work better out of the gate.
  • Having a pile of concurrent displays is great, and since you get two pages of them, it's even better.  Speed, cadence, heart rate, time and distance on the main one, and then a bunch of summaries (avg speed, avg heart rate, etc.) on the second page.
  • The provided visualization/export tools are lame (surprise), but MotionBased.com's upload agent uses a fantastic data format.  It's XML (surprise again), and it's very well structured.  That's going to be the data format that I use for all my stuff, which is kind of ghetto, but until Garmin provides decent native tools, it'll work.  I'm on vacation next week, and hopefully I'll be able to get the import mechanism all built out so the data's in the database, and then from there, the sky's the limit.

My New Toy (and a Bug Fix)

As if I were lacking on the toy front, last night I picked up a Garmin Edge 305 bicycle computer. I'd been looking for a GPS data logger for a while, and this one is pretty much exactly what I'm looking for. In addition to the time/lat/long/elevation track points, it also has a heart rate monitor and a cadence/wheelspeed sensor which is also correlated to the track points. This yields more data than you can shake a stick at.

I haven't yet figured out exactly how I want to approach the problem of extracting, storing, and visualizing all this data, but I've got some ideas. The data is a superset of what I already track with my bike dashboard (by design), so I'll continue to use that as a base, just with the ability to drill down into more detail per ride. Wheeeee!

I also fixed a rounding error in one of the dashboard's view definitions that artificially inflated mileage (and therefore average speed) throughout the app.  I'd misaligned my CAST AS DECIMAL and ROUND calls, and ended up rounding at the wrong spot.  The raw data was uncompromised, only the view's computations based on it were affected.

Google Browser Sync

If you're like me you use Firefox on a bunch of different computers, and often multiple profiles on a single computer.  Most of my settings I want to keep separate, but I wanted to keep my bookmarks synced across all my instances.  Enter Google Browser Sync, a little plugin that'll sync all kinds of settings between Firefox instances, but most importantly bookmarks.  I'm running it on WinXP, OSX, and Linux (CentOS) without issue, and keeping everything all in order.  Only thing missing is being able to set an arbitrary folder as the Bookmarks Toolbar Folder per instance, but that's a very minor annoyance for the savings.

Skinning Sliders Flex

I've been working with a custom Slider extension (which selects a range of elements from a dataProvider, rather than numerics, plus a couple other little things), and got to skinning it this afternoon.  One of the design specs required different images for the two sliders, both pointing towards each other (i.e. mirror images of each other).  Obviously you can't do that with the single 'thumbSkin' style on Slider, so more digging was needed.

A quick look at the API docs shows that SliderThumb extends Button, and therefore inherits it's 'skin' style.  Except that you can't use it; it gets silently ignored.  After some digging around inside the Slider source, I found that the style names get realiased to be their "proper" names, with a 'thumb' prefix, so you actually need to use 'thumbSkin', just like you do on the Slider control itself.  It seems that instead of replicating the style attribute from the Slider to the SliderThumbs (using appropriate names), the whole Slider style definition is copied into the SliderThumbs.  I'd expect this [strange] behaviour to be the same for other auto-created elements (headers in DataGrids, etc.) as well, so watch out, because there is no documentation except digging through the framework source.

Bicycle Dashboard Again

Another relatively minor change: the charts now use a DateTimeAxis instead of a category axis.  This way temporal spacing is preserved, rather than just ordering, and the axis isn't as cluttered, because it can drop labels without affecting the display semantics.

My New New Bike

This weekend I picked up my new new bike, a Fuji Cross Comp.  The other bike just wasn't stiff enough.  To be fair, it was a huge frame and designed for comfort not rigidity.  This one, however, is like a friggin' rock.  I can't bend it at all, no matter how hard I crank on it, let alone enough to interfere with the derailleur.  It's also got better components, bigger wheels, a bit different gearing (2×10 rather than 3×8), and it a kg or so lighter.  It's a cyclocross bike, so it's got small gears and quasi-knobbies on it at the moment.  Should have slicks and bigger rings this week sometime, at which time it's going to fly.

And in case it wasn't obvious, I love riding.  ;)  I haven't done anything seriously active for the past 7 years (I quit swimming in January 2000), and I didn't realize how much I'd missed it.  Riding's nice, because you get fairly constant scenery changes (unlike the bottom of a pool), dynamic exertion from hills and such, and it's just so much fun work your way up a big hill and get to scream down the other side.

Outage Update – It's Actually Over

Turns out when I posted last night about the downtime being over, it was a lie.  I managed to hit a window where things worked pretty well, but there were issues through midday today.  Cari has confirmed that the issue has been addressed and no more problems anticipated.  Hopefully they're right.  ;)