• A five part series

    In preparation for a navigation and tracking device with GPS and Display I looked into using the GPS module. The current module is very frugal. It supports just the NMEA $PGGA - essential Fix 3D position - sentence - basically: time, longitude, latitude, and altitude.

    Initially I wanted to use my 20+ years old Magellan marine GPS with RS232 serial connectivity, but I dropped it in favor of a more recent and readily available ublox NEO-6M module, the same as used in Espruinos GPS moduel doc - http://www.espruino.com/GPS.

    Hardware and software connectivity worked right away out of box. It took some time for the GPS module to pickup the satellites, but everything came together nicely.

    From the past I knew that NMEA has also sentences that deliver, bearing, speed, etc... Speed over ground and bearing are part of a the NMEA $PGRMC - essential pvt (position/velocity/time) - sentence.

    Looking into the implementation of the GPS module to extend it with a handler for other sentences - handleGPSLine()-methods - was a bit sobering... to say the least. My basic expectation was to find an externally extensible sentence filter with hooks and registration for other line handlers, or at least a way to override the already implemented, default GPS line handler. Unfortunately, the module is built in a way that ,gives no access what so ever for modifications or extensions. ;(

    Sure, it is not much code there, but what I liked and noticed by rolling up the history of the modules especially in the forum, that quite some enhancements and validation went into the on.data method to extract and process available - (CR) LF delimited = lines.

    Below is some code that is backward compatible and allows even the reuse of the existing default lineHandler for the use in extended mode. (The code is temporarily adjusted to run embedded while elaborating on the extensibility - see respective comments.)

    // ---- beg (modified) GPS module code
    function defaultLineHandler(line, callback) {
      var tag = line.substr(3,3);
      if (tag=="GGA") {
        var d = line.split(",");
        var dlat = d[2].indexOf(".");
        var dlon = d[4].indexOf(".");
        callback({ tag: tag,
          time : d[1].substr(0,2)+":"+d[1].substr(2,2)+":­"+d[1].substr(4,2),
          lat : (parseInt(d[2].substr(0,dlat-2),10)+pars­eFloat(d[2].substr(dlat-2))/60)*(d[3]=="­S"?-1:1),
          lon : (parseInt(d[4].substr(0,dlon-2),10)+pars­eFloat(d[4].substr(dlon-2))/60)*(d[5]=="­W"?-1:1),
          fix : parseInt(d[6],10),
          satellites : parseInt(d[7],10),
          altitude : parseFloat(d[9])
    var connect = function(serial, handler) { // replace with next line for module
    // exports.connect = function(serial, callback) {
      var gps = {line:""}; var lineHandler;
      if (typeof handler == "function") {
        lineHandler = defaultLineHandler;
      } else {
        lineHandler = handler.lineHandler;
        if (handler.includesDefaultLineHandler) {
          handler.defaultLineHandler = defaultLineHandler;
      serial.on('data', function(data) {
        gps.line += data;
        var idx = gps.line.indexOf("\n");
        while (idx>=0) {
          var line = gps.line.substr(0, idx);
          gps.line = gps.line.substr(idx+1);
          lineHandler(line, handler);
          idx = gps.line.indexOf("\n");     
        if (gps.line.length > 80)
          gps.line = gps.line.substr(-80);
      return gps;
    ; var gps = {connect: connect}; // drop line for module
    // ---- end (modified) GPS module code

    Btw, I added the tag into the line handler's return object. It is useful when having only one callback for multiple sentences.

    The basic idea of detecting and operating in the extended mode vs. the basic mode is by passing a handler object vs a handler callback function. Line 23 implements the detection. With this approach backward compatibility is given, and extensibility is opened up.

    I adjusted some names to make the code extensions and the multi purpose use of the existing implementation pattern obvious.

    The existing internal - not exposed - .handleGPSLine() function is renamed to defaultLineHandler, and is assigned to an internal variable named lineHandler in basic mode - when a in the .connect() a callback function is passed as handler (line 24). This variable is then used in the serial's on-data callback for each detected line (line 37). The function expects - as in the original code - a line and the external, custom callback for the line handler for invocation with the data object as built from the line.

    The above setup to retain backward compatibility create constraints/requirements for the handler object in the enhanced mode: the handler object has to have a .lineHandler() method that accepts the same line and callback function arguments as the (existing) .handleGPSLine() - respectively renamed - .defaultLineHandler() function. This handler object's .lineHandler() method is then also assigned to the same internal variable named lineHandler, and corollary, is then also invoked by the serial's on-data callback for each detected line (line 37).

    When the handler object includes a true evaluating .includeDefaultLineHandler property, it is complemented with the .defaultLineHandler() function as same-named method and therefore accessible in the handler object's lineHandler method - as the follow up post will show.

    part 1 of 5 of: GPS Module, Extensions, and u-blox NEO-6M GPS receiver


Avatar for allObjects @allObjects started