QGIS API Documentation  3.20.0-Odense (decaadbb31)
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  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  mLastGPSInformation.quality = result.sig;
193  mLastGPSInformation.satellitesUsed = result.satinuse;
194  }
195 }
196 
197 void QgsNmeaConnection::processGstSentence( const char *data, int len )
198 {
199  nmeaGPGST result;
200  if ( nmea_parse_GPGST( data, len, &result ) )
201  {
202  //update mLastGPSInformation
203  double sig_lat = result.sig_lat;
204  double sig_lon = result.sig_lon;
205  double sig_alt = result.sig_alt;
206 
207  // Horizontal RMS
208  mLastGPSInformation.hacc = sqrt( ( pow( sig_lat, 2 ) + pow( sig_lon, 2 ) ) / 2.0 );
209  // Vertical RMS
210  mLastGPSInformation.vacc = sig_alt;
211  // 3D RMS
212  mLastGPSInformation.hvacc = sqrt( ( pow( sig_lat, 2 ) + pow( sig_lon, 2 ) + pow( sig_alt, 2 ) ) / 3.0 );
213  }
214 }
215 
216 void QgsNmeaConnection::processHdtSentence( const char *data, int len )
217 {
218  nmeaGPHDT result;
219  if ( nmea_parse_GPHDT( data, len, &result ) )
220  {
221  mLastGPSInformation.direction = result.heading;
222  }
223 }
224 
225 void QgsNmeaConnection::processHchdgSentence( const char *data, int len )
226 {
227  nmeaHCHDG result;
228  if ( nmea_parse_HCHDG( data, len, &result ) )
229  {
230  mLastGPSInformation.direction = result.mag_heading;
231  if ( result.ew_variation == 'E' )
232  mLastGPSInformation.direction += result.mag_variation;
233  else
234  mLastGPSInformation.direction -= result.mag_variation;
235  }
236 }
237 
238 void QgsNmeaConnection::processHchdtSentence( const char *data, int len )
239 {
240  nmeaHCHDT result;
241  if ( nmea_parse_HCHDT( data, len, &result ) )
242  {
243  mLastGPSInformation.direction = result.direction;
244  }
245 }
246 
247 void QgsNmeaConnection::processRmcSentence( const char *data, int len )
248 {
249  nmeaGPRMC result;
250  if ( nmea_parse_GPRMC( data, len, &result ) )
251  {
252  double longitude = result.lon;
253  if ( result.ew == 'W' )
254  {
255  longitude = -longitude;
256  }
257  double latitude = result.lat;
258  if ( result.ns == 'S' )
259  {
260  latitude = -latitude;
261  }
262  mLastGPSInformation.longitude = nmea_ndeg2degree( longitude );
263  mLastGPSInformation.latitude = nmea_ndeg2degree( latitude );
264  mLastGPSInformation.speed = KNOTS_TO_KMH * result.speed;
265  if ( !std::isnan( result.direction ) )
266  mLastGPSInformation.direction = result.direction;
267  mLastGPSInformation.status = result.status; // A,V
268 
269  //date and time
270  QDate date( result.utc.year + 1900, result.utc.mon + 1, result.utc.day );
271  QTime time( result.utc.hour, result.utc.min, result.utc.sec, result.utc.msec ); // added msec part
272  if ( date.isValid() && time.isValid() )
273  {
274  mLastGPSInformation.utcDateTime.setTimeSpec( Qt::UTC );
275  mLastGPSInformation.utcDateTime.setDate( date );
276  mLastGPSInformation.utcDateTime.setTime( time );
277  QgsDebugMsgLevel( QStringLiteral( "utc time:" ), 2 );
279  QgsDebugMsgLevel( QStringLiteral( "local time:" ), 2 );
280  QgsDebugMsgLevel( mLastGPSInformation.utcDateTime.toLocalTime().toString(), 2 );
281  }
282  }
283 }
284 
285 void QgsNmeaConnection::processGsvSentence( const char *data, int len )
286 {
287  nmeaGPGSV result;
288  if ( nmea_parse_GPGSV( data, len, &result ) )
289  {
290  //clear satellite information when a new series of packs arrives
291  if ( result.pack_index == 1 )
292  {
294  }
295 
296  // for determining when to graph sat info
297  mLastGPSInformation.satInfoComplete = ( result.pack_index == result.pack_count );
298 
299  for ( int i = 0; i < NMEA_SATINPACK; ++i )
300  {
301  nmeaSATELLITE currentSatellite = result.sat_data[i];
302  QgsSatelliteInfo satelliteInfo;
303  satelliteInfo.azimuth = currentSatellite.azimuth;
304  satelliteInfo.elevation = currentSatellite.elv;
305  satelliteInfo.id = currentSatellite.id;
306  satelliteInfo.inUse = currentSatellite.in_use; // the GSA processing below does NOT set the sats in use
307  satelliteInfo.signal = currentSatellite.sig;
308  mLastGPSInformation.satellitesInView.append( satelliteInfo );
309  }
310 
311  }
312 }
313 
314 void QgsNmeaConnection::processVtgSentence( const char *data, int len )
315 {
316  nmeaGPVTG result;
317  if ( nmea_parse_GPVTG( data, len, &result ) )
318  {
319  mLastGPSInformation.speed = result.spk;
320  }
321 }
322 
323 void QgsNmeaConnection::processGsaSentence( const char *data, int len )
324 {
325  nmeaGPGSA result;
326  if ( nmea_parse_GPGSA( data, len, &result ) )
327  {
328  mLastGPSInformation.satPrn.clear();
329  mLastGPSInformation.hdop = result.HDOP;
330  mLastGPSInformation.pdop = result.PDOP;
331  mLastGPSInformation.vdop = result.VDOP;
332  mLastGPSInformation.fixMode = result.fix_mode;
333  mLastGPSInformation.fixType = result.fix_type;
334  for ( int i = 0; i < NMEA_MAXSAT; i++ )
335  {
336  mLastGPSInformation.satPrn.append( result.sat_prn[ i ] );
337  }
338  }
339 }
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.
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)
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