CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pynmea2

Python library for parsing and generating NMEA 0183 protocol messages used in GPS and marine navigation systems

77

1.11x
Overview
Eval results
Files

core-parsing.mddocs/

Core Parsing and Generation

The core parsing system in pynmea2 provides functions and classes for converting between NMEA sentence strings and structured Python objects. This includes parsing incoming NMEA data, generating NMEA strings from data, and validating checksums.

Main Parse Function

def parse(line: str, check: bool = False) -> NMEASentence:
    """
    Parse a string representing a NMEA 0183 sentence.
    
    Args:
        line: NMEA sentence string (leading '$' optional, trailing whitespace ignored)
        check: If True, raise ChecksumError if checksum is missing
    
    Returns:
        NMEASentence object of appropriate subclass
    
    Raises:
        ParseError: If string cannot be parsed
        ChecksumError: If checksum validation fails or missing when check=True
        SentenceTypeError: If sentence type is not recognized
    """

Usage Examples

import pynmea2

# Parse with checksum validation (default)
msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D")
print(type(msg))  # <class 'pynmea2.types.talker.GGA'>

# Parse without requiring checksum
msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000", check=False)

# Parse with strict checksum requirement
try:
    msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000", check=True)
except pynmea2.ChecksumError:
    print("Checksum missing - required when check=True")

Base NMEASentence Class

class NMEASentence:
    """Base class for all NMEA sentence types."""
    
    data: List[str]
    sentence_types: Dict[str, Type['NMEASentence']]
    
    @staticmethod
    def parse(line: str, check: bool = False) -> 'NMEASentence':
        """
        Parse NMEA sentence string into appropriate sentence object.
        
        Args:
            line: NMEA sentence string
            check: Require checksum validation
            
        Returns:
            Appropriate NMEASentence subclass instance
        """
    
    @staticmethod
    def checksum(nmea_str: str) -> int:
        """
        Calculate XOR checksum for NMEA string.
        
        Args:
            nmea_str: NMEA string without '$' or checksum
            
        Returns:
            Calculated checksum as integer
        """
    
    def render(self, checksum: bool = True, dollar: bool = True, newline: Union[bool, str] = False) -> str:
        """
        Render sentence as NMEA string.
        
        Args:
            checksum: Include checksum (*HH format)
            dollar: Include leading '$'
            newline: Include trailing newline (True = \\r\\n, str = custom)
            
        Returns:
            Formatted NMEA sentence string
        """
    
    def identifier(self) -> str:
        """Return string identifier for sentence type (abstract method)."""
    
    def __str__(self) -> str:
        """Return rendered NMEA sentence with checksum and dollar sign."""
    
    def __repr__(self) -> str:
        """Return detailed representation showing field names and values."""

Usage Examples

import pynmea2

# Parse sentence
msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D")

# Generate NMEA string with different formatting options
print(msg.render())                          # $GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D
print(msg.render(checksum=False))           # $GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000
print(msg.render(dollar=False))             # GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D
print(msg.render(newline=True))             # $GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D\r\n

# Calculate checksum manually
nmea_str = "GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000"
checksum = pynmea2.NMEASentence.checksum(nmea_str)
print(f"Checksum: {checksum:02X}")          # Checksum: 6D

# String representations
print(str(msg))                             # $GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D
print(repr(msg))                            # <GGA(timestamp=datetime.time(18, 43, 53), lat='1929.045', ...)>

TalkerSentence Class

class TalkerSentence(NMEASentence):
    """Base class for standard NMEA talker sentences."""
    
    talker: str
    sentence_type: str
    data: List[str]
    
    def __init__(self, talker: str, sentence_type: str, data: List[str]):
        """
        Initialize talker sentence.
        
        Args:
            talker: Two-character talker ID (e.g., 'GP', 'GL', 'GA')
            sentence_type: Three-character sentence type (e.g., 'GGA', 'RMC') 
            data: List of sentence field data as strings
        """
    
    def identifier(self) -> str:
        """Return sentence identifier (e.g., 'GPGGA,')."""

Usage Examples

import pynmea2

# Create GGA sentence manually
gga_data = ['184353.07', '1929.045', 'S', '02410.506', 'E', '1', '04', '2.6', '100.00', 'M', '-33.9', 'M', '', '0000']
msg = pynmea2.GGA('GP', 'GGA', gga_data)

