QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsgpsdetector.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgpsdetector.cpp - description
3 --------------------
4 begin : January 13th, 2009
5 copyright : (C) 2009 by Juergen E. Fischer
6 email : jef at norbit dot de
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 "qgsgpsdetector.h"
19#include "qgsgpsconnection.h"
20#include "qgsnmeaconnection.h"
21#include "qgsgpsdconnection.h"
22#include "qgssettingstree.h"
24#include "qgslogger.h"
25
26#if defined(QT_POSITIONING_LIB)
28#endif
29
30#include <QStringList>
31#include <QFileInfo>
32#include <QTimer>
33
34#if defined( HAVE_QTSERIALPORT )
35#include <QSerialPortInfo>
36#include <QSerialPort>
37
38const QgsSettingsEntryEnumFlag<QSerialPort::FlowControl> *QgsGpsDetector::settingsGpsFlowControl = new QgsSettingsEntryEnumFlag<QSerialPort::FlowControl>( QStringLiteral( "flow-control" ), QgsSettingsTree::sTreeGps, QSerialPort::NoFlowControl );
39const QgsSettingsEntryEnumFlag<QSerialPort::StopBits> *QgsGpsDetector::settingsGpsStopBits = new QgsSettingsEntryEnumFlag<QSerialPort::StopBits>( QStringLiteral( "stop-bits" ), QgsSettingsTree::sTreeGps, QSerialPort::OneStop );
40const QgsSettingsEntryEnumFlag<QSerialPort::DataBits> *QgsGpsDetector::settingsGpsDataBits = new QgsSettingsEntryEnumFlag<QSerialPort::DataBits>( QStringLiteral( "data-bits" ), QgsSettingsTree::sTreeGps, QSerialPort::Data8 );
41const QgsSettingsEntryEnumFlag<QSerialPort::Parity> *QgsGpsDetector::settingsGpsParity = new QgsSettingsEntryEnumFlag<QSerialPort::Parity>( QStringLiteral( "parity" ), QgsSettingsTree::sTreeGps, QSerialPort::NoParity );
42#endif
43
44QList< QPair<QString, QString> > QgsGpsDetector::availablePorts()
45{
46 QList< QPair<QString, QString> > devs;
47
48 // try local QtLocation first
49#if defined(QT_POSITIONING_LIB)
50 devs << QPair<QString, QString>( QStringLiteral( "internalGPS" ), tr( "internal GPS" ) );
51#endif
52
53 // try local gpsd first
54 devs << QPair<QString, QString>( QStringLiteral( "localhost:2947:" ), tr( "local gpsd" ) );
55
56 // try serial ports
57#if defined( HAVE_QTSERIALPORT )
58 for ( const QSerialPortInfo &p : QSerialPortInfo::availablePorts() )
59 {
60 devs << QPair<QString, QString>( p.portName(), tr( "%1: %2" ).arg( p.portName(), p.description() ) );
61 }
62#endif
63
64 return devs;
65}
66
67QgsGpsDetector::QgsGpsDetector( const QString &portName, bool useUnsafeSignals )
68 : mUseUnsafeSignals( useUnsafeSignals )
69{
70#if defined( HAVE_QTSERIALPORT )
71 mBaudList << QSerialPort::Baud4800 << QSerialPort::Baud9600 << QSerialPort::Baud38400 << QSerialPort::Baud57600 << QSerialPort::Baud115200; //add 57600 for SXBlueII GPS unit
72#endif
73
74 if ( portName.isEmpty() )
75 {
76 QgsDebugMsgLevel( QStringLiteral( "Attempting to autodetect GPS connection" ), 2 );
77 mPortList = availablePorts();
78 }
79 else
80 {
81 QgsDebugMsgLevel( QStringLiteral( "Attempting GPS connection for %1" ).arg( portName ), 2 );
82 mPortList << QPair<QString, QString>( portName, portName );
83 }
84
85 mTimeoutTimer = new QTimer( this );
86 mTimeoutTimer->setSingleShot( true );
87 connect( mTimeoutTimer, &QTimer::timeout, this, &QgsGpsDetector::connectionTimeout );
88}
89
91{
92 QgsDebugMsgLevel( QStringLiteral( "Destroying GPS detector" ), 2 );
93}
94
96{
97 if ( mUseUnsafeSignals )
98 {
99 QgsDebugError( QStringLiteral( "QgsGpsDetector::takeConnection() incorrectly called when useUnsafeSignals option is in effect" ) );
100 return nullptr;
101 }
102
103 if ( mConn )
104 {
105 // this is NOT the detectors connection anymore, so disconnect all signals from the connection
106 // to the detector so that there's no unwanted interaction with the detector
107 mConn->disconnect( this );
108 }
109
110#ifdef QGISDEBUG
111 if ( mConn )
112 {
113 QgsDebugMsgLevel( QStringLiteral( "Detected GPS connection is being taken by caller" ), 2 );
114 }
115 else
116 {
117 QgsDebugError( QStringLiteral( "Something is trying to take the GPS connection, but it doesn't exist!" ) );
118 }
119#endif
120
121 return mConn.release();
122}
123
125{
126 if ( mConn )
127 {
128 QgsDebugMsgLevel( QStringLiteral( "Destroying existing connection to attempt next configuration combination" ), 2 );
129 mConn.reset();
130 }
131
132 QgsDebugMsgLevel( QStringLiteral( "Trying to find a connection..." ), 2 );
133
134 while ( !mConn )
135 {
136 mBaudIndex++;
137 if ( mBaudIndex == mBaudList.size() )
138 {
139 mBaudIndex = 0;
140 mPortIndex++;
141 }
142
143 if ( mPortIndex == mPortList.size() )
144 {
145 QgsDebugError( QStringLiteral( "No more devices to try!" ) );
146 emit detectionFailed();
147 deleteLater();
148 return;
149 }
150
151 QgsDebugMsgLevel( QStringLiteral( "Attempting connection to device %1 @ %2" ).arg( mPortIndex ).arg( mBaudIndex ), 2 );
152
153 if ( mPortList.at( mPortIndex ).first.contains( ':' ) )
154 {
155 mBaudIndex = mBaudList.size() - 1;
156
157 QStringList gpsParams = mPortList.at( mPortIndex ).first.split( ':' );
158
159 Q_ASSERT( gpsParams.size() >= 3 );
160 QgsDebugMsgLevel( QStringLiteral( "Connecting to GPSD device %1" ).arg( gpsParams.join( ',' ) ), 2 );
161
162 mConn = std::make_unique< QgsGpsdConnection >( gpsParams[0], gpsParams[1].toShort(), gpsParams[2] );
163 }
164 else if ( mPortList.at( mPortIndex ).first.contains( QLatin1String( "internalGPS" ) ) )
165 {
166#if defined(QT_POSITIONING_LIB)
167 QgsDebugMsgLevel( QStringLiteral( "Connecting to QtLocation service device" ), 2 );
168 mConn = std::make_unique< QgsQtLocationConnection >();
169#else
170 QgsDebugError( QStringLiteral( "QT_POSITIONING_LIB not found and mPortList matches internalGPS, this should never happen" ) );
171 qWarning( "QT_POSITIONING_LIB not found and mPortList matches internalGPS, this should never happen" );
172#endif
173 }
174 else
175 {
176#if defined( HAVE_QTSERIALPORT )
177 std::unique_ptr< QSerialPort > serial = std::make_unique< QSerialPort >( mPortList.at( mPortIndex ).first );
178
179 serial->setBaudRate( mBaudList[ mBaudIndex ] );
180
181 serial->setFlowControl( QgsGpsDetector::settingsGpsFlowControl->value() );
182 serial->setParity( QgsGpsDetector::settingsGpsParity->value() );
183 serial->setDataBits( QgsGpsDetector::settingsGpsDataBits->value() );
184 serial->setStopBits( QgsGpsDetector::settingsGpsStopBits->value() );
185
186 QgsDebugMsgLevel( QStringLiteral( "Connecting to serial GPS device %1 (@ %2)" ).arg( mPortList.at( mPortIndex ).first ).arg( mBaudList[ mBaudIndex ] ), 2 );
187
188 if ( serial->open( QIODevice::ReadOnly ) )
189 {
190 QgsDebugMsgLevel( QStringLiteral( "Successfully opened, have a port connection ready" ), 2 );
191 mConn = std::make_unique< QgsNmeaConnection >( serial.release() );
192 }
193 else
194 {
195 QgsDebugError( QStringLiteral( "Serial port could NOT be opened" ) );
196 }
197#else
198 QgsDebugError( QStringLiteral( "QT5SERIALPORT not found and mPortList matches serial port, this should never happen" ) );
199 qWarning( "QT5SERIALPORT not found and mPortList matches serial port, this should never happen" );
200#endif
201 }
202
203 if ( !mConn )
204 {
205 QgsDebugError( QStringLiteral( "Got to end of connection handling loop, but have no connection!" ) );
206 }
207 }
208
209 QgsDebugMsgLevel( QStringLiteral( "Have a connection, now listening for messages" ), 2 );
210
211 connect( mConn.get(), &QgsGpsConnection::stateChanged, this, qOverload< const QgsGpsInformation & >( &QgsGpsDetector::detected ) );
212 if ( mUseUnsafeSignals )
213 {
214 connect( mConn.get(), &QObject::destroyed, this, &QgsGpsDetector::connDestroyed );
215 }
216
217 // leave 2s to pickup a valid string
218 mTimeoutTimer->start( 2000 );
219}
220
222{
223 QgsDebugMsgLevel( QStringLiteral( "Detected information" ), 2 );
224
225 if ( !mConn )
226 {
227 mTimeoutTimer->stop();
228
229 // advance if connection was destroyed
230 QgsDebugError( QStringLiteral( "Got information, but CONNECTION WAS DESTROYED EXTERNALLY!" ) );
231 advance();
232 }
233 else if ( mConn->status() == QgsGpsConnection::GPSDataReceived )
234 {
235 mTimeoutTimer->stop();
236 // stop listening for state changed signals, we've already validated this connection and don't want subsequent calls
237 // to QgsGpsDetector::detected being made
238 disconnect( mConn.get(), &QgsGpsConnection::stateChanged, this, qOverload< const QgsGpsInformation & >( &QgsGpsDetector::detected ) );
239
240 // signal detected
241 QgsDebugMsgLevel( QStringLiteral( "Connection status IS GPSDataReceived" ), 2 );
242
243 if ( mUseUnsafeSignals )
244 {
245 // let's hope there's a single, unique connection to this signal... otherwise... boom!
247 emit detected( mConn.release() );
249 }
250 else
251 {
252 emit connectionDetected();
253 }
254
255 deleteLater();
256 }
257 else
258 {
259 // don't stop timeout, we keep waiting to see if later we get the desired connection status...
260 QgsDebugMsgLevel( QStringLiteral( "Connection status is NOT GPSDataReceived. It is %1" ).arg( mConn->status() ), 2 );
261 }
262}
263
264void QgsGpsDetector::connectionTimeout()
265{
266 QgsDebugMsgLevel( QStringLiteral( "No data received within max listening time" ), 2 );
267 advance();
268}
269
271{
272 QgsDebugError( QStringLiteral( "CONNECTION WAS DESTROYED EXTERNALLY!" ) );
273
274 // WTF? This whole class needs re-writing...
275 if ( obj == mConn.get() )
276 {
277 mConn.release();
278 }
279}
Abstract base class for connection to a GPS device.
void stateChanged(const QgsGpsInformation &info)
Emitted whenever the GPS state is changed.
~QgsGpsDetector() override
void connDestroyed(QObject *)
void detected(const QgsGpsInformation &)
static QList< QPair< QString, QString > > availablePorts()
QgsGpsConnection * takeConnection()
Returns the detected GPS connection, and removes it from the detector.
void connectionDetected()
Emitted when a GPS connection is successfully detected.
QgsGpsDetector(const QString &portName=QString(), bool useUnsafeSignals=true)
Constructor for QgsGpsDetector.
void detectionFailed()
Emitted when the detector could not find a valid GPS connection.
Encapsulates information relating to a GPS position fix.
A template class for enum and flag settings entry.
static QgsSettingsTreeNode * sTreeGps
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:5776
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5775
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38