Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/checkpr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install deploy dependencies
Expand Down
14 changes: 6 additions & 8 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ jobs:
release-build:
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.13

Expand All @@ -24,7 +23,7 @@ jobs:
python -m build

- name: Upload distributions # upload build artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: release-dists
path: dist/
Expand All @@ -35,13 +34,12 @@ jobs:
name: Upload release to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
name: pypi
permissions:
id-token: write
steps:

- name: Download all the dists # download build artifacts saved in previous job
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
name: release-dists
path: dist/
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ jobs:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install deploy dependencies
Expand Down
3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
"C_Cpp.copilotHover": "disabled",
"chat.agent.enabled": false,
"chat.commandCenter.enabled": false,
"chat.notifyWindowOnConfirmation": false,
"chat.notifyWindowOnConfirmation": "off",
"telemetry.feedback.enabled": false,
"python.analysis.addHoverSummaries": false,
"python-envs.defaultEnvManager": "ms-python.python:venv",
"python-envs.pythonProjects": [],
"python.defaultInterpreterPath": "${userHome}/pygpsclient/bin/python3",
"python.testing.unittestEnabled": true,
"python.testing.unittestArgs": [
Expand Down
37 changes: 20 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,40 +173,43 @@ The `parse()` method accepts the following optional keyword arguments:
* `parsebitfield`: 1 = parse bitfields ('X' type properties) as individual bit flags, where defined (default), 0 = leave bitfields as byte sequences
* `msgmode`: `GET` (0) (default), `SET` (1), `POLL` (2)

Example A - parsing VERSION output message:
Example A - parsing BESTNAV output message:
```python
from pyunigps import GET, VALCKSUM, UNIReader

msg = UNIReader.parse(
b'\xaaD\xb5\x00\x11\x004\x01\x00\x00f\t\x8f\xf4\x0e\x02\x00\x00\x00\x00\x00\x00\x00\x00M982R4.10Build5251 HRPT00-S10C-P - ffff48ffff0fffff 2021/11/26 #\x87\x83\xb9'
,
b"\xaaD\xb5YF\x08x\x00\x00\xa0e\t\xe8\...\x98\x15\xa5\xf1",
validate=VALCKSUM,
parsebitfield=1,
)
print(msg)
```
```
<UNI(VERSION, cpuidle=0, timeref=0, timestatus=0, wno=2406, tow=34534543, version=0, leapsecond=0, delay=0, device=18, swversion=R4.10Build5251, authtype=HRPT00-S10C-P, psn=-, efuseid=ffff48ffff0fffff, comptime=2021/11/26)>
"<UNI(BESTNAV, cpuidle=89, timeref=0, timestatus=160, wno=2405, tow=75521000, version=0, leapsecond=18, delay=13, solstatus=0, postype=16, lat=43.450634678833644, lon=-1.1303087675041795, hmsl=36.32093767914921, undulation=51.67765808105469, datumid=61, latstd=2.6222991943359375, lonstd=1.9253981113433838, hmslstd=2.7721946239471436, stationid=0, diffage=0.0, solage=0.0, numsvs=25, numsolnsvs=21, reserved1=21, reserved2=0, reserved3=1, rtkverify=0, psrionocorr=1, gale1=1, gale5b=0, gale5a=0, bdsb1l=1, bdsb3l=0, bdsb2a=0, bdsb1c=0, gpsl1=1, gpsl2=0, gpsl5=0, glol1=1, glol2=0, bdsb2l=0, vsolstatus=0, veltype=8, latency=0.0, age=0.0, horspd=0.06034742542911377, trkgnd=192.53216796929004, vertspd=-0.001533079795667136, verspdstd=0.14649531245231628, horspdstd=0.11643162369728088)>"
```

The `UNIMessage` object exposes different public attributes depending on its message type or 'identity'. Attributes which are enumerations may have corresponding decodes in `pyunigps.unitypes_decodes` e.g. the `VERSION` message has the following attributes:
The `UNIMessage` object exposes different public attributes depending on its message type or 'identity'. Helper methods are available to convert position coordinates into different formats. Attributes which are enumerations may have corresponding decodes in `pyunigps.unitypes_decodes` e.g. the `BESTNAV` message has the following attributes:

```python
from pyunigps import DEVICE
print(msg)
from pyunigps import POSTYPE, PSRIONOCORR, SOLSTATUS, latlon2dms, llh2iso6709, wnotow2utc
print(msg.identity)
print(msg.device)
print(DEVICE[msg.device])
print(swversion)
print(comptime)
print(wnotow2utc(msg.wno, msg.tow, msg.leapsecond))
print(msg.lat, msg.lon, msg.hmsl)
print(latlon2dms(msg.lat, msg.lon))
print(llh2iso6709(msg.lat, msg.lon, msg.hmsl))
print(msg.solstatus, SOLSTATUS[msg.solstatus])
print(msg.postype, POSTYPE[msg.postype])
print(msg.psrionocorr, PSRIONOCORR[msg.psrionocorr])
```
```
<UNI(VERSION, cpuidle=0, timeref=0, timestatus=0, wno=2406, tow=34534543, version=0, leapsecond=18, delay=0, device=18, swversion=R4.10Build5251, authtype=HRPT00-S10C-P, psn=-, efuseid=ffff48ffff0fffff, comptime=2021/11/26)>
VERSION
18
UM980
R4.10Build5251
2021/11/26
BESTNAV
2026-02-08 20:58:23+00:00
43.450634678833644, -1.1303087675041795, 36.32093767914921
('43°27′2.28484″N', '1°7′49.11156″W')
+43.450634678833644-1.1303087675041794+36.32093767914921CRSWGS_84/
0 SOL_COMPUTED
16 SINGLE
1 Klobuchareph
```

The `payload` attribute always contains the raw payload as bytes. Attributes within repeating groups are parsed with a two-digit suffix (prn_01, prn_02, etc.).
Expand Down
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# pyunigps Release Notes

### RELEASE 1.0.0

1. Update status to Stable.
1. Remove duplicate keys from DEVICE decode.
1. Inherit helper methods from pynmeagps.

### RELEASE 0.2.0

1. Update for Protocol Specification R1.13 Dec 2025.
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ readme = "README.md"
requires-python = ">=3.10"
classifiers = [
"Operating System :: OS Independent",
"Development Status :: 4 - Beta",
"Development Status :: 5 - Production/Stable",
"Environment :: MacOS X",
"Environment :: Win32 (MS Windows)",
"Environment :: X11 Applications",
Expand All @@ -32,7 +32,7 @@ classifiers = [
"Topic :: Scientific/Engineering :: GIS",
]

dependencies = ["pynmeagps >= 1.1.0", "pyrtcm>=1.1.10"]
dependencies = ["pynmeagps >= 1.1.2", "pyrtcm>=1.1.11"]

[project.urls]
homepage = "https://github.com/semuconsulting/pyunigps"
Expand Down
19 changes: 18 additions & 1 deletion src/pyunigps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,24 @@
:license: BSD 3-Clause
"""

from pynmeagps import SocketWrapper
from pynmeagps import (
SocketWrapper,
area,
bearing,
deg2dmm,
deg2dms,
dms2deg,
ecef2llh,
haversine,
latlon2dmm,
latlon2dms,
leapsecond,
llh2ecef,
llh2iso6709,
planar,
utc2wnotow,
wnotow2utc,
)

from pyunigps._version import __version__
from pyunigps.exceptions import (
Expand Down
2 changes: 1 addition & 1 deletion src/pyunigps/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
:license: BSD 3-Clause
"""

__version__ = "0.2.0"
__version__ = "1.0.0"
25 changes: 3 additions & 22 deletions src/pyunigps/unihelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Collection of UNI helper methods which can be used
outside the UNIMessage or UNIReader classes.

pyunigps also inherits pynmeagps helper methods.

Created on 6 Oct 2025

:author: semuadmin (Steve Smith)
Expand All @@ -14,7 +16,7 @@
from types import NoneType
from typing import Any

from pynmeagps import leapsecond
from pynmeagps import utc2wnotow

import pyunigps.exceptions as une
from pyunigps.unitypes_core import ATTTYPE, SCALROUND, U4, UNI_MSGIDS
Expand Down Expand Up @@ -492,27 +494,6 @@ def nomval(adef: str) -> Any:
return val


def utc2wnotow(utc: datetime | NoneType = None) -> tuple[int, int, int]:
"""
Get GPS Week number (wno), Time of Week (tow) in milliseconds
and leapsecond offset for given utc datetime.

GPS Epoch 0 = 6th Jan 1980

:param datetime | NoneType utc: utc datetime (defaults to now if None)
:return: wno, tow, leapsecond
:rtype: tuple[int,int, int]
"""

if utc is None:
utc = datetime.now(tz=timezone.utc)
ls = leapsecond(utc.replace(tzinfo=None)) # method is not tz aware
ts = ((utc - GPSEPOCH0).total_seconds() + ls) * 1000
wno = int((utc - GPSEPOCH0).days / 7)
tow = int(ts - wno * 604800000)
return wno, tow, ls


def val2bytes(val: Any, adef: str) -> bytes:
"""
Convert value to bytes for given UNI attribute type.
Expand Down
3 changes: 2 additions & 1 deletion src/pyunigps/unimessage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from types import NoneType

from pynmeagps import utc2wnotow

from pyunigps.exceptions import UNIMessageError
from pyunigps.unihelpers import (
attsiz,
Expand All @@ -23,7 +25,6 @@
header2bytes,
msgname2id,
nomval,
utc2wnotow,
val2bytes,
)
from pyunigps.unitypes_core import (
Expand Down
2 changes: 0 additions & 2 deletions src/pyunigps/unitypes_decodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@
24: "UM960L",
26: "UM981",
31: "UM981S",
24: "UM960L",
26: "UM981",
40: "UMD982",
41: "UMD981",
42: "UMD981S",
Expand Down
3 changes: 3 additions & 0 deletions tests/pygpsdata_nmea.log
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ $GNGSA,A,3,,,,,,,,,,,,,9.62,5.88,7.62,3*08
$GNGSA,A,3,,,,,,,,,,,,,9.62,5.88,7.62,4*0F
$GPGSV,3,1,11,01,06,014,08,12,43,207,28,14,06,049,,15,44,171,23,1*6B
$GPGSV,3,2,11,17,32,064,16,19,33,094,,20,20,251,31,21,04,354,,1*63
$GNRMC,075233.00,A,5451.99268133,N,00125.70810842,W,0.009,13.6,240226,0.2,W,A,C*75
$GNGGA,075233.00,5451.99268133,N,00125.70810842,W,1,28,0.5,103.8322,M,49.2859,M,,*63
$GNGLL,5451.99268133,N,-0125.70810842,W,075233.00,A,A*76
Loading