QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsGpsConnection::stateChanged
void stateChanged(const QgsGpsInformation &info)
Qgis::GpsQualityIndicator::RTK
@ RTK
Real-time-kynematic.
QgsNmeaConnection::processHdtSentence
void processHdtSentence(const char *data, int len)
process HDT sentence
Definition: qgsnmeaconnection.cpp:226
QgsGpsInformation::vdop
double vdop
Vertical dilution of precision.
Definition: qgsgpsconnection.h:190
QgsGpsInformation::hdop
double hdop
Horizontal dilution of precision.
Definition: qgsgpsconnection.h:185
Qgis::GpsQualityIndicator
GpsQualityIndicator
GPS signal quality indicator.
Definition: qgis.h:833
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Qgis::GpsQualityIndicator::Estimated
@ Estimated
Estimated.
QgsNmeaConnection::processGsaSentence
void processGsaSentence(const char *data, int len)
process GSA sentence
Definition: qgsnmeaconnection.cpp:389
Qgis::GpsQualityIndicator::Simulation
@ Simulation
Simulation mode.
QgsGpsConnection::mStatus
Status mStatus
Connection status.
Definition: qgsgpsconnection.h:347
QgsGpsInformation::hvacc
double hvacc
3D RMS
Definition: qgsgpsconnection.h:202
Qgis::GpsQualityIndicator::FloatRTK
@ FloatRTK
Float real-time-kynematic.
QgsGpsConnection::nmeaSentenceReceived
void nmeaSentenceReceived(const QString &substring)
QgsGpsInformation::vacc
double vacc
Vertical accuracy in meters.
Definition: qgsgpsconnection.h:196
QgsSatelliteInfo
Encapsulates information relating to a GPS satellite.
Definition: qgsgpsconnection.h:41
QgsNmeaConnection::processHchdtSentence
void processHchdtSentence(const char *data, int len)
process HCHDT sentence
Definition: qgsnmeaconnection.cpp:248
QgsGpsInformation::satellitesUsed
int satellitesUsed
Count of satellites used in obtaining the fix.
Definition: qgsgpsconnection.h:246
QgsSatelliteInfo::signal
int signal
Signal strength (0-99dB), or -1 if not available.
Definition: qgsgpsconnection.h:90
QgsGpsInformation::pdop
double pdop
Dilution of precision.
Definition: qgsgpsconnection.h:180
QgsNmeaConnection::parseData
void parseData() override
Parse available data source content.
Definition: qgsnmeaconnection.cpp:41
Qgis::GpsQualityIndicator::Unknown
@ Unknown
Unknown.
QgsGpsConnection::mSource
std::unique_ptr< QIODevice > mSource
Data source (e.g. serial device, socket, file,...)
Definition: qgsgpsconnection.h:343
QgsNmeaConnection::processGsvSentence
void processGsvSentence(const char *data, int len)
process GSV sentence
Definition: qgsnmeaconnection.cpp:351
Qgis::GpsQualityIndicator::PPS
@ PPS
PPS.
QgsNmeaConnection::mStringBuffer
QString mStringBuffer
Store data from the device before it is processed.
Definition: qgsnmeaconnection.h:60
QgsGpsInformation::utcDateTime
QDateTime utcDateTime
The date and time at which this position was reported, in UTC time.
Definition: qgsgpsconnection.h:219
QgsSatelliteInfo::elevation
double elevation
Elevation of the satellite, in degrees.
Definition: qgsgpsconnection.h:64
Qgis::GpsQualityIndicator::GPS
@ GPS
Standalone.
QgsGpsInformation::fixMode
QChar fixMode
Fix mode (where M = Manual, forced to operate in 2D or 3D or A = Automatic, 3D/2D)
Definition: qgsgpsconnection.h:224
QgsGpsInformation::fixType
int fixType
Contains the fix type, where 1 = no fix, 2 = 2d fix, 3 = 3d fix.
Definition: qgsgpsconnection.h:229
QgsNmeaConnection::processHchdgSentence
void processHchdgSentence(const char *data, int len)
process HCHDG sentence
Definition: qgsnmeaconnection.cpp:235
QgsSatelliteInfo::inUse
bool inUse
true if satellite was used in obtaining the position fix.
Definition: qgsgpsconnection.h:57
QgsNmeaConnection::QgsNmeaConnection
QgsNmeaConnection(QIODevice *device)
Constructs a QgsNmeaConnection with given device.
Definition: qgsnmeaconnection.cpp:36
QgsGpsConnection::mLastGPSInformation
QgsGpsInformation mLastGPSInformation
Last state of the gps related variables (e.g. position, time, ...)
Definition: qgsgpsconnection.h:345
QgsGpsInformation::qualityIndicator
Qgis::GpsQualityIndicator qualityIndicator
Returns the signal quality indicator.
Definition: qgsgpsconnection.h:241
QgsSatelliteInfo::id
int id
Contains the satellite identifier number.
Definition: qgsgpsconnection.h:52
QgsGpsInformation::satInfoComplete
bool satInfoComplete
true if satellite information is complete.
Definition: qgsgpsconnection.h:261
QgsNmeaConnection::processStringBuffer
void processStringBuffer()
Splits mStringBuffer into sentences and calls libnmea.
Definition: qgsnmeaconnection.cpp:75
QgsSatelliteInfo::azimuth
double azimuth
The azimuth of the satellite to true north, in degrees.
Definition: qgsgpsconnection.h:78
QgsGpsConnection::DataReceived
@ DataReceived
Definition: qgsgpsconnection.h:313
QgsNmeaConnection::processGstSentence
void processGstSentence(const char *data, int len)
process GST sentence
Definition: qgsnmeaconnection.cpp:207
QgsGpsInformation::speed
double speed
Ground speed, in km/h.
Definition: qgsgpsconnection.h:156
QgsNmeaConnection::processVtgSentence
void processVtgSentence(const char *data, int len)
process VTG sentence
Definition: qgsnmeaconnection.cpp:380
QgsGpsInformation::latitude
double latitude
Latitude in decimal degrees, using the WGS84 datum.
Definition: qgsgpsconnection.h:132
QgsGpsInformation::quality
int quality
GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive, etc....
Definition: qgsgpsconnection.h:235
QgsNmeaConnection::processRmcSentence
void processRmcSentence(const char *data, int len)
process RMC sentence
Definition: qgsnmeaconnection.cpp:257
QgsGpsConnection
Abstract base class for connection to a GPS device.
Definition: qgsgpsconnection.h:287
QgsGpsInformation::satPrn
QList< int > satPrn
IDs of satellites used in the position fix.
Definition: qgsgpsconnection.h:256
QgsGpsInformation::satellitesInView
QList< QgsSatelliteInfo > satellitesInView
Contains a list of information relating to the current satellites in view.
Definition: qgsgpsconnection.h:175
qgslogger.h
Qgis::GpsQualityIndicator::Invalid
@ Invalid
Invalid.
QgsGpsInformation::elevation
double elevation
Altitude (in meters) above or below the mean sea level.
Definition: qgsgpsconnection.h:143
Qgis::GpsQualityIndicator::DGPS
@ DGPS
Differential GPS.
QgsGpsConnection::GPSDataReceived
@ GPSDataReceived
Definition: qgsgpsconnection.h:314
QgsGpsInformation::elevation_diff
double elevation_diff
Geoidal separation (Diff.
Definition: qgsgpsconnection.h:151
qgsnmeaconnection.h
QgsGpsInformation::longitude
double longitude
Longitude in decimal degrees, using the WGS84 datum.
Definition: qgsgpsconnection.h:138
QgsNmeaConnection::processGgaSentence
void processGgaSentence(const char *data, int len)
process GGA sentence
Definition: qgsnmeaconnection.cpp:171
QgsGpsInformation::hacc
double hacc
Horizontal accuracy in meters.
Definition: qgsgpsconnection.h:194
Qgis::GpsQualityIndicator::Manual
@ Manual
Manual input mode.
QgsGpsInformation::direction
double direction
The bearing measured in degrees clockwise from true north to the direction of travel.
Definition: qgsgpsconnection.h:163
QgsGpsInformation::status
QChar status
Status (A = active or V = void)
Definition: qgsgpsconnection.h:251
KNOTS_TO_KMH
#define KNOTS_TO_KMH
Definition: qgsnmeaconnection.cpp:34