1 /***************************************************************************
2                           QgsQtLocationConnection.cpp  -  description
3                           ---------------------
4     begin                : December 7th, 2011
5     copyright            : (C) 2011 by Marco Bernasocchi, Bernawebdesign.ch
6     email                : marco at bernawebdesign dot ch
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 "qgsqtlocationconnection.h"
19 #include "qgslogger.h"
20 
21 #include <QLocalSocket>
22 #include <QTimer>
23 #include <QMetaType>
24 
QgsQtLocationConnection()25 QgsQtLocationConnection::QgsQtLocationConnection()
26   : QgsGpsConnection( new QLocalSocket() )
27 {
28   //needed to fix https://sourceforge.net/p/necessitas/tickets/146/
29   qRegisterMetaType< QList<QGeoSatelliteInfo> >( "QList<QGeoSatelliteInfo>" );
30 
31   startSatelliteMonitor();
32   startGPS();
33 
34   //HACK to signal the gpsinformationwidget that we have a QtLocationConnection
35   QTimer::singleShot( 500, this, SLOT( broadcastConnectionAvailable() ) );
36 }
37 
38 //Needed to make connection detectable (half HACK)
39 //this signals that the device has started the GPS successfully,
40 //not that it has a fix yet.
broadcastConnectionAvailable()41 void QgsQtLocationConnection::broadcastConnectionAvailable()
42 {
43   if ( locationDataSource )
44   {
45     mStatus = GPSDataReceived;
46     emit stateChanged( mLastGPSInformation );
47   }
48 }
49 
50 //TODO: Temporarily needed to workaround https://sourceforge.net/p/necessitas/tickets/147/
positionUpdated(const QGeoPositionInfo & info)51 void QgsQtLocationConnection::positionUpdated( const QGeoPositionInfo &info )
52 {
53   mInfo = info;
54   parseData();
55 }
56 
parseData()57 void QgsQtLocationConnection::parseData()
58 {
59   if ( locationDataSource )
60   {
61     mStatus = GPSDataReceived;
62     //const QGeoPositionInfo &info = locationDataSource->lastKnownPosition();
63     if ( mInfo.isValid() )
64     {
65       // mInfo.HorizontalAccuracy;
66       mLastGPSInformation.latitude = mInfo.coordinate().latitude();
67       mLastGPSInformation.longitude = mInfo.coordinate().longitude();
68       mLastGPSInformation.elevation = mInfo.coordinate().altitude();
69       mLastGPSInformation.speed = mInfo.attribute( QGeoPositionInfo::GroundSpeed ) * 3.6; // m/s to km/h
70       mLastGPSInformation.direction = mInfo.attribute( QGeoPositionInfo::Direction );
71       mLastGPSInformation.utcDateTime = mInfo.timestamp();
72       mLastGPSInformation.fixType = mInfo.coordinate().type() + 1;
73       //< fixType, used for navigation (1 = Fix not available; 2 = 2D; 3 = 3D)
74       //< coordinate().type(), returns 0 = Fix not available; 1 = 2D; 2 = 3D)
75       mLastGPSInformation.hacc = mInfo.attribute( QGeoPositionInfo::HorizontalAccuracy );   //< Horizontal dilution of precision
76       mLastGPSInformation.vacc = mInfo.attribute( QGeoPositionInfo::VerticalAccuracy );   //< Vertical dilution of precision
77 
78       //TODO implement dop maybe by getting a
79       //http://developer.android.com/reference/android/location/GpsStatus.NmeaListener.html
80       //http://doc.qt.nokia.com/qtmobility-1.1/qnmeapositioninfosource.html
81       //into QtLocation and subclass QgsNMEAConnection directly?
82       //mLastGPSInformation.pdop;     //< Dilution of precision
83       //mLastGPSInformation.hdop;     //< Horizontal dilution of precision
84       //mLastGPSInformation.vdop;     //< Vertical dilution of precision
85 
86       //mLastGPSInformation.fixMode;  //< Mode (M = Manual, forced to operate in 2D or 3D; A = Automatic, 3D/2D)
87       //mLastGPSInformation.quality;  //< GPS quality indicator (0 = Invalid; 1 = Fix; 2 = Differential, 3 = Sensitive)
88       //mLastGPSInformation.status;   //< Status (A = active or V = void)
89 
90       emit stateChanged( mLastGPSInformation );
91       QgsDebugMsg( QStringLiteral( "Valid QGeoPositionInfo, positionUpdated" ) );
92     }
93   }
94 }
95 
satellitesInViewUpdated(const QList<QGeoSatelliteInfo> & satellites)96 void QgsQtLocationConnection::satellitesInViewUpdated(
97   const QList<QGeoSatelliteInfo> &satellites )
98 {
99   // The number of satellites in view is updated
100   mLastGPSInformation.satellitesInView.clear();
101   for ( int i = 0; i < satellites.size(); ++i )
102   {
103     QGeoSatelliteInfo currentSatellite = satellites.at( i );
104     QgsSatelliteInfo satelliteInfo;
105     satelliteInfo.azimuth = currentSatellite.attribute( QGeoSatelliteInfo::Azimuth );
106     satelliteInfo.elevation = currentSatellite.attribute( QGeoSatelliteInfo::Elevation );
107 #if defined(HAVE_QT_MOBILITY_LOCATION )
108     satelliteInfo.id = currentSatellite.prnNumber();
109 #else // QtPositioning
110     satelliteInfo.id = currentSatellite.satelliteIdentifier();
111 #endif
112     satelliteInfo.signal = currentSatellite.signalStrength();
113     mLastGPSInformation.satellitesInView.append( satelliteInfo );
114   }
115   mLastGPSInformation.satInfoComplete = true;  //to be used to determine when to graph signal and satellite position
116   emit stateChanged( mLastGPSInformation );
117   QgsDebugMsg( QStringLiteral( "satellitesInViewUpdated" ) );
118 }
119 
satellitesInUseUpdated(const QList<QGeoSatelliteInfo> & satellites)120 void QgsQtLocationConnection::satellitesInUseUpdated(
121   const QList<QGeoSatelliteInfo> &satellites )
122 {
123   // The number of satellites in use is updated
124   mLastGPSInformation.satellitesUsed = QString::number( satellites.count() ).toInt();
125 
126   mLastGPSInformation.satPrn.clear();
127   for ( const QGeoSatelliteInfo &currentSatellite : satellites )
128   {
129     //add pnr to mLastGPSInformation.satPrn
130 #if defined(HAVE_QT_MOBILITY_LOCATION )
131     mLastGPSInformation.satPrn.append( currentSatellite.prnNumber() );
132 #else // QtPositioning
133     mLastGPSInformation.satPrn.append( currentSatellite.satelliteIdentifier() );
134 #endif
135 
136     //set QgsSatelliteInfo.inuse to true for the satellites in use
137     for ( QgsSatelliteInfo &satInView : mLastGPSInformation.satellitesInView )
138     {
139 #if defined(HAVE_QT_MOBILITY_LOCATION )
140       if ( satInView.id == currentSatellite.prnNumber() )
141 #else // QtPositioning
142       if ( satInView.id == currentSatellite.satelliteIdentifier() )
143 #endif
144       {
145         satInView.inUse = true;
146         break;
147       }
148     }
149   }
150   mLastGPSInformation.satInfoComplete = true;  //to be used to determine when to graph signal and satellite position
151   emit stateChanged( mLastGPSInformation );
152   QgsDebugMsg( QStringLiteral( "satellitesInUseUpdated" ) );
153 }
154 
startGPS()155 void QgsQtLocationConnection::startGPS()
156 {
157   QgsDebugMsg( QStringLiteral( "Starting GPS QtLocation connection" ) );
158   // Obtain the location data source if it is not obtained already
159   if ( !locationDataSource )
160   {
161     locationDataSource = QGeoPositionInfoSource::createDefaultSource( this );
162     if ( locationDataSource )
163     {
164       locationDataSource->setPreferredPositioningMethods( QGeoPositionInfoSource::SatellitePositioningMethods );  //QGeoPositionInfoSource::AllPositioningMethods
165       locationDataSource->setUpdateInterval( 1000 );
166       // Whenever the location data source signals that the current
167       // position is updated, the positionUpdated function is called.
168       QObject::connect( locationDataSource.data(),
169                         &QGeoPositionInfoSource::positionUpdated,
170                         this,
171                         &QgsQtLocationConnection::positionUpdated );
172       // Start listening for position updates
173       locationDataSource->startUpdates();
174     }
175     else
176     {
177       // Not able to obtain the location data source
178       QgsDebugMsg( QStringLiteral( "No QtLocation Position Source" ) );
179     }
180   }
181   else
182   {
183     // Start listening for position updates
184     locationDataSource->startUpdates();
185   }
186 }
187 
startSatelliteMonitor()188 void QgsQtLocationConnection::startSatelliteMonitor()
189 {
190   QgsDebugMsg( QStringLiteral( "Starting GPS QtLocation satellite monitor" ) );
191 
192   if ( !satelliteInfoSource )
193   {
194     satelliteInfoSource = QGeoSatelliteInfoSource::createDefaultSource( this );
195     if ( satelliteInfoSource )
196     {
197       QgsDebugMsg( QStringLiteral( "satelliteMonitor started" ) );
198       // Whenever the satellite info source signals that the number of
199       // satellites in use is updated, the satellitesInUseUpdated function
200       // is called
201       QObject::connect( satelliteInfoSource.data(),
202                         &QGeoSatelliteInfoSource::satellitesInUseUpdated,
203                         this,
204                         &QgsQtLocationConnection::satellitesInUseUpdated );
205 
206       // Whenever the satellite info source signals that the number of
207       // satellites in view is updated, the satellitesInViewUpdated function
208       // is called
209       QObject::connect( satelliteInfoSource.data(),
210                         &QGeoSatelliteInfoSource::satellitesInViewUpdated,
211                         this,
212                         &QgsQtLocationConnection::satellitesInViewUpdated );
213 
214       // Start listening for satellite updates
215       satelliteInfoSource->startUpdates();
216     }
217     else
218     {
219       // Not able to obtain the Satellite data source
220       QgsDebugMsg( QStringLiteral( "No QtLocation Satellite Source" ) );
221     }
222   }
223   else
224   {
225     // Start listening for position updates
226     satelliteInfoSource->startUpdates();
227   }
228 }
229