Lab 03b: Where Are They Now?

Spring 2019

The questions below are due on Thursday February 21, 2019; 10:00:00 PM.
 

Partners: You have not yet been assigned a partner for this lab.
You are not logged in.

If you are a current student, please Log In for full access to the web site.
Note that this link will take you to an external site (https://oidc.mit.edu) to authenticate, and then you will be redirected back to this page.

Music of My Youth

 

Goals: In this lab, you will learn how to use the GPS module's output to acquire location data, and you'll parse the incoming data stream to find out when it is and where you are.

1) Today's to-do

  • First, we'll hook up the GPS module.
  • Then we'll learn how to decode the standard statements that are sent by GPS modules, and check to see if our reported location is accurate.
  • Finally, we'll create a GPS parser to extract out the relevant data from a GPS statement and display it on our screen.

2) Getting Started

The code for today's Lab can be found HERE. There is only ONE file for today. Download it.

2.1) Adding the GPS Module

Go get a GPS unit up front. We're handing them out using Kerberos, so make sure you get it yourself OR your partner knows your Kerberos so they can get it for you.

We'll be using the ATGM336H-5N GNSS module for GPS signal acquisition in this class. GNSS stands for Global Navigation Satellite System, of which GPS (Global Positioning System) is a type. We usually just refer to the GPS module since that's the system we'll be collecting information from, but technically speaking this is a GNSS module. Other types of GNSS's include Galileo (Europe), and Beidou (Chinese/Southeast Asia). Awesomely, this chipset works with all of these.

The ATGM336H-5N Board. The thing dangling off the top is an Active Antenna. Your antenna cable will actually be long than what is shown.

The GPS board listens to incoming satellite data and calculates its position before communicating it with the ESP32 using the UART protocol (Universal Asynchronous Receive Transmit), which is a two-wire communiation protocol that we talked about a lot in lecture on Tuesday. We won't really talk to the GPS module in this class, but we will definitely listen to it over UART.

We can create a serial object that we'll call gps to handle the timing and low-level operations needed to parse the incoming serial data. This way we'll only have to work with the text it sends over1.

HardwareSerial gps(2);

In the setup function we'll then specify the pins we want to use with it using the following call:

gps.begin(9600,SERIAL_8N1,32,33);

Schematically, we'll need to integrate the GPS unit into our system in the way shown in the below so that it agrees with how we've set up the serial object. Note the communication lines (TX and RX pins on the GPS) link to the IO32 and IO33 pins we specify in our serial object specification above. The GPS unit will also need a ground connection whereever is convenient, and we will power it using 3.3V and ground. Use the schematic below to wire up your system Remember a schematic does not necessarily imply relative physical location of pins. Use the pin numbers and names to identify and determine how to wire things up. Do not assume the pins are spatially where they are in the schematic.

Our GPS board requires four connections, two for power (a ground and a voltage source, and two for communications.

Place the module in this general area of your board since it'll allow easy mounting of the antenna to the top (using a piece of double-sided tape to secure the antenna to the side of the breadboard. Feel free to position the antenna further away so that your longer cable doesn't form an easily-snagged loop.).

We recommend placing your GPS/GNSS module in the top right corner of your board. Grab a piece of double-sided tape to secure your antenna to the side of a breadboard somewhere (note your anteanna's cable will 95% chance be longer since the TAs took all the short ones without thinking about how it would affect you, the students, so you may want to mount your antenna further away so there's less of a "loop" of your wire for things to get snagged on.)

3) Look at the Raw Data

With the GPS unit hooked up, open up lab03b.ino from this week's code distribution, compile, and upload it to the ESP32. As it comes to you, it is an extremely simple script, relative to recent ones we've been doing, but let's take a look at the main part of it below.

The GPS module we use sends data in the following high-level format:

  • Each satellite system's navigation information is reported on a single line. Each line has a unique naming identifier so you know which system it is reporting on ("BDGSA" is the Chinese Beidou system, for example) and/or what information it is trying to convey.
  • Within each line, individual measurements and data are separated by commas.
  • Data is calculated and reported for all satellite systems once per second.

