Skip to content

Instantly share code, notes, and snippets.

@javisantana
Last active August 30, 2023 07:07
Show Gist options
  • Save javisantana/1326141 to your computer and use it in GitHub Desktop.
Save javisantana/1326141 to your computer and use it in GitHub Desktop.
NMEA parser in java which does not suck
this software is under the terms of MIT license: http://opensource.org/licenses/MIT
package es.agroguia.model;
import java.util.HashMap;
import java.util.Map;
public class NMEA {
interface SentenceParser {
public boolean parse(String [] tokens, GPSPosition position);
}
// utils
static float Latitude2Decimal(String lat, String NS) {
float med = Float.parseFloat(lat.substring(2))/60.0f;
med += Float.parseFloat(lat.substring(0, 2));
if(NS.startsWith("S")) {
med = -med;
}
return med;
}
static float Longitude2Decimal(String lon, String WE) {
float med = Float.parseFloat(lon.substring(3))/60.0f;
med += Float.parseFloat(lon.substring(0, 3));
if(WE.startsWith("W")) {
med = -med;
}
return med;
}
// parsers
class GPGGA implements SentenceParser {
public boolean parse(String [] tokens, GPSPosition position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[2], tokens[3]);
position.lon = Longitude2Decimal(tokens[4], tokens[5]);
position.quality = Integer.parseInt(tokens[6]);
position.altitude = Float.parseFloat(tokens[9]);
return true;
}
}
class GPGGL implements SentenceParser {
public boolean parse(String [] tokens, GPSPosition position) {
position.lat = Latitude2Decimal(tokens[1], tokens[2]);
position.lon = Longitude2Decimal(tokens[3], tokens[4]);
position.time = Float.parseFloat(tokens[5]);
return true;
}
}
class GPRMC implements SentenceParser {
public boolean parse(String [] tokens, GPSPosition position) {
position.time = Float.parseFloat(tokens[1]);
position.lat = Latitude2Decimal(tokens[3], tokens[4]);
position.lon = Longitude2Decimal(tokens[5], tokens[6]);
position.velocity = Float.parseFloat(tokens[7]);
position.dir = Float.parseFloat(tokens[8]);
return true;
}
}
class GPVTG implements SentenceParser {
public boolean parse(String [] tokens, GPSPosition position) {
position.dir = Float.parseFloat(tokens[3]);
return true;
}
}
class GPRMZ implements SentenceParser {
public boolean parse(String [] tokens, GPSPosition position) {
position.altitude = Float.parseFloat(tokens[1]);
return true;
}
}
public class GPSPosition {
public float time = 0.0f;
public float lat = 0.0f;
public float lon = 0.0f;
public boolean fixed = false;
public int quality = 0;
public float dir = 0.0f;
public float altitude = 0.0f;
public float velocity = 0.0f;
public void updatefix() {
fixed = quality > 0;
}
public String toString() {
return String.format("POSITION: lat: %f, lon: %f, time: %f, Q: %d, dir: %f, alt: %f, vel: %f", lat, lon, time, quality, dir, altitude, velocity);
}
}
GPSPosition position = new GPSPosition();
private static final Map<String, SentenceParser> sentenceParsers = new HashMap<String, SentenceParser>();
public NMEA() {
sentenceParsers.put("GPGGA", new GPGGA());
sentenceParsers.put("GPGGL", new GPGGL());
sentenceParsers.put("GPRMC", new GPRMC());
sentenceParsers.put("GPRMZ", new GPRMZ());
//only really good GPS devices have this sentence but ...
sentenceParsers.put("GPVTG", new GPVTG());
}
public GPSPosition parse(String line) {
if(line.startsWith("$")) {
String nmea = line.substring(1);
String[] tokens = nmea.split(",");
String type = tokens[0];
//TODO check crc
if(sentenceParsers.containsKey(type)) {
sentenceParsers.get(type).parse(tokens, position);
}
position.updatefix();
}
return position;
}
}
Copy link

ghost commented Nov 13, 2016

Hey Mate.
I am very new to java do you have a usage example?

@wb9coy
Copy link

wb9coy commented Apr 28, 2017

Here is some very basic test code:

public class TestMain
{
static String GPGGA_Test = "$GPGGA,211034,4738.9577,N,12220.9329,W,1,09,1.0,10.8,M,-18.4,M,,*42";
public static void main(String [ ] args)
{
NMEA nmea = new NMEA();
nmea.parse(GPGGA_Test);
System.out.println(nmea.position);

}

}

@yunus89
Copy link

yunus89 commented Nov 19, 2017

Hi wb9coy,

Degrees could be a one, two or three digit number. So your implementation of latitude and longitude will be wrong in other scenarios. Correct way would be as follows:

 // utils
 static float Latitude2Decimal(String lat, String NS) {
    int minutesPosition = lat.indexOf('.') - 2;
    float minutes = Float.parseFloat(lat.substring(minutesPosition));
    float decimalDegrees = Float.parseFloat(lat.substring(minutesPosition))/60.0f;

    float degree = Float.parseFloat(lat) - minutes;
    float wholeDegrees = (int)degree/100;

    float latitudeDegrees = wholeDegrees + decimalDegrees;
    if(NS.startsWith("S")) {
        latitudeDegrees = -latitudeDegrees;
    }
    return latitudeDegrees;
}

static float Longitude2Decimal(String lon, String WE) {
    int minutesPosition = lon.indexOf('.') - 2;
    float minutes = Float.parseFloat(lon.substring(minutesPosition));
    float decimalDegrees = Float.parseFloat(lon.substring(minutesPosition))/60.0f;

    float degree = Float.parseFloat(lon) - minutes;
    float wholeDegrees = (int)degree/100;
    
    float longitudeDegrees = wholeDegrees + decimalDegrees;
    if(WE.startsWith("W")) {
        longitudeDegrees = -longitudeDegrees;
    }
    return longitudeDegrees;
}`

@AhmdMnsoor
Copy link

AhmdMnsoor commented Feb 17, 2018

pls. any help as I am also a new java user
why no result shown when running this example

public class TestMain
{
static String GPGGA_Test = "$GPGGA,211034,4738.9577,N,12220.9329,W,1,09,1.0,10.8,M,-18.4,M,,*42";
public static void main(String [ ] args)
{
NMEA nmea = new NMEA();
nmea.parse(GPGGA_Test);
System.out.println(nmea.position);

@Sgw32
Copy link

Sgw32 commented Jun 1, 2021

  • No "try.. catch" in parseFloats - the NMEA receivers in non-established state cause exception and overall fault
  • No "GNGGA" and others - mainly now data is gathered from GPS, GLONASS and Galileo +BeiDou sometimes and reports GNRMC etc not GPRMC and etc.
  • No support for feeding data(incomplete sentences will be lost)
  • does not suck 👍

@robertoricotti
Copy link

Not too much good...Lat and Lon must to be double instead of float

@javisantana
Copy link
Author

thanks for the comments. This code is not meant to do everything but is that simple that you can modify and adapt to your needs. So instead of using a huge library you don't understand, you use a small piece of code you know how it works and everybody can read and understand in 5 minutes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment