From b9e06e93f258ef3251b8104e19c9cfd435d12d60 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 23 Nov 2021 13:02:04 -0600 Subject: [PATCH 01/80] update rtd py version --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 95ec218..1335112 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 python: - version: "3.6" + version: "3.7" install: - requirements: docs/requirements.txt - requirements: requirements.txt From 41a4adff44a33fa92ede0a657baf4ff2c56a29c0 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 8 Dec 2021 21:41:28 +0100 Subject: [PATCH 02/80] Enhanced parsing of timestamp_utc --- adafruit_gps.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 10bc89c..a28a065 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -50,11 +50,11 @@ _SENTENCE_PARAMS = ( # 0 - _GLL - "dcdcfcC", + "dcdcscC", # 1 - _RMC - "fcdcdcffiDCC", + "scdcdcffsDCC", # 2 - _GGA - "fdcdciiffsfsIS", + "sdcdciiffsfsIS", # 3 - _GSA "ciIIIIIIIIIIIIfff", # 4 - _GSA_4_11 @@ -68,7 +68,7 @@ # 8 - _GSV19 "iiiiiiIiiiIiiiIiiiI", # 9 - _RMC_4_1 - "fcdcdcffiDCCC", + "scdcdcffsDCCC", ) @@ -394,9 +394,9 @@ def _parse_sentence(self): return (data_type, sentence[delimiter + 1 :]) def _update_timestamp_utc(self, time_utc, date=None): - hours = time_utc // 10000 - mins = (time_utc // 100) % 100 - secs = time_utc % 100 + hours = int(time_utc[0:2]) + mins = int(time_utc[2:4]) + secs = int(time_utc[4:6]) if date is None: if self.timestamp_utc is None: day, month, year = 0, 0, 0 @@ -405,9 +405,9 @@ def _update_timestamp_utc(self, time_utc, date=None): month = self.timestamp_utc.tm_mon year = self.timestamp_utc.tm_year else: - day = date // 10000 - month = (date // 100) % 100 - year = 2000 + date % 100 + day = int(date[0:2]) + 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) @@ -429,7 +429,7 @@ def _parse_gll(self, data): self.longitude = _read_degrees(data, 2, "w") # UTC time of position - self._update_timestamp_utc(int(data[4])) + self._update_timestamp_utc(data[4]) # Status Valid(A) or Invalid(V) self.isactivedata = data[5] @@ -450,7 +450,7 @@ def _parse_rmc(self, data): return False # Params didn't parse # UTC time of position and date - self._update_timestamp_utc(int(data[0]), data[8]) + self._update_timestamp_utc(data[0], data[8]) # Status Valid(A) or Invalid(V) self.isactivedata = data[1] @@ -494,7 +494,7 @@ def _parse_gga(self, data): return False # Params didn't parse # UTC time of position - self._update_timestamp_utc(int(data[0])) + self._update_timestamp_utc(data[0]) # Latitude self.latitude = _read_degrees(data, 1, "s") From bf26b17d90c3abf37e3d29a1ca5f891c068c4de9 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 9 Dec 2021 16:51:49 +0100 Subject: [PATCH 03/80] Updated timestamp tests for new type format --- tests/adafruit_gps_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 9e09c85..7bbc060 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -140,14 +140,14 @@ def test_GPS_update_timestamp_UTC_date_None(): assert gps.datetime is None assert gps.timestamp_utc is None exp_struct = time.struct_time((0, 0, 0, 22, 14, 11, 0, 0, -1)) - gps._update_timestamp_utc(time_utc=221411) + gps._update_timestamp_utc(time_utc='221411') assert gps.timestamp_utc == exp_struct def test_GPS_update_timestamp_UTC_date_not_None(): gps = GPS(uart=UartMock()) exp_struct = time.struct_time((2021, 10, 2, 22, 14, 11, 0, 0, -1)) - gps._update_timestamp_utc(time_utc=221411, date=21021) + gps._update_timestamp_utc(time_utc='221411', date='21021') assert gps.timestamp_utc == exp_struct @@ -157,7 +157,7 @@ def test_GPS_update_timestamp_timestamp_utc_was_not_none_new_date_none(): gps.timestamp_utc = time.struct_time((2021, 10, 2, 22, 10, 11, 0, 0, -1)) exp_struct = time.struct_time((2021, 10, 2, 22, 14, 11, 0, 0, -1)) # update the timestamp - gps._update_timestamp_utc(time_utc=221411) + gps._update_timestamp_utc(time_utc='221411') assert gps.timestamp_utc == exp_struct From 10142fd0d9602afba630d3b746a9d6e965d2bf2b Mon Sep 17 00:00:00 2001 From: root Date: Thu, 9 Dec 2021 17:50:41 +0100 Subject: [PATCH 04/80] Learning about formatting and black --- tests/adafruit_gps_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 7bbc060..53aea9d 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -140,14 +140,14 @@ def test_GPS_update_timestamp_UTC_date_None(): assert gps.datetime is None assert gps.timestamp_utc is None exp_struct = time.struct_time((0, 0, 0, 22, 14, 11, 0, 0, -1)) - gps._update_timestamp_utc(time_utc='221411') + gps._update_timestamp_utc(time_utc="221411") assert gps.timestamp_utc == exp_struct def test_GPS_update_timestamp_UTC_date_not_None(): gps = GPS(uart=UartMock()) exp_struct = time.struct_time((2021, 10, 2, 22, 14, 11, 0, 0, -1)) - gps._update_timestamp_utc(time_utc='221411', date='21021') + gps._update_timestamp_utc(time_utc="221411", date="21021") assert gps.timestamp_utc == exp_struct @@ -157,7 +157,7 @@ def test_GPS_update_timestamp_timestamp_utc_was_not_none_new_date_none(): gps.timestamp_utc = time.struct_time((2021, 10, 2, 22, 10, 11, 0, 0, -1)) exp_struct = time.struct_time((2021, 10, 2, 22, 14, 11, 0, 0, -1)) # update the timestamp - gps._update_timestamp_utc(time_utc='221411') + gps._update_timestamp_utc(time_utc="221411") assert gps.timestamp_utc == exp_struct From ef300a2edbe9d672f8e9f7e00edaeb3d346f7ae3 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 9 Dec 2021 18:07:28 +0100 Subject: [PATCH 05/80] Added leading 0 to date string since it is no longer int and to follow NMEA sentence format of DDMMYY --- tests/adafruit_gps_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 53aea9d..8a956f3 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -147,7 +147,7 @@ def test_GPS_update_timestamp_UTC_date_None(): def test_GPS_update_timestamp_UTC_date_not_None(): gps = GPS(uart=UartMock()) exp_struct = time.struct_time((2021, 10, 2, 22, 14, 11, 0, 0, -1)) - gps._update_timestamp_utc(time_utc="221411", date="21021") + gps._update_timestamp_utc(time_utc="221411", date="021021") assert gps.timestamp_utc == exp_struct From 3e41e4494d52b353f0808213c90df75aa146371f Mon Sep 17 00:00:00 2001 From: dherrada Date: Thu, 13 Jan 2022 16:27:30 -0500 Subject: [PATCH 06/80] First part of patch Signed-off-by: dherrada --- .../PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md | 2 +- .github/workflows/build.yml | 6 +++--- .github/workflows/release.yml | 8 ++++---- .readthedocs.yaml | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md index 71ef8f8..8de294e 100644 --- a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md +++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md @@ -4,7 +4,7 @@ Thank you for contributing! Before you submit a pull request, please read the following. -Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://circuitpython.readthedocs.io/en/latest/docs/design_guide.html +Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 874b268..2d3ddee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,10 @@ jobs: awk -F '\/' '{ print tolower($2) }' | tr '_' '-' ) - - name: Set up Python 3.7 - uses: actions/setup-python@v1 + - name: Set up Python 3.x + uses: actions/setup-python@v2 with: - python-version: 3.7 + python-version: "3.x" - name: Versions run: | python3 --version diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d0015a..a65e5de 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,10 +24,10 @@ jobs: awk -F '\/' '{ print tolower($2) }' | tr '_' '-' ) - - name: Set up Python 3.6 - uses: actions/setup-python@v1 + - name: Set up Python 3.x + uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: "3.x" - name: Versions run: | python3 --version @@ -67,7 +67,7 @@ jobs: echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) - name: Set up Python if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1335112..f8b2891 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,7 +9,7 @@ version: 2 python: - version: "3.7" + version: "3.x" install: - requirements: docs/requirements.txt - requirements: requirements.txt From e17c63eb1ae9f3b74e30a166416a9e02630b747b Mon Sep 17 00:00:00 2001 From: dherrada Date: Mon, 24 Jan 2022 16:46:16 -0500 Subject: [PATCH 07/80] Updated docs link, updated python docs link, updated setup.py --- README.rst | 4 ++-- docs/conf.py | 4 ++-- docs/index.rst | 2 +- setup.py | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 08d96cb..8cac3f7 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ Introduction ============ .. image:: https://readthedocs.org/projects/adafruit-circuitpython-gps/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/gps/en/latest/ + :target: https://docs.circuitpython.org/projects/gps/en/latest/ :alt: Documentation Status .. image :: https://img.shields.io/discord/327254708534116352.svg @@ -122,7 +122,7 @@ should fix this. Documentation ============= -API documentation for this library can be found on `Read the Docs `_. +API documentation for this library can be found on `Read the Docs `_. Contributing ============ diff --git a/docs/conf.py b/docs/conf.py index 22cd1e0..dcc3567 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,8 +21,8 @@ ] intersphinx_mapping = { - "python": ("https://docs.python.org/3.4", None), - "CircuitPython": ("https://circuitpython.readthedocs.io/en/latest/", None), + "python": ("https://docs.python.org/3", None), + "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), } # Add any paths that contain templates here, relative to this directory. diff --git a/docs/index.rst b/docs/index.rst index adb092b..7507d48 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -40,7 +40,7 @@ Table of Contents :caption: Other Links Download - CircuitPython Reference Documentation + CircuitPython Reference Documentation CircuitPython Support Forum Discord Chat Adafruit Learning System diff --git a/setup.py b/setup.py index 53c0474..b151510 100644 --- a/setup.py +++ b/setup.py @@ -49,8 +49,6 @@ "Topic :: System :: Hardware", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", ], # What does your project relate to? keywords="adafruit gps module latitude longitude breakout hardware micropython circuitpython", From 2590a683173f7f2dbd12e7500b4fdb4109bfb63e Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Wed, 9 Feb 2022 12:58:29 -0500 Subject: [PATCH 08/80] Consolidate Documentation sections of README --- README.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 8cac3f7..0ba6aa9 100644 --- a/README.rst +++ b/README.rst @@ -124,14 +124,11 @@ Documentation API documentation for this library can be found on `Read the Docs `_. +For information on building library documentation, please check out `this guide `_. + Contributing ============ Contributions are welcome! Please read our `Code of Conduct `_ before contributing to help this project stay welcoming. - -Documentation -============= - -For information on building library documentation, please check out `this guide `_. From 83b2ff439d9781e040e9d807c9d4902eba1a25b6 Mon Sep 17 00:00:00 2001 From: dherrada Date: Mon, 14 Feb 2022 15:35:02 -0500 Subject: [PATCH 09/80] Fixed readthedocs build Signed-off-by: dherrada --- .readthedocs.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index f8b2891..33c2a61 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,8 +8,12 @@ # Required version: 2 +build: + os: ubuntu-20.04 + tools: + python: "3" + python: - version: "3.x" install: - requirements: docs/requirements.txt - requirements: requirements.txt From 44c3fbec4a581ed887210b8bd358e03f65b986b2 Mon Sep 17 00:00:00 2001 From: Kattni Rembor Date: Mon, 28 Mar 2022 15:52:04 -0400 Subject: [PATCH 10/80] Update Black to latest. Signed-off-by: Kattni Rembor --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b9fadc..7467c1d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/python/black - rev: 20.8b1 + rev: 22.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool From 57ecffd3a5c9165cb261bceba5ff2d3a963596a4 Mon Sep 17 00:00:00 2001 From: Eva Herrada <33632497+evaherrada@users.noreply.github.com> Date: Thu, 21 Apr 2022 18:37:58 -0400 Subject: [PATCH 11/80] Update .gitignore --- .gitignore | 50 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index b45b80a..544ec4a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,47 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries # -# SPDX-License-Identifier: Unlicense +# SPDX-License-Identifier: MIT +# Do not include files and directories created by your personal work environment, such as the IDE +# you use, except for those already listed here. Pull requests including changes to this file will +# not be accepted. + +# This .gitignore file contains rules for files generated by working with CircuitPython libraries, +# including building Sphinx, testing with pip, and creating a virual environment, as well as the +# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs. + +# If you find that there are files being generated on your machine that should not be included in +# your git commit, you should create a .gitignore_global file on your computer to include the +# files created by your personal setup. To do so, follow the two steps below. + +# First, create a file called .gitignore_global somewhere convenient for you, and add rules for +# the files you want to exclude from git commits. + +# Second, configure Git to use the exclude file for all Git repositories by running the +# following via commandline, replacing "path/to/your/" with the actual path to your newly created +# .gitignore_global file: +# git config --global core.excludesfile path/to/your/.gitignore_global + +# CircuitPython-specific files *.mpy -.idea + +# Python-specific files __pycache__ -_build *.pyc + +# Sphinx build-specific files +_build + +# This file results from running `pip -e install .` in a local repository +*.egg-info + +# Virtual environment-specific files .env -bundles + +# MacOS-specific files *.DS_Store -.eggs -dist -**/*.egg-info -.tox -.coverage + +# IDE-specific files +.idea +.vscode +*~ From 8050aee54531736c0330da10044d4f403c8b8edc Mon Sep 17 00:00:00 2001 From: evaherrada Date: Fri, 22 Apr 2022 15:58:43 -0400 Subject: [PATCH 12/80] Patch: Replaced discord badge image --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 0ba6aa9..4a3326e 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Introduction :target: https://docs.circuitpython.org/projects/gps/en/latest/ :alt: Documentation Status -.. image :: https://img.shields.io/discord/327254708534116352.svg +.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord From e233afd3d05414cb27df3fe58faadda545317a6d Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 24 Apr 2022 13:51:55 -0500 Subject: [PATCH 13/80] change discord badge --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4a3326e..660e7c8 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Introduction :target: https://docs.circuitpython.org/projects/gps/en/latest/ :alt: Documentation Status -.. image:: https://github.com/adafruit/Adafruit_CircuitPython_Bundle/blob/main/badges/adafruit_discord.svg +.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg :target: https://adafru.it/discord :alt: Discord From 2b4cdefe1d67a137fd2285ba90db523c24905bf1 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Sun, 15 May 2022 12:47:55 -0400 Subject: [PATCH 14/80] Patch .pre-commit-config.yaml --- .pre-commit-config.yaml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7467c1d..3343606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,40 +3,40 @@ # SPDX-License-Identifier: Unlicense repos: -- repo: https://github.com/python/black + - repo: https://github.com/python/black rev: 22.3.0 hooks: - - id: black -- repo: https://github.com/fsfe/reuse-tool - rev: v0.12.1 + - id: black + - repo: https://github.com/fsfe/reuse-tool + rev: v0.14.0 hooks: - - id: reuse -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 + - id: reuse + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.2.0 hooks: - - id: check-yaml - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/pycqa/pylint + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/pycqa/pylint rev: v2.11.1 hooks: - - id: pylint + - id: pylint name: pylint (library code) types: [python] args: - --disable=consider-using-f-string exclude: "^(docs/|examples/|tests/|setup.py$)" - - id: pylint + - 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 + - --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 + - --disable=missing-docstring,consider-using-f-string,duplicate-code From d25f2ec1872fbd76935357acc26b74e841756963 Mon Sep 17 00:00:00 2001 From: caternuson Date: Wed, 25 May 2022 12:36:07 -0700 Subject: [PATCH 15/80] switch decode to str --- adafruit_gps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index a28a065..d6d5d86 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -546,7 +546,7 @@ def _parse_gsa(self, talker, data): self.fix_quality_3d = 0 return False # Params didn't parse - talker = talker.decode("ascii") + talker = str(talker, 'ascii') # Selection mode: 'M' - manual, 'A' - automatic self.sel_mode = data[0] @@ -585,7 +585,7 @@ def _parse_gsv(self, talker, data): if data is None: return False # Params didn't parse - talker = talker.decode("ascii") + talker = str(talker, 'ascii') # Number of messages self.total_mess_num = data[0] From c4d9dd1660a3e03258589fdce890de6ad1ef3c9c Mon Sep 17 00:00:00 2001 From: caternuson Date: Wed, 25 May 2022 12:45:42 -0700 Subject: [PATCH 16/80] black says double quotes please --- adafruit_gps.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index d6d5d86..36a17d2 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -546,7 +546,7 @@ def _parse_gsa(self, talker, data): self.fix_quality_3d = 0 return False # Params didn't parse - talker = str(talker, 'ascii') + talker = str(talker, "ascii") # Selection mode: 'M' - manual, 'A' - automatic self.sel_mode = data[0] @@ -585,7 +585,7 @@ def _parse_gsv(self, talker, data): if data is None: return False # Params didn't parse - talker = str(talker, 'ascii') + talker = str(talker, "ascii") # Number of messages self.total_mess_num = data[0] From a24fa7e21066266b916ce1d1c105661dde087449 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Sun, 22 May 2022 00:18:55 -0400 Subject: [PATCH 17/80] Increase min lines similarity Signed-off-by: Alec Delaney --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index e06d2f6..fe0cbee 100644 --- a/.pylintrc +++ b/.pylintrc @@ -252,7 +252,7 @@ ignore-docstrings=yes ignore-imports=yes # Minimum lines number of a similarity. -min-similarity-lines=4 +min-similarity-lines=12 [BASIC] From 40b74abb7b937e4d9dc57e9aa8089ca621c8ee83 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 30 May 2022 14:25:04 -0400 Subject: [PATCH 18/80] Set language to "en" for documentation Signed-off-by: Alec Delaney --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index dcc3567..c7cf4f6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. From b0a4b6c1ca58b6178fffd3cf163a8426d2246618 Mon Sep 17 00:00:00 2001 From: evaherrada Date: Tue, 7 Jun 2022 15:34:24 -0400 Subject: [PATCH 19/80] Added cp.org link to index.rst --- docs/index.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 7507d48..a6e23c9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -39,7 +39,8 @@ Table of Contents .. toctree:: :caption: Other Links - Download + Download from GitHub + Download Library Bundle CircuitPython Reference Documentation CircuitPython Support Forum Discord Chat From 28b2d2543f82cc39cdcfd86de85db1b5aa7d3f96 Mon Sep 17 00:00:00 2001 From: gamblor21 Date: Mon, 13 Jun 2022 16:36:16 -0500 Subject: [PATCH 20/80] Add handling with extra precision in lat/long --- adafruit_gps.py | 34 +++++++++++++++++++++++++++++----- examples/gps_simpletest.py | 10 ++++++++++ tests/adafruit_gps_test.py | 6 +++--- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 36a17d2..92105e7 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -80,10 +80,15 @@ def _parse_degrees(nmea_data): # Where ddd is the degrees, mm.mmmm is the minutes. if nmea_data is None or len(nmea_data) < 3: return None - raw = float(nmea_data) - deg = raw // 100 - minutes = raw % 100 - return deg + minutes / 60 + # To avoid losing precision handle degrees and minutes separately + # Return the final value as an integer. Further functions can parse + # this into a float or separate parts to retain the precision + raw = nmea_data.split(".") + deg = int(raw[0]) // 100 * 1000000 # the ddd + minutes = int(raw[0]) % 100 * 10000 # the mm. + minutes += int(raw[1]) # the .mmmm + minutes = int(minutes / 60 * 100) + return deg + minutes def _parse_int(nmea_data): @@ -105,12 +110,21 @@ def _parse_str(nmea_data): def _read_degrees(data, index, neg): - x = data[index] + # This function loses precision with float32 + x = data[index] / 1000000 if data[index + 1].lower() == neg: x *= -1.0 return x +def _read_int_degress(data, index, neg): + deg = data[index] // 1000000 + minutes = data[index] % 1000000 / 10000 + if data[index + 1].lower() == neg: + deg *= -1 + return (deg, minutes) + + def _parse_talker(data_type): # Split the data_type into talker and sentence_type if data_type[:1] == b"P": # Proprietary codes @@ -208,7 +222,11 @@ def __init__(self, uart, debug=False): # Initialize null starting values for GPS attributes. self.timestamp_utc = None self.latitude = None + self.latitude_degrees = None + self.latitude_minutes = None # Use for full precision minutes self.longitude = None + self.longitude_degrees = None + self.longitude_minutes = None # Use for full precision minutes self.fix_quality = 0 self.fix_quality_3d = 0 self.satellites = None @@ -424,9 +442,11 @@ def _parse_gll(self, data): # Latitude self.latitude = _read_degrees(data, 0, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 0, "s") # Longitude self.longitude = _read_degrees(data, 2, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 2, "w") # UTC time of position self._update_timestamp_utc(data[4]) @@ -462,9 +482,11 @@ def _parse_rmc(self, data): # Latitude self.latitude = _read_degrees(data, 2, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 2, "s") # Longitude self.longitude = _read_degrees(data, 4, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 4, "w") # Speed over ground, knots self.speed_knots = data[6] @@ -498,9 +520,11 @@ def _parse_gga(self, data): # Latitude self.latitude = _read_degrees(data, 1, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 1, "s") # Longitude self.longitude = _read_degrees(data, 3, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 3, "w") # GPS quality indicator # 0 - fix not available, diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 4e41a8d..34af8f8 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -82,6 +82,16 @@ ) print("Latitude: {0:.6f} degrees".format(gps.latitude)) print("Longitude: {0:.6f} degrees".format(gps.longitude)) + print( + "Precise Latitude: {:2.}{:2.4f} degrees".format( + gps.latitude_degrees, gps.latitude_minutes + ) + ) + print( + "Precise Longitude: {:2.}{:2.4f} degrees".format( + gps.longitude_degrees, gps.longitude_minutes + ) + ) print("Fix quality: {}".format(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! diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 8a956f3..926e4af 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -21,9 +21,9 @@ @pytest.mark.parametrize( ("val", "exp"), ( - pytest.param("0023.456", 0.390933, id="leading zero"), - pytest.param("6413.9369", 64.23228, id="regular value"), - pytest.param("2747.416122087989", 27.79027, id="long value"), + pytest.param("0023.456", 390933, id="leading zero"), + pytest.param("6413.9369", 64232281, id="regular value"), + pytest.param("2747.416122087989", 27790268, id="long value"), ), ) def test_parse_degrees(val, exp): From d22cc67f5c365d6c08ee961df0b65957bcab263a Mon Sep 17 00:00:00 2001 From: gamblor21 Date: Mon, 13 Jun 2022 22:30:35 -0500 Subject: [PATCH 21/80] Fix to handle other nmea data string lengths --- adafruit_gps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 92105e7..114fa8f 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -85,9 +85,9 @@ def _parse_degrees(nmea_data): # this into a float or separate parts to retain the precision raw = nmea_data.split(".") deg = int(raw[0]) // 100 * 1000000 # the ddd - minutes = int(raw[0]) % 100 * 10000 # the mm. - minutes += int(raw[1]) # the .mmmm - minutes = int(minutes / 60 * 100) + minutes = int(raw[0]) % 100 # the mm. + minutes += int(f"{raw[1][:4]:0<4}") / 10000 # the .mmmm + minutes = int(minutes / 60 * 1000000) return deg + minutes From 4ccc6b963034dfff4c0b71ba28d49a843589bc74 Mon Sep 17 00:00:00 2001 From: gamblor21 Date: Mon, 13 Jun 2022 22:31:18 -0500 Subject: [PATCH 22/80] Changed tests as internal representation of degrees changed --- tests/adafruit_gps_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 926e4af..eb8de9b 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -61,10 +61,10 @@ def test_parse_float_invalid(val): @pytest.mark.parametrize( ("data", "neg", "exp"), ( - pytest.param([27.79027, "S"], "s", -27.79027, id="south negative"), - pytest.param([64.23228, "N"], "s", 64.23228, id="north not negative"), - pytest.param([123.4567, "W"], "w", -123.4567, id="west negative"), - pytest.param([10.7891, "E"], "w", 10.7891, id="east not negative"), + pytest.param([27790270, "S"], "s", -27.79027, id="south negative"), + pytest.param([64232280, "N"], "s", 64.23228, id="north not negative"), + pytest.param([123456700, "W"], "w", -123.4567, id="west negative"), + pytest.param([10789100, "E"], "w", 10.7891, id="east not negative"), ), ) def test_read_degrees(data, neg, exp): From 9f90c48e1747ce52355ebba65a90e339eb638018 Mon Sep 17 00:00:00 2001 From: gamblor21 Date: Mon, 13 Jun 2022 22:47:12 -0500 Subject: [PATCH 23/80] Refactor for mpy-cross --- adafruit_gps.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 114fa8f..4c51fe7 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -86,7 +86,8 @@ def _parse_degrees(nmea_data): raw = nmea_data.split(".") deg = int(raw[0]) // 100 * 1000000 # the ddd minutes = int(raw[0]) % 100 # the mm. - minutes += int(f"{raw[1][:4]:0<4}") / 10000 # the .mmmm + tmp = raw[1][:4] # mpy-cross wont compile if this is directly in the next line + minutes += int(f"{tmp:0<4}") / 10000 minutes = int(minutes / 60 * 1000000) return deg + minutes From 2cab6bd262d8a56592ba2a6f7a9d3106a6c30d7e Mon Sep 17 00:00:00 2001 From: Eva Herrada <33632497+evaherrada@users.noreply.github.com> Date: Wed, 15 Jun 2022 17:43:07 -0400 Subject: [PATCH 24/80] Changed code to use sdcardio as preferred method --- examples/gps_datalogging.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/gps_datalogging.py b/examples/gps_datalogging.py index 1eee0d8..cb507f2 100644 --- a/examples/gps_datalogging.py +++ b/examples/gps_datalogging.py @@ -29,14 +29,24 @@ # lines to import the necessary library and initialize the SD card: # NOT for use with a single board computer like Raspberry Pi! """ -import adafruit_sdcard -import digitalio +# Comment out if your board doesn't support sdcardio +import sdcardio + +# Uncomment if your board doesn't support sdcardio +#import adafruit_sdcard +#import digitalio + import storage SD_CS_PIN = board.D10 # CS for SD card using Adalogger Featherwing spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -sd_cs = digitalio.DigitalInOut(SD_CS_PIN) -sdcard = adafruit_sdcard.SDCard(spi, sd_cs) +# Comment out if your board doesn't support sdcardio +sdcard = sdcardio.SDCard(spi, SD_CS_PIN) + +# Uncomment if your board doesn't support sdcardio +#sd_cs = digitalio.DigitalInOut(SD_CS_PIN) +#sdcard = adafruit_sdcard.SDCard(spi, sd_cs) + vfs = storage.VfsFat(sdcard) storage.mount(vfs, '/sd') # Mount SD card under '/sd' path in filesystem. LOG_FILE = '/sd/gps.txt' # Example for writing to SD card path /sd/gps.txt From 41be1c34c38d62e2a25933e5862127e6bc74aff7 Mon Sep 17 00:00:00 2001 From: evaherrada Date: Thu, 16 Jun 2022 14:25:20 -0400 Subject: [PATCH 25/80] Example auto chooses to use sd and what sd lib to use --- examples/gps_datalogging.py | 43 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/examples/gps_datalogging.py b/examples/gps_datalogging.py index cb507f2..1507049 100644 --- a/examples/gps_datalogging.py +++ b/examples/gps_datalogging.py @@ -10,6 +10,8 @@ # MUST carefully follow the steps in this guide to enable writes to the # internal filesystem: # https://learn.adafruit.com/adafruit-ultimate-gps-featherwing/circuitpython-library +import sys + import board import busio import adafruit_gps @@ -25,32 +27,29 @@ # like to erase the file and start clean each time use the value 'wb' instead. LOG_MODE = "ab" -# If writing to SD card on a microcontroller customize and uncomment these -# lines to import the necessary library and initialize the SD card: -# NOT for use with a single board computer like Raspberry Pi! -""" -# Comment out if your board doesn't support sdcardio -import sdcardio - -# Uncomment if your board doesn't support sdcardio -#import adafruit_sdcard -#import digitalio +# sdcardio and adafruit_sdcard are NOT supported on blinka. If you are using a +# Raspberry Pi or other single-board linux computer, the code will save the +# output to the path defined in LOG_FILE above. +if sys.platform != "linux": + import storage -import storage + SD_CS_PIN = board.D10 # CS for SD card using Adalogger Featherwing + try: + import sdcardio -SD_CS_PIN = board.D10 # CS for SD card using Adalogger Featherwing -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -# Comment out if your board doesn't support sdcardio -sdcard = sdcardio.SDCard(spi, SD_CS_PIN) + sdcard = sdcardio.SDCard(board.SPI, SD_CS_PIN) + except ImportError: + import adafruit_sdcard + import digitalio -# Uncomment if your board doesn't support sdcardio -#sd_cs = digitalio.DigitalInOut(SD_CS_PIN) -#sdcard = adafruit_sdcard.SDCard(spi, sd_cs) + sdcard = adafruit_sdcard.SDCard( + board.SPI(), + digitalio.DigitalInOut(SD_CS_PIN), + ) -vfs = storage.VfsFat(sdcard) -storage.mount(vfs, '/sd') # Mount SD card under '/sd' path in filesystem. -LOG_FILE = '/sd/gps.txt' # Example for writing to SD card path /sd/gps.txt -""" + vfs = storage.VfsFat(sdcard) + storage.mount(vfs, "/sd") # Mount SD card under '/sd' path in filesystem. + LOG_FILE = "/sd/gps.txt" # Example for writing to SD card path /sd/gps.txt # Create a serial connection for the GPS connection using default speed and # a slightly higher timeout (GPS modules typically update once a second). From 872ce6e4d5f6556b252aca4b2fdb28d7a8933161 Mon Sep 17 00:00:00 2001 From: gamblor21 Date: Thu, 16 Jun 2022 18:19:09 -0500 Subject: [PATCH 26/80] Spelling mistake, comment --- adafruit_gps.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 4c51fe7..2845846 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -84,12 +84,11 @@ def _parse_degrees(nmea_data): # Return the final value as an integer. Further functions can parse # this into a float or separate parts to retain the precision raw = nmea_data.split(".") - deg = int(raw[0]) // 100 * 1000000 # the ddd + degrees = int(raw[0]) // 100 * 1000000 # the ddd minutes = int(raw[0]) % 100 # the mm. - tmp = raw[1][:4] # mpy-cross wont compile if this is directly in the next line - minutes += int(f"{tmp:0<4}") / 10000 + minutes += int(f"{raw[1][:4]:0<4}") / 10000 minutes = int(minutes / 60 * 1000000) - return deg + minutes + return degrees + minutes # return parsed string in the format dddmmmmmm def _parse_int(nmea_data): @@ -118,7 +117,7 @@ def _read_degrees(data, index, neg): return x -def _read_int_degress(data, index, neg): +def _read_int_degrees(data, index, neg): deg = data[index] // 1000000 minutes = data[index] % 1000000 / 10000 if data[index + 1].lower() == neg: @@ -443,11 +442,11 @@ def _parse_gll(self, data): # Latitude self.latitude = _read_degrees(data, 0, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 0, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 0, "s") # Longitude self.longitude = _read_degrees(data, 2, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 2, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 2, "w") # UTC time of position self._update_timestamp_utc(data[4]) @@ -483,11 +482,11 @@ def _parse_rmc(self, data): # Latitude self.latitude = _read_degrees(data, 2, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 2, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 2, "s") # Longitude self.longitude = _read_degrees(data, 4, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 4, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 4, "w") # Speed over ground, knots self.speed_knots = data[6] @@ -521,11 +520,11 @@ def _parse_gga(self, data): # Latitude self.latitude = _read_degrees(data, 1, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degress(data, 1, "s") + self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 1, "s") # Longitude self.longitude = _read_degrees(data, 3, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degress(data, 3, "w") + self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 3, "w") # GPS quality indicator # 0 - fix not available, From 361f7994b71cf435c996e7f6adb593a77e15234c Mon Sep 17 00:00:00 2001 From: evaherrada Date: Fri, 22 Jul 2022 13:58:49 -0400 Subject: [PATCH 27/80] Changed .env to .venv in README.rst --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 660e7c8..d644ee9 100644 --- a/README.rst +++ b/README.rst @@ -48,8 +48,8 @@ To install in a virtual environment in your current project: .. code-block:: shell mkdir project-name && cd project-name - python3 -m venv .env - source .env/bin/activate + python3 -m venv .venv + source .venv/bin/activate pip3 install adafruit-circuitpython-gps Usage Example From 8e98eedc83d5735852ae13e213c38aa24ad3fb34 Mon Sep 17 00:00:00 2001 From: evaherrada Date: Tue, 2 Aug 2022 17:00:37 -0400 Subject: [PATCH 28/80] Added Black formatting badge --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index d644ee9..0961ca0 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,10 @@ 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 + 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. From cca0f0f859eee28cef6fe891365397c1d20f3e41 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 8 Aug 2022 22:05:52 -0400 Subject: [PATCH 29/80] Switched to pyproject.toml --- .github/workflows/build.yml | 27 ++++++---------- .github/workflows/release.yml | 17 +++++----- optional_requirements.txt | 3 ++ pyproject.toml | 47 ++++++++++++++++++++++++++++ requirements.txt | 4 +-- setup.py | 58 ----------------------------------- 6 files changed, 72 insertions(+), 84 deletions(-) create mode 100644 optional_requirements.txt create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d3ddee..22f6582 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,13 +47,11 @@ jobs: pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - name: Library version run: git describe --dirty --always --tags + - name: Setup problem matchers + uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 - name: Pre-commit hooks run: | pre-commit run --all-files - - name: run tests - run: | - pip install --upgrade tox - tox -e py - name: Build assets run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - name: Archive bundles @@ -61,24 +59,19 @@ jobs: with: name: bundles path: ${{ github.workspace }}/bundles/ - - name: Check For docs folder - id: need-docs - run: | - echo ::set-output name=docs::$( find . -wholename './docs' ) - name: Build docs - if: contains(steps.need-docs.outputs.docs, 'docs') working-directory: docs run: sphinx-build -E -W -b html . _build/html - - name: Check For setup.py + - name: Check For pyproject.toml id: need-pypi run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - name: Build Python package - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') run: | - pip install --upgrade setuptools wheel twine readme_renderer testresources - python setup.py sdist - python setup.py bdist_wheel --universal + pip install --upgrade build twine + for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do + sed -i -e "s/0.0.0-auto.0/1.2.3/" $file; + done; + python -m build twine check dist/* - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a65e5de..d1b4f8d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,25 +61,28 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - - name: Check For setup.py + - name: Check For pyproject.toml id: need-pypi run: | - echo ::set-output name=setup-py::$( find . -wholename './setup.py' ) + echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - name: Set up Python - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install dependencies - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') run: | python -m pip install --upgrade pip - pip install setuptools wheel twine + pip install --upgrade build twine - name: Build and publish - if: contains(steps.need-pypi.outputs.setup-py, 'setup.py') + if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') env: TWINE_USERNAME: ${{ secrets.pypi_username }} TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | - python setup.py sdist + for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do + sed -i -e "s/0.0.0-auto.0/${{github.event.release.tag_name}}/" $file; + done; + python -m build twine upload dist/* diff --git a/optional_requirements.txt b/optional_requirements.txt new file mode 100644 index 0000000..d4e27c4 --- /dev/null +++ b/optional_requirements.txt @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c8cbbc2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +[build-system] +requires = [ + "setuptools", + "wheel", +] + +[project] +name = "adafruit-circuitpython-gps" +description = "CircuitPython library for GPS modules." +version = "0.0.0-auto.0" +readme = "README.rst" +authors = [ + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} +] +urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_GPS"} +keywords = [ + "adafruit", + "gps", + "module", + "latitude", + "longitude", + "breakout", + "hardware", + "micropython", + "circuitpython", +] +license = {text = "MIT"} +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Embedded Systems", + "Topic :: System :: Hardware", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", +] +dynamic = ["dependencies", "optional-dependencies"] + +[tool.setuptools] +py-modules = ["adafruit_gps"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} +optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} diff --git a/requirements.txt b/requirements.txt index 5c35412..f39dc93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense Adafruit-Blinka -pyserial adafruit-circuitpython-busdevice +pyserial diff --git a/setup.py b/setup.py deleted file mode 100644 index b151510..0000000 --- a/setup.py +++ /dev/null @@ -1,58 +0,0 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -"""A setuptools based setup module. - -See: -https://packaging.python.org/en/latest/distributing.html -https://github.com/pypa/sampleproject -""" - -# Always prefer setuptools over distutils -from setuptools import setup, find_packages - -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, "README.rst"), encoding="utf-8") as f: - long_description = f.read() - -setup( - name="adafruit-circuitpython-gps", - use_scm_version=True, - setup_requires=["setuptools_scm"], - description="CircuitPython library for GPS modules.", - long_description=long_description, - long_description_content_type="text/x-rst", - # The project's main homepage. - url="https://github.com/adafruit/Adafruit_CircuitPython_GPS", - # Author details - author="Adafruit Industries", - author_email="circuitpython@adafruit.com", - install_requires=[ - "pyserial", - "Adafruit-Blinka", - "adafruit-circuitpython-busdevice", - ], - # Choose your license - license="MIT", - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Libraries", - "Topic :: System :: Hardware", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3", - ], - # What does your project relate to? - keywords="adafruit gps module latitude longitude breakout hardware micropython circuitpython", - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - py_modules=["adafruit_gps"], -) From d5881c725d49401f1b632a8cbf769a0b9a51b0b0 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 9 Aug 2022 12:03:54 -0400 Subject: [PATCH 30/80] Add setuptools-scm to build system requirements Signed-off-by: Alec Delaney --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index c8cbbc2..b18c599 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ requires = [ "setuptools", "wheel", + "setuptools-scm", ] [project] From ad5b067e9237174c5a187827fe756c7f7df964f4 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 16 Aug 2022 18:09:13 -0400 Subject: [PATCH 31/80] Update version string --- adafruit_gps.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 2845846..f8e4d94 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -29,7 +29,7 @@ import time from micropython import const -__version__ = "0.0.0-auto.0" +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_GPS.git" diff --git a/pyproject.toml b/pyproject.toml index b18c599..c76eaf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ requires = [ [project] name = "adafruit-circuitpython-gps" description = "CircuitPython library for GPS modules." -version = "0.0.0-auto.0" +version = "0.0.0+auto.0" readme = "README.rst" authors = [ {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} From 262826891e46d3b9d1bb7fcf1b949432f76724a8 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 16 Aug 2022 21:09:13 -0400 Subject: [PATCH 32/80] Fix version strings in workflow files --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 22f6582..cb2f60e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,7 +71,7 @@ jobs: run: | pip install --upgrade build twine for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0-auto.0/1.2.3/" $file; + sed -i -e "s/0.0.0+auto.0/1.2.3/" $file; done; python -m build twine check dist/* diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1b4f8d..f3a0325 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,7 @@ jobs: TWINE_PASSWORD: ${{ secrets.pypi_password }} run: | for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0-auto.0/${{github.event.release.tag_name}}/" $file; + sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" $file; done; python -m build twine upload dist/* From 05f5213d7a3a018889879b8a30da341d491c0b03 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 22 Aug 2022 21:36:30 -0400 Subject: [PATCH 33/80] Keep copyright up to date in documentation --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index c7cf4f6..9227044 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,6 +6,7 @@ import os import sys +import datetime sys.path.insert(0, os.path.abspath("..")) From caf51e53d1e6fac30fb1dbe13f68b17907a3d4f5 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Mon, 22 Aug 2022 21:56:11 -0400 Subject: [PATCH 34/80] Fix copyright string --- docs/conf.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 9227044..6663efe 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,7 +36,8 @@ # General information about the project. project = "Adafruit GPS Library" -copyright = "2017 Tony DiCola, 2021 James Carr" +current_year = str(datetime.datetime.now().year) +copyright = current_year + " Tony DiCola, James Carr" author = "Tony DiCola, James Carr" # The version info for the project you're documenting, acts as replacement for From f67f0e78202de13810b94b34ed239b801f189b1a Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 23 Aug 2022 17:26:20 -0400 Subject: [PATCH 35/80] Use year duration range for copyright attribution --- docs/conf.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 6663efe..ab83199 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,8 +36,14 @@ # General information about the project. project = "Adafruit GPS Library" +creation_year = "2017" current_year = str(datetime.datetime.now().year) -copyright = current_year + " Tony DiCola, James Carr" +year_duration = ( + current_year + if current_year == creation_year + else creation_year + " - " + current_year +) +copyright = year_duration + " Tony DiCola, James Carr" author = "Tony DiCola, James Carr" # The version info for the project you're documenting, acts as replacement for From f98cd66f5a34a864e33ba4c1f80bd854fc453546 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Tue, 30 Aug 2022 11:52:13 -0400 Subject: [PATCH 36/80] Add type annotations --- adafruit_gps.py | 83 ++++++++++++++++++++++++++++-------------------- requirements.txt | 2 ++ 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index f8e4d94..1e8e0d5 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -29,6 +29,14 @@ import time from micropython import const +try: + from typing import Optional, Tuple, List + from typing_extensions import Literal + from circuitpython_typing import ReadableBuffer + from busio import UART, I2C +except ImportError: + pass + __version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_GPS.git" @@ -75,7 +83,7 @@ # Internal helper parsing functions. # These handle input that might be none or null and return none instead of # throwing errors. -def _parse_degrees(nmea_data): +def _parse_degrees(nmea_data: str) -> int: # Parse a NMEA lat/long data pair 'dddmm.mmmm' into a pure degrees value. # Where ddd is the degrees, mm.mmmm is the minutes. if nmea_data is None or len(nmea_data) < 3: @@ -91,25 +99,25 @@ def _parse_degrees(nmea_data): return degrees + minutes # return parsed string in the format dddmmmmmm -def _parse_int(nmea_data): +def _parse_int(nmea_data: str) -> int: if nmea_data is None or nmea_data == "": return None return int(nmea_data) -def _parse_float(nmea_data): +def _parse_float(nmea_data: str) -> float: if nmea_data is None or nmea_data == "": return None return float(nmea_data) -def _parse_str(nmea_data): +def _parse_str(nmea_data: str) -> str: if nmea_data is None or nmea_data == "": return None return str(nmea_data) -def _read_degrees(data, index, neg): +def _read_degrees(data: List[float], index: int, neg: str) -> float: # This function loses precision with float32 x = data[index] / 1000000 if data[index + 1].lower() == neg: @@ -117,7 +125,7 @@ def _read_degrees(data, index, neg): return x -def _read_int_degrees(data, index, neg): +def _read_int_degrees(data: List[float], index: int, neg: str) -> float: deg = data[index] // 1000000 minutes = data[index] % 1000000 / 10000 if data[index + 1].lower() == neg: @@ -125,7 +133,7 @@ def _read_int_degrees(data, index, neg): return (deg, minutes) -def _parse_talker(data_type): +def _parse_talker(data_type: bytes) -> Tuple[bytes, bytes]: # Split the data_type into talker and sentence_type if data_type[:1] == b"P": # Proprietary codes return (data_type[:1], data_type[1:]) @@ -133,7 +141,7 @@ def _parse_talker(data_type): return (data_type[:2], data_type[2:]) -def _parse_data(sentence_type, data): +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. """ @@ -217,7 +225,7 @@ class GPS: GPS modules to read latitude, longitude, and more. """ - def __init__(self, uart, debug=False): + def __init__(self, uart: UART, debug: bool = False) -> None: self._uart = uart # Initialize null starting values for GPS attributes. self.timestamp_utc = None @@ -253,7 +261,7 @@ def __init__(self, uart, debug=False): self._magnetic_variation = None self.debug = debug - def update(self): + def update(self) -> bool: """Check for updated data from the GPS module and process it accordingly. Returns True if new data was processed, and False if nothing new was received. @@ -303,7 +311,7 @@ def update(self): return result - def send_command(self, command, add_checksum=True): + def send_command(self, command: bytes, add_checksum: bool = True) -> None: """Send a command string to the GPS. If add_checksum is True (the default) a NMEA checksum will automatically be computed and added. Note you should NOT add the leading $ and trailing * to the command @@ -320,48 +328,48 @@ def send_command(self, command, add_checksum=True): self.write(b"\r\n") @property - def has_fix(self): + def has_fix(self) -> bool: """True if a current fix for location information is available.""" return self.fix_quality is not None and self.fix_quality >= 1 @property - def has_3d_fix(self): + def has_3d_fix(self) -> bool: """Returns true if there is a 3d fix available. use has_fix to determine if a 2d fix is available, passing it the same data""" return self.fix_quality_3d is not None and self.fix_quality_3d >= 2 @property - def datetime(self): + def datetime(self) -> Optional[time.struct_time]: """Return struct_time object to feed rtc.set_time_source() function""" return self.timestamp_utc @property - def nmea_sentence(self): + def nmea_sentence(self) -> Optional[str]: """Return raw_sentence which is the raw NMEA sentence read from the GPS""" return self._raw_sentence - def read(self, num_bytes): + def read(self, num_bytes: Optional[int]) -> Optional[bytes]: """Read up to num_bytes of data from the GPS directly, without parsing. - Returns a bytearray with up to num_bytes or None if nothing was read""" + Returns a bytestring with up to num_bytes or None if nothing was read""" return self._uart.read(num_bytes) - def write(self, bytestr): + def write(self, bytestr: ReadableBuffer) -> Optional[int]: """Write a bytestring data to the GPS directly, without parsing or checksums""" return self._uart.write(bytestr) @property - def in_waiting(self): + def in_waiting(self) -> int: """Returns number of bytes available in UART read buffer""" return self._uart.in_waiting - def readline(self): - """Returns a newline terminated bytearray, must have timeout set for + def readline(self) -> Optional[bytes]: + """Returns a newline terminated bytestring, must have timeout set for the underlying UART or this will block forever!""" return self._uart.readline() - def _read_sentence(self): + 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. @@ -394,7 +402,7 @@ def _read_sentence(self): # At this point we don't have a valid sentence return None - def _parse_sentence(self): + def _parse_sentence(self) -> Optional[Tuple[str, str]]: sentence = self._read_sentence() # sentence is a valid NMEA with a valid checksum @@ -411,7 +419,7 @@ def _parse_sentence(self): data_type = sentence[1:delimiter] return (data_type, sentence[delimiter + 1 :]) - def _update_timestamp_utc(self, time_utc, date=None): + def _update_timestamp_utc(self, time_utc: str, date: Optional[str] = None) -> None: hours = int(time_utc[0:2]) mins = int(time_utc[2:4]) secs = int(time_utc[4:6]) @@ -431,7 +439,7 @@ def _update_timestamp_utc(self, time_utc, date=None): (year, month, day, hours, mins, secs, 0, 0, -1) ) - def _parse_gll(self, data): + def _parse_gll(self, data: List[str]) -> bool: # GLL - Geographic Position - Latitude/Longitude if data is None or len(data) != 7: @@ -459,7 +467,7 @@ def _parse_gll(self, data): return True - def _parse_rmc(self, data): + def _parse_rmc(self, data: List[str]) -> bool: # RMC - Recommended Minimum Navigation Information if data is None or len(data) not in (12, 13): @@ -505,7 +513,7 @@ def _parse_rmc(self, data): return True - def _parse_gga(self, data): + def _parse_gga(self, data: List[str]) -> bool: # GGA - Global Positioning System Fix Data if data is None or len(data) != 14: @@ -557,7 +565,7 @@ def _parse_gga(self, data): return True - def _parse_gsa(self, talker, data): + 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): @@ -596,7 +604,7 @@ def _parse_gsa(self, talker, data): return True - def _parse_gsv(self, talker, data): + def _parse_gsv(self, talker: bytes, data: List[str]) -> bool: # GSV - Satellites in view # pylint: disable=too-many-branches @@ -675,8 +683,13 @@ class GPS_GtopI2C(GPS): """ def __init__( - self, i2c_bus, *, address=_GPSI2C_DEFAULT_ADDRESS, debug=False, timeout=5 - ): + self, + i2c_bus: I2C, + *, + address: int = _GPSI2C_DEFAULT_ADDRESS, + debug: bool = False, + timeout: float = 5, + ) -> None: from adafruit_bus_device import ( # pylint: disable=import-outside-toplevel i2c_device, ) @@ -688,7 +701,7 @@ def __init__( self._internalbuffer = [] self._timeout = timeout - def read(self, num_bytes=1): + def read(self, num_bytes: int = 1) -> bytearray: """Read up to num_bytes of data from the GPS directly, without parsing. Returns a bytearray with up to num_bytes or None if nothing was read""" result = [] @@ -704,19 +717,19 @@ def read(self, num_bytes=1): self._lastbyte = char # keep track of the last character approved return bytearray(result) - def write(self, bytestr): + def write(self, bytestr: ReadableBuffer) -> None: """Write a bytestring data to the GPS directly, without parsing or checksums""" with self._i2c as i2c: i2c.write(bytestr) @property - def in_waiting(self): + def in_waiting(self) -> Literal[16]: """Returns number of bytes available in UART read buffer, always 16 since I2C does not have the ability to know how much data is available""" return 16 - def readline(self): + def readline(self) -> Optional[bytearray]: """Returns a newline terminated bytearray, must have timeout set for the underlying UART or this will block forever!""" timeout = time.monotonic() + self._timeout diff --git a/requirements.txt b/requirements.txt index f39dc93..162d884 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,5 @@ Adafruit-Blinka adafruit-circuitpython-busdevice pyserial +adafruit-circuitpython-typing +typing-extensions~=4.0 From f6d762345499d2fb873390d192bb09e0d65819d0 Mon Sep 17 00:00:00 2001 From: imzeerak Date: Wed, 31 Aug 2022 00:42:06 +0500 Subject: [PATCH 37/80] added missing optional requirement --- optional_requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/optional_requirements.txt b/optional_requirements.txt index d4e27c4..320d6e3 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,3 +1,5 @@ # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense + +adafruit-circuitpython-sdcard from adafruit-circuitpython-sd \ No newline at end of file From 2849319d6db9c03a26f131f69270a15535776a19 Mon Sep 17 00:00:00 2001 From: Zeerak Babar <89310445+zeerakb1@users.noreply.github.com> Date: Wed, 31 Aug 2022 15:50:03 +0500 Subject: [PATCH 38/80] Update optional_requirements.txt Co-authored-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- optional_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional_requirements.txt b/optional_requirements.txt index 320d6e3..e5a3dbb 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -adafruit-circuitpython-sdcard from adafruit-circuitpython-sd \ No newline at end of file +adafruit-circuitpython-sd \ No newline at end of file From 6dacd0970ab7c99967c576b4b9878fcfc0beb217 Mon Sep 17 00:00:00 2001 From: Zeerak Babar <89310445+zeerakb1@users.noreply.github.com> Date: Wed, 31 Aug 2022 19:24:49 +0500 Subject: [PATCH 39/80] Update optional_requirements.txt Co-authored-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- optional_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optional_requirements.txt b/optional_requirements.txt index e5a3dbb..3f62e00 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Unlicense -adafruit-circuitpython-sd \ No newline at end of file +adafruit-circuitpython-sd From ceb92a6121dc2bc1a8bf31416ef695dc1cd2e36d Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:51:24 -0400 Subject: [PATCH 40/80] Remove comment regarding string type --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 1e8e0d5..6f58ae3 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -96,7 +96,7 @@ def _parse_degrees(nmea_data: str) -> int: minutes = int(raw[0]) % 100 # the mm. minutes += int(f"{raw[1][:4]:0<4}") / 10000 minutes = int(minutes / 60 * 1000000) - return degrees + minutes # return parsed string in the format dddmmmmmm + return degrees + minutes def _parse_int(nmea_data: str) -> int: From 6542aa5dc1558061432b4f3b94f2034621248bcc Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:54:08 -0400 Subject: [PATCH 41/80] Change timeout default value to float --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 6f58ae3..4883f97 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -688,7 +688,7 @@ def __init__( *, address: int = _GPSI2C_DEFAULT_ADDRESS, debug: bool = False, - timeout: float = 5, + timeout: float = 5.0, ) -> None: from adafruit_bus_device import ( # pylint: disable=import-outside-toplevel i2c_device, From 4e2c583e2a7b2012cdc837e36e6a51a81329e13d Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:56:09 -0400 Subject: [PATCH 42/80] Fix annotation for _read_int_degrees() --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 4883f97..40f1b10 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -125,7 +125,7 @@ def _read_degrees(data: List[float], index: int, neg: str) -> float: return x -def _read_int_degrees(data: List[float], index: int, neg: str) -> float: +def _read_int_degrees(data: List[float], index: int, neg: str) -> Tuple[int, float]: deg = data[index] // 1000000 minutes = data[index] % 1000000 / 10000 if data[index + 1].lower() == neg: From 5c1bf71f34ad9dfebd3143c2e45cb104d4c362c4 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 6 Oct 2022 17:12:45 -0400 Subject: [PATCH 43/80] Add commented out busio import --- examples/gps_satellitefix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/gps_satellitefix.py b/examples/gps_satellitefix.py index 37de93b..c96d0db 100644 --- a/examples/gps_satellitefix.py +++ b/examples/gps_satellitefix.py @@ -15,6 +15,7 @@ # a slightly higher timeout (GPS modules typically update once a second). # These are the defaults you should use for the GPS FeatherWing. # For other boards set RX = GPS module TX, and TX = GPS module RX pins. +# import busio # uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10) # for a computer, use the pyserial library for uart access From 2f6015e254493e11c11efdd2ced998f422bf5478 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:02:49 -0400 Subject: [PATCH 44/80] Switching to composite actions --- .github/workflows/build.yml | 67 +---------------------- .github/workflows/release.yml | 88 ------------------------------ .github/workflows/release_gh.yml | 14 +++++ .github/workflows/release_pypi.yml | 14 +++++ 4 files changed, 30 insertions(+), 153 deletions(-) delete mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/release_gh.yml create mode 100644 .github/workflows/release_pypi.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb2f60e..041a337 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,68 +10,5 @@ jobs: test: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install dependencies - # (e.g. - apt-get: gettext, etc; pip: circuitpython-build-tools, requirements.txt; etc.) - run: | - source actions-ci/install.sh - - name: Pip install Sphinx, pre-commit - run: | - pip install --force-reinstall Sphinx sphinx-rtd-theme pre-commit - - name: Library version - run: git describe --dirty --always --tags - - name: Setup problem matchers - uses: adafruit/circuitpython-action-library-ci-problem-matchers@v1 - - name: Pre-commit hooks - run: | - pre-commit run --all-files - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Archive bundles - uses: actions/upload-artifact@v2 - with: - name: bundles - path: ${{ github.workspace }}/bundles/ - - name: Build docs - working-directory: docs - run: sphinx-build -E -W -b html . _build/html - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Build Python package - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - pip install --upgrade build twine - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/1.2.3/" $file; - done; - python -m build - twine check dist/* + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f3a0325..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,88 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -name: Release Actions - -on: - release: - types: [published] - -jobs: - upload-release-assets: - runs-on: ubuntu-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Translate Repo Name For Build Tools filename_prefix - id: repo-name - run: | - echo ::set-output name=repo-name::$( - echo ${{ github.repository }} | - awk -F '\/' '{ print tolower($2) }' | - tr '_' '-' - ) - - name: Set up Python 3.x - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - name: Versions - run: | - python3 --version - - name: Checkout Current Repo - uses: actions/checkout@v1 - with: - submodules: true - - name: Checkout tools repo - uses: actions/checkout@v2 - with: - repository: adafruit/actions-ci-circuitpython-libs - path: actions-ci - - name: Install deps - run: | - source actions-ci/install.sh - - name: Build assets - run: circuitpython-build-bundles --filename_prefix ${{ steps.repo-name.outputs.repo-name }} --library_location . - - name: Upload Release Assets - # the 'official' actions version does not yet support dynamically - # supplying asset names to upload. @csexton's version chosen based on - # discussion in the issue below, as its the simplest to implement and - # allows for selecting files with a pattern. - # https://github.com/actions/upload-release-asset/issues/4 - #uses: actions/upload-release-asset@v1.0.1 - uses: csexton/release-asset-action@master - with: - pattern: "bundles/*" - github-token: ${{ secrets.GITHUB_TOKEN }} - - upload-pypi: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - name: Check For pyproject.toml - id: need-pypi - run: | - echo ::set-output name=pyproject-toml::$( find . -wholename './pyproject.toml' ) - - name: Set up Python - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install dependencies - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - run: | - python -m pip install --upgrade pip - pip install --upgrade build twine - - name: Build and publish - if: contains(steps.need-pypi.outputs.pyproject-toml, 'pyproject.toml') - env: - TWINE_USERNAME: ${{ secrets.pypi_username }} - TWINE_PASSWORD: ${{ secrets.pypi_password }} - run: | - for file in $(find -not -path "./.*" -not -path "./docs*" \( -name "*.py" -o -name "*.toml" \) ); do - sed -i -e "s/0.0.0+auto.0/${{github.event.release.tag_name}}/" $file; - done; - python -m build - twine upload dist/* diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main From 21963113a1c3b9279c4946cd8c372bad0bba25d0 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 00:46:59 -0400 Subject: [PATCH 45/80] Updated pylint version to 2.13.0 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3343606..4c43710 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.11.1 + rev: v2.13.0 hooks: - id: pylint name: pylint (library code) From 501207a777f5017783b7958c78ed170f455e7da0 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 08:15:19 -0400 Subject: [PATCH 46/80] Update pylint to 2.15.5 --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c43710..0e5fccc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.13.0 + rev: v2.15.5 hooks: - id: pylint name: pylint (library code) From 91e5ba04ba108c756e609ea445cc3e985447c946 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 09:12:43 -0400 Subject: [PATCH 47/80] Fix release CI files --- .github/workflows/release_gh.yml | 14 +++++++++----- .github/workflows/release_pypi.yml | 15 ++++++++++----- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index 041a337..b8aa8d6 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -2,13 +2,17 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: GitHub Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml index 041a337..65775b7 100644 --- a/.github/workflows/release_pypi.yml +++ b/.github/workflows/release_pypi.yml @@ -2,13 +2,18 @@ # # SPDX-License-Identifier: MIT -name: Build CI +name: PyPI Release Actions -on: [pull_request, push] +on: + release: + types: [published] jobs: - test: + upload-release-assets: runs-on: ubuntu-latest steps: - - name: Run Build CI workflow - uses: adafruit/workflows-circuitpython-libs/build@main + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} From 9c43ee37c2e9f94bb9681aab595cbb5d6e636c6c Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Fri, 4 Nov 2022 18:34:32 -0400 Subject: [PATCH 48/80] Update .pylintrc for v2.15.5 --- .pylintrc | 45 ++++----------------------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/.pylintrc b/.pylintrc index fe0cbee..40208c3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries # # SPDX-License-Identifier: Unlicense @@ -26,7 +26,7 @@ jobs=1 # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins= +load-plugins=pylint.extensions.no_self_use # Pickle collected data for later comparisons. persistent=yes @@ -54,8 +54,8 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,bad-continuation,pointless-string-statement,unspecified-encoding +# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call +disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option @@ -225,12 +225,6 @@ max-line-length=100 # Maximum number of lines in a module max-module-lines=1000 -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no @@ -257,38 +251,22 @@ min-similarity-lines=12 [BASIC] -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct argument names argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct attribute names attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - # Regular expression matching correct class names # class-rgx=[A-Z_][a-zA-Z0-9]+$ class-rgx=[A-Z_][a-zA-Z0-9_]+$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ @@ -296,9 +274,6 @@ const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # ones are exempt. docstring-min-length=-1 -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct function names function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ @@ -309,21 +284,12 @@ good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ # Include a hint for the correct naming format with invalid-name include-naming-hint=no -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct method names method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -339,9 +305,6 @@ no-docstring-rgx=^_ # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - # Regular expression matching correct variable names variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ From ceb1d743e1d7f0d2c3a2ec7ef76c7219f105f2df Mon Sep 17 00:00:00 2001 From: dherrada Date: Fri, 18 Nov 2022 13:05:24 -0500 Subject: [PATCH 49/80] Added commented out board.STEMMA_I2C with explanation --- examples/gps_datalogging.py | 3 ++- examples/gps_echotest.py | 3 ++- examples/gps_satellitefix.py | 3 ++- examples/gps_simpletest.py | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/gps_datalogging.py b/examples/gps_datalogging.py index 1507049..eb2d391 100644 --- a/examples/gps_datalogging.py +++ b/examples/gps_datalogging.py @@ -63,7 +63,8 @@ # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10) # If using I2C, we'll create an I2C interface to talk to using default pins -# i2c = board.I2C() +# i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller # Create a GPS module instance. gps = adafruit_gps.GPS(uart) # Use UART/pyserial diff --git a/examples/gps_echotest.py b/examples/gps_echotest.py index baae47f..9a3acda 100644 --- a/examples/gps_echotest.py +++ b/examples/gps_echotest.py @@ -21,7 +21,8 @@ # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10) # If using I2C, we'll create an I2C interface to talk to using default pins -# i2c = board.I2C() +# i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller # Create a GPS module instance. gps = adafruit_gps.GPS(uart) # Use UART/pyserial diff --git a/examples/gps_satellitefix.py b/examples/gps_satellitefix.py index c96d0db..3927b3a 100644 --- a/examples/gps_satellitefix.py +++ b/examples/gps_satellitefix.py @@ -23,7 +23,8 @@ # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10) # If using I2C, we'll create an I2C interface to talk to using default pins -i2c = board.I2C() +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller # Create a GPS module instance. # gps = adafruit_gps.GPS(uart, debug=False) # Use UART/pyserial diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 34af8f8..0e028e4 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -21,7 +21,8 @@ # uart = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=10) # If using I2C, we'll create an I2C interface to talk to using default pins -# i2c = board.I2C() +# i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller # Create a GPS module instance. gps = adafruit_gps.GPS(uart, debug=False) # Use UART/pyserial From 4b82d0593eaa1e3c3ffefac18b0e0c56c247bd54 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 1 Sep 2022 20:16:31 -0400 Subject: [PATCH 50/80] Add .venv to .gitignore Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 544ec4a..db3d538 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ _build # Virtual environment-specific files .env +.venv # MacOS-specific files *.DS_Store From f25a987200931249b035825db95c88b116d7ce1f Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Thu, 19 Jan 2023 23:39:55 -0500 Subject: [PATCH 51/80] Add upload url to release action Signed-off-by: Alec Delaney <89490472+tekktrik@users.noreply.github.com> --- .github/workflows/release_gh.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml index b8aa8d6..9acec60 100644 --- a/.github/workflows/release_gh.yml +++ b/.github/workflows/release_gh.yml @@ -16,3 +16,4 @@ jobs: uses: adafruit/workflows-circuitpython-libs/release-gh@main with: github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} From df8c75e21e27eeb6b500a44fa482766090890866 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Mon, 24 Apr 2023 17:50:04 -0400 Subject: [PATCH 52/80] add docstrings to GPS class attrs --- adafruit_gps.py | 64 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 40f1b10..2916b2d 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -229,37 +229,86 @@ def __init__(self, uart: UART, debug: bool = False) -> None: self._uart = uart # Initialize null starting values for GPS attributes. self.timestamp_utc = None + """Timestamp in UTC""" self.latitude = None + """Degrees latitude""" self.latitude_degrees = None + """Degrees component of latitude measurement""" self.latitude_minutes = None # Use for full precision minutes + """Minutes component of latitude measurement""" self.longitude = None + """Degrees longitude""" self.longitude_degrees = None + """Degrees component of longitude measurement""" self.longitude_minutes = None # Use for full precision minutes + """Minutes component of longitude measurement""" self.fix_quality = 0 + """ + GPS quality indicator + + | 0 - fix not available + | 1 - GPS fix + | 2 - Differential GPS fix (values above 2 are 2.3 features) + | 3 - PPS fix + | 4 - Real Time Kinematic + | 5 - Float RTK + | 6 - estimated (dead reckoning) + | 7 - Manual input mode + | 8 - Simulation mode + """ self.fix_quality_3d = 0 + """ + The type of fix for a reading + + | 1 - no fix + | 2 - 2D fix + | 3 - 3D fix + """ self.satellites = None + """The number of satellites in use, 0 - 12""" self.satellites_prev = None + """The number of satellites in use from the previous data point, 0 - 12""" self.horizontal_dilution = None + """Horizontal dilution of precision (GGA)""" self.altitude_m = None + """Antenna altitude relative to mean sea level""" self.height_geoid = None + """Geoidal separation relative to WGS 84""" self.speed_knots = None + """Ground speed in knots""" self.track_angle_deg = None + """Track angle in degrees""" self._sats = None # Temporary holder for information from GSV messages - self.sats = None # Completed information from GSV messages + self.sats = None + """Information from GSV messages""" self.isactivedata = None - self.true_track = None - self.mag_track = None + """Status Valid(A) or Invalid(V)""" + self.true_track = None ### TODO: unused? + self.mag_track = None ### TODO: unused? self.sat_prns = None + """Satellite pseudorandom noise code""" self.sel_mode = None + """ + Selection mode + + | 'M' - manual + | 'A' - automatic + """ self.pdop = None + """Dilution of precision""" self.hdop = None + """Horizontal dilution of precision (GSA)""" self.vdop = None + """Vertical dilution of precision""" self.total_mess_num = None + """Number of messages""" self.mess_num = None + """Message number""" self._raw_sentence = None self._mode_indicator = None self._magnetic_variation = None self.debug = debug + """Toggles debug mode. When True, prints the incoming data sentence to the console""" def update(self) -> bool: """Check for updated data from the GPS module and process it @@ -535,15 +584,6 @@ def _parse_gga(self, data: List[str]) -> bool: self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 3, "w") # GPS quality indicator - # 0 - fix not available, - # 1 - GPS fix, - # 2 - Differential GPS fix (values above 2 are 2.3 features) - # 3 - PPS fix - # 4 - Real Time Kinematic - # 5 - Float RTK - # 6 - estimated (dead reckoning) - # 7 - Manual input mode - # 8 - Simulation mode self.fix_quality = data[5] # Number of satellites in use, 0 - 12 From 5282286ea3ef1a15a2299b5e6557d0037e98d4bd Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Mon, 24 Apr 2023 19:27:45 -0400 Subject: [PATCH 53/80] linter fixes --- adafruit_gps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 2916b2d..8f428d5 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -243,7 +243,7 @@ def __init__(self, uart: UART, debug: bool = False) -> None: self.longitude_minutes = None # Use for full precision minutes """Minutes component of longitude measurement""" self.fix_quality = 0 - """ + """ GPS quality indicator | 0 - fix not available @@ -258,7 +258,7 @@ def __init__(self, uart: UART, debug: bool = False) -> None: """ self.fix_quality_3d = 0 """ - The type of fix for a reading + The type of fix for a reading | 1 - no fix | 2 - 2D fix @@ -290,7 +290,7 @@ def __init__(self, uart: UART, debug: bool = False) -> None: self.sel_mode = None """ Selection mode - + | 'M' - manual | 'A' - automatic """ From 224bf57cffd4655df9ccce4998fa4a18ff1a7140 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Mon, 24 Apr 2023 19:28:05 -0400 Subject: [PATCH 54/80] remove unused vars --- adafruit_gps.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 8f428d5..9118a3b 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -283,8 +283,6 @@ def __init__(self, uart: UART, debug: bool = False) -> None: """Information from GSV messages""" self.isactivedata = None """Status Valid(A) or Invalid(V)""" - self.true_track = None ### TODO: unused? - self.mag_track = None ### TODO: unused? self.sat_prns = None """Satellite pseudorandom noise code""" self.sel_mode = None From a46021a7619efa32a326e2d9fb286374fe4abc63 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Mon, 24 Apr 2023 19:38:07 -0400 Subject: [PATCH 55/80] fixing pylint errors --- adafruit_gps.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 9118a3b..a613581 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -225,6 +225,8 @@ class GPS: 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. @@ -700,8 +702,7 @@ def _parse_gsv(self, talker: bytes, data: List[str]) -> bool: # been seen for 30 seconds timestamp = time.monotonic() old = [] - for i in self.sats: - sat = self.sats[i] + for sat in self.sats.items(): if (timestamp - sat[4]) > 30: old.append(i) for i in old: From a9d9a1160ef1711c3abb68a13ab71a07aad3c421 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Tue, 25 Apr 2023 20:39:58 -0400 Subject: [PATCH 56/80] use pylint rule name instead of code --- adafruit_gps.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index a613581..b4ef622 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -216,10 +216,7 @@ def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]: return params -# lint warning about too many attributes disabled -# pylint: disable-msg=R0902 - - +# 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. From 1e8ec15b8b4678db4234e75cb503843112df1f20 Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Tue, 9 May 2023 20:26:25 -0400 Subject: [PATCH 57/80] Update pre-commit hooks Signed-off-by: Tekktrik --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e5fccc..70ade69 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,21 +4,21 @@ repos: - repo: https://github.com/python/black - rev: 22.3.0 + rev: 23.3.0 hooks: - id: black - repo: https://github.com/fsfe/reuse-tool - rev: v0.14.0 + rev: v1.1.2 hooks: - id: reuse - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.4.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/pycqa/pylint - rev: v2.15.5 + rev: v2.17.4 hooks: - id: pylint name: pylint (library code) From 57a402adc79a7f8ddb1b452aff63a111ea43297a Mon Sep 17 00:00:00 2001 From: Tekktrik Date: Wed, 10 May 2023 22:33:52 -0400 Subject: [PATCH 58/80] Run pre-commit --- examples/gps_time_source.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/gps_time_source.py b/examples/gps_time_source.py index 0572b1a..0199ca5 100644 --- a/examples/gps_time_source.py +++ b/examples/gps_time_source.py @@ -38,7 +38,6 @@ def _format_datetime(datetime): last_print = time.monotonic() while True: - gps.update() # Every second print out current time from GPS, RTC and time.localtime() current = time.monotonic() From a310747c2bd62419933421da546b271d2e69cf31 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Thu, 18 May 2023 19:53:44 -0400 Subject: [PATCH 59/80] use values for satellite dict access instead of items --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index b4ef622..7f68aa2 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -699,7 +699,7 @@ def _parse_gsv(self, talker: bytes, data: List[str]) -> bool: # been seen for 30 seconds timestamp = time.monotonic() old = [] - for sat in self.sats.items(): + for sat in self.sats.values(): if (timestamp - sat[4]) > 30: old.append(i) for i in old: From 6971e892fbbd831584934d968faac534a6ee51bd Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Thu, 18 May 2023 20:29:07 -0400 Subject: [PATCH 60/80] include key as well as value when iterating over satellite dictionary --- adafruit_gps.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 7f68aa2..e97ca46 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -699,9 +699,9 @@ def _parse_gsv(self, talker: bytes, data: List[str]) -> bool: # been seen for 30 seconds timestamp = time.monotonic() old = [] - for sat in self.sats.values(): - if (timestamp - sat[4]) > 30: - old.append(i) + for sat_id, sat_data in self.sats.items(): + if (timestamp - sat_data[4]) > 30: + old.append(sat_id) for i in old: self.sats.pop(i) for sat in self._sats: From b75667f5b93197124ab2dd64941f574d6a573415 Mon Sep 17 00:00:00 2001 From: Emily Charles Date: Thu, 18 May 2023 21:48:20 -0400 Subject: [PATCH 61/80] move dev requirements to optional requirements --- optional_requirements.txt | 5 +++++ requirements-dev.txt | 7 ------- 2 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 requirements-dev.txt diff --git a/optional_requirements.txt b/optional_requirements.txt index 3f62e00..261f775 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,5 +1,10 @@ +# SPDX-FileCopyrightText: 2021 Jonas Kittner # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense adafruit-circuitpython-sd +covdefaults +coverage +freezegun +pytest diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 64bc8d4..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Jonas Kittner -# -# SPDX-License-Identifier: Unlicense -covdefaults -coverage -freezegun -pytest From 5e21680f418fb617839e7dee042c99462b2601f9 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Tue, 23 May 2023 23:07:13 -0400 Subject: [PATCH 62/80] Update .pylintrc, fix jQuery for docs --- .pylintrc | 2 +- docs/conf.py | 1 + docs/requirements.txt | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 40208c3..f945e92 100644 --- a/.pylintrc +++ b/.pylintrc @@ -396,4 +396,4 @@ min-public-methods=1 # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/docs/conf.py b/docs/conf.py index ab83199..a12c835 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,6 +17,7 @@ # ones. extensions = [ "sphinx.ext.autodoc", + "sphinxcontrib.jquery", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", ] diff --git a/docs/requirements.txt b/docs/requirements.txt index 88e6733..797aa04 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Unlicense sphinx>=4.0.0 +sphinxcontrib-jquery From 337fafd28390df772bce52df5148be1120a625d4 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 19 Sep 2023 18:47:33 -0500 Subject: [PATCH 63/80] fix rtd theme --- docs/conf.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a12c835..c46666b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -92,19 +92,10 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] - except: - html_theme = "default" - html_theme_path = ["."] -else: - html_theme_path = ["."] +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From e7b04f733fb88b8d135eae599a7301e52acc59f2 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 4 Dec 2023 14:03:40 -0600 Subject: [PATCH 64/80] unpin sphinx and add sphinx-rtd-theme to docs reqs --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 797aa04..979f568 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,6 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.0.0 +sphinx sphinxcontrib-jquery +sphinx-rtd-theme From ae0019d7145c045a46d0bb1ce228a651c5ff4ca4 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Mon, 22 May 2023 23:17:38 -0400 Subject: [PATCH 65/80] Fix format specifier --- examples/gps_simpletest.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 0e028e4..8c5714d 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -7,6 +7,7 @@ import time import board import busio +import math import adafruit_gps @@ -84,13 +85,13 @@ print("Latitude: {0:.6f} degrees".format(gps.latitude)) print("Longitude: {0:.6f} degrees".format(gps.longitude)) print( - "Precise Latitude: {:2.}{:2.4f} degrees".format( - gps.latitude_degrees, gps.latitude_minutes + "Precise Latitude: {:3.0f} degs, {:2.4f} mins".format( + math.floor(gps.latitude_degrees), gps.latitude_minutes ) ) print( - "Precise Longitude: {:2.}{:2.4f} degrees".format( - gps.longitude_degrees, gps.longitude_minutes + "Precise Longitude: {:3.0f} degs, {:2.4f} mins".format( + math.floor(gps.longitude_degrees), gps.longitude_minutes ) ) print("Fix quality: {}".format(gps.fix_quality)) From 9fd82a7b77c36f314553769c6d063bff5e777481 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Mon, 22 May 2023 23:23:57 -0400 Subject: [PATCH 66/80] Reorganize imports --- examples/gps_simpletest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 8c5714d..6434f27 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -5,9 +5,9 @@ # Will wait for a fix and print a message every second with the current location # and other details. import time +import math import board import busio -import math import adafruit_gps From 42dd7550473f902fa7c5e9b747fc2ba6703acda2 Mon Sep 17 00:00:00 2001 From: Jonas Kittner Date: Tue, 30 May 2023 17:32:00 +0200 Subject: [PATCH 67/80] fix minute parsing --- adafruit_gps.py | 100 ++++++++++++++++++++++--------------- tests/adafruit_gps_test.py | 35 ++++++++++++- tox.ini | 2 +- 3 files changed, 96 insertions(+), 41 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index e97ca46..4d3ec99 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -95,7 +95,7 @@ def _parse_degrees(nmea_data: str) -> int: degrees = int(raw[0]) // 100 * 1000000 # the ddd minutes = int(raw[0]) % 100 # the mm. minutes += int(f"{raw[1][:4]:0<4}") / 10000 - minutes = int(minutes / 60 * 1000000) + minutes = int((minutes * 1000000) / 60) return degrees + minutes @@ -125,12 +125,22 @@ def _read_degrees(data: List[float], index: int, neg: str) -> float: return x -def _read_int_degrees(data: List[float], index: int, neg: str) -> Tuple[int, float]: - deg = data[index] // 1000000 - minutes = data[index] % 1000000 / 10000 +def _read_deg_mins(data: List[str], index: int, neg: str) -> Tuple[int, float]: + # the degrees come in different formats and vary between latitudes and + # longitudes, which makes parsing tricky: + # for latitudes: ddmm,mmmm (0 - 7 decimal places, not zero padded) + # for longitudes: dddmm,mmmm (0 - 7 decimal places, not zero padded) + int_part, _, minutes_decimal = data[index].partition(".") + # we need to parse from right to left, minutes can only have 2 digits + minutes_int = int_part[-2:] + # the rest mus be degrees which are either 2 or 3 digits + deg = int(int_part[:-2]) + # combine the parts of the minutes, this also works when there are no + # decimal places specified in the sentence + minutes = float(f"{minutes_int}.{minutes_decimal}") if data[index + 1].lower() == neg: deg *= -1 - return (deg, minutes) + return deg, minutes def _parse_talker(data_type: bytes) -> Tuple[bytes, bytes]: @@ -490,26 +500,30 @@ def _parse_gll(self, data: List[str]) -> bool: if data is None or len(data) != 7: return False # Unexpected number of params. - data = _parse_data(_GLL, data) + parsed_data = _parse_data(_GLL, data) if data is None: return False # Params didn't parse # Latitude - self.latitude = _read_degrees(data, 0, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 0, "s") + self.latitude = _read_degrees(parsed_data, 0, "s") + self.latitude_degrees, self.latitude_minutes = _read_deg_mins( + data=data, index=0, neg="s" + ) # Longitude - self.longitude = _read_degrees(data, 2, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 2, "w") + self.longitude = _read_degrees(parsed_data, 2, "w") + self.longitude_degrees, self.longitude_minutes = _read_deg_mins( + data=data, index=2, neg="w" + ) # UTC time of position - self._update_timestamp_utc(data[4]) + self._update_timestamp_utc(parsed_data[4]) # Status Valid(A) or Invalid(V) - self.isactivedata = data[5] + self.isactivedata = parsed_data[5] # Parse FAA mode indicator - self._mode_indicator = data[6] + self._mode_indicator = parsed_data[6] return True @@ -518,44 +532,48 @@ def _parse_rmc(self, data: List[str]) -> bool: if data is None or len(data) not in (12, 13): return False # Unexpected number of params. - data = _parse_data({12: _RMC, 13: _RMC_4_1}[len(data)], data) - if data is None: + parsed_data = _parse_data({12: _RMC, 13: _RMC_4_1}[len(data)], data) + if parsed_data is None: self.fix_quality = 0 return False # Params didn't parse # UTC time of position and date - self._update_timestamp_utc(data[0], data[8]) + self._update_timestamp_utc(parsed_data[0], parsed_data[8]) # Status Valid(A) or Invalid(V) - self.isactivedata = data[1] - if data[1].lower() == "a": + self.isactivedata = parsed_data[1] + if parsed_data[1].lower() == "a": if self.fix_quality == 0: self.fix_quality = 1 else: self.fix_quality = 0 # Latitude - self.latitude = _read_degrees(data, 2, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 2, "s") + self.latitude = _read_degrees(parsed_data, 2, "s") + self.latitude_degrees, self.latitude_minutes = _read_deg_mins( + data=data, index=2, neg="s" + ) # Longitude - self.longitude = _read_degrees(data, 4, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 4, "w") + self.longitude = _read_degrees(parsed_data, 4, "w") + self.longitude_degrees, self.longitude_minutes = _read_deg_mins( + data=data, index=4, neg="w" + ) # Speed over ground, knots - self.speed_knots = data[6] + self.speed_knots = parsed_data[6] # Track made good, degrees true - self.track_angle_deg = data[7] + self.track_angle_deg = parsed_data[7] # Magnetic variation - if data[9] is None or data[10] is None: + if parsed_data[9] is None or parsed_data[10] is None: self._magnetic_variation = None else: - self._magnetic_variation = _read_degrees(data, 9, "w") + self._magnetic_variation = _read_degrees(parsed_data, 9, "w") # Parse FAA mode indicator - self._mode_indicator = data[11] + self._mode_indicator = parsed_data[11] return True @@ -564,37 +582,41 @@ def _parse_gga(self, data: List[str]) -> bool: if data is None or len(data) != 14: return False # Unexpected number of params. - data = _parse_data(_GGA, data) - if data is None: + parsed_data = _parse_data(_GGA, data) + if parsed_data is None: self.fix_quality = 0 return False # Params didn't parse # UTC time of position - self._update_timestamp_utc(data[0]) + self._update_timestamp_utc(parsed_data[0]) # Latitude - self.latitude = _read_degrees(data, 1, "s") - self.latitude_degrees, self.latitude_minutes = _read_int_degrees(data, 1, "s") + self.latitude = _read_degrees(parsed_data, 1, "s") + self.longitude_degrees, self.longitude_minutes = _read_deg_mins( + data=data, index=3, neg="w" + ) # Longitude - self.longitude = _read_degrees(data, 3, "w") - self.longitude_degrees, self.longitude_minutes = _read_int_degrees(data, 3, "w") + self.longitude = _read_degrees(parsed_data, 3, "w") + self.latitude_degrees, self.latitude_minutes = _read_deg_mins( + data=data, index=1, neg="s" + ) # GPS quality indicator - self.fix_quality = data[5] + self.fix_quality = parsed_data[5] # Number of satellites in use, 0 - 12 - self.satellites = data[6] + self.satellites = parsed_data[6] # Horizontal dilution of precision - self.horizontal_dilution = data[7] + self.horizontal_dilution = parsed_data[7] # Antenna altitude relative to mean sea level - self.altitude_m = _parse_float(data[8]) + self.altitude_m = _parse_float(parsed_data[8]) # data[9] - antenna altitude unit, always 'M' ??? # Geoidal separation relative to WGS 84 - self.height_geoid = _parse_float(data[10]) + self.height_geoid = _parse_float(parsed_data[10]) # data[11] - geoidal separation unit, always 'M' ??? # data[12] - Age of differential GPS data, can be null diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index eb8de9b..e2c09f6 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -15,6 +15,7 @@ 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 @@ -177,6 +178,10 @@ def test_GPS_update_rmc_no_magnetic_variation(): assert gps.timestamp_utc == exp_time assert gps.latitude == pytest.approx(12.57613) assert gps.longitude == pytest.approx(1.385391) + assert gps.latitude_degrees == 12 + assert gps.longitude_degrees == 1 + assert gps.latitude_minutes == 34.5678 + assert gps.longitude_minutes == 23.12345 assert gps.fix_quality == 1 assert gps.fix_quality_3d == 0 assert gps.speed_knots == 0.45 @@ -324,6 +329,10 @@ def test_GPS_update_from_GLL(): assert gps.timestamp_utc == exp_time assert gps.latitude == pytest.approx(49.27417) assert gps.longitude == pytest.approx(-123.1853) + assert gps.latitude_degrees == 49 + assert gps.longitude_degrees == -123 + assert gps.latitude_minutes == 16.45 + assert gps.longitude_minutes == 11.12 assert gps.isactivedata == "A" assert gps._mode_indicator == "A" assert gps.fix_quality == 0 @@ -335,7 +344,7 @@ def test_GPS_update_from_GLL(): def test_GPS_update_from_RMC(): - r = b"$GNRMC,001031.00,A,4404.13993,N,12118.86023,W,0.146,084.4,100117,,,A*5d\r\n" + r = b"$GNRMC,001031.00,A,4404.1399,N,12118.8602,W,0.146,084.4,100117,,,A*5d\r\n" # TODO: length 13 and 14 version with mock.patch.object(GPS, "readline", return_value=r): gps = GPS(uart=UartMock()) @@ -349,6 +358,10 @@ def test_GPS_update_from_RMC(): assert gps.has_3d_fix is False assert gps.latitude == pytest.approx(44.069) assert gps.longitude == pytest.approx(-121.3143) + assert gps.latitude_degrees == 44 + assert gps.longitude_degrees == -121 + assert gps.latitude_minutes == 4.1399 + assert gps.longitude_minutes == 18.8602 assert gps.speed_knots == 0.146 assert gps.track_angle_deg == 84.4 assert gps._magnetic_variation is None @@ -364,6 +377,10 @@ def test_GPS_update_from_GGA(): assert gps.timestamp_utc == exp_time assert gps.latitude == pytest.approx(48.1173) assert gps.longitude == pytest.approx(11.51667) + assert gps.latitude_degrees == 48 + assert gps.longitude_degrees == 11 + assert gps.latitude_minutes == 7.038 + assert gps.longitude_minutes == 31.000 assert gps.fix_quality == 1 assert gps.fix_quality_3d == 0 assert gps.satellites == 8 @@ -467,3 +484,19 @@ def test_GPS_update_from_GSV_both_parts_sats_are_removed(): assert gps.update() assert gps.satellites == 2 assert set(gps.sats.keys()) == {"GP12", "GP14", "GP13", "GP15"} + + +@pytest.mark.parametrize( + ("input_str", "exp", "neg"), + ( + (["3723.2475", "N"], (37, 23.2475), "S"), + (["3723.2475", "S"], (-37, 23.2475), "s"), + (["00123.1234", "E"], (1, 23.1234), "W"), + (["00123", "E"], (1, 23), "W"), + (["1234.5678", "E"], (12, 34.5678), "W"), + (["3723.2475123", "N"], (37, 23.2475123), "S"), + (["3723", "N"], (37, 23), "S"), + ), +) +def test_read_min_secs(input_str, exp, neg): + assert _read_deg_mins(data=input_str, index=0, neg=neg) == exp diff --git a/tox.ini b/tox.ini index 6fe1a7a..9383bb5 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ envlist = py37 [testenv] -deps = -rrequirements-dev.txt +deps = -roptional_requirements.txt commands = coverage erase From 01bcf65ece2a83e8e7d15db6d1f9bc5606866890 Mon Sep 17 00:00:00 2001 From: Jonas Kittner Date: Sun, 29 Oct 2023 17:46:02 +0100 Subject: [PATCH 68/80] fix float formatters in example with longitude_degrees being always ints --- examples/gps_simpletest.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 6434f27..ecf1ca7 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -5,7 +5,6 @@ # Will wait for a fix and print a message every second with the current location # and other details. import time -import math import board import busio @@ -85,13 +84,13 @@ print("Latitude: {0:.6f} degrees".format(gps.latitude)) print("Longitude: {0:.6f} degrees".format(gps.longitude)) print( - "Precise Latitude: {:3.0f} degs, {:2.4f} mins".format( - math.floor(gps.latitude_degrees), gps.latitude_minutes + "Precise Latitude: {} degs, {:2.4f} mins".format( + gps.latitude_degrees, gps.latitude_minutes ) ) print( - "Precise Longitude: {:3.0f} degs, {:2.4f} mins".format( - math.floor(gps.longitude_degrees), gps.longitude_minutes + "Precise Longitude: {} degs, {:2.4f} mins".format( + gps.longitude_degrees, gps.longitude_minutes ) ) print("Fix quality: {}".format(gps.fix_quality)) From 500559b0d8dc14d56a1515c82d7e8bc94565c6d2 Mon Sep 17 00:00:00 2001 From: Jonas Kittner Date: Thu, 18 Jan 2024 17:00:50 +0100 Subject: [PATCH 69/80] fix typo --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 4d3ec99..ae5ab5c 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -133,7 +133,7 @@ def _read_deg_mins(data: List[str], index: int, neg: str) -> Tuple[int, float]: int_part, _, minutes_decimal = data[index].partition(".") # we need to parse from right to left, minutes can only have 2 digits minutes_int = int_part[-2:] - # the rest mus be degrees which are either 2 or 3 digits + # the rest must be degrees which are either 2 or 3 digits deg = int(int_part[:-2]) # combine the parts of the minutes, this also works when there are no # decimal places specified in the sentence From f89329a981b758b61df27bb9c5930a16e0ee0108 Mon Sep 17 00:00:00 2001 From: Jonas Kittner Date: Thu, 18 Jan 2024 17:02:39 +0100 Subject: [PATCH 70/80] remove tox --- tox.ini | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 tox.ini diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 9383bb5..0000000 --- a/tox.ini +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: 2021 Jonas Kittner -# -# SPDX-License-Identifier: Unlicense -[tox] -envlist = py37 - -[testenv] -deps = -roptional_requirements.txt - -commands = - coverage erase - coverage run -m pytest tests/ - coverage report From 35f4904de310e1168fc5160065244ec5fa2fd85c Mon Sep 17 00:00:00 2001 From: Jonas Kittner Date: Fri, 19 Jan 2024 17:25:24 +0100 Subject: [PATCH 71/80] replace partition with split partition is not available on all boards --- adafruit_gps.py | 6 +++++- tests/adafruit_gps_test.py | 14 +++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index ae5ab5c..163ade2 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -130,7 +130,11 @@ def _read_deg_mins(data: List[str], index: int, neg: str) -> Tuple[int, float]: # longitudes, which makes parsing tricky: # for latitudes: ddmm,mmmm (0 - 7 decimal places, not zero padded) # for longitudes: dddmm,mmmm (0 - 7 decimal places, not zero padded) - int_part, _, minutes_decimal = data[index].partition(".") + if "." in data[index]: + int_part, minutes_decimal = data[index].split(".") + else: + int_part, minutes_decimal = data[index], 0 + # we need to parse from right to left, minutes can only have 2 digits minutes_int = int_part[-2:] # the rest must be degrees which are either 2 or 3 digits diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index e2c09f6..291fd09 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -489,13 +489,13 @@ def test_GPS_update_from_GSV_both_parts_sats_are_removed(): @pytest.mark.parametrize( ("input_str", "exp", "neg"), ( - (["3723.2475", "N"], (37, 23.2475), "S"), - (["3723.2475", "S"], (-37, 23.2475), "s"), - (["00123.1234", "E"], (1, 23.1234), "W"), - (["00123", "E"], (1, 23), "W"), - (["1234.5678", "E"], (12, 34.5678), "W"), - (["3723.2475123", "N"], (37, 23.2475123), "S"), - (["3723", "N"], (37, 23), "S"), + (["3723.2475", "n"], (37, 23.2475), "s"), + (["3723.2475", "s"], (-37, 23.2475), "s"), + (["00123.1234", "e"], (1, 23.1234), "w"), + (["00123", "e"], (1, 23), "w"), + (["1234.5678", "e"], (12, 34.5678), "w"), + (["3723.2475123", "n"], (37, 23.2475123), "s"), + (["3723", "n"], (37, 23), "s"), ), ) def test_read_min_secs(input_str, exp, neg): From 41f15963f7bbb308064f20f22a69b7cc3238da41 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 22 Mar 2024 11:54:01 -0400 Subject: [PATCH 72/80] Fix typo in _parse_gil() --- adafruit_gps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 163ade2..82b4c0b 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -505,7 +505,7 @@ def _parse_gll(self, data: List[str]) -> bool: if data is None or len(data) != 7: return False # Unexpected number of params. parsed_data = _parse_data(_GLL, data) - if data is None: + if parsed_data is None: return False # Params didn't parse # Latitude From d43cdabb32fd774403d99c4a45aeeadea8c5542b Mon Sep 17 00:00:00 2001 From: snkYmkrct Date: Thu, 3 Oct 2024 17:09:17 +0200 Subject: [PATCH 73/80] Add displayio example --- examples/gps_displayio_simpletest.py | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/gps_displayio_simpletest.py diff --git a/examples/gps_displayio_simpletest.py b/examples/gps_displayio_simpletest.py new file mode 100644 index 0000000..07480ae --- /dev/null +++ b/examples/gps_displayio_simpletest.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: 2024 +# SPDX-License-Identifier: MIT + +import time +import board +from adafruit_display_text.label import Label +from displayio import Group +from terminalio import FONT + +import adafruit_gps + +# import busio +# uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10) +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector + +# Create a GPS module instance. +# gps = adafruit_gps.GPS(uart, debug=False) # Use UART +gps = adafruit_gps.GPS_GtopI2C(i2c, debug=False) # Use I2C interface + +# Turn on the basic GGA and RMC info (what you typically want) +gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") + +# Set update rate to once a second 1hz (what you typically want) +gps.send_command(b"PMTK220,1000") + + +# Example written for boards with built-in displays +display = board.DISPLAY + +# Create a main_group to hold anything we want to show on the display. +main_group = Group() + +# Create a Label to show the readings. If you have a very small +# display you may need to change to scale=1. +display_output_label = Label(FONT, text="", scale=2) + +# Place the label near the top left corner with anchored positioning +display_output_label.anchor_point = (0, 0) +display_output_label.anchored_position = (4, 4) + +# Add the label to the main_group +main_group.append(display_output_label) + +# Set the main_group as the root_group of the display +display.root_group = main_group + + +last_print = time.monotonic() + +# Begin main loop +while True: + gps.update() + + current = time.monotonic() + # Update display data every second + if current - last_print >= 1.0: + last_print = current + if not gps.has_fix: + # Try again if we don't have a fix yet. + display_output_label.text = "Waiting for fix..." + continue + # We have a fix! (gps.has_fix is true) + t = gps.timestamp_utc + + # Update the label.text property to change the text on the display + display_output_label.text = f"Timestamp (UTC): \ + \n{t.tm_mday}/{t.tm_mon}/{t.tm_year} {t.tm_hour}:{t.tm_min:02}:{t.tm_sec:02}\ + \nLat: {gps.latitude:.6f}\ + \nLong: {gps.longitude:.6f}" From dffaebcc2379ce82ede0db220fad5d498eb9fad3 Mon Sep 17 00:00:00 2001 From: snkYmkrct Date: Mon, 7 Oct 2024 18:13:16 +0200 Subject: [PATCH 74/80] Add NMEA VTG sentence parse for the km/h speed value --- adafruit_gps.py | 39 +++++++++++++++++++++++++++++++++++++- examples/gps_simpletest.py | 4 ++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/adafruit_gps.py b/adafruit_gps.py index 82b4c0b..aa98d46 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -53,8 +53,9 @@ _GSV15 = 7 _GSV19 = 8 _RMC_4_1 = 9 +_VTG = 10 _ST_MIN = _GLL -_ST_MAX = _RMC_4_1 +_ST_MAX = _VTG _SENTENCE_PARAMS = ( # 0 - _GLL @@ -77,6 +78,8 @@ "iiiiiiIiiiIiiiIiiiI", # 9 - _RMC_4_1 "scdcdcffsDCCC", + # 10 - _VTG + "fcFCfcfcC", ) @@ -202,6 +205,12 @@ def _parse_data(sentence_type: int, data: List[str]) -> Optional[List]: elif pti == "f": # A floating point number params.append(_parse_float(dti)) + elif pti == "F": + # A floating point number or Nothing + if nothing: + params.append(None) + else: + params.append(_parse_float(dti)) elif pti == "i": # An integer params.append(_parse_int(dti)) @@ -289,6 +298,8 @@ def __init__(self, uart: UART, debug: bool = False) -> None: """Geoidal separation relative to WGS 84""" self.speed_knots = None """Ground speed in knots""" + self.speed_kmh = None + """Ground speed in km/h""" self.track_angle_deg = None """Track angle in degrees""" self._sats = None # Temporary holder for information from GSV messages @@ -368,6 +379,8 @@ def update(self) -> bool: result = self._parse_gsv(talker, args) elif sentence_type == b"GSA": # GPS DOP and active satellites result = self._parse_gsa(talker, args) + elif sentence_type == b"VTG": # Ground speed + result = self._parse_vtg(args) return result @@ -499,6 +512,30 @@ def _update_timestamp_utc(self, time_utc: str, date: Optional[str] = None) -> No (year, month, day, hours, mins, secs, 0, 0, -1) ) + def _parse_vtg(self, data: List[str]) -> bool: + # VTG - Course Over Ground and Ground Speed + + if data is None or len(data) != 9: + return False # Unexpected number of params + + parsed_data = _parse_data(_VTG, data) + if parsed_data is None: + return False # Params didn't parse + + # Track made good, degrees true + self.track_angle_deg = parsed_data[0] + + # Speed over ground, knots + self.speed_knots = parsed_data[4] + + # Speed over ground, kilometers / hour + self.speed_kmh = parsed_data[6] + + # Parse FAA mode indicator + self._mode_indicator = parsed_data[8] + + return True + def _parse_gll(self, data: List[str]) -> bool: # GLL - Geographic Position - Latitude/Longitude diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index ecf1ca7..96eeebe 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -36,6 +36,8 @@ # Turn on the basic GGA and RMC info (what you typically want) gps.send_command(b"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") +# Turn on the basic GGA and RMC info + VTG for speed in km/h +# gps.send_command(b"PMTK314,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0") # Turn on just minimum info (RMC only, location): # gps.send_command(b'PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0') # Turn off everything: @@ -102,6 +104,8 @@ print("Altitude: {} meters".format(gps.altitude_m)) if gps.speed_knots is not None: print("Speed: {} knots".format(gps.speed_knots)) + if gps.speed_kmh is not None: + print("Speed: {} km/h".format(gps.speed_kmh)) if gps.track_angle_deg is not None: print("Track angle: {} degrees".format(gps.track_angle_deg)) if gps.horizontal_dilution is not None: From 70298f4a28d6c5a57de39a698ab551a8302bd38e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 7 Oct 2024 09:24:05 -0500 Subject: [PATCH 75/80] remove deprecated get_html_theme_path() call Signed-off-by: foamyguy --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index c46666b..57e6afd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -95,7 +95,6 @@ import sphinx_rtd_theme html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From 2671a7f644cf359b8df7bf706f2edefe3c67b0ef Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Jan 2025 11:32:34 -0600 Subject: [PATCH 76/80] add sphinx configuration to rtd.yaml Signed-off-by: foamyguy --- .readthedocs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 33c2a61..88bca9f 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,6 +8,9 @@ # Required version: 2 +sphinx: + configuration: docs/conf.py + build: os: ubuntu-20.04 tools: From dd02ced33b5dbcc9048fb2d1fbad18d4fe93113c Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 14 May 2025 16:16:14 +0000 Subject: [PATCH 77/80] change to ruff --- .gitattributes | 11 + .pre-commit-config.yaml | 43 +-- .pylintrc | 399 --------------------------- README.rst | 6 +- adafruit_gps.py | 67 ++--- docs/api.rst | 6 + docs/conf.py | 8 +- examples/gps_datalogging.py | 1 + examples/gps_displayio_simpletest.py | 1 + examples/gps_echotest.py | 1 + examples/gps_satellitefix.py | 1 + examples/gps_simpletest.py | 35 +-- examples/gps_time_source.py | 19 +- ruff.toml | 108 ++++++++ tests/adafruit_gps_test.py | 60 ++-- 15 files changed, 215 insertions(+), 551 deletions(-) create mode 100644 .gitattributes delete mode 100644 .pylintrc create mode 100644 ruff.toml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..21c125c --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 70ade69..ff19dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index f945e92..0000000 --- a/.pylintrc +++ /dev/null @@ -1,399 +0,0 @@ -# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries -# -# SPDX-License-Identifier: Unlicense - -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the ignore-list. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the ignore-list. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins=pylint.extensions.no_self_use - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# disable=import-error,raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,deprecated-str-translate-call -disable=raw-checker-failed,bad-inline-option,locally-disabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,import-error,pointless-string-statement,unspecified-encoding - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=FIXME,XXX - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=board - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# expected-line-ending-format= -expected-line-ending-format=LF - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=yes - -# Minimum lines number of a similarity. -min-similarity-lines=12 - - -[BASIC] - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class names -# class-rgx=[A-Z_][a-zA-Z0-9]+$ -class-rgx=[A-Z_][a-zA-Z0-9_]+$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -# good-names=i,j,k,ex,Run,_ -good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -# max-attributes=7 -max-attributes=11 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=builtins.Exception diff --git a/README.rst b/README.rst index 0961ca0..052c987 100644 --- a/README.rst +++ b/README.rst @@ -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://raw.githubusercontent.com/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. diff --git a/adafruit_gps.py b/adafruit_gps.py index aa98d46..57a087e 100644 --- a/adafruit_gps.py +++ b/adafruit_gps.py @@ -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) diff --git a/docs/api.rst b/docs/api.rst index d5009a9..5c7cd3d 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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: diff --git a/docs/conf.py b/docs/conf.py index 57e6afd..3f5c7c9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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" diff --git a/examples/gps_datalogging.py b/examples/gps_datalogging.py index eb2d391..3c327d0 100644 --- a/examples/gps_datalogging.py +++ b/examples/gps_datalogging.py @@ -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 diff --git a/examples/gps_displayio_simpletest.py b/examples/gps_displayio_simpletest.py index 07480ae..984655d 100644 --- a/examples/gps_displayio_simpletest.py +++ b/examples/gps_displayio_simpletest.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: MIT import time + import board from adafruit_display_text.label import Label from displayio import Group diff --git a/examples/gps_echotest.py b/examples/gps_echotest.py index 9a3acda..316b0bf 100644 --- a/examples/gps_echotest.py +++ b/examples/gps_echotest.py @@ -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 diff --git a/examples/gps_satellitefix.py b/examples/gps_satellitefix.py index 3927b3a..931afe1 100644 --- a/examples/gps_satellitefix.py +++ b/examples/gps_satellitefix.py @@ -7,6 +7,7 @@ # * GSV - Satellites in view import time + import board import adafruit_gps diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index 96eeebe..cc74222 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -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") diff --git a/examples/gps_time_source.py b/examples/gps_time_source.py index 0199ca5..bd3742b 100644 --- a/examples/gps_time_source.py +++ b/examples/gps_time_source.py @@ -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)}") diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..9894228 --- /dev/null +++ b/ruff.toml @@ -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" diff --git a/tests/adafruit_gps_test.py b/tests/adafruit_gps_test.py index 291fd09..f00a497 100644 --- a/tests/adafruit_gps_test.py +++ b/tests/adafruit_gps_test.py @@ -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,7 +300,7 @@ 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'") @@ -316,7 +308,7 @@ 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", From 0cccfbaecda8d1ad26e3cc6a5d9932893566ad9d Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 4 Jun 2025 10:00:20 -0500 Subject: [PATCH 78/80] update rtd.yml file Signed-off-by: foamyguy --- .readthedocs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 88bca9f..255dafd 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,7 +12,7 @@ sphinx: configuration: docs/conf.py build: - os: ubuntu-20.04 + os: ubuntu-lts-latest tools: python: "3" From e9b3b6ebe728b3464a53d11d4414225bbedb008d Mon Sep 17 00:00:00 2001 From: Anne Barela <1911920+TheKitty@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:30:18 -0400 Subject: [PATCH 79/80] Update gps_simpletest.py Add comments for use of GP4 and GP5 for Raspberry Pi Pico per guide feedback and forum post --- examples/gps_simpletest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/gps_simpletest.py b/examples/gps_simpletest.py index cc74222..49b3b1d 100644 --- a/examples/gps_simpletest.py +++ b/examples/gps_simpletest.py @@ -15,7 +15,9 @@ # a slightly higher timeout (GPS modules typically update once a second). # These are the defaults you should use for the GPS FeatherWing. # For other boards set RX = GPS module TX, and TX = GPS module RX pins. -uart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10) +rx = board.RX # Change to board.GP4 for Raspberry Pi Pico boards +tx = board.TX # Change to board.GP5 for Raspberry Pi Pico boards +uart = busio.UART(rx, tx, baudrate=9600, timeout=10) # for a computer, use the pyserial library for uart access # import serial From f21c96756ede63ce334c5b1f0ea361471ae9ce6c Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 10 Oct 2025 16:18:40 -0500 Subject: [PATCH 80/80] remove deprecated ruff rule, workaround RTD theme property inline issue. Signed-off-by: foamyguy --- docs/_static/custom.css | 8 ++++++++ docs/conf.py | 3 +++ ruff.toml | 1 - 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 docs/_static/custom.css diff --git a/docs/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 0000000..d60cf4b --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,8 @@ +/* SPDX-FileCopyrightText: 2025 Sam Blenny + * SPDX-License-Identifier: MIT + */ + +/* Monkey patch the rtd theme to prevent horizontal stacking of short items + * see https://github.com/readthedocs/sphinx_rtd_theme/issues/1301 + */ +.py.property{display: block !important;} diff --git a/docs/conf.py b/docs/conf.py index 3f5c7c9..7f12b1e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -97,6 +97,9 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] +# Include extra css to work around rtd theme glitches +html_css_files = ["custom.css"] + # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. diff --git a/ruff.toml b/ruff.toml index 9894228..17265dd 100644 --- a/ruff.toml +++ b/ruff.toml @@ -17,7 +17,6 @@ extend-select = [ "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