QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsnmeaconnection.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsnmeaconnection.cpp - description
3  ---------------------
4  begin : November 30th, 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : marco at hugis dot net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsnmeaconnection.h"
19 #include "qgslogger.h"
20 
21 #include <QIODevice>
22 #include <QApplication>
23 #include <QStringList>
24 
25 
26 //from libnmea
27 #include "parse.h"
28 #include "gmath.h"
29 #include "info.h"
30 
31 // for sqrt
32 #include <math.h>
33 
34 #define KNOTS_TO_KMH 1.852
35 
37  : QgsGpsConnection( device )
38 {
39 }
40 
42 {
43  if ( !mSource )
44  {
45  return;
46  }
47 
48  //print out the data as a test
49  qint64 numBytes = 0;
50  if ( ! mSource->isSequential() ) //necessary because of a bug in QExtSerialPort //SLM - bytesAvailable() works on Windows, so I reversed the logic (added ! ); this is what QIODevice docs say to do; the orig impl of win_qextserialport had an (unsigned int)-1 return on error - it should be (qint64)-1, which was fixed by ?
51  {
52  numBytes = mSource->size();
53  }
54  else
55  {
56  numBytes = mSource->bytesAvailable();
57  }
58 
59  QgsDebugMsgLevel( "numBytes:" + QString::number( numBytes ), 2 );
60 
61  if ( numBytes >= 6 )
62  {
63  if ( mStatus != GPSDataReceived )
64  {
66  }
67 
68  //append new data to the remaining results from last parseData() call
69  mStringBuffer.append( mSource->read( numBytes ) );
72  }
73 }
74 
76 {
77  int endSentenceIndex = 0;
78  int dollarIndex;
79 
80  while ( ( endSentenceIndex = mStringBuffer.indexOf( QLatin1String( "\r\n" ) ) ) && endSentenceIndex != -1 )
81  {
82  endSentenceIndex = mStringBuffer.indexOf( QLatin1String( "\r\n" ) );
83 
84  dollarIndex = mStringBuffer.indexOf( QLatin1Char( '$' ) );
85  if ( endSentenceIndex == -1 )
86  {
87  break;
88  }
89 
90 
91  if ( endSentenceIndex >= dollarIndex )
92  {
93  if ( dollarIndex != -1 )
94  {
95  const QString substring = mStringBuffer.mid( dollarIndex, endSentenceIndex );
96  QByteArray ba = substring.toLocal8Bit();
97  if ( substring.startsWith( QLatin1String( "$GPGGA" ) ) || substring.startsWith( QLatin1String( "$GNGGA" ) ) )
98  {
99  QgsDebugMsgLevel( substring, 2 );
100  processGgaSentence( ba.data(), ba.length() );
102  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
103  }
104  else if ( substring.startsWith( QLatin1String( "$GPRMC" ) ) || substring.startsWith( QLatin1String( "$GNRMC" ) ) )
105  {
106  QgsDebugMsgLevel( substring, 2 );
107  processRmcSentence( ba.data(), ba.length() );
109  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
110  }
111  else if ( substring.startsWith( QLatin1String( "$GPGSV" ) ) || substring.startsWith( QLatin1String( "$GNGSV" ) ) )
112  {
113  QgsDebugMsgLevel( substring, 2 );
114  processGsvSentence( ba.data(), ba.length() );
116  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
117  }
118  else if ( substring.startsWith( QLatin1String( "$GPVTG" ) ) || substring.startsWith( QLatin1String( "$GNVTG" ) ) )
119  {
120  QgsDebugMsgLevel( substring, 2 );
121  processVtgSentence( ba.data(), ba.length() );
123  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
124  }
125  else if ( substring.startsWith( QLatin1String( "$GPGSA" ) ) || substring.startsWith( QLatin1String( "$GNGSA" ) ) )
126  {
127  QgsDebugMsgLevel( substring, 2 );
128  processGsaSentence( ba.data(), ba.length() );
130  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
131  }
132  else if ( substring.startsWith( QLatin1String( "$GPGST" ) ) || substring.startsWith( QLatin1String( "$GNGST" ) ) )
133  {
134  QgsDebugMsgLevel( substring, 2 );
135  processGstSentence( ba.data(), ba.length() );
137  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
138  }
139  else if ( substring.startsWith( QLatin1String( "$GPHDT" ) ) || substring.startsWith( QLatin1String( "$GNHDT" ) ) )
140  {
141  QgsDebugMsgLevel( substring, 2 );
142  processHdtSentence( ba.data(), ba.length() );
144  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
145  }
146  else if ( substring.startsWith( QLatin1String( "$HCHDG" ) ) )
147  {
148  QgsDebugMsgLevel( substring, 2 );
149  processHchdgSentence( ba.data(), ba.length() );
151  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
152  }
153  else if ( substring.startsWith( QLatin1String( "$HCHDT" ) ) )
154  {
155  QgsDebugMsgLevel( substring, 2 );
156  processHchdtSentence( ba.data(), ba.length() );
158  QgsDebugMsgLevel( QStringLiteral( "*******************GPS data received****************" ), 2 );
159  }
160  else
161  {
162  QgsDebugMsgLevel( QStringLiteral( "unknown nmea sentence: %1" ).arg( substring ), 2 );
163  }
164  emit nmeaSentenceReceived( substring ); // added to be able to save raw data
165  }
166  }
167  mStringBuffer.remove( 0, endSentenceIndex + 2 );
168  }
169 }
170 
171 void QgsNmeaConnection::processGgaSentence( const char *data, int len )
172 {
173  nmeaGPGGA result;
174  if ( nmea_parse_GPGGA( data, len, &result ) )
175  {
176  //update mLastGPSInformation
177  double longitude = result.lon;
178  if ( result.ew == 'W' )
179  {
180  longitude = -longitude;
181  }
182  double latitude = result.lat;
183  if ( result.ns == 'S' )
184  {
185  latitude = -latitude;
186  }
187 
188  mLastGPSInformation.longitude = nmea_ndeg2degree( longitude );
189  mLastGPSInformation.latitude = nmea_ndeg2degree( latitude );
190  mLastGPSInformation.elevation = result.elv;
191  mLastGPSInformation.elevation_diff = result.diff;
192 
193  mLastGPSInformation.quality = result.sig;
194  if ( result.sig >= 0 && result.sig <= 8 )
195  {
197  }
198  else
199  {
201  }
202 
203  mLastGPSInformation.satellitesUsed = result.satinuse;
204  }
205 }
206 
207 void QgsNmeaConnection::processGstSentence( const char *data, int len )
208 {
209  nmeaGPGST result;
210  if ( nmea_parse_GPGST( data, len, &result ) )
211  {
212  //update mLastGPSInformation
213  const double sig_lat = result.sig_lat;
214  const double sig_lon = result.sig_lon;
215  const double sig_alt = result.sig_alt;
216 
217  // Horizontal RMS
218  mLastGPSInformation.hacc = sqrt( ( pow( sig_lat, 2 ) + pow( sig_lon, 2 ) ) / 2.0 );
219  // Vertical RMS
220  mLastGPSInformation.vacc = sig_alt;
221  // 3D RMS
222  mLastGPSInformation.hvacc = sqrt( ( pow( sig_lat, 2 ) + pow( sig_lon, 2 ) + pow( sig_alt, 2 ) ) / 3.0 );
223  }
224 }
225 
226 void QgsNmeaConnection::processHdtSentence( const char *data, int len )
227 {
228  nmeaGPHDT result;
229  if ( nmea_parse_GPHDT( data, len, &result ) )
230  {
231  mLastGPSInformation.direction = result.heading;
232  }
233 }
234 
235 void QgsNmeaConnection::processHchdgSentence( const char *data, int len )
236 {
237  nmeaHCHDG result;
238  if ( nmea_parse_HCHDG( data, len, &result ) )
239  {
240  mLastGPSInformation.direction = result.mag_heading;
241  if ( result.ew_variation == 'E' )
242  mLastGPSInformation.direction += result.mag_variation;
243  else
244  mLastGPSInformation.direction -= result.mag_variation;
245  }
246 }
247 
248 void QgsNmeaConnection::processHchdtSentence( const char *data, int len )
249 {
250  nmeaHCHDT result;
251  if ( nmea_parse_HCHDT( data, len, &result ) )
252  {
253  mLastGPSInformation.direction = result.direction;
254  }
255 }
256 
257 void QgsNmeaConnection::processRmcSentence( const char *data, int len )
258 {
259  nmeaGPRMC result;
260  if ( nmea_parse_GPRMC( data, len, &result ) )
261  {
262  double longitude = result.lon;
263  if ( result.ew == 'W' )
264  {
265  longitude = -longitude;
266  }
267  double latitude = result.lat;
268  if ( result.ns == 'S' )
269  {
270  latitude = -latitude;
271  }
272  mLastGPSInformation.longitude = nmea_ndeg2degree( longitude );
273  mLastGPSInformation.latitude = nmea_ndeg2degree( latitude );
274  mLastGPSInformation.speed = KNOTS_TO_KMH * result.speed;
275  if ( !std::isnan( result.direction ) )
276  mLastGPSInformation.direction = result.direction;
277  mLastGPSInformation.status = result.status; // A,V
278 
279  //date and time
280  const QDate date( result.utc.year + 1900, result.utc.mon + 1, result.utc.day );
281  const QTime time( result.utc.hour, result.utc.min, result.utc.sec, result.utc.msec ); // added msec part
282  if ( date.isValid() && time.isValid() )
283  {
284  mLastGPSInformation.utcDateTime.setTimeSpec( Qt::UTC );
285  mLastGPSInformation.utcDateTime.setDate( date );
286  mLastGPSInformation.utcDateTime.setTime( time );
287  QgsDebugMsgLevel( QStringLiteral( "utc time:" ), 2 );
289  QgsDebugMsgLevel( QStringLiteral( "local time:" ), 2 );
290  QgsDebugMsgLevel( mLastGPSInformation.utcDateTime.toLocalTime().toString(), 2 );
291  }
292 
293  // convert mode to signal (aka quality) indicator
294  // (see https://gitlab.com/fhuberts/nmealib/-/blob/master/src/info.c#L27)
295  if ( result.status == 'A' )
296  {
297  if ( result.mode == 'A' )
298  {
301  }
302  else if ( result.mode == 'D' )
303  {
306  }
307  else if ( result.mode == 'P' )
308  {
311  }
312  else if ( result.mode == 'R' )
313  {
316  }
317  else if ( result.mode == 'F' )
318  {
321  }
322  else if ( result.mode == 'E' )
323  {
326  }
327  else if ( result.mode == 'M' )
328  {
331  }
332  else if ( result.mode == 'S' )
333  {
336  }
337  else
338  {
341  }
342  }
343  else
344  {
347  }
348  }
349 }
350 
351 void QgsNmeaConnection::processGsvSentence( const char *data, int len )
352 {
353  nmeaGPGSV result;
354  if ( nmea_parse_GPGSV( data, len, &result ) )
355  {
356  //clear satellite information when a new series of packs arrives
357  if ( result.pack_index == 1 )
358  {
360  }
361 
362  // for determining when to graph sat info
363  mLastGPSInformation.satInfoComplete = ( result.pack_index == result.pack_count );
364 
365  for ( int i = 0; i < NMEA_SATINPACK; ++i )
366  {
367  const nmeaSATELLITE currentSatellite = result.sat_data[i];
368  QgsSatelliteInfo satelliteInfo;
369  satelliteInfo.azimuth = currentSatellite.azimuth;
370  satelliteInfo.elevation = currentSatellite.elv;
371  satelliteInfo.id = currentSatellite.id;
372  satelliteInfo.inUse = currentSatellite.in_use; // the GSA processing below does NOT set the sats in use
373  satelliteInfo.signal = currentSatellite.sig;
374  mLastGPSInformation.satellitesInView.append( satelliteInfo );
375  }
376 
377  }
378 }
379 
380 void QgsNmeaConnection::processVtgSentence( const char *data, int len )
381 {
382  nmeaGPVTG result;
383  if ( nmea_parse_GPVTG( data, len, &result ) )
384  {
385  mLastGPSInformation.speed = result.spk;
386  }
387 }
388 
389 void QgsNmeaConnection::processGsaSentence( const char *data, int len )
390 {
391  nmeaGPGSA result;
392  if ( nmea_parse_GPGSA( data, len, &result ) )
393  {
394  mLastGPSInformation.satPrn.clear();
395  mLastGPSInformation.hdop = result.HDOP;
396  mLastGPSInformation.pdop = result.PDOP;
397  mLastGPSInformation.vdop = result.VDOP;
398  mLastGPSInformation.fixMode = result.fix_mode;
399  mLastGPSInformation.fixType = result.fix_type;
400  for ( int i = 0; i < NMEA_MAXSAT; i++ )
401  {
402  mLastGPSInformation.satPrn.append( result.sat_prn[ i ] );
403  }
404  }
405 }
GpsQualityIndicator
GPS signal quality indicator.
Definition: qgis.h:615
@ RTK
Real-time-kynematic.
@ DGPS
Differential GPS.
@ Simulation
Simulation mode.
@ FloatRTK
Float real-time-kynematic.
@ Manual
Manual input mode.
Abstract base class for connection to a GPS device.
QgsGpsInformation mLastGPSInformation
Last state of the gps related variables (e.g. position, time, ...)
void nmeaSentenceReceived(const QString &substring)
std::unique_ptr< QIODevice > mSource
Data source (e.g. serial device, socket, file,...)
Status mStatus
Connection status.
void stateChanged(const QgsGpsInformation &info)
double vdop
Vertical dilution of precision.
double direction
The bearing measured in degrees clockwise from true north to the direction of travel.
int fixType
Contains the fix type, where 1 = no fix, 2 = 2d fix, 3 = 3d fix.
QChar status
Status (A = active or V = void)
double speed
Ground speed, in km/h.
double vacc
Vertical accuracy in meters.
Qgis::GpsQualityIndicator qualityIndicator
Returns the signal quality indicator.
double latitude
Latitude in decimal degrees, using the WGS84 datum.
double longitude
Longitude in decimal degrees, using the WGS84 datum.
QList< QgsSatelliteInfo > satellitesInView
Contains a list of information relating to the current satellites in view.
QChar fixMode
Fix mode (where M = Manual, forced to operate in 2D or 3D or A = Automatic, 3D/2D)
QDateTime utcDateTime
The date and time at which this position was reported, in UTC time.
QList< int > satPrn
IDs of satellites used in the position fix.
double elevation
Altitude (in meters) above or below the mean sea level.
bool satInfoComplete
true if satellite information is complete.
double pdop
Dilution of precision.
int satellitesUsed
Count of satellites used in obtaining the fix.
double elevation_diff
Geoidal separation (Diff.
double hdop
Horizontal dilution of precision.
int quality
GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive, etc....
double hacc
Horizontal accuracy in meters.
void processVtgSentence(const char *data, int len)
process VTG sentence
void processRmcSentence(const char *data, int len)
process RMC sentence
void processHchdtSentence(const char *data, int len)
process HCHDT sentence
void parseData() override
Parse available data source content.
void processHchdgSentence(const char *data, int len)
process HCHDG sentence
void processGgaSentence(const char *data, int len)
process GGA sentence
void processGsvSentence(const char *data, int len)
process GSV sentence
void processGstSentence(const char *data, int len)
process GST sentence
void processHdtSentence(const char *data, int len)
process HDT sentence
void processGsaSentence(const char *data, int len)
process GSA sentence
QString mStringBuffer
Store data from the device before it is processed.
QgsNmeaConnection(QIODevice *device)
Constructs a QgsNmeaConnection with given device.
void processStringBuffer()
Splits mStringBuffer into sentences and calls libnmea.
Encapsulates information relating to a GPS satellite.
double elevation
Elevation of the satellite, in degrees.
bool inUse
true if satellite was used in obtaining the position fix.
int signal
Signal strength (0-99dB), or -1 if not available.
int id
Contains the satellite identifier number.
double azimuth
The azimuth of the satellite to true north, in degrees.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define KNOTS_TO_KMH