Compare commits
88 Commits
v4.0.0-RC1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a87dd69a01 | ||
|
|
79737c82cf | ||
|
|
2d773118fd | ||
|
|
7c6fc179a1 | ||
|
|
2513f5d1e5 | ||
|
|
32a9ecddbf | ||
|
|
f2f5c1dc3d | ||
|
|
f67ee9bc92 | ||
|
|
7a696451f0 | ||
|
|
ff91da3c20 | ||
|
|
53a7a1dda2 | ||
|
|
c229f51945 | ||
|
|
2cbeec6af3 | ||
|
|
463a532c27 | ||
|
|
a3bc1b0c59 | ||
|
|
ccc6e99a7f | ||
|
|
b8e6075aa0 | ||
|
|
ddb952b894 | ||
|
|
76bcbac60a | ||
|
|
319b11e77c | ||
|
|
5f513b1c76 | ||
|
|
36648e08cc | ||
|
|
d61caa1ada | ||
|
|
6fdd36b548 | ||
|
|
6213e8b1ca | ||
|
|
d0bfbe40d7 | ||
|
|
ebda950c87 | ||
|
|
c803a72489 | ||
|
|
70758ea8bd | ||
|
|
6c0b1b7e04 | ||
|
|
c17c1fdbea | ||
|
|
8138cf8e38 | ||
|
|
0b416f0a2e | ||
|
|
1d0c459402 | ||
|
|
0f898ff6f5 | ||
|
|
c58d85c6a2 | ||
|
|
f73034c35c | ||
|
|
77c43813a0 | ||
|
|
b01bd99ecf | ||
|
|
146a5d1605 | ||
|
|
ea245c853b | ||
|
|
e57e9bf8e4 | ||
|
|
70ed158b02 | ||
|
|
1d795b688e | ||
|
|
52c4fbcce9 | ||
|
|
79891899ce | ||
|
|
dde223d2ac | ||
|
|
43ddee410a | ||
|
|
ecbf10ebb5 | ||
|
|
5d3cdb7abb | ||
|
|
5b8670814b | ||
|
|
19acf11b1a | ||
|
|
39056d1d91 | ||
|
|
436c54b733 | ||
|
|
b48a42dcc8 | ||
|
|
e00e21c46a | ||
|
|
2aa821ee65 | ||
|
|
5ab0c39aa9 | ||
|
|
f09bbd3311 | ||
|
|
91589018a3 | ||
|
|
d41c9e1ed6 | ||
|
|
0cbd9a7a0b | ||
|
|
7db68ab2ad | ||
|
|
c7c53b5a68 | ||
|
|
9d2443b0f0 | ||
|
|
2c3ffd5e66 | ||
|
|
485eccb373 | ||
|
|
faf9a5293a | ||
|
|
e58cf4d206 | ||
|
|
1b48405c14 | ||
|
|
c8fcb2c3e0 | ||
|
|
ab8403c16e | ||
|
|
7929c23ac5 | ||
|
|
707cc001cf | ||
|
|
4594237c09 | ||
|
|
10607c88ea | ||
|
|
f61527ed70 | ||
|
|
4e7ebcc2f5 | ||
|
|
16e2668fe9 | ||
|
|
cc607fdd28 | ||
|
|
f5565a6b5e | ||
|
|
04bc4cce5f | ||
|
|
c6afcc0e75 | ||
|
|
3fdbdbfae4 | ||
|
|
359e5c9076 | ||
|
|
7a89b64ca8 | ||
|
|
e2a48e7a54 | ||
|
|
2a70e42a59 |
4
.github/workflows/linux.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
artemis_version:
|
artemis_version:
|
||||||
description: 'Version'
|
description: 'Version'
|
||||||
required: true
|
required: true
|
||||||
default: '4.0.0'
|
default: '4.1.0'
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.12'
|
||||||
|
|
||||||
- name: Build and package
|
- name: Build and package
|
||||||
run: sh ./building/Linux/build_linux.sh
|
run: sh ./building/Linux/build_linux.sh
|
||||||
|
|||||||
6
.github/workflows/macOS.yml
vendored
@@ -6,12 +6,12 @@ on:
|
|||||||
artemis_version:
|
artemis_version:
|
||||||
description: 'Version'
|
description: 'Version'
|
||||||
required: true
|
required: true
|
||||||
default: '4.0.0'
|
default: '4.1.0'
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macos-x86_64:
|
build-macos-x86_64:
|
||||||
runs-on: macos-11
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.12'
|
||||||
|
|
||||||
- name: Build and package
|
- name: Build and package
|
||||||
run: sh ./building/macOS/build_macos.sh
|
run: sh ./building/macOS/build_macos.sh
|
||||||
|
|||||||
4
.github/workflows/windows.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
artemis_version:
|
artemis_version:
|
||||||
description: 'Version'
|
description: 'Version'
|
||||||
required: true
|
required: true
|
||||||
default: '4.0.0'
|
default: '4.1.0'
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -19,7 +19,7 @@ jobs:
|
|||||||
- name: Setup python
|
- name: Setup python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.12'
|
||||||
|
|
||||||
- name: Build and package
|
- name: Build and package
|
||||||
run: ./building/Windows/build_windows.ps1
|
run: ./building/Windows/build_windows.ps1
|
||||||
|
|||||||
4
.gitignore
vendored
@@ -5,3 +5,7 @@ data/
|
|||||||
.flake8
|
.flake8
|
||||||
*.qtds
|
*.qtds
|
||||||
artemis_rc.py
|
artemis_rc.py
|
||||||
|
site
|
||||||
|
Generated
|
||||||
|
app.build
|
||||||
|
app.dist
|
||||||
|
|||||||
63
CHANGELOG.md
@@ -3,7 +3,53 @@
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
> This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and the format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased] - 2024-05-28
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [4.1.0] - 2024-10-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Created a setting panel for the audio player
|
||||||
|
- Selection of the audio output device. Closes [#4](https://github.com/AresValley/Artemis/issues/4)
|
||||||
|
- Added the following (advanced) products to the space weather module. Closes [#53](https://github.com/AresValley/Artemis/issues/53):
|
||||||
|
- Sun Synoptic Analysis (SAS)
|
||||||
|
- GOES Solar Ultraviolet Imager (SUVI), all wavelenght (94 Å, 131 Å, 171 Å, 195 Å, 284 Å, 304 Å)
|
||||||
|
- GOES Thematic Map (from SUVI)
|
||||||
|
- Large Angle and Spectrometric Coronagraph (LASCO), both C2/C3 range for optical imaging of the Sun’s corona
|
||||||
|
- Helioseismic and Magnetic Imager (HMI): Magnetogram, Intensitygram, Dopplergram
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Fixed margins for downloader window
|
||||||
|
|
||||||
|
## [4.0.5] - 2024-06-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Possibility to navigate Artemis just with the keyboard [#50](https://github.com/AresValley/Artemis/issues/50)
|
||||||
|
- **Windows:** automatic updates have been implemented. When a software update is available, Artemis will download the new version and install the updates automatically
|
||||||
|
- Multiple sigID databases can be conserved. In the case of autoload, the latest local version will be loaded
|
||||||
|
- Added creation date and DB version in DB manager window
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Improved readability of labels for filter ranges for frequency, bandwidth, and ACF
|
||||||
|
- Improved Update manager and Downloader functionalities
|
||||||
|
- OS-dependent temporary folders are now used for database download and Artemis updates
|
||||||
|
- The logic for searching the last sigID database has changed now the discriminant is no longer the folder name but is reported as a signature in the database itself (-1 in the editable field; see documentation)
|
||||||
|
- Old sigID databases are not deleted anymore when a new version is downloaded. This is to avoid removing databases with user changes or additions
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Added a database load check to avoid (noncritical) exceptions when applying filters without having loaded a database.
|
||||||
|
- Fixed a potential issue involving the forcible closure of the downloader window, but the downloader instance keeps running
|
||||||
|
- With the new logic in the latest sigID database search, manually imported sigID databases are officially recognized as proper ones
|
||||||
|
|
||||||
|
## [4.0.3] - 2024-06-10
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Optimized final package size (reduced by 30% to 50%) by explicitly including necessary plugins/DLLs and excluding unnecessary ones with Nuitka [#47](https://github.com/AresValley/Artemis/issues/47)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- When the links/urls in the description field of a signal are clicked, they open the default browser [#46](https://github.com/AresValley/Artemis/issues/46)
|
||||||
|
- Fixed an error occurig on Linux where configuration file path are not properly resolved during startup with the binary version of the program (they are if running from source) [#48](https://github.com/AresValley/Artemis/issues/48)
|
||||||
|
|
||||||
|
## [4.0.1] - 2024-06-9
|
||||||
### Added
|
### Added
|
||||||
- Database format has been changed from .csv to a proper relational DB (sqlite) which is much easier handled thanks to the native library shipped with python
|
- Database format has been changed from .csv to a proper relational DB (sqlite) which is much easier handled thanks to the native library shipped with python
|
||||||
- Possibility to create an arbitrary number of new databases for storing new custom signals
|
- Possibility to create an arbitrary number of new databases for storing new custom signals
|
||||||
@@ -12,13 +58,18 @@
|
|||||||
- Databases can be exported/imported for easy sharing
|
- Databases can be exported/imported for easy sharing
|
||||||
- Possibility to store and view all type of documents related to a signal entry
|
- Possibility to store and view all type of documents related to a signal entry
|
||||||
- Filtration process is now much more efficient due to usage of SQL queries
|
- Filtration process is now much more efficient due to usage of SQL queries
|
||||||
|
- D-Region Absorption Predictions (DRAP) and Aurora OVATION model are now present in the Space Weather window
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Updated GUI libray from PyQt5 to PySide6. Artemis 4 now relies on the QtQuick framework.
|
- Updated GUI libray from PyQt5 to PySide6. Artemis 4 now relies on the QtQuick framework.
|
||||||
- Undefined value for frequency and bandwidth is now deprecated.
|
- SigID standard database is now hosted on GitHub (the server is much faster) along with the website parser
|
||||||
|
- Undefined value for frequency and bandwidth is now deprecated
|
||||||
- Drastically reduced the number of third party libraries
|
- Drastically reduced the number of third party libraries
|
||||||
- The signals filtering page has been simplified to be more immediate and user friendly
|
- The signals filtering page has been simplified to be more immediate and user friendly
|
||||||
- Space weather page now relies on Poseidon daemon (hosted on aresvalley.com)
|
- Space weather page has been greatly improved and now relies on Poseidon daemon (hosted on aresvalley.com)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Artemis can be execunted inside standard pretected folder (such as Program Files) without using elevated privileges
|
||||||
|
|
||||||
## [3.2.4] - 2022-09-30
|
## [3.2.4] - 2022-09-30
|
||||||
### Fixed
|
### Fixed
|
||||||
@@ -84,7 +135,11 @@ First release.
|
|||||||
|
|
||||||
|
|
||||||
<!-- Links definitions -->
|
<!-- Links definitions -->
|
||||||
[Unreleased]: https://github.com/AresValley/Artemis/compare/v3.2.4...HEAD
|
[Unreleased]: https://github.com/AresValley/Artemis/compare/v4.1.0...HEAD
|
||||||
|
[4.1.0]: https://github.com/AresValley/Artemis/compare/v4.0.5...v4.1.0
|
||||||
|
[4.0.5]: https://github.com/AresValley/Artemis/compare/v4.0.3...v4.0.5
|
||||||
|
[4.0.3]: https://github.com/AresValley/Artemis/compare/v4.0.1...v4.0.3
|
||||||
|
[4.0.1]: https://github.com/AresValley/Artemis/compare/v3.2.4...v4.0.1
|
||||||
[3.2.4]: https://github.com/AresValley/Artemis/compare/v3.2.1...v3.2.4
|
[3.2.4]: https://github.com/AresValley/Artemis/compare/v3.2.1...v3.2.4
|
||||||
[3.2.3]: https://github.com/AresValley/Artemis/compare/v3.2.2...v3.2.3
|
[3.2.3]: https://github.com/AresValley/Artemis/compare/v3.2.2...v3.2.3
|
||||||
[3.2.2]: https://github.com/AresValley/Artemis/compare/v3.2.1...v3.2.2
|
[3.2.2]: https://github.com/AresValley/Artemis/compare/v3.2.1...v3.2.2
|
||||||
|
|||||||
16
README.md
@@ -4,17 +4,21 @@
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
<a href=""></a>
|

|
||||||
<a href=""></a>
|

|
||||||
<a href=""></a>
|

|
||||||
<a href=""></a>
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
**Artemis** is a software designed to assist **radio frequency (RF) signal identification and storage**. It simplifies real-time spectrum analysis by leveraging one of the most extensive and community-driven databases, containing nearly **500 recognized signals**. This comprehensive software solution allows users to collect RF signals with specific parameters such as frequency, bandwidth, modulation, etc. Users can also store spectrum waterfalls, audio samples, and all types of documents for future reference. Artemis provides a robust platform to manage a wide range of RF data with precision and ease.
|
**Artemis** is a software designed to assist **radio frequency (RF) signal identification and storage**. It simplifies real-time spectrum analysis by leveraging one of the most extensive and community-driven databases, containing over **500 recognized signals**. This comprehensive software solution allows users to collect RF signals with specific parameters such as frequency, bandwidth, modulation, etc. Users can also store spectrum waterfalls, audio samples, and all types of documents for future reference. Artemis provides a robust platform to manage a wide range of RF data with precision and ease.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="docs/assets/artemis_preview.png">
|
<img src="docs/assets/artemis_preview.webp">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
<file>images/icons/browser.svg</file>
|
<file>images/icons/browser.svg</file>
|
||||||
<file>images/icons/documents.svg</file>
|
<file>images/icons/documents.svg</file>
|
||||||
<file>images/icons/abort.svg</file>
|
<file>images/icons/abort.svg</file>
|
||||||
|
<file>images/icons/settings.svg</file>
|
||||||
<file>images/spectrum_not_available.svg</file>
|
<file>images/spectrum_not_available.svg</file>
|
||||||
|
<file>images/artemis_not_available.svg</file>
|
||||||
|
|
||||||
<file>ui/Artemis.qml</file>
|
<file>ui/Artemis.qml</file>
|
||||||
<file>ui/DbManager.qml</file>
|
<file>ui/DbManager.qml</file>
|
||||||
@@ -37,6 +39,10 @@
|
|||||||
<file>ui/SignalPage.qml</file>
|
<file>ui/SignalPage.qml</file>
|
||||||
<file>ui/SpaceWeatherCurrentPage.qml</file>
|
<file>ui/SpaceWeatherCurrentPage.qml</file>
|
||||||
<file>ui/SpaceWeatherForecastPage.qml</file>
|
<file>ui/SpaceWeatherForecastPage.qml</file>
|
||||||
|
<file>ui/SpaceWeatherDRAPPage.qml</file>
|
||||||
|
<file>ui/SpaceWeatherAuroraPage.qml</file>
|
||||||
|
<file>ui/SpaceWeatherSSA.qml</file>
|
||||||
|
<file>ui/SpaceWeatherSunImagers.qml</file>
|
||||||
|
|
||||||
<file>ui/About.qml</file>
|
<file>ui/About.qml</file>
|
||||||
|
|
||||||
|
|||||||
5152
artemis/resources.py
@@ -4,12 +4,13 @@ from PySide6.QtQml import QQmlApplicationEngine
|
|||||||
from PySide6.QtCore import QObject, Slot, Signal
|
from PySide6.QtCore import QObject, Slot, Signal
|
||||||
|
|
||||||
from artemis.utils.constants import Constants, Messages
|
from artemis.utils.constants import Constants, Messages
|
||||||
from artemis.utils.sys_utils import open_directory, pack_db, unpack_db
|
from artemis.utils.sys_utils import open_directory, make_tar, unpack_tar
|
||||||
from artemis.utils.sql_utils import ArtemisDatabase, ArtemisSignal
|
from artemis.utils.sql_utils import ArtemisDatabase, ArtemisSignal
|
||||||
from artemis.utils.path_utils import check_data_dir
|
from artemis.utils.update_utils import UpdateManager
|
||||||
from artemis.utils.network_utils import NetworkManager
|
|
||||||
from artemis.utils.generic_utils import generate_filter_query
|
from artemis.utils.generic_utils import generate_filter_query
|
||||||
from artemis.utils.path_utils import normalize_dialog_path
|
from artemis.utils.path_utils import normalize_dialog_path
|
||||||
|
from artemis.utils.path_utils import DATA_DIR
|
||||||
|
from artemis.utils.config_utils import CONFIGURE_QT
|
||||||
|
|
||||||
from artemis.ui.preferences import UIPreferences
|
from artemis.ui.preferences import UIPreferences
|
||||||
from artemis.ui.dbmanager import UIdbmanager
|
from artemis.ui.dbmanager import UIdbmanager
|
||||||
@@ -24,6 +25,7 @@ import artemis.resources
|
|||||||
|
|
||||||
class UIArtemis(QObject):
|
class UIArtemis(QObject):
|
||||||
# Python > QML Signals
|
# Python > QML Signals
|
||||||
|
close_ui = Signal()
|
||||||
populate_sig_list = Signal(list)
|
populate_sig_list = Signal(list)
|
||||||
populate_sig_details = Signal(list)
|
populate_sig_details = Signal(list)
|
||||||
populate_filter_modulation = Signal(list)
|
populate_filter_modulation = Signal(list)
|
||||||
@@ -36,7 +38,7 @@ class UIArtemis(QObject):
|
|||||||
|
|
||||||
show_dialog_popup = Signal(str, str, str)
|
show_dialog_popup = Signal(str, str, str)
|
||||||
show_dialog_download_db = Signal(str, str, str)
|
show_dialog_download_db = Signal(str, str, str)
|
||||||
show_dialog_download_art = Signal(str, str, str)
|
show_dialog_update_artemis = Signal(str, str, str, bool)
|
||||||
update_info_bar = Signal(str, str)
|
update_info_bar = Signal(str, str)
|
||||||
|
|
||||||
|
|
||||||
@@ -61,15 +63,15 @@ class UIArtemis(QObject):
|
|||||||
# Creating istances for other windows
|
# Creating istances for other windows
|
||||||
self.preferences = UIPreferences(self)
|
self.preferences = UIPreferences(self)
|
||||||
self.dbmanager = UIdbmanager(self)
|
self.dbmanager = UIdbmanager(self)
|
||||||
self.downloader = UIDownloader(self)
|
|
||||||
self.spaceweather = UIspaceweather(self)
|
self.spaceweather = UIspaceweather(self)
|
||||||
self.docmanager = UIdocumentsmanager(self)
|
self.docmanager = UIdocumentsmanager(self)
|
||||||
self.sigeditor = UIsignaleditor(self)
|
self.sigeditor = UIsignaleditor(self)
|
||||||
self.cateditor = UIcategoryeditor(self)
|
self.cateditor = UIcategoryeditor(self)
|
||||||
|
self.downloader = UIDownloader(self)
|
||||||
|
|
||||||
self.network_manager = NetworkManager(self)
|
self.update_manager = UpdateManager(self)
|
||||||
|
|
||||||
check_data_dir()
|
self.autoload_db()
|
||||||
|
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
@@ -78,8 +80,9 @@ class UIArtemis(QObject):
|
|||||||
self._window.loadSignal.connect(self.load_sig)
|
self._window.loadSignal.connect(self.load_sig)
|
||||||
self._window.showPref.connect(self.show_pref_ui)
|
self._window.showPref.connect(self.show_pref_ui)
|
||||||
self._window.openSigEditor.connect(self.open_sig_editor)
|
self._window.openSigEditor.connect(self.open_sig_editor)
|
||||||
self._window.startDownloader.connect(self.start_download_db)
|
self._window.checkForUpdate.connect(self.check_for_update)
|
||||||
self._window.checkDbUpdates.connect(self.check_update_db)
|
self._window.updateDb.connect(self.update_db)
|
||||||
|
self._window.updateArtemis.connect(self.update_artemis)
|
||||||
self._window.showSpaceWeather.connect(self.show_space_weather_ui)
|
self._window.showSpaceWeather.connect(self.show_space_weather_ui)
|
||||||
self._window.openDbDirectory.connect(self.open_db_directory)
|
self._window.openDbDirectory.connect(self.open_db_directory)
|
||||||
self._window.showCatManager.connect(self.open_cat_manager)
|
self._window.showCatManager.connect(self.open_cat_manager)
|
||||||
@@ -97,12 +100,13 @@ class UIArtemis(QObject):
|
|||||||
self._window_signal.addCatTag.connect(self.add_cat_tag)
|
self._window_signal.addCatTag.connect(self.add_cat_tag)
|
||||||
|
|
||||||
# Python > QML connections
|
# Python > QML connections
|
||||||
|
self.close_ui.connect(self._window.close)
|
||||||
self.populate_sig_list.connect(self._window.populateList)
|
self.populate_sig_list.connect(self._window.populateList)
|
||||||
self.clear_list.connect(self._window.clearList)
|
self.clear_list.connect(self._window.clearList)
|
||||||
self.update_info_bar.connect(self._window.bottomInfoBar)
|
self.update_info_bar.connect(self._window.bottomInfoBar)
|
||||||
self.show_dialog_popup.connect(self._window.openGeneralDialog)
|
self.show_dialog_popup.connect(self._window.openGeneralDialog)
|
||||||
self.show_dialog_download_db.connect(self._window.openDialogDownloadDb)
|
self.show_dialog_download_db.connect(self._window.openDialogDownloadDb)
|
||||||
self.show_dialog_download_art.connect(self._window.openDialogDownloadArtemis)
|
self.show_dialog_update_artemis.connect(self._window.openDialogUpdateArtemis)
|
||||||
self.lock_menu.connect(self._window.lockMenu)
|
self.lock_menu.connect(self._window.lockMenu)
|
||||||
|
|
||||||
self.populate_sig_details.connect(self._window_signal.populateSignalParam)
|
self.populate_sig_details.connect(self._window_signal.populateSignalParam)
|
||||||
@@ -167,17 +171,18 @@ class UIArtemis(QObject):
|
|||||||
the details to generate a search query
|
the details to generate a search query
|
||||||
"""
|
"""
|
||||||
filter_status = filter_status.toVariant()
|
filter_status = filter_status.toVariant()
|
||||||
if filter_status != {}:
|
if self.loaded_db is not None:
|
||||||
filter_query = generate_filter_query(filter_status)
|
if filter_status != {}:
|
||||||
self.loaded_db.select_by_filter(filter_query)
|
filter_query = generate_filter_query(filter_status)
|
||||||
|
self.loaded_db.select_by_filter(filter_query)
|
||||||
|
|
||||||
self.clear_signal_page.emit()
|
self.clear_signal_page.emit()
|
||||||
self.populate_sig_list.emit(self.loaded_db.all_signals)
|
self.populate_sig_list.emit(self.loaded_db.all_signals)
|
||||||
|
|
||||||
total_signals = len(self.loaded_db.all_signals)
|
total_signals = len(self.loaded_db.all_signals)
|
||||||
self.bottom_info_bar("FILTERS ACTIVE: {} signals found".format(total_signals), "warning")
|
self.bottom_info_bar("FILTERS ACTIVE: {} signals found".format(total_signals), "warning")
|
||||||
else:
|
else:
|
||||||
self.load_db(self.loaded_db.db_dir_name)
|
self.load_db(self.loaded_db.db_dir_name)
|
||||||
|
|
||||||
|
|
||||||
def show_pref_ui(self):
|
def show_pref_ui(self):
|
||||||
@@ -212,18 +217,10 @@ class UIArtemis(QObject):
|
|||||||
self.docmanager.load_documentsmanager_ui()
|
self.docmanager.load_documentsmanager_ui()
|
||||||
|
|
||||||
|
|
||||||
def check_update_db(self):
|
def check_for_update(self):
|
||||||
""" User manual check for updates db updates
|
""" User manual check for updates updates
|
||||||
"""
|
"""
|
||||||
self.network_manager.show_popup = True
|
self.update_manager.check_updates(True)
|
||||||
self.network_manager.check_updates()
|
|
||||||
|
|
||||||
|
|
||||||
def start_download_db(self):
|
|
||||||
""" Show the downloader and start the download of the sigid db
|
|
||||||
"""
|
|
||||||
self.downloader.show_ui.emit()
|
|
||||||
self.downloader.on_start()
|
|
||||||
|
|
||||||
|
|
||||||
def dialog_download_db(self, message_type, title, message):
|
def dialog_download_db(self, message_type, title, message):
|
||||||
@@ -232,10 +229,24 @@ class UIArtemis(QObject):
|
|||||||
self.show_dialog_download_db.emit(message_type, title, message)
|
self.show_dialog_download_db.emit(message_type, title, message)
|
||||||
|
|
||||||
|
|
||||||
def dialog_download_artemis(self, message_type, title, message):
|
def dialog_update_artemis(self, message_type, title, message, auto=False):
|
||||||
""" Dialog popup for artemis download confirmation
|
""" Dialog popup for Artemis download confirmation
|
||||||
"""
|
"""
|
||||||
self.show_dialog_download_art.emit(message_type, title, message)
|
self.show_dialog_update_artemis.emit(message_type, title, message, auto)
|
||||||
|
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def update_db(self):
|
||||||
|
""" Start the download of the sigID DB
|
||||||
|
"""
|
||||||
|
self.update_manager.download_db()
|
||||||
|
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def update_artemis(self):
|
||||||
|
""" Start the download of Artemis
|
||||||
|
"""
|
||||||
|
self.update_manager.download_artemis()
|
||||||
|
|
||||||
|
|
||||||
def open_db_directory(self):
|
def open_db_directory(self):
|
||||||
@@ -277,7 +288,7 @@ class UIArtemis(QObject):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
dest_path = normalize_dialog_path(save_path)
|
dest_path = normalize_dialog_path(save_path)
|
||||||
pack_db(dest_path, self.loaded_db.db_dir)
|
make_tar(dest_path, self.loaded_db.db_dir)
|
||||||
self.dialog_popup(
|
self.dialog_popup(
|
||||||
Messages.DIALOG_TYPE_INFO,
|
Messages.DIALOG_TYPE_INFO,
|
||||||
Messages.GENERIC_SUCCESS,
|
Messages.GENERIC_SUCCESS,
|
||||||
@@ -300,7 +311,8 @@ class UIArtemis(QObject):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
origin_path = normalize_dialog_path(tar_path)
|
origin_path = normalize_dialog_path(tar_path)
|
||||||
unpack_db(origin_path, str(uuid.uuid4()))
|
save_path = DATA_DIR / str(uuid.uuid4())
|
||||||
|
unpack_tar(origin_path, save_path)
|
||||||
self.dialog_popup(
|
self.dialog_popup(
|
||||||
Messages.DIALOG_TYPE_INFO,
|
Messages.DIALOG_TYPE_INFO,
|
||||||
Messages.GENERIC_SUCCESS,
|
Messages.GENERIC_SUCCESS,
|
||||||
@@ -332,6 +344,16 @@ class UIArtemis(QObject):
|
|||||||
self.cateditor.load_cateditor_ui()
|
self.cateditor.load_cateditor_ui()
|
||||||
|
|
||||||
|
|
||||||
|
def autoload_db(self):
|
||||||
|
""" This will autoload the latest local sigID DB, if present
|
||||||
|
according to the user settings
|
||||||
|
"""
|
||||||
|
sig_id_db = self.dbmanager.get_latest_local_sigid_db()
|
||||||
|
autoload = CONFIGURE_QT.value("Database", "autoload", 0)
|
||||||
|
if sig_id_db is not None and int(autoload):
|
||||||
|
self.load_db(sig_id_db.db_dir_name)
|
||||||
|
|
||||||
|
|
||||||
def dialog_popup(self, message_type, title, message):
|
def dialog_popup(self, message_type, title, message):
|
||||||
""" Opens a general dialog popup
|
""" Opens a general dialog popup
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from PySide6.QtQml import QQmlApplicationEngine
|
from PySide6.QtQml import QQmlApplicationEngine
|
||||||
from PySide6.QtCore import QObject, Signal, Slot
|
from PySide6.QtCore import QObject, Signal, Slot
|
||||||
|
|
||||||
from artemis.utils.path_utils import *
|
from artemis.utils.path_utils import DATA_DIR
|
||||||
from artemis.utils.generic_utils import *
|
from artemis.utils.generic_utils import *
|
||||||
from artemis.utils.sql_utils import ArtemisDatabase
|
from artemis.utils.sql_utils import ArtemisDatabase
|
||||||
from artemis.utils.constants import Constants
|
from artemis.utils.sys_utils import delete_dir
|
||||||
|
|
||||||
|
|
||||||
class UIdbmanager(QObject):
|
class UIdbmanager(QObject):
|
||||||
@@ -46,8 +48,24 @@ class UIdbmanager(QObject):
|
|||||||
def load_local_db_list(self):
|
def load_local_db_list(self):
|
||||||
""" Scan for all the valid DBs in the data folder and show them on the list
|
""" Scan for all the valid DBs in the data folder and show them on the list
|
||||||
"""
|
"""
|
||||||
|
db_param = []
|
||||||
valid_db_list = self.scan_db_dir()
|
valid_db_list = self.scan_db_dir()
|
||||||
self.populate_db_list.emit(valid_db_list)
|
|
||||||
|
for db in valid_db_list:
|
||||||
|
db_param.append(
|
||||||
|
{
|
||||||
|
'name': db.name,
|
||||||
|
'version': db.version,
|
||||||
|
'date': parse_date(db.date),
|
||||||
|
'db_dir_name': db.db_dir_name,
|
||||||
|
'documents_n': db.stats['documents'],
|
||||||
|
'signals_n': db.stats['signals'],
|
||||||
|
'images_n': db.stats['images'],
|
||||||
|
'audio_n': db.stats['audio']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.populate_db_list.emit(db_param)
|
||||||
|
|
||||||
|
|
||||||
def load_db(self, db_dir_name):
|
def load_db(self, db_dir_name):
|
||||||
@@ -67,7 +85,7 @@ class UIdbmanager(QObject):
|
|||||||
self._parent.lock_menu.emit(True)
|
self._parent.lock_menu.emit(True)
|
||||||
self._parent.clear_list.emit()
|
self._parent.clear_list.emit()
|
||||||
self._parent.clear_signal_page.emit()
|
self._parent.clear_signal_page.emit()
|
||||||
delete_db_dir(db_dir_name)
|
delete_dir(DATA_DIR / db_dir_name)
|
||||||
self.load_local_db_list()
|
self.load_local_db_list()
|
||||||
|
|
||||||
|
|
||||||
@@ -82,24 +100,32 @@ class UIdbmanager(QObject):
|
|||||||
|
|
||||||
def scan_db_dir(self):
|
def scan_db_dir(self):
|
||||||
""" Scans the data directory for valid databases and
|
""" Scans the data directory for valid databases and
|
||||||
return a dictionary containing only the valid ones with a summary
|
return a dictionary containing only the valid ones.
|
||||||
|
Returns a list of objects (dbs)
|
||||||
"""
|
"""
|
||||||
valid_db_list = []
|
valid_db_list = []
|
||||||
db_dirs = next(os.walk(Constants.DB_DIR))[1]
|
db_dirs = next(os.walk(DATA_DIR))[1]
|
||||||
|
|
||||||
for db_dir_name in db_dirs:
|
for db_dir_name in db_dirs:
|
||||||
if valid_db(db_dir_name):
|
try:
|
||||||
database = ArtemisDatabase(db_dir_name)
|
database = ArtemisDatabase(db_dir_name)
|
||||||
database.load()
|
database.load()
|
||||||
valid_db_list.append(
|
valid_db_list.append(database)
|
||||||
{
|
except:
|
||||||
'name': database.name,
|
continue
|
||||||
'db_dir_name': database.db_dir_name,
|
|
||||||
'documents_n': database.stats['documents'],
|
|
||||||
'signals_n': database.stats['signals'],
|
|
||||||
'images_n': database.stats['images'],
|
|
||||||
'audio_n': database.stats['audio']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return valid_db_list
|
return valid_db_list
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_local_sigid_db(self):
|
||||||
|
""" Return the newest valid local sigID database.
|
||||||
|
Returns None if no valid sigID database is found.
|
||||||
|
"""
|
||||||
|
valid_dbs = self._parent.dbmanager.scan_db_dir()
|
||||||
|
sig_id_dbs = [db for db in valid_dbs if db.editable == -1]
|
||||||
|
|
||||||
|
if len(sig_id_dbs) != 0:
|
||||||
|
sig_id_latest = max(sig_id_dbs, key=lambda x: x.version)
|
||||||
|
return sig_id_latest
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
from PySide6.QtQml import QQmlApplicationEngine
|
from PySide6.QtQml import QQmlApplicationEngine
|
||||||
from PySide6.QtCore import QObject, Slot, Signal, QUrl, QSaveFile, QDir, QIODevice
|
from PySide6.QtCore import QObject, Slot, Signal, QUrl, QSaveFile, QDir, QIODevice
|
||||||
from PySide6.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
|
from PySide6.QtNetwork import QNetworkReply, QNetworkRequest, QNetworkAccessManager
|
||||||
|
|
||||||
from artemis.utils.config_utils import *
|
|
||||||
from artemis.utils.sys_utils import delete_file, match_hash, unpack_db
|
|
||||||
from artemis.utils.constants import Messages
|
from artemis.utils.constants import Messages
|
||||||
from artemis.utils.sys_utils import delete_db_dir
|
|
||||||
|
|
||||||
|
|
||||||
class UIDownloader(QObject):
|
class UIDownloader(QObject):
|
||||||
# Python > QML Signals
|
# Python > QML Signals
|
||||||
show_ui = Signal()
|
show_ui = Signal()
|
||||||
close_ui = Signal()
|
close_ui = Signal()
|
||||||
|
update_progress_bar = Signal(int, int)
|
||||||
|
set_indeterminate_bar = Signal()
|
||||||
|
update_status = Signal(str)
|
||||||
|
finished = Signal()
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -21,8 +25,13 @@ class UIDownloader(QObject):
|
|||||||
self._engine = QQmlApplicationEngine()
|
self._engine = QQmlApplicationEngine()
|
||||||
self._engine.load('qrc:/ui/Downloader.qml')
|
self._engine.load('qrc:/ui/Downloader.qml')
|
||||||
self._window = self._engine.rootObjects()[0]
|
self._window = self._engine.rootObjects()[0]
|
||||||
self._progress_bar = self._window.findChild(QObject, "progressBar")
|
|
||||||
self._label_progress = self._window.findChild(QObject, "labelProgress")
|
self.file_url = None
|
||||||
|
self.file_size = None
|
||||||
|
self.dest_file = None
|
||||||
|
self.file = None
|
||||||
|
self.manager = None
|
||||||
|
self.reply = None
|
||||||
|
|
||||||
self._connect()
|
self._connect()
|
||||||
|
|
||||||
@@ -34,49 +43,55 @@ class UIDownloader(QObject):
|
|||||||
# Python > QML connections
|
# Python > QML connections
|
||||||
self.show_ui.connect(self._window.show)
|
self.show_ui.connect(self._window.show)
|
||||||
self.close_ui.connect(self._window.close)
|
self.close_ui.connect(self._window.close)
|
||||||
|
self.update_progress_bar.connect(self._window.updateProgressBar)
|
||||||
|
self.set_indeterminate_bar.connect(self._window.setIndeterminateBar)
|
||||||
|
self.update_status.connect(self._window.updateStatus)
|
||||||
|
|
||||||
|
|
||||||
@Slot()
|
def on_start(self, url, save_path):
|
||||||
def on_start(self):
|
""" Start the download process using the specified URL
|
||||||
""" Start the download of the DB taking the needed url and size from
|
|
||||||
the attributes of the UpdatesController class
|
Args:
|
||||||
|
url (str): url from where download the file
|
||||||
|
save_path (str): path where to save the downloaded file
|
||||||
"""
|
"""
|
||||||
url_file = QUrl(self._parent.network_manager.remote_db_url)
|
self._clear_ui()
|
||||||
dest_path = QDir(Constants.DB_DIR)
|
self.show_ui.emit()
|
||||||
self.dest_file = dest_path.filePath(url_file.fileName())
|
|
||||||
|
self.file_url = QUrl(url)
|
||||||
|
self.file_size = self._get_filesize(url)
|
||||||
|
dest_path = QDir(save_path)
|
||||||
|
self.dest_file = dest_path.filePath(self.file_url.fileName())
|
||||||
self.file = QSaveFile(self.dest_file)
|
self.file = QSaveFile(self.dest_file)
|
||||||
|
|
||||||
if self.file.open(QIODevice.WriteOnly):
|
if self.file.open(QIODevice.WriteOnly):
|
||||||
# Start a GET HTTP request
|
# Start a GET HTTP request
|
||||||
self.manager = QNetworkAccessManager(self)
|
self.manager = QNetworkAccessManager(self)
|
||||||
self.reply = self.manager.get(QNetworkRequest(url_file))
|
self.reply = self.manager.get(QNetworkRequest(self.file_url))
|
||||||
self.reply.downloadProgress.connect(self.on_progress)
|
self.reply.downloadProgress.connect(self.on_progress)
|
||||||
self.reply.finished.connect(self.on_finished)
|
self.reply.finished.connect(self.on_finished)
|
||||||
self.reply.readyRead.connect(self.on_ready_read)
|
self.reply.readyRead.connect(self.on_ready_read)
|
||||||
self.reply.errorOccurred.connect(self.on_error)
|
self.reply.errorOccurred.connect(self.on_error)
|
||||||
else:
|
else:
|
||||||
self.close_ui.emit()
|
self.close_ui.emit()
|
||||||
self.show_popup_error(
|
self.show_popup_error(self.file.errorString())
|
||||||
self.file.errorString()
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_abort(self):
|
def on_abort(self):
|
||||||
""" Stop the download when user press abort button """
|
""" Stop the download when user presses the abort button
|
||||||
|
"""
|
||||||
if self.reply:
|
if self.reply:
|
||||||
self.reply.abort()
|
self.reply.abort()
|
||||||
self._progress_bar.setProperty("value", 0)
|
|
||||||
|
|
||||||
if self.file:
|
if self.file:
|
||||||
self.file.cancelWriting()
|
self.file.cancelWriting()
|
||||||
|
|
||||||
self.close_ui.emit()
|
|
||||||
|
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_ready_read(self):
|
def on_ready_read(self):
|
||||||
""" Get available bytes and store them into the file """
|
""" Write available bytes to the file
|
||||||
|
"""
|
||||||
if self.reply:
|
if self.reply:
|
||||||
if self.reply.error() == QNetworkReply.NoError:
|
if self.reply.error() == QNetworkReply.NoError:
|
||||||
self.file.write(self.reply.readAll())
|
self.file.write(self.reply.readAll())
|
||||||
@@ -84,8 +99,9 @@ class UIDownloader(QObject):
|
|||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def on_finished(self):
|
def on_finished(self):
|
||||||
""" Delete reply, close the file, check the hash for integrity,
|
""" Finalize the download process and, if no errors
|
||||||
extract the database and delete the downloaded zip
|
occurs, emits the finished signal usefull for
|
||||||
|
a callback
|
||||||
"""
|
"""
|
||||||
if self.reply:
|
if self.reply:
|
||||||
self.reply.deleteLater()
|
self.reply.deleteLater()
|
||||||
@@ -93,25 +109,21 @@ class UIDownloader(QObject):
|
|||||||
if self.file:
|
if self.file:
|
||||||
self.file.commit()
|
self.file.commit()
|
||||||
|
|
||||||
self._label_progress.setProperty("text", "Checking DB integrity (SHA-256)")
|
if self.reply.error() == QNetworkReply.NoError:
|
||||||
|
self.finished.emit()
|
||||||
|
|
||||||
if match_hash(self.dest_file, self._parent.network_manager.remote_db_hash):
|
self.close_ui.emit()
|
||||||
self._label_progress.setProperty("text", "Unpacking archive...")
|
|
||||||
delete_db_dir('SigID')
|
|
||||||
unpack_db(self.dest_file, 'SigID')
|
|
||||||
delete_file(self.dest_file)
|
|
||||||
self._parent.load_db('SigID')
|
|
||||||
self.close_ui.emit()
|
|
||||||
|
|
||||||
|
|
||||||
@Slot(int, int)
|
@Slot(int, int)
|
||||||
def on_progress(self, bytesReceived: int):
|
def on_progress(self, bytesReceived: int):
|
||||||
""" Update progress bar and label
|
""" Update progress bar and status label
|
||||||
"""
|
"""
|
||||||
total_bytes = self._parent.network_manager.remote_db_size
|
if self.file_size is not None:
|
||||||
self._label_progress.setProperty("text", "{:.1f} Mb / {:.1f} Mb".format(bytesReceived/10**6, total_bytes/10**6))
|
self.update_status.emit("{:.1f} Mb / {:.1f} Mb".format(bytesReceived/10**6, self.file_size/10**6))
|
||||||
self._progress_bar.setProperty("to", total_bytes)
|
self.update_progress_bar.emit(bytesReceived, self.file_size)
|
||||||
self._progress_bar.setProperty("value", bytesReceived)
|
else:
|
||||||
|
self.update_status.emit("{:.1f} Mb".format(bytesReceived/10**6))
|
||||||
|
|
||||||
|
|
||||||
@Slot(QNetworkReply.NetworkError)
|
@Slot(QNetworkReply.NetworkError)
|
||||||
@@ -125,6 +137,28 @@ class UIDownloader(QObject):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_filesize(self, url):
|
||||||
|
""" Get the file size by sending a HEAD request to the URL.
|
||||||
|
If the Content-Length in HTTP headers is missing, returns None
|
||||||
|
and set the progress_bar as 'indeterminate' like a 'busy indicator'
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): URL to check the file size
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(url, stream=True)
|
||||||
|
size = int(response.headers.get('content-length'))
|
||||||
|
return size
|
||||||
|
except:
|
||||||
|
self.set_indeterminate_bar.emit()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _clear_ui(self):
|
||||||
|
self.update_progress_bar.emit(0, 0)
|
||||||
|
self.update_status.emit('')
|
||||||
|
|
||||||
|
|
||||||
def show_popup_error(self, error_msg):
|
def show_popup_error(self, error_msg):
|
||||||
self._parent.dialog_popup(
|
self._parent.dialog_popup(
|
||||||
Messages.DIALOG_TYPE_ERROR,
|
Messages.DIALOG_TYPE_ERROR,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ class UIPreferences(QObject):
|
|||||||
show_ui = Signal()
|
show_ui = Signal()
|
||||||
load_material_accent = Signal(str)
|
load_material_accent = Signal(str)
|
||||||
load_material_theme = Signal(str)
|
load_material_theme = Signal(str)
|
||||||
|
load_autoload = Signal(int)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@@ -27,18 +28,21 @@ class UIPreferences(QObject):
|
|||||||
# QML > Python connections
|
# QML > Python connections
|
||||||
self._window.saveMaterialAccent.connect(self.save_material_accent)
|
self._window.saveMaterialAccent.connect(self.save_material_accent)
|
||||||
self._window.saveMaterialTheme.connect(self.save_material_theme)
|
self._window.saveMaterialTheme.connect(self.save_material_theme)
|
||||||
|
self._window.saveAutoload.connect(self.save_autoload)
|
||||||
|
|
||||||
# Python > QML connections
|
# Python > QML connections
|
||||||
self.show_ui.connect(self._window.show)
|
self.show_ui.connect(self._window.show)
|
||||||
self.load_material_accent.connect(self._window.loadMaterialAccent)
|
self.load_material_accent.connect(self._window.loadMaterialAccent)
|
||||||
self.load_material_theme.connect(self._window.loadMaterialTheme)
|
self.load_material_theme.connect(self._window.loadMaterialTheme)
|
||||||
|
self.load_autoload.connect(self._window.loadAutoload)
|
||||||
|
|
||||||
|
|
||||||
def load_preferences_ui(self):
|
def load_preferences_ui(self):
|
||||||
""" Loading all the initial preferences from the conf file to the UI
|
""" Loading all the initial preferences from the conf file to the UI
|
||||||
"""
|
"""
|
||||||
self.load_material_accent.emit(CONFIGURE_QT.get_or_default("Material", "Accent", "Green"))
|
self.load_material_accent.emit(CONFIGURE_QT.value("Material", "Accent", "Green"))
|
||||||
self.load_material_theme.emit(CONFIGURE_QT.get_or_default("Material", "Theme", "System"))
|
self.load_material_theme.emit(CONFIGURE_QT.value("Material", "Theme", "System"))
|
||||||
|
self.load_autoload.emit(int(CONFIGURE_QT.value("Database", "autoload", 0)))
|
||||||
self.show_ui.emit()
|
self.show_ui.emit()
|
||||||
|
|
||||||
|
|
||||||
@@ -54,3 +58,10 @@ class UIPreferences(QObject):
|
|||||||
""" Saving material theme setting
|
""" Saving material theme setting
|
||||||
"""
|
"""
|
||||||
CONFIGURE_QT.set("Material", "Theme", material_theme)
|
CONFIGURE_QT.set("Material", "Theme", material_theme)
|
||||||
|
|
||||||
|
|
||||||
|
@Slot(int)
|
||||||
|
def save_autoload(self, autoload):
|
||||||
|
""" Saving autoload setting
|
||||||
|
"""
|
||||||
|
CONFIGURE_QT.set("Database", "autoload", str(autoload))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from PySide6.QtCore import QObject, Signal, Slot
|
|||||||
from artemis.utils.path_utils import *
|
from artemis.utils.path_utils import *
|
||||||
from artemis.utils.generic_utils import *
|
from artemis.utils.generic_utils import *
|
||||||
from artemis.utils.sql_utils import ArtemisSignal
|
from artemis.utils.sql_utils import ArtemisSignal
|
||||||
|
from artemis.utils.sys_utils import delete_file
|
||||||
|
|
||||||
|
|
||||||
class UIsignaleditor(QObject):
|
class UIsignaleditor(QObject):
|
||||||
@@ -104,6 +105,7 @@ class UIsignaleditor(QObject):
|
|||||||
"""
|
"""
|
||||||
if param_type == 'Signal':
|
if param_type == 'Signal':
|
||||||
self._parent.loaded_sig.delete_signal()
|
self._parent.loaded_sig.delete_signal()
|
||||||
|
self._parent.lock_audio_player.emit()
|
||||||
for doc in self._parent.loaded_sig.documents:
|
for doc in self._parent.loaded_sig.documents:
|
||||||
doc_file_name = '{}.{}'.format(str(doc[0]), doc[1])
|
doc_file_name = '{}.{}'.format(str(doc[0]), doc[1])
|
||||||
doc_file_path = self._parent.loaded_db.media_dir / doc_file_name
|
doc_file_path = self._parent.loaded_db.media_dir / doc_file_name
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ class UIspaceweather(QObject):
|
|||||||
show_ui = Signal()
|
show_ui = Signal()
|
||||||
load_poseidon_report = Signal(dict)
|
load_poseidon_report = Signal(dict)
|
||||||
load_poseidon_forecast_report = Signal(dict)
|
load_poseidon_forecast_report = Signal(dict)
|
||||||
|
load_poseidon_drap_report = Signal(dict)
|
||||||
|
load_poseidon_SSA_report = Signal(dict)
|
||||||
|
load_poseidon_sun_images_report = Signal(dict)
|
||||||
|
load_aurora_report = Signal()
|
||||||
update_bottom_bar = Signal(str)
|
update_bottom_bar = Signal(str)
|
||||||
|
|
||||||
|
|
||||||
@@ -26,6 +30,10 @@ class UIspaceweather(QObject):
|
|||||||
|
|
||||||
self._window_current = self._window.findChild(QObject, "spaceWeatherCurrentObj")
|
self._window_current = self._window.findChild(QObject, "spaceWeatherCurrentObj")
|
||||||
self._window_forecast = self._window.findChild(QObject, "spaceWeatherForecastObj")
|
self._window_forecast = self._window.findChild(QObject, "spaceWeatherForecastObj")
|
||||||
|
self._window_drap = self._window.findChild(QObject, "spaceWeatherDRAPObj")
|
||||||
|
self._window_aurora = self._window.findChild(QObject, "spaceWeatherAuroraObj")
|
||||||
|
self._window_SSA = self._window.findChild(QObject, "spaceWeatherSSA")
|
||||||
|
self._window_sun_images = self._window.findChild(QObject, "spaceWeatherSunImagers")
|
||||||
|
|
||||||
self._connect()
|
self._connect()
|
||||||
|
|
||||||
@@ -38,6 +46,10 @@ class UIspaceweather(QObject):
|
|||||||
self.update_bottom_bar.connect(self._window.updateBottomBar)
|
self.update_bottom_bar.connect(self._window.updateBottomBar)
|
||||||
self.load_poseidon_report.connect(self._window_current.loadReport)
|
self.load_poseidon_report.connect(self._window_current.loadReport)
|
||||||
self.load_poseidon_forecast_report.connect(self._window_forecast.loadForecastReport)
|
self.load_poseidon_forecast_report.connect(self._window_forecast.loadForecastReport)
|
||||||
|
self.load_poseidon_drap_report.connect(self._window_drap.loadDrapReport)
|
||||||
|
self.load_poseidon_SSA_report.connect(self._window_SSA.loadDrapReport)
|
||||||
|
self.load_poseidon_sun_images_report.connect(self._window_sun_images.loadDrapReport)
|
||||||
|
self.load_aurora_report.connect(self._window_aurora.loadAuroraReport)
|
||||||
|
|
||||||
|
|
||||||
def load_spaceweather_ui(self):
|
def load_spaceweather_ui(self):
|
||||||
@@ -47,14 +59,18 @@ class UIspaceweather(QObject):
|
|||||||
|
|
||||||
|
|
||||||
def download_poseidon_report(self):
|
def download_poseidon_report(self):
|
||||||
network_manager = self._parent.network_manager
|
update_manager = self._parent.update_manager
|
||||||
network_manager.show_popup = True
|
poseidon_data = update_manager.fetch_remote_json(
|
||||||
poseidon_data = network_manager.fetch_remote_json(
|
Constants.POSEIDON_REPORT_URL,
|
||||||
Constants.POSEIDON_REPORT
|
True
|
||||||
)
|
)
|
||||||
if poseidon_data:
|
if poseidon_data:
|
||||||
self.load_poseidon_report.emit(poseidon_data)
|
self.load_poseidon_report.emit(poseidon_data)
|
||||||
self.load_poseidon_forecast_report.emit(poseidon_data)
|
self.load_poseidon_forecast_report.emit(poseidon_data)
|
||||||
|
self.load_poseidon_drap_report.emit(poseidon_data)
|
||||||
|
self.load_poseidon_sun_images_report.emit(poseidon_data)
|
||||||
|
self.load_poseidon_SSA_report.emit(poseidon_data)
|
||||||
|
self.load_aurora_report.emit()
|
||||||
|
|
||||||
self.update_bottom_bar.emit(
|
self.update_bottom_bar.emit(
|
||||||
'Loaded Poseidon report issued on {} at {} UTC'.format(
|
'Loaded Poseidon report issued on {} at {} UTC'.format(
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from artemis.utils.constants import Constants
|
|
||||||
|
from artemis.utils.path_utils import PREFERENCES_DIR, BASE_DIR
|
||||||
|
from artemis.utils.sys_utils import copy_file
|
||||||
|
|
||||||
|
|
||||||
class Config(ConfigParser):
|
class Config(ConfigParser):
|
||||||
""" Custom configuration class derived from ConfigParser.
|
""" Custom configuration class derived from ConfigParser.
|
||||||
Used to get, set, save and remove any configuration from the conf file
|
Used to get value, set, save and remove any configuration from the conf file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config_file_path, space_around_delimiters=False):
|
def __init__(self, config_file_path, space_around_delimiters=False):
|
||||||
@@ -13,11 +15,13 @@ class Config(ConfigParser):
|
|||||||
self.read(self._config_file_path)
|
self.read(self._config_file_path)
|
||||||
self._space_around_delimiters = space_around_delimiters
|
self._space_around_delimiters = space_around_delimiters
|
||||||
|
|
||||||
def get_or_default(self, section, option, default_value):
|
def value(self, section, option, default_value):
|
||||||
value = super().get(section, option)
|
value = super().get(section, option, fallback=default_value)
|
||||||
return value if value else default_value
|
return value
|
||||||
|
|
||||||
def set(self, section, option, value=None):
|
def set(self, section, option, value=None):
|
||||||
|
if not self.has_section(section):
|
||||||
|
self.add_section(section)
|
||||||
super().set(section, option, value)
|
super().set(section, option, value)
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
@@ -29,4 +33,43 @@ class Config(ConfigParser):
|
|||||||
with open(self._config_file_path, 'w') as f:
|
with open(self._config_file_path, 'w') as f:
|
||||||
self.write(f, space_around_delimiters=self._space_around_delimiters)
|
self.write(f, space_around_delimiters=self._space_around_delimiters)
|
||||||
|
|
||||||
CONFIGURE_QT = Config((Constants.PREFERENCES_DIR / 'qtquickcontrols2.conf').resolve().as_posix())
|
|
||||||
|
def merge_config_files(old_config_path, template_config_path):
|
||||||
|
""" Merge two configuration files: if the old one lacks some
|
||||||
|
sections or options from a comparison with a template,
|
||||||
|
this function will add what is missing to the old conf file
|
||||||
|
"""
|
||||||
|
old_config = ConfigParser()
|
||||||
|
old_config.read(old_config_path)
|
||||||
|
|
||||||
|
new_config = ConfigParser()
|
||||||
|
new_config.read(template_config_path)
|
||||||
|
|
||||||
|
for section in new_config.sections():
|
||||||
|
if not old_config.has_section(section):
|
||||||
|
old_config.add_section(section)
|
||||||
|
for option in new_config.options(section):
|
||||||
|
if not old_config.has_option(section, option):
|
||||||
|
old_config.set(section, option, new_config.get(section, option))
|
||||||
|
|
||||||
|
with open(old_config_path, 'w') as f:
|
||||||
|
old_config.write(f)
|
||||||
|
|
||||||
|
|
||||||
|
def check_conf_file():
|
||||||
|
""" Check the integrity of the used conf file.
|
||||||
|
If it is not present it will add a copy to the PREF_DIR
|
||||||
|
and if it is different in structure (different section/options)
|
||||||
|
it will merge the conf file with the new template one
|
||||||
|
"""
|
||||||
|
active_conf = (PREFERENCES_DIR / 'qtquickcontrols2.conf').resolve()
|
||||||
|
template_conf = (BASE_DIR / 'config' / 'qtquickcontrols2.conf').resolve()
|
||||||
|
|
||||||
|
if not active_conf.exists():
|
||||||
|
copy_file(template_conf, active_conf)
|
||||||
|
else:
|
||||||
|
merge_config_files(active_conf, template_conf)
|
||||||
|
|
||||||
|
|
||||||
|
check_conf_file()
|
||||||
|
CONFIGURE_QT = Config((PREFERENCES_DIR / 'qtquickcontrols2.conf').resolve().as_posix())
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import os
|
|
||||||
import locale
|
import locale
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PySide6.QtCore import qVersion
|
from PySide6.QtCore import qVersion
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
class Constants():
|
class Constants():
|
||||||
@@ -12,19 +10,12 @@ class Constants():
|
|||||||
APPLICATION_NAME = 'Artemis'
|
APPLICATION_NAME = 'Artemis'
|
||||||
ORGANIZATION_NAME = 'AresValley'
|
ORGANIZATION_NAME = 'AresValley'
|
||||||
ORGANIZATION_DOMAIN = 'aresvalley.com'
|
ORGANIZATION_DOMAIN = 'aresvalley.com'
|
||||||
APPLICATION_VERSION = '4.0.0'
|
APPLICATION_VERSION = '4.1.0'
|
||||||
|
|
||||||
BASE_DIR = Path(os.path.dirname(__file__)) / '../..'
|
|
||||||
PREFERENCES_DIR = BASE_DIR / 'config'
|
|
||||||
DB_DIR = BASE_DIR / 'data'
|
|
||||||
UI_DIR = BASE_DIR / 'ui'
|
|
||||||
IMAGES_DIR = BASE_DIR / 'images'
|
|
||||||
LOGS_DIR = BASE_DIR / 'logs'
|
|
||||||
|
|
||||||
SQL_NAME = 'data.sqlite'
|
SQL_NAME = 'data.sqlite'
|
||||||
|
|
||||||
DB_LATEST_VERSION = 'https://www.aresvalley.com/artemis/v4/latest.json'
|
LATEST_VERSION_URL = 'https://raw.githubusercontent.com/AresValley/Artemis/master/config/release-info.json'
|
||||||
POSEIDON_REPORT = 'https://www.aresvalley.com/poseidon_engine/data.json'
|
POSEIDON_REPORT_URL = 'https://www.aresvalley.com/poseidon_engine/data.json'
|
||||||
|
|
||||||
DEFAULT_ENCODING = 'utf-8'
|
DEFAULT_ENCODING = 'utf-8'
|
||||||
SYSTEM_LANGUAGE = 'en_US' # locale.getdefaultlocale()[0]
|
SYSTEM_LANGUAGE = 'en_US' # locale.getdefaultlocale()[0]
|
||||||
@@ -48,6 +39,7 @@ class Messages:
|
|||||||
UP_TO_DATE = "You're up to date!"
|
UP_TO_DATE = "You're up to date!"
|
||||||
DB_NEW_VER = "New SigID DB version available!"
|
DB_NEW_VER = "New SigID DB version available!"
|
||||||
ART_NEW_VER = "New Artemis version available!"
|
ART_NEW_VER = "New Artemis version available!"
|
||||||
|
DB_CORRUPTED = "Database Corruption Detected"
|
||||||
|
|
||||||
# Messages
|
# Messages
|
||||||
DB_CREATION_SUCCESS_MSG = "The new database has been created succesfully."
|
DB_CREATION_SUCCESS_MSG = "The new database has been created succesfully."
|
||||||
@@ -59,8 +51,10 @@ class Messages:
|
|||||||
NO_CONNECTION_MSG = "Unable to check for updates. It appears that there is a problem with your internet connection. Please check your network settings and try again later. {}"
|
NO_CONNECTION_MSG = "Unable to check for updates. It appears that there is a problem with your internet connection. Please check your network settings and try again later. {}"
|
||||||
UP_TO_DATE_MSG = "The latest version of Artemis and SigID wiki is installed on your computer."
|
UP_TO_DATE_MSG = "The latest version of Artemis and SigID wiki is installed on your computer."
|
||||||
DB_NEW_VER_MSG = "A new version of the database ({}) is available for download. Download now?"
|
DB_NEW_VER_MSG = "A new version of the database ({}) is available for download. Download now?"
|
||||||
ART_NEW_VER_MSG = "A new version of Artemis ({}) is available for download. Check GitHub page now?"
|
ART_NEW_VER_MANUAL_MSG = "A new version of Artemis ({}) is available for download. Check GitHub page now?"
|
||||||
DOWNLOAD_CORRUPTED_MSG = "Downloaded data corrupted or invalid. Please retry."
|
ART_NEW_VER_AUTO_MSG = "A new version of Artemis ({}) is available for download. Update Artemis now?"
|
||||||
|
DB_CORRUPTED_MSG = "Downloaded data corrupted or invalid. Please retry."
|
||||||
|
DB_DOWNLOAD_SUCCESS_MSG = "The database has been successfully downloaded and is now being loaded."
|
||||||
|
|
||||||
|
|
||||||
class Query():
|
class Query():
|
||||||
@@ -68,13 +62,13 @@ class Query():
|
|||||||
|
|
||||||
############################## SELECT
|
############################## SELECT
|
||||||
|
|
||||||
SELECT_ALL_SIGNALS = "SELECT SIG_ID, NAME FROM signals ORDER BY NAME ASC"
|
SELECT_ALL_SIGNALS = "SELECT SIG_ID, NAME, DESCRIPTION FROM signals ORDER BY NAME ASC"
|
||||||
|
|
||||||
SELECT_ALL_MODULATION = "SELECT DISTINCT VALUE FROM modulation ORDER BY VALUE ASC"
|
SELECT_ALL_MODULATION = "SELECT DISTINCT VALUE FROM modulation ORDER BY VALUE ASC"
|
||||||
|
|
||||||
SELECT_ALL_LOCATION = "SELECT DISTINCT VALUE FROM location ORDER BY VALUE ASC"
|
SELECT_ALL_LOCATION = "SELECT DISTINCT VALUE FROM location ORDER BY VALUE ASC"
|
||||||
|
|
||||||
SELECT_SIG_ID = "SELECT SIG_ID, NAME FROM signals WHERE SIG_ID IN ({}) ORDER BY NAME ASC"
|
SELECT_SIG_ID = "SELECT SIG_ID, NAME, DESCRIPTION FROM signals WHERE SIG_ID IN ({}) ORDER BY NAME ASC"
|
||||||
|
|
||||||
SELECT_ALL_CAT_LABELS = "SELECT CLB_ID, VALUE FROM category_label ORDER BY VALUE ASC"
|
SELECT_ALL_CAT_LABELS = "SELECT CLB_ID, VALUE FROM category_label ORDER BY VALUE ASC"
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from artemis.utils.constants import Query
|
from artemis.utils.constants import Query
|
||||||
|
|
||||||
|
|
||||||
@@ -75,3 +77,17 @@ def generate_filter_query(filer_status):
|
|||||||
))
|
))
|
||||||
|
|
||||||
return ' INTERSECT '.join(query)
|
return ' INTERSECT '.join(query)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(date_str):
|
||||||
|
""" Parses a date string in "%Y-%m-%d %H:%M:%S.%f" format and returns
|
||||||
|
the date in "YYYY-MM-DD" format. If parsing fails, returns the original string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
date_str (str): The date string to parse.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
form_date = datetime.strptime(date_str, "%Y-%m-%d %H:%M:%S.%f")
|
||||||
|
return str(form_date.date())
|
||||||
|
except ValueError:
|
||||||
|
return date_str
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
import os
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from packaging.version import Version
|
|
||||||
|
|
||||||
from artemis.utils.constants import Constants, Messages
|
|
||||||
from artemis.utils.sql_utils import ArtemisDatabase
|
|
||||||
from artemis.utils.sys_utils import is_windows, is_linux, is_macos
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkManager:
|
|
||||||
""" Class that checks for DB or software updates
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
|
||||||
self._parent = parent
|
|
||||||
self.sigid_db_path = Constants.DB_DIR / 'sigID' / Constants.SQL_NAME
|
|
||||||
|
|
||||||
self.show_popup = False
|
|
||||||
self.db_update = None
|
|
||||||
self.art_update = None
|
|
||||||
|
|
||||||
self.remote_db_url = None
|
|
||||||
self.remote_db_hash = None
|
|
||||||
self.remote_db_version = None
|
|
||||||
self.remote_db_size = None
|
|
||||||
|
|
||||||
self.remote_art_version = None
|
|
||||||
|
|
||||||
self.check_updates()
|
|
||||||
|
|
||||||
|
|
||||||
def check_updates(self):
|
|
||||||
""" Checks if a new DB update is available.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
popup (bool, optional): Suppress the "already up-to-date" message on startup.
|
|
||||||
Defaults to False.
|
|
||||||
"""
|
|
||||||
latest_json = self.fetch_remote_json(Constants.DB_LATEST_VERSION)
|
|
||||||
if latest_json:
|
|
||||||
local_db = self.load_local_db()
|
|
||||||
remote_db = latest_json['sigID_DB']
|
|
||||||
|
|
||||||
self.remote_db_version = remote_db['version']
|
|
||||||
self.remote_db_url = remote_db['url']
|
|
||||||
self.remote_db_hash = remote_db['sha256_hash']
|
|
||||||
self.remote_db_size = remote_db['total_bytes']
|
|
||||||
|
|
||||||
if is_windows():
|
|
||||||
self.remote_art_version = latest_json['windows']['version']
|
|
||||||
elif is_linux():
|
|
||||||
self.remote_art_version = latest_json['linux']['version']
|
|
||||||
elif is_macos():
|
|
||||||
self.remote_art_version = latest_json['mac']['version']
|
|
||||||
|
|
||||||
if Version(self.remote_art_version) > Version(Constants.APPLICATION_VERSION):
|
|
||||||
self.art_update = True
|
|
||||||
else:
|
|
||||||
self.art_update = False
|
|
||||||
|
|
||||||
if self.art_update:
|
|
||||||
self.show_popup_art_update()
|
|
||||||
else:
|
|
||||||
if local_db:
|
|
||||||
if self.remote_db_version > local_db.version:
|
|
||||||
self.show_popup_db_update()
|
|
||||||
elif self.show_popup:
|
|
||||||
self.show_popup_up_to_date()
|
|
||||||
else:
|
|
||||||
self.show_popup_initial_db_download()
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_remote_json(self, url):
|
|
||||||
""" Fetches the remote json from a url
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
response = requests.get(url)
|
|
||||||
response.raise_for_status()
|
|
||||||
return response.json()
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
if self.show_popup:
|
|
||||||
self._parent.dialog_popup(
|
|
||||||
Messages.DIALOG_TYPE_ERROR,
|
|
||||||
Messages.NO_CONNECTION,
|
|
||||||
Messages.NO_CONNECTION_MSG.format(e)
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def load_local_db(self):
|
|
||||||
""" Loads the local database if exists
|
|
||||||
"""
|
|
||||||
if os.path.exists(self.sigid_db_path):
|
|
||||||
local_db = ArtemisDatabase('sigID')
|
|
||||||
local_db.load()
|
|
||||||
return local_db
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def show_popup_db_update(self):
|
|
||||||
"""Prompts the user to download the updated version of the database."""
|
|
||||||
self._parent.dialog_download_db(
|
|
||||||
Messages.DIALOG_TYPE_WARN,
|
|
||||||
Messages.DB_NEW_VER,
|
|
||||||
Messages.DB_NEW_VER_MSG.format(self.remote_db_version)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def show_popup_art_update(self):
|
|
||||||
"""Prompts the user to download the updated version of the database."""
|
|
||||||
self._parent.dialog_download_artemis(
|
|
||||||
Messages.DIALOG_TYPE_WARN,
|
|
||||||
Messages.ART_NEW_VER,
|
|
||||||
Messages.ART_NEW_VER_MSG.format(self.remote_art_version)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def show_popup_up_to_date(self):
|
|
||||||
"""Notifies the user that the database is up to date."""
|
|
||||||
self._parent.dialog_popup(
|
|
||||||
Messages.DIALOG_TYPE_INFO,
|
|
||||||
Messages.UP_TO_DATE,
|
|
||||||
Messages.UP_TO_DATE_MSG
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def show_popup_initial_db_download(self):
|
|
||||||
"""Prompts the user to download the database for the first time."""
|
|
||||||
self._parent.dialog_download_db(
|
|
||||||
Messages.DIALOG_TYPE_QUEST,
|
|
||||||
Messages.NO_DB_DETECTED,
|
|
||||||
Messages.NO_DB_DETECTED_MSG
|
|
||||||
)
|
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from artemis.utils.sql_utils import ArtemisDatabase
|
|
||||||
from artemis.utils.constants import Constants
|
from artemis.utils.constants import Constants
|
||||||
from artemis.utils.sys_utils import *
|
from artemis.utils.sys_utils import is_windows, is_linux, is_macos
|
||||||
|
|
||||||
|
|
||||||
def check_data_dir():
|
|
||||||
if not os.path.exists(Constants.DB_DIR):
|
|
||||||
os.makedirs(Constants.DB_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
def normalize_dialog_path(path):
|
def normalize_dialog_path(path):
|
||||||
@@ -19,38 +13,47 @@ def normalize_dialog_path(path):
|
|||||||
return norm_path
|
return norm_path
|
||||||
|
|
||||||
|
|
||||||
def logs_dir():
|
def _app_dir():
|
||||||
if is_macos():
|
if is_macos():
|
||||||
logs_dir_path = Path.home() / 'Library/Logs/' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME
|
app_dir_path = Path.home() / 'Library' / 'Application Support' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME
|
||||||
elif is_windows():
|
elif is_windows():
|
||||||
logs_dir_path = Path.home() / 'AppData/Local/' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME / 'logs'
|
app_dir_path = Path.home() / 'AppData' / 'Local' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME
|
||||||
elif is_linux():
|
elif is_linux():
|
||||||
logs_dir_path = Path.home() / '/var/log/' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME
|
app_dir_path = Path.home() / '.local' / 'share' / Constants.ORGANIZATION_NAME / Constants.APPLICATION_NAME
|
||||||
else:
|
else:
|
||||||
logs_dir_path = Constants.LOGS_DIR
|
app_dir_path = BASE_DIR.resolve()
|
||||||
|
|
||||||
if not logs_dir_path.exists():
|
if not app_dir_path.exists():
|
||||||
logs_dir_path.mkdir(parents=True)
|
app_dir_path.mkdir(parents=True)
|
||||||
|
|
||||||
return logs_dir_path
|
return app_dir_path
|
||||||
|
|
||||||
|
|
||||||
def valid_db(db_dir_name):
|
def _data_dir():
|
||||||
""" Checks if db_dir_name is a valid db dir containing a `data.sqlite` file.
|
data_dir_path = APP_DIR / 'data'
|
||||||
Db must be valid as well and should be properly initialized and loaded with
|
if not data_dir_path.exists():
|
||||||
no errors.
|
data_dir_path.mkdir(parents=True)
|
||||||
|
return data_dir_path
|
||||||
|
|
||||||
Args:
|
|
||||||
db_dir_name (str): name of the db folder
|
def _tmp_dir():
|
||||||
"""
|
if is_windows():
|
||||||
if os.path.exists(Constants.DB_DIR / db_dir_name / Constants.SQL_NAME):
|
tmp_dir_path = Path.home() / 'AppData' / 'Local' / 'Temp'
|
||||||
try:
|
|
||||||
database = ArtemisDatabase(db_dir_name)
|
|
||||||
database.load()
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
# Invalid or corrupted DB
|
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
# The dir is not containing a data.sqlite file
|
tmp_dir_path = Path('/tmp')
|
||||||
return False
|
|
||||||
|
return tmp_dir_path
|
||||||
|
|
||||||
|
|
||||||
|
def _preference_dir():
|
||||||
|
preference_dir_path = APP_DIR / 'config'
|
||||||
|
if not preference_dir_path.exists():
|
||||||
|
preference_dir_path.mkdir(parents=True)
|
||||||
|
return preference_dir_path
|
||||||
|
|
||||||
|
|
||||||
|
BASE_DIR = Path(os.path.dirname(__file__)) / '../..'
|
||||||
|
APP_DIR = _app_dir()
|
||||||
|
DATA_DIR = _data_dir()
|
||||||
|
TMP_DIR = _tmp_dir()
|
||||||
|
PREFERENCES_DIR = _preference_dir()
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import sqlite3
|
|
||||||
import os
|
import os
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
from PySide6.QtCore import QUrl
|
from PySide6.QtCore import QUrl
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from contextlib import closing
|
||||||
|
|
||||||
from artemis.utils.constants import Query, Constants
|
from artemis.utils.constants import Query, Constants
|
||||||
from artemis.utils.generic_utils import *
|
from artemis.utils.generic_utils import format_frequency
|
||||||
from contextlib import closing
|
from artemis.utils.path_utils import DATA_DIR
|
||||||
|
|
||||||
|
|
||||||
class Database():
|
class Database():
|
||||||
@@ -51,7 +52,7 @@ class ArtemisDatabase(Database):
|
|||||||
|
|
||||||
def __init__(self, db_dir_name):
|
def __init__(self, db_dir_name):
|
||||||
self.db_dir_name = db_dir_name
|
self.db_dir_name = db_dir_name
|
||||||
self.db_dir = Constants.DB_DIR / db_dir_name
|
self.db_dir = DATA_DIR / db_dir_name
|
||||||
self.sql_path = self.db_dir / Constants.SQL_NAME
|
self.sql_path = self.db_dir / Constants.SQL_NAME
|
||||||
self.media_dir = self.db_dir / 'media'
|
self.media_dir = self.db_dir / 'media'
|
||||||
super().__init__(self.sql_path)
|
super().__init__(self.sql_path)
|
||||||
@@ -95,7 +96,7 @@ class ArtemisDatabase(Database):
|
|||||||
contains the SIG_ID and the NAME of the signal
|
contains the SIG_ID and the NAME of the signal
|
||||||
"""
|
"""
|
||||||
self.all_signals = self.execute(Query.SELECT_ALL_SIGNALS)
|
self.all_signals = self.execute(Query.SELECT_ALL_SIGNALS)
|
||||||
keys = ('SIG_ID', 'name')
|
keys = ('SIG_ID', 'name', 'description')
|
||||||
result = [dict(zip(keys, values)) for values in self.all_signals]
|
result = [dict(zip(keys, values)) for values in self.all_signals]
|
||||||
self.all_signals = result
|
self.all_signals = result
|
||||||
|
|
||||||
@@ -131,7 +132,7 @@ class ArtemisDatabase(Database):
|
|||||||
sig_ids = ",".join(str(num[0]) for num in matching_sig_ids)
|
sig_ids = ",".join(str(num[0]) for num in matching_sig_ids)
|
||||||
|
|
||||||
self.all_signals = self.execute(Query.SELECT_SIG_ID.format(sig_ids))
|
self.all_signals = self.execute(Query.SELECT_SIG_ID.format(sig_ids))
|
||||||
keys = ('SIG_ID', 'name')
|
keys = ('SIG_ID', 'name', 'description')
|
||||||
result = [dict(zip(keys, values)) for values in self.all_signals]
|
result = [dict(zip(keys, values)) for values in self.all_signals]
|
||||||
self.all_signals = result
|
self.all_signals = result
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ class ArtemisDatabase(Database):
|
|||||||
""" Create new db in the data folder.
|
""" Create new db in the data folder.
|
||||||
The name of folder containing the new db has a unique id as name (db_dir_name).
|
The name of folder containing the new db has a unique id as name (db_dir_name).
|
||||||
"""
|
"""
|
||||||
meta = [name, datetime.now(), 0, 0]
|
meta = [name, datetime.now(), 1, 1]
|
||||||
os.makedirs(self.db_dir)
|
os.makedirs(self.db_dir)
|
||||||
os.makedirs(self.media_dir)
|
os.makedirs(self.media_dir)
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import hashlib
|
|||||||
from shutil import rmtree, copyfile, make_archive, unpack_archive
|
from shutil import rmtree, copyfile, make_archive, unpack_archive
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from artemis.utils.constants import Constants, Messages
|
from artemis.utils.constants import Messages
|
||||||
|
|
||||||
|
|
||||||
def is_windows():
|
def is_windows():
|
||||||
@@ -46,29 +46,38 @@ def open_directory(directory, timeout=3):
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def delete_db_dir(db_dir_name):
|
|
||||||
"""Delete the db folder"""
|
|
||||||
db_dir = Constants.DB_DIR / db_dir_name
|
|
||||||
if os.path.exists(db_dir):
|
|
||||||
rmtree(db_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def copy_file(src_file_path, dst_file_path):
|
def copy_file(src_file_path, dst_file_path):
|
||||||
copyfile(src_file_path, dst_file_path)
|
copyfile(src_file_path, dst_file_path)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_dir(dir_path):
|
||||||
|
if os.path.exists(dir_path):
|
||||||
|
rmtree(dir_path)
|
||||||
|
|
||||||
|
|
||||||
def delete_file(file_path):
|
def delete_file(file_path):
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
os.remove(file_path)
|
os.remove(file_path)
|
||||||
|
|
||||||
|
|
||||||
def pack_db(save_path, db_dir):
|
def make_tar(save_path, origin_path):
|
||||||
make_archive(save_path, 'tar', db_dir.resolve().as_posix())
|
""" Create a tar archive from a folder
|
||||||
|
|
||||||
|
Args:
|
||||||
|
save_path: destination path where new tar is saved
|
||||||
|
origin_path: directory path of the folder to be archived
|
||||||
|
"""
|
||||||
|
make_archive(save_path, 'tar', origin_path.resolve().as_posix())
|
||||||
|
|
||||||
|
|
||||||
def unpack_db(tar_path, db_dir_name):
|
def unpack_tar(tar_path, destination_path):
|
||||||
db_dir = Constants.DB_DIR / db_dir_name
|
""" Unpack a tar archive in a folder
|
||||||
unpack_archive(tar_path, db_dir, 'tar')
|
|
||||||
|
Args:
|
||||||
|
tar_path: path of the tar to be unpacked
|
||||||
|
destination_path: path where the tar is extracted
|
||||||
|
"""
|
||||||
|
unpack_archive(tar_path, destination_path, 'tar')
|
||||||
|
|
||||||
|
|
||||||
def match_hash(data, reference_hash):
|
def match_hash(data, reference_hash):
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from artemis.utils.config_utils import CONFIGURE_QT
|
|||||||
|
|
||||||
|
|
||||||
def set_ui():
|
def set_ui():
|
||||||
os.environ['QT_QUICK_CONTROLS_STYLE'] = CONFIGURE_QT.get_or_default('Controls', 'style', 'Material')
|
os.environ['QT_QUICK_CONTROLS_STYLE'] = CONFIGURE_QT.value('Controls', 'style', 'Material')
|
||||||
os.environ['QT_QUICK_CONTROLS_MATERIAL_VARIANT'] = CONFIGURE_QT.get_or_default('Material', 'variant', 'Dense')
|
os.environ['QT_QUICK_CONTROLS_MATERIAL_VARIANT'] = CONFIGURE_QT.value('Material', 'variant', 'Dense')
|
||||||
os.environ['QT_QUICK_CONTROLS_MATERIAL_THEME'] = CONFIGURE_QT.get_or_default('Material', 'theme', 'System')
|
os.environ['QT_QUICK_CONTROLS_MATERIAL_THEME'] = CONFIGURE_QT.value('Material', 'theme', 'System')
|
||||||
os.environ['QT_QUICK_CONTROLS_MATERIAL_ACCENT'] = CONFIGURE_QT.get_or_default('Material', 'accent', 'Green')
|
os.environ['QT_QUICK_CONTROLS_MATERIAL_ACCENT'] = CONFIGURE_QT.value('Material', 'accent', 'Green')
|
||||||
|
|
||||||
if is_windows():
|
if is_windows():
|
||||||
os.environ['QSG_RHI_BACKEND'] = 'opengl'
|
os.environ['QSG_RHI_BACKEND'] = 'opengl'
|
||||||
@@ -19,3 +19,5 @@ def set_ui():
|
|||||||
|
|
||||||
os.environ['QT_ENABLE_GLYPH_CACHE_WORKAROUND'] = '1'
|
os.environ['QT_ENABLE_GLYPH_CACHE_WORKAROUND'] = '1'
|
||||||
os.environ['QML_USE_GLYPHCACHE_WORKAROUND'] = '1'
|
os.environ['QML_USE_GLYPHCACHE_WORKAROUND'] = '1'
|
||||||
|
|
||||||
|
os.environ['QT_DEBUG_PLUGINS'] = CONFIGURE_QT.value('Develop', 'debug_plugin', '0')
|
||||||
|
|||||||
222
artemis/utils/update_utils.py
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
import uuid
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from packaging.version import Version
|
||||||
|
|
||||||
|
from artemis.utils.constants import Constants, Messages
|
||||||
|
from artemis.utils.sys_utils import is_windows, is_linux, is_macos, delete_file, match_hash, unpack_tar, open_file
|
||||||
|
from artemis.utils.path_utils import DATA_DIR, TMP_DIR
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateManager:
|
||||||
|
""" Class used to manage DB and software updates
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
self._parent = parent
|
||||||
|
|
||||||
|
self.db_update = None
|
||||||
|
self.art_update = None
|
||||||
|
|
||||||
|
self.remote_db_url = None
|
||||||
|
self.remote_db_hash = None
|
||||||
|
self.remote_db_version = None
|
||||||
|
self.remote_db_size = None
|
||||||
|
self.remote_db_file_name = None
|
||||||
|
|
||||||
|
self.remote_artemis_version = None
|
||||||
|
self.remote_artemis_url = None
|
||||||
|
self.remote_artemis_file_name = None
|
||||||
|
|
||||||
|
self.check_updates()
|
||||||
|
|
||||||
|
|
||||||
|
def check_updates(self, show_popup=False):
|
||||||
|
""" Checks if a software or DB update is available.
|
||||||
|
Prioritize Artemis updates over the DB one.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
show_popup (bool, optional):
|
||||||
|
If False, suppress the "already up-to-date" message on startup.
|
||||||
|
Defaults to False. True is usefull when the user manual check for
|
||||||
|
updates.
|
||||||
|
"""
|
||||||
|
latest_json = self.fetch_remote_json(Constants.LATEST_VERSION_URL, show_popup)
|
||||||
|
if latest_json:
|
||||||
|
local_db = self._parent.dbmanager.get_latest_local_sigid_db()
|
||||||
|
remote_db = latest_json['sigID_DB']
|
||||||
|
|
||||||
|
self.remote_db_version = remote_db['version']
|
||||||
|
self.remote_db_url = remote_db['url']
|
||||||
|
self.remote_db_hash = remote_db['sha256_hash']
|
||||||
|
self.remote_db_size = remote_db['total_bytes']
|
||||||
|
self.remote_db_file_name = self.remote_db_url.split('/')[-1]
|
||||||
|
|
||||||
|
if is_windows():
|
||||||
|
self.remote_artemis_version = latest_json['windows']['version']
|
||||||
|
self.remote_artemis_url = latest_json['windows']['url']
|
||||||
|
elif is_linux():
|
||||||
|
self.remote_artemis_version = latest_json['linux']['version']
|
||||||
|
self.remote_artemis_url = latest_json['linux']['url']
|
||||||
|
elif is_macos():
|
||||||
|
self.remote_artemis_version = latest_json['mac']['version']
|
||||||
|
self.remote_artemis_url = latest_json['mac']['url']
|
||||||
|
|
||||||
|
self.remote_artemis_file_name = self.remote_artemis_url.split('/')[-1]
|
||||||
|
|
||||||
|
if Version(self.remote_artemis_version) > Version(Constants.APPLICATION_VERSION):
|
||||||
|
self.art_update = True
|
||||||
|
else:
|
||||||
|
self.art_update = False
|
||||||
|
|
||||||
|
if self.art_update:
|
||||||
|
self._show_popup_art_update()
|
||||||
|
else:
|
||||||
|
if local_db:
|
||||||
|
if self.remote_db_version > local_db.version:
|
||||||
|
self._show_popup_db_update()
|
||||||
|
elif show_popup:
|
||||||
|
self._show_popup_up_to_date()
|
||||||
|
else:
|
||||||
|
self._show_popup_initial_db_download()
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_remote_json(self, url, show_popup=False):
|
||||||
|
""" Fetches the remote json from a url
|
||||||
|
|
||||||
|
Args:
|
||||||
|
show_popup (bool, optional): If false, suppress any error message
|
||||||
|
Defaults to False (to avoid error if the program is used offline)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(url)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
if show_popup:
|
||||||
|
self._parent.dialog_popup(
|
||||||
|
Messages.DIALOG_TYPE_ERROR,
|
||||||
|
Messages.NO_CONNECTION,
|
||||||
|
Messages.NO_CONNECTION_MSG.format(e)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def download_db(self):
|
||||||
|
""" Open the downloader and download the sigID database in the
|
||||||
|
TMP_DIR folder. After a succesfull download the callback function
|
||||||
|
from the downloader is post_download_db
|
||||||
|
"""
|
||||||
|
self._parent.downloader.finished.connect(self.post_download_db)
|
||||||
|
self._parent.downloader.on_start(
|
||||||
|
self.remote_db_url,
|
||||||
|
TMP_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def post_download_db(self):
|
||||||
|
""" After a succesfull DB download, this function check the hash
|
||||||
|
for possible corrupted data, delete old sigID DB and extract
|
||||||
|
the new one
|
||||||
|
"""
|
||||||
|
latest_db_tar_path = TMP_DIR / self.remote_db_file_name
|
||||||
|
if match_hash(latest_db_tar_path, self.remote_db_hash):
|
||||||
|
db_dir_name = str(uuid.uuid4())
|
||||||
|
unpack_tar(latest_db_tar_path, DATA_DIR / db_dir_name)
|
||||||
|
self._parent.load_db(db_dir_name)
|
||||||
|
self._show_popup_db_download_complete()
|
||||||
|
else:
|
||||||
|
self._show_popup_db_hash_failed()
|
||||||
|
delete_file(latest_db_tar_path)
|
||||||
|
|
||||||
|
|
||||||
|
def download_artemis(self):
|
||||||
|
""" Open the downloader and download Artemis in the
|
||||||
|
TMP_DIR folder. After a succesfull download the callback function
|
||||||
|
from the downloader is post_download_artemis
|
||||||
|
"""
|
||||||
|
self._parent.downloader.finished.connect(self.post_download_artemis)
|
||||||
|
self._parent.downloader.on_start(
|
||||||
|
self.remote_artemis_url,
|
||||||
|
TMP_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def post_download_artemis(self):
|
||||||
|
""" After a succesfull Artemis download, this open the installer
|
||||||
|
and close the application
|
||||||
|
"""
|
||||||
|
if is_windows():
|
||||||
|
open_file(TMP_DIR / self.remote_artemis_file_name)
|
||||||
|
self._parent.close_ui.emit()
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_db_update(self):
|
||||||
|
""" Prompts the user to download the updated version of the database
|
||||||
|
"""
|
||||||
|
self._parent.dialog_download_db(
|
||||||
|
Messages.DIALOG_TYPE_WARN,
|
||||||
|
Messages.DB_NEW_VER,
|
||||||
|
Messages.DB_NEW_VER_MSG.format(self.remote_db_version)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_art_update(self):
|
||||||
|
""" Alerts the user of a new version of Artemis.
|
||||||
|
Windows - asks to download with automatic update
|
||||||
|
Linux, macOS - redirects to GitHub page
|
||||||
|
"""
|
||||||
|
if is_windows():
|
||||||
|
self._parent.dialog_update_artemis(
|
||||||
|
Messages.DIALOG_TYPE_QUEST,
|
||||||
|
Messages.ART_NEW_VER,
|
||||||
|
Messages.ART_NEW_VER_AUTO_MSG.format(self.remote_artemis_version),
|
||||||
|
True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._parent.dialog_update_artemis(
|
||||||
|
Messages.DIALOG_TYPE_QUEST,
|
||||||
|
Messages.ART_NEW_VER,
|
||||||
|
Messages.ART_NEW_VER_MANUAL_MSG.format(self.remote_artemis_version),
|
||||||
|
False
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_up_to_date(self):
|
||||||
|
""" Notifies the user that the database is up to date
|
||||||
|
"""
|
||||||
|
self._parent.dialog_popup(
|
||||||
|
Messages.DIALOG_TYPE_INFO,
|
||||||
|
Messages.UP_TO_DATE,
|
||||||
|
Messages.UP_TO_DATE_MSG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_initial_db_download(self):
|
||||||
|
""" Prompts the user to download the database for the first time
|
||||||
|
"""
|
||||||
|
self._parent.dialog_download_db(
|
||||||
|
Messages.DIALOG_TYPE_QUEST,
|
||||||
|
Messages.NO_DB_DETECTED,
|
||||||
|
Messages.NO_DB_DETECTED_MSG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_db_download_complete(self):
|
||||||
|
""" DB has been succesfully downloaded
|
||||||
|
"""
|
||||||
|
self._parent.dialog_popup(
|
||||||
|
Messages.DIALOG_TYPE_INFO,
|
||||||
|
Messages.GENERIC_SUCCESS,
|
||||||
|
Messages.DB_DOWNLOAD_SUCCESS_MSG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _show_popup_db_hash_failed(self):
|
||||||
|
""" Notify the user after detection of a corrupted database
|
||||||
|
"""
|
||||||
|
self._parent.dialog_popup(
|
||||||
|
Messages.DIALOG_TYPE_ERROR,
|
||||||
|
Messages.DB_CORRUPTED,
|
||||||
|
Messages.DB_CORRUPTED_MSG
|
||||||
|
)
|
||||||
@@ -4,22 +4,27 @@ echo "Building Linux target ..."
|
|||||||
|
|
||||||
echo "Installing requirements ..."
|
echo "Installing requirements ..."
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install nuitka
|
pip install nuitka==2.4.10
|
||||||
|
|
||||||
echo "Building with Nuitka ..."
|
echo "Building with Nuitka ..."
|
||||||
python -m nuitka app.py \
|
python -m nuitka app.py \
|
||||||
--standalone \
|
--standalone \
|
||||||
--follow-imports \
|
|
||||||
--show-modules \
|
--show-modules \
|
||||||
--assume-yes-for-downloads \
|
--assume-yes-for-downloads \
|
||||||
--disable-console \
|
|
||||||
--enable-plugin=pyside6 \
|
--enable-plugin=pyside6 \
|
||||||
--include-qt-plugins=sensible,styles,qml,multimedia \
|
--noinclude-dlls=libQt6Charts* \
|
||||||
|
--noinclude-dlls=libQt6Quick3D* \
|
||||||
|
--noinclude-dlls=libQt6Sensors* \
|
||||||
|
--noinclude-dlls=libQt6Test* \
|
||||||
|
--noinclude-dlls=libQt6WebEngine* \
|
||||||
|
--include-qt-plugins=styles \
|
||||||
|
--include-qt-plugins=qml \
|
||||||
|
--include-qt-plugins=multimedia \
|
||||||
--include-data-files=./artemis/resources.py=./artemis/resources.py \
|
--include-data-files=./artemis/resources.py=./artemis/resources.py \
|
||||||
--include-data-files=./config/qtquickcontrols2.conf=./config/qtquickcontrols2.conf \
|
--include-data-files=./config/qtquickcontrols2.conf=./config/qtquickcontrols2.conf \
|
||||||
--include-data-files=./building/Linux/create_shortcut.sh=./create_shortcut.sh \
|
--include-data-files=./building/Linux/create_shortcut.sh=./create_shortcut.sh \
|
||||||
--include-data-files=./images/artemis_icon.svg=./images/artemis_icon.svg
|
--include-data-files=./images/artemis_icon.svg=./images/artemis_icon.svg \
|
||||||
|
--force-stderr-spec="{TEMP}/artemis.err.log" \
|
||||||
chmod 755 ./app.dist/app.bin
|
--force-stdout-spec="{TEMP}/artemis.out.log"
|
||||||
|
|
||||||
echo "Building Linux target finished."
|
echo "Building Linux target finished."
|
||||||
|
|||||||
@@ -2,23 +2,31 @@ Write-Output "Building Windows target"
|
|||||||
|
|
||||||
Write-Output "Installing requirements ..."
|
Write-Output "Installing requirements ..."
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install nuitka
|
pip install nuitka==2.4.10
|
||||||
|
|
||||||
Write-Output "Building with Nuitka ..."
|
Write-Output "Building with Nuitka ..."
|
||||||
python -m nuitka app.py `
|
python -m nuitka app.py `
|
||||||
--standalone `
|
--standalone `
|
||||||
--follow-imports `
|
|
||||||
--show-modules `
|
--show-modules `
|
||||||
--assume-yes-for-downloads `
|
--assume-yes-for-downloads `
|
||||||
--disable-console `
|
--windows-console-mode=disable `
|
||||||
--enable-plugin=pyside6 `
|
--enable-plugin=pyside6 `
|
||||||
--include-qt-plugins=sensible,styles,qml,multimedia `
|
--noinclude-dlls="Qt6Charts*" `
|
||||||
|
--noinclude-dlls="Qt6Quick3D*" `
|
||||||
|
--noinclude-dlls="Qt6Sensors*" `
|
||||||
|
--noinclude-dlls="Qt6Test*" `
|
||||||
|
--noinclude-dlls="Qt6WebEngine*" `
|
||||||
|
--include-qt-plugins=styles `
|
||||||
|
--include-qt-plugins=qml `
|
||||||
|
--include-qt-plugins=multimedia `
|
||||||
--include-data-files=.\artemis\resources.py=.\artemis\resources.py `
|
--include-data-files=.\artemis\resources.py=.\artemis\resources.py `
|
||||||
--include-data-files=.\config\qtquickcontrols2.conf=.\config\qtquickcontrols2.conf `
|
--include-data-files=.\config\qtquickcontrols2.conf=.\config\qtquickcontrols2.conf `
|
||||||
|
--force-stderr-spec="{TEMP}\artemis.err.log" `
|
||||||
|
--force-stdout-spec="{TEMP}\artemis.out.log" `
|
||||||
--windows-company-name=Aresvalley.com `
|
--windows-company-name=Aresvalley.com `
|
||||||
--windows-product-name=Artemis `
|
--windows-product-name=Artemis `
|
||||||
--windows-file-version=4.0.0 `
|
--windows-file-version=4.1.0 `
|
||||||
--windows-product-version=4.0.0 `
|
--windows-product-version=4.1.0 `
|
||||||
--windows-file-description=Artemis `
|
--windows-file-description=Artemis `
|
||||||
--windows-icon-from-ico=images\artemis_icon.ico
|
--windows-icon-from-ico=images\artemis_icon.ico
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#define MyAppName "Artemis"
|
#define MyAppName "Artemis"
|
||||||
#define MyAppVersion "4.0.0"
|
#define MyAppVersion "4.1.0"
|
||||||
#define MyAppPublisher "AresValley"
|
#define MyAppPublisher "AresValley"
|
||||||
#define MyAppURL "https://www.aresvalley.com/"
|
#define MyAppURL "https://www.aresvalley.com/"
|
||||||
#define MyAppExeName "artemis.exe"
|
#define MyAppExeName "artemis.exe"
|
||||||
@@ -18,7 +18,7 @@ LicenseFile=..\..\LICENSE
|
|||||||
PrivilegesRequiredOverridesAllowed=dialog
|
PrivilegesRequiredOverridesAllowed=dialog
|
||||||
OutputDir=..\
|
OutputDir=..\
|
||||||
OutputBaseFilename=Artemis
|
OutputBaseFilename=Artemis
|
||||||
SetupIconFile=..\..\images\artemis_icon.ico
|
SetupIconFile=..\..\images\installer_icon.ico
|
||||||
Compression=lzma2/ultra64
|
Compression=lzma2/ultra64
|
||||||
SolidCompression=yes
|
SolidCompression=yes
|
||||||
VersionInfoVersion={#MyAppVersion}
|
VersionInfoVersion={#MyAppVersion}
|
||||||
@@ -42,3 +42,7 @@ Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: de
|
|||||||
|
|
||||||
[Run]
|
[Run]
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
|
||||||
|
|
||||||
|
[UninstallDelete]
|
||||||
|
Type: filesandordirs; Name: "{localappdata}\{#MyAppPublisher}\{#MyAppName}\cache"
|
||||||
|
Type: filesandordirs; Name: "{localappdata}\{#MyAppPublisher}\{#MyAppName}\config"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ echo "Building maacOS target ..."
|
|||||||
|
|
||||||
echo "Installing requirements ..."
|
echo "Installing requirements ..."
|
||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
pip install nuitka imageio
|
pip install nuitka==2.3 imageio
|
||||||
|
|
||||||
echo "Building with Nuitka ..."
|
echo "Building with Nuitka ..."
|
||||||
python -m nuitka app.py \
|
python -m nuitka app.py \
|
||||||
@@ -12,7 +12,6 @@ python -m nuitka app.py \
|
|||||||
--follow-imports \
|
--follow-imports \
|
||||||
--show-modules \
|
--show-modules \
|
||||||
--assume-yes-for-downloads \
|
--assume-yes-for-downloads \
|
||||||
--disable-console \
|
|
||||||
--enable-plugin=pyside6 \
|
--enable-plugin=pyside6 \
|
||||||
--include-qt-plugins=sensible,styles,qml,multimedia \
|
--include-qt-plugins=sensible,styles,qml,multimedia \
|
||||||
--include-data-files=./artemis/resources.py=./artemis/resources.py \
|
--include-data-files=./artemis/resources.py=./artemis/resources.py \
|
||||||
@@ -24,6 +23,6 @@ python -m nuitka app.py \
|
|||||||
--macos-app-name=Artemis \
|
--macos-app-name=Artemis \
|
||||||
--macos-app-mode=gui \
|
--macos-app-mode=gui \
|
||||||
--macos-sign-identity=ad-hoc \
|
--macos-sign-identity=ad-hoc \
|
||||||
--macos-app-version=4.0.0
|
--macos-app-version=4.1.0
|
||||||
|
|
||||||
echo "Building Linux target finished."
|
echo "Building Linux target finished."
|
||||||
|
|||||||
@@ -5,3 +5,9 @@ style=Material
|
|||||||
variant=Dense
|
variant=Dense
|
||||||
theme=System
|
theme=System
|
||||||
accent=Green
|
accent=Green
|
||||||
|
|
||||||
|
[Database]
|
||||||
|
autoload=0
|
||||||
|
|
||||||
|
[Develop]
|
||||||
|
debug_plugin=0
|
||||||
|
|||||||
20
config/release-info.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"sigID_DB": {
|
||||||
|
"version": 72,
|
||||||
|
"url": "https://github.com/AresValley/Artemis-DB/releases/download/v72/v72.tar",
|
||||||
|
"sha256_hash": "e41c211b0b51ad752404ffefe807638039b6386a6a21d8e5dac161f6143c95c5",
|
||||||
|
"total_bytes": 199249920
|
||||||
|
},
|
||||||
|
"windows": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"url": "https://github.com/AresValley/Artemis/releases/download/v4.1.0/Artemis-Windows-x86_64-4.1.0.exe"
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"url": "https://github.com/AresValley/Artemis/releases/download/v4.1.0/Artemis-Linux-x86_64-4.1.0.zip"
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"url": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,16 @@ where $Y_{i+τ}$ is a lagged data by $τ$ of $Y_i$ and $\bar{Y}$ is the average
|
|||||||
## Example: ACF Analysis
|
## Example: ACF Analysis
|
||||||
### STANAG 4285
|
### STANAG 4285
|
||||||
|
|
||||||
The first example will be a NATO standard known as STANAG 4285. You can download a .wav sample from HERE. We start to collect some information about the structure of this signal: a good starting point is the official declassified NATO document dated 1989. The main frame structure can be found at **Annex A-3/5** with a graphical view at **Annex A-7**, also reported below:
|
The first example will be a NATO standard known as STANAG 4285. You can download a .wav sample from [:material-download: HERE](assets/stanag_4285.wav).
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<audio controls>
|
||||||
|
<source src="https://raw.githubusercontent.com/AresValley/Artemis/master/docs/assets/stanag_4285.wav" type="audio/wav">
|
||||||
|
Your browser does not support the audio player.
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
We start to collect some information about the structure of this signal: a good starting point is the official declassified NATO document dated 1989. The main frame structure can be found at **Annex A-3/5** with a graphical view at **Annex A-7**, also reported below:
|
||||||
|
|
||||||
* [STANAG 4285 (i)](assets/acf_2.png)
|
* [STANAG 4285 (i)](assets/acf_2.png)
|
||||||
* [STANAG 4285 A-3](assets/acf_3.png)
|
* [STANAG 4285 A-3](assets/acf_3.png)
|
||||||
|
|||||||
BIN
docs/assets/HMIB.webp
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
docs/assets/HMID.webp
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
docs/assets/HMII.webp
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/assets/aia_0094.webp
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
docs/assets/aia_0131.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docs/assets/aia_0171.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/assets/aia_0193.webp
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
docs/assets/aia_0304.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
docs/assets/aia_0335.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
docs/assets/aia_1600.webp
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/assets/aia_1700.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 810 KiB |
BIN
docs/assets/artemis_preview.webp
Normal file
|
After Width: | Height: | Size: 209 KiB |
BIN
docs/assets/lasco_c2.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/assets/lasco_c3.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/assets/main_window.webp
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
docs/assets/ssa_1.webp
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
docs/assets/stanag_4285.wav
Normal file
BIN
docs/assets/sw_1.webp
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
docs/assets/sw_2.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
docs/assets/sw_3.webp
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
docs/assets/sw_4.webp
Normal file
|
After Width: | Height: | Size: 154 KiB |
109
docs/basic_operations.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 1. Main Menu
|
||||||
|
|
||||||
|
### File
|
||||||
|
|
||||||
|
* **New Database**
|
||||||
|
|
||||||
|
Create a new database.
|
||||||
|
|
||||||
|
* **Load Database**
|
||||||
|
|
||||||
|
Open the Database Manager windows in order to open, rename, or delete a database.
|
||||||
|
|
||||||
|
* **Import Database**
|
||||||
|
|
||||||
|
Import an Artemis database with a standard .tar format.
|
||||||
|
!!! tip "Offline Importing of SigID Database"
|
||||||
|
Sometimes it may happen that a computer does not have network access and unfortunately Artemis cannot download the SigID database. To solve this you can:
|
||||||
|
|
||||||
|
1. Download the .tar database [:material-download: HERE](https://github.com/AresValley/Artemis-DB/releases) from a PC with an internet access
|
||||||
|
2. Import the downloaded .tar on the target PC (without internet access) using the **Import Database** function
|
||||||
|
|
||||||
|
* **Export Database**
|
||||||
|
|
||||||
|
Export the loaded database with a standard .tar format.
|
||||||
|
|
||||||
|
* **Edit Tags**
|
||||||
|
|
||||||
|
Open the tags editor window. From here, you can add, rename, or delete tags. The tags can be added to a signal from the [tags menu](#4-tags)
|
||||||
|
|
||||||
|
* **Open Database Folder**
|
||||||
|
|
||||||
|
Shows the folder of the currently loaded database in the explorer.
|
||||||
|
|
||||||
|
* **Preferences**
|
||||||
|
|
||||||
|
Open the program settings window.
|
||||||
|
|
||||||
|
* **Exit**
|
||||||
|
|
||||||
|
This will close the application.
|
||||||
|
|
||||||
|
### Signal
|
||||||
|
* **New**
|
||||||
|
|
||||||
|
Add a new signal to the database.
|
||||||
|
|
||||||
|
* **Edit**
|
||||||
|
|
||||||
|
Edit the current/selected signal from the loaded database.
|
||||||
|
|
||||||
|
### Space Weather
|
||||||
|
|
||||||
|
* **Check Report**
|
||||||
|
|
||||||
|
Open the main [Space Weather window](space_weather/current.md) and retrieve all the live data from Poseidon Crawler.
|
||||||
|
|
||||||
|
## 2. Signal List
|
||||||
|
This is the signal list where all the database entries are shown. When a signal is selected, it will load on the right panel.
|
||||||
|
|
||||||
|
### Filter by Name/Description
|
||||||
|
On top of the list, there is a field for filtering signals by name or any keyword inside the description of the signal: this filter has the highest priority among all the filters.
|
||||||
|
|
||||||
|
## 3. Signal Menu
|
||||||
|
Here you can swithc between the main **signal** window and the **filter** page.
|
||||||
|
|
||||||
|
## 4. Tags
|
||||||
|
|
||||||
|
* **Associate Tag**
|
||||||
|
|
||||||
|
Custom tags can be associated to the selected signal with the :octicons-plus-circle-16: icon
|
||||||
|
|
||||||
|
* **Remove Tag**
|
||||||
|
|
||||||
|
In order to remove a tag, just click on its badge.
|
||||||
|
|
||||||
|
* **Add/Rename Tag**
|
||||||
|
|
||||||
|
To add a new tag open the [Tags Editor](#1-main-menu) in the main menu.
|
||||||
|
|
||||||
|
## 5. Add Parameter
|
||||||
|
Click on the labels to add the corresponding parameter to the signal (e.g. click on **Frequency** to add a new frequency).
|
||||||
|
|
||||||
|
## 6. Edit Parameter
|
||||||
|
Click on the parameter badge to open the Signal Editor windows. From here, you can edit or delete the corresponding parameter.
|
||||||
|
|
||||||
|
!!! tip "Parameter Description"
|
||||||
|
All the parameters have a description field: if some text is added, it will appear when the corresponding parameter badge is hovered with the mouse pointer.
|
||||||
|
|
||||||
|
## 7. Description :simple-markdown:{ title="Markdown Supported" }
|
||||||
|
This is the description of the signal and can be edited from the [Main Menu](#1-main-menu) (`Signal/Edit...`)
|
||||||
|
|
||||||
|
!!! tip "Markdown Supported"
|
||||||
|
The Description field can render **Markdown**, a simple markup language for creating rich text using plain text. Headers, emphasis, lists, links, code blocks, and many more features for advanced text formatting. [Markdown Basic Syntax :simple-markdown:](https://www.markdownguide.org/basic-syntax/)
|
||||||
|
|
||||||
|
## 8. Audio Sample
|
||||||
|
This is a player where an audio sample of the signal can be played. To associate an audio file to be shown as the **main** audio sample, check in the [extra menu](#10-extra) below the signal spectrum.
|
||||||
|
|
||||||
|
## 9. Image Sample
|
||||||
|
This is an image box that commonly contains the signal spectrum/waterfall. To associate an image file to be shown as the **main** image sample, check in the [extra menu](#10-extra) below the signal spectrum.
|
||||||
|
|
||||||
|
## 10. Extra
|
||||||
|
|
||||||
|
:material-earth: This button is only available for the standard SigID wiki database and connects the local signal to its counterpart on the [sigidwiki website](https://www.sigidwiki.com/).
|
||||||
|
|
||||||
|
:material-file-document-multiple: This will open the **Documents Manager**. From here you can add any file (audio, image, pdf, etc.) to the signal entry. It is also possible to mark only one image and one audio to be shown on the main signal window.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# Build Package
|
# Build Package
|
||||||
Building a distributable package with an executable for Artemis creates a practical solution for end-users, as they can run the application without needing to interact with the terminal and they can easily share the application as a stand-alone package.
|
Building a distributable package with an executable for Artemis creates a practical solution for end-users, as they can run the application without needing to interact with the terminal and they can easily share the application as a standalone package.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* Python (3.11 or higher)
|
* Python (3.11 or higher)
|
||||||
@@ -8,7 +8,7 @@ Building a distributable package with an executable for Artemis creates a practi
|
|||||||
We assume that Python is already installed on the system and the Artemis source code has been downloaded and extracted. If these prerequisites are not met, please follow steps 1 to 3 in the [run from source section](run_from_source.md).
|
We assume that Python is already installed on the system and the Artemis source code has been downloaded and extracted. If these prerequisites are not met, please follow steps 1 to 3 in the [run from source section](run_from_source.md).
|
||||||
|
|
||||||
!!! warning "Cross-Compilation"
|
!!! warning "Cross-Compilation"
|
||||||
To generate standalone packages, an operating system that matches the target OS must be used, as Nuitka does not support cross-compilation. For example: you cannot build binaries on Windows that work on Linux or macOS.
|
An operating system that matches the target OS must be used to generate standalone packages, as Nuitka does not support cross-compilation. For example, you cannot build binaries on Windows that work on Linux or macOS.
|
||||||
|
|
||||||
## :simple-windows: Windows
|
## :simple-windows: Windows
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Building a distributable package with an executable for Artemis creates a practi
|
|||||||
.\building\Windows\build_windows.ps1
|
.\building\Windows\build_windows.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Wait for the build process to complete. This may take a few minutes depending on your system's performance. Once the process finishes, check the `artemis.dist/` directory: it will contain the stand-alone software with the `artemis.exe` executable.
|
2. Wait for the build process to complete. This may take a few minutes depending on your system's performance. Once the process finishes, check the `artemis.dist/` directory: it will contain the standalone software with the `artemis.exe` executable.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ Building a distributable package with an executable for Artemis creates a practi
|
|||||||
. ./building/Linux/build_linux.sh
|
. ./building/Linux/build_linux.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Wait for the build process to complete. This may take a few minutes depending on your system's performance. Once the process finishes, check the `artemis.dist/` directory: it will contain the stand-alone software with the `app.bin` executable.
|
2. Wait for the build process to complete. This may take a few minutes depending on your system's performance. Once the process finishes, check the `artemis.dist/` directory: it will contain the standalone software with the `app.bin` executable.
|
||||||
3. If you wish to create a shortcut, follows the procedure in the [installation section](installation.md/#create-a-shortcut)
|
3. If you wish to create a shortcut, follows the procedure in the [installation section](installation.md/#create-a-shortcut)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
# Contribute
|
# Contribute
|
||||||
Artemis is an open source project an every contribution, no matter how small, is valuable and greatly appreciated. Don't worry about getting everything perfect, we are happy to work with you on your contribution and help you along the way. This guide will help you get started by outlining various ways you can contribute.
|
Artemis is an open-source project, and every contribution, no matter how small, is valuable and greatly appreciated. Don't worry about getting everything perfect; we are happy to work with you on your contribution and help you along the way. This guide will help you get started by outlining various ways you can contribute.
|
||||||
|
|
||||||
<div class="grid cards" markdown>
|
<div class="grid cards" markdown>
|
||||||
|
|
||||||
- :material-bug: __Spot a bug?__
|
- :material-bug: __Spot a bug?__
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Open an issue (or pull request) and let us know the problem you faced (or you're working on)
|
Please open an issue (or pull request) and let us know the problem you faced (or you're working on)
|
||||||
|
|
||||||
[:octicons-arrow-right-24: Open an Issue](https://github.com/AresValley/Artemis/issues)
|
[:octicons-arrow-right-24: Open an Issue](https://github.com/AresValley/Artemis/issues)
|
||||||
|
|
||||||
- :material-source-fork: __Fork the repository__
|
- :material-source-fork: __Fork the repository__
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Create your own copy of the codebase that you can modify and submit pull requests from.
|
Create a copy of the codebase from which you can modify and submit pull requests.
|
||||||
|
|
||||||
|
|
||||||
[:octicons-arrow-right-24: Fork the repo](https://github.com/AresValley/Artemis)
|
[:octicons-arrow-right-24: Fork the repo](https://github.com/AresValley/Artemis)
|
||||||
|
|
||||||
- :material-lightbulb-on: __Ideas?__
|
- :material-lightbulb-on: __Ideas?__
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Idea for a new feature? Open an issue on the project's GitHub repository to describe your proposal in detail.
|
Idea for a new feature? Open an issue on the project's GitHub repository to describe your proposal.
|
||||||
|
|
||||||
[:octicons-arrow-right-24: Open an Issue](https://github.com/AresValley/Artemis/issues)
|
[:octicons-arrow-right-24: Open an Issue](https://github.com/AresValley/Artemis/issues)
|
||||||
|
|
||||||
- :material-heart: __Spreading the word!__
|
- :material-heart: __Spreading the word!__
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Do you like Artemis? Don't forgeto to share it with your network and your friends!
|
Do you like Artemis? Remember to share it with your network and your friends!
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,65 +12,3 @@ Artemis is maintained by Marco Dalla Tiezza and released under the [GPLv3](https
|
|||||||
* [**Eric Wiessner (KI7POL)**](https://github.com/WheezyE "GitHub profile") - *ARM port (Raspberry Pi3B+ and Pi4B)*
|
* [**Eric Wiessner (KI7POL)**](https://github.com/WheezyE "GitHub profile") - *ARM port (Raspberry Pi3B+ and Pi4B)*
|
||||||
* [**Pierpaolo Pravatto**](https://github.com/ppravatto "GitHub profile") - *Wiki page, β Tester*
|
* [**Pierpaolo Pravatto**](https://github.com/ppravatto "GitHub profile") - *Wiki page, β Tester*
|
||||||
* [**Francesco Capostagno**](https://github.com/fcapostagno "GitHub profile"), **Luca**, **Pietro** - *β Tester*
|
* [**Francesco Capostagno**](https://github.com/fcapostagno "GitHub profile"), **Luca**, **Pietro** - *β Tester*
|
||||||
|
|
||||||
## Donators
|
|
||||||
|
|
||||||
* Eric Hahn
|
|
||||||
* Alan Lawrence
|
|
||||||
* Diego Gil Fernandez
|
|
||||||
* Torsten Teichert
|
|
||||||
* Charles Preston
|
|
||||||
* Brad Hein
|
|
||||||
* Paolo Romani
|
|
||||||
* Michelle Corbani
|
|
||||||
* Martin van Duinen
|
|
||||||
* Valentino Zardi
|
|
||||||
* Emmanuel Fabre
|
|
||||||
* Oscar Nilsson
|
|
||||||
* Pierre Declercq
|
|
||||||
* Detlef Jahn
|
|
||||||
* Oliver Schellenberg
|
|
||||||
* Stephane Imbertone
|
|
||||||
* Roel Ketelaars
|
|
||||||
* Timothy Ehrhart
|
|
||||||
* George Mager
|
|
||||||
* Gerhard Amon
|
|
||||||
* Gerald Schmidt
|
|
||||||
* Carlos Rocha
|
|
||||||
* Joshua Frohberg
|
|
||||||
* Bill Riches
|
|
||||||
* Jeffrey Krehbiel
|
|
||||||
* Володимир Багмет
|
|
||||||
* Philip Hamlin
|
|
||||||
* David Davies
|
|
||||||
* Nigel P. Lawrence
|
|
||||||
* Marco Rissi (PP5ZX)
|
|
||||||
* Martin van Duinen
|
|
||||||
* Alex Diamantopulo
|
|
||||||
* Joseph Winter
|
|
||||||
* Mark Bender
|
|
||||||
* Rolf Gerhardt
|
|
||||||
* Denese Harris
|
|
||||||
* Benjamin Steele
|
|
||||||
* Alexander Irmscher
|
|
||||||
* Jonathan Chang
|
|
||||||
* Torsten Lipke
|
|
||||||
* Massimo Petrantoni
|
|
||||||
* William Arcand
|
|
||||||
* Jon Carp
|
|
||||||
* Robert Crone
|
|
||||||
* William Houston
|
|
||||||
* Richard Quasne
|
|
||||||
* Tom Krugliakov
|
|
||||||
* Francisco Neira Basso
|
|
||||||
* Alistair Macrae
|
|
||||||
* Kevin Arburn
|
|
||||||
* Marek Barłóg
|
|
||||||
* Gabriel Glösmann
|
|
||||||
* Corbin Williams
|
|
||||||
* Ton Machielsen
|
|
||||||
* Ivan Rancic
|
|
||||||
* Alipio Fernandez
|
|
||||||
* Matt Eisele
|
|
||||||
* Martin Dudel
|
|
||||||
* Harald Geier
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
The table contains 4 columns explained below.
|
The table contains 4 columns explained below.
|
||||||
|
|
||||||
!!! example
|
!!! example
|
||||||
A technical explanation on how autocorrelation function works along with a practical example is reported [HERE](acf_analysis.md)
|
A technical explanation on how autocorrelation function works along with a practical example is reported [HERE](../acf_analysis.md)
|
||||||
|
|
||||||
## ACF_ID
|
## ACF_ID
|
||||||
`INTEGER` :material-key-outline:{ title="Primary key" } :material-upload-outline:{ title="Auto-increment" }
|
`INTEGER` :material-key-outline:{ title="Primary key" } :material-upload-outline:{ title="Auto-increment" }
|
||||||
@@ -21,8 +21,6 @@ A simple integer to denote the database version.
|
|||||||
|
|
||||||
This field should serve as a writing protection on the database.
|
This field should serve as a writing protection on the database.
|
||||||
|
|
||||||
|
* **-1**: reserved to sigID database. This is the primary way to distinguish a valid sigID database
|
||||||
* **0**: read-only database
|
* **0**: read-only database
|
||||||
* **1**: database can be edited with no restrictions
|
* **1**: database can be edited with no restrictions
|
||||||
|
|
||||||
!!! example "Experimental"
|
|
||||||
This feature is experimental and not yet implemented.
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
With the release of Artemis 4, we have made a significant upgrade in our data management system by transitioning from a CSV file to a full relational SQL database. This change brings a multitude of advantages that enhance the efficiency, scalability, and reliability of our system. In the following sections, we will explore, table by table, the structure of the new database.
|
With the release of Artemis 4, we have made a significant upgrade in our data management system by transitioning from a CSV file to a full relational SQL database. This change brings a multitude of advantages that enhance the efficiency, scalability, and reliability of our system. In the following sections, we will explore, table by table, the structure of the new database.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +1,18 @@
|
|||||||
# SigID Wiki Database
|
# SigID Wiki Database
|
||||||
|
|
||||||
|
<div align="center" markdown>
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
Artemis serves as a valuable resource for both personal signal collection and leveraging a vast repository of pre-identified signals. This software application allows users to curate their own collections, but its true strength lies in its integration with a comprehensive database of known signals. This database is directly sourced from the [Signal Identification Wiki](https://www.sigidwiki.com/wiki/Signal_Identification_Guide), an open-source resource collaboratively maintained by a global community of radio enthusiasts.
|
Artemis serves as a valuable resource for both personal signal collection and leveraging a vast repository of pre-identified signals. This software application allows users to curate their own collections, but its true strength lies in its integration with a comprehensive database of known signals. This database is directly sourced from the [Signal Identification Wiki](https://www.sigidwiki.com/wiki/Signal_Identification_Guide), an open-source resource collaboratively maintained by a global community of radio enthusiasts.
|
||||||
|
|
||||||
!!! tip "Database Revision"
|
!!! tip "Database Revision"
|
||||||
For quality control purposes, the database undergoes a rigorous review process before integration into Artemis. This review adheres to established guidelines (**DIANA** crawler, not yet released), ensuring the accuracy and completeness of the information presented to users. The specifics of this review process are outlined in the following section.
|
For quality control purposes, the database undergoes a rigorous review process before integration into Artemis. This review adheres to established [guidelines](https://github.com/AresValley/Artemis-DB), ensuring the accuracy and completeness of the information presented to users. The specifics of this review process are outlined in the following section.
|
||||||
|
|
||||||
## Modulation
|
## Modulation
|
||||||
A good practise (reported also on ) is to write the primary type of modulation (if known) and not all the possible variants. A practical example is reported on [Signal Identification Wiki](https://www.sigidwiki.com/wiki/Signal_Identification_Guide): there is no need to write **8-PSK** or **QPSK**, **PSK** is enough. The Artemis SigID database is provided without any modulation variants included. The recognized modulations are listed below:
|
A good practise (reported also on ) is to write the primary type of modulation (if known) and not all the possible variants. A practical example is reported on [Signal Identification Wiki](https://www.sigidwiki.com/wiki/Signal_Identification_Guide): there is no need to write **8-PSK** or **QPSK**, **PSK** is enough. The Artemis SigID database is provided without any modulation variants included. The recognized modulations are listed below:
|
||||||
18
docs/faq.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
### What about an automatic signal recognition feature?
|
||||||
|
This question has been asked countless times, and for good reason: in many fields, machine learning (ML) has disrupted the way we solve problems, and when applied to the right field, you can get outstanding results. **So why not on signals?**
|
||||||
|
|
||||||
|
Well, the story dates back to at least 2017 when I started discussing it with several people on the rtl-sdr blog: at first, it seemed promising, but like many things, all that glitters is not gold. Let us proceed in order:
|
||||||
|
|
||||||
|
#### ML / Deep Learning approach
|
||||||
|
A machine learning/neural network approach would not be complex (from a technical standpoint) if there were not the problem of the dataframe completeness. To be effective, neural models need to be trained on a large number of entries. The precise number can vary and strongly depends on the nature of data used for training, but commonly, numbers can be tens of thousands of entries or even more, for example. This would not be a significant concern in the case of RF signals because the various encodings that differentiate can be created artificially using for example [Fldigi](http://www.w1hkj.com/). To make these synthetic signals more like a real one, noise and artificial distortions can be added. However, this approach allows us to have a spectrum that ranges only over civilian, non-proprietary, and non-military signals (a little bit more than 150 out of 500, in the best-case scenario). Many efforts have been made in this field and some excellent results have been reported below:
|
||||||
|
|
||||||
|
- [O’Shea, Corgan, and Clancy](https://arxiv.org/pdf/1602.04105) - Training Size: 900000 signals, 11 modulations
|
||||||
|
- [Stefan Scholl](https://arxiv.org/pdf/1906.04459) - Training Size: 120000 signals, 18 modulations
|
||||||
|
- [Stefan Scholl](https://panoradio-sdr.de/automatic-identification-of-160-shortwave-rf-signals-with-deep-learning/) - Training Size: 1.2 million signals, 143 modulations tested in HF
|
||||||
|
|
||||||
|
Therefore, The main point is to have a good quality data frame to train the model; this is not always an easy task for the above reasons. Subsequently, as Stefan Scholl pointed out on his blog, similar modulations (MFSK-32 vs Olivia 16/500) can significantly decrease the effectiveness of discrimination. The quality of reception can also influence the result as well: high SNR cannot always be an ideal point since the reception can be disrupted by interference or fading.
|
||||||
|
|
||||||
|
#### Classical Audio Analysis
|
||||||
|
More classical methods, such as similarity recognition between audio samples (such as using Mel-frequency cepstral coefficients, for example), could be effective, albeit marginally, if applied to the 500 signals audio sample library from sigID wiki. With the same signal encoding, the content of the signal alone can affect the similarity index and, thus, the method's effectiveness. Listening to a signal with the same modulation but encoding different information can significantly decrease the accuracy of signal recognition.
|
||||||
@@ -3,9 +3,12 @@ title: Documentation
|
|||||||
---
|
---
|
||||||
#
|
#
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<iframe width="560" height="315" src="https://www.youtube.com/embed/W_8Y_4FvoHI?si=0hBqRnxnzCUWmTxK" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p align="center" markdown>
|
<p align="center" markdown>
|
||||||
{width="400"}
|
[Artemis Homepage](https://www.aresvalley.com){ .md-button }
|
||||||
{width="400"}
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
**Artemis** is a software designed to assist **radio frequency (RF) signal identification and storage**. It simplifies real-time spectrum analysis by leveraging one of the most extensive and community-driven databases, containing nearly **500 recognized signals**. This comprehensive software solution allows users to collect RF signals with specific parameters such as frequency, bandwidth, modulation, etc. Users can also store spectrum waterfalls, audio samples, and all types of documents for future reference. Artemis provides a robust platform to manage a wide range of RF data with precision and ease.
|
**Artemis** is a software designed to assist **radio frequency (RF) signal identification and storage**. It simplifies real-time spectrum analysis by leveraging one of the most extensive and community-driven databases, containing over **520 recognized signals**. This comprehensive software solution allows users to collect RF signals with specific parameters such as frequency, bandwidth, modulation, etc. Users can also store spectrum waterfalls, audio samples, and all types of documents for future reference. Artemis provides a robust platform to manage a wide range of RF data with precision and ease.
|
||||||
|
|||||||
@@ -7,12 +7,23 @@
|
|||||||
* **macOS** 11+ (Big Sur or later)
|
* **macOS** 11+ (Big Sur or later)
|
||||||
|
|
||||||
## :simple-windows: Windows
|
## :simple-windows: Windows
|
||||||
Just download the installer and follow the guided procedure to complete the installation process.
|
1. Download the installer `Artemis-Windows-x86_64-4.x.x.exe` in the Assets menu from the [:material-download: LATEST RELEASE](https://github.com/AresValley/Artemis/releases) and follow the guided procedure to complete the installation process.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## :simple-linux: Linux
|
## :simple-linux: Linux
|
||||||
Download and extract the tarball archive in a folder of your choice and run the executable `app.bin`.
|
1. On Linux, the xcb plugin is utilized to supply the essential functionality required for Qt GUI and Qt Widgets to operate on [X11](https://doc.qt.io/qt-6/linux-requirements.html). On some Linux distributions the required dependencies are already met, but in many cases, you will need to install them. To install the dependencies use:
|
||||||
|
|
||||||
|
``` bash title="Debian-based distro (Ubuntu, Mint, Pop! OS, Kali, ...)"
|
||||||
|
sudo apt install libxcb-*
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Download `Artemis-Linux-x86_64-4.x.x.tar` in the Assets menu from the [:material-download: LATEST RELEASE](https://github.com/AresValley/Artemis/releases) and extract the tarball archive in a folder of your choice.
|
||||||
|
3. Before running `app.bin`, be sure to have the executable permissions to the binary file with:
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod 700 app.bin
|
||||||
|
```
|
||||||
|
|
||||||
### Create a Shortcut
|
### Create a Shortcut
|
||||||
|
|
||||||
@@ -31,8 +42,12 @@ This script will:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## :simple-apple: Mac OS
|
## :simple-apple: Mac OS
|
||||||
|
!!! warning
|
||||||
|
The macOS support is temporarily limited!
|
||||||
|
|
||||||
The support for the macOS compiled version of the program is temporarily limited due to a lack of machines for extensive testing. To use Artemis on a macOS device, you have the following options:
|
The support for the macOS compiled version of the program is temporarily limited due to a lack of machines for extensive testing. To use Artemis on a macOS device, you have the following options:
|
||||||
|
|
||||||
* **Run the program directly from the source:** Follow the instructions provided in [this chapter](run_from_source.md) to launch the program from the source code.
|
* **Run the program directly from the source:** Follow the instructions provided in [this chapter](run_from_source.md) to launch the program from the source code.
|
||||||
* **Compile the Artemis 4 binaries on your machine:** In this case, you can contribute by reporting any issues you encounter by [opening an Issue](https://github.com/AresValley/Artemis/issues).
|
* **Compile the Artemis 4 binaries on your machine:** In this case, you can contribute by reporting any issues you encounter by [opening an Issue](https://github.com/AresValley/Artemis/issues).
|
||||||
* **Use the last available compiled version (3.2.1):** Although this version is no longer officially supported, it remains available for use.
|
* **Use the last available compiled version (3.2.1):** Although this version is no longer officially supported, it remains available for use: [:material-download: Artemis-3.2.1.dmg](https://www.aresvalley.com/?sdm_process_download=1&download_id=377).
|
||||||
|
|
||||||
|
|||||||
@@ -27,3 +27,18 @@ Running Artemis directly from the source code using the Python interpreter is co
|
|||||||
```
|
```
|
||||||
pyside6-rcc ./artemis.qrc -o artemis/resources.py
|
pyside6-rcc ./artemis.qrc -o artemis/resources.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Folders Structure
|
||||||
|
Artemis can be safely executed and/or installed in any folder (even protected ones, such as `Program Files (x86)` in Windows) because Artemis performs read-only operations in the `BASE_DIR` folder from where it runs. All the reading-writing operations (such as database ops, logging, etc.) are performed in standard folders as follow:
|
||||||
|
|
||||||
|
### :simple-windows: Windows
|
||||||
|
* Data, Cache, Configurations: `$USER\AppData\Local\AresValley\Artemis`
|
||||||
|
* Logs: `$USER\AppData\Local\Temp`
|
||||||
|
|
||||||
|
### :simple-linux: Linux
|
||||||
|
* Data, Cache, Configurations: `~/.local/share/AresValley/Artemis`
|
||||||
|
* Logs: `/tmp`
|
||||||
|
|
||||||
|
### :simple-apple: Mac OS
|
||||||
|
* Data, Cache, Configurations: `~/Library/Application Support/AresValley/Artemis`
|
||||||
|
* Logs: `/tmp`
|
||||||
|
|||||||
37
docs/space_weather/SSA.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Solar Synoptic Analysis
|
||||||
|
Synoptic maps of the solar surface are drawn each day by SWPC forecasters, providing forecasters with a broad outline of solar surface features. These maps were started on June 2, 1972 and have been produced daily since then.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
!!! quote "Source"
|
||||||
|
The Solar Synoptic Analysis is courtesy of the Space Weather Prediction Center (SWPC) part of the National Oceanic and Atmospheric Administration (NOAA), located in Boulder, Colorado.
|
||||||
|
|
||||||
|
## How to Read
|
||||||
|
|
||||||
|
### Active Regions
|
||||||
|
Active regions are localized magnetic fields on the Sun. Areas with strong or intense magnetic fields provide energy for solar flares and coronal mass ejections (CMEs), so accurate forecasting of space weather activity requires an accurate picture of these regions.
|
||||||
|
**Active regions are given official numbers by SWPC, and the drawings include the probabilities of C, M, and X class flares for the next 24 hours associated with each active region, along with a proton event probability.**
|
||||||
|
|
||||||
|
### Coronal Holes
|
||||||
|
Coronal holes are single polarity magnetic regions that are the source of high speed solar winds which drive magnetospheric activity. Coronal holes are the most common cause of geomagnetic storms. Coronal holes have historically been identified from He I 10830A ground-based observations. **The boundaries of coronal holes are shown on the synoptic drawings as lines with hash marks on the coronal hole side of the boundary line.**
|
||||||
|
|
||||||
|
### Neutral Lines
|
||||||
|
Large magnetic field structures of one magnetic polarity have a ‘neutral line’ at the boundary of the different magnetic polarities of the fields. Neutral lines are associated with flaring in active regions, and filaments/prominences are often associated with the neutral lines on a quiet sun. **Neutral lines appear as dashed lines on the synoptic drawings and the forecaster indicates the polarity of the magnetic field on either side of the neutral line with + (positive) and – (negative) signs.**
|
||||||
|
|
||||||
|
### Plages
|
||||||
|
Plages make up most of an Active Region, and appear bright in conjunction with the dark sunspots. Plages have strong magnetic fields but disorganized magnetic fields, unlike the highly organized fields of sunspots. **In the synoptic drawings, plages are colored red.** It is quite normal to have regions of plage with no sunspots, which do not receive an official number since they are not considered active regions and are unlikely to produce solar flares.
|
||||||
|
|
||||||
|
### Filaments and Prominences
|
||||||
|
Highly-stable regions of high density gas in the low density corona are called filaments. When these occur near the limb and can be seen protruding from the corona, often in spectacular fashion, they are called prominences. When they erupt they can be a geomagnetic storm threat, but the eruptions are usually slow and don’t often drive large storms. **The filaments and prominences are drawn as outlines with hash marks.**
|
||||||
|
|
||||||
|
## Solar Coordinates
|
||||||
|
- Lt: The current Carrington longitude line (north to south) at solar center disk.
|
||||||
|
- Bt: Referred to as the B-angle. The angle measured from the current position of Earth within its tilted (inclined) orbit, compared to the Sun’s equator. It ranges between + or – 7.23 degrees.
|
||||||
|
- Pt: Referred to as the position angle. Essentially, the current angle between Earth’s geocentric north pole and the Sun’s rotational north pole. The range is between + or – 26.31 degrees.
|
||||||
|
- Returning Carringtons: Refers to the next 3 days of returning Carrington longitude lines to the east limb of the Sun.
|
||||||
|
|
||||||
|
## Spot Group Labeling
|
||||||
|
Spot groups are labeled with their assigned NOAA SWPC number. Underneath that number are four probability numbers (from 1 to 100 %) for C-class flares, M-class flares, X-class flares, and energetic proton events.
|
||||||
|
|
||||||
|
## Coronal Hole Labeling
|
||||||
|
Coronal holes are labeled with their assigned number. Underneath that number is a plus or minus sign representing the polarity of the coronal hole. Beside that figure is a number from 1 to 4 representing confidence of coronal hole analysis (4=good; 3=fair; 2=poor; 1=uncertain).
|
||||||
9
docs/space_weather/aurora.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This short-term aurora forecast predicts its location and intensity over the next 30 to 90 minutes, based on the [OVATION model](https://www.swpc.noaa.gov/products/aurora-30-minute-forecast). The lead time of the forecast corresponds to the duration it takes for the solar wind to travel from the L1 observation point to Earth.
|
||||||
|
|
||||||
|
Auroras indicate current geomagnetic storm conditions and provide situational awareness for various technologies: for example, they directly affect HF radio communication and GPS/GNSS satellite navigation and are related to ground-induced currents impacting electric power transmission.
|
||||||
|
|
||||||
|
For many people, the aurora is a beautiful nighttime phenomenon that is worth traveling to arctic regions just to observe. It is the only way for most people to actually experience space weather.
|
||||||
225
docs/space_weather/current.md
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 1. Kp Index
|
||||||
|
The **K index** is a number (from 0 to 9) that shows how much Earth's magnetic field is disturbed. A K index of 1 means things are calm, while a K index of 5 or higher indicates a geomagnetic storm. These disturbances are measured with magnetometers that track changes in Earth's magnetic field every three hours. The K itself comes from a German word "Kennziffer" meaning "characteristic digit". To get a big picture of what's happening around the world, an official planetary **Kp index** is calculated. This is done by averaging the K indices from a special network of 13 geomagnetic observatories located around the globe at mid-latitudes.
|
||||||
|
|
||||||
|
|Index|Activity Level|High Latitudes|Low Latitudes|Possible Source|
|
||||||
|
|-|-|-|-|-|
|
||||||
|
|**Kp 0**|Inactive|Weak & slow aurora possible|Aurora extremely unlikely|Small influx of particles due to some reconnections mostly at the magnetotail|
|
||||||
|
|**Kp 1**|Very Quiet|Weak & slow aurora likely|Aurora very unlikely|Vide supra|
|
||||||
|
|**Kp 2**|Quiet|Moderate auroral display|Aurora unlikely|Vide supra|
|
||||||
|
|**Kp 3**|Unsettled|Active auroral display, sporadic substorm possible|Weak aurora display possible|Coronal hole sending fast winds or remains after days of storming, enhanced solar wind|
|
||||||
|
|**Kp 4**|Active|Active auroral display, multiple sporadic substorms possible|Weak Aurora Display Possible|Vide supra|
|
||||||
|
|**Kp 5**|Minor Storm (G1)|Very active auroral display, multiple substorms likely|Aurora display likely|Coronal hole sending fast winds or coronal mass ejection (CME), enhanced solar wind|
|
||||||
|
|**Kp 6**|Moderate Storm (G2)|Strong auroral display, longer substorms|Active auroral display very likely|Vide supra|
|
||||||
|
|**Kp 7**|Strong Storm (G3)|Very strong auroral display|Strong auroral display extremely likely|Large CMEs caused by solar storms or flares, very enhanced solar wind with strong shock wave|
|
||||||
|
|**Kp 8**|Severe Storm (G4)|Extremely strong aurora, long periods of substorming|Strong auroral display extremely likely|Vide supra|
|
||||||
|
|**Kp 9**|Extreme Storm (G5)|Extremely strong aurora, long periods of substorming|Very strong auroral display, overhead aurora possible|Super CMEs, Carrington-class events, devastating solar wind with extreme shock waves|
|
||||||
|
|
||||||
|
## 2. Ap Index
|
||||||
|
The **A index** represents the three-hourly equivalent amplitude of geomagnetic activity at a specific magnetometer station, derived from the station-specific K index. Due to the quasi-logarithmic nature of the K-scale in relation to magnetometer fluctuations, directly averaging a set of K indices is not really meaningful. Instead each K is converted back into a linear scale. The **Ap index** is determined by averaging the eight daily Ap values (3-hour) and using the same stations grid explained for the Kp index in the previous section. This provides a measure of geomagnetic activity for a specific day. Days with higher levels of geomagnetic activity correspond to higher daily Ap values.
|
||||||
|
|
||||||
|
## 3. NOAA Space Weather Scale
|
||||||
|
|
||||||
|
### Geomagnetic Storm
|
||||||
|
|
||||||
|
??? danger "G5 (Extreme)"
|
||||||
|
* **Physical measure:** Kp = 9
|
||||||
|
* **Average Frequency:** 4 days per cycle (11 years)
|
||||||
|
|
||||||
|
**Power systems:** Widespread voltage control problems and protective system problems can occur, some grid systems may experience complete collapse or blackouts. Transformers may experience damage.
|
||||||
|
|
||||||
|
**Spacecraft operations:** May experience extensive surface charging, problems with orientation, uplink/downlink and tracking satellites.
|
||||||
|
|
||||||
|
**Other systems:** Pipeline currents can reach hundreds of amps, HF (high frequency) radio propagation may be impossible in many areas for one to two days, satellite navigation may be degraded for days, low-frequency radio navigation can be out for hours, and aurora has been seen as low as Florida and southern Texas (typically 40° geomagnetic lat.).
|
||||||
|
|
||||||
|
??? danger "G4 (Severe)"
|
||||||
|
* **Physical measure:** Kp = 8 to 9-
|
||||||
|
* **Average Frequency:** 60 days per cycle (11 years)
|
||||||
|
|
||||||
|
**Power systems:** Possible widespread voltage control problems and some protective systems will mistakenly trip out key assets from the grid.
|
||||||
|
|
||||||
|
**Spacecraft operations:** May experience surface charging and tracking problems, corrections may be needed for orientation problems.
|
||||||
|
|
||||||
|
**Other systems:** Induced pipeline currents affect preventive measures, HF radio propagation sporadic, satellite navigation degraded for hours, low-frequency radio navigation disrupted, and aurora has been seen as low as Alabama and northern California (typically 45° geomagnetic lat.).
|
||||||
|
|
||||||
|
??? warning "G3 (Strong)"
|
||||||
|
* **Physical measure:** Kp = 7
|
||||||
|
* **Average Frequency:** 130 days per cycle (11 years)
|
||||||
|
|
||||||
|
**Power systems:** Voltage corrections may be required, false alarms triggered on some protection devices.
|
||||||
|
|
||||||
|
**Spacecraft operations:** Surface charging may occur on satellite components, drag may increase on low-Earth-orbit satellites, and corrections may be needed for orientation problems.
|
||||||
|
|
||||||
|
**Other systems:** Intermittent satellite navigation and low-frequency radio navigation problems may occur, HF radio may be intermittent, and aurora has been seen as low as Illinois and Oregon (typically 50° geomagnetic lat.).
|
||||||
|
|
||||||
|
??? warning "G2 (Moderate)"
|
||||||
|
* **Physical measure:** Kp = 6
|
||||||
|
* **Average Frequency:** 360 days per cycle (11 years)
|
||||||
|
|
||||||
|
**Power systems:** High-latitude power systems may experience voltage alarms, long-duration storms may cause transformer damage.
|
||||||
|
|
||||||
|
**Spacecraft operations:** Corrective actions to orientation may be required by ground control; possible changes in drag affect orbit predictions.
|
||||||
|
|
||||||
|
**Other systems:** HF radio propagation can fade at higher latitudes, and aurora has been seen as low as New York and Idaho (typically 55° geomagnetic lat.).
|
||||||
|
|
||||||
|
??? info "G1 (Minor)"
|
||||||
|
* **Physical measure:** Kp = 5
|
||||||
|
* **Average Frequency:** 900 days per cycle (11 years)
|
||||||
|
|
||||||
|
**Power systems:** Weak power grid fluctuations can occur.
|
||||||
|
|
||||||
|
**Spacecraft operations:** Minor impact on satellite operations possible.
|
||||||
|
|
||||||
|
**Other systems:** Migratory animals are affected at this and higher levels; aurora is commonly visible at high latitudes (northern Michigan and Maine).
|
||||||
|
|
||||||
|
### Solar Radiation Storms
|
||||||
|
|
||||||
|
??? danger "S5 (Extreme)"
|
||||||
|
* **Physical measure:** Flux level of $\ge 10 MeV$ particles = $10^5$
|
||||||
|
* **Average Frequency:** < 1 event per cycle (11 years)
|
||||||
|
|
||||||
|
**Biological:** Unavoidable high radiation hazard to astronauts on EVA (extra-vehicular activity); passengers and crew in high-flying aircraft at high latitudes may be exposed to radiation risk.
|
||||||
|
|
||||||
|
**Satellite operations:** Satellites may be rendered useless, memory impacts can cause loss of control, may cause serious noise in image data, star-trackers may be unable to locate sources; permanent damage to solar panels possible.
|
||||||
|
|
||||||
|
**Other systems:** Complete blackout of HF (high frequency) communications possible through the polar regions, and position errors make navigation operations extremely difficult.
|
||||||
|
|
||||||
|
??? danger "S4 (Severe)"
|
||||||
|
* **Physical measure:** Flux level of $\ge 10 MeV$ particles = $10^4$
|
||||||
|
* **Average Frequency:** 3 events per cycle (11 years)
|
||||||
|
|
||||||
|
**Biological:** Unavoidable radiation hazard to astronauts on EVA; passengers and crew in high-flying aircraft at high latitudes may be exposed to radiation risk.
|
||||||
|
|
||||||
|
**Satellite operations:** May experience memory device problems and noise on imaging systems; star-tracker problems may cause orientation problems, and solar panel efficiency can be degraded.
|
||||||
|
|
||||||
|
**Other systems:** Blackout of HF radio communications through the polar regions and increased navigation errors over several days are likely.
|
||||||
|
|
||||||
|
??? warning "S3 (Strong)"
|
||||||
|
* **Physical measure:** Flux level of $\ge 10 MeV$ particles = $10^3$
|
||||||
|
* **Average Frequency:** 10 events per cycle (11 years)
|
||||||
|
|
||||||
|
**Biological:** Radiation hazard avoidance recommended for astronauts on EVA; passengers and crew in high-flying aircraft at high latitudes may be exposed to radiation risk.
|
||||||
|
|
||||||
|
**Satellite operations:** Single-event upsets, noise in imaging systems, and slight reduction of efficiency in solar panel are likely.
|
||||||
|
|
||||||
|
**Other systems:** Degraded HF radio propagation through the polar regions and navigation position errors likely.
|
||||||
|
|
||||||
|
??? warning "S2 (Moderate)"
|
||||||
|
* **Physical measure:** Flux level of $\ge 10 MeV$ particles = $10^2$
|
||||||
|
* **Average Frequency:** 25 events per cycle (11 years)
|
||||||
|
|
||||||
|
**Biological:** Passengers and crew in high-flying aircraft at high latitudes may be exposed to elevated radiation risk.
|
||||||
|
|
||||||
|
**Satellite operations:** Infrequent single-event upsets possible.
|
||||||
|
|
||||||
|
**Other systems:** Small effects on HF propagation through the polar regions and navigation at polar cap locations possibly affected.
|
||||||
|
|
||||||
|
??? info "S1 (Minor)"
|
||||||
|
* **Physical measure:** Flux level of $\ge 10 MeV$ particles = $10$
|
||||||
|
* **Average Frequency:** 50 events per cycle (11 years)
|
||||||
|
|
||||||
|
**Biological:** None.
|
||||||
|
|
||||||
|
**Satellite operations:** None.
|
||||||
|
|
||||||
|
**Other systems:** Minor impacts on HF radio in the polar regions.
|
||||||
|
|
||||||
|
### Radio Blackouts
|
||||||
|
|
||||||
|
??? danger "R5 (Extreme)"
|
||||||
|
* **Physical measure:*** X20 ($2e^{-3} Wm^{-2}$)
|
||||||
|
* **Average Frequency:** < 1 days per cycle (11 years)
|
||||||
|
|
||||||
|
**HF Radio:** Complete HF (high frequency**) radio blackout on the entire sunlit side of the Earth lasting for a
|
||||||
|
number of hours. This results in no HF radio contact with mariners and en route aviators in this sector.
|
||||||
|
|
||||||
|
**Navigation:** Low-frequency navigation signals used by maritime and general aviation systems experience outages
|
||||||
|
on the sunlit side of the Earth for many hours, causing loss in positioning. Increased satellite navigation errors in
|
||||||
|
positioning for several hours on the sunlit side of Earth, which may spread into the night side.
|
||||||
|
|
||||||
|
*GOES X-ray peak brightness by class and by flux (measured in the 0.1-0.8 nm range, in W·m-2)
|
||||||
|
|
||||||
|
??? danger "R4 (Severe)"
|
||||||
|
* **Physical measure:*** X10 ($10^{-3} Wm^{-2}$)
|
||||||
|
* **Average Frequency:** 8 days per cycle (11 years)
|
||||||
|
|
||||||
|
**HF Radio:** HF radio communication blackout on most of the sunlit side of Earth for one to two hours. HF radio
|
||||||
|
contact lost during this time.
|
||||||
|
|
||||||
|
**Navigation:** Outages of low-frequency navigation signals cause increased error in positioning for one to two
|
||||||
|
hours. Minor disruptions of satellite navigation possible on the sunlit side of Earth.
|
||||||
|
|
||||||
|
*GOES X-ray peak brightness by class and by flux (measured in the 0.1-0.8 nm range, in W·m-2)
|
||||||
|
|
||||||
|
??? warning "R3 (Strong)"
|
||||||
|
* **Physical measure:*** X1 ($10^{-4} Wm^{-2}$)
|
||||||
|
* **Average Frequency:** 140 days per cycle (11 years)
|
||||||
|
|
||||||
|
**HF Radio:** Wide area blackout of HF radio communication, loss of radio contact for about an hour on sunlit side
|
||||||
|
of Earth.
|
||||||
|
|
||||||
|
**Navigation:** Low-frequency navigation signals degraded for about an hour.
|
||||||
|
|
||||||
|
*GOES X-ray peak brightness by class and by flux (measured in the 0.1-0.8 nm range, in W·m-2)
|
||||||
|
|
||||||
|
??? warning "R2 (Moderate)"
|
||||||
|
* **Physical measure:*** M5 ($5e^{-5} Wm^{-2}$)
|
||||||
|
* **Average Frequency:** 300 days per cycle (11 years)
|
||||||
|
|
||||||
|
**HF Radio:** Limited blackout of HF radio communication on sunlit side of the Earth, loss of radio contact for tens
|
||||||
|
of minutes.
|
||||||
|
|
||||||
|
**Navigation:** Degradation of low-frequency navigation signals for tens of minutes.
|
||||||
|
|
||||||
|
*GOES X-ray peak brightness by class and by flux (measured in the 0.1-0.8 nm range, in W·m-2)
|
||||||
|
|
||||||
|
??? info "R1 (Minor)"
|
||||||
|
* **Physical measure:*** M1 ($10^{-5} Wm^{-2}$)
|
||||||
|
* **Average Frequency:** 950 days per cycle (11 years)
|
||||||
|
|
||||||
|
**HF Radio:** Weak or minor degradation of HF radio communication on sunlit side of the Earth, occasional loss of
|
||||||
|
radio contact.
|
||||||
|
|
||||||
|
**Navigation:** Low-frequency navigation signals degraded for brief intervals.
|
||||||
|
|
||||||
|
*GOES X-ray peak brightness by class and by flux (measured in the 0.1-0.8 nm range, in W·m-2)
|
||||||
|
|
||||||
|
## 4. X-Ray Solar Activity
|
||||||
|
This is a summary of the **X-Ray Flare Class**. Large solar X-ray flares can change the Earth’s ionosphere, which blocks high-frequency (HF) radio transmissions on the sunlit side of the Earth. Solar flares are also associated with Coronal Mass Ejections (CMEs) which can ultimately lead to geomagnetic storms. SWPC sends out space weather alerts at the M5 level. Some large flares are accompanied by strong radio bursts that may interfere with other radio frequencies and cause problems for satellite communication and radio navigation (GPS).
|
||||||
|
|
||||||
|
| Class | Peak Strength (W/m<sup>2</sup>) | Effects on Earth |
|
||||||
|
|-------|---------------------------------|--------------------------------------------------------------|
|
||||||
|
| B | I < 10<sup>-6</sup> | Too small to harm Earth |
|
||||||
|
| C | 10<sup>-6</sup> ≤ I < 10<sup>-5</sup> | Small with few noticeable consequences |
|
||||||
|
| M | 10<sup>-5</sup> ≤ I < 10<sup>-4</sup> | Brief radio blackouts in polar regions, minor radiation storms |
|
||||||
|
| X | I ≥ 10<sup>-4</sup> | Planet-wide radio blackouts, long-lasting radiation storms |
|
||||||
|
|
||||||
|
## 5. RF Propagation
|
||||||
|
|
||||||
|
### Maximum Usable Frequency
|
||||||
|
In radio transmission, the maximum usable frequency (MUF) is the highest frequency that can be effectively used for communication between two locations on Earth by reflecting off the ionosphere (via skywave or skip) at a given time, regardless of the transmitter's power. This measurement is particularly valuable for shortwave transmissions.
|
||||||
|
|
||||||
|
### Earth-Moon-Earth
|
||||||
|
Earth–Moon–Earth communication (EME), commonly referred to as Moon bounce, is a radio communication method in which radio waves are transmitted from an Earth-based station, reflected off the Moon's surface, and then received back on Earth. The value gives the probability of a succesfull connection.
|
||||||
|
|
||||||
|
### Meteor Scatter
|
||||||
|
Meteor burst communications (MBC), also known as meteor scatter (MS) communications, is a radio propagation technique that uses the ionized trails created by meteors entering the atmosphere to establish brief communication links between radio stations up to 2,250 kilometers (1,400 miles) apart. This can involve either forward-scatter or back-scatter of the radio waves. Like EME, the value gives the probability of a succesfull connection.
|
||||||
|
|
||||||
|
### Sporadic-E
|
||||||
|
**Report of the latest E-skip spots on 50, 70 & 144 MHz by [DXrobot](https://dxrobot.gooddx.net/)**
|
||||||
|
|
||||||
|
Sporadic E (Es or SpE) is a rare type of radio propagation that uses a lower part of the Earth's ionosphere, which typically doesn't refract radio waves. It reflects signals off small "clouds" in the E region at altitudes of 95-150 km (50-100 miles). Unlike the regular F region skywave propagation, which depends on daily cycles of ionized layers from UV light, Sporadic E uses transient ionized patches. This allows for occasional long-distance VHF communication, usually during the six weeks around the summer solstice, beyond the normal line-of-sight range.
|
||||||
|
|
||||||
|
### Aurora Spots
|
||||||
|
**Report of the latest aurora spots on 50, 70 & 144 MHz by [DXrobot](https://dxrobot.gooddx.net/)**
|
||||||
|
|
||||||
|
Auroral propagation, or auroral backscatter, is a form of radio propagation that occurs during an auroral event, affecting VHF and UHF communications. Increased ionization in the E layer of the ionosphere reflects signals at much higher frequencies than usual, enabling communication up to 1000 MHz, though 500 MHz is more common. Signals are directed towards the auroral region and reflected back, but they are often distorted due to particle movement (the signal is roughly Doppler shifted of 1 kHz at around 150 MHz as the electrons stream down).
|
||||||
|
|
||||||
|
### Expected HF Noise
|
||||||
|
This is just the expected noise in HF based on the current Space Weather conditions.
|
||||||
|
|
||||||
|
## 6. Report Age
|
||||||
|
Poseidon Daemon is in charge of parse all the necessary data used for the Space Weather module. The data of the last generated report is written here.
|
||||||
7
docs/space_weather/drap.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The D-Region Absorption Product (DRAP) evaluates the effects of solar X-ray flux and solar energetic particle (SEP) events on HF radio communication. Long-distance communications using high frequency (HF) radio waves (3 - 30 MHz) rely on signal reflection in the ionosphere. Typically, radio waves reflect near the peak of the F2 layer (~300 km altitude), but during their journey to and from this peak, the signals experience attenuation due to absorption by the intervening ionosphere.
|
||||||
|
|
||||||
|
The [D-Region Absorption Prediction model](https://www.swpc.noaa.gov/products/d-region-absorption-predictions-d-rap) provides guidance to understand the degradation and blackouts of HF radio communications that can result from these conditions.
|
||||||
15
docs/space_weather/forecasts.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 1. Forecast Summary
|
||||||
|
Geomagnetic activity, Solar Radiation Storms and Radio Blackouts probable events (in the next 3 days) are described in this section.
|
||||||
|
|
||||||
|
## 2. 3-Day Kp Index
|
||||||
|
This is a 3 day projection of the [Kp index](current.md).
|
||||||
|
|
||||||
|
## 3. Events Probability
|
||||||
|
The probability (in percentage) of different events that can take place and generate some important conditions for RF propagation. All the event are explained [here](current.md).
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
Geomagnetic Activity has two percentual value per day: the left one refers to high-latitude location and the right one is for middle-latitude locations.
|
||||||
114
docs/space_weather/imagers.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Sun Imagers
|
||||||
|
|
||||||
|
## Atmospheric Imagery Assembly
|
||||||
|
### 94 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel (as well as AIA 131) is designed to study solar flares. It measures extremely hot temperatures around 6 million Kelvin (10.8 million F). It can take images every 2 seconds (instead of 10) in a reduced field of view in order to look at flares in more detail.
|
||||||
|
|
||||||
|
- **Where:** Flaring regions of the corona
|
||||||
|
- **Wavelength:** 94 angstroms (0.0000000094 m) = Extreme Ultraviolet/soft X-rays
|
||||||
|
- **Primary ions seen:** 17 times ionized iron (Fe XVIII)
|
||||||
|
- **Characteristic temperature:** 6 million K (10.8 million F)
|
||||||
|
|
||||||
|
### 131 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel (as well as AIA 094) is designed to study solar flares. It measures extremely hot temperatures around 10 million K (18 million F), as well as cool plasmas around 400,000 K (720,000 F). It can take images every 2 seconds (instead of 10) in a reduced field of view in order to look at flares in more detail.
|
||||||
|
|
||||||
|
- **Where:** Flaring regions of the corona
|
||||||
|
- **Wavelength:** 131 angstroms (0.0000000131 m) = Extreme Ultraviolet
|
||||||
|
- **Primary ions seen:** 20 and 7 times ionized iron (Fe VIII, Fe XXI)
|
||||||
|
- **Characteristic temperatures:** 10 million K (18 million F)
|
||||||
|
|
||||||
|
### 171 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel is especially good at showing coronal loops - the arcs extending off of the Sun where plasma moves along magnetic field lines. The brightest spots seen here are locations where the magnetic field near the surface is exceptionally strong.
|
||||||
|
|
||||||
|
- **Where:** Quiet corona and upper transition region
|
||||||
|
- **Wavelength:** 171 angstroms (0.0000000171 m) = Extreme Ultraviolet
|
||||||
|
- **Primary ions seen:** 8 times ionized iron (Fe IX)
|
||||||
|
- **Characteristic temperature:** 1 million K (1.8 million F)
|
||||||
|
|
||||||
|
### 193 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel highlights the outer atmosphere of the Sun - called the corona - as well as hot flare plasma. Hot active regions, solar flares, and coronal mass ejections will appear bright here. The dark areas - called coronal holes - are places where very little radiation is emitted, yet are the main source of solar wind particles.
|
||||||
|
|
||||||
|
- **Where:** Corona and hot flare plasma
|
||||||
|
- **Wavelength:** 193 angstroms (0.0000000193 m) = Extreme Ultraviolet
|
||||||
|
- **Primary ions seen:** 11 times ionized iron (Fe XII)
|
||||||
|
- **Characteristic temperature:** 1.25 million K (2.25 million F)
|
||||||
|
|
||||||
|
### 304 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel is especially good at showing areas where cooler dense plumes of plasma (filaments and prominences) are located above the visible surface of the Sun. Many of these features either can't be seen or appear as dark lines in the other channels. The bright areas show places where the plasma has a high density.
|
||||||
|
|
||||||
|
- **Where:** Upper chromosphere and lower transition region
|
||||||
|
- **Wavelength:** 304 angstroms (0.0000000304 m) = Extreme Ultraviolet
|
||||||
|
- **Primary ions seen:** singly ionized helium (He II)
|
||||||
|
- **Characteristic temperature:** 50,000 K (90,000 F)
|
||||||
|
|
||||||
|
### 335 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel (as well as AIA 211) highlights the active region of the outer atmosphere of the Sun - the corona. Active regions, solar flares, and coronal mass ejections will appear bright here. The dark areas - or coronal holes - are places where very little radiation is emitted, yet are the main source of solar wind particles.
|
||||||
|
|
||||||
|
- **Where:** Active regions of the corona
|
||||||
|
- **Wavelength:** 335 angstroms (0.0000000335 m) = Extreme Ultraviolet
|
||||||
|
- **Primary ions seen:** 15 times ionized iron (Fe XVI)
|
||||||
|
- **Characteristic temperature:** 2.8 million K (5 million F)
|
||||||
|
|
||||||
|
### 1600 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel (as well as AIA 1700) often shows a web-like pattern of bright areas that highlight places where bundles of magnetic fields lines are concentrated. However, small areas with a lot of field lines will appear black, usually near sunspots and active regions.
|
||||||
|
|
||||||
|
- **Where:** Transition region and upper photosphere
|
||||||
|
- **Wavelength:** 1600 angstroms (0.00000016 m) = Far Ultraviolet
|
||||||
|
- **Primary ions seen:** thrice ionized carbon (C IV) and Continuum
|
||||||
|
- **Characteristic temperatures:** 6,000 K (11,000 F), and 100,000 K (180,000 F)
|
||||||
|
|
||||||
|
### 1700 Å
|
||||||
|

|
||||||
|
|
||||||
|
This channel (as well as AIA 1600) often shows a web-like pattern of bright areas that highlight places where bundles of magnetic fields lines are concentrated. However, small areas with a lot of field lines will appear black, usually near sunspots and active regions.
|
||||||
|
|
||||||
|
- **Where:** Temperature minimum and photosphere
|
||||||
|
- **Wavelength:** 1700 angstroms (0.00000017 m) = Far Ultraviolet
|
||||||
|
- **Primary ions seen:** Continuum
|
||||||
|
- **Characteristic temperature:** 6,000 K (11,000 F)
|
||||||
|
|
||||||
|
---
|
||||||
|
## Helioseismic and Magnetic Imager
|
||||||
|
### Magnetogram
|
||||||
|

|
||||||
|
|
||||||
|
Magnetograms show maps of the magnetic field on the Sun’s surface. The HMI instrument uses the Zeeman effect to measure the intensity of the magnetic field component along the line of sight by making use of the circularly polarized spectral line. The color chart of the magnetic field along the line of sight is designed to visually show both high and low values. Intensities less than 24G are shades of gray. Positive values of the field are green and blue. Negative values are yellow and red. Regions with a weak field appear mainly in yellow or green. Progressively positive values range from dark green to light green (at 236 G). Negative values range from light yellow to orange (at -236G). There is a strong discontinuity in the coloration at 236G. Positive or negative sunspots and other regions with an intense field appear blue or red with dark umbrae. There are 254 colors arranged symmetrically around 0. The 127 positive values include 2 grays tending toward dark, 13 greens toward light, and 110 blues toward dark. The 127 negative values include 2 grays tending toward light, 18 yellows toward dark, and 107 reds toward dark. Nominally, each color indicates a range of about 11.81 G, and the coloration altogether spans the range between -1500 G and 1500 G
|
||||||
|
|
||||||
|
### Intensitygram
|
||||||
|

|
||||||
|
|
||||||
|
HMI samples the Fe I absorption line at 6173.3 Å at six points, assuming that the "pure" profile of the Fe I line is Gaussian and the transmission profiles are delta functions, the first and second Fourier coefficients of the Fe I line profile can be calculated, and Doppler velocity estimation can be performed. An estimate of the intensity in the continuum is obtained by "reconstructing" the solar line from the Doppler offset and the thickness and depth of the line.
|
||||||
|
|
||||||
|
### Dopplergram
|
||||||
|

|
||||||
|
|
||||||
|
HMI camera 2 takes 72 images to construct a single Doppler diagram. Six images are taken at six positions across the spectral line at 6173.3 Å. Each image is taken in two polarization states, circularly polarized to the right (RCP or Stokes I-V) and circularly polarized to the left (LCP or Stokes I+V). Assuming that the absorption line is Gaussian and the transmission profiles of the HMI filter are delta functions, Fourier coefficients are calculated and then used to estimate the magnetic field B along the line of sight.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Large Angle and Spectrometric Coronagraph
|
||||||
|
### LASCO C1 (NOT AVAILABLE)
|
||||||
|
A Fabry–Pérot interferometer coronagraph imaging from 1.1 to 3 solar radii, non-functional since the 24 June 1998 SOHO Mission Interruption
|
||||||
|
|
||||||
|
### LASCO C2 (orange)
|
||||||
|

|
||||||
|
|
||||||
|
A white light coronagraph imaging from 1.5 to 6 solar radii. The LASCO camera is equipped with an occulting disk that blocks out the Sun to be able to study the faint details in the corona.
|
||||||
|
|
||||||
|
### LASCO C3 (blue)
|
||||||
|

|
||||||
|
|
||||||
|
A white light coronagraph imaging from 3.7 to 30 solar radii
|
||||||
1
images/artemis_not_available.svg
Normal file
|
After Width: | Height: | Size: 20 KiB |
1
images/icons/settings.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M433-80q-27 0-46.5-18T363-142l-9-66q-13-5-24.5-12T307-235l-62 26q-25 11-50 2t-39-32l-47-82q-14-23-8-49t27-43l53-40q-1-7-1-13.5v-27q0-6.5 1-13.5l-53-40q-21-17-27-43t8-49l47-82q14-23 39-32t50 2l62 26q11-8 23-15t24-12l9-66q4-26 23.5-44t46.5-18h94q27 0 46.5 18t23.5 44l9 66q13 5 24.5 12t22.5 15l62-26q25-11 50-2t39 32l47 82q14 23 8 49t-27 43l-53 40q1 7 1 13.5v27q0 6.5-2 13.5l53 40q21 17 27 43t-8 49l-48 82q-14 23-39 32t-50-2l-60-26q-11 8-23 15t-24 12l-9 66q-4 26-23.5 44T527-80h-94Zm49-260q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 690 B |
BIN
images/installer_icon.ico
Normal file
|
After Width: | Height: | Size: 173 KiB |
37
mkdocs.yml
@@ -53,22 +53,31 @@ nav:
|
|||||||
- Installation: 'installation.md'
|
- Installation: 'installation.md'
|
||||||
- Run from source: 'run_from_source.md'
|
- Run from source: 'run_from_source.md'
|
||||||
- Build Package: 'build_package.md'
|
- Build Package: 'build_package.md'
|
||||||
|
- Basic Operations: 'basic_operations.md'
|
||||||
- Database:
|
- Database:
|
||||||
- 'Overview': 'db_overview.md'
|
- Overview: 'database/db_overview.md'
|
||||||
- Structure:
|
- Structure:
|
||||||
- 'Info': 'db_info.md'
|
- Info: 'database/db_info.md'
|
||||||
- 'Signals': 'db_signals.md'
|
- Signals: 'database/db_signals.md'
|
||||||
- 'Frequency': 'db_frequency.md'
|
- Frequency: 'database/db_frequency.md'
|
||||||
- 'Bandwidth': 'db_bandwidth.md'
|
- Bandwidth: 'database/db_bandwidth.md'
|
||||||
- 'Modulation': 'db_modulation.md'
|
- Modulation: 'database/db_modulation.md'
|
||||||
- 'Mode': 'db_mode.md'
|
- Mode: 'database/db_mode.md'
|
||||||
- 'Location': 'db_location.md'
|
- Location: 'database/db_location.md'
|
||||||
- 'Category': 'db_category.md'
|
- Category: 'database/db_category.md'
|
||||||
- 'Category Label': 'db_cat_label.md'
|
- Category Label: 'database/db_cat_label.md'
|
||||||
- 'ACF': 'db_acf.md'
|
- ACF: 'database/db_acf.md'
|
||||||
- SigID: 'sigid.md'
|
- SigID: 'database/sigid.md'
|
||||||
- Example:
|
- Space Weather:
|
||||||
- ACF Analysis: 'acf_analysis.md'
|
- Current: 'space_weather/current.md'
|
||||||
|
- Forecasts: 'space_weather/forecasts.md'
|
||||||
|
- DRAP: 'space_weather/drap.md'
|
||||||
|
- Aurora: 'space_weather/aurora.md'
|
||||||
|
- SSA: 'space_weather/SSA.md'
|
||||||
|
- Sun Imagers: 'space_weather/imagers.md'
|
||||||
|
- Signal Analysis:
|
||||||
|
- Autocorrelation (ACF): 'acf_analysis.md'
|
||||||
|
- FAQ: 'faq.md'
|
||||||
- Contribute: 'contribute.md'
|
- Contribute: 'contribute.md'
|
||||||
- Changelog: https://github.com/AresValley/Artemis/blob/master/CHANGELOG.md
|
- Changelog: https://github.com/AresValley/Artemis/blob/master/CHANGELOG.md
|
||||||
- License & Credits: 'credits.md'
|
- License & Credits: 'credits.md'
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
PySide6==6.7.1
|
PySide6==6.8.0.1
|
||||||
requests==2.32.2
|
requests==2.32.3
|
||||||
packaging==24.0
|
packaging==24.1
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Window
|
import QtQuick.Window
|
||||||
import QtQuick.Controls.Material
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
|
||||||
Dialog {
|
Dialog {
|
||||||
x: (parent.width - width) / 2
|
x: (parent.width - width) / 2
|
||||||
y: (parent.height - height) / 2
|
y: (parent.height - height) / 2
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Window {
|
|||||||
height: 800
|
height: 800
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
title: qsTr("Artemis")
|
title: qsTr("Artemis")
|
||||||
@@ -31,8 +31,9 @@ Window {
|
|||||||
signal showCatManager()
|
signal showCatManager()
|
||||||
signal openSigEditor(string type, var sig_param, bool is_new)
|
signal openSigEditor(string type, var sig_param, bool is_new)
|
||||||
signal showSpaceWeather()
|
signal showSpaceWeather()
|
||||||
signal checkDbUpdates()
|
signal checkForUpdate()
|
||||||
signal startDownloader()
|
signal updateDb()
|
||||||
|
signal updateArtemis()
|
||||||
signal openDbDirectory()
|
signal openDbDirectory()
|
||||||
signal newDb(string name)
|
signal newDb(string name)
|
||||||
signal exportDb(string path)
|
signal exportDb(string path)
|
||||||
@@ -55,8 +56,9 @@ Window {
|
|||||||
listModel.clear()
|
listModel.clear()
|
||||||
for (var i = 0; i < loadedList.length; i++) {
|
for (var i = 0; i < loadedList.length; i++) {
|
||||||
var name = loadedList[i].name.toLowerCase()
|
var name = loadedList[i].name.toLowerCase()
|
||||||
|
var description = loadedList[i].description.toLowerCase()
|
||||||
var search = textFieldSearch.text.toLowerCase()
|
var search = textFieldSearch.text.toLowerCase()
|
||||||
if (name.includes(search)) {
|
if (name.includes(search) || description.includes(search)) {
|
||||||
listModel.append(loadedList[i])
|
listModel.append(loadedList[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,11 +122,12 @@ Window {
|
|||||||
dialogDownloadDb.open()
|
dialogDownloadDb.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
function openDialogDownloadArtemis(messageType, title, message) {
|
function openDialogUpdateArtemis(messageType, title, message, auto) {
|
||||||
dialogDownloadArtemis.messageType = messageType
|
dialogUpdateArtemis.messageType = messageType
|
||||||
dialogDownloadArtemis.title = title
|
dialogUpdateArtemis.title = title
|
||||||
dialogDownloadArtemis.message = message
|
dialogUpdateArtemis.message = message
|
||||||
dialogDownloadArtemis.open()
|
dialogUpdateArtemis.autoUpdate = auto
|
||||||
|
dialogUpdateArtemis.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogMessage {
|
DialogMessage {
|
||||||
@@ -134,18 +137,24 @@ Window {
|
|||||||
standardButtons: Dialog.Cancel | Dialog.Yes
|
standardButtons: Dialog.Cancel | Dialog.Yes
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
startDownloader()
|
updateDb()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DialogMessage {
|
DialogMessage {
|
||||||
id: dialogDownloadArtemis
|
id: dialogUpdateArtemis
|
||||||
modal: true
|
modal: true
|
||||||
|
|
||||||
|
property bool autoUpdate
|
||||||
|
|
||||||
standardButtons: Dialog.Cancel | Dialog.Yes
|
standardButtons: Dialog.Cancel | Dialog.Yes
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
Qt.openUrlExternally("https://github.com/AresValley/Artemis")
|
if (autoUpdate) {
|
||||||
|
updateArtemis();
|
||||||
|
} else {
|
||||||
|
Qt.openUrlExternally("https://github.com/AresValley/Artemis");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,13 +178,16 @@ Window {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
Label {
|
||||||
|
text: qsTr("Enter the name of the new database:")
|
||||||
|
Layout.bottomMargin: 15
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: newDbName
|
id: newDbName
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("New DB Name")
|
placeholderText: qsTr("Name")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
@@ -211,9 +223,13 @@ Window {
|
|||||||
|
|
||||||
Page {
|
Page {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
leftPadding: 5
|
leftPadding: 10
|
||||||
rightPadding: 5
|
bottomPadding: 10
|
||||||
bottomPadding: 5
|
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
Keys.onDownPressed: listView.incrementCurrentIndex()
|
||||||
|
Keys.onUpPressed: listView.decrementCurrentIndex()
|
||||||
|
|
||||||
header: MenuBar {
|
header: MenuBar {
|
||||||
id: topBar
|
id: topBar
|
||||||
@@ -312,11 +328,16 @@ Window {
|
|||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Check for Updates"
|
text: "Check for Updates"
|
||||||
onClicked: {checkDbUpdates()}
|
onClicked: {checkForUpdate()}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuSeparator {}
|
MenuSeparator {}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: "Project Homepage"
|
||||||
|
onClicked: {Qt.openUrlExternally('https://aresvalley.com/')}
|
||||||
|
}
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: "Documentation"
|
text: "Documentation"
|
||||||
onClicked: {Qt.openUrlExternally('https://AresValley.github.io/Artemis')}
|
onClicked: {Qt.openUrlExternally('https://AresValley.github.io/Artemis')}
|
||||||
@@ -355,16 +376,18 @@ Window {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 20
|
spacing: 10
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.maximumWidth: 250
|
Layout.maximumWidth: 250
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
id: textFieldSearch
|
id: textFieldSearch
|
||||||
|
Layout.preferredHeight: 39
|
||||||
|
Layout.topMargin: 5
|
||||||
enabled: false
|
enabled: false
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 10
|
|
||||||
placeholderText: qsTr("Search")
|
placeholderText: qsTr("Search")
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
refreshList()
|
refreshList()
|
||||||
@@ -379,7 +402,6 @@ Window {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
clip: true
|
clip: true
|
||||||
focus: true
|
|
||||||
ScrollBar.vertical: bar
|
ScrollBar.vertical: bar
|
||||||
highlight: Rectangle { color: Material.accent; radius: 5 }
|
highlight: Rectangle { color: Material.accent; radius: 5 }
|
||||||
onCurrentIndexChanged: { itemChangedList() }
|
onCurrentIndexChanged: { itemChangedList() }
|
||||||
@@ -413,16 +435,14 @@ Window {
|
|||||||
|
|
||||||
TabBar {
|
TabBar {
|
||||||
id: tabBar
|
id: tabBar
|
||||||
width: parent.width
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
TabButton {
|
TabButton {
|
||||||
text: qsTr("Signal")
|
text: qsTr("SIGNAL")
|
||||||
}
|
}
|
||||||
|
|
||||||
TabButton {
|
TabButton {
|
||||||
text: qsTr("Filter")
|
text: qsTr("FILTERS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ Window {
|
|||||||
height: 400
|
height: 400
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
@@ -41,14 +41,6 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModel() {
|
|
||||||
var modelList = []
|
|
||||||
for (var i = 0; i < myModel.count; i++) {
|
|
||||||
modelList.push(myModel.get(i).value)
|
|
||||||
}
|
|
||||||
return modelList
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAll() {
|
function clearAll() {
|
||||||
myModel.clear()
|
myModel.clear()
|
||||||
}
|
}
|
||||||
@@ -79,11 +71,15 @@ Window {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
Label {
|
||||||
|
text: qsTr("Enter the name of the new tag:")
|
||||||
|
Layout.bottomMargin: 15
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: newCatName
|
id: newCatName
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("Tag")
|
placeholderText: qsTr("Tag Name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +101,15 @@ Window {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
Label {
|
||||||
|
text: qsTr("Enter the new name for the tag:")
|
||||||
|
Layout.bottomMargin: 15
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: renameCatName
|
id: renameCatName
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("Tag")
|
placeholderText: qsTr("Tag Name")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Controls.Material
|
import QtQuick.Controls.Material
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: windowDBmanager
|
id: windowDBmanager
|
||||||
|
|
||||||
@@ -32,6 +33,8 @@ Window {
|
|||||||
if (selected_db !== undefined) {
|
if (selected_db !== undefined) {
|
||||||
lockMenu(false)
|
lockMenu(false)
|
||||||
titleLabel.text = myModel.get(listView.currentIndex).name
|
titleLabel.text = myModel.get(listView.currentIndex).name
|
||||||
|
versionLabel.text = 'VERSION ' + myModel.get(listView.currentIndex).version
|
||||||
|
dateLabel.text = myModel.get(listView.currentIndex).date
|
||||||
totDocsLabel.text = myModel.get(listView.currentIndex).documents_n
|
totDocsLabel.text = myModel.get(listView.currentIndex).documents_n
|
||||||
totSignalsLabel.text = myModel.get(listView.currentIndex).signals_n
|
totSignalsLabel.text = myModel.get(listView.currentIndex).signals_n
|
||||||
totImagesLabel.text = myModel.get(listView.currentIndex).images_n
|
totImagesLabel.text = myModel.get(listView.currentIndex).images_n
|
||||||
@@ -41,16 +44,10 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getModel() {
|
|
||||||
var modelList = []
|
|
||||||
for (var i = 0; i < myModel.count; i++) {
|
|
||||||
modelList.push(myModel.get(i).name)
|
|
||||||
}
|
|
||||||
return modelList
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearAll() {
|
function clearAll() {
|
||||||
titleLabel.text = 'N/A'
|
titleLabel.text = 'N/A'
|
||||||
|
versionLabel.text = ''
|
||||||
|
dateLabel.text = ''
|
||||||
totDocsLabel.text = ''
|
totDocsLabel.text = ''
|
||||||
totSignalsLabel.text = ''
|
totSignalsLabel.text = ''
|
||||||
totImagesLabel.text = ''
|
totImagesLabel.text = ''
|
||||||
@@ -62,7 +59,6 @@ Window {
|
|||||||
loadDB(myModel.get(listView.currentIndex).db_dir_name)
|
loadDB(myModel.get(listView.currentIndex).db_dir_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function renameDb() {
|
function renameDb() {
|
||||||
if (textDBName.readOnly) {
|
if (textDBName.readOnly) {
|
||||||
textDBName.focus = true
|
textDBName.focus = true
|
||||||
@@ -120,7 +116,11 @@ Window {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
Label {
|
||||||
|
text: qsTr("Enter the new for the database:")
|
||||||
|
Layout.bottomMargin: 15
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
TextField {
|
TextField {
|
||||||
id: newDbName
|
id: newDbName
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -179,11 +179,16 @@ Window {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: titleLabel
|
id: titleLabel
|
||||||
Layout.bottomMargin: 20
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
font.pointSize: 15
|
font.pointSize: 15
|
||||||
font.bold: true
|
font.bold: true
|
||||||
}
|
}
|
||||||
|
Label {
|
||||||
|
id: versionLabel
|
||||||
|
Layout.bottomMargin: 20
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
font.pointSize: 9
|
||||||
|
}
|
||||||
GridLayout {
|
GridLayout {
|
||||||
columnSpacing: 25
|
columnSpacing: 25
|
||||||
columns: 2
|
columns: 2
|
||||||
@@ -235,6 +240,19 @@ Window {
|
|||||||
text: qsTr("0")
|
text: qsTr("0")
|
||||||
font.pointSize: 12
|
font.pointSize: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.topMargin: 20
|
||||||
|
text: qsTr("DB Created:")
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: dateLabel
|
||||||
|
Layout.topMargin: 20
|
||||||
|
text: qsTr("")
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Window {
|
|||||||
height: 500
|
height: 500
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
@@ -82,18 +82,6 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function contentChanged() {
|
|
||||||
if (listView.currentIndex !== -1) {
|
|
||||||
myModel.set(
|
|
||||||
listView.currentIndex,
|
|
||||||
{
|
|
||||||
'name': nameField.text,
|
|
||||||
'description': descriptionField.text,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lockMenu(toggle) {
|
function lockMenu(toggle) {
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
openButton.enabled = false
|
openButton.enabled = false
|
||||||
@@ -167,9 +155,9 @@ Window {
|
|||||||
id: fileDialog
|
id: fileDialog
|
||||||
title: "Please choose a file"
|
title: "Please choose a file"
|
||||||
nameFilters: [
|
nameFilters: [
|
||||||
"Image (*.jpg *.png)",
|
"Image (*.png *.jpg *.jpeg *.gif *.bmp *.tiff *.tif *.webp *.svg *.heic *.raw *.cr2 *.nef *.orf *.sr2 *.arw *.dng)",
|
||||||
"Audio (*.mp3 *.m4a *.ogg)",
|
"Audio (*.mp3 *.wav *.aac *.flac *.alac *.wma *.ogg *.m4a *.aiff *.aif *.amr *.opus *.mid *.midi *.pcm)",
|
||||||
"Document (*.txt *.pdf)",
|
"Document (*.doc *.docx *.pdf *.txt *.rtf *.odt *.html *.htm *.xml *.ppt *.pptx *.xls *.xlsx *.csv *.epub *.mobi *.md *.tex *.wps)",
|
||||||
"All files (*)"
|
"All files (*)"
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -236,16 +224,18 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
Flickable {
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
ScrollBar.vertical.interactive: true
|
Layout.fillHeight: true
|
||||||
|
TextArea.flickable: TextArea {
|
||||||
TextArea {
|
|
||||||
id: newDescriptionField
|
id: newDescriptionField
|
||||||
placeholderText: qsTr("Description")
|
placeholderText: qsTr("Description")
|
||||||
|
font.pointSize: 10
|
||||||
wrapMode: TextEdit.WordWrap
|
wrapMode: TextEdit.WordWrap
|
||||||
}
|
}
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
width: 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,16 +283,18 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
Flickable {
|
||||||
Layout.fillHeight: true
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
ScrollBar.vertical.interactive: true
|
Layout.fillHeight: true
|
||||||
|
TextArea.flickable: TextArea {
|
||||||
TextArea {
|
|
||||||
id: editDescriptionField
|
id: editDescriptionField
|
||||||
placeholderText: qsTr("Description")
|
placeholderText: qsTr("Description")
|
||||||
|
font.pointSize: 10
|
||||||
wrapMode: TextEdit.WordWrap
|
wrapMode: TextEdit.WordWrap
|
||||||
}
|
}
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
width: 10
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +372,6 @@ Window {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
clip: true
|
clip: true
|
||||||
focus: true
|
|
||||||
ScrollBar.vertical: bar
|
ScrollBar.vertical: bar
|
||||||
highlight: Rectangle { color: Material.accent; radius: 5 }
|
highlight: Rectangle { color: Material.accent; radius: 5 }
|
||||||
onCurrentIndexChanged: { itemChanged() }
|
onCurrentIndexChanged: { itemChanged() }
|
||||||
@@ -446,9 +437,7 @@ Window {
|
|||||||
id: nameField
|
id: nameField
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
placeholderText: qsTr("Name")
|
placeholderText: qsTr("Name")
|
||||||
onTextChanged: {
|
readOnly: true
|
||||||
contentChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextField {
|
TextField {
|
||||||
@@ -458,18 +447,18 @@ Window {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
Flickable {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
ScrollBar.vertical.interactive: true
|
TextArea.flickable: TextArea {
|
||||||
|
|
||||||
TextArea {
|
|
||||||
id: descriptionField
|
id: descriptionField
|
||||||
wrapMode: TextEdit.WordWrap
|
placeholderText: qsTr("Description")
|
||||||
|
readOnly: true
|
||||||
font.pointSize: 10
|
font.pointSize: 10
|
||||||
onTextChanged: {
|
wrapMode: TextEdit.WordWrap
|
||||||
contentChanged()
|
}
|
||||||
}
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
width: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import QtQuick.Controls
|
|||||||
import QtQuick.Controls.Material
|
import QtQuick.Controls.Material
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: windowDownloader
|
id: windowDownloader
|
||||||
|
|
||||||
@@ -23,6 +24,23 @@ Window {
|
|||||||
|
|
||||||
signal onAbort()
|
signal onAbort()
|
||||||
|
|
||||||
|
onClosing: {
|
||||||
|
onAbort()
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateProgressBar(bytesReceived, bytesTotal) {
|
||||||
|
progressBar.value = bytesReceived
|
||||||
|
progressBar.to = bytesTotal
|
||||||
|
}
|
||||||
|
|
||||||
|
function setIndeterminateBar() {
|
||||||
|
progressBar.indeterminate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStatus(arg) {
|
||||||
|
progressLabel.text = arg
|
||||||
|
}
|
||||||
|
|
||||||
Page {
|
Page {
|
||||||
id: page
|
id: page
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -30,6 +48,10 @@ Window {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: columnLayout
|
id: columnLayout
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Downloading in progress...")
|
text: qsTr("Downloading in progress...")
|
||||||
@@ -37,22 +59,26 @@ Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ProgressBar {
|
ProgressBar {
|
||||||
objectName: "progressBar"
|
id: progressBar
|
||||||
Layout.rightMargin: 20
|
Layout.rightMargin: 20
|
||||||
Layout.leftMargin: 20
|
Layout.leftMargin: 20
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
indeterminate: false
|
||||||
value: 0
|
value: 0
|
||||||
|
to: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
objectName: "labelProgress"
|
id: progressLabel
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||||
text: qsTr("Abort")
|
text: qsTr("Abort")
|
||||||
icon.source: "qrc:/images/icons/abort.svg"
|
icon.source: "qrc:/images/icons/abort.svg"
|
||||||
display: AbstractButton.TextBesideIcon
|
display: AbstractButton.TextBesideIcon
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
flat: true
|
||||||
onClicked: { onAbort() }
|
onClicked: { onAbort() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,8 @@ Page {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
GridLayout {
|
GridLayout {
|
||||||
rows: 2
|
rows: 2
|
||||||
@@ -299,11 +301,12 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: summaryFreq
|
id: summaryFreq
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
color: Material.color(Material.Green)
|
color: Material.color(Material.Green)
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.fillWidth: true
|
font.pointSize: 16
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,11 +391,12 @@ Page {
|
|||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: summaryBand
|
id: summaryBand
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
color: Material.color(Material.Green)
|
color: Material.color(Material.Green)
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.fillWidth: true
|
font.pointSize: 16
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -463,11 +467,12 @@ Page {
|
|||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
id: summaryACF
|
id: summaryACF
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
color: Material.color(Material.Green)
|
color: Material.color(Material.Green)
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
Layout.fillWidth: true
|
font.pointSize: 16
|
||||||
Layout.fillHeight: true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -501,7 +506,6 @@ Page {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
clip: true
|
clip: true
|
||||||
focus: true
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
active: true
|
active: true
|
||||||
}
|
}
|
||||||
@@ -553,7 +557,6 @@ Page {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
clip: true
|
clip: true
|
||||||
focus: true
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
active: true
|
active: true
|
||||||
}
|
}
|
||||||
@@ -605,7 +608,6 @@ Page {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
clip: true
|
clip: true
|
||||||
focus: true
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
ScrollBar.vertical: ScrollBar {
|
||||||
active: true
|
active: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Window {
|
|||||||
height: 400
|
height: 400
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
@@ -23,10 +23,12 @@ Window {
|
|||||||
|
|
||||||
signal saveMaterialAccent(string arg)
|
signal saveMaterialAccent(string arg)
|
||||||
signal saveMaterialTheme(string arg)
|
signal saveMaterialTheme(string arg)
|
||||||
|
signal saveAutoload(int arg)
|
||||||
|
|
||||||
function saveAll() {
|
function saveAll() {
|
||||||
saveMaterialAccent(comboBoxAccent.currentText)
|
saveMaterialAccent(comboBoxAccent.currentText)
|
||||||
saveMaterialTheme(comboBoxTheme.currentText)
|
saveMaterialTheme(comboBoxTheme.currentText)
|
||||||
|
saveAutoload(checkBoxAutoload.checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadMaterialAccent(accent) {
|
function loadMaterialAccent(accent) {
|
||||||
@@ -47,6 +49,14 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadAutoload(toggle) {
|
||||||
|
if (toggle) {
|
||||||
|
checkBoxAutoload.checked = true
|
||||||
|
} else {
|
||||||
|
checkBoxAutoload.checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DialogMessage {
|
DialogMessage {
|
||||||
id: dialogPreferencesSaved
|
id: dialogPreferencesSaved
|
||||||
modal: true
|
modal: true
|
||||||
@@ -67,10 +77,10 @@ Window {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 15
|
anchors.rightMargin: 10
|
||||||
anchors.leftMargin: 15
|
anchors.leftMargin: 10
|
||||||
anchors.bottomMargin: 15
|
anchors.bottomMargin: 10
|
||||||
anchors.topMargin: 15
|
anchors.topMargin: 10
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
@@ -132,6 +142,21 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Auto-load SigID database on startup (latest version)"
|
||||||
|
font.pixelSize: 12
|
||||||
|
clip: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: checkBoxAutoload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ Window {
|
|||||||
height: 400
|
height: 400
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
@@ -154,17 +154,18 @@ Window {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
Flickable {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
ScrollBar.vertical.interactive: true
|
Layout.topMargin: 5
|
||||||
|
TextArea.flickable: TextArea {
|
||||||
TextArea {
|
|
||||||
id: paramDescription
|
id: paramDescription
|
||||||
placeholderText: qsTr("Description")
|
placeholderText: qsTr("Description")
|
||||||
wrapMode: TextEdit.WordWrap
|
|
||||||
font.pointSize: 10
|
font.pointSize: 10
|
||||||
|
wrapMode: TextEdit.WordWrap
|
||||||
|
}
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
width: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ Page {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: signalName
|
id: signalName
|
||||||
@@ -272,7 +274,6 @@ Page {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
width: 100
|
width: 100
|
||||||
height: 100
|
height: 100
|
||||||
@@ -571,18 +572,23 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollView {
|
Flickable {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 5
|
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
ScrollBar.vertical.interactive: true
|
Layout.topMargin: 5
|
||||||
|
TextArea.flickable: TextArea {
|
||||||
TextArea {
|
|
||||||
id: descriptionTextArea
|
id: descriptionTextArea
|
||||||
|
placeholderText: qsTr("Description")
|
||||||
|
font.pointSize: 10
|
||||||
wrapMode: TextEdit.WordWrap
|
wrapMode: TextEdit.WordWrap
|
||||||
textFormat: Text.MarkdownText
|
textFormat: Text.MarkdownText
|
||||||
font.pointSize: 10
|
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
onLinkActivated: (link) => {
|
||||||
|
Qt.openUrlExternally(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
width: 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ Window {
|
|||||||
height: 700
|
height: 700
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
x = Screen.width/2 - width/2
|
x = Screen.width / 2 - width / 2
|
||||||
y = Screen.height/2 - height/2
|
y = Screen.height / 2 - height / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
@@ -50,6 +50,18 @@ Window {
|
|||||||
TabButton {
|
TabButton {
|
||||||
text: qsTr("Forecasts")
|
text: qsTr("Forecasts")
|
||||||
}
|
}
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("DRAP")
|
||||||
|
}
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("Aurora")
|
||||||
|
}
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("SSA")
|
||||||
|
}
|
||||||
|
TabButton {
|
||||||
|
text: qsTr("Sun Imagers")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StackLayout {
|
StackLayout {
|
||||||
@@ -68,6 +80,30 @@ Window {
|
|||||||
id: spaceWeatherForecastPage
|
id: spaceWeatherForecastPage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
SpaceWeatherDRAPPage {
|
||||||
|
id: spaceWeatherDRAPPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
SpaceWeatherAuroraPage {
|
||||||
|
id: spaceWeatherAuroraPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
SpaceWeatherSSA {
|
||||||
|
id: spaceWeatherSSA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
SpaceWeatherSunImagers {
|
||||||
|
id: spaceWeatherSunImagers
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
ui/SpaceWeatherAuroraPage.qml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Window
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: spaceWeatherAurora
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
objectName: "spaceWeatherAuroraObj"
|
||||||
|
|
||||||
|
function loadAuroraReport() {
|
||||||
|
checkUrlExists("https://www.aresvalley.com/poseidon_engine/aurora.png", function(exists) {
|
||||||
|
if (exists) {
|
||||||
|
imageBox.source = "https://www.aresvalley.com/poseidon_engine/aurora.png"
|
||||||
|
} else {
|
||||||
|
imageBox.source = "qrc:///images/artemis_not_available.svg"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUrlExists(url, callback) {
|
||||||
|
var xhr = new XMLHttpRequest()
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
callback(xhr.status === 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open("HEAD", url, true)
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imageBox
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@ Page {
|
|||||||
labelMux.text = poseidon_data['PROPAGATION']['MUX']
|
labelMux.text = poseidon_data['PROPAGATION']['MUX']
|
||||||
labelEME.text = poseidon_data['PROPAGATION']['EME']
|
labelEME.text = poseidon_data['PROPAGATION']['EME']
|
||||||
labelMS.text = poseidon_data['PROPAGATION']['MS']
|
labelMS.text = poseidon_data['PROPAGATION']['MS']
|
||||||
|
labelESEU50.text = poseidon_data['PROPAGATION']['ES_EU_50']
|
||||||
|
labelESEU70.text = poseidon_data['PROPAGATION']['ES_EU_70']
|
||||||
|
labelESEU144.text = poseidon_data['PROPAGATION']['ES_EU_144']
|
||||||
|
labelESAURORA.text = poseidon_data['PROPAGATION']['ES_AURORA']
|
||||||
labelHfNoise.text = poseidon_data['AK']['exp_noise']
|
labelHfNoise.text = poseidon_data['AK']['exp_noise']
|
||||||
|
|
||||||
labelPeakFluxClass.text = poseidon_data['XRAY']['peak_flux_class']
|
labelPeakFluxClass.text = poseidon_data['XRAY']['peak_flux_class']
|
||||||
@@ -75,10 +79,10 @@ Page {
|
|||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 20
|
anchors.rightMargin: 10
|
||||||
anchors.leftMargin: 20
|
anchors.leftMargin: 10
|
||||||
anchors.bottomMargin: 20
|
anchors.bottomMargin: 10
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 10
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -261,7 +265,6 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Current Flux Class:")
|
text: qsTr("Current Flux Class:")
|
||||||
font.capitalization: Font.SmallCaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -274,7 +277,6 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Peak 3h Flux Class:")
|
text: qsTr("Peak 3h Flux Class:")
|
||||||
font.capitalization: Font.SmallCaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -287,7 +289,6 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Peak 24h Flux Class:")
|
text: qsTr("Peak 24h Flux Class:")
|
||||||
font.capitalization: Font.SmallCaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -321,7 +322,7 @@ Page {
|
|||||||
columns: 2
|
columns: 2
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("MUX (MHz):")
|
text: qsTr("Maximum Usable Frequency (MHz):")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -334,7 +335,6 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Earth-Moon-Earth:")
|
text: qsTr("Earth-Moon-Earth:")
|
||||||
font.capitalization: Font.SmallCaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -347,7 +347,6 @@ Page {
|
|||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Meteor Scatter:")
|
text: qsTr("Meteor Scatter:")
|
||||||
font.capitalization: Font.SmallCaps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
@@ -359,8 +358,55 @@ Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
text: qsTr("Expected HF Noise:")
|
text: qsTr("Sporadic-E EU 50 MHz:")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelESEU50
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
font.capitalization: Font.SmallCaps
|
font.capitalization: Font.SmallCaps
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Sporadic-E EU 70 MHz:")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelESEU70
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
font.capitalization: Font.SmallCaps
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Sporadic-E EU 144 MHz:")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelESEU144
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
font.capitalization: Font.SmallCaps
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Aurora Spots:")
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: labelESAURORA
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
font.capitalization: Font.SmallCaps
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Expected HF Noise:")
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
|||||||
107
ui/SpaceWeatherDRAPPage.qml
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Window
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
|
||||||
|
Page {
|
||||||
|
id: spaceWeatherDRAP
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
objectName: "spaceWeatherDRAPObj"
|
||||||
|
|
||||||
|
function loadDrapReport(poseidon_data) {
|
||||||
|
labelRecovery.text = poseidon_data['DRAP']['Recovery Time']
|
||||||
|
labelXrayMsg.text = poseidon_data['DRAP']['XRay Msg']
|
||||||
|
labelProtonMsg.text = poseidon_data['DRAP']['Proton Msg']
|
||||||
|
|
||||||
|
checkUrlExists("https://www.aresvalley.com/poseidon_engine/drap.png", function(exists) {
|
||||||
|
if (exists) {
|
||||||
|
imageBox.source = "https://www.aresvalley.com/poseidon_engine/drap.png"
|
||||||
|
} else {
|
||||||
|
imageBox.source = "qrc:///images/artemis_not_available.svg"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkUrlExists(url, callback) {
|
||||||
|
var xhr = new XMLHttpRequest()
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE) {
|
||||||
|
callback(xhr.status === 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.open("HEAD", url, true)
|
||||||
|
xhr.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 10
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
anchors.topMargin: 10
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imageBox
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
text: qsTr("RECOVERY TIME:")
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: labelRecovery
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("X-RAY STATUS")
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: labelXrayMsg
|
||||||
|
font.pointSize: 12
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Label {
|
||||||
|
text: qsTr("PROTON STATUS")
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
id: labelProtonMsg
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
font.pointSize: 12
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,7 +53,6 @@ Page {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
labelDay1Event.text = poseidon_data['FORCST']['PRE_DATES'][0]
|
labelDay1Event.text = poseidon_data['FORCST']['PRE_DATES'][0]
|
||||||
labelDay2Event.text = poseidon_data['FORCST']['PRE_DATES'][1]
|
labelDay2Event.text = poseidon_data['FORCST']['PRE_DATES'][1]
|
||||||
labelDay3Event.text = poseidon_data['FORCST']['PRE_DATES'][2]
|
labelDay3Event.text = poseidon_data['FORCST']['PRE_DATES'][2]
|
||||||
@@ -119,13 +118,12 @@ Page {
|
|||||||
labelEventMajor2.text = geoMajorM2 + ' / ' + geoMajorH2
|
labelEventMajor2.text = geoMajorM2 + ' / ' + geoMajorH2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 20
|
anchors.rightMargin: 10
|
||||||
anchors.leftMargin: 20
|
anchors.leftMargin: 10
|
||||||
anchors.bottomMargin: 20
|
anchors.bottomMargin: 10
|
||||||
anchors.topMargin: 20
|
anchors.topMargin: 10
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|||||||