Skip to content

change to ruff #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 14, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

.py text eol=lf
.rst text eol=lf
.txt text eol=lf
.yaml text eol=lf
.toml text eol=lf
.license text eol=lf
.md text eol=lf
43 changes: 11 additions & 32 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,42 +1,21 @@
# SPDX-FileCopyrightText: 2020 Diego Elio Pettenò
# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

repos:
- repo: https://github.com/python/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/fsfe/reuse-tool
rev: v1.1.2
hooks:
- id: reuse
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v2.17.4
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.3.4
hooks:
- id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
description: Run pylint rules on "examples/*.py" files
types: [python]
files: "^examples/"
args:
- --disable=missing-docstring,invalid-name,consider-using-f-string,duplicate-code
- id: pylint
name: pylint (test code)
description: Run pylint rules on "tests/*.py" files
types: [python]
files: "^tests/"
args:
- --disable=missing-docstring,consider-using-f-string,duplicate-code
- id: ruff-format
- id: ruff
args: ["--fix"]
- repo: https://github.com/fsfe/reuse-tool
rev: v3.0.1
hooks:
- id: reuse
399 changes: 0 additions & 399 deletions .pylintrc

This file was deleted.

6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@ Introduction
:target: https://github.com/adafruit/Adafruit_CircuitPython_GPS/actions/
:alt: Build Status

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/psf/black
:alt: Code Style: Black
.. image:: https://img.shields.io/endpoint?url=https://github.com/raw/astral-sh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Code Style: Ruff