This data in its raw format is rendered via the displayAllGPS function in the skeleton code. Using a globally-declared, large empty char array, if data is available from the GPS serial, it is tacked onto this array until a '\n' is found (that's how we know it is the end of a line). This is carried out by the readBytesUntil function call. The data is then printed to the Serial port.

The GPS receiver produces data even if it doesn't have a fix on the GPS satellites necessary for location estimation, so if you have no signal, you'll just be getting lots of comma-filled lines.

void displayAllGPS(){
  if (gps.available()) {     // If anything comes in Serial1 (pins 0 & 1)
    gps.readBytesUntil('\n', buffer, BUFFER_LENGTH); // read it and send it out Serial (USB)
    Serial.println(buffer);
  }
}

Open up your Serial Terminal. When running, you should see something like this at first:

$GNGGA,,,,,,0,00,25.5,,,,,,*64
$GNGLL,,,,,,V,M*79
$GPGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*02
$BDGSA,A,1,,,,,,,,,,,,,25.5,25.5,25.5*13
$GPGSV,1,1,01,07,,,34*78
$BDGSV,1,1,00*68
$GNRMC,,V,,,,,,,,,,M*4E
$GNVTG,,,,,,,,,M*2D
$GNZDA,,,,,,*56
$GPTXT,01,01,01,ANTENNA OK*35

After maybe one minute (or significantly less depending on signal strength which is pretty damn good in the 6.08 room, if we do say so ourselves), the device will lock onto some satellite(s) signal and will start to update these outputs with more data:

$GNGGA,005132.000,4221.6666,N,07105.5423,W,1,08,1.3,2.4,M,0.0,M,,*6F
$GNGLL,4221.6666,N,07105.5423,W,005132.000,A,A*56
$GPGSA,A,3,01,17,11,07,08,28,30,13,,,,,2.3,1.3,1.9*3A
$BDGSA,A,3,,,,,,,,,,,,,2.3,1.3,1.9*28
$GPGSV,3,1,10,01,63,136,23,07,44,194,26,08,36,058,27,11,77,100,23*71
$GPGSV,3,2,10,13,11,299,17,17,23,253,23,22,11,124,,28,55,313,23*70
$GPGSV,3,3,10,30,58,231,23,34,,,24*46
$BDGSV,1,1,00*68
$GNRMC,005536.000,A,4236.1732,N,07109.0595,W,0.00,0.00,230218,,,A*68
$GNVTG,0.00,T,,M,0.00,N,0.00,K,A*23
$GNZDA,005132.000,23,02,2018,00,00*45
$GPTXT,01,01,01,ANTENNA OK*35

These statements will be repeated over-and-over, as a new set of data is sent from the GPS module to the ESP32 every second.

The different lines mean different things, but in general they are encoding a variety of GNSS information. A number of these lines give out GPS information in a variety of formats, while others work for other systems. For example, the readout below is clearly empty, but that line corresponds to the Beidou system which is only valid over South East Asia currently.

$BDGSA,A,3,,,,,,,,,,,,,1.4,0.8,1.1*2E

Like we said above, we have different types of GPS statements, like GNRMC, and GPGSV, etc. These are all industry-standard statements that virtually all GPS modules report. A nice explanation of all these statements, which are known as NMEA statements, can be found here. The GNGGA statement gives information as to the quality of the fix, while the GNVTG focuses on ground-speed information, etc. (Note that the statements listed in the link above all start with GP, while some of ours start with GN. This appears to be because we are using a GPS unit with support for more than just the GPS system. However, messages with the same last three letters (eg. GPGGA and GNGGA) report the same information, and are formatted the same way.)

In GPS-speak, we use the term "fix" to denote when the GPS unit has obtained enough information to discern, or fix, our location.

The line of interest here is the one that starts $GNRMC. Each following piece of data is delimited by a comma.

  • Time (Greenwich Mean Time) in hhmmss.milliseconds (usually milliseconds round to 0) for this module.
  • V meaning data is Void (invalid) or A meaning data is Active (valid)
  • 4 pieces of geolocation data (which will be empty before your GPS gets a fix)
  • Ground speed (in knots), estimated by the difference in subsequent locations
  • Tracking angle (in degrees), similarly estimated
  • Date in ddmmyy format
  • The rest is a checksum (used for verification of data)

The first time you power up your unit it may take a little bit of time to get a new fix, though in the 6.08 lab, it won't be too bad since we actually have a GPS repeater installed in the room2 You can tell it doesn't have a fix not only because most entries are blank, and not only because the GNRMC statement has a "V" in it, but also because the LED on the bottom of the board will be red and not blink. When it gets a signal, it will start to blink:

The LED under the GPS board will be a steady red when there is no fix (it is hunting) and it will flash at approximately 1Hz when it has a fix and is reporting data.

So, it will start out looking something like this:

$GNRMC,,V,,,,,,,,,,M*4E

After some time, you should start seeing something more akin to:

$GNRMC,005536.000,A,4236.1732,N,07109.0595,W,0.00,0.00,230218,,,A*68

Now you notice that there is an A after the second comma, the time and date are correct (in UTC), and there is location information:

4236.1732,N,07109.0595,W

This means that we are at 42 degrees, 36.1732 minutes North, and 71 degrees, 09.0595 minutes West. You should also notice the the LED on the underside of the module is now blinking.

3.1) GPS Location Representations

An important aspect of GPS location data is that it is routinely presented in three slightly different ways depending on what system you're working in. You need to be aware of the differences, lest you think you're in the wrong place!
  • Degrees, minutes and seconds (DMS). This will often look like 42^{\circ} 36' 10.4''N   71^{\circ} 9' 3.6''W.
  • Degrees and decimal minutes (DDM). In this representation, the whole minutes are listed and the seconds are converted into fractions of a minute, i.e., 21 seconds = 21/60 = 0.35 minutes. This would look like 42 36.1732 N, 71 9.0595 W. This is what we'll use today, and what our GPS gives us.
  • Decimal degrees (DD). Here the minutes range from 0 to 1. It takes the decimal minutes from above and divides that by 60 to range from 0 to 1, i.e., decimal degrees = degrees + decimal minutes/60. The location above would look like 42.0602887 N, 71.150992 W.

