Skip to main content

How To Use GNSS With The imp006

Take Advantage Of A Cell Modem’s Satellite-driven Location Sensing

This documentation covers a feature not yet available in a production version of impOS and has been provided for early testers only. It is preliminary and subject to change.

Note Using GNSS with the imp006 requires impOS™ 43. As an odd-numbered release, this is an in-development version so must not be used with production devices. If you would like to test impOS 43’s GNSS functionality, please contact us with one or more development device IDs to get them upgraded to impOS 43.

The imp006 in combination with the Quectel BG96 cellular modem and impOS 43 is capable of using the BG96’s integrated GNSS (Global Navigation Satellite System) functionality to determine its geographical location. This is made possible using a new extension to the imp API.

However, for many customers whose cellular application may benefit from precise location data, it is sufficient to use Electric Imp’s BG96 GPS library, which significantly simplifies GNSS setup and usage. For this reason, this guide will focus on implementing the library. Developers who require more control over the GNSS sub-system than the library provides should consult the API documentation.

Fix An Active GNSS Antenna

The BG96 will require an external antenna to pick up signals from navigation satellites. There are many GNSS antennas available. For simplicity and performance, an active patch antenna with integrated ground plane is preferable. We have had good results with the Molex 206640, but there are many alternatives, such as those made by Taoglas.

A Molex active GNSS antenna

If you are testing impOS’ GNSS functionality with the imp006 Breakout Board, there is a µ.FL connector ready to take the antenna. Please note that µ.FL connectors are generally rated only for 30 insertions.

The imp006 Breakout Board GNSS antenna connector

Add The Library To Your Code

The BG96 GPS library is currently available in two forms. The first, version 0.0.1, is intended for use with impOS 42 only, and will be removed when impOS 44 — the public version of impOS 43 — goes into production. Please use the second form of the library, version 0.1.3 or above, which makes use of the new GNSS API and therefore requires impOS 43 or above. It will throw an exception if used on an earlier version of impOS.

The library code to use is available in our BG96_GPS repo in the develop branch. The library must be copied and pasted into your development code; it will be made available for use in a standard #require statement when impOS 44 is released.

Library Usage

The library is not implemented as a class but as a Squirrel table called BG96_GPS. It therefore has no constructor — you simply call BG96_GPSenableGNSS() function, passing in settings as a table. Here’s an example:

// Set the library to show debug output
BG96_GPS.debug = true;

// Attempt to enable GNSS and take readings
BG96_GPS.enableGNSS({
    "maxPosTime": 120,
    "checkFreq":  20,
    "onEnabled":  onEnabled,
    "onLocation": onLocation
});

The setup options table has specific keys. Those used in the example above set, respectively:

  • How long the modem should be allowed to determine the device’s location before an error is reported. The time is in seconds, in the range 1-255.
  • How often fix data is returned, in seconds.
  • A callback function to notify your application that GNSS is enabled (or could not be enabled).
  • A callback function to which location data (or an error) is posted.

When code calls BG96_GPS.enableGNSS() the library attempts to enable the modem’s GNSS sub-system, and then to establish a GNSS session. The GNSS Session is represented by an imp API object through which the library, or your code if it is working directly with the GNSS API, communicates with the GNSS sub-system.

The Library’s Callbacks

Assuming the setup process proceeds smoothly, the library triggers the onEnabled callback, if set. If the onLocation callback has been sett too, the library then begins periodically polling the modem for the device’s location. Let’s look at these callbacks in more detail. First, onEnabled — here’s a basic example:

function onEnabled(error) {
    if (error != null) {
        server.error("BG96 GNSS not enabled (error: " + error + ")");
    } else {
        server.log("BG96 GNSS enabled");
    }
}

This function just reports success or failure, but your application might use this callback to determine whether it needs to perform additional set-up or clean-up tasks. The callback itself has a single parameter which will receive null if GNSS was enabled, or an error message string.

The onLocation callback can be straightforward too:

function onLocation(result) {
    if ("fix" in result) {
        server.log("Got fix:");
        foreach (key, value in result) {
            if (typeof value == "table") {
                foreach (k, v in value) {
                    server.log(" " + k + ": " + v);
                }
            } else {
                server.log(key + ": " + value);
            }
        }
    } else {
        server.error(result.error);
    }
}

Here the code dumps out the fix data to the log, or displays any error reported by the library. This will be [BG96_GPS] GPS fix not available if the library has not yet determined the device’s location.

Note All library-issued errors and status messages are prefixed [BG96_GPS] for easy identification in the device log.

A cold fix can take up to 12.5 minutes if new almanacs and ephemerides need to be fetched, so you may see the [BG96_GPS] GPS fix not available error posted frequently until the code reports that it has valid fix data. When a fix is achieved, the code above will output fix data like this:

