forked from jperson2000/GPS.Net
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NmeaInterpreter.cs
294 lines (236 loc) · 12.4 KB
/
NmeaInterpreter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using GeoFramework.Gps.IO;
namespace GeoFramework.Gps.Nmea
{
/// <summary>
/// Represents an interpreter for GPS data from the National Marine Electronics Association (NMEA).
/// </summary>
public class NmeaInterpreter : Interpreter
{
private NmeaReader _Stream;
/// <summary>
/// Represents a synchronization object used to prevent changes to GPS data when reading multiple GPS values.
/// </summary>
public object DataChangeSyncRoot = new object();
#region Constructors
public NmeaInterpreter()
{
Initialize();
}
#endregion
#region Events
/// <summary>
/// Occurs when a new line of NMEA data has arrived.
/// </summary>
[Obsolete("This event is intended for debugging. Use it to confirm that GPS data is arriving from your GPS device, but avoid it during production use to improve your application's performance.")]
public event EventHandler<NmeaSentenceEventArgs> SentenceReceived;
///// <summary>
///// Obsolete. See compiler warnings for upgrade help.
///// </summary>
//[Obsolete("In GPS.NET 3.0, valid NMEA sentences can be partially processed. Use the 'SentenceReceived' event to examine the flow of raw GPS data.")]
//public event EventHandler<NmeaSentenceEventArgs> SentenceInterpreted;
/// <summary>
/// Occurs when a packet of data has been recorded to the recording stream.
/// </summary>
public event EventHandler<NmeaSentenceEventArgs> SentenceRecorded;
protected void OnSentenceReceived(NmeaSentence sentence)
{
if (SentenceReceived != null)
SentenceReceived(this, new NmeaSentenceEventArgs(sentence));
}
protected void OnSentenceRecorded(NmeaSentence sentence)
{
if (SentenceRecorded != null)
SentenceRecorded(this, new NmeaSentenceEventArgs(sentence));
}
#endregion
#region Public Methods
public void Parse(NmeaSentence sentence)
{
/* NMEA data is parsed in a specific order to maximize data quality and also to reduce latency
* problems. The date/time is processed first to minimize latency. Then, dilution of precision
* values are processed. Finally, if precision is good enough to work with, remaining values are
* processed.
*/
// Is this a fix method message?
IFixMethodSentence fixMethodSentence = sentence as IFixMethodSentence;
if (fixMethodSentence != null)
SetFixMethod(fixMethodSentence.FixMethod);
// Is this a fix quality message?
IFixQualitySentence fixQualitySentence = sentence as IFixQualitySentence;
if (fixQualitySentence != null)
SetFixQuality(fixQualitySentence.FixQuality);
#region Process common GPS information
// If a fix is required, don't process time
if (!IsFixRequired || (IsFixRequired && IsFixed))
{
// Does this sentence support the UTC date and time?
IUtcDateTimeSentence dateTimeSentence = sentence as IUtcDateTimeSentence;
if (dateTimeSentence != null)
SetDateTimes(dateTimeSentence.UtcDateTime);
/* Some NMEA sentences provide UTC time information, but no date! To make this work,
* we must combine the time report with the current UTC date.
*/
IUtcTimeSentence timeSentence = sentence as IUtcTimeSentence;
if (timeSentence != null && !timeSentence.UtcTime.Equals(TimeSpan.MinValue) && !timeSentence.UtcTime.Equals(TimeSpan.Zero))
SetDateTimes(new DateTime(UtcDateTime.Year, UtcDateTime.Month, UtcDateTime.Day,
timeSentence.UtcTime.Hours, timeSentence.UtcTime.Minutes, timeSentence.UtcTime.Seconds, timeSentence.UtcTime.Milliseconds,
DateTimeKind.Utc));
}
// Does this sentence support horizontal DOP?
IHorizontalDilutionOfPrecisionSentence hdopSentence = sentence as IHorizontalDilutionOfPrecisionSentence;
if (hdopSentence != null)
SetHorizontalDilutionOfPrecision(hdopSentence.HorizontalDilutionOfPrecision);
// Does the sentence support vertical DOP?
IVerticalDilutionOfPrecisionSentence vdopSentence = sentence as IVerticalDilutionOfPrecisionSentence;
if (vdopSentence != null)
SetVerticalDilutionOfPrecision(vdopSentence.VerticalDilutionOfPrecision);
// Does the sentence support mean DOP?
IPositionDilutionOfPrecisionSentence mdopSentence = sentence as IPositionDilutionOfPrecisionSentence;
if (mdopSentence != null)
SetMeanDilutionOfPrecision(mdopSentence.PositionDilutionOfPrecision);
#endregion
// If a fix is required, don't process time
if (!IsFixRequired || (IsFixRequired && IsFixed))
{
// Is precision good enough to work with?
if (this.HorizontalDilutionOfPrecision.Value <= this.MaximumHorizontalDilutionOfPrecision.Value)
{
#region Process real-time positional data
// Does this sentence support lat/long info?
IPositionSentence positionSentence = sentence as IPositionSentence;
if (positionSentence != null)
SetPosition(positionSentence.Position);
// Does this sentence support bearing?
IBearingSentence bearingSentence = sentence as IBearingSentence;
if (bearingSentence != null)
SetBearing(bearingSentence.Bearing);
// Does this sentence support speed?
ISpeedSentence speedSentence = sentence as ISpeedSentence;
if (speedSentence != null)
SetSpeed(speedSentence.Speed);
#endregion
}
// Is the vertical DOP low enough to be worth processing?
if (this.VerticalDilutionOfPrecision.Value <= this.MaximumVerticalDilutionOfPrecision.Value)
{
#region Process altitude data
// Does this sentence support altitude?
IAltitudeSentence altitudeSentence = sentence as IAltitudeSentence;
if (altitudeSentence != null)
SetAltitude(altitudeSentence.Altitude);
// Does this sentence support altitude?
IAltitudeAboveEllipsoidSentence altitudeAboveEllipsoidSentence = sentence as IAltitudeAboveEllipsoidSentence;
if (altitudeAboveEllipsoidSentence != null)
SetAltitude(altitudeAboveEllipsoidSentence.AltitudeAboveEllipsoid);
// Does this sentence support geoidal separation?
IGeoidalSeparationSentence geoidSeparationSentence = sentence as IGeoidalSeparationSentence;
if (geoidSeparationSentence != null)
SetGeoidalSeparation(geoidSeparationSentence.GeoidalSeparation);
#endregion
}
}
#region Lower-priority information
// Is this a fix mode sentence?
IFixModeSentence fixModeSentence = sentence as IFixModeSentence;
if (fixModeSentence != null)
SetFixMode(fixModeSentence.FixMode);
// Does this sentence have fix status?
IFixStatusSentence fixedSentence = sentence as IFixStatusSentence;
if (fixedSentence != null)
SetFixStatus(fixedSentence.FixStatus);
// Does this sentence support magnetic variation?
IMagneticVariationSentence magVarSentence = sentence as IMagneticVariationSentence;
if (magVarSentence != null)
SetMagneticVariation(magVarSentence.MagneticVariation);
// Process satellite data
ISatelliteCollectionSentence satelliteSentence = sentence as ISatelliteCollectionSentence;
if (satelliteSentence != null)
{
/* GPS.NET 2.0 performed thorough comparison of satellites in order to update
* an *existing* instance of Satellite objects. I think now that this was overkill.
* For performance and limited memory use, satellites are just overwritten.
*/
AppendSatellites(satelliteSentence.Satellites);
}
// Fixed satellite count
IFixedSatelliteCountSentence fixedCountSentence = sentence as IFixedSatelliteCountSentence;
if (fixedCountSentence != null)
SetFixedSatelliteCount(fixedCountSentence.FixedSatelliteCount);
// Process fixed satellites
IFixedSatellitesSentence fixedSatellitesSentence = sentence as IFixedSatellitesSentence;
if (fixedSatellitesSentence != null)
{
SetFixedSatellites(fixedSatellitesSentence.FixedSatellites);
}
#endregion
}
#endregion
#region Overrides
protected override void OnDeviceChanged()
{
// Wrap it in an NMEA stream
_Stream = new NmeaReader(Device.BaseStream);
}
protected override void OnReadPacket()
{
// Read a sentence from the underlying stream
NmeaSentence sentence = _Stream.ReadTypedSentence();
// If we have a sentence, the device is NMEA! Flag it if needed.
if (Device != null && !Device.IsGpsDevice)
Device.SetIsGpsDevice(true);
/* The NmeaInterpreter in GPS.NET 3.0 uses a slimmed-down architecture to reduce
* the memory and CPU footprint of processing NMEA data.
*/
// All data updates in a single shot to prevent race conditions
lock (DataChangeSyncRoot)
{
// Parse the sentence
Parse(sentence);
}
// If we're recording, output the sentence
lock(RecordingSyncRoot)
{
if (RecordingStream != null)
{
byte[] buffer = ASCIIEncoding.ASCII.GetBytes(sentence.Sentence + "\r\n");
RecordingStream.Write(buffer, 0, buffer.Length);
OnSentenceRecorded(sentence);
}
}
// Notify of the sentence
OnSentenceReceived(sentence);
}
protected override void OnStopped()
{
// Clear all values
Initialize();
// Continue stopping
base.OnStopped();
}
#endregion
//#region GPS.NET 2.0 upgrade help
///// <summary>
///// Obsolete. See compiler warnings for upgrade help.
///// </summary>
//[Obsolete("Timeouts are now controlled at the device level. You can modify the static 'DefaultReadTimeout' property in the devices class. If this line of code is part of a Form, you can safely delete this line to use default values.")]
//public TimeSpan WriteTimeout
//{
// get { throw new NotSupportedException(); }
// set { throw new NotSupportedException(); }
//}
///// <summary>
///// Obsolete. See compiler warnings for upgrade help.
///// </summary>
//[Obsolete("Timeouts are now controlled at the device level. You can modify the static 'DefaultReadTimeout' property in the devices class. If this line of code is part of a Form, you can safely delete this line to use default values.")]
//public TimeSpan ReadTimeout
//{
// get { throw new NotSupportedException(); }
// set { throw new NotSupportedException(); }
//}
//#endregion
}
}