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