3.2) Check Our First Fix

Let's check if our received location is correct. In your browser, open Google Maps. We can't enter the numbers into Google Maps directly, we have to separate out the degrees and decimal minutes, and we have to use a + for N or E, and a - for S or W. For the location information from above, this translates to:

+42 36.1732, -71 09.0595

Type your location into the location box in Google Maps. Where are you?

Checkoff 1:
Show your Google Maps screen and Serial Monitor data to a staff member.

4) Parse the NMEA String

We want to be able to write our location to the LCD, and as such we'll want to write a data parser that will pull out the time, date, latitude, and longitude and send that to the display. You should write a function called extract that when called should populate a set of global variables with the parsed GPS data. These variables include:

  • int lat_deg; //degrees portion of latitude
  • float lat_dm; //latitude decimal/minutes portion
  • char lat_dir; //latitude direction
  • int lon_deg; //longitude degrees portion
  • float lon_dm; //longitude decimal/minutes portion
  • char lon_dir; //longitude direction
  • int year; //year
  • int month; //month
  • int day; //day of month
  • int hour; //hour (24 clock GMT)
  • int minute; //minute
  • int second; //second
  • bool valid; //is the data valid

The tricky thing about this parser, which is new for us, is that the string is continually being read in from the serial port. So we want to monitor the string until we see that it has a complete and correct statement, and then (and only then) take action. If we decode the string too early, it will be incomplete. If we wait too long, the string will be unnecessarily long. We've taken care of most of this part using the following function (to start using it, comment out displayAllGPS in the loop and uncomment the call to extractGNRMC):

void extractGNRMC(){
  while (gps.available()) { //if anything has come in on the serial
    gps.readBytesUntil('\n', buffer, BUFFER_LENGTH); // read the whole line
    char* info = strstr(buffer,"GNRMC");  //use strstr to check if a GNRMC is inside whole line
    if (info!=NULL){  //if GNRMC is NOT present, info is a NULL pointer.  If it is...it points to its location
      Serial.println(buffer); //print only GNRMC data
      extract(buffer);  //send to the extract function to fill in global info variables (your task)
    }
  }
}

readBytesUntil guarantees we'll only be analyzing full lines of readout3 Once we've found a whole line, we check if "GNRMC" is a substring of the whole string using strstr (which is another, awesome function from the string.h library...docs here)

You may (will) find the string.h library useful for this assignment. Think of it as a way to get more practice. In particular the following functions (among others) may prove very useful for what you need to do:

  • strtok: From Exercise 02. A beautiful diagram of its operation is provided here as a review
  • strncpy: Copy a portion of one string into another (BE CAREFUL ABOUT USING THIS...CONSIDER strncat INSTEAD)
  • strncat: use this to concatenate to strings...use this to copy by making one string empty and concatenating on the other.
  • strcmp: Compare two strings
  • strstr: Check if one string is inside another
  • atoi: Convert a proper C-string (null-terminated char array) into an integer
  • atof: Convert a proper C-string (null-terminated char array) into a float

Your task is to complete the missing parts of lab03b.ino so that the LCD displays the date, time, and latitude and longitude (in DDM) when a signal is valid and "No valid Fix" when the signal is not valid. We've taken care of one implementation of how to render the contents of our variables of interest already in the code (just uncomment the lines that display stuff to the LCD), but feel free to change it if you'd like to make it nicer looking.

An example of how one could display their data. This is the default that comes up if you uncomment the rendering lines in the loop function

This lab is good practice for parsing data. We don't want to spend too much time doing this, but it is a very real part of working in a system that has communication between disparate elements involved in it.

Checkoff 2:
Show your working code to a staff member and explain your approach.

The signal that your GPS is able to get in the 6.08 room is abnormally good because of the repeater that we installed. In a regular building, you may not get very good signal so as you use GPS for other projects be aware that you may need to go outside or near a window or in a location with a good Line-of-Sight to the sky in order to let it get a lock!
* strcmp: Compare two strings
 
Footnotes

1Those low-level operations include the timing and counting that go into making sure you syncrhonize your sampling and measure the 10 bits to get each 8-bit data packet. The stuff we talked about in Lecture 02! (click to return to text)

2An antenna on the roof of 38 is channeled down to a small box above the water cooler and then it re-broadcasts the GPS signal for us. (click to return to text)

3This is an extremely versatile function and you may find this useful in lots of future work. (click to return to text)



This page was last updated on Thursday February 21, 2019 at 11:52:48 AM (revision fb127dd).
 
Course Site powered by CAT-SOOP 14.0.4.dev5.
CAT-SOOP is free/libre software, available under the terms
of the GNU Affero General Public License, version 3.
(Download Source Code)
CSS/stryling from the Outboxcraft library Beauter, licensed under MIT
Copyright 2017