GPS parsing module. Can send commands to, and parse simple NMEA data sentences
from serial and I2C GPS modules to read latitude, longitude, and more.
67 changes: 24 additions & 43 deletions adafruit_gps.py
Original file line number Diff line number Diff line change
@@ -26,14 +26,17 @@
https://github.com/adafruit/circuitpython/releases
"""

import time

from micropython import const

try:
from typing import Optional, Tuple, List
from typing_extensions import Literal
from typing import List, Optional, Tuple

from busio import I2C, UART
from circuitpython_typing import ReadableBuffer
from busio import UART, I2C
from typing_extensions import Literal
except ImportError:
pass

@@ -103,19 +106,19 @@ def _parse_degrees(nmea_data: str) -> int:


def _parse_int(nmea_data: str) -> int:
if nmea_data is None or nmea_data == "":
if nmea_data is None or not nmea_data:
return None
return int(nmea_data)


def _parse_float(nmea_data: str) -> float:
if nmea_data is None or nmea_data == "":
if nmea_data is None or not nmea_data:
return None
return float(nmea_data)


def _parse_str(nmea_data: str) -> str:
if nmea_data is None or nmea_data == "":
if nmea_data is None or not nmea_data:
return None
return str(nmea_data)

@@ -162,7 +165,6 @@ def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]:
"""Parse sentence data for the specified sentence type and
return a list of parameters in the correct format, or return None.
"""
# pylint: disable=too-many-branches

if not _ST_MIN <= sentence_type <= _ST_MAX:
# The sentence_type is unknown
@@ -239,14 +241,11 @@ def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]:
return params


# pylint: disable-msg=too-many-instance-attributes
class GPS:
"""GPS parsing module. Can parse simple NMEA data sentences from serial
GPS modules to read latitude, longitude, and more.
"""

# lint warning about too many statements disabled
# pylint: disable-msg=R0915
def __init__(self, uart: UART, debug: bool = False) -> None:
self._uart = uart
# Initialize null starting values for GPS attributes.
@@ -362,7 +361,7 @@ def update(self) -> bool:
# GP - GPS
# GQ - QZSS
# GN - GNSS / More than one of the above
if talker not in (b"GA", b"GB", b"GI", b"GL", b"GP", b"GQ", b"GN"):
if talker not in {b"GA", b"GB", b"GI", b"GL", b"GP", b"GQ", b"GN"}:
# It's not a known GNSS source of data
# Assume it's a valid packet anyway
return True
@@ -397,7 +396,7 @@ def send_command(self, command: bytes, add_checksum: bool = True) -> None:
for char in command:
checksum ^= char
self.write(b"*")
self.write(bytes("{:02x}".format(checksum).upper(), "ascii"))
self.write(bytes(f"{checksum:02x}".upper(), "ascii"))
self.write(b"\r\n")

@property
@@ -444,7 +443,6 @@ def readline(self) -> Optional[bytes]:

def _read_sentence(self) -> Optional[str]:
# Parse any NMEA sentence that is available.
# pylint: disable=len-as-condition
# This needs to be refactored when it can be tested.

# Only continue if we have at least 11 bytes in the input buffer
@@ -508,9 +506,7 @@ def _update_timestamp_utc(self, time_utc: str, date: Optional[str] = None) -> No
month = int(date[2:4])
year = 2000 + int(date[4:6])

self.timestamp_utc = time.struct_time(
(year, month, day, hours, mins, secs, 0, 0, -1)
)
self.timestamp_utc = time.struct_time((year, month, day, hours, mins, secs, 0, 0, -1))

def _parse_vtg(self, data: List[str]) -> bool:
# VTG - Course Over Ground and Ground Speed
@@ -547,15 +543,11 @@ def _parse_gll(self, data: List[str]) -> bool:

# Latitude
self.latitude = _read_degrees(parsed_data, 0, "s")
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(
data=data, index=0, neg="s"
)
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(data=data, index=0, neg="s")

# Longitude
self.longitude = _read_degrees(parsed_data, 2, "w")
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(
data=data, index=2, neg="w"
)
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(data=data, index=2, neg="w")

# UTC time of position
self._update_timestamp_utc(parsed_data[4])
@@ -571,7 +563,7 @@ def _parse_gll(self, data: List[str]) -> bool:
def _parse_rmc(self, data: List[str]) -> bool:
# RMC - Recommended Minimum Navigation Information

if data is None or len(data) not in (12, 13):
if data is None or len(data) not in {12, 13}:
return False # Unexpected number of params.
parsed_data = _parse_data({12: _RMC, 13: _RMC_4_1}[len(data)], data)
if parsed_data is None:
@@ -591,15 +583,11 @@ def _parse_rmc(self, data: List[str]) -> bool:

# Latitude
self.latitude = _read_degrees(parsed_data, 2, "s")
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(
data=data, index=2, neg="s"
)
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(data=data, index=2, neg="s")

# Longitude
self.longitude = _read_degrees(parsed_data, 4, "w")
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(
data=data, index=4, neg="w"
)
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(data=data, index=4, neg="w")

# Speed over ground, knots
self.speed_knots = parsed_data[6]
@@ -633,15 +621,11 @@ def _parse_gga(self, data: List[str]) -> bool:

# Latitude
self.latitude = _read_degrees(parsed_data, 1, "s")
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(
data=data, index=3, neg="w"
)
self.longitude_degrees, self.longitude_minutes = _read_deg_mins(data=data, index=3, neg="w")

# Longitude
self.longitude = _read_degrees(parsed_data, 3, "w")
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(
data=data, index=1, neg="s"
)
self.latitude_degrees, self.latitude_minutes = _read_deg_mins(data=data, index=1, neg="s")

# GPS quality indicator
self.fix_quality = parsed_data[5]
@@ -668,7 +652,7 @@ def _parse_gga(self, data: List[str]) -> bool:
def _parse_gsa(self, talker: bytes, data: List[str]) -> bool:
# GSA - GPS DOP and active satellites

if data is None or len(data) not in (17, 18):
if data is None or len(data) not in {17, 18}:
return False # Unexpected number of params.
if len(data) == 17:
data = _parse_data(_GSA, data)
@@ -689,7 +673,7 @@ def _parse_gsa(self, talker: bytes, data: List[str]) -> bool:
satlist = list(filter(None, data[2:-4]))
self.sat_prns = []
for sat in satlist:
self.sat_prns.append("{}{}".format(talker, sat))
self.sat_prns.append(f"{talker}{sat}")

# PDOP, dilution of precision
self.pdop = _parse_float(data[14])
@@ -706,9 +690,8 @@ def _parse_gsa(self, talker: bytes, data: List[str]) -> bool:

def _parse_gsv(self, talker: bytes, data: List[str]) -> bool:
# GSV - Satellites in view
# pylint: disable=too-many-branches

if data is None or len(data) not in (7, 11, 15, 19):
if data is None or len(data) not in {7, 11, 15, 19}:
return False # Unexpected number of params.
data = _parse_data(
{7: _GSV7, 11: _GSV11, 15: _GSV15, 19: _GSV19}[len(data)],
@@ -734,7 +717,7 @@ def _parse_gsv(self, talker: bytes, data: List[str]) -> bool:
j = i * 4
value = (
# Satellite number
"{}{}".format(talker, sat_tup[0 + j]),
f"{talker}{sat_tup[0 + j]}",
# Elevation in degrees
sat_tup[1 + j],
# Azimuth in degrees
@@ -789,9 +772,7 @@ def __init__(
debug: bool = False,
timeout: float = 5.0,
) -> None:
from adafruit_bus_device import ( # pylint: disable=import-outside-toplevel
i2c_device,
)
from adafruit_bus_device import i2c_device # noqa: PLC0415

super().__init__(None, debug) # init the parent with no UART
self._i2c = i2c_device.I2CDevice(i2c_bus, address)
6 changes: 6 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@

.. If you created a package, create one automodule per module in the package.
.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
.. use this format as the module name: "adafruit_foo.foo"
API Reference
#############

.. automodule:: adafruit_gps
:members:
8 changes: 2 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# -*- coding: utf-8 -*-

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
#
# SPDX-License-Identifier: MIT

import datetime
import os
import sys
import datetime

sys.path.insert(0, os.path.abspath(".."))

@@ -40,9 +38,7 @@
creation_year = "2017"
current_year = str(datetime.datetime.now().year)
year_duration = (
current_year
if current_year == creation_year
else creation_year + " - " + current_year
current_year if current_year == creation_year else creation_year + " - " + current_year
)
copyright = year_duration + " Tony DiCola, James Carr"
author = "Tony DiCola, James Carr"
1 change: 1 addition & 0 deletions examples/gps_datalogging.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@

import board
import busio

import adafruit_gps

# Path to the file to log GPS data. By default this will be appended to
1 change: 1 addition & 0 deletions examples/gps_displayio_simpletest.py
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
# SPDX-License-Identifier: MIT

import time

import board
from adafruit_display_text.label import Label
from displayio import Group
1 change: 1 addition & 0 deletions examples/gps_echotest.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
# Will print NMEA sentences received from the GPS, great for testing connection
# Uses the GPS to send some commands, then reads directly from the GPS
import time

import board
import busio

1 change: 1 addition & 0 deletions examples/gps_satellitefix.py
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
# * GSV - Satellites in view

import time

import board

import adafruit_gps
35 changes: 14 additions & 21 deletions examples/gps_simpletest.py
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
# Will wait for a fix and print a message every second with the current location
# and other details.
import time

import board
import busio

@@ -74,7 +75,7 @@
# Print out details about the fix like location, date, etc.
print("=" * 40) # Print a separator line.
print(
"Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}".format(
"Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}".format( # noqa: UP032
gps.timestamp_utc.tm_mon, # Grab parts of the time from the
gps.timestamp_utc.tm_mday, # struct_time object that holds
gps.timestamp_utc.tm_year, # the fix time. Note you might
@@ -83,32 +84,24 @@
gps.timestamp_utc.tm_sec,
)
)
print("Latitude: {0:.6f} degrees".format(gps.latitude))
print("Longitude: {0:.6f} degrees".format(gps.longitude))
print(
"Precise Latitude: {} degs, {:2.4f} mins".format(
gps.latitude_degrees, gps.latitude_minutes
)
)
print(
"Precise Longitude: {} degs, {:2.4f} mins".format(
gps.longitude_degrees, gps.longitude_minutes
)
)
print("Fix quality: {}".format(gps.fix_quality))
print(f"Latitude: {gps.latitude:.6f} degrees")
print(f"Longitude: {gps.longitude:.6f} degrees")
print(f"Precise Latitude: {gps.latitude_degrees} degs, {gps.latitude_minutes:2.4f} mins")
print(f"Precise Longitude: {gps.longitude_degrees} degs, {gps.longitude_minutes:2.4f} mins")
print(f"Fix quality: {gps.fix_quality}")
# Some attributes beyond latitude, longitude and timestamp are optional
# and might not be present. Check if they're None before trying to use!
if gps.satellites is not None:
print("# satellites: {}".format(gps.satellites))
print(f"# satellites: {gps.satellites}")
if gps.altitude_m is not None:
print("Altitude: {} meters".format(gps.altitude_m))
print(f"Altitude: {gps.altitude_m} meters")
if gps.speed_knots is not None:
print("Speed: {} knots".format(gps.speed_knots))
print(f"Speed: {gps.speed_knots} knots")
if gps.speed_kmh is not None:
print("Speed: {} km/h".format(gps.speed_kmh))
print(f"Speed: {gps.speed_kmh} km/h")
if gps.track_angle_deg is not None:
print("Track angle: {} degrees".format(gps.track_angle_deg))
print(f"Track angle: {gps.track_angle_deg} degrees")
if gps.horizontal_dilution is not None:
print("Horizontal dilution: {}".format(gps.horizontal_dilution))
print(f"Horizontal dilution: {gps.horizontal_dilution}")
if gps.height_geoid is not None:
print("Height geoid: {} meters".format(gps.height_geoid))
print(f"Height geoid: {gps.height_geoid} meters")
19 changes: 8 additions & 11 deletions examples/gps_time_source.py
Original file line number Diff line number Diff line change
@@ -6,9 +6,11 @@
# time while there is powersource (ie coin cell battery)

import time

import board
import busio
import rtc

import adafruit_gps

uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10)
@@ -26,14 +28,9 @@


def _format_datetime(datetime):
return "{:02}/{:02}/{} {:02}:{:02}:{:02}".format(
datetime.tm_mon,
datetime.tm_mday,
datetime.tm_year,
datetime.tm_hour,
datetime.tm_min,
datetime.tm_sec,
)
date_part = f"{datetime.tm_mon:02}/{datetime.tm_mday:02}/{datetime.tm_year}"
time_part = f"{datetime.tm_hour:02}:{datetime.tm_min:02}:{datetime.tm_sec:02}"
return f"{date_part} {time_part}"


last_print = time.monotonic()
@@ -47,12 +44,12 @@ def _format_datetime(datetime):
print("No time data from GPS yet")
continue
# Time & date from GPS informations
print("Fix timestamp: {}".format(_format_datetime(gps.timestamp_utc)))
print(f"Fix timestamp: {_format_datetime(gps.timestamp_utc)}")

# Time & date from internal RTC
print("RTC timestamp: {}".format(_format_datetime(the_rtc.datetime)))
print(f"RTC timestamp: {_format_datetime(the_rtc.datetime)}")

# Time & date from time.localtime() function
local_time = time.localtime()

print("Local time: {}".format(_format_datetime(local_time)))
print(f"Local time: {_format_datetime(local_time)}")
108 changes: 108 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT

target-version = "py38"
line-length = 100

[lint]
preview = true
select = ["I", "PL", "UP"]

extend-select = [
"D419", # empty-docstring
"E501", # line-too-long
"W291", # trailing-whitespace
"PLC0414", # useless-import-alias
"PLC2401", # non-ascii-name
"PLC2801", # unnecessary-dunder-call
"PLC3002", # unnecessary-direct-lambda-call
"E999", # syntax-error
"PLE0101", # return-in-init
"F706", # return-outside-function
"F704", # yield-outside-function
"PLE0116", # continue-in-finally
"PLE0117", # nonlocal-without-binding
"PLE0241", # duplicate-bases
"PLE0302", # unexpected-special-method-signature
"PLE0604", # invalid-all-object
"PLE0605", # invalid-all-format
"PLE0643", # potential-index-error
"PLE0704", # misplaced-bare-raise
"PLE1141", # dict-iter-missing-items
"PLE1142", # await-outside-async
"PLE1205", # logging-too-many-args
"PLE1206", # logging-too-few-args
"PLE1307", # bad-string-format-type
"PLE1310", # bad-str-strip-call
"PLE1507", # invalid-envvar-value
"PLE2502", # bidirectional-unicode
"PLE2510", # invalid-character-backspace
"PLE2512", # invalid-character-sub
"PLE2513", # invalid-character-esc
"PLE2514", # invalid-character-nul
"PLE2515", # invalid-character-zero-width-space
"PLR0124", # comparison-with-itself
"PLR0202", # no-classmethod-decorator
"PLR0203", # no-staticmethod-decorator
"UP004", # useless-object-inheritance
"PLR0206", # property-with-parameters
"PLR0904", # too-many-public-methods
"PLR0911", # too-many-return-statements
"PLR0912", # too-many-branches
"PLR0913", # too-many-arguments
"PLR0914", # too-many-locals
"PLR0915", # too-many-statements
"PLR0916", # too-many-boolean-expressions
"PLR1702", # too-many-nested-blocks
"PLR1704", # redefined-argument-from-local
"PLR1711", # useless-return
"C416", # unnecessary-comprehension
"PLR1733", # unnecessary-dict-index-lookup
"PLR1736", # unnecessary-list-index-lookup

# ruff reports this rule is unstable
#"PLR6301", # no-self-use

"PLW0108", # unnecessary-lambda
"PLW0120", # useless-else-on-loop
"PLW0127", # self-assigning-variable
"PLW0129", # assert-on-string-literal
"B033", # duplicate-value
"PLW0131", # named-expr-without-context
"PLW0245", # super-without-brackets
"PLW0406", # import-self
"PLW0602", # global-variable-not-assigned
"PLW0603", # global-statement
"PLW0604", # global-at-module-level

# fails on the try: import typing used by libraries
#"F401", # unused-import

"F841", # unused-variable
"E722", # bare-except
"PLW0711", # binary-op-exception
"PLW1501", # bad-open-mode
"PLW1508", # invalid-envvar-default
"PLW1509", # subprocess-popen-preexec-fn
"PLW2101", # useless-with-lock
"PLW3301", # nested-min-max
]

ignore = [
"PLR2004", # magic-value-comparison
"UP030", # format literals
"PLW1514", # unspecified-encoding
"PLR0913", # too-many-arguments
"PLR0915", # too-many-statements
"PLR0917", # too-many-positional-arguments
"PLR0904", # too-many-public-methods
"PLR0912", # too-many-branches
"PLR0916", # too-many-boolean-expressions
]

[lint.per-file-ignores]
"tests/adafruit_gps_test.py" = ["PLC2701"]

[format]
line-ending = "lf"
60 changes: 24 additions & 36 deletions tests/adafruit_gps_test.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
# pylint: disable=missing-function-docstring,missing-module-docstring,invalid-name,protected-access,no-self-use,missing-class-docstring

# SPDX-FileCopyrightText: 2021 Jonas Kittner
#
# SPDX-License-Identifier: MIT
import time
from unittest import mock
from freezegun import freeze_time

import pytest
from freezegun import freeze_time

from adafruit_gps import _parse_degrees
from adafruit_gps import _parse_int
from adafruit_gps import _parse_float
from adafruit_gps import _parse_str
from adafruit_gps import _read_degrees
from adafruit_gps import _parse_talker
from adafruit_gps import _parse_data
from adafruit_gps import _read_deg_mins
from adafruit_gps import GPS
from adafruit_gps import (
GPS,
_parse_data,
_parse_degrees,
_parse_float,
_parse_int,
_parse_str,
_parse_talker,
_read_deg_mins,
_read_degrees,
)


@pytest.mark.parametrize(
@@ -115,7 +116,7 @@ def test_parse_data_unexpected_parameter_type():
class UartMock:
"""mocking the UART connection an its methods"""

def write(self, bytestr):
def write(self, bytestr): # noqa: PLR6301
print(bytestr, end="")

@property
@@ -202,12 +203,8 @@ def test_GPS_update_rmc_no_magnetic_variation():


def test_GPS_update_rmc_fix_is_set():
r_valid = (
b"$GPRMC,215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*6A\r\n"
)
r_invalid = (
b"$GPRMC,215032.086,V,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*7D\r\n"
)
r_valid = b"$GPRMC,215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*6A\r\n"
r_invalid = b"$GPRMC,215032.086,V,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*7D\r\n"
with mock.patch.object(GPS, "readline", return_value=r_valid):
gps = GPS(uart=UartMock())
assert gps.update()
@@ -221,9 +218,7 @@ def test_GPS_update_rmc_fix_is_set():


def test_GPS_update_rmc_fix_is_set_new():
r_valid = (
b"$GPRMC,215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*6A\r\n"
)
r_valid = b"$GPRMC,215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A*6A\r\n"
r_invalid = b"$GPRMC,215032.086,V,ABC,N,00123.12345,E,0.45,56.35,021021,,,A*1B\r\n"
with mock.patch.object(GPS, "readline", return_value=r_valid):
gps = GPS(uart=UartMock())
@@ -290,11 +285,8 @@ def test_GPS_update_rmc_debug_shows_sentence(capsys):
gps = GPS(uart=UartMock(), debug=True)
assert gps.update()
out, err = capsys.readouterr()
assert err == ""
assert (
out
== "('GPRMC', '215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A')\n"
)
assert not err
assert out == "('GPRMC', '215032.086,A,1234.5678,N,00123.12345,E,0.45,56.35,021021,,,A')\n"


def test_GPS_update_data_type_too_short():
@@ -308,15 +300,15 @@ def test_GPS_send_command_with_checksum(capsys):
gps = GPS(uart=UartMock())
gps.send_command(command=b"$PMTK001,314,3\r\n", add_checksum=True)
out, err = capsys.readouterr()
assert err == ""
assert not err
assert out == ("b'$'" "b'$PMTK001,314,3\\r\\n'" "b'*'" "b'15'" "b'\\r\\n'")


def test_GPS_send_command_without_checksum(capsys):
gps = GPS(uart=UartMock())
gps.send_command(command=b"$PMTK001,314,3\r\n", add_checksum=False)
out, err = capsys.readouterr()
assert err == ""
assert not err
assert out == ("b'$'" "b'$PMTK001,314,3\\r\\n'" "b'\\r\\n'")


@@ -391,21 +383,17 @@ def test_GPS_update_from_GGA():
assert gps.has_3d_fix is False
assert gps.datetime == exp_time
assert (
gps._raw_sentence
== "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
gps._raw_sentence == "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
)
assert (
gps.nmea_sentence
== "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
gps.nmea_sentence == "$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47"
)


@pytest.mark.parametrize(
"r",
(
pytest.param(
b"$GPGSA,A,3,15,18,14,,,31,,,23,,,,04.5,02.1,04.0*0f\r\n", id="smaller v4.1"
),
pytest.param(b"$GPGSA,A,3,15,18,14,,,31,,,23,,,,04.5,02.1,04.0*0f\r\n", id="smaller v4.1"),
pytest.param(
b"$GPGSA,A,3,15,18,14,,,31,,,23,,,,04.5,02.1,04.0,3*10\r\n",
id="greater v4.1",