Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgeosatelliteinfosource_gypsy.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#ifdef Q_LOCATION_GYPSY_DEBUG
8#include <QDebug>
9#endif
10#include <QFile>
11#include <QVariantMap>
12
14
15#define UPDATE_TIMEOUT_COLD_START 120000
16
17static const auto deviceNameParameter = "deviceName";
18static const auto gconfKeyParameter = "gconfKey";
19static const auto defaultGconfKey = "/apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice";
20
21// Callback function for 'satellites-changed' -signal
22static void satellites_changed (GypsySatellite *satellite,
23 GPtrArray *satellites,
24 gpointer userdata)
25{
26#ifdef Q_LOCATION_GYPSY_DEBUG
27 qDebug() << "QGeoSatelliteInfoSourceGypsy Gypsy satellites-changed -signal received.";
28#endif
29 ((QGeoSatelliteInfoSourceGypsy *)userdata)->satellitesChanged(satellite, satellites);
30}
31
32SatelliteGypsyEngine::SatelliteGypsyEngine(QGeoSatelliteInfoSource *parent) :
33 m_owner(parent)
34{
35}
36SatelliteGypsyEngine::~SatelliteGypsyEngine()
37{
38}
39
40// Glib symbols
41gulong SatelliteGypsyEngine::eng_g_signal_connect(gpointer instance,
42 const gchar *detailed_signal,
43 GCallback c_handler,
44 gpointer data)
45{
46 return ::g_signal_connect(instance, detailed_signal, c_handler, data);
47}
48guint SatelliteGypsyEngine::eng_g_signal_handlers_disconnect_by_func (gpointer instance,
49 gpointer func,
50 gpointer data)
51{
52 return ::g_signal_handlers_disconnect_by_func(instance, func, data);
53}
54
55void SatelliteGypsyEngine::eng_g_free(gpointer mem)
56{
57 return ::g_free(mem);
58}
59// Gypsy symbols
60GypsyControl *SatelliteGypsyEngine::eng_gypsy_control_get_default (void)
61{
62 return ::gypsy_control_get_default();
63}
64char *SatelliteGypsyEngine::eng_gypsy_control_create (GypsyControl *control, const char *device_name, GError **error)
65{
66 return ::gypsy_control_create(control, device_name, error);
67}
68GypsyDevice *SatelliteGypsyEngine::eng_gypsy_device_new (const char *object_path)
69{
70 return ::gypsy_device_new(object_path);
71}
72GypsySatellite *SatelliteGypsyEngine::eng_gypsy_satellite_new (const char *object_path)
73{
74 return ::gypsy_satellite_new (object_path);
75}
76gboolean SatelliteGypsyEngine::eng_gypsy_device_start (GypsyDevice *device, GError **error)
77{
78 return ::gypsy_device_start(device, error);
79}
80gboolean SatelliteGypsyEngine::eng_gypsy_device_stop (GypsyDevice *device, GError **error)
81{
82 // Unfortunately this cannot be done; calling this will stop the GPS device
83 // (basically makes gypsy-daemon unusable for anyone), regardless of applications
84 // using it (see bug http://bugs.meego.com/show_bug.cgi?id=11707).
85 Q_UNUSED(device);
86 Q_UNUSED(error);
87 return true;
88 //return ::gypsy_device_stop (device, error);
89}
90GypsyDeviceFixStatus SatelliteGypsyEngine::eng_gypsy_device_get_fix_status (GypsyDevice *device, GError **error)
91{
92 return ::gypsy_device_get_fix_status (device, error);
93}
94GPtrArray *SatelliteGypsyEngine::eng_gypsy_satellite_get_satellites (GypsySatellite *satellite, GError **error)
95{
96 return ::gypsy_satellite_get_satellites (satellite, error);
97}
98void SatelliteGypsyEngine::eng_gypsy_satellite_free_satellite_array (GPtrArray *satellites)
99{
100 return ::gypsy_satellite_free_satellite_array(satellites);
101}
102// GConf symbols (mockability due to X11 requirement)
103GConfClient *SatelliteGypsyEngine::eng_gconf_client_get_default(void)
104{
105 return ::gconf_client_get_default();
106}
107gchar *SatelliteGypsyEngine::eng_gconf_client_get_string(GConfClient *client, const gchar *key, GError** err)
108{
109 return ::gconf_client_get_string(client, key, err);
110}
111
112QGeoSatelliteInfoSourceGypsy::QGeoSatelliteInfoSourceGypsy(QObject *parent)
113 : QGeoSatelliteInfoSource(parent), m_engine(0), m_satellite(0), m_device(0),
114 m_requestTimer(this), m_updatesOngoing(false), m_requestOngoing(false)
115{
116 m_requestTimer.setSingleShot(true);
117 QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout()));
118}
119
121{
122 delete m_engine;
123 m_engine = new SatelliteGypsyEngine(this);
124}
125
127{
128 GError *error = NULL;
129 if (m_device) {
130 m_engine->eng_gypsy_device_stop (m_device, &error);
131 g_object_unref(m_device);
132 }
133 if (m_satellite)
134 g_object_unref(m_satellite);
135 if (m_control)
136 g_object_unref(m_control);
137 if (error)
138 g_error_free(error);
139 delete m_engine;
140}
141
143{
144 if (prn >= 1 && prn <= 32)
145 return QGeoSatelliteInfo::GPS;
146 if (prn >= 33 && prn <= 64)
147 return QGeoSatelliteInfo::SBAS;
148 else if (prn >= 65 && prn <= 96)
149 return QGeoSatelliteInfo::GLONASS;
150 else if (prn >= 193 && prn <= 200)
151 return QGeoSatelliteInfo::QZSS;
152 else if ((prn >= 201 && prn <= 235) || (prn >= 401 && prn <= 437))
153 return QGeoSatelliteInfo::BEIDOU;
154 else if (prn >= 301 && prn <= 336)
155 return QGeoSatelliteInfo::GALILEO;
156 return QGeoSatelliteInfo::Undefined;
157}
158
159void QGeoSatelliteInfoSourceGypsy::satellitesChanged(GypsySatellite *satellite,
160 GPtrArray *satellites)
161{
162 if (!satellite || !satellites)
163 return;
164 // We have satellite data and assume it is valid.
165 // If a single updateRequest was active, send signals right away.
166 // If a periodic timer was running (meaning that the client wishes
167 // to have updates at defined intervals), store the data for later sending.
168 QList<QGeoSatelliteInfo> lastSatellitesInView;
169 QList<QGeoSatelliteInfo> lastSatellitesInUse;
170
171 unsigned int i;
172 for (i = 0; i < satellites->len; i++) {
173 GypsySatelliteDetails *details = (GypsySatelliteDetails *)satellites->pdata[i];
174 QGeoSatelliteInfo info;
175 info.setSatelliteIdentifier(details->satellite_id);
176 info.setSatelliteSystem(idToSystem(details->satellite_id));
177 info.setAttribute(QGeoSatelliteInfo::Elevation, details->elevation);
178 info.setAttribute(QGeoSatelliteInfo::Azimuth, details->azimuth);
179 info.setSignalStrength(details->snr);
180 if (details->in_use)
181 lastSatellitesInUse.append(info);
182 lastSatellitesInView.append(info);
183 }
184 bool sendUpdates(false);
185 // If a single updateRequest() has been issued:
186 if (m_requestOngoing) {
187 sendUpdates = true;
188 m_requestTimer.stop();
189 m_requestOngoing = false;
190 // If there is no regular updates ongoing, disconnect now.
191 if (!m_updatesOngoing) {
192 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
193 }
194 }
195 // If regular updates are to be delivered as they come:
196 if (m_updatesOngoing)
197 sendUpdates = true;
198
199 if (sendUpdates) {
200 emit satellitesInUseUpdated(lastSatellitesInUse);
201 emit satellitesInViewUpdated(lastSatellitesInView);
202 }
203}
204
205QString QGeoSatelliteInfoSourceGypsy::extractDeviceNameFromParameters(const QVariantMap &parameters) const
206{
207 // The logic is as follows:
208 // 1. If the deviceNameParameter is specified, its value is used to get the
209 // device name.
210 // 2. If the gconfKeyParameter is specified, its value is used as a key to
211 // extract the device name from GConf.
212 // 3. If nothing is specified, defaultGconfKey is used as a key to extract
213 // the device name from GConf.
214 if (parameters.contains(deviceNameParameter))
215 return parameters.value(deviceNameParameter).toString();
216
217 QString gconfKey = parameters.value(gconfKeyParameter).toString();
218 if (gconfKey.isEmpty())
219 gconfKey = defaultGconfKey;
220
221 if (!m_engine)
222 return QString();
223
224 GConfClient *client = m_engine->eng_gconf_client_get_default();
225 if (!client)
226 return QString();
227
228 gchar *device_name = m_engine->eng_gconf_client_get_string(client,
229 gconfKey.toLatin1().constData(),
230 nullptr);
231 g_object_unref(client);
232
233 const QString deviceName = QString::fromLatin1(device_name);
234 m_engine->eng_g_free(device_name);
235
236 return deviceName;
237}
238
239int QGeoSatelliteInfoSourceGypsy::init(const QVariantMap parameters)
240{
241 GError *error = NULL;
242 char *path;
243
244#if !GLIB_CHECK_VERSION(2, 36, 0)
245 g_type_init (); // this function was deprecated in glib 2.36
246#endif
248
249 const QString deviceName = extractDeviceNameFromParameters(parameters);
250
251 if (deviceName.isEmpty() ||
252 (deviceName.trimmed().at(0) == '/' && !QFile::exists(deviceName.trimmed()))) {
253 qWarning ("QGeoSatelliteInfoSourceGypsy Empty/nonexistent GPS device name detected.");
254 qWarning("Use '%s' plugin parameter to specify device name directly", deviceNameParameter);
255 qWarning("or use '%s' plugin parameter to specify a GConf key to extract the device name.",
257 qWarning ("If the GConf key is used, the gconftool-2 tool can be used to set device name "
258 "for the selected key, e.g. on terminal:");
259 qWarning ("gconftool-2 -t string -s %s /dev/ttyUSB0", gconfKeyParameter);
260 return -1;
261 }
262 m_control = m_engine->eng_gypsy_control_get_default();
263 if (!m_control) {
264 qWarning("QGeoSatelliteInfoSourceGypsy unable to create Gypsy control.");
265 return -1;
266 }
267 // (path is the DBus path)
268 path = m_engine->eng_gypsy_control_create(m_control, deviceName.toLatin1().constData(), &error);
269 if (!path) {
270 qWarning ("QGeoSatelliteInfoSourceGypsy error creating client.");
271 if (error) {
272 qWarning ("error message: %s", error->message);
273 g_error_free (error);
274 }
275 return -1;
276 }
277 m_device = m_engine->eng_gypsy_device_new (path);
278 m_satellite = m_engine->eng_gypsy_satellite_new (path);
279 m_engine->eng_g_free(path);
280 if (!m_device || !m_satellite) {
281 qWarning ("QGeoSatelliteInfoSourceGypsy error creating satellite device.");
282 qWarning ("Please check that the GPS device is specified correctly.");
283 qWarning("Use '%s' plugin parameter to specify device name directly", deviceNameParameter);
284 qWarning("or use '%s' plugin parameter to specify a GConf key to extract the device name.",
286 qWarning ("If the GConf key is used, the gconftool-2 tool can be used to set device name "
287 "for the selected key, e.g. on terminal:");
288 qWarning ("gconftool-2 -t string -s %s /dev/ttyUSB0", gconfKeyParameter);
289 if (m_device)
290 g_object_unref(m_device);
291 if (m_satellite)
292 g_object_unref(m_satellite);
293 return -1;
294 }
295 m_engine->eng_gypsy_device_start (m_device, &error);
296 if (error) {
297 qWarning ("QGeoSatelliteInfoSourceGypsy error starting device: %s ",
298 error->message);
299 g_error_free(error);
300 g_object_unref(m_device);
301 g_object_unref(m_satellite);
302 return -1;
303 }
304 return 0;
305}
306
308{
309 return 1;
310}
311
313{
314 return m_error;
315}
316
318{
319 if (m_updatesOngoing)
320 return;
321
322 m_error = QGeoSatelliteInfoSource::NoError;
323
324 // If there is a request timer ongoing, we've connected to the signal already
325 if (!m_requestTimer.isActive()) {
326 m_engine->eng_g_signal_connect (m_satellite, "satellites-changed",
327 G_CALLBACK (satellites_changed), this);
328 }
329 m_updatesOngoing = true;
330}
331
333{
334 if (!m_updatesOngoing)
335 return;
336 m_updatesOngoing = false;
337 // Disconnect only if there is no single update request ongoing. Once single update request
338 // is completed and it notices that there is no active update ongoing, it will disconnect
339 // the signal.
340 if (!m_requestTimer.isActive())
341 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
342}
343
345{
346 if (m_requestOngoing)
347 return;
348
349 m_error = QGeoSatelliteInfoSource::NoError;
350
351 if (timeout < 0) {
352 setError(QGeoSatelliteInfoSource::UpdateTimeoutError);
353 return;
354 }
355 m_requestOngoing = true;
356 GError *error = 0;
357 // If GPS has a fix a already, request current data.
358 GypsyDeviceFixStatus fixStatus = m_engine->eng_gypsy_device_get_fix_status(m_device, &error);
359 if (!error && (fixStatus != GYPSY_DEVICE_FIX_STATUS_INVALID &&
360 fixStatus != GYPSY_DEVICE_FIX_STATUS_NONE)) {
361#ifdef Q_LOCATION_GYPSY_DEBUG
362 qDebug() << "QGeoSatelliteInfoSourceGypsy fix available, requesting current satellite data";
363#endif
364 GPtrArray *satelliteData = m_engine->eng_gypsy_satellite_get_satellites(m_satellite, &error);
365 if (!error) {
366 // The fix was available and we have satellite data to deliver right away.
367 satellitesChanged(m_satellite, satelliteData);
368 m_engine->eng_gypsy_satellite_free_satellite_array(satelliteData);
369 return;
370 }
371 }
372 // No fix is available. If updates are not ongoing already, start them.
373 m_requestTimer.setInterval(timeout == 0? UPDATE_TIMEOUT_COLD_START: timeout);
374 if (!m_updatesOngoing) {
375 m_engine->eng_g_signal_connect (m_satellite, "satellites-changed",
376 G_CALLBACK (satellites_changed), this);
377 }
378 m_requestTimer.start();
379 if (error) {
380#ifdef Q_LOCATION_GYPSY_DEBUG
381 qDebug() << "QGeoSatelliteInfoSourceGypsy error asking fix status or satellite data: " << error->message;
382#endif
383 g_error_free(error);
384 }
385}
386
387void QGeoSatelliteInfoSourceGypsy::requestUpdateTimeout()
388{
389#ifdef Q_LOCATION_GYPSY_DEBUG
390 qDebug("QGeoSatelliteInfoSourceGypsy request update timeout occurred.");
391#endif
392 // If we end up here, there has not been valid satellite update.
393 // Emit timeout and disconnect from signal if regular updates are not
394 // ongoing (as we were listening just for one single requestUpdate).
395 if (!m_updatesOngoing) {
396 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
397 }
398 m_requestOngoing = false;
399 setError(QGeoSatelliteInfoSource::UpdateTimeoutError);
400}
401
402void QGeoSatelliteInfoSourceGypsy::setError(QGeoSatelliteInfoSource::Error error)
403{
404 m_error = error;
405 if (m_error != QGeoSatelliteInfoSource::NoError)
406 emit QGeoSatelliteInfoSource::errorOccurred(m_error);
407}
408
409QT_END_NAMESPACE
void satellitesChanged(GypsySatellite *satellite, GPtrArray *satellites)
void requestUpdate(int timeout=5000) override
int init(const QVariantMap parameters)
Error error() const override
Returns the last error that occurred.
Combined button and popup list for selecting options.
static const auto deviceNameParameter
static const auto gconfKeyParameter
static const auto defaultGconfKey
static void satellites_changed(GypsySatellite *satellite, GPtrArray *satellites, gpointer userdata)
static QGeoSatelliteInfo::SatelliteSystem idToSystem(int prn)
#define UPDATE_TIMEOUT_COLD_START