diff options
author | Dominic Radermacher <dominic@familie-radermacher.ch> | 2024-02-02 17:08:13 +0100 |
---|---|---|
committer | Dominic Radermacher <dominic@familie-radermacher.ch> | 2024-02-02 17:08:13 +0100 |
commit | 47961728bd952b9e596e7cc4d6ebbc4816704928 (patch) | |
tree | 019fbf29a4906802d1b7090ecc20abde7947d090 |
initial commitv0.21
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 Binary files differnew file mode 100644 index 0000000..23bbe22 --- /dev/null +++ b/icons/108x108/harbour-gpslocator.png diff --git a/icons/128x128/harbour-gpslocator.png b/icons/128x128/harbour-gpslocator.png Binary files differnew file mode 100644 index 0000000..e1457e2 --- /dev/null +++ b/icons/128x128/harbour-gpslocator.png diff --git a/icons/172x172/harbour-gpslocator.png b/icons/172x172/harbour-gpslocator.png Binary files differnew file mode 100644 index 0000000..c61cc92 --- /dev/null +++ b/icons/172x172/harbour-gpslocator.png diff --git a/icons/256x256/harbour-gpslocator.png b/icons/256x256/harbour-gpslocator.png Binary files differnew file mode 100644 index 0000000..bd3d73e --- /dev/null +++ b/icons/256x256/harbour-gpslocator.png diff --git a/icons/86x86/harbour-gpslocator.png b/icons/86x86/harbour-gpslocator.png Binary files differnew file mode 100644 index 0000000..2677584 --- /dev/null +++ b/icons/86x86/harbour-gpslocator.png 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> |