0
# Stream Processing
1
2
Stream processing capabilities allow handling continuous NMEA data from files, serial ports, and other streaming sources with flexible error handling and parsing options.
3
4
## NMEAStreamReader
5
6
```python { .api }
7
class NMEAStreamReader:
8
"""Reads NMEA sentences from a stream with configurable error handling."""
9
10
def __init__(self, stream=None, errors: str = 'raise'):
11
"""
12
Create NMEAStreamReader object.
13
14
Args:
15
stream: File-like object with readline() method (optional)
16
errors: Error handling behavior:
17
- 'raise': Raise exception on parse errors (default)
18
- 'yield': Yield ParseError objects as stream elements
19
- 'ignore': Skip parse errors silently
20
"""
21
22
def next(self, data: str = None) -> Iterator[NMEASentence]:
23
"""
24
Parse data and yield NMEA sentence objects.
25
26
Args:
27
data: Optional string data to parse. If None, reads from stream.
28
29
Yields:
30
NMEASentence objects or ParseError objects (if errors='yield')
31
"""
32
33
def __iter__(self) -> Iterator[List[NMEASentence]]:
34
"""Iterator protocol support for use in for loops."""
35
36
def __next__(self) -> List[NMEASentence]:
37
"""Python 3 iterator protocol."""
38
```
39
40
### Usage Examples
41
42
```python
43
import pynmea2
44
import io
45
46
# Stream processing with string data
47
nmea_data = """$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D
48
$GPRMC,184353.07,A,1929.045,S,02410.506,E,0.13,309.62,120598,,A*70
49
invalid sentence
50
$GPGLL,1929.045,S,02410.506,E,184353.07,A,A*4C"""
51
52
stream = io.StringIO(nmea_data)
53
reader = pynmea2.NMEAStreamReader(stream, errors='ignore')
54
55
for batch in reader:
56
for msg in batch:
57
print(f"{type(msg).__name__}: {msg}")
58
59
# Manual data feeding
60
reader = pynmea2.NMEAStreamReader(errors='yield')
61
for sentence in reader.next("$GPGGA,184353.07,1929.045,S,02410.506,E,1,04,2.6,100.00,M,-33.9,M,,0000*6D\n"):
62
if isinstance(sentence, pynmea2.ParseError):
63
print(f"Parse error: {sentence}")
64
else:
65
print(f"Parsed: {sentence}")
66
67
# File processing with error handling
68
with open('nmea_log.txt', 'r') as f:
69
reader = pynmea2.NMEAStreamReader(f, errors='raise')
70
try:
71
for batch in reader:
72
for msg in batch:
73
print(f"Position: {getattr(msg, 'latitude', 'N/A')}, {getattr(msg, 'longitude', 'N/A')}")
74
except pynmea2.ParseError as e:
75
print(f"Failed to parse: {e}")
76
```
77
78
## NMEAFile
79
80
```python { .api }
81
class NMEAFile:
82
"""File reader for NMEA sentences with file-like interface."""
83
84
def __init__(self, f, *args, **kwargs):
85
"""
86
Open NMEA file for reading.
87
88
Args:
89
f: File path string or file-like object
90
*args, **kwargs: Additional arguments passed to open() if f is a path
91
"""
92
93
def open(self, fp: str, mode: str = 'r'):
94
"""Open file at specified path."""
95
96
def close(self):
97
"""Close the file."""
98
99
def __iter__(self) -> Iterator[NMEASentence]:
100
"""Iterate through file yielding NMEA sentences."""
101
102
def next(self) -> NMEASentence:
103
"""Read next NMEA sentence from file."""
104
105
def readline(self) -> NMEASentence:
106
"""Read next NMEA sentence (alias for next())."""
107
108
def read(self) -> List[NMEASentence]:
109
"""Read all sentences as list."""
110
111
def parse(self, s: str) -> NMEASentence:
112
"""Parse single sentence string."""
113
114
def __enter__(self):
115
"""Context manager entry."""
116
117
def __exit__(self, exc_type, exc_val, exc_tb):
118
"""Context manager exit."""
119
```
120
121
### Usage Examples
122
123
```python
124
import pynmea2
125
126
# Read NMEA file with context manager
127
with pynmea2.NMEAFile('gps_log.nmea') as nmea_file:
128
for sentence in nmea_file:
129
if hasattr(sentence, 'latitude'):
130
print(f"Position: {sentence.latitude}, {sentence.longitude}")
131
132
# Read all sentences at once
133
nmea_file = pynmea2.NMEAFile('gps_log.nmea')
134
all_sentences = nmea_file.read()
135
nmea_file.close()
136
137
print(f"Total sentences: {len(all_sentences)}")
138
gga_sentences = [s for s in all_sentences if isinstance(s, pynmea2.GGA)]
139
print(f"GGA sentences: {len(gga_sentences)}")
140
141
# Read file line by line
142
nmea_file = pynmea2.NMEAFile('gps_log.nmea')
143
try:
144
while True:
145
sentence = nmea_file.next()
146
print(sentence)
147
except StopIteration:
148
pass
149
finally:
150
nmea_file.close()
151
152
# Open file with custom parameters
153
nmea_file = pynmea2.NMEAFile('gps_log.nmea', mode='r', encoding='utf-8')
154
```
155
156
## Serial Port Processing
157
158
```python
159
import pynmea2
160
import serial
161
import io
162
163
# Process NMEA data from serial GPS device
164
ser = serial.Serial('/dev/ttyUSB0', 4800, timeout=5.0)
165
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
166
167
reader = pynmea2.NMEAStreamReader(sio, errors='ignore')
168
169
try:
170
for batch in reader:
171
for msg in batch:
172
if isinstance(msg, pynmea2.GGA) and msg.is_valid:
173
print(f"GPS Fix: {msg.latitude:.6f}, {msg.longitude:.6f}")
174
print(f"Altitude: {msg.altitude}m, Satellites: {msg.num_sats}")
175
elif isinstance(msg, pynmea2.RMC) and msg.is_valid:
176
print(f"Speed: {msg.spd_over_grnd} knots, Course: {msg.true_course}°")
177
178
except KeyboardInterrupt:
179
print("Stopping GPS monitoring")
180
finally:
181
ser.close()
182
```
183
184
## Error Handling Strategies
185
186
### Raise Errors (Default)
187
188
```python
189
reader = pynmea2.NMEAStreamReader(stream, errors='raise')
190
try:
191
for batch in reader:
192
for msg in batch:
193
process_message(msg)
194
except pynmea2.ParseError as e:
195
print(f"Parse failed: {e}")
196
# Handle error and potentially continue
197
```
198
199
### Yield Errors
200
201
```python
202
reader = pynmea2.NMEAStreamReader(stream, errors='yield')
203
for batch in reader:
204
for item in batch:
205
if isinstance(item, pynmea2.ParseError):
206
print(f"Skipping bad sentence: {item}")
207
else:
208
process_message(item)
209
```
210
211
### Ignore Errors
212
213
```python
214
reader = pynmea2.NMEAStreamReader(stream, errors='ignore')
215
for batch in reader:
216
for msg in batch:
217
# Only valid sentences are yielded
218
process_message(msg)
219
```
220
221
## Performance Considerations
222
223
```python
224
import pynmea2
225
226
# Efficient processing of large files
227
def process_large_nmea_file(filename):
228
gga_count = 0
229
positions = []
230
231
with pynmea2.NMEAFile(filename) as f:
232
for sentence in f:
233
if isinstance(sentence, pynmea2.GGA) and sentence.is_valid:
234
gga_count += 1
235
positions.append((sentence.latitude, sentence.longitude))
236
237
# Process in batches to manage memory
238
if len(positions) >= 1000:
239
process_position_batch(positions)
240
positions.clear()
241
242
# Process remaining positions
243
if positions:
244
process_position_batch(positions)
245
246
return gga_count
247
248
def process_position_batch(positions):
249
# Batch processing logic
250
pass
251
```