/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtPositioning module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgeosatelliteinfosource_gypsy_p.h" #ifdef Q_LOCATION_GYPSY_DEBUG #include #endif #include QT_BEGIN_NAMESPACE #define UPDATE_TIMEOUT_COLD_START 120000 // Callback function for 'satellites-changed' -signal static void satellites_changed (GypsySatellite *satellite, GPtrArray *satellites, gpointer userdata) { #ifdef Q_LOCATION_GYPSY_DEBUG qDebug() << "QGeoSatelliteInfoSourceGypsy Gypsy satellites-changed -signal received."; #endif ((QGeoSatelliteInfoSourceGypsy *)userdata)->satellitesChanged(satellite, satellites); } SatelliteGypsyEngine::SatelliteGypsyEngine(QGeoSatelliteInfoSource *parent) : m_owner(parent) { } SatelliteGypsyEngine::~SatelliteGypsyEngine() { } // Glib symbols gulong SatelliteGypsyEngine::eng_g_signal_connect(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data) { return ::g_signal_connect(instance, detailed_signal, c_handler, data); } guint SatelliteGypsyEngine::eng_g_signal_handlers_disconnect_by_func (gpointer instance, gpointer func, gpointer data) { return ::g_signal_handlers_disconnect_by_func(instance, func, data); } void SatelliteGypsyEngine::eng_g_free(gpointer mem) { return ::g_free(mem); } // Gypsy symbols GypsyControl *SatelliteGypsyEngine::eng_gypsy_control_get_default (void) { return ::gypsy_control_get_default(); } char *SatelliteGypsyEngine::eng_gypsy_control_create (GypsyControl *control, const char *device_name, GError **error) { return ::gypsy_control_create(control, device_name, error); } GypsyDevice *SatelliteGypsyEngine::eng_gypsy_device_new (const char *object_path) { return ::gypsy_device_new(object_path); } GypsySatellite *SatelliteGypsyEngine::eng_gypsy_satellite_new (const char *object_path) { return ::gypsy_satellite_new (object_path); } gboolean SatelliteGypsyEngine::eng_gypsy_device_start (GypsyDevice *device, GError **error) { return ::gypsy_device_start(device, error); } gboolean SatelliteGypsyEngine::eng_gypsy_device_stop (GypsyDevice *device, GError **error) { // Unfortunately this cannot be done; calling this will stop the GPS device // (basically makes gypsy-daemon unusable for anyone), regardless of applications // using it (see bug http://bugs.meego.com/show_bug.cgi?id=11707). Q_UNUSED(device); Q_UNUSED(error); return true; //return ::gypsy_device_stop (device, error); } GypsyDeviceFixStatus SatelliteGypsyEngine::eng_gypsy_device_get_fix_status (GypsyDevice *device, GError **error) { return ::gypsy_device_get_fix_status (device, error); } GPtrArray *SatelliteGypsyEngine::eng_gypsy_satellite_get_satellites (GypsySatellite *satellite, GError **error) { return ::gypsy_satellite_get_satellites (satellite, error); } void SatelliteGypsyEngine::eng_gypsy_satellite_free_satellite_array (GPtrArray *satellites) { return ::gypsy_satellite_free_satellite_array(satellites); } // GConf symbols (mockability due to X11 requirement) GConfClient *SatelliteGypsyEngine::eng_gconf_client_get_default(void) { return ::gconf_client_get_default(); } gchar *SatelliteGypsyEngine::eng_gconf_client_get_string(GConfClient *client, const gchar *key, GError** err) { return ::gconf_client_get_string(client, key, err); } QGeoSatelliteInfoSourceGypsy::QGeoSatelliteInfoSourceGypsy(QObject *parent) : QGeoSatelliteInfoSource(parent), m_engine(0), m_satellite(0), m_device(0), m_requestTimer(this), m_updatesOngoing(false), m_requestOngoing(false) { m_requestTimer.setSingleShot(true); QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); } void QGeoSatelliteInfoSourceGypsy::createEngine() { delete m_engine; m_engine = new SatelliteGypsyEngine(this); } QGeoSatelliteInfoSourceGypsy::~QGeoSatelliteInfoSourceGypsy() { GError *error = NULL; if (m_device) { m_engine->eng_gypsy_device_stop (m_device, &error); g_object_unref(m_device); } if (m_satellite) g_object_unref(m_satellite); if (error) g_error_free(error); delete m_engine; } void QGeoSatelliteInfoSourceGypsy::satellitesChanged(GypsySatellite *satellite, GPtrArray *satellites) { if (!satellite || !satellites) return; // We have satellite data and assume it is valid. // If a single updateRequest was active, send signals right away. // If a periodic timer was running (meaning that the client wishes // to have updates at defined intervals), store the data for later sending. QList lastSatellitesInView; QList lastSatellitesInUse; unsigned int i; for (i = 0; i < satellites->len; i++) { GypsySatelliteDetails *details = (GypsySatelliteDetails *)satellites->pdata[i]; QGeoSatelliteInfo info; info.setAttribute(QGeoSatelliteInfo::Elevation, details->elevation); info.setAttribute(QGeoSatelliteInfo::Azimuth, details->azimuth); info.setSignalStrength(details->snr); if (details->in_use) lastSatellitesInUse.append(info); lastSatellitesInView.append(info); } bool sendUpdates(false); // If a single updateRequest() has been issued: if (m_requestOngoing) { sendUpdates = true; m_requestTimer.stop(); m_requestOngoing = false; // If there is no regular updates ongoing, disconnect now. if (!m_updatesOngoing) { m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); } } // If regular updates are to be delivered as they come: if (m_updatesOngoing) sendUpdates = true; if (sendUpdates) { emit satellitesInUseUpdated(lastSatellitesInUse); emit satellitesInViewUpdated(lastSatellitesInView); } } int QGeoSatelliteInfoSourceGypsy::init() { GError *error = NULL; char *path; GConfClient *client; gchar *device_name; g_type_init (); createEngine(); client = m_engine->eng_gconf_client_get_default(); if (!client) { qWarning ("QGeoSatelliteInfoSourceGypsy client creation failed."); return -1; } device_name = m_engine->eng_gconf_client_get_string(client, "/apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice", NULL); g_object_unref(client); QString deviceName(QString::fromLatin1(device_name)); if (deviceName.isEmpty() || (deviceName.trimmed().at(0) == '/' && !QFile::exists(deviceName.trimmed()))) { qWarning ("QGeoSatelliteInfoSourceGypsy Empty/nonexistent GPS device name detected."); qWarning ("Use gconftool-2 to set it, e.g. on terminal: "); qWarning ("gconftool-2 -t string -s /apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice /dev/ttyUSB0"); m_engine->eng_g_free(device_name); return -1; } GypsyControl *control = NULL; control = m_engine->eng_gypsy_control_get_default(); if (!control) { qWarning("QGeoSatelliteInfoSourceGypsy unable to create Gypsy control."); m_engine->eng_g_free(device_name); return -1; } // (path is the DBus path) path = m_engine->eng_gypsy_control_create (control, device_name, &error); m_engine->eng_g_free(device_name); g_object_unref(control); if (!path) { qWarning ("QGeoSatelliteInfoSourceGypsy error creating client."); if (error) { qWarning ("error message: %s", error->message); g_error_free (error); } return -1; } m_device = m_engine->eng_gypsy_device_new (path); m_satellite = m_engine->eng_gypsy_satellite_new (path); m_engine->eng_g_free(path); if (!m_device || !m_satellite) { qWarning ("QGeoSatelliteInfoSourceGypsy error creating satellite device."); qWarning ("Is GPS device set correctly? If not, use gconftool-2 to set it, e.g.: "); qWarning ("gconftool-2 -t string -s /apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice /dev/ttyUSB0"); if (m_device) g_object_unref(m_device); if (m_satellite) g_object_unref(m_satellite); return -1; } m_engine->eng_gypsy_device_start (m_device, &error); if (error) { qWarning ("QGeoSatelliteInfoSourceGypsy error starting device: %s ", error->message); g_error_free(error); g_object_unref(m_device); g_object_unref(m_satellite); return -1; } return 0; } int QGeoSatelliteInfoSourceGypsy::minimumUpdateInterval() const { return 1; } QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceGypsy::error() const { return NoError; } void QGeoSatelliteInfoSourceGypsy::startUpdates() { if (m_updatesOngoing) return; // If there is a request timer ongoing, we've connected to the signal already if (!m_requestTimer.isActive()) { m_engine->eng_g_signal_connect (m_satellite, "satellites-changed", G_CALLBACK (satellites_changed), this); } m_updatesOngoing = true; } void QGeoSatelliteInfoSourceGypsy::stopUpdates() { if (!m_updatesOngoing) return; m_updatesOngoing = false; // Disconnect only if there is no single update request ongoing. Once single update request // is completed and it notices that there is no active update ongoing, it will disconnect // the signal. if (!m_requestTimer.isActive()) m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); } void QGeoSatelliteInfoSourceGypsy::requestUpdate(int timeout) { if (m_requestOngoing) return; if (timeout < 0) { emit requestTimeout(); return; } m_requestOngoing = true; GError *error = 0; // If GPS has a fix a already, request current data. GypsyDeviceFixStatus fixStatus = m_engine->eng_gypsy_device_get_fix_status(m_device, &error); if (!error && (fixStatus != GYPSY_DEVICE_FIX_STATUS_INVALID && fixStatus != GYPSY_DEVICE_FIX_STATUS_NONE)) { #ifdef Q_LOCATION_GYPSY_DEBUG qDebug() << "QGeoSatelliteInfoSourceGypsy fix available, requesting current satellite data"; #endif GPtrArray *satelliteData = m_engine->eng_gypsy_satellite_get_satellites(m_satellite, &error); if (!error) { // The fix was available and we have satellite data to deliver right away. satellitesChanged(m_satellite, satelliteData); m_engine->eng_gypsy_satellite_free_satellite_array(satelliteData); return; } } // No fix is available. If updates are not ongoing already, start them. m_requestTimer.setInterval(timeout == 0? UPDATE_TIMEOUT_COLD_START: timeout); if (!m_updatesOngoing) { m_engine->eng_g_signal_connect (m_satellite, "satellites-changed", G_CALLBACK (satellites_changed), this); } m_requestTimer.start(); if (error) { #ifdef Q_LOCATION_GYPSY_DEBUG qDebug() << "QGeoSatelliteInfoSourceGypsy error asking fix status or satellite data: " << error->message; #endif g_error_free(error); } } void QGeoSatelliteInfoSourceGypsy::requestUpdateTimeout() { #ifdef Q_LOCATION_GYPSY_DEBUG qDebug("QGeoSatelliteInfoSourceGypsy request update timeout occurred."); #endif // If we end up here, there has not been valid satellite update. // Emit timeout and disconnect from signal if regular updates are not // ongoing (as we were listening just for one single requestUpdate). if (!m_updatesOngoing) { m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); } m_requestOngoing = false; emit requestTimeout(); } QT_END_NAMESPACE