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 else if (prn >= 65 && prn <= 96)
146 return QGeoSatelliteInfo::GLONASS;
147 else if (prn >= 193 && prn <= 200)
148 return QGeoSatelliteInfo::QZSS;
149 else if ((prn >= 201 && prn <= 235) || (prn >= 401 && prn <= 437))
150 return QGeoSatelliteInfo::BEIDOU;
151 else if (prn >= 301 && prn <= 336)
152 return QGeoSatelliteInfo::GALILEO;
153 return QGeoSatelliteInfo::Undefined;
154}
155
156void QGeoSatelliteInfoSourceGypsy::satellitesChanged(GypsySatellite *satellite,
157 GPtrArray *satellites)
158{
159 if (!satellite || !satellites)
160 return;
161 // We have satellite data and assume it is valid.
162 // If a single updateRequest was active, send signals right away.
163 // If a periodic timer was running (meaning that the client wishes
164 // to have updates at defined intervals), store the data for later sending.
165 QList<QGeoSatelliteInfo> lastSatellitesInView;
166 QList<QGeoSatelliteInfo> lastSatellitesInUse;
167
168 unsigned int i;
169 for (i = 0; i < satellites->len; i++) {
170 GypsySatelliteDetails *details = (GypsySatelliteDetails *)satellites->pdata[i];
171 QGeoSatelliteInfo info;
172 info.setSatelliteIdentifier(details->satellite_id);
173 info.setSatelliteSystem(idToSystem(details->satellite_id));
174 info.setAttribute(QGeoSatelliteInfo::Elevation, details->elevation);
175 info.setAttribute(QGeoSatelliteInfo::Azimuth, details->azimuth);
176 info.setSignalStrength(details->snr);
177 if (details->in_use)
178 lastSatellitesInUse.append(info);
179 lastSatellitesInView.append(info);
180 }
181 bool sendUpdates(false);
182 // If a single updateRequest() has been issued:
183 if (m_requestOngoing) {
184 sendUpdates = true;
185 m_requestTimer.stop();
186 m_requestOngoing = false;
187 // If there is no regular updates ongoing, disconnect now.
188 if (!m_updatesOngoing) {
189 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
190 }
191 }
192 // If regular updates are to be delivered as they come:
193 if (m_updatesOngoing)
194 sendUpdates = true;
195
196 if (sendUpdates) {
197 emit satellitesInUseUpdated(lastSatellitesInUse);
198 emit satellitesInViewUpdated(lastSatellitesInView);
199 }
200}
201
202QString QGeoSatelliteInfoSourceGypsy::extractDeviceNameFromParameters(const QVariantMap &parameters) const
203{
204 // The logic is as follows:
205 // 1. If the deviceNameParameter is specified, its value is used to get the
206 // device name.
207 // 2. If the gconfKeyParameter is specified, its value is used as a key to
208 // extract the device name from GConf.
209 // 3. If nothing is specified, defaultGconfKey is used as a key to extract
210 // the device name from GConf.
211 if (parameters.contains(deviceNameParameter))
212 return parameters.value(deviceNameParameter).toString();
213
214 QString gconfKey = parameters.value(gconfKeyParameter).toString();
215 if (gconfKey.isEmpty())
216 gconfKey = defaultGconfKey;
217
218 if (!m_engine)
219 return QString();
220
221 GConfClient *client = m_engine->eng_gconf_client_get_default();
222 if (!client)
223 return QString();
224
225 gchar *device_name = m_engine->eng_gconf_client_get_string(client,
226 gconfKey.toLatin1().constData(),
227 nullptr);
228 g_object_unref(client);
229
230 const QString deviceName = QString::fromLatin1(device_name);
231 m_engine->eng_g_free(device_name);
232
233 return deviceName;
234}
235
236int QGeoSatelliteInfoSourceGypsy::init(const QVariantMap parameters)
237{
238 GError *error = NULL;
239 char *path;
240
241#if !GLIB_CHECK_VERSION(2, 36, 0)
242 g_type_init (); // this function was deprecated in glib 2.36
243#endif
245
246 const QString deviceName = extractDeviceNameFromParameters(parameters);
247
248 if (deviceName.isEmpty() ||
249 (deviceName.trimmed().at(0) == '/' && !QFile::exists(deviceName.trimmed()))) {
250 qWarning ("QGeoSatelliteInfoSourceGypsy Empty/nonexistent GPS device name detected.");
251 qWarning("Use '%s' plugin parameter to specify device name directly", deviceNameParameter);
252 qWarning("or use '%s' plugin parameter to specify a GConf key to extract the device name.",
254 qWarning ("If the GConf key is used, the gconftool-2 tool can be used to set device name "
255 "for the selected key, e.g. on terminal:");
256 qWarning ("gconftool-2 -t string -s %s /dev/ttyUSB0", gconfKeyParameter);
257 return -1;
258 }
259 m_control = m_engine->eng_gypsy_control_get_default();
260 if (!m_control) {
261 qWarning("QGeoSatelliteInfoSourceGypsy unable to create Gypsy control.");
262 return -1;
263 }
264 // (path is the DBus path)
265 path = m_engine->eng_gypsy_control_create(m_control, deviceName.toLatin1().constData(), &error);
266 if (!path) {
267 qWarning ("QGeoSatelliteInfoSourceGypsy error creating client.");
268 if (error) {
269 qWarning ("error message: %s", error->message);
270 g_error_free (error);
271 }
272 return -1;
273 }
274 m_device = m_engine->eng_gypsy_device_new (path);
275 m_satellite = m_engine->eng_gypsy_satellite_new (path);
276 m_engine->eng_g_free(path);
277 if (!m_device || !m_satellite) {
278 qWarning ("QGeoSatelliteInfoSourceGypsy error creating satellite device.");
279 qWarning ("Please check that the GPS device is specified correctly.");
280 qWarning("Use '%s' plugin parameter to specify device name directly", deviceNameParameter);
281 qWarning("or use '%s' plugin parameter to specify a GConf key to extract the device name.",
283 qWarning ("If the GConf key is used, the gconftool-2 tool can be used to set device name "
284 "for the selected key, e.g. on terminal:");
285 qWarning ("gconftool-2 -t string -s %s /dev/ttyUSB0", gconfKeyParameter);
286 if (m_device)
287 g_object_unref(m_device);
288 if (m_satellite)
289 g_object_unref(m_satellite);
290 return -1;
291 }
292 m_engine->eng_gypsy_device_start (m_device, &error);
293 if (error) {
294 qWarning ("QGeoSatelliteInfoSourceGypsy error starting device: %s ",
295 error->message);
296 g_error_free(error);
297 g_object_unref(m_device);
298 g_object_unref(m_satellite);
299 return -1;
300 }
301 return 0;
302}
303
305{
306 return 1;
307}
308
310{
311 return m_error;
312}
313
315{
316 if (m_updatesOngoing)
317 return;
318
319 m_error = QGeoSatelliteInfoSource::NoError;
320
321 // If there is a request timer ongoing, we've connected to the signal already
322 if (!m_requestTimer.isActive()) {
323 m_engine->eng_g_signal_connect (m_satellite, "satellites-changed",
324 G_CALLBACK (satellites_changed), this);
325 }
326 m_updatesOngoing = true;
327}
328
330{
331 if (!m_updatesOngoing)
332 return;
333 m_updatesOngoing = false;
334 // Disconnect only if there is no single update request ongoing. Once single update request
335 // is completed and it notices that there is no active update ongoing, it will disconnect
336 // the signal.
337 if (!m_requestTimer.isActive())
338 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
339}
340
342{
343 if (m_requestOngoing)
344 return;
345
346 m_error = QGeoSatelliteInfoSource::NoError;
347
348 if (timeout < 0) {
349 setError(QGeoSatelliteInfoSource::UpdateTimeoutError);
350 return;
351 }
352 m_requestOngoing = true;
353 GError *error = 0;
354 // If GPS has a fix a already, request current data.
355 GypsyDeviceFixStatus fixStatus = m_engine->eng_gypsy_device_get_fix_status(m_device, &error);
356 if (!error && (fixStatus != GYPSY_DEVICE_FIX_STATUS_INVALID &&
357 fixStatus != GYPSY_DEVICE_FIX_STATUS_NONE)) {
358#ifdef Q_LOCATION_GYPSY_DEBUG
359 qDebug() << "QGeoSatelliteInfoSourceGypsy fix available, requesting current satellite data";
360#endif
361 GPtrArray *satelliteData = m_engine->eng_gypsy_satellite_get_satellites(m_satellite, &error);
362 if (!error) {
363 // The fix was available and we have satellite data to deliver right away.
364 satellitesChanged(m_satellite, satelliteData);
365 m_engine->eng_gypsy_satellite_free_satellite_array(satelliteData);
366 return;
367 }
368 }
369 // No fix is available. If updates are not ongoing already, start them.
370 m_requestTimer.setInterval(timeout == 0? UPDATE_TIMEOUT_COLD_START: timeout);
371 if (!m_updatesOngoing) {
372 m_engine->eng_g_signal_connect (m_satellite, "satellites-changed",
373 G_CALLBACK (satellites_changed), this);
374 }
375 m_requestTimer.start();
376 if (error) {
377#ifdef Q_LOCATION_GYPSY_DEBUG
378 qDebug() << "QGeoSatelliteInfoSourceGypsy error asking fix status or satellite data: " << error->message;
379#endif
380 g_error_free(error);
381 }
382}
383
384void QGeoSatelliteInfoSourceGypsy::requestUpdateTimeout()
385{
386#ifdef Q_LOCATION_GYPSY_DEBUG
387 qDebug("QGeoSatelliteInfoSourceGypsy request update timeout occurred.");
388#endif
389 // If we end up here, there has not been valid satellite update.
390 // Emit timeout and disconnect from signal if regular updates are not
391 // ongoing (as we were listening just for one single requestUpdate).
392 if (!m_updatesOngoing) {
393 m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this);
394 }
395 m_requestOngoing = false;
396 setError(QGeoSatelliteInfoSource::UpdateTimeoutError);
397}
398
399void QGeoSatelliteInfoSourceGypsy::setError(QGeoSatelliteInfoSource::Error error)
400{
401 m_error = error;
402 if (m_error != QGeoSatelliteInfoSource::NoError)
403 emit QGeoSatelliteInfoSource::errorOccurred(m_error);
404}
405
406QT_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