0
# Utility Functions and Mixins
1
2
Utility functions and mixin classes provide coordinate conversion, timestamp parsing, validation helpers, and extended functionality for NMEA sentence objects.
3
4
## Utility Functions
5
6
### Timestamp and Date Parsing
7
8
```python { .api }
9
def timestamp(s: str) -> datetime.time:
10
"""
11
Convert HHMMSS[.ss] ASCII string to datetime.time object.
12
13
Args:
14
s: Time string in format HHMMSS or HHMMSS.ss
15
16
Returns:
17
datetime.time object with UTC timezone
18
19
Example:
20
timestamp('184353.07') -> datetime.time(18, 43, 53, 70000, tzinfo=datetime.timezone.utc)
21
"""
22
23
def datestamp(s: str) -> datetime.date:
24
"""
25
Convert DDMMYY ASCII string to datetime.date object.
26
27
Args:
28
s: Date string in format DDMMYY
29
30
Returns:
31
datetime.date object
32
33
Example:
34
datestamp('120598') -> datetime.date(1998, 5, 12)
35
"""
36
```
37
38
### Coordinate Conversion
39
40
```python { .api }
41
def dm_to_sd(dm: str) -> float:
42
"""
43
Convert degrees/minutes format to signed decimal degrees.
44
45
Args:
46
dm: Coordinate in DDDMM.MMMM format (e.g., '12319.943281')
47
48
Returns:
49
Decimal degrees as float
50
51
Raises:
52
ValueError: If format is invalid
53
54
Example:
55
dm_to_sd('12319.943281') -> 123.33238801666667
56
"""
57
```
58
59
### Status Validation
60
61
```python { .api }
62
def valid(s: str) -> bool:
63
"""
64
Check if status flag equals 'A' (active/valid).
65
66
Args:
67
s: Status character
68
69
Returns:
70
True if s == 'A', False otherwise
71
"""
72
```
73
74
### Usage Examples
75
76
```python
77
import pynmea2
78
from pynmea2 import timestamp, datestamp, dm_to_sd, valid
79
80
# Parse timestamps
81
time_obj = timestamp('184353.07')
82
print(f"Time: {time_obj}") # 18:43:53.070000+00:00
83
print(f"Hour: {time_obj.hour}") # 18
84
print(f"Microseconds: {time_obj.microsecond}")# 70000
85
86
# Parse dates
87
date_obj = datestamp('120598')
88
print(f"Date: {date_obj}") # 1998-05-12
89
90
# Convert coordinates
91
decimal_deg = dm_to_sd('12319.943281')
92
print(f"Decimal degrees: {decimal_deg}") # 123.33238801666667
93
94
# Validate status
95
print(f"Valid status A: {valid('A')}") # True
96
print(f"Valid status V: {valid('V')}") # False
97
```
98
99
## Mixin Classes
100
101
### LatLonFix
102
103
```python { .api }
104
class LatLonFix:
105
"""
106
Mixin adding latitude/longitude properties as signed decimal degrees.
107
108
Requires sentence to have lat, lat_dir, lon, lon_dir fields.
109
"""
110
111
@property
112
def latitude(self) -> float:
113
"""
114
Latitude in signed decimal degrees.
115
116
Returns:
117
Positive for North, negative for South
118
"""
119
120
@property
121
def longitude(self) -> float:
122
"""
123
Longitude in signed decimal degrees.
124
125
Returns:
126
Positive for East, negative for West
127
"""
128
129
@property
130
def latitude_minutes(self) -> float:
131
"""Minutes component of latitude."""
132
133
@property
134
def longitude_minutes(self) -> float:
135
"""Minutes component of longitude."""
136
137
@property
138
def latitude_seconds(self) -> float:
139
"""Seconds component of latitude."""
140
141
@property
142
def longitude_seconds(self) -> float:
143
"""Seconds component of longitude."""
144
```
145
146
### DatetimeFix
147
148
```python { .api }
149
class DatetimeFix:
150
"""
151
Mixin adding datetime property combining date and time fields.
152
153
Requires sentence to have datestamp and timestamp fields.
154
"""
155
156
@property
157
def datetime(self) -> datetime.datetime:
158
"""Combined date and time as datetime object."""
159
```
160
161
### Validation Mixins
162
163
```python { .api }
164
class ValidStatusFix:
165
"""
166
Mixin adding is_valid property checking status == 'A'.
167
168
Requires sentence to have status field.
169
"""
170
171
@property
172
def is_valid(self) -> bool:
173
"""True if status field equals 'A'."""
174
175
class ValidGGAFix:
176
"""
177
Mixin for GGA sentence validity checking.
178
179
Requires sentence to have gps_qual field.
180
"""
181
182
@property
183
def is_valid(self) -> bool:
184
"""True if GPS quality indicates valid fix (1-5)."""
185
186
class ValidGSAFix:
187
"""
188
Mixin for GSA sentence validity checking.
189
190
Requires sentence to have mode_fix_type field.
191
"""
192
193
@property
194
def is_valid(self) -> bool:
195
"""True if fix type is 2D or 3D (2 or 3)."""
196
197
class ValidRMCStatusFix(ValidStatusFix):
198
"""
199
Extended validity checking for RMC sentences.
200
201
Checks status, mode_indicator, and nav_status fields.
202
"""
203
204
@property
205
def is_valid(self) -> bool:
206
"""True if all status indicators are valid."""
207
```
208
209
## Usage Examples
210
211
```python
212
import pynmea2
213
214
# LatLonFix example
215
msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D")
216
217
# Decimal degrees (most useful)
218
print(f"Position: {msg.latitude:.6f}, {msg.longitude:.6f}") # -19.484083, 24.175100
219
220
# Component access
221
print(f"Lat minutes: {msg.latitude_minutes:.3f}") # 29.045
222
print(f"Lon minutes: {msg.longitude_minutes:.3f}") # 10.506
223
print(f"Lat seconds: {msg.latitude_seconds:.1f}") # 2.7
224
print(f"Lon seconds: {msg.longitude_seconds:.1f}") # 30.4
225
226
# Format coordinates in different ways
227
abs_lat, abs_lon = abs(msg.latitude), abs(msg.longitude)
228
print(f"DMS: {abs_lat:.0f}°{msg.latitude_minutes:.3f}'{msg.lat_dir} {abs_lon:.0f}°{msg.longitude_minutes:.3f}'{msg.lon_dir}")
229
230
# DatetimeFix example
231
msg = pynmea2.parse("$GPRMC,184353.07,A,1929.045,S,02410.506,E,0.13,309.62,120598,,A*70")
232
print(f"Date: {msg.datestamp}") # 1998-05-12
233
print(f"Time: {msg.timestamp}") # 18:43:53.070000+00:00
234
print(f"Combined: {msg.datetime}") # 1998-05-12 18:43:53.070000+00:00
235
236
# Validation examples
237
gga_msg = pynmea2.parse("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D")
238
print(f"GGA valid: {gga_msg.is_valid}") # True (GPS quality = 1)
239
240
rmc_msg = pynmea2.parse("$GPRMC,184353.07,A,1929.045,S,02410.506,E,0.13,309.62,120598,,A*70")
241
print(f"RMC valid: {rmc_msg.is_valid}") # True (status = A)
242
243
gsa_msg = pynmea2.parse("$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39")
244
print(f"GSA valid: {gsa_msg.is_valid}") # True (3D fix)
245
```
246
247
## Timezone Support
248
249
```python { .api }
250
class TZInfo(datetime.tzinfo):
251
"""Custom timezone info class for local time zones."""
252
253
def __init__(self, hh: int, mm: int):
254
"""
255
Initialize with hour and minute offset from UTC.
256
257
Args:
258
hh: Hour offset from UTC
259
mm: Minute offset from UTC
260
"""
261
262
def utcoffset(self, dt: datetime.datetime) -> datetime.timedelta:
263
"""Return offset from UTC."""
264
265
def tzname(self, dt: datetime.datetime) -> str:
266
"""Return timezone name."""
267
268
def dst(self, dt: datetime.datetime) -> datetime.timedelta:
269
"""Return DST offset (always 0)."""
270
```
271
272
### Usage Example
273
274
```python
275
import pynmea2
276
from pynmea2 import TZInfo
277
278
# Create timezone info for UTC+5:30 (India Standard Time)
279
ist = TZInfo(5, 30)
280
281
msg = pynmea2.parse("$GPZDA,184353.07,12,05,1998,05,30*4F")
282
utc_time = msg.datetime
283
284
# Convert to local time
285
local_time = utc_time.replace(tzinfo=pynmea2.datetime.timezone.utc).astimezone(ist)
286
print(f"UTC: {utc_time}") # 1998-05-12 18:43:53.070000
287
print(f"Local: {local_time}") # 1998-05-13 00:13:53.070000+05:30
288
```
289
290
## SeaTalk Support
291
292
```python { .api }
293
class SeaTalk:
294
"""
295
Mixin adding SeaTalk protocol functionality.
296
297
Based on Thomas Knauf's SeaTalk documentation.
298
Requires sentence to have cmd field.
299
"""
300
301
byte_to_command: Dict[str, str] # Mapping of command bytes to descriptions
302
303
@property
304
def command_name(self) -> str:
305
"""Get human-readable command name from cmd field."""
306
```
307
308
### Usage Example
309
310
```python
311
import pynmea2
312
313
# Parse SeaTalk sentence (ALK sentence type)
314
msg = pynmea2.parse("$STALK,84,96,82,00,00,00,08,02,00*77")
315
print(f"Command: {msg.cmd}") # 84
316
print(f"Command name: {msg.command_name}") # Compass heading Autopilot course and Rudder position
317
```