QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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( QLatin1String( "$" ) );
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" ) ) )
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" ) ) )
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" ) ) )
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.quality = result.sig;
192  mLastGPSInformation.satellitesUsed = result.satinuse;
193  }
194 }
195 
196 void QgsNmeaConnection::processGstSentence( const char *data, int len )
197 {
198  nmeaGPGST result;
199  if ( nmea_parse_GPGST( data, len, &result ) )
200  {
201  //update mLastGPSInformation
202  double sig_lat = result.sig_lat;
203  double sig_lon = result.sig_lon;
204  double sig_alt = result.sig_alt;
205 
206  // Horizontal RMS
207  mLastGPSInformation.hacc = sqrt( ( pow( sig_lat, 2 ) + pow( sig_lon, 2 ) ) / 2.0 );
208  // Vertical RMS
209  mLastGPSInformation.vacc = sig_alt;
210  }
211 }
212 
213 void QgsNmeaConnection::processHdtSentence( const char *data, int len )
214 {
215  nmeaGPHDT result;
216  if ( nmea_parse_GPHDT( data, len, &result ) )
217  {
218  mLastGPSInformation.direction = result.heading;
219  }
220 }
221 
222 void QgsNmeaConnection::processHchdgSentence( const char *data, int len )
223 {
224  nmeaHCHDG result;
225  if ( nmea_parse_HCHDG( data, len, &result ) )
226  {
227  mLastGPSInformation.direction = result.mag_heading;
228  if ( result.ew_variation == 'E' )
229  mLastGPSInformation.direction += result.mag_variation;
230  else
231  mLastGPSInformation.direction -= result.mag_variation;
232  }
233 }
234 
235 void QgsNmeaConnection::processHchdtSentence( const char *data, int len )
236 {
237  nmeaHCHDT result;
238  if ( nmea_parse_HCHDT( data, len, &result ) )
239  {
240  mLastGPSInformation.direction = result.direction;
241  }
242 }
243 
244 void QgsNmeaConnection::processRmcSentence( const char *data, int len )
245 {
246  nmeaGPRMC result;
247  if ( nmea_parse_GPRMC( data, len, &result ) )
248  {
249  double longitude = result.lon;
250  if ( result.ew == 'W' )
251  {
252  longitude = -longitude;
253  }
254  double latitude = result.lat;
255  if ( result.ns == 'S' )
256  {
257  latitude = -latitude;
258  }
259  mLastGPSInformation.longitude = nmea_ndeg2degree( longitude );
260  mLastGPSInformation.latitude = nmea_ndeg2degree( latitude );
261  mLastGPSInformation.speed = KNOTS_TO_KMH * result.speed;
262  if ( !std::isnan( result.direction ) )
263  mLastGPSInformation.direction = result.direction;
264  mLastGPSInformation.status = result.status; // A,V
265 
266  //date and time
267  QDate date( result.utc.year + 1900, result.utc.mon + 1, result.utc.day );
268  QTime time( result.utc.hour, result.utc.min, result.utc.sec, result.utc.msec ); // added msec part
269  if ( date.isValid() && time.isValid() )
270  {
271  mLastGPSInformation.utcDateTime.setTimeSpec( Qt::UTC );
272  mLastGPSInformation.utcDateTime.setDate( date );
273  mLastGPSInformation.utcDateTime.setTime( time );
274  QgsDebugMsgLevel( QStringLiteral( "utc time:" ), 2 );
276  QgsDebugMsgLevel( QStringLiteral( "local time:" ), 2 );
277  QgsDebugMsgLevel( mLastGPSInformation.utcDateTime.toLocalTime().toString(), 2 );
278  }
279  }
280 }
281 
282 void QgsNmeaConnection::processGsvSentence( const char *data, int len )
283 {
284  nmeaGPGSV result;
285  if ( nmea_parse_GPGSV( data, len, &result ) )
286  {
287  //clear satellite information when a new series of packs arrives
288  if ( result.pack_index == 1 )
289  {
291  }
292 
293  // for determining when to graph sat info
294  mLastGPSInformation.satInfoComplete = ( result.pack_index == result.pack_count );
295 
296  for ( int i = 0; i < NMEA_SATINPACK; ++i )
297  {
298  nmeaSATELLITE currentSatellite = result.sat_data[i];
299  QgsSatelliteInfo satelliteInfo;
300  satelliteInfo.azimuth = currentSatellite.azimuth;
301  satelliteInfo.elevation = currentSatellite.elv;
302  satelliteInfo.id = currentSatellite.id;
303  satelliteInfo.inUse = currentSatellite.in_use; // the GSA processing below does NOT set the sats in use
304  satelliteInfo.signal = currentSatellite.sig;
305  mLastGPSInformation.satellitesInView.append( satelliteInfo );
306  }
307 
308  }
309 }
310 
311 void QgsNmeaConnection::processVtgSentence( const char *data, int len )
312 {
313  nmeaGPVTG result;
314  if ( nmea_parse_GPVTG( data, len, &result ) )
315  {
316  mLastGPSInformation.speed = result.spk;
317  }
318 }
319 
320 void QgsNmeaConnection::processGsaSentence( const char *data, int len )
321 {
322  nmeaGPGSA result;
323  if ( nmea_parse_GPGSA( data, len, &result ) )
324  {
325  mLastGPSInformation.satPrn.clear();
326  mLastGPSInformation.hdop = result.HDOP;
327  mLastGPSInformation.pdop = result.PDOP;
328  mLastGPSInformation.vdop = result.VDOP;
329  mLastGPSInformation.fixMode = result.fix_mode;
330  mLastGPSInformation.fixType = result.fix_type;
331  for ( int i = 0; i < NMEA_MAXSAT; i++ )
332  {
333  mLastGPSInformation.satPrn.append( result.sat_prn[ i ] );
334  }
335  }
336 }
double vdop
Vertical dilution of precision.
double hacc
Horizontal accuracy in meters.
bool inUse
true if satellite was used in obtaining the position fix.
QChar status
Status (A = active or V = void)
QgsGpsInformation mLastGPSInformation
Last state of the gps related variables (e.g. position, time, ...)
Encapsulates information relating to a GPS satellite.
void processHdtSentence(const char *data, int len)
process HDT sentence
double hdop
Horizontal dilution of precision.
double vacc
Vertical accuracy in meters.
void processHchdtSentence(const char *data, int len)
process HCHDT sentence
double pdop
Dilution of precision.
QString mStringBuffer
Store data from the device before it is processed.
void processGsaSentence(const char *data, int len)
process GSA sentence
QDateTime utcDateTime
The date and time at which this position was reported, in UTC time.
void parseData() override
Parse available data source content.
int signal
Signal strength (0-99dB), or -1 if not available.
int fixType
Contains the fix type, where 1 = no fix, 2 = 2d fix, 3 = 3d fix.
double elevation
Elevation of the satellite, in degrees.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
int satellitesUsed
Count of satellites used in obtaining the fix.
void stateChanged(const QgsGpsInformation &info)
void processGsvSentence(const char *data, int len)
process GSV sentence
void processStringBuffer()
Splits mStringBuffer into sentences and calls libnmea.
void processGstSentence(const char *data, int len)
process GST sentence
void processHchdgSentence(const char *data, int len)
process HCHDG sentence
double speed
Ground speed, in km/h.
QgsNmeaConnection(QIODevice *device)
Constructs a QgsNmeaConnection with given device.
QChar fixMode
Fix mode (where M = Manual, forced to operate in 2D or 3D or A = Automatic, 3D/2D) ...
QList< int > satPrn
IDs of satellites used in the position fix.
int quality
GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive)
double elevation
Altitude (in meters) above or below the mean sea level.
void processRmcSentence(const char *data, int len)
process RMC sentence
Status mStatus
Connection status.
void processGgaSentence(const char *data, int len)
process GGA sentence
#define KNOTS_TO_KMH
double azimuth
The azimuth of the satellite to true north, in degrees.
bool satInfoComplete
true if satellite information is complete.
double direction
The bearing measured in degrees clockwise from true north to the direction of travel.
std::unique_ptr< QIODevice > mSource
Data source (e.g. serial device, socket, file,...)
void nmeaSentenceReceived(const QString &substring)
double latitude
Latitude in decimal degrees, using the WGS84 datum.
void processVtgSentence(const char *data, int len)
process VTG sentence
QList< QgsSatelliteInfo > satellitesInView
Contains a list of information relating to the current satellites in view.
int id
Contains the satellite identifier number.
Abstract base class for connection to a GPS device.
double longitude
Longitude in decimal degrees, using the WGS84 datum.