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