2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device] Got fix:
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  cog: 215.05
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  alt: 133.0
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  fixType: 3
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  time: 2020-12-30 11:27:08.100Z
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  numSats: 04
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  lat: 51.56292
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  spkm: 3.1
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  spkn: 1.7
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  lon: -0.14084
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  utc: 112708.100
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  date: 301220
2020-12-30 11:27:15.012 +00:00 "imp006-BK": [Device]  hdop: 2.2
2020-12-30 11:27:33.015 +00:00 "imp006-BK": [Device] [BG96_GPS] Parsing location data

The meaning of most of the data points is clear from the keys by which they are read from the table passed into the onLocation callback. To learn the meanings of all the keys, please see the API documentation.

The format of the latitude (lat) and longitude (lon) readings is (-)dd.ddddd by default. You can change the format to either ddmm.mmmm N/S and ddmm.mmmm E/W, or ddmm.mmmmmm N/S and ddmm.mmmmmm E/W by including the key locMode in your setup options table and provide the value BG96_GNSS_LOCATION_MODE.ZERO or BG96_GNSS_LOCATION_MODE.ONE, respectively. The default is BG96_GNSS_LOCATION_MODE.TWO.

Request location data directly

The onEnabled and onLocation callbacks are optional. If you don’t provide the latter to BG96_GPS.enableGNSS(), the library will not poll for location data; instead you will need to call the library’s BG96_GPS.getLocation() function. This also takes a set of options provided as a table. One of these options is also an onLocation callback to relay location data. By default, BG96_GPS.getLocation() only performs one location look-up, but you can force it to poll periodically by including the key poll into the function’s options table.

Use the mode key and one of the BG96_GNSS_LOCATION_MODE constants listed earlier to set the format of the location data. Include the checkFreq key and a period in seconds to tell the function how frequently to return fresh location data.

Load GNSS Location Assist Data

The BG96 supports Quectel’s gpsOneXTRA feature, which allows you to pre-load the modem’s GNSS sub-system with satellite ephemeris data in order to accelerate the location process. This data is stored in a binary format and is available from Quectel at the following URLs:

The xtra2.bin data supports GPS and Glonass; the xtra3grc.bin data supports GPS, Glonass and BeiDou. Choose the data package that best meets your needs. Both are well under 50KB, so can be easily acquired by your agent:

// Wait for a request from the device
device.on("get.assist.data", function(ignored) {
    // Set up an HTTP request to get the assist data
    local assistDataURL = "http://xtrapath4.izatcloud.net/xtra3grc.bin";
    local request = http.get(assistDataURL);

    // Send the request asynchronously
    request.sendasync(function(response) {
        // We've got a response -- it is good?
        if (response.statuscode == 200) {
            // Yes! Relay the data to the device as a blob
            local adb = blob(response.body.len());
            adb.writestring(response.body);
            device.send("set.assist.data", adb);
        } else {
            // No! 
            server.error("Could not get assist data (Error " + response.statuscode + ")");
        }
    });
});

The device code shown earlier can be used largely as is, but we need to update the last part of the code so that the operation to set up GNSS and begin taking readings occurs in response to assist data being sent through by the agent. The code also needs to tell the agent to request the data:

// Enable debug logging
BG96_GPS.debug = true;

// Handle assist data received from the agent
agent.on("set.assist.data", function(assistData) {
    server.log("Enabling GNSS and getting fix...");

    BG96_GPS.enableGNSS({
        "maxPosTime" : 120,
        "checkFreq" : 20,
        "onLocation" : onLocation,
        "onEnabled": onEnabled,
        "assistData" : assistData
    });
}.bindenv(this));

// Get the agent to request the ephemerides
agent.send("get.assist.data", true);

How to disable GNSS

Depending on your application’s power requirements and how often it needs to determine its current location, you may prefer to power down GNSS once you have location data. You can do this by calling BG96_GPS.disableGNSS(). It returns true or false to indicate whether it was successful or not.

If you need to re-enable GNSS to get a subsequent fix, just call BG96_GPS.enableGNSS() (and BG96_GPS.getLocation(), if used) once more. You can flag the installation of assist data so that you don’t re-request it next time. In this case, add the useAssist key with the value true to the options table passed into BG96_GPS.enableGNSS() in order to make use of the already loaded data.

Assist Data Validity

Ephemeris data is not permanent, so it’s important to check whether the current data is valid. Quectel data downloaded from the URLs listed above has a validity of seven days. At any time, you can call BG96_GPS.isAssistDataValid() to determine assist data validity. This function returns a table which contains the key valid — its value is true or false, depending on whether the data is valid or not.

If the data is valid (valid is true) then the table will also contain the key time with a value that is the remaining validity period in minutes. You can either wait until the data becomes invalid and then get fresh data, or schedule a periodic update.

For example, you might add this code to your onLocation callback:

local validity = BG96_GPS.isAssistDataValid();
if (validity.valid && assistRefreshTimer == null) {
    // Set up a timer to retrieve fresh assist data 31 minutes before it expires
    assistRefreshTimer = imp.wakeup(60 * (validity.time - 31), function() {
        // Get new assist data
        agent.send("get.assist.data", true);
        assistRefreshTimer = null;
    });
}

You also need to add the following global variable:

assistRefreshTimer <- null;