summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominic Radermacher <dominic@familie-radermacher.ch>2024-02-02 17:08:13 +0100
committerDominic Radermacher <dominic@familie-radermacher.ch>2024-02-02 17:08:13 +0100
commit47961728bd952b9e596e7cc4d6ebbc4816704928 (patch)
tree019fbf29a4906802d1b7090ecc20abde7947d090
initial commitv0.21
-rw-r--r--.gitignore10
-rwxr-xr-xcompile.sh6
-rw-r--r--harbour-gpslocator.desktop11
-rw-r--r--harbour-gpslocator.pro55
-rw-r--r--icons/108x108/harbour-gpslocator.pngbin0 -> 10447 bytes
-rw-r--r--icons/128x128/harbour-gpslocator.pngbin0 -> 13147 bytes
-rw-r--r--icons/172x172/harbour-gpslocator.pngbin0 -> 20966 bytes
-rw-r--r--icons/256x256/harbour-gpslocator.pngbin0 -> 37844 bytes
-rw-r--r--icons/86x86/harbour-gpslocator.pngbin0 -> 7354 bytes
-rw-r--r--include/gpssatellite.h41
-rw-r--r--include/locator.h34
-rw-r--r--include/pos_source.h52
-rw-r--r--include/preferences.h119
-rw-r--r--include/qml_interface.h115
-rw-r--r--include/satinfo_source.h48
-rw-r--r--qml/components/AboutLabel.qml9
-rw-r--r--qml/components/DoubleSwitch.qml37
-rw-r--r--qml/components/InfoField.qml30
-rw-r--r--qml/components/MainMenu.qml24
-rw-r--r--qml/harbour-gpslocator.qml12
-rw-r--r--qml/pages/AboutPage.qml91
-rw-r--r--qml/pages/CoverPage.qml82
-rw-r--r--qml/pages/FirstPage.qml107
-rw-r--r--qml/pages/Preferences.qml228
-rw-r--r--qml/pages/SatInfoPage.qml156
-rw-r--r--rpm/harbour-gpslocator.yaml48
-rw-r--r--src/gpssatellite.cpp77
-rw-r--r--src/harbour-gpslocator.cpp44
-rw-r--r--src/locator.cpp84
-rw-r--r--src/pos_source.cpp160
-rw-r--r--src/preferences.cpp318
-rw-r--r--src/qml_interface.cpp361
-rw-r--r--src/satinfo_source.cpp215
-rw-r--r--translations/harbour-gpslocator.ts351
-rw-r--r--translations/harbour-gpslocator_de.ts291
35 files changed, 3216 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ceeef2d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+# Specify filepatterns you want git to ignore.
+*.old
+*.bak
+*.pro.*
+*.qm
+*.spec*
+*.autosave
+RPMS
+shadow
+/build/*
diff --git a/compile.sh b/compile.sh
new file mode 100755
index 0000000..ca41c89
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+sfdk tools list
+arch="aarch64"
+#arch="armv7hl"
+sfdk config --global target=SailfishOS-4.5.0.18-${arch}
+mkdir -p build/${arch}_release && cd build/${arch}_release && sfdk build ../../
diff --git a/harbour-gpslocator.desktop b/harbour-gpslocator.desktop
new file mode 100644
index 0000000..04d81cf
--- /dev/null
+++ b/harbour-gpslocator.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Type=Application
+X-Nemo-Application-Type=silica-qt5
+Name=GPSLocator
+Icon=harbour-gpslocator
+Exec=harbour-gpslocator
+
+[X-Sailjail]
+Permissions=Location
+OrganizationName=hb9hei
+ApplicationName=GPSLocator
diff --git a/harbour-gpslocator.pro b/harbour-gpslocator.pro
new file mode 100644
index 0000000..f1e9a71
--- /dev/null
+++ b/harbour-gpslocator.pro
@@ -0,0 +1,55 @@
+TARGET = harbour-gpslocator
+
+CONFIG += sailfishapp
+CONFIG += sailfishapp_i18n
+
+INCLUDEPATH += include
+
+HEADERS += \
+ include/gpssatellite.h \
+ include/locator.h \
+ include/pos_source.h \
+ include/preferences.h \
+ include/qml_interface.h \
+ include/satinfo_source.h
+
+SOURCES += \
+ src/gpssatellite.cpp \
+ src/harbour-gpslocator.cpp \
+ src/locator.cpp \
+ src/pos_source.cpp \
+ src/preferences.cpp \
+ src/qml_interface.cpp \
+ src/satinfo_source.cpp
+
+DISTFILES += \
+ harbour-gpslocator.desktop \
+ qml/components/AboutLabel.qml \
+ qml/components/DoubleSwitch.qml \
+ qml/components/InfoField.qml \
+ qml/components/MainMenu.qml \
+ qml/pages/AboutPage.qml \
+ qml/pages/CoverPage.qml \
+ qml/pages/FirstPage.qml \
+ qml/pages/LicensePage.qml \
+ qml/pages/Preferences.qml \
+ qml/pages/SatInfoPage.qml \
+ qml/harbour-gpslocator.qml \
+ rpm/harbour-gpslocator.changes \
+ rpm/harbour-gpslocator.spec \
+ rpm/harbour-gpslocator.yaml \
+ translations/harbour-gpslocator_de.ts
+
+SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172 256x256
+
+QT += positioning
+
+TRANSLATIONS += \
+ translations/harbour-gpslocator_de.ts
+
+#images.files = \
+# images/coverbg.png
+
+#images.path = /usr/share/harbour-gpslocator/images
+
+#INSTALLS += images
diff --git a/icons/108x108/harbour-gpslocator.png b/icons/108x108/harbour-gpslocator.png
new file mode 100644
index 0000000..23bbe22
--- /dev/null
+++ b/icons/108x108/harbour-gpslocator.png
Binary files differ
diff --git a/icons/128x128/harbour-gpslocator.png b/icons/128x128/harbour-gpslocator.png
new file mode 100644
index 0000000..e1457e2
--- /dev/null
+++ b/icons/128x128/harbour-gpslocator.png
Binary files differ
diff --git a/icons/172x172/harbour-gpslocator.png b/icons/172x172/harbour-gpslocator.png
new file mode 100644
index 0000000..c61cc92
--- /dev/null
+++ b/icons/172x172/harbour-gpslocator.png
Binary files differ
diff --git a/icons/256x256/harbour-gpslocator.png b/icons/256x256/harbour-gpslocator.png
new file mode 100644
index 0000000..bd3d73e
--- /dev/null
+++ b/icons/256x256/harbour-gpslocator.png
Binary files differ
diff --git a/icons/86x86/harbour-gpslocator.png b/icons/86x86/harbour-gpslocator.png
new file mode 100644
index 0000000..2677584
--- /dev/null
+++ b/icons/86x86/harbour-gpslocator.png
Binary files differ
diff --git a/include/gpssatellite.h b/include/gpssatellite.h
new file mode 100644
index 0000000..5455424
--- /dev/null
+++ b/include/gpssatellite.h
@@ -0,0 +1,41 @@
+#ifndef GPSSATELLITE_H
+#define GPSSATELLITE_H
+
+#include <QObject>
+#include <QGeoSatelliteInfo>
+
+class GPSSatellite : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal azimuth READ get_azimuth CONSTANT) // no write access from QML
+ Q_PROPERTY(qreal elevation READ get_elevation CONSTANT)
+ Q_PROPERTY(int identifier READ get_identifier CONSTANT)
+ Q_PROPERTY(int system READ get_system CONSTANT)
+ Q_PROPERTY(bool is_used READ is_used CONSTANT)
+ Q_PROPERTY(int signal_strength READ get_signal_strength CONSTANT)
+public:
+ explicit GPSSatellite(QObject* parent = nullptr);
+ explicit GPSSatellite(const QGeoSatelliteInfo& satinfo, QObject* parent = nullptr);
+
+ bool is_used() const;
+ qreal get_azimuth() const;
+ qreal get_elevation() const;
+ int get_identifier() const;
+ int get_signal_strength() const;
+ int get_system() const;
+ void set_azimuth(const qreal val);
+ void set_elevation(const qreal val);
+ void set_identifier(const int val);
+ void set_system(const int val);
+ void set_used(const bool val);
+ void set_signal_strength(const int val);
+private:
+ qreal azimuth;
+ qreal elevation;
+ int identifier;
+ int system;
+ int signal_strength;
+ bool used;
+};
+
+#endif // GPSSATELLITE_H
diff --git a/include/locator.h b/include/locator.h
new file mode 100644
index 0000000..969bf1f
--- /dev/null
+++ b/include/locator.h
@@ -0,0 +1,34 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#ifndef LOCATOR_H
+#define LOCATOR_H
+
+#include <QGeoCoordinate>
+
+class Locator
+{
+public:
+ explicit Locator();
+ static QString from_geocoord(const QGeoCoordinate& pos);
+};
+
+#endif // LOCATOR_H
diff --git a/include/pos_source.h b/include/pos_source.h
new file mode 100644
index 0000000..10b717c
--- /dev/null
+++ b/include/pos_source.h
@@ -0,0 +1,52 @@
+#ifndef POS_SOURCE_H
+#define POS_SOURCE_H
+
+#include <QObject>
+#include <QDateTime>
+#include <QGeoCoordinate>
+
+QT_FORWARD_DECLARE_CLASS(QTimer)
+QT_FORWARD_DECLARE_CLASS(QGeoPositionInfo)
+QT_FORWARD_DECLARE_CLASS(QGeoPositionInfoSource)
+
+class PosSource : public QObject
+{
+ Q_OBJECT
+ // The signal name *MUST* end with Changed (capital C) otherwise QML does not get it
+ Q_PROPERTY(QGeoCoordinate coordinate READ get_coordinate NOTIFY coordinateChanged)
+ Q_PROPERTY(double horiz_acc READ get_h_accuracy NOTIFY HorizAccuracyChanged)
+ Q_PROPERTY(double vert_acc READ get_v_accuracy NOTIFY VertAccuracyChanged)
+public:
+ explicit PosSource(QObject* parent = nullptr);
+
+ bool is_active() const;
+ QGeoCoordinate get_coordinate() const;
+ double get_h_accuracy() const;
+ double get_v_accuracy() const;
+ bool is_valid_fix() const;
+ void set_active(const bool val);
+ void set_update_interval(const int val);
+ void set_coordinate(const QGeoCoordinate& val);
+ void set_h_accuracy(const double val);
+ void set_v_accuracy(const double val);
+signals:
+ void ActiveChanged(bool);
+ void coordinateChanged(QGeoCoordinate);
+ void last_updateChanged(QDateTime);
+ void lastfixChanged(QDateTime);
+ void HorizAccuracyChanged(double);
+ void VertAccuracyChanged(double);
+private:
+ void clear_data();
+ void slot_simulator_tick();
+ void slot_position_updated(const QGeoPositionInfo& info);
+
+ QGeoPositionInfoSource* gp_src;
+ bool active;
+ QGeoCoordinate pos;
+ QTimer* simulator_timer;
+ double acc_h;
+ double acc_v;
+};
+
+#endif // POS_SOURCE_H
diff --git a/include/preferences.h b/include/preferences.h
new file mode 100644
index 0000000..e8a748e
--- /dev/null
+++ b/include/preferences.h
@@ -0,0 +1,119 @@
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#include <QObject>
+
+QT_FORWARD_DECLARE_CLASS(QSettings)
+
+class Preferences : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString coordinate_format READ get_coordinate_fmt WRITE set_coordinate_fmt NOTIFY coordinateFormatChanged)
+ Q_PROPERTY(QString locale READ get_locale WRITE set_locale NOTIFY localeChanged)
+ Q_PROPERTY(bool app_show_altitude READ get_app_show_alt WRITE set_app_show_alt NOTIFY showAltitudeAppChanged)
+ Q_PROPERTY(bool app_show_gps_state READ get_app_show_gps_state WRITE set_app_show_gps_state NOTIFY showGpsStateAppChanged)
+ Q_PROPERTY(bool app_show_lastupd READ get_app_show_lastupd WRITE set_app_show_lastupd NOTIFY showLastUpdateAppChanged)
+ Q_PROPERTY(bool app_show_latitude READ get_app_show_lat WRITE set_app_show_lat NOTIFY showLatitudeAppChanged)
+ Q_PROPERTY(bool app_show_longitude READ get_app_show_lon WRITE set_app_show_lon NOTIFY showLongitudeAppChanged)
+ Q_PROPERTY(bool app_show_qth READ get_app_show_qth WRITE set_app_show_qth NOTIFY show_qth_app_changed)
+ Q_PROPERTY(bool app_show_satinfo READ get_app_show_satinfo WRITE set_app_show_satinfo NOTIFY showSatelliteInfoAppChanged)
+ Q_PROPERTY(bool app_show_timefirstfix READ get_app_show_timefirstfix WRITE set_app_show_timefirstfix NOTIFY showTimeFirstFixAppChanged)
+ Q_PROPERTY(bool app_show_h_acc READ get_app_show_h_acc WRITE set_app_show_h_acc NOTIFY showHorizontalAccuracyAppChanged)
+ Q_PROPERTY(bool app_show_v_acc READ get_app_show_v_acc WRITE set_app_show_v_acc NOTIFY showVerticalAccuracyAppChanged)
+ Q_PROPERTY(bool cover_show_altitude READ get_cover_show_alt WRITE set_cover_show_alt NOTIFY showAltitudeCoverChanged)
+ Q_PROPERTY(bool cover_show_gps_state READ get_cover_show_gps_state WRITE set_cover_show_gps_state NOTIFY showGpsStateCoverChanged)
+ Q_PROPERTY(bool cover_show_lastupd READ get_cover_show_lastupd WRITE set_cover_show_lastupd NOTIFY showLastUpdateCoverChanged)
+ Q_PROPERTY(bool cover_show_latitude READ get_cover_show_lat WRITE set_cover_show_lat NOTIFY showLatitudeCoverChanged)
+ Q_PROPERTY(bool cover_show_longitude READ get_cover_show_lon WRITE set_cover_show_lon NOTIFY showLongitudeCoverChanged)
+ Q_PROPERTY(bool cover_show_qth READ get_cover_show_qth WRITE set_cover_show_qth NOTIFY show_qth_cover_changed)
+ Q_PROPERTY(bool cover_show_satinfo READ get_cover_show_satinfo WRITE set_cover_show_satinfo NOTIFY showSatelliteInfoCoverChanged)
+ Q_PROPERTY(bool cover_show_h_acc READ get_cover_show_h_acc WRITE set_cover_show_h_acc NOTIFY showHorizontalAccuracyCoverChanged)
+ Q_PROPERTY(bool cover_show_v_acc READ get_cover_show_v_acc WRITE set_cover_show_v_acc NOTIFY showVerticalAccuracyCoverChanged)
+ Q_PROPERTY(int update_interval READ get_update_interval WRITE set_update_interval NOTIFY updateIntervalChanged)
+ Q_PROPERTY(bool show_empty_channels READ get_show_empty_channels WRITE set_show_empty_channels NOTIFY showEmptyChannelsChanged)
+public:
+ explicit Preferences(QObject* parent = nullptr);
+ QVariant value(const QString& key) const;
+ QVariant value(const QString& key, const QVariant& default_val) const;
+ QVariant value(const QString& grp, const QString& key, const QVariant& default_val) const;
+ void set_value(const QString& key, const QVariant& val);
+ void set_value(const QString& grp, const QString& key, const QVariant& val);
+
+ QString get_coordinate_fmt() const;
+ QString get_locale() const;
+ bool get_app_show_alt() const;
+ bool get_app_show_gps_state() const;
+ bool get_app_show_lastupd() const;
+ bool get_app_show_lat() const;
+ bool get_app_show_lon() const;
+ bool get_app_show_qth() const;
+ bool get_app_show_satinfo() const;
+ bool get_app_show_timefirstfix() const;
+ bool get_app_show_h_acc() const;
+ bool get_app_show_v_acc() const;
+ bool get_cover_show_alt() const;
+ bool get_cover_show_gps_state() const;
+ bool get_cover_show_lastupd() const;
+ bool get_cover_show_lat() const;
+ bool get_cover_show_lon() const;
+ bool get_cover_show_qth() const;
+ bool get_cover_show_satinfo() const;
+ bool get_cover_show_h_acc() const;
+ bool get_cover_show_v_acc() const;
+
+ int get_update_interval() const;
+ bool get_show_empty_channels() const;
+
+ void set_coordinate_fmt(const QString val);
+ void set_locale(const QString& val);
+ void set_app_show_alt(const bool val);
+ void set_app_show_gps_state(const bool val);
+ void set_app_show_lastupd(const bool val);
+ void set_app_show_lat(const bool val);
+ void set_app_show_lon(const bool val);
+ void set_app_show_qth(const bool val);
+ void set_app_show_satinfo(const bool val);
+ void set_app_show_timefirstfix(const bool val);
+ void set_app_show_h_acc(const bool val);
+ void set_app_show_v_acc(const bool val);
+ void set_cover_show_alt(const bool val);
+ void set_cover_show_gps_state(const bool val);
+ void set_cover_show_lastupd(const bool val);
+ void set_cover_show_lat(const bool val);
+ void set_cover_show_lon(const bool val);
+ void set_cover_show_qth(const bool val);
+ void set_cover_show_satinfo(const bool val);
+ void set_cover_show_h_acc(const bool val);
+ void set_cover_show_v_acc(const bool val);
+ void set_update_interval(const int val);
+ void set_show_empty_channels(const bool val);
+signals:
+ void coordinateFormatChanged(QString);
+ void localeChanged(QString);
+ void showAltitudeAppChanged(bool);
+ void showAltitudeCoverChanged(bool);
+ void showDirectionIndicatorChanged(bool);
+ void showGpsStateAppChanged(bool);
+ void showGpsStateCoverChanged(bool);
+ void showHorizontalAccuracyAppChanged(bool);
+ void showHorizontalAccuracyCoverChanged(bool);
+ void showLastUpdateAppChanged(bool);
+ void showLastUpdateCoverChanged(bool);
+ void show_qth_app_changed(const bool);
+ void show_qth_cover_changed(const bool);
+ void showLatitudeAppChanged(bool);
+ void showLatitudeCoverChanged(bool);
+ void showLongitudeAppChanged(bool);
+ void showLongitudeCoverChanged(bool);
+ void showSatelliteInfoAppChanged(bool);
+ void showSatelliteInfoCoverChanged(bool);
+ void showTimeFirstFixAppChanged(bool);
+ void showVerticalAccuracyAppChanged(bool);
+ void showVerticalAccuracyCoverChanged(bool);
+ void updateIntervalChanged(int);
+ void showEmptyChannelsChanged(bool);
+private:
+ QSettings* settings;
+};
+
+#endif // PREFERENCES_H
diff --git a/include/qml_interface.h b/include/qml_interface.h
new file mode 100644
index 0000000..dee562e
--- /dev/null
+++ b/include/qml_interface.h
@@ -0,0 +1,115 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#ifndef QML_INTERFACE_H
+#define QML_INTERFACE_H
+
+#include <QObject>
+#include <QDateTime>
+#include <QGeoCoordinate>
+#include <QColor>
+
+QT_FORWARD_DECLARE_CLASS(Preferences)
+QT_FORWARD_DECLARE_CLASS(PosSource)
+QT_FORWARD_DECLARE_CLASS(SatInfoSource)
+
+class QmlInterface : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString app_name READ get_app_name CONSTANT)
+ Q_PROPERTY(QString app_version READ get_app_version CONSTANT)
+ Q_PROPERTY(QString locator READ get_locator NOTIFY locatorChanged)
+ Q_PROPERTY(QString latitude_app READ get_lat_app NOTIFY latitudeChanged)
+ Q_PROPERTY(QString latitude_short READ get_lat_short NOTIFY latitudeChanged)
+ Q_PROPERTY(QString longitude_app READ get_lon_app NOTIFY longitudeChanged)
+ Q_PROPERTY(QString longitude_short READ get_lon_short NOTIFY longitudeChanged)
+ Q_PROPERTY(QString altitude READ get_altitude NOTIFY altitudeChanged)
+ Q_PROPERTY(QString h_acc READ get_h_acc NOTIFY h_accChanged)
+ Q_PROPERTY(QString v_acc READ get_v_acc NOTIFY v_accChanged)
+ Q_PROPERTY(bool gps_active READ get_gps_active WRITE set_gps_active NOTIFY gps_activeChanged)
+ Q_PROPERTY(QString last_update READ get_last_update NOTIFY last_updateChanged)
+ Q_PROPERTY(QString first_fix READ get_firstfix NOTIFY first_fixChanged)
+public:
+ explicit QmlInterface(QObject* parent = nullptr);
+
+ static QString get_app_name();
+ static QString get_app_bin_name();
+ static QString get_app_version();
+
+ void get_translations();
+
+ bool get_gps_active() const;
+ void set_gps_active(const bool state);
+ QString get_locator() const;
+ QString get_lat_app();
+ QString get_lat_short();
+ QString get_lon_app();
+ QString get_lon_short();
+ QString get_altitude() const;
+ QString get_h_acc() const;
+ QString get_v_acc() const;
+ QString get_last_update() const;
+ QString get_firstfix();
+
+ Q_INVOKABLE QColor get_rssi_color(const int rssi, const int opacity);
+ Q_INVOKABLE void toggle_active();
+
+ Preferences* prefs;
+ PosSource* pos_src;
+ SatInfoSource* satinfo_src;
+signals:
+ void latitudeChanged();
+ void longitudeChanged();
+ void altitudeChanged(const QString);
+ void locatorChanged(const QString);
+ void h_accChanged(const QString);
+ void v_accChanged(const QString);
+ void gps_activeChanged(const bool);
+ void last_updateChanged();
+ void first_fixChanged();
+private:
+ QString fmt_elapsed_time(const int secs);
+ static QString fmt_dms(const qreal coord, const bool ext);
+ static QString fmt_lat(const qreal lat, const QString& fmt, const bool ext);
+ static QString fmt_lon(const qreal lon, const QString& fmt, const bool ext);
+
+ void slot_position_changed(const QGeoCoordinate& val);
+ void slot_horiz_acc_changed(const qreal val);
+ void slot_vert_acc_changed(const qreal val);
+ void slot_show_empty_channels_changed(const bool val);
+ void slot_update_interval_changed(const int val);
+ void slot_coord_fmt_changed(const QString& fmt);
+ void slot_last_update_changed(const QDateTime& val);
+ void slot_last_fix_changed(const QDateTime& val);
+
+ QGeoCoordinate m_pos;
+ QDateTime ts_gps_activation; // timestamp when GPS was activated
+ QDateTime ts_last_upd;
+ QDateTime ts_last_fix;
+ QDateTime ts_first_fix;
+ QString m_locator;
+ QString m_coord_fmt;
+ qreal m_alt;
+ qreal m_h_acc;
+ qreal m_v_acc;
+};
+
+#endif
diff --git a/include/satinfo_source.h b/include/satinfo_source.h
new file mode 100644
index 0000000..7e590af
--- /dev/null
+++ b/include/satinfo_source.h
@@ -0,0 +1,48 @@
+#ifndef SATINFO_SOURCE_H
+#define SATINFO_SOURCE_H
+
+#include <QObject>
+#include <QVariant>
+#include <QMap>
+
+#include "gpssatellite.h"
+
+QT_FORWARD_DECLARE_CLASS(QTimer)
+QT_FORWARD_DECLARE_CLASS(QGeoSatelliteInfo)
+QT_FORWARD_DECLARE_CLASS(QGeoSatelliteInfoSource)
+
+class SatInfoSource : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariantList satellites READ get_satellites NOTIFY satellitesChanged)
+ Q_PROPERTY(int sats_used READ get_sats_used NOTIFY sats_usedChanged)
+ Q_PROPERTY(int sats_visible READ get_sats_visible NOTIFY sats_visibleChanged)
+public:
+ explicit SatInfoSource(QObject* parent = nullptr);
+
+ bool is_active() const;
+ int get_sats_used() const;
+ int get_sats_visible() const;
+ QVariantList get_satellites() const;
+ void set_active(const bool val);
+ void set_sats_used(const int val);
+ void set_update_interval(const int val);
+ void set_show_empty_channels(const bool val);
+signals:
+ void activeChanged(bool);
+ void sats_usedChanged(int);
+ void sats_visibleChanged(int);
+ void satellitesChanged();
+private:
+ void slot_simulator_tick();
+ void slot_visible_sats_updated(const QList<QGeoSatelliteInfo>& infos);
+ void slot_used_sats_updated(const QList<QGeoSatelliteInfo>& infos);
+ QGeoSatelliteInfoSource* sat_src;
+ QMap<int, GPSSatellite*> m_sat_map; // fwd declr wont work here
+ QTimer* simulator_timer;
+ bool active;
+ bool show_empty_channels;
+ int sats_used;
+};
+
+#endif // SATINFO_SOURCE_H
diff --git a/qml/components/AboutLabel.qml b/qml/components/AboutLabel.qml
new file mode 100644
index 0000000..fbc256c
--- /dev/null
+++ b/qml/components/AboutLabel.qml
@@ -0,0 +1,9 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+Label {
+ anchors.horizontalCenter: parent.horizontalCenter
+ width: parent.width - 2*Theme.horizontalPageMargin
+ horizontalAlignment: Text.AlignHCenter
+ wrapMode: Text.Wrap
+}
diff --git a/qml/components/DoubleSwitch.qml b/qml/components/DoubleSwitch.qml
new file mode 100644
index 0000000..de961b0
--- /dev/null
+++ b/qml/components/DoubleSwitch.qml
@@ -0,0 +1,37 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+Item {
+ height: Theme.iconSizeMedium
+ width: parent ? parent.width : Screen.width
+ property alias text: label.text
+ property alias lSw: leftSwitch
+ property alias rSw: rightSwitch
+ Label {
+ id: label
+ anchors {
+ left: parent.left
+ leftMargin: Theme.paddingLarge
+ right: leftSwitch.left
+ verticalCenter: parent.verticalCenter
+ }
+ maximumLineCount: 1
+ truncationMode: TruncationMode.Fade
+ }
+ Switch {
+ id: leftSwitch
+ anchors {
+ verticalCenter: parent.verticalCenter
+ right: rightSwitch.left
+ rightMargin: Theme.paddingLarge
+ }
+ }
+ Switch {
+ id: rightSwitch
+ anchors {
+ verticalCenter: parent.verticalCenter
+ right: parent.right
+ rightMargin: Theme.paddingLarge
+ }
+ }
+}
diff --git a/qml/components/InfoField.qml b/qml/components/InfoField.qml
new file mode 100644
index 0000000..7cfb6ce
--- /dev/null
+++ b/qml/components/InfoField.qml
@@ -0,0 +1,30 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+Item {
+ property alias label: label.text
+ property alias value: value.text
+ property bool highlight: false //alias highlighted: value.highlighted
+ property alias color: value.color
+ property real fontpixelSize: Theme.fontSizeLarge
+ anchors {
+ left: parent.left
+ leftMargin: Theme.paddingMedium
+ right: parent.right
+ rightMargin: Theme.paddingMedium
+ }
+ height: 40
+ Label {
+ id: label
+ color: Theme.primaryColor
+ font.pixelSize: fontpixelSize
+ text: "Label"
+ }
+ Label {
+ id: value
+ anchors.right: parent.right
+ color: highlight ? Theme.secondaryHighlightColor : Theme.secondaryColor
+ font.pixelSize: fontpixelSize
+ text: "Value"
+ }
+}
diff --git a/qml/components/MainMenu.qml b/qml/components/MainMenu.qml
new file mode 100644
index 0000000..90b8551
--- /dev/null
+++ b/qml/components/MainMenu.qml
@@ -0,0 +1,24 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+
+Item {
+ PullDownMenu {
+ MenuItem {
+ text: qsTr("About")
+ onClicked: pageStack.push(Qt.resolvedUrl("../pages/AboutPage.qml"))
+ }
+ MenuItem {
+ text: qsTr("Settings")
+ onClicked: pageStack.push(Qt.resolvedUrl("../pages/Preferences.qml"))
+ }
+ MenuItem {
+ text: qmlinterface.gps_active ? qsTr("Deactivate GPS") : qsTr("Activate GPS")
+ onClicked: qmlinterface.toggle_active()
+ }
+ MenuItem {
+ enabled: pos.coordinate.isValid
+ text: qsTr("Copy location")
+ onClicked: Clipboard.text = qmlinterface.locator
+ }
+ }
+}
diff --git a/qml/harbour-gpslocator.qml b/qml/harbour-gpslocator.qml
new file mode 100644
index 0000000..e80de9b
--- /dev/null
+++ b/qml/harbour-gpslocator.qml
@@ -0,0 +1,12 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "pages"
+
+ApplicationWindow
+{
+ SatInfoPage { id: satinfo_page }
+ FirstPage { id: main_page }
+ CoverPage { id: cover_page }
+ initialPage: main_page
+ cover: cover_page
+}
diff --git a/qml/pages/AboutPage.qml b/qml/pages/AboutPage.qml
new file mode 100644
index 0000000..0b2c91a
--- /dev/null
+++ b/qml/pages/AboutPage.qml
@@ -0,0 +1,91 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "../components"
+
+Page {
+ id: aboutPage
+ allowedOrientations: Orientation.Portrait | Orientation.Landscape | Orientation.LandscapeInverted
+ SilicaFlickable {
+ id: aboutFlickable
+ anchors.fill: parent
+ contentHeight: column.height + header.height
+ PageHeader {
+ id: header
+ title: qsTr("About")
+ }
+ Column {
+ id: column
+ anchors {
+ top: header.bottom
+ horizontalCenter: parent.horizontalCenter
+ }
+ width: Math.min(Screen.width, aboutFlickable.width)
+ spacing: Theme.paddingLarge
+ Image {
+ anchors.horizontalCenter: parent.horizontalCenter
+ source: Qt.resolvedUrl("/usr/share/icons/hicolor/256x256/apps/harbour-gpslocator.png")
+ width: Theme.iconSizeExtraLarge
+ height: Theme.iconSizeExtraLarge
+ smooth: true
+ asynchronous: true
+ }
+ AboutLabel {
+ font.pixelSize: Theme.fontSizeLarge
+ color: Theme.highlightColor
+ text: qmlinterface.app_name
+ }
+ AboutLabel {
+ font.pixelSize: Theme.fontSizeExtraSmall
+ color: Theme.secondaryColor
+ text: qsTr("Version ") + qmlinterface.app_version
+ }
+ AboutLabel {
+ font.pixelSize: Theme.fontSizeMedium
+ color: Theme.primaryColor
+ text: qsTr("Show GPS position information for HAM Radio")
+ }
+ SectionHeader {
+ text: qsTr("Development")
+ }
+ AboutLabel {
+ font.pixelSize: Theme.fontSizeExtraSmall
+ color: Theme.secondaryColor
+ text: "Copyright © 2022-2024 Dominic Radermacher (HB9HEI)\n"
+ }
+ Button {
+ text: "HB9HEI Website"
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: Qt.openUrlExternally("https://hb9hei.familie-radermacher.ch/software/sailfish-gpslocator/")
+ }
+ Button {
+ text: "Git Repo"
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: Qt.openUrlExternally("https://git.familie-radermacher.ch/sailfish/harbour-gpslocator.git/")
+ }
+ SectionHeader {
+ text: qsTr("Translations")
+ }
+ AboutLabel {
+ font.pixelSize: Theme.fontSizeExtraSmall
+ color: Theme.secondaryColor
+ text: "English: Dominic Radermacher\n"+
+ "Deutsch: Dominic Radermacher\n"
+ }
+ SectionHeader {
+ text: qsTr("License")
+ }
+ AboutLabel {
+ text: qsTr("GPSLocator is licensed under the terms of the GPL-3.0")
+ }
+ Button {
+ text: qsTr("View License")
+ anchors.horizontalCenter: parent.horizontalCenter
+ onClicked: Qt.openUrlExternally("https://www.gnu.org/licenses/gpl-3.0.txt")
+ }
+ Item {
+ width: parent.width
+ height: Theme.paddingMedium
+ }
+ }
+ }
+}
diff --git a/qml/pages/CoverPage.qml b/qml/pages/CoverPage.qml
new file mode 100644
index 0000000..f65bd5e
--- /dev/null
+++ b/qml/pages/CoverPage.qml
@@ -0,0 +1,82 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "../components"
+
+CoverBackground {
+ Column {
+ id: column
+ anchors.top: parent.top
+ anchors.topMargin: Theme.paddingMedium
+ width: parent.width
+ spacing: Theme.paddingMedium
+ InfoField {
+ label: qsTr("GPS")
+ visible: prefs.cover_show_gps_state
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.gps_active ? qsTr("active") : qsTr("inactive")
+ }
+ InfoField {
+ label: qsTr("QTH")
+ visible: prefs.cover_show_qth
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.locator.substring(0,8)
+ }
+ InfoField {
+ label: qsTr("Lat")
+ visible: prefs.cover_show_latitude
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.latitude_short
+ }
+ InfoField {
+ label: qsTr("Lon")
+ visible: prefs.cover_show_longitude
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.longitude_short
+ }
+ InfoField {
+ label: qsTr("Alt")
+ visible: prefs.cover_show_altitude
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.altitude
+ }
+ InfoField {
+ label: qsTr("Updated")
+ visible: prefs.cover_show_lastupd
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.last_update
+ }
+ InfoField {
+ label: qsTr("Hor. acc.")
+ visible: prefs.cover_show_h_acc
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.h_acc
+ }
+ InfoField {
+ label: qsTr("Vert. acc.")
+ visible: prefs.cover_show_v_acc
+ fontpixelSize: Theme.fontSizeMedium
+ value: qmlinterface.v_acc
+ }
+ InfoField {
+ label: qsTr("Sat")
+ visible: prefs.cover_show_satinfo
+ fontpixelSize: Theme.fontSizeMedium
+ value: satinfo.sats_used + "/" + satinfo.sats_visible
+ }
+ }
+ CoverActionList {
+ id: coverAction
+ CoverAction {
+ iconSource: qmlinterface.gps_active ? "image://theme/icon-cover-pause" : "image://theme/icon-cover-play"
+ onTriggered: {
+ qmlinterface.toggle_active()
+ }
+ }
+ CoverAction {
+ iconSource: "image://theme/icon-cover-new"
+ onTriggered: {
+ Clipboard.text = qmlinterface.locator
+ }
+ }
+ }
+}
diff --git a/qml/pages/FirstPage.qml b/qml/pages/FirstPage.qml
new file mode 100644
index 0000000..7ddd461
--- /dev/null
+++ b/qml/pages/FirstPage.qml
@@ -0,0 +1,107 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "../components"
+
+Page {
+ id: main_page
+ allowedOrientations: Orientation.Portrait | Orientation.Landscape | Orientation.LandscapeInverted
+ property bool skyview_page_pushed: false
+ onStatusChanged: {
+ if (!skyview_page_pushed && status === PageStatus.Active) {
+ // console.log("push satinfo page")
+ pageStack.pushAttached(satinfo_page)
+ skyview_page_pushed = true
+ }
+ }
+ states: [
+ State {
+ name: 'landscape';
+ when: orientation === Orientation.Landscape || orientation === Orientation.LandscapeInverted;
+ PropertyChanges {
+ target: column;
+ anchors {
+ leftMargin: page.width * 0.125;
+ rightMargin: page.width * 0.125;
+ }
+ }
+ }
+ ]
+ SilicaFlickable {
+ anchors.fill: parent
+ MainMenu { }
+ contentHeight: pageHeader.height + column.height
+ PageHeader {
+ id: pageHeader
+ title: qmlinterface.app_name
+ }
+ Column {
+ id: column
+ spacing: Theme.paddingLarge
+ anchors {
+ top: pageHeader.bottom
+ left: parent.left
+ right: parent.right
+ leftMargin: 0
+ rightMargin: Theme.paddingSmall
+ }
+ InfoField {
+ label: qsTr("GPS")
+ visible: prefs.app_show_gps_state
+ value: qmlinterface.gps_active ? qsTr("active") : qsTr("inactive")
+ }
+ InfoField {
+ label: qsTr("Locator")
+ visible: prefs.app_show_qth
+ value: qmlinterface.locator
+ }
+ InfoField {
+ label: qsTr("Latitude")
+ visible: prefs.app_show_latitude
+ value: qmlinterface.latitude_app
+ }
+ InfoField {
+ label: qsTr("Longitude")
+ visible: prefs.app_show_longitude
+ value: qmlinterface.longitude_app
+ }
+ InfoField {
+ label: qsTr("Altitude")
+ visible: prefs.app_show_altitude
+ value: qmlinterface.altitude
+ }
+ InfoField {
+ label: qsTr("Last update")
+ visible: prefs.app_show_lastupd
+ value: qmlinterface.last_update
+ }
+ InfoField {
+ label: qsTr("Time to First Fix")
+ visible: prefs.app_show_timefirstfix
+ value: qmlinterface.first_fix
+ //highlight:
+ }
+ InfoField {
+ label: qsTr("Horizontal accuracy")
+ visible: prefs.app_show_h_acc
+ value: qmlinterface.h_acc
+ }
+ InfoField {
+ label: qsTr("Vertical accuracy")
+ visible: prefs.app_show_v_acc
+ value: qmlinterface.v_acc
+ }
+ InfoField {
+ label: qsTr("Satellites in use / view")
+ visible: prefs.app_show_satinfo
+ value: qmlinterface.gps_active ? satinfo.sats_used + " / " + satinfo.sats_visible : "-"
+ }
+ // This element is "necessary", because Sony Xperia XA2 Ultra (at least)
+ // messes up the column height calculation with only InfoFields...
+ Rectangle {
+ color: "transparent"
+ width: parent.width
+ height: 1.0
+ }
+ }
+ }
+}
diff --git a/qml/pages/Preferences.qml b/qml/pages/Preferences.qml
new file mode 100644
index 0000000..717fce0
--- /dev/null
+++ b/qml/pages/Preferences.qml
@@ -0,0 +1,228 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "../components"
+
+Page {
+ id: preferences_page
+ allowedOrientations: Orientation.Portrait | Orientation.Landscape | Orientation.LandscapeInverted
+ states: [
+ State {
+ name: 'landscape';
+ when: orientation === Orientation.Landscape || orientation === Orientation.LandscapeInverted;
+ PropertyChanges {
+ target: listView;
+ anchors {
+ leftMargin: preferences_page.width * 0.125;
+ rightMargin: preferences_page.width * 0.125;
+ }
+ }
+ }
+ ]
+ function setLanguageCombobox() {
+ switch (prefs.locale) {
+ case "de": return 1
+ default: return 0
+ }
+ }
+ SilicaListView {
+ id: listView
+ anchors.fill: parent
+ header: PageHeader {
+ title: qsTr("Preferences")
+ }
+ model: VisualItemModel {
+ ComboBox {
+ id: languageCombobox
+ label: qsTr("Language")
+ menu: ContextMenu {
+ MenuItem { text: "English"; onClicked: prefs.locale = "en"; }
+ MenuItem { text: "Deutsch"; onClicked: prefs.locale = "de"; }
+ }
+ Component.onCompleted: currentIndex = setLanguageCombobox()
+ description: qsTr("Requires app restart")
+ }
+ Label {
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.paddingMedium * 2
+ text: qsTr("Update interval")
+ }
+ Slider {
+ minimumValue: 1
+ maximumValue: 120
+ stepSize: 1
+ value: prefs.update_interval
+ valueText: value + "s"
+ width: parent.width
+ onReleased: prefs.update_interval = value
+ }
+ ComboBox {
+ label: qsTr("Coordinate format")
+ menu: ContextMenu {
+ MenuItem {
+ text: qsTr("decimal degrees")
+ onClicked: prefs.coordinate_format = "DEG"
+ }
+ MenuItem {
+ text: qsTr("sexagesimal degree")
+ onClicked: prefs.coordinate_format = "DMS"
+ }
+ }
+ Component.onCompleted: currentIndex = prefs.coordinate_format === "DEG" ? 0 : 1
+ }
+ ComboBox {
+ label: qsTr("Show empty channels")
+ menu: ContextMenu {
+ MenuItem {
+ text: qsTr("yes")
+ onClicked: {
+ prefs.show_empty_channels = true;
+ }
+ }
+ MenuItem {
+ text: qsTr("no")
+ onClicked: {
+ prefs.show_empty_channels = false;
+ }
+ }
+ }
+ Component.onCompleted: currentIndex = prefs.show_empty_channels ? 0 : 1
+ }
+ Item {
+ width: parent.width
+ height: Theme.iconSizeLarge * 1.2
+ Label {
+ id: showLabel
+ anchors {
+ left: parent.left
+ leftMargin: Theme.paddingLarge
+ verticalCenter: parent.verticalCenter
+ }
+ text: qsTr("Show") + "..."
+ }
+ Label {
+ id: appviewLabel
+ anchors.verticalCenter: parent.verticalCenter
+ x: gpsSwitches.lSw.x + gpsSwitches.lSw.width / 2 - width / 2
+ text: qsTr("Appview")
+ }
+ Label {
+ id: coverLabel
+ anchors.verticalCenter: parent.verticalCenter
+ x: gpsSwitches.rSw.x + gpsSwitches.rSw.width / 2 - width / 2
+ text: qsTr("Cover")
+ }
+ }
+ DoubleSwitch {
+ id: gpsSwitches
+ text: qsTr("GPS state")
+ lSw {
+ checked: prefs.app_show_gps_state
+ onClicked: prefs.app_show_gps_state = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_gps_state
+ onClicked: prefs.cover_show_gps_state = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("QTH Locator")
+ lSw {
+ checked: prefs.app_show_qth
+ onClicked: prefs.app_show_qth = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_qth
+ onClicked: prefs.cover_show_qth = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Latitude")
+ lSw {
+ checked: prefs.app_show_latitude
+ onClicked: prefs.app_show_latitude = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_latitude
+ onClicked: prefs.cover_show_latitude = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Longitude")
+ lSw {
+ checked: prefs.app_show_longitude
+ onClicked: prefs.app_show_longitude = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_longitude
+ onClicked: prefs.cover_show_longitude = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Altitude")
+ lSw {
+ checked: prefs.app_show_altitude
+ onClicked: prefs.app_show_altitude = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_altitude
+ onClicked: prefs.cover_show_altitude = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Last Update")
+ lSw {
+ checked: prefs.app_show_lastupd
+ onClicked: prefs.app_show_lastupd = lSw.checked
+ }
+ rSw {
+ checked: false
+ enabled: false
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Time to first fix")
+ lSw {
+ checked: prefs.app_show_timefirstfix
+ onClicked: prefs.app_show_timefirstfix = lSw.checked
+ }
+ rSw {
+ checked: false
+ enabled: false
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Vertical Accuracy")
+ lSw {
+ checked: prefs.app_show_v_acc
+ onClicked: prefs.app_show_v_acc = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_v_acc
+ onClicked: prefs.cover_show_v_acc = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Horizontal Accuracy")
+ lSw {
+ checked: prefs.app_show_h_acc
+ onClicked: prefs.app_show_h_acc = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_h_acc
+ onClicked: prefs.cover_show_h_acc = rSw.checked
+ }
+ }
+ DoubleSwitch {
+ text: qsTr("Satellite Info")
+ lSw {
+ checked: prefs.app_show_satinfo
+ onClicked: prefs.app_show_satinfo = lSw.checked
+ }
+ rSw {
+ checked: prefs.cover_show_satinfo
+ onClicked: prefs.cover_show_satinfo = rSw.checked
+ }
+ }
+ }
+ }
+}
diff --git a/qml/pages/SatInfoPage.qml b/qml/pages/SatInfoPage.qml
new file mode 100644
index 0000000..0a95c30
--- /dev/null
+++ b/qml/pages/SatInfoPage.qml
@@ -0,0 +1,156 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import QtGraphicalEffects 1.0
+import "../components"
+
+Page {
+ id: satinfo_page
+ allowedOrientations: Orientation.Portrait | Orientation.Landscape | Orientation.LandscapeInverted
+ property variant satellites: status === PageStatus.Inactive ? [] : satinfo.satellites;
+ property variant sortedSatellites: status === PageStatus.Inactive ? [] : satinfo.satellites.sort(function(a,b) {return (a.is_used ? 1:-1) - (b.is_used ? 1:-1)}) //so we can draw in use sats on top...
+ states: [
+ State {
+ name: 'landscape';
+ when: orientation === Orientation.Landscape || orientation === Orientation.LandscapeInverted;
+ AnchorChanges {
+ target: skyview;
+ anchors {
+ horizontalCenter: undefined;
+ left: satinfo_page.left;
+ }
+ }
+ PropertyChanges {
+ target: satellitesInfo;
+ width: satinfo_page.width / 2;
+ anchors.leftMargin: satinfo_page.width / 2.2;
+ }
+ }
+ ]
+ property int skyview_width: Screen.width - Theme.paddingLarge
+ property int diameter: skyview_width - 2 * Theme.paddingLarge
+ property int radius: diameter / 2
+ property int center: skyview_width / 2
+ SilicaFlickable {
+ anchors.fill: parent
+ MainMenu { }
+ PageHeader {
+ title: qsTr("Satellite Info")
+ }
+ // skyview background gradient is symmetrical,
+ // so we don't have to waste cycles rotating it.
+ RadialGradient {
+ id: skyview_backgnd
+ anchors.centerIn: skyview
+ width: diameter
+ height: diameter
+ source: Rectangle {
+ width: skyview_backgnd.width
+ height: width
+ radius: width / 2
+ }
+ horizontalOffset: 0
+ horizontalRadius: width / 2
+ verticalRadius: width / 2
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: Qt.rgba(0.0, 0.0, 0.3, 0.8) }
+ GradientStop { position: 1.0; color: Qt.rgba(0.0, 0.0, 0.7, 0.8) }
+ }
+ }
+ // The main skyview container item
+ Item {
+ id: skyview
+ width: skyview_width
+ height: skyview_width
+ anchors {
+ verticalCenter: parent.verticalCenter
+ horizontalCenter: parent.horizontalCenter;
+ left: undefined;
+ }
+ rotation: 0
+ // North, East, South, West indicators
+ Repeater {
+ model: [qsTr("N", "North"), qsTr("E", "East"), qsTr("S", "South"), qsTr("W", "West")]
+ delegate: Label {
+ x: center + Math.sin((index * 90) * Math.PI / 180) * (radius+width/2) - width / 2.0
+ y: center - Math.cos((index * 90) * Math.PI / 180) * (radius+width/2) - height / 2.0
+ color: "white"
+ font {
+ weight: Font.Bold
+ pixelSize: Theme.fontSizeExtraSmall
+ }
+ text: " " + modelData + " "
+ rotation: 0
+ Rectangle {
+ z: -1
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+ width: parent.width + parent.font.pixelSize / 8.0
+ height: parent.height + parent.font.pixelSize / 8.0
+ color: "blue"
+ radius: parent.font.pixelSize / 8.0
+ }
+ }
+ }
+ // Satellite identifiers (numbers), and their respective box rssi color and inUse border
+ // first draw them all solid , hiding the skyview chart background, and ensuring colors are correct
+ Repeater {
+ model: status === PageStatus.Inactive ? [] : sortedSatellites
+ delegate: Label {
+ x: center + Math.sin((modelData.azimuth) * Math.PI / 180) * radius * Math.cos(modelData.elevation * Math.PI / 180) - width / 2.0
+ y: center - Math.cos((modelData.azimuth) * Math.PI / 180) * radius * Math.cos(modelData.elevation * Math.PI / 180) - height / 2.0
+ font {
+ weight: Font.Bold
+ pixelSize: Theme.fontSizeExtraSmall
+ }
+ text: " " + modelData.identifier + " "
+ color: "white"
+ opacity: 1.0
+ rotation: 0
+ Rectangle {
+ z: -1
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+ width: parent.width + parent.font.pixelSize / 8.0
+ height: parent.height + parent.font.pixelSize / 8.0
+ color: qmlinterface.get_rssi_color(modelData.signal_strength, 255);
+ opacity: 1
+ radius: parent.font.pixelSize / 8.0
+ border {
+ color: modelData.is_used ? "white" : "transparent"
+ width: modelData.is_used ? Theme.iconSizeExtraSmall / 10.0 : 0.0
+ }
+ }
+ }
+ }
+ // Satellite identifiers (numbers), box rssi color and in_use border
+ // now draw transparent, so that overlaid numbers can be read.
+ Repeater {
+ model: status === PageStatus.Inactive ? [] : sortedSatellites // active sats on top.
+ delegate: Label {
+ x: center + Math.sin((modelData.azimuth) * Math.PI / 180) * radius * Math.cos(modelData.elevation * Math.PI / 180) - width / 2.0
+ y: center - Math.cos((modelData.azimuth) * Math.PI / 180) * radius * Math.cos(modelData.elevation * Math.PI / 180) - height / 2.0
+ font {
+ weight: Font.Bold
+ pixelSize: Theme.fontSizeExtraSmall
+ }
+ text: " " + modelData.identifier + " "
+ color: "white"
+ rotation: 0
+ }
+ }
+ }
+ InfoField {
+ id: satellitesInfo
+ label: qsTr("Satellites in use / view")
+ value: qmlinterface.gps_active ? satinfo.sats_used + " / " + satinfo.sats_visible : "-"
+ anchors {
+ bottom: parent.bottom
+ bottomMargin: Theme.paddingLarge * 1.1 // move up a bit for parallax clipping at glass edge
+ }
+ }
+ }
+}
diff --git a/rpm/harbour-gpslocator.yaml b/rpm/harbour-gpslocator.yaml
new file mode 100644
index 0000000..1de4a2a
--- /dev/null
+++ b/rpm/harbour-gpslocator.yaml
@@ -0,0 +1,48 @@
+Name: harbour-gpslocator
+Summary: GPSLocator
+# Checklist: AboutPage.qml
+Version: 0.21
+Release: 1
+# The contents of the Group field should be one of the groups listed here:
+# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
+Group: Qt/Qt
+URL: https://hb9hei.familie-radermacher.ch/
+License: GPL-2.0
+# This must be generated before uploading a package to a remote build service.
+# Usually this line does not need to be modified.
+Sources:
+- '%{name}-%{version}.tar.bz2'
+Description: |
+ An app to show all position information
+Configure: none
+# The qtc5 builder inserts macros to allow QtCreator to have fine
+# control over qmake/make execution
+Builder: qtc5
+
+# This section specifies build dependencies that are resolved using pkgconfig.
+# This is the preferred way of specifying build dependencies for your package.
+PkgConfigBR:
+ - sailfishapp >= 1.0.2
+ - Qt5Core
+ - Qt5Qml
+ - Qt5Quick
+
+# Build dependencies without a pkgconfig setup can be listed here
+PkgBR:
+ - qt5-qtdeclarative-import-positioning
+ - qt5-qtpositioning-devel
+
+# Runtime dependencies which are not automatically detected
+Requires:
+ - sailfishsilica-qt5 >= 0.10.9
+ - qt5-qtdeclarative-import-positioning
+
+# All installed files
+Files:
+ - '%{_bindir}/%{name}'
+ - '%{_datadir}/%{name}'
+ - '%{_datadir}/applications/%{name}.desktop'
+ - '%{_datadir}/icons/hicolor/*/apps/%{name}.png'
+
+# For more information about yaml and what's supported in Sailfish OS
+# build system, please see https://wiki.merproject.org/wiki/Spectacle
diff --git a/src/gpssatellite.cpp b/src/gpssatellite.cpp
new file mode 100644
index 0000000..7a1f444
--- /dev/null
+++ b/src/gpssatellite.cpp
@@ -0,0 +1,77 @@
+#include <QDebug>
+
+#include "gpssatellite.h"
+
+GPSSatellite::GPSSatellite(QObject *parent) : QObject(parent)
+{
+}
+
+GPSSatellite::GPSSatellite(const QGeoSatelliteInfo& si, QObject *parent) : QObject(parent)
+{
+ identifier = si.satelliteIdentifier();
+ azimuth = si.attribute(QGeoSatelliteInfo::Azimuth);
+ elevation = si.attribute(QGeoSatelliteInfo::Elevation);
+ system = si.satelliteSystem();
+ signal_strength = si.signalStrength();
+ used = false;
+}
+
+qreal GPSSatellite::get_azimuth() const
+{
+ return azimuth;
+}
+
+qreal GPSSatellite::get_elevation() const
+{
+ return elevation;
+}
+
+int GPSSatellite::get_identifier() const
+{
+ return identifier;
+}
+
+int GPSSatellite::get_signal_strength() const
+{
+ return signal_strength;
+}
+
+int GPSSatellite::get_system() const
+{
+ return system;
+}
+
+bool GPSSatellite::is_used() const
+{
+ return used;
+}
+
+void GPSSatellite::set_azimuth(const qreal val)
+{
+ azimuth = val;
+}
+
+void GPSSatellite::set_elevation(const qreal val)
+{
+ elevation = val;
+}
+
+void GPSSatellite::set_identifier(const int val)
+{
+ identifier = val;
+}
+
+void GPSSatellite::set_system(const int val)
+{
+ system = val;
+}
+
+void GPSSatellite::set_used(const bool val)
+{
+ used = val;
+}
+
+void GPSSatellite::set_signal_strength(const int val)
+{
+ signal_strength = val;
+}
diff --git a/src/harbour-gpslocator.cpp b/src/harbour-gpslocator.cpp
new file mode 100644
index 0000000..06e026c
--- /dev/null
+++ b/src/harbour-gpslocator.cpp
@@ -0,0 +1,44 @@
+#include <QtQuick>
+#include <QTranslator>
+#include <QScopedPointer>
+
+#include <sailfishapp.h>
+
+#include "satinfo_source.h"
+#include "preferences.h"
+#include "pos_source.h"
+#include "qml_interface.h"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication::setApplicationVersion(QmlInterface::get_app_version()); // somehow not detected automatically
+ // IMPORTANT: First letter of the exported name must be upper case - its a requirement of QML
+ qmlRegisterType<GPSSatellite>("hb9hei.gpslocator", 1, 0, "GPSSatelite");
+ qmlRegisterType<PosSource>("hb9hei.gpslocator", 1, 0, "PosSource");
+ qmlRegisterType<SatInfoSource>("hb9hei.gpslocator", 1, 0, "SatInfoSource");
+ qmlRegisterType<QmlInterface>("hb9hei.gpslocator", 1, 0, "QmlInterface");
+ QScopedPointer<QGuiApplication> app(SailfishApp::application(argc, argv));
+ if (QCoreApplication::organizationName().isEmpty()) {
+ qDebug() << "Warning, App organisation is unset, this will cause problems";
+ }
+ if (QCoreApplication::applicationName().isEmpty()) {
+ qDebug() << "Warning, applicationName is unset, this will cause problems";
+ }
+ QmlInterface* qml_interface = new QmlInterface();
+ qml_interface->get_translations();
+ QString curr_locale = qml_interface->prefs->get_locale();
+ QTranslator* translator = new QTranslator();
+ QString tr_file = QmlInterface::get_app_bin_name() + QString(curr_locale.compare("en") == 0 ? ".qm" : "_" + curr_locale + ".qm");
+ QString tr_dir = QStringLiteral("/usr/share/") + QmlInterface::get_app_bin_name() + QStringLiteral("/translations/");
+ qDebug() << "loading language" << curr_locale << "from" << tr_dir << tr_file;
+ translator->load(tr_file, tr_dir);
+ QGuiApplication::installTranslator(translator);
+ QScopedPointer<QQuickView> view(SailfishApp::createView());
+ view->rootContext()->setContextProperty("qmlinterface", qml_interface);
+ view->rootContext()->setContextProperty("prefs", qml_interface->prefs);
+ view->rootContext()->setContextProperty("pos", qml_interface->pos_src);
+ view->rootContext()->setContextProperty("satinfo", qml_interface->satinfo_src);
+ view->setSource(SailfishApp::pathToMainQml());
+ view->showFullScreen();
+ return app->exec();
+}
diff --git a/src/locator.cpp b/src/locator.cpp
new file mode 100644
index 0000000..889e547
--- /dev/null
+++ b/src/locator.cpp
@@ -0,0 +1,84 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#include <QtMath> // for qFabs()
+#include <QGeoCoordinate>
+
+#include "locator.h"
+
+Locator::Locator()
+{
+}
+
+QString Locator::from_geocoord(const QGeoCoordinate& pos)
+{
+ if (!pos.isValid()) {
+ return QStringLiteral("-");
+ }
+ double x = pos.longitude();
+ if (x < 0.0) {
+ x = 180.0 - qFabs(x);
+ } else {
+ x = 180.0 + qFabs(x);
+ }
+ double y = pos.latitude();
+ if (y < 0.0) {
+ y = 90.0 - qFabs(y);
+ } else {
+ y = 90.0 + qFabs(y);
+ }
+ uint32_t x_deg = static_cast<uint32_t>(x);
+ uint32_t x_frac = static_cast<uint32_t>((x - static_cast<double>(x_deg)) * 1000000UL);
+ uint32_t y_deg = static_cast<uint32_t>(y);
+ uint32_t y_frac = static_cast<uint32_t>((y - static_cast<double>(y_deg)) * 1000000UL);
+ // --------------------------------
+ QString res;
+ res += QChar('A' + (x_deg / 20));
+ x_deg %= 20;
+ res += QChar('A' + (y_deg / 10));
+ y_deg %= 10;
+ res += QChar('0' + (x_deg / 2));
+ res += QChar('0' + y_deg);
+ // now for the fraction part
+ if ((x_deg % 2) == 1) { // x might have fract leftover
+ x_frac += 1000000;
+ }
+ if ((x_frac < 83333) && (y_frac < 41666)) {
+ return res;
+ }
+ res += QChar('A' + (x_frac / 83333)); // 83333 = 2'000'000/24
+ x_frac %= 83333;
+ res += QChar('A' + (y_frac / 41666)); // 41666 = 1'000'000/24
+ y_frac %= 41666;
+ if ((x_frac < 8333) && (y_frac < 4166)) {
+ return res;
+ }
+ res += QChar('0' + (x_frac / 8333)); // 8333 = 83333/10
+ x_frac %= 8333;
+ res += QChar('0' + (y_frac / 4166)); // 4166 = 41666/10
+ y_frac %= 4166;
+ if ((x_frac < 347) && (y_frac < 173)) {
+ return res;
+ }
+ res += QChar('A' + (x_frac / 347)); // 347 = 8333/24
+ res += QChar('A' + (y_frac / 173)); // 173 = 4166/24
+ return res;
+}
diff --git a/src/pos_source.cpp b/src/pos_source.cpp
new file mode 100644
index 0000000..0776162
--- /dev/null
+++ b/src/pos_source.cpp
@@ -0,0 +1,160 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#include <QDebug>
+#include <QTimer>
+#include <QGeoPositionInfoSource>
+
+#include "pos_source.h"
+
+PosSource::PosSource(QObject *parent) : QObject(parent)
+{
+ active = false;
+ simulator_timer = new QTimer();
+ acc_h = 0.0;
+ acc_v = 0.0;
+ gp_src = QGeoPositionInfoSource::createDefaultSource(this);
+ if (gp_src) {
+ qDebug() << "created QGeoPositionInfoSource" << gp_src->sourceName();
+ QObject::connect(gp_src, &QGeoPositionInfoSource::positionUpdated, this, &PosSource::slot_position_updated);
+ } else {
+ qDebug() << "no default QGeoPositionInfoSource";
+ QObject::connect(simulator_timer, &QTimer::timeout, this, &PosSource::slot_simulator_tick);
+ simulator_timer->setInterval(1000);
+ }
+}
+
+void PosSource::slot_position_updated(const QGeoPositionInfo& info)
+{
+ if (info.isValid()) {
+ Q_EMIT lastfixChanged(info.timestamp());
+ QGeoCoordinate tpos = info.coordinate();
+ if (pos != tpos) {
+ //qDebug() << "called with old" << pos << "new" << tpos;
+ set_coordinate(tpos);
+ }
+ double tacc = info.attribute(QGeoPositionInfo::HorizontalAccuracy);
+ if (tacc != acc_h) {
+ set_h_accuracy(tacc);
+ }
+ tacc = info.attribute(QGeoPositionInfo::VerticalAccuracy);
+ if (tacc != acc_v) {
+ set_v_accuracy(tacc);
+ }
+ } else { // info is *not* valid
+ // what to do? invalidate position?
+ }
+ Q_EMIT last_updateChanged(info.timestamp());
+}
+
+QGeoCoordinate PosSource::get_coordinate() const
+{
+ return pos;
+}
+
+double PosSource::get_h_accuracy() const
+{
+ return acc_h;
+}
+
+void PosSource::set_h_accuracy(const double val)
+{
+ acc_h = val;
+ Q_EMIT HorizAccuracyChanged(val);
+}
+
+double PosSource::get_v_accuracy() const
+{
+ return acc_v;
+}
+
+void PosSource::clear_data()
+{
+ QGeoCoordinate coord;
+ set_coordinate(coord);
+ set_h_accuracy(0.0);
+ set_v_accuracy(0.0);
+}
+
+void PosSource::set_v_accuracy(const double val)
+{
+ acc_v = val;
+ Q_EMIT VertAccuracyChanged(val);
+}
+
+void PosSource::set_coordinate(const QGeoCoordinate& val)
+{
+ pos = val;
+ Q_EMIT coordinateChanged(val);
+}
+
+void PosSource::set_active(const bool val)
+{
+ // qDebug() << "PosSource::set_active called with" << val;
+ clear_data();
+ if (!active && val) {
+ if (gp_src) {
+ qDebug() << "activating source...";
+ gp_src->startUpdates();
+ } else {
+ simulator_timer->start();
+ }
+ active = true;
+ Q_EMIT ActiveChanged(true);
+ } else if (active && !val) {
+ if (gp_src) {
+ qDebug() << "deactivating source...";
+ gp_src->stopUpdates();
+ } else {
+ simulator_timer->stop();
+ }
+ active = false;
+ Q_EMIT ActiveChanged(false);
+ }
+}
+
+void PosSource::set_update_interval(const int val)
+{
+ if (gp_src) {
+ gp_src->setUpdateInterval(val);
+ }
+ simulator_timer->setInterval(val * 1000UL);
+}
+
+bool PosSource::is_active() const
+{
+ return active;
+}
+
+void PosSource::slot_simulator_tick()
+{
+ qreal dither = (rand() / static_cast<qreal>(RAND_MAX));
+ QGeoCoordinate coord;
+ coord.setLatitude(47.3 + (dither / 3600.0));
+ coord.setLongitude(7.91 + (dither / 3600.0));
+ coord.setAltitude(433.0 + (5.0 * dither));
+ QGeoPositionInfo pos;
+ pos.setCoordinate(coord);
+ pos.setTimestamp(QDateTime::currentDateTime());
+ pos.setAttribute(QGeoPositionInfo::HorizontalAccuracy, 5.0 + (2.0 * dither));
+ pos.setAttribute(QGeoPositionInfo::VerticalAccuracy, 8.0 + (4.0 * dither));
+ slot_position_updated(pos);
+}
diff --git a/src/preferences.cpp b/src/preferences.cpp
new file mode 100644
index 0000000..f59d240
--- /dev/null
+++ b/src/preferences.cpp
@@ -0,0 +1,318 @@
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QLocale>
+#include <QStandardPaths>
+#include <QSettings>
+
+#include "preferences.h"
+#include "qml_interface.h"
+
+Preferences::Preferences(QObject* parent) : QObject(parent)
+{
+ QString cf = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QChar('/') + QmlInterface::get_app_bin_name() + QStringLiteral(".conf");
+ qDebug() << "config file at" << cf;
+ settings = new QSettings(cf, QSettings::NativeFormat, this);
+ if (!settings->contains("locale")) {
+ settings->setValue("locale", QLocale().name().mid(0, 2));
+ }
+}
+
+QVariant Preferences::value(const QString& key) const
+{
+ //qDebug() << "load settings value" << key << "->" << settings->value(key);
+ return settings->value(key);
+}
+
+QVariant Preferences::value(const QString& key, const QVariant& defaultValue) const
+{
+ if (settings->contains(key)) {
+ //qDebug() << "load settings value" << key << "->" << settings->value(key);
+ return settings->value(key);
+ } else {
+ //qDebug() << "load settings value" << key << "-> returning default" << defaultValue;
+ return defaultValue;
+ }
+}
+
+QVariant Preferences::value(const QString& grp, const QString& key, const QVariant& defaultValue) const
+{
+ settings->beginGroup(grp);
+ QVariant ret;
+ if (settings->contains(key)) {
+ //qDebug() << "load settings value" << key << "->" << settings->value(key);
+ ret = settings->value(key);
+ } else {
+ //qDebug() << "load settings value" << key << "-> returning default" << defaultValue;
+ ret = defaultValue;
+ }
+ settings->endGroup();
+ return ret;
+}
+
+void Preferences::set_value(const QString& key, const QVariant& value)
+{
+ //qDebug() << "set settings value" << key << "->" << value;
+ settings->setValue(key, value);
+}
+
+void Preferences::set_value(const QString& grp, const QString& key, const QVariant& value)
+{
+ //qDebug() << "set settings value" << key << "->" << value;
+ settings->beginGroup(grp);
+ settings->setValue(key, value);
+ settings->endGroup();
+}
+
+QString Preferences::get_coordinate_fmt() const
+{
+ return value("coord_fmt", "DEG").toString();
+}
+
+QString Preferences::get_locale() const
+{
+ return value("locale", QLocale().name()).toString();
+}
+
+bool Preferences::get_app_show_alt() const
+{
+ return value("app", "show_alt", true).toBool();
+}
+
+bool Preferences::get_app_show_gps_state() const
+{
+ return value("app", "show_gps_state", true).toBool();
+}
+
+bool Preferences::get_app_show_lastupd() const
+{
+ return value("app", "show_lastupd", true).toBool();
+}
+
+bool Preferences::get_app_show_timefirstfix() const
+{
+ return value("app", "show_timefirstfix", true).toBool();
+}
+
+bool Preferences::get_app_show_lat() const
+{
+ return value("app", "show_lat", true).toBool();
+}
+
+bool Preferences::get_app_show_lon() const
+{
+ return value("app", "show_lon", true).toBool();
+}
+
+bool Preferences::get_app_show_qth() const
+{
+ return value("app", "show_qth", true).toBool();
+}
+
+bool Preferences::get_app_show_satinfo() const
+{
+ return value("app", "show_satinfo", true).toBool();
+}
+
+bool Preferences::get_app_show_h_acc() const
+{
+ return value("app", "show_h_acc", true).toBool();
+}
+
+bool Preferences::get_app_show_v_acc() const
+{
+ return value("app", "show_v_acc", true).toBool();
+}
+
+bool Preferences::get_cover_show_alt() const
+{
+ return value("cover", "show_alt", false).toBool();
+}
+
+bool Preferences::get_cover_show_gps_state() const
+{
+ return value("cover", "show_gps_state", true).toBool();
+}
+
+bool Preferences::get_cover_show_lastupd() const
+{
+ return value("cover", "show_lastupd", false).toBool();
+}
+
+bool Preferences::get_cover_show_lat() const
+{
+ return value("cover", "show_lat", true).toBool();
+}
+
+bool Preferences::get_cover_show_lon() const
+{
+ return value("cover", "show_lon", true).toBool();
+}
+
+bool Preferences::get_cover_show_qth() const
+{
+ return value("cover", "show_qth", true).toBool();
+}
+
+bool Preferences::get_cover_show_satinfo() const
+{
+ return value("cover", "show_satinfo", false).toBool();
+}
+
+bool Preferences::get_cover_show_h_acc() const
+{
+ return value("cover", "show_h_acc", false).toBool();
+}
+
+bool Preferences::get_cover_show_v_acc() const
+{
+ return value("cover", "show_v_acc", false).toBool();
+}
+
+int Preferences::get_update_interval() const
+{
+ return value("update_interval", 1).toInt();
+}
+
+bool Preferences::get_show_empty_channels() const
+{
+ return value("show_empty_channels", false).toBool();
+}
+
+void Preferences::set_coordinate_fmt(const QString val)
+{
+ set_value("coord_fmt", val);
+ emit coordinateFormatChanged(val);
+}
+
+void Preferences::set_locale(const QString& val)
+{
+ set_value("locale", val);
+ emit localeChanged(val);
+}
+
+void Preferences::set_app_show_alt(const bool val)
+{
+ set_value("app", "show_alt", val);
+ emit showAltitudeAppChanged(val);
+}
+
+void Preferences::set_app_show_gps_state(const bool val)
+{
+ set_value("app", "show_gps_state", val);
+ emit showGpsStateAppChanged(val);
+}
+
+void Preferences::set_app_show_lastupd(const bool val)
+{
+ set_value("app", "show_lastupd", val);
+ emit showLastUpdateAppChanged(val);
+}
+
+void Preferences::set_app_show_timefirstfix(const bool val)
+{
+ set_value("app", "show_timefirstfix", val);
+ emit showTimeFirstFixAppChanged(val);
+}
+
+void Preferences::set_app_show_lat(const bool val)
+{
+ set_value("app", "show_lat", val);
+ emit showLatitudeAppChanged(val);
+}
+
+void Preferences::set_app_show_lon(const bool val)
+{
+ set_value("app", "show_lon", val);
+ emit showLongitudeAppChanged(val);
+}
+
+void Preferences::set_app_show_qth(const bool val)
+{
+ set_value("app", "show_qth", val);
+ emit show_qth_app_changed(val);
+}
+
+void Preferences::set_app_show_satinfo(const bool val)
+{
+ set_value("app", "show_satinfo", val);
+ emit showSatelliteInfoAppChanged(val);
+}
+
+void Preferences::set_app_show_h_acc(const bool val)
+{
+ set_value("app", "show_h_acc", val);
+ emit showHorizontalAccuracyAppChanged(val);
+}
+
+void Preferences::set_app_show_v_acc(const bool val)
+{
+ set_value("app", "show_v_acc", val);
+ emit showVerticalAccuracyAppChanged(val);
+}
+
+void Preferences::set_cover_show_alt(const bool val)
+{
+ set_value("cover", "show_alt", val);
+ emit showAltitudeCoverChanged(val);
+}
+
+void Preferences::set_cover_show_gps_state(const bool val)
+{
+ set_value("cover", "show_gps_state", val);
+ emit showGpsStateCoverChanged(val);
+}
+
+void Preferences::set_cover_show_lastupd(const bool val)
+{
+ set_value("cover", "show_lastupd", val);
+ emit showLastUpdateCoverChanged(val);
+}
+
+void Preferences::set_cover_show_lat(const bool val)
+{
+ set_value("cover", "show_lat", val);
+ emit showLatitudeCoverChanged(val);
+}
+
+void Preferences::set_cover_show_lon(const bool val)
+{
+ set_value("cover", "show_lon", val);
+ emit showLongitudeCoverChanged(val);
+}
+
+void Preferences::set_cover_show_qth(const bool val)
+{
+ set_value("cover", "show_qth", val);
+ emit show_qth_cover_changed(val);
+}
+
+void Preferences::set_cover_show_satinfo(const bool val)
+{
+ set_value("cover", "show_satinfo", val);
+ emit showSatelliteInfoCoverChanged(val);
+}
+
+void Preferences::set_cover_show_h_acc(const bool val)
+{
+ set_value("cover", "show_h_acc", val);
+ emit showHorizontalAccuracyCoverChanged(val);
+}
+
+void Preferences::set_cover_show_v_acc(const bool val)
+{
+ set_value("cover", "show_v_acc", val);
+ emit showVerticalAccuracyCoverChanged(val);
+}
+
+void Preferences::set_update_interval(const int val)
+{
+ set_value("update_interval", val);
+ emit updateIntervalChanged(val);
+}
+
+void Preferences::set_show_empty_channels(const bool val)
+{
+ set_value("show_empty_channels", val);
+ emit showEmptyChannelsChanged(val);
+}
diff --git a/src/qml_interface.cpp b/src/qml_interface.cpp
new file mode 100644
index 0000000..0ca136d
--- /dev/null
+++ b/src/qml_interface.cpp
@@ -0,0 +1,361 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#include <QDebug>
+#include <QDir>
+#include <QCoreApplication>
+#include <QtMath> // qFabs(), qFloor()
+#include <QColor>
+#include <QTranslator>
+
+#include "locator.h"
+#include "preferences.h"
+#include "pos_source.h"
+#include "satinfo_source.h"
+#include "qml_interface.h"
+
+QmlInterface::QmlInterface(QObject* parent) : QObject(parent)
+{
+ prefs = new Preferences(this);
+ pos_src = new PosSource(this);
+ m_locator = QStringLiteral("-");
+ m_alt = 0.0;
+ m_h_acc = 0.0;
+ m_v_acc = 0.0;
+ pos_src->set_update_interval(prefs->get_update_interval());
+ ts_gps_activation = QDateTime::currentDateTimeUtc();
+ satinfo_src = new SatInfoSource(this);
+ satinfo_src->set_update_interval(prefs->get_update_interval());
+ satinfo_src->set_show_empty_channels(prefs->get_show_empty_channels());
+ QObject::connect(prefs, &Preferences::showEmptyChannelsChanged, this, &QmlInterface::slot_show_empty_channels_changed);
+ QObject::connect(prefs, &Preferences::updateIntervalChanged, this, &QmlInterface::slot_update_interval_changed);
+ QObject::connect(prefs, &Preferences::coordinateFormatChanged, this, &QmlInterface::slot_coord_fmt_changed);
+ QObject::connect(pos_src, &PosSource::coordinateChanged, this, &QmlInterface::slot_position_changed);
+ QObject::connect(pos_src, &PosSource::HorizAccuracyChanged, this, &QmlInterface::slot_horiz_acc_changed);
+ QObject::connect(pos_src, &PosSource::VertAccuracyChanged, this, &QmlInterface::slot_vert_acc_changed);
+ QObject::connect(pos_src, &PosSource::last_updateChanged, this, &QmlInterface::slot_last_update_changed);
+ QObject::connect(pos_src, &PosSource::lastfixChanged, this, &QmlInterface::slot_last_fix_changed);
+ set_gps_active(true);
+}
+
+QString QmlInterface::get_app_name()
+{
+ return QCoreApplication::applicationName();
+}
+
+QString QmlInterface::get_app_bin_name()
+{
+ return QStringLiteral("harbour-gpslocator");
+}
+
+QString QmlInterface::get_app_version()
+{
+ return QStringLiteral("0.21.0");
+}
+
+bool QmlInterface::get_gps_active() const
+{
+ return pos_src->is_active();
+}
+
+void QmlInterface::set_gps_active(const bool enable)
+{
+ if (enable) {
+ qDebug() << "Activating GPS...";
+ ts_gps_activation = QDateTime::currentDateTimeUtc();
+ satinfo_src->set_active(true);
+ pos_src->set_active(true);
+ Q_EMIT gps_activeChanged(true);
+ } else {
+ qDebug() << "Deactivating GPS...";
+ ts_gps_activation = QDateTime();
+ ts_first_fix = QDateTime();
+ satinfo_src->set_active(false);
+ pos_src->set_active(false);
+ Q_EMIT gps_activeChanged(false);
+ }
+}
+
+void QmlInterface::toggle_active()
+{
+ if (pos_src->is_active()) {
+ set_gps_active(false);
+ } else {
+ set_gps_active(true);
+ }
+}
+
+QString QmlInterface::get_locator() const
+{
+ return m_locator;
+}
+
+QString QmlInterface::get_altitude() const
+{
+ if (m_pos.isValid()) {
+ return QString::number(m_pos.altitude(), 'f', 1) + QStringLiteral(" m");
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_lat_app()
+{
+ if (m_pos.isValid()) {
+ return fmt_lat(m_pos.latitude(), prefs->get_coordinate_fmt(), true);
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_lat_short()
+{
+ if (m_pos.isValid()) {
+ return fmt_lat(m_pos.latitude(), prefs->get_coordinate_fmt(), false);
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_lon_app()
+{
+ if (m_pos.isValid()) {
+ return fmt_lon(m_pos.longitude(), prefs->get_coordinate_fmt(), true);
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_lon_short()
+{
+ if (m_pos.isValid()) {
+ return fmt_lon(m_pos.longitude(), prefs->get_coordinate_fmt(), false);
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_h_acc() const
+{
+ if (m_h_acc > 0.0) {
+ return QString::number(m_h_acc, 'f', 1) + QStringLiteral(" m");
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_v_acc() const
+{
+ if (m_v_acc > 0.0) {
+ return QString::number(m_v_acc, 'f', 1) + QStringLiteral(" m");
+ } else {
+ return QStringLiteral("-");
+ }
+}
+
+QString QmlInterface::get_last_update() const
+{
+/* if (pos.timestamp_lastfix > 0) {
+ return Qt.formatTime(pos.timestamp_lastfix, "hh:mm:ss");
+ } else {
+ return "-";
+ } */
+ if (ts_last_fix.isValid()) {
+ return ts_last_fix.toString("hh:mm:ss");
+ } else {
+ return "-";
+ }
+}
+
+QString QmlInterface::get_firstfix()
+{
+ if (!ts_gps_activation.isValid()) {
+ return QStringLiteral("-");
+ }
+ if (ts_first_fix.isValid()) {
+ int secs = ts_first_fix.toTime_t() - ts_gps_activation.toTime_t();
+ return fmt_elapsed_time(secs);
+ }
+ return QStringLiteral("-");
+}
+
+void QmlInterface::get_translations()
+{
+ QString tr_dir = QStringLiteral("/usr/share/") + get_app_bin_name() + QStringLiteral("/translations/");
+ QDir localesDir(tr_dir);
+ QStringList locales;
+ if (localesDir.exists()) {
+ locales = localesDir.entryList(QStringList() << "*.qm", QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::IgnoreCase);
+ }
+ locales.replaceInStrings(".qm", "");
+ qDebug() << locales;
+ QTranslator translator;
+ for (QList<QString>::ConstIterator itr = locales.cbegin(); itr != locales.cend(); ++itr) {
+ QString tr_file = *itr;
+ translator.load(tr_file, tr_dir);
+ // QTranslator::language since QT 5.15
+ //qDebug() << tr_file << translator.language();
+ }
+}
+
+QString QmlInterface::fmt_dms(const qreal coord, const bool ext)
+{
+ qreal x = qFabs(coord);
+ QString ret = QString::number(qFloor(x), 10);
+ ret += QStringLiteral("°");
+ x = (x - qFloor(x)) * 60.0; // minutes
+ ret += QString::number(qFloor(x), 10).rightJustified(2, '0');
+ ret += QChar('\'');
+ x = (x - qFloor(x)) * 60.0; // seconds
+ ret += QString::number(qFloor(x), 10).rightJustified(2, '0');
+ if (ext) {
+ ret += QChar('.');
+ x = (x - qFloor(x)) * 100.0;
+ ret += QString::number(qFloor(x), 10).rightJustified(2, '0');
+ }
+ ret += QChar('"');
+ return ret;
+}
+
+QString QmlInterface::fmt_lat(const qreal lat, const QString& fmt, const bool extended)
+{
+ double y = qFabs(lat);
+ QString ret = QString::number(qFloor(y), 10);
+ if (fmt.startsWith("DMS")) {
+ ret = fmt_dms(lat, extended);
+ } else { // Defaults to Degree
+ ret += QStringLiteral(".");
+ if (extended) {
+ y = (y - qFloor(y)) * 1000000.0; // for 6 digits after decimal point
+ ret += QString::number(qFloor(y), 10).rightJustified(6, '0');
+ } else {
+ y = (y - qFloor(y)) * 10000.0; // for 4 digits after decimal point
+ ret += QString::number(qFloor(y), 10).rightJustified(4, '0');
+ }
+ ret += QStringLiteral("°");
+ }
+ ret += QChar((lat < 0)?'S':'N');
+ return ret;
+}
+
+QString QmlInterface::fmt_lon(const qreal lon, const QString& fmt, const bool extended)
+{
+ double x = qFabs(lon);
+ QString ret = QString::number(qFloor(x), 10);
+ if (fmt.startsWith("DMS")) {
+ ret = fmt_dms(lon, extended);
+ } else { // Defaults to Degree
+ ret += QStringLiteral(".");
+ if (extended) {
+ x = (x - qFloor(x)) * 1000000.0; // for 6 digits after decimal point
+ ret += QString::number(qFloor(x), 10).rightJustified(6, '0');
+ } else {
+ x = (x - qFloor(x)) * 10000.0; // for 4 digits after decimal point
+ ret += QString::number(qFloor(x), 10).rightJustified(4, '0');
+ }
+ ret += QStringLiteral("°");
+ }
+ ret += QChar((lon < 0)?'W':'E');
+ return ret;
+}
+
+QString QmlInterface::fmt_elapsed_time(const int secs)
+{
+ int t = secs;
+ if (t <= 90) {
+ return QString::number(t) + " secs";
+ }
+ t /= 60;
+ return QString::number(t) + " mins";
+}
+
+QColor QmlInterface::get_rssi_color(const int rssi, const int opacity)
+{
+ if (rssi == 0) {
+ return QColor(127,127,127,opacity);
+ }
+ return QColor::fromHsl((rssi < 40 ? rssi : 40) * 3, 255, 127, opacity);
+ //return "hsla(" + (rssi < 40 ? rssi : 40) * 3 + ",100%,35%," + opacity + ")";
+}
+
+void QmlInterface::slot_show_empty_channels_changed(const bool val)
+{
+ satinfo_src->set_show_empty_channels(val);
+}
+
+void QmlInterface::slot_update_interval_changed(const int val)
+{
+ satinfo_src->set_update_interval(val);
+ pos_src->set_update_interval(val);
+}
+
+void QmlInterface::slot_coord_fmt_changed(const QString& fmt)
+{
+ qDebug() << "coord fmt" << fmt;
+ m_coord_fmt = fmt;
+ // TODO: check if really changed, if yes emit lat lon changed
+}
+
+void QmlInterface::slot_position_changed(const QGeoCoordinate& val)
+{
+ if (m_pos != val) {
+ m_pos = val;
+ Q_EMIT latitudeChanged();
+ Q_EMIT longitudeChanged();
+ }
+ if (m_alt != m_pos.altitude()) {
+ m_alt = m_pos.altitude();
+ Q_EMIT altitudeChanged(get_altitude());
+ }
+ QString tmp = Locator::from_geocoord(val);
+ if (tmp != m_locator) {
+ m_locator = tmp;
+ Q_EMIT locatorChanged(m_locator);
+ }
+}
+
+void QmlInterface::slot_horiz_acc_changed(const qreal val)
+{
+ m_h_acc = val;
+ Q_EMIT h_accChanged(get_h_acc());
+}
+
+void QmlInterface::slot_vert_acc_changed(const qreal val)
+{
+ m_v_acc = val;
+ Q_EMIT v_accChanged(get_v_acc());
+}
+
+void QmlInterface::slot_last_update_changed(const QDateTime& val)
+{
+ ts_last_upd = val;
+ Q_EMIT last_updateChanged();
+}
+
+void QmlInterface::slot_last_fix_changed(const QDateTime& val)
+{
+ ts_last_fix = val;
+ if (!ts_first_fix.isValid()) {
+ ts_first_fix = ts_last_fix;
+ Q_EMIT first_fixChanged();
+ }
+ Q_EMIT first_fixChanged();
+}
diff --git a/src/satinfo_source.cpp b/src/satinfo_source.cpp
new file mode 100644
index 0000000..091a9aa
--- /dev/null
+++ b/src/satinfo_source.cpp
@@ -0,0 +1,215 @@
+/* ---------------------------------------------------------------------
+
+ This file is part of harbour-gpslocator
+
+ Copyright (C) 2022-2024 Dominic Radermacher <dominic@familie-radermacher.ch>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 3 as
+ published by the Free Software Foundation
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ -------------------------------------------------------------------- */
+
+#include <QDebug>
+#include <QTimer>
+#include <QGeoSatelliteInfoSource>
+
+#include "satinfo_source.h"
+#include "gpssatellite.h"
+#include "preferences.h"
+
+SatInfoSource::SatInfoSource(QObject* parent) : QObject(parent)
+{
+ active = false;
+ show_empty_channels = false;
+ sats_used = 0;
+ simulator_timer = new QTimer();
+ sat_src = QGeoSatelliteInfoSource::createDefaultSource(this);
+ if (sat_src) {
+ qDebug() << "created QGeoSatelliteInfoSource" << sat_src->sourceName();
+ QObject::connect(sat_src, &QGeoSatelliteInfoSource::satellitesInViewUpdated, this, &SatInfoSource::slot_visible_sats_updated);
+ QObject::connect(sat_src, &QGeoSatelliteInfoSource::satellitesInUseUpdated, this, &SatInfoSource::slot_used_sats_updated);
+ } else {
+ qDebug() << "no default QGeoSatelliteInfoSource";
+ QObject::connect(simulator_timer, &QTimer::timeout, this, &SatInfoSource::slot_simulator_tick);
+ simulator_timer->setInterval(1000);
+ }
+}
+
+void SatInfoSource::slot_visible_sats_updated(const QList<QGeoSatelliteInfo>& infos)
+{
+ qDeleteAll(m_sat_map);
+ m_sat_map.clear();
+ for (QList<QGeoSatelliteInfo>::const_iterator itr = infos.cbegin(); itr != infos.cend(); ++itr) {
+ if (show_empty_channels || (itr->signalStrength() > 0)) {
+ GPSSatellite* sat = new GPSSatellite((*itr), this);
+ m_sat_map[itr->satelliteIdentifier()] = sat;
+ }
+ }
+ Q_EMIT satellitesChanged();
+ Q_EMIT sats_visibleChanged(m_sat_map.size());
+}
+
+void SatInfoSource::slot_used_sats_updated(const QList<QGeoSatelliteInfo>& infos)
+{
+ for (QList<QGeoSatelliteInfo>::const_iterator itr = infos.cbegin(); itr != infos.cend(); ++itr) {
+ if (m_sat_map.contains(itr->satelliteIdentifier())) {
+ m_sat_map[itr->satelliteIdentifier()]->set_used(true);
+ } else {
+ GPSSatellite* sat = new GPSSatellite((*itr), this);
+ sat->set_used(true);
+ m_sat_map[itr->satelliteIdentifier()] = sat;
+ }
+ }
+ Q_EMIT satellitesChanged();
+ set_sats_used(infos.size());
+}
+
+QVariantList SatInfoSource::get_satellites() const
+{
+ QList<GPSSatellite*> sats = m_sat_map.values();
+ QVariantList result;
+ for (QList<GPSSatellite*>::const_iterator itr = sats.cbegin(); itr != sats.cend(); ++itr) {
+ result << QVariant::fromValue(*itr);
+ }
+ return result;
+}
+
+void SatInfoSource::set_active(const bool val)
+{
+ if (!active && val) {
+ if (sat_src) {
+ qDebug() << "activating source...";
+ sat_src->startUpdates();
+ } else {
+ simulator_timer->start();
+ }
+ active = true;
+ Q_EMIT activeChanged(true);
+ } else if (active && !val) {
+ if (sat_src) {
+ qDebug() << "deactivating source...";
+ sat_src->stopUpdates();
+ qDeleteAll(m_sat_map);
+ m_sat_map.clear();
+ Q_EMIT satellitesChanged();
+ } else {
+ simulator_timer->stop();
+ }
+ active = false;
+ Q_EMIT activeChanged(false);
+ }
+}
+
+void SatInfoSource::set_update_interval(const int val)
+{
+ if (sat_src) {
+ sat_src->setUpdateInterval(val);
+ }
+ simulator_timer->setInterval(val * 1000UL);
+}
+
+void SatInfoSource::set_show_empty_channels(const bool val)
+{
+ show_empty_channels = val;
+}
+
+int SatInfoSource::get_sats_used() const
+{
+ return sats_used;
+}
+
+int SatInfoSource::get_sats_visible() const
+{
+ return m_sat_map.size();
+}
+
+bool SatInfoSource::is_active() const
+{
+ return active;
+}
+
+void SatInfoSource::set_sats_used(const int val)
+{
+ sats_used = val;
+ Q_EMIT sats_usedChanged(val);
+}
+
+void SatInfoSource::slot_simulator_tick()
+{
+ //qDebug() << "called";
+ QList<QGeoSatelliteInfo> satellites, sats_in_use;
+ QGeoSatelliteInfo sat;
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 10.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 80.0);
+ sat.setSatelliteIdentifier(11);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(0);
+ satellites.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 60.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 45.0);
+ sat.setSatelliteIdentifier(22);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(40);
+ satellites.append(sat);
+ sats_in_use.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 100.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 40.0);
+ sat.setSatelliteIdentifier(33);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GLONASS);
+ sat.setSignalStrength(35);
+ satellites.append(sat);
+ sats_in_use.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 200.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 60.0);
+ sat.setSatelliteIdentifier(44);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(30);
+ satellites.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 80.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 45.0);
+ sat.setSatelliteIdentifier(55);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(22);
+ satellites.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 120.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 40.0);
+ sat.setSatelliteIdentifier(66);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GLONASS);
+ sat.setSignalStrength(16);
+ satellites.append(sat);
+ sats_in_use.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 250.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 60.0);
+ sat.setSatelliteIdentifier(77);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(3);
+ satellites.append(sat);
+ sats_in_use.append(sat);
+
+ sat.setAttribute(QGeoSatelliteInfo::Azimuth, 280.0);
+ sat.setAttribute(QGeoSatelliteInfo::Elevation, 75.0);
+ sat.setSatelliteIdentifier(88);
+ sat.setSatelliteSystem(QGeoSatelliteInfo::GPS);
+ sat.setSignalStrength(0);
+ satellites.append(sat);
+
+ slot_visible_sats_updated(satellites);
+ slot_used_sats_updated(sats_in_use);
+}
diff --git a/translations/harbour-gpslocator.ts b/translations/harbour-gpslocator.ts
new file mode 100644
index 0000000..c61a14f
--- /dev/null
+++ b/translations/harbour-gpslocator.ts
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+ <name>AboutPage</name>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="40"/>
+ <source>Version </source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="14"/>
+ <source>About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="45"/>
+ <source>Show GPS position information for HAM Radio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="48"/>
+ <source>Development</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="66"/>
+ <source>Translations</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="75"/>
+ <source>License</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="78"/>
+ <source>GPSLocator is licensed under the terms of the GPL-3.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/AboutPage.qml" line="81"/>
+ <source>View License</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>CoverPage</name>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="13"/>
+ <source>GPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="16"/>
+ <source>active</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="16"/>
+ <source>inactive</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="19"/>
+ <source>QTH</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="43"/>
+ <source>Updated</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="61"/>
+ <source>Sat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="55"/>
+ <source>Vert. acc.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="25"/>
+ <source>Lat</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="31"/>
+ <source>Lon</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="37"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/CoverPage.qml" line="49"/>
+ <source>Hor. acc.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>FirstPage</name>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="48"/>
+ <source>GPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="50"/>
+ <source>active</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="50"/>
+ <source>inactive</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="53"/>
+ <source>Locator</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="58"/>
+ <source>Latitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="63"/>
+ <source>Longitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="68"/>
+ <source>Altitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="73"/>
+ <source>Last update</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="78"/>
+ <source>Time to First Fix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="89"/>
+ <source>Vertical accuracy</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="84"/>
+ <source>Horizontal accuracy</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/FirstPage.qml" line="94"/>
+ <source>Satellites in use / view</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainMenu</name>
+ <message>
+ <location filename="../qml/components/MainMenu.qml" line="7"/>
+ <source>About</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/components/MainMenu.qml" line="11"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/components/MainMenu.qml" line="15"/>
+ <source>Deactivate GPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/components/MainMenu.qml" line="15"/>
+ <source>Activate GPS</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/components/MainMenu.qml" line="20"/>
+ <source>Copy location</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Preferences</name>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="31"/>
+ <source>Preferences</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="36"/>
+ <source>Language</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="42"/>
+ <source>Requires app restart</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="47"/>
+ <source>Update interval</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="59"/>
+ <source>Coordinate format</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="62"/>
+ <source>decimal degrees</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="66"/>
+ <source>sexagesimal degree</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="73"/>
+ <source>Show empty channels</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="76"/>
+ <source>yes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="82"/>
+ <source>no</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="100"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="106"/>
+ <source>Appview</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="112"/>
+ <source>Cover</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="117"/>
+ <source>GPS state</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="128"/>
+ <source>QTH Locator</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="139"/>
+ <source>Latitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="150"/>
+ <source>Longitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="161"/>
+ <source>Altitude</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="172"/>
+ <source>Last Update</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="183"/>
+ <source>Time to first fix</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="194"/>
+ <source>Vertical Accuracy</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="205"/>
+ <source>Horizontal Accuracy</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/Preferences.qml" line="216"/>
+ <source>Satellite Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SatInfoPage</name>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="37"/>
+ <source>Satellite Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="72"/>
+ <source>N</source>
+ <comment>North</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="72"/>
+ <source>E</source>
+ <comment>East</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="72"/>
+ <source>S</source>
+ <comment>South</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="72"/>
+ <source>W</source>
+ <comment>West</comment>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../qml/pages/SatInfoPage.qml" line="148"/>
+ <source>Satellites in use / view</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/translations/harbour-gpslocator_de.ts b/translations/harbour-gpslocator_de.ts
new file mode 100644
index 0000000..ec1cac5
--- /dev/null
+++ b/translations/harbour-gpslocator_de.ts
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+ <name>AboutPage</name>
+ <message>
+ <source>Version </source>
+ <translation>Version </translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>Über</translation>
+ </message>
+ <message>
+ <source>Show GPS position information for HAM Radio</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>Development</source>
+ <translation>Entwicklung</translation>
+ </message>
+ <message>
+ <source>Translations</source>
+ <translation>Übersetzungen</translation>
+ </message>
+ <message>
+ <source>License</source>
+ <translation>Lizenz</translation>
+ </message>
+ <message>
+ <source>GPSLocator is licensed under the terms of the GPL-3.0</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <source>View License</source>
+ <translation>Lizenz anschauen</translation>
+ </message>
+</context>
+<context>
+ <name>CoverPage</name>
+ <message>
+ <source>GPS</source>
+ <translation>GPS</translation>
+ </message>
+ <message>
+ <source>active</source>
+ <translation>aktiv</translation>
+ </message>
+ <message>
+ <source>inactive</source>
+ <translation>inaktiv</translation>
+ </message>
+ <message>
+ <source>Vert. acc.</source>
+ <translation>V.Gen.</translation>
+ </message>
+ <message>
+ <source>Hor. acc.</source>
+ <translation>H.Gen.</translation>
+ </message>
+ <message>
+ <source>QTH</source>
+ <translation>QTH</translation>
+ </message>
+ <message>
+ <source>Sat</source>
+ <translation>Sat</translation>
+ </message>
+ <message>
+ <source>Lat</source>
+ <translation>Lat</translation>
+ </message>
+ <message>
+ <source>Lon</source>
+ <translation>Lon</translation>
+ </message>
+ <message>
+ <source>Alt</source>
+ <translatorcomment>Altitude</translatorcomment>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <source>Updated</source>
+ <translation>Aktualisiert</translation>
+ </message>
+</context>
+<context>
+ <name>FirstPage</name>
+ <message>
+ <source>GPS</source>
+ <translation>GPS</translation>
+ </message>
+ <message>
+ <source>active</source>
+ <translation>aktiv</translation>
+ </message>
+ <message>
+ <source>inactive</source>
+ <translation>inaktiv</translation>
+ </message>
+ <message>
+ <source>Locator</source>
+ <translation>Locator</translation>
+ </message>
+ <message>
+ <source>Latitude</source>
+ <translation>Breite</translation>
+ </message>
+ <message>
+ <source>Longitude</source>
+ <translation>Länge</translation>
+ </message>
+ <message>
+ <source>Altitude</source>
+ <translation>Höhe</translation>
+ </message>
+ <message>
+ <source>Last update</source>
+ <translation>Letzte Aktualisierung</translation>
+ </message>
+ <message>
+ <source>Time to First Fix</source>
+ <translation>Zeit bis zum ersten Fix</translation>
+ </message>
+ <message>
+ <source>Vertical accuracy</source>
+ <translation>Vertikale Genauigkeit</translation>
+ </message>
+ <message>
+ <source>Horizontal accuracy</source>
+ <translation>Horizontale Genauigkeit</translation>
+ </message>
+ <message>
+ <source>Satellites in use / view</source>
+ <translation>Satelliten in Ben. / Sicht</translation>
+ </message>
+</context>
+<context>
+ <name>MainMenu</name>
+ <message>
+ <source>About</source>
+ <translation>Über</translation>
+ </message>
+ <message>
+ <source>Settings</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <source>Deactivate GPS</source>
+ <translation>GPS deaktivieren</translation>
+ </message>
+ <message>
+ <source>Activate GPS</source>
+ <translation>GPS aktivieren</translation>
+ </message>
+ <message>
+ <source>Copy location</source>
+ <translation>Kopiere Standort</translation>
+ </message>
+</context>
+<context>
+ <name>Preferences</name>
+ <message>
+ <source>Language</source>
+ <translation>Sprache</translation>
+ </message>
+ <message>
+ <source>Requires app restart</source>
+ <translation>Benötigt Neustart der App</translation>
+ </message>
+ <message>
+ <source>Update interval</source>
+ <translation>Aktualisierungs Intervall</translation>
+ </message>
+ <message>
+ <source>Coordinate format</source>
+ <translation>Koordinatenformat</translation>
+ </message>
+ <message>
+ <source>Show empty channels</source>
+ <translation>Zeige leere Kanäle</translation>
+ </message>
+ <message>
+ <source>yes</source>
+ <translation>ja</translation>
+ </message>
+ <message>
+ <source>no</source>
+ <translation>nein</translation>
+ </message>
+ <message>
+ <source>Show</source>
+ <translation>Anzeigen</translation>
+ </message>
+ <message>
+ <source>Appview</source>
+ <translation>Appansicht</translation>
+ </message>
+ <message>
+ <source>Cover</source>
+ <translation>Coveransicht</translation>
+ </message>
+ <message>
+ <source>GPS state</source>
+ <translation>GPS-Status</translation>
+ </message>
+ <message>
+ <source>QTH Locator</source>
+ <translation>QTH Locator</translation>
+ </message>
+ <message>
+ <source>Latitude</source>
+ <translation>Breite</translation>
+ </message>
+ <message>
+ <source>Longitude</source>
+ <translation>Länge</translation>
+ </message>
+ <message>
+ <source>Altitude</source>
+ <translation>Höhe</translation>
+ </message>
+ <message>
+ <source>Last Update</source>
+ <translation>Letztes Update</translation>
+ </message>
+ <message>
+ <source>Vertical Accuracy</source>
+ <translation>Vertikale Genauigkeit</translation>
+ </message>
+ <message>
+ <source>Horizontal Accuracy</source>
+ <translation>Horiz. Genauigkeit</translation>
+ </message>
+ <message>
+ <source>Satellite Info</source>
+ <translation>Satelliteninformationen</translation>
+ </message>
+ <message>
+ <source>Preferences</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <source>Time to first fix</source>
+ <translation>Zeit zum ersten Fix</translation>
+ </message>
+ <message>
+ <source>decimal degrees</source>
+ <translation>Dezimalgrad</translation>
+ </message>
+ <message>
+ <source>sexagesimal degree</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SatInfoPage</name>
+ <message>
+ <source>Satellite Info</source>
+ <translation>Satelliteninformationen</translation>
+ </message>
+ <message>
+ <source>N</source>
+ <comment>North</comment>
+ <translatorcomment>Norden</translatorcomment>
+ <translation>N</translation>
+ </message>
+ <message>
+ <source>E</source>
+ <comment>East</comment>
+ <translatorcomment>Osten</translatorcomment>
+ <translation>O</translation>
+ </message>
+ <message>
+ <source>S</source>
+ <comment>South</comment>
+ <translatorcomment>Süden</translatorcomment>
+ <translation>S</translation>
+ </message>
+ <message>
+ <source>W</source>
+ <comment>West</comment>
+ <translatorcomment>Westen</translatorcomment>
+ <translation>W</translation>
+ </message>
+ <message>
+ <source>Satellites in use / view</source>
+ <translation>Satelliten in Ben. / Sicht</translation>
+ </message>
+</context>
+</TS>