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