print(msg.talker)          # 'GP'
print(msg.sentence_type)   # 'GGA'
print(msg.identifier())    # 'GPGGA,'
print(str(msg))           # $GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D

ProprietarySentence Class

class ProprietarySentence(NMEASentence):
    """Base class for proprietary manufacturer sentences."""
    
    manufacturer: str
    data: List[str]
    
    def __init__(self, manufacturer: str, data: List[str]):
        """
        Initialize proprietary sentence.
        
        Args:
            manufacturer: Three-character manufacturer code (e.g., 'ASH', 'GRM')
            data: List of sentence field data as strings
        """
    
    def identifier(self) -> str:
        """Return sentence identifier (e.g., 'PASH')."""

Usage Examples

import pynmea2

# Parse proprietary sentence
msg = pynmea2.parse("$PGRME,15.0,M,45.0,M,25.0,M*22")
print(type(msg))          # <class 'pynmea2.types.proprietary.grm.GRME'>
print(msg.manufacturer)   # 'GRM'
print(msg.identifier())   # 'PGRM'

# Create proprietary sentence manually  
data = ['15.0', 'M', '45.0', 'M', '25.0', 'M']
msg = pynmea2.GRME('GRM', data)
print(str(msg))          # $PGRME,15.0,M,45.0,M,25.0,M*22

QuerySentence Class

class QuerySentence(NMEASentence):
    """Class for NMEA query sentences."""
    
    talker: str
    listener: str
    sentence_type: str
    data: List[str]
    
    def __init__(self, talker: str, listener: str, sentence_type: str):
        """
        Initialize query sentence.
        
        Args:
            talker: Two-character querying device ID
            listener: Two-character target device ID
            sentence_type: Three-character requested sentence type
        """
    
    def identifier(self) -> str:
        """Return query identifier (e.g., 'CCGPQ,GGA')."""

Usage Examples

import pynmea2

# Parse query sentence
msg = pynmea2.parse("$CCGPQ,GGA")
print(type(msg))          # <class 'pynmea2.nmea.QuerySentence'>
print(msg.talker)         # 'CC'
print(msg.listener)       # 'GP'
print(msg.sentence_type)  # 'GGA'
print(msg.identifier())   # 'CCGPQ,GGA'

# Create query sentence manually
query = pynmea2.QuerySentence('CC', 'GP', 'GGA')
print(str(query))         # $CCGPQ,GGA*27

Field Access and Dynamic Properties

All sentence objects provide dynamic field access based on the sentence type's field definitions:

import pynmea2

msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D")

# Access fields by name (defined in sentence class)
print(msg.timestamp)      # datetime.time(18, 43, 53, 70000)
print(msg.lat)           # '1929.045'
print(msg.lat_dir)       # 'S'
print(msg.gps_qual)      # '1'
print(msg.altitude)      # 100.0

# Access raw field data by index
print(msg.data[0])       # '184353.07'
print(msg.data[1])       # '1929.045'
print(msg.data[2])       # 'S'

# Set field values (modifies data list)
msg.gps_qual = '2'
print(msg.gps_qual)      # '2'
print(msg.data[5])       # '2'

Error Handling

class ParseError(ValueError):
    """Base exception for NMEA parsing errors."""
    
    def __init__(self, message: str, data: str):
        """Initialize with error message and problematic data."""

class ChecksumError(ParseError):
    """Raised when NMEA sentence checksum validation fails."""

class SentenceTypeError(ParseError):
    """Raised when sentence type is not recognized."""

Error Handling Examples

import pynmea2

# Handle parse errors
try:
    msg = pynmea2.parse("invalid nmea data")
except pynmea2.ParseError as e:
    print(f"Parse error: {e.args[0]}")    # Error message
    print(f"Bad data: {e.args[1]}")       # Raw data that failed

# Handle checksum errors
try:
    msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*FF")
except pynmea2.ChecksumError as e:
    print(f"Checksum error: {e}")

# Handle unknown sentence types
try:
    msg = pynmea2.parse("$GPXYZ,some,unknown,sentence,type*12")
except pynmea2.SentenceTypeError as e:
    print(f"Unknown sentence type: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-pynmea2

docs

core-parsing.md

depth-sonar.md

gps-positioning.md

index.md

navigation-course.md

proprietary-sentences.md

stream-processing.md

utilities.md

wind-weather.md

tile.json