From 87ca5f1102e0f3ab7433fd8495edb0389aa9d02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Wed, 16 Nov 2022 17:45:21 +0100 Subject: [PATCH 01/46] Foundations for Python bindings --- README.md | 3 +++ rinex/Cargo.toml | 3 ++- rinex/pyproject.toml | 15 +++++++++++++++ rinex/python/example.py | 1 + rinex/src/epoch/mod.rs | 3 +++ rinex/src/lib.rs | 3 +++ rinex/src/python.rs | 9 +++++++++ 7 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 rinex/pyproject.toml create mode 100644 rinex/python/example.py create mode 100644 rinex/src/python.rs diff --git a/README.md b/README.md index c6976d504..ed9c38c26 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,9 @@ a given (usually current..) location on Earth. * `--flate2` allow native parsing of .gz compressed RINEX files. Otherwise, user must uncompress manually the `.gz` extension first. +* `--pyo3` (experimental) +Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. + ## Performances Parsing and `--sv` enumeration requested with `rinex-cli` diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 512d5a819..87b927b43 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -35,7 +35,8 @@ geo = { version = "0.23.0", optional = true } wkt = { version = "0.10.0", default-features = false, optional = true } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } flate2 = { version = "1.0.24", optional = true, default-features = false, features = ["zlib"] } -hifitime = { version = "3.7.0", features = ["serde", "std"] } +hifitime = { version = "3.7.0", features = ["serde", "std"] } +pyo3 = { version = "0.17.3", features = ["extension-module"], optional = true} [dev-dependencies] criterion = "0.4" diff --git a/rinex/pyproject.toml b/rinex/pyproject.toml new file mode 100644 index 000000000..f3776ad89 --- /dev/null +++ b/rinex/pyproject.toml @@ -0,0 +1,15 @@ +[build-system] +requires = ["maturin>=0.14"] +build-backend = "maturin" + +[project] +name = "rinex" +requires-python = ">=3.9" +classifiers = [ + "Development Status :: 4 - Beta", + "Topic :: Scientific/Engineering :: Physics", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: MIT License", + "License :: OSI Approved :: Apache Software License" +] + diff --git a/rinex/python/example.py b/rinex/python/example.py new file mode 100644 index 000000000..eacdc66f7 --- /dev/null +++ b/rinex/python/example.py @@ -0,0 +1 @@ +from rinex import Rinex diff --git a/rinex/src/epoch/mod.rs b/rinex/src/epoch/mod.rs index 1191f1367..d572ae1fb 100644 --- a/rinex/src/epoch/mod.rs +++ b/rinex/src/epoch/mod.rs @@ -32,6 +32,9 @@ pub enum Error { NanosecsError, } +#[cfg(feature = "serde")] +use serde::{Serialize}; + /* * Infaillible `Epoch::now()` call. */ diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index a422fcf84..8b126d7c4 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -85,6 +85,9 @@ use sampling::*; use gnss_time::TimeScaling; use crate::channel::Channel; +#[cfg(feature = "pyo3")] +pub mod python; + #[cfg(feature = "serde")] #[macro_use] extern crate serde; diff --git a/rinex/src/python.rs b/rinex/src/python.rs new file mode 100644 index 000000000..3e22d6d4b --- /dev/null +++ b/rinex/src/python.rs @@ -0,0 +1,9 @@ +use pyo3::{exceptions::PyException, prelude::*}; + +use crate::prelude::*; + +#[pymodule] +fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + Ok(()) +} From 9a2f25d009d2a2db21413d7413e3a51ac766ae5e Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Mon, 28 Nov 2022 21:18:28 +0100 Subject: [PATCH 02/46] python-bindings: basics * Cargo.toml: we need hifitime with "python" * epoch naturally comes included * add basis for "Rinex" struct Signed-off-by: Guillaume W. Bres --- rinex/Cargo.toml | 2 +- rinex/src/lib.rs | 3 ++- rinex/src/python.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 87b927b43..d013e6756 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -35,7 +35,7 @@ geo = { version = "0.23.0", optional = true } wkt = { version = "0.10.0", default-features = false, optional = true } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } flate2 = { version = "1.0.24", optional = true, default-features = false, features = ["zlib"] } -hifitime = { version = "3.7.0", features = ["serde", "std"] } +hifitime = { version = "3.7.0", features = ["serde", "std", "python"] } pyo3 = { version = "0.17.3", features = ["extension-module"], optional = true} [dev-dependencies] diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 8b126d7c4..6fed1a63f 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -86,7 +86,7 @@ use gnss_time::TimeScaling; use crate::channel::Channel; #[cfg(feature = "pyo3")] -pub mod python; +use pyo3::prelude::*; #[cfg(feature = "serde")] #[macro_use] @@ -149,6 +149,7 @@ macro_rules! hourly_session { #[derive(Clone, Debug)] #[derive(PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] /// `Rinex` describes a `RINEX` file. /// ``` /// use rinex::prelude::*; diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 3e22d6d4b..4a549ff0f 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -4,6 +4,6 @@ use crate::prelude::*; #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { - m.add_class::()?; + m.add_class::()?; Ok(()) } From 3b6754bbd062541cedcbd419370c6d5a6da7dddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Mon, 28 Nov 2022 22:32:58 +0100 Subject: [PATCH 03/46] clarify maturin building --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed9c38c26..087c185cd 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,8 @@ a given (usually current..) location on Earth. * `--flate2` allow native parsing of .gz compressed RINEX files. Otherwise, user must uncompress manually the `.gz` extension first. -* `--pyo3` (experimental) -Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. +* `--pyo3` (experimental) +Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. Maturin will then build and place the resulting .whl file in `/target/wheels/`. ## Performances From d9e447dd85497fd5802ef60f21fd9ebc6307978f Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Mon, 28 Nov 2022 21:18:28 +0100 Subject: [PATCH 04/46] python-bindings: basics * Cargo.toml: we need hifitime with "python" * epoch naturally comes included * convert epoch flag to pyobject Signed-off-by: Guillaume W. Bres --- rinex/examples/python/example.py | 1 + rinex/python/example.py | 1 - rinex/src/epoch/flag.rs | 4 ++++ rinex/src/epoch/mod.rs | 3 --- rinex/src/python.rs | 8 +++++++- 5 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 rinex/examples/python/example.py delete mode 100644 rinex/python/example.py diff --git a/rinex/examples/python/example.py b/rinex/examples/python/example.py new file mode 100644 index 000000000..0c8a24565 --- /dev/null +++ b/rinex/examples/python/example.py @@ -0,0 +1 @@ +from rinex import EpochFlag diff --git a/rinex/python/example.py b/rinex/python/example.py deleted file mode 100644 index eacdc66f7..000000000 --- a/rinex/python/example.py +++ /dev/null @@ -1 +0,0 @@ -from rinex import Rinex diff --git a/rinex/src/epoch/flag.rs b/rinex/src/epoch/flag.rs index 870ce5607..eb152f819 100644 --- a/rinex/src/epoch/flag.rs +++ b/rinex/src/epoch/flag.rs @@ -10,10 +10,14 @@ pub enum Error { UnknownFlag, } +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// `EpochFlag` validates an epoch, /// or describes possible events that occurred #[derive(Copy, Clone, Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum EpochFlag { /// Epoch is sane diff --git a/rinex/src/epoch/mod.rs b/rinex/src/epoch/mod.rs index d572ae1fb..1191f1367 100644 --- a/rinex/src/epoch/mod.rs +++ b/rinex/src/epoch/mod.rs @@ -32,9 +32,6 @@ pub enum Error { NanosecsError, } -#[cfg(feature = "serde")] -use serde::{Serialize}; - /* * Infaillible `Epoch::now()` call. */ diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 4a549ff0f..7e60083b3 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -2,8 +2,14 @@ use pyo3::{exceptions::PyException, prelude::*}; use crate::prelude::*; +impl std::convert::From for PyErr { + fn from(err: Error) -> PyErr { + PyException::new_err(err.to_string()) + } +} + #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { - m.add_class::()?; + m.add_class::()?; Ok(()) } From 0dd07ea786a8ac367eacd4a50dd971287374775a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Mon, 28 Nov 2022 22:41:36 +0100 Subject: [PATCH 05/46] add pip instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 087c185cd..5e9ebae98 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ a given (usually current..) location on Earth. allow native parsing of .gz compressed RINEX files. Otherwise, user must uncompress manually the `.gz` extension first. * `--pyo3` (experimental) -Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. Maturin will then build and place the resulting .whl file in `/target/wheels/`. +Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. Maturin will then build and place the resulting .whl file in `/target/wheels/`, after which you can install the package with `pip install rinex`. ## Performances From e7fea37462001ebc2bce6906ea9e4fcf32c4f767 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 09:13:19 +0100 Subject: [PATCH 06/46] python: correct Cargo.toml Signed-off-by: Guillaume W. Bres --- rinex/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 28919797b..d1a22f97c 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -14,6 +14,7 @@ rust-version = "1.61" [features] default = [] # no features by default sbas = ["geo", "wkt"] +pyo3 = ["dep:pyo3", "hifitime/python"] [build-dependencies] serde_json = { version = "1.0", features = ["preserve_order"] } @@ -35,8 +36,8 @@ geo = { version = "0.23.0", optional = true } wkt = { version = "0.10.0", default-features = false, optional = true } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } flate2 = { version = "1.0.24", optional = true, default-features = false, features = ["zlib"] } -hifitime = { version = "3.7.0", features = ["serde", "std", "python"] } -pyo3 = { version = "0.17.3", features = ["extension-module"], optional = true} +hifitime = { version = "3.7.0", default-features = false, features = ["serde", "std"] } +pyo3 = { version = "0.17.3", default-features = false, features = ["extension-module"], optional = true} [dev-dependencies] criterion = "0.4" From e925c25138c7bd1811f2eef466e24d40ca9734d1 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 10:34:48 +0100 Subject: [PATCH 07/46] python: basics Signed-off-by: Guillaume W. Bres --- README.md | 5 +++++ doc/python.md | 24 ++++++++++++++++++++++++ rinex/examples/python/example.py | 6 +++++- rinex/src/epoch/flag.rs | 7 ++++--- rinex/src/lib.rs | 3 +++ rinex/src/python.rs | 4 ++-- 6 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 doc/python.md diff --git a/README.md b/README.md index 7f74738c1..2daa222c5 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ RINEX Rust tool suites to parse, analyze and process `RINEX` files * [`rinex`](rinex/) is the core library + * [`rinex-cli`](rinex-cli/) is a command line application based on the core library. It can be used to process RINEX files or perform operations similar to `teqc`. The application is auto-generated for Windows, download it from the @@ -32,6 +33,10 @@ By default all timestamps are in UTC with leap seconds correctly managed. get falsely offset to the 21st century. This only applies to OBS(V2) and NAV(V2) files generated prior year 2000. +## `RINEX` in Python + +Refer to the [python package](doc/python.md) to understand how to build the python3 wheel. + ## Supported `RINEX` types | Type | Parser | Writer | CLI | UBX | Notes | diff --git a/doc/python.md b/doc/python.md new file mode 100644 index 000000000..edc336467 --- /dev/null +++ b/doc/python.md @@ -0,0 +1,24 @@ +Python3 wheel +============= + +Install requirements: python3 (>3.8) and maturin + +Then build the python bindings with `maturin` + +```bash +cd rinex/ +maturin -F pyo3 +``` + +A pip3 wheel is generated for your architecture. Use pip3 once again, +to install the library + +```bash +pip3 --force-reinstall install rinex/target/wheels/rinex-xxx.whl +``` + +Now move on to the provided examples, run the basics with + +```bash +python3 rinex/examples/python/basic.py +``` diff --git a/rinex/examples/python/example.py b/rinex/examples/python/example.py index 0c8a24565..8ddb74da0 100644 --- a/rinex/examples/python/example.py +++ b/rinex/examples/python/example.py @@ -1 +1,5 @@ -from rinex import EpochFlag +from rinex import * + +if __name__ == "__main__": + flag = EpochFlag.Ok + assert(flag.is_ok() == True) diff --git a/rinex/src/epoch/flag.rs b/rinex/src/epoch/flag.rs index eb152f819..bedf981b4 100644 --- a/rinex/src/epoch/flag.rs +++ b/rinex/src/epoch/flag.rs @@ -17,8 +17,8 @@ use pyo3::prelude::*; /// or describes possible events that occurred #[derive(Copy, Clone, Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "pyo3", pyclass)] pub enum EpochFlag { /// Epoch is sane Ok, @@ -42,10 +42,11 @@ impl Default for EpochFlag { } } +#[cfg_attr(feature = "pyo3", pymethods)] impl EpochFlag { /// Returns True if self is a valid epoch - pub fn is_ok(self) -> bool { - self == Self::Ok + pub fn is_ok(&self) -> bool { + *self == Self::Ok } } diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 3456f0091..54b8e2315 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -46,6 +46,9 @@ use observation::Crinex; use navigation::OrbitItem; use hifitime::Duration; +#[cfg(feature = "pyo3")] +pub mod python; + // Convenient package to import, that // comprises all basic and major structures pub mod prelude { diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 7e60083b3..774cc3d91 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -2,11 +2,11 @@ use pyo3::{exceptions::PyException, prelude::*}; use crate::prelude::*; -impl std::convert::From for PyErr { +/*impl std::convert::From for PyErr { fn from(err: Error) -> PyErr { PyException::new_err(err.to_string()) } -} +}*/ #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { From e4dee0c73b2ba2efed7440b1bfb06c3569280e57 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 17:28:20 +0100 Subject: [PATCH 08/46] python: github workflow to generate the python wheel Signed-off-by: Guillaume W. Bres --- .github/workflows/python.yml | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 .github/workflows/python.yml diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml new file mode 100644 index 000000000..10281c99c --- /dev/null +++ b/.github/workflows/python.yml @@ -0,0 +1,105 @@ +name: python + +on: + push: + pull_request: + release: + types: [created] + +jobs: + macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + architecture: x64 + - uses: dtolnay/rust-toolchain@stable + - name: Build wheels - x86_64 + uses: PyO3/maturin-action@v1 + with: + target: x64 + args: --release --all-features --out dist --sdist -m rinex/Cargo.toml + - name: Install built wheel - x86_64 + run: | + pip install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + windows: + runs-on: windows-latest + strategy: + matrix: + target: [x64, x86] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + architecture: ${{ matrix.target }} + - uses: dtolnay/rust-toolchain@stable + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + python-version: 3.9 + args: --release --all-features --out dist --sdist -m rinex/Cargo.toml + - name: Install built wheel + run: | + pip install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + architecture: x64 + - uses: dtolnay/rust-toolchain@stable + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: x64 + manylinux: auto + args: --release -i python3.9 --all-features --out dist -m rinex/Cargo.toml + - name: Install built wheel + run: | + pip3 install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist + + release: + name: Release + runs-on: ubuntu-latest + if: "startsWith(github.ref, 'refs/tags/')" + needs: [ macos, windows, linux ] + steps: + - uses: actions/download-artifact@v3 + with: + name: wheels + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + - name: Publish to PyPI + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + pip install --upgrade twine + twine upload --skip-existing * From b660deee2837620ba6616dae88a8894ac856fff3 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 20:12:50 +0100 Subject: [PATCH 09/46] python.yml: add linux-x86 wheel Signed-off-by: Guillaume W. Bres --- .github/workflows/python.yml | 45 +++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 10281c99c..f12032d15 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -61,28 +61,31 @@ jobs: linux: runs-on: ubuntu-latest + strategy: + matrix: + target: [x64, x86] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: x64 - - uses: dtolnay/rust-toolchain@stable - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: x64 - manylinux: auto - args: --release -i python3.9 --all-features --out dist -m rinex/Cargo.toml - - name: Install built wheel - run: | - pip3 install rinex --no-index --find-links dist --force-reinstall - python -c "import rinex" - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + architecture: ${{ matrix.target }} + - uses: dtolnay/rust-toolchain@stable + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + python-version: 3.9 + args: --release --all-features --out dist --sdist -m rinex/Cargo.toml + - name: Install built wheel + run: | + pip install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist release: name: Release From 16e35675a3cdd8dff54a039a861a7cf1b1adfd52 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 20:17:53 +0100 Subject: [PATCH 10/46] python.yml: linux x86 does not work Signed-off-by: Guillaume W. Bres --- .github/workflows/python.yml | 45 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index f12032d15..10281c99c 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -61,31 +61,28 @@ jobs: linux: runs-on: ubuntu-latest - strategy: - matrix: - target: [x64, x86] steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: ${{ matrix.target }} - - uses: dtolnay/rust-toolchain@stable - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: ${{ matrix.target }} - python-version: 3.9 - args: --release --all-features --out dist --sdist -m rinex/Cargo.toml - - name: Install built wheel - run: | - pip install rinex --no-index --find-links dist --force-reinstall - python -c "import rinex" - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + architecture: x64 + - uses: dtolnay/rust-toolchain@stable + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: x64 + manylinux: auto + args: --release -i python3.9 --all-features --out dist -m rinex/Cargo.toml + - name: Install built wheel + run: | + pip3 install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist release: name: Release From 9da50ceac2144db5e4f4e5ef1bf2ed329581ddaa Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 20:47:11 +0100 Subject: [PATCH 11/46] python.yml: test advanced script, remove publication Signed-off-by: Guillaume W. Bres --- .github/workflows/python.yml | 95 +++++++++++++++++------------------- 1 file changed, 46 insertions(+), 49 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 10281c99c..8ffa43d5e 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -13,14 +13,15 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: 3.9 - architecture: x64 + python-version: '3.10' - uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-darwin - name: Build wheels - x86_64 uses: PyO3/maturin-action@v1 with: - target: x64 - args: --release --all-features --out dist --sdist -m rinex/Cargo.toml + target: x86_64-apple-darwin + args: --release --all-features -m rinex/Cargo.toml --out dist --sdist - name: Install built wheel - x86_64 run: | pip install rinex --no-index --find-links dist --force-reinstall @@ -35,20 +36,24 @@ jobs: runs-on: windows-latest strategy: matrix: - target: [x64, x86] + platform: [ + { python-architecture: "x64", target: "x86_64-pc-windows-msvc" }, + { python-architecture: "x86", target: "i686-pc-windows-msvc" }, + ] steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: - python-version: 3.9 - architecture: ${{ matrix.target }} + python-version: 3.6 + architecture: ${{ matrix.platform.python-architecture }} - uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.platform.target }} - name: Build wheels uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.target }} - python-version: 3.9 - args: --release --all-features --out dist --sdist -m rinex/Cargo.toml + target: ${{ matrix.platform.target }} + args: --release --all-features -m rinex/Cargo.toml --out dist - name: Install built wheel run: | pip install rinex --no-index --find-links dist --force-reinstall @@ -61,45 +66,37 @@ jobs: linux: runs-on: ubuntu-latest + strategy: + matrix: + platform: [ + { target: "x86_64-unknown-linux-musl", image_tag: "x86_64-musl" }, + { target: "i686-unknown-linux-musl", image_tag: "i686-musl" }, + { target: "aarch64-unknown-linux-musl", image_tag: "aarch64-musl" }, + { target: "armv7-unknown-linux-musleabihf", image_tag: "armv7-musleabihf" }, + { target: "powerpc64le-unknown-linux-musl", image_tag: "powerpc64le-musl" }, + ] + container: + image: docker://messense/rust-musl-cross:${{ matrix.platform.image_tag }} + env: + CFLAGS_armv7_unknown_linux_musleabihf: '-mfpu=vfpv3-d16' steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: 3.9 - architecture: x64 - - uses: dtolnay/rust-toolchain@stable - - name: Build wheels - uses: PyO3/maturin-action@v1 - with: - target: x64 - manylinux: auto - args: --release -i python3.9 --all-features --out dist -m rinex/Cargo.toml - - name: Install built wheel - run: | - pip3 install rinex --no-index --find-links dist --force-reinstall - python -c "import rinex" - - name: Upload wheels - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - release: - name: Release - runs-on: ubuntu-latest - if: "startsWith(github.ref, 'refs/tags/')" - needs: [ macos, windows, linux ] - steps: - - uses: actions/download-artifact@v3 + - uses: actions/checkout@v3 + - name: Build Wheels - manylinux + uses: PyO3/maturin-action@main with: - name: wheels - - uses: actions/setup-python@v4 + target: ${{ matrix.platform.target }} + manylinux: auto + container: off + args: --release --all-features -m rinex/Cargo.toml -o dist + - name: Build Wheels - musllinux + uses: PyO3/maturin-action@main with: - python-version: 3.9 - - name: Publish to PyPI - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - pip install --upgrade twine - twine upload --skip-existing * + target: ${{ matrix.platform.target }} + manylinux: musllinux_1_1 + container: off + args: --release --all-features -m rinex/Cargo.toml -o dist + - name: Upload wheels + uses: actions/upload-artifact@v3 + with: + name: wheels + path: dist From 2b51f9d540f5c7cc9bcf8f9f1b3a84dd1f9ca05c Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 21:07:41 +0100 Subject: [PATCH 12/46] python.yml: sanitization --- .github/workflows/python.yml | 65 +++++++++++++++--------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 8ffa43d5e..9ee7af5aa 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -11,22 +11,23 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: 3.9 - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin - - name: Build wheels - x86_64 + - name: Build uses: PyO3/maturin-action@v1 with: target: x86_64-apple-darwin args: --release --all-features -m rinex/Cargo.toml --out dist --sdist - - name: Install built wheel - x86_64 + - name: Install run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Upload wheels + - name: Upload uses: actions/upload-artifact@v3 with: name: wheels @@ -36,29 +37,27 @@ jobs: runs-on: windows-latest strategy: matrix: - platform: [ - { python-architecture: "x64", target: "x86_64-pc-windows-msvc" }, - { python-architecture: "x86", target: "i686-pc-windows-msvc" }, - ] + target: [x64, x86] steps: - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: - python-version: 3.6 - architecture: ${{ matrix.platform.python-architecture }} + python-version: 3.9 + architecture: ${{ matrix.target }} - uses: dtolnay/rust-toolchain@stable with: - targets: ${{ matrix.platform.target }} - - name: Build wheels + targets: ${{ matrix.target }} + - name: Build uses: PyO3/maturin-action@v1 with: target: ${{ matrix.platform.target }} args: --release --all-features -m rinex/Cargo.toml --out dist - - name: Install built wheel + - name: Install run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Upload wheels + - name: Upload uses: actions/upload-artifact@v3 with: name: wheels @@ -66,36 +65,24 @@ jobs: linux: runs-on: ubuntu-latest - strategy: - matrix: - platform: [ - { target: "x86_64-unknown-linux-musl", image_tag: "x86_64-musl" }, - { target: "i686-unknown-linux-musl", image_tag: "i686-musl" }, - { target: "aarch64-unknown-linux-musl", image_tag: "aarch64-musl" }, - { target: "armv7-unknown-linux-musleabihf", image_tag: "armv7-musleabihf" }, - { target: "powerpc64le-unknown-linux-musl", image_tag: "powerpc64le-musl" }, - ] - container: - image: docker://messense/rust-musl-cross:${{ matrix.platform.image_tag }} - env: - CFLAGS_armv7_unknown_linux_musleabihf: '-mfpu=vfpv3-d16' steps: - uses: actions/checkout@v3 - - name: Build Wheels - manylinux - uses: PyO3/maturin-action@main + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-python@v4 with: - target: ${{ matrix.platform.target }} - manylinux: auto - container: off - args: --release --all-features -m rinex/Cargo.toml -o dist - - name: Build Wheels - musllinux + python-version: 3.9 + architecture: x64 + - name: Build uses: PyO3/maturin-action@main with: - target: ${{ matrix.platform.target }} - manylinux: musllinux_1_1 - container: off - args: --release --all-features -m rinex/Cargo.toml -o dist - - name: Upload wheels + target: x64 + manylinux: auto + args: --release -i python3.9 --all-features -m rinex/Cargo.toml -o dist + - name: Install + run: | + pip3 install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Upload uses: actions/upload-artifact@v3 with: name: wheels From 4a6ff200a4e64275260936ce49f05a3e99025c10 Mon Sep 17 00:00:00 2001 From: gwbres Date: Wed, 30 Nov 2022 21:14:47 +0100 Subject: [PATCH 13/46] Update python.yml --- .github/workflows/python.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9ee7af5aa..4e0841e16 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -78,10 +78,10 @@ jobs: target: x64 manylinux: auto args: --release -i python3.9 --all-features -m rinex/Cargo.toml -o dist - - name: Install - run: | - pip3 install rinex --no-index --find-links dist --force-reinstall - python -c "import rinex" + - name: Build + run: | + pip3 install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" - name: Upload uses: actions/upload-artifact@v3 with: From fc5503049312f8bd2c7cd556451a1fca005bf86e Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 21:18:28 +0100 Subject: [PATCH 14/46] python: fix wheel compilation on windows Signed-off-by: Guillaume W. Bres --- .github/workflows/python.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 4e0841e16..dfb07c713 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -40,7 +40,6 @@ jobs: target: [x64, x86] steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: python-version: 3.9 From c234b95ea4bb8560d35b5e3c701b0efd68423a85 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 21:41:38 +0100 Subject: [PATCH 15/46] github: fix windows wheel build --- .github/workflows/python.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index dfb07c713..ee1e4b70d 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -40,13 +40,11 @@ jobs: target: [x64, x86] steps: - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: python-version: 3.9 architecture: ${{ matrix.target }} - - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.target }} - name: Build uses: PyO3/maturin-action@v1 with: From 7c597279708d2b01b3961d5f4c10e20e40f756a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Wed, 30 Nov 2022 22:10:04 +0100 Subject: [PATCH 16/46] remove unused and outcommented --- rinex/src/python.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 774cc3d91..f2d6486b7 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -2,12 +2,6 @@ use pyo3::{exceptions::PyException, prelude::*}; use crate::prelude::*; -/*impl std::convert::From for PyErr { - fn from(err: Error) -> PyErr { - PyException::new_err(err.to_string()) - } -}*/ - #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; From 0fcc683587af9ed5a46fd4296d75efd56dfebd1a Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 23:32:04 +0100 Subject: [PATCH 17/46] observation: basics Signed-off-by: Guillaume W. Bres --- rinex/examples/python/basic.py | 4 +++ rinex/examples/python/example.py | 5 --- rinex/examples/python/observation.py | 11 +++++++ rinex/src/observation/mod.rs | 25 +++++++++++++++ rinex/src/observation/record.rs | 47 +++++++++++++++++++++------- rinex/src/python.rs | 19 ++++++++++- rinex/src/version.rs | 18 +++++++++++ 7 files changed, 112 insertions(+), 17 deletions(-) create mode 100644 rinex/examples/python/basic.py delete mode 100644 rinex/examples/python/example.py create mode 100644 rinex/examples/python/observation.py diff --git a/rinex/examples/python/basic.py b/rinex/examples/python/basic.py new file mode 100644 index 000000000..9e57ef5ad --- /dev/null +++ b/rinex/examples/python/basic.py @@ -0,0 +1,4 @@ +from rinex import * + +if __name__ == "__main__": + print(Epoch.system_now()) diff --git a/rinex/examples/python/example.py b/rinex/examples/python/example.py deleted file mode 100644 index 8ddb74da0..000000000 --- a/rinex/examples/python/example.py +++ /dev/null @@ -1,5 +0,0 @@ -from rinex import * - -if __name__ == "__main__": - flag = EpochFlag.Ok - assert(flag.is_ok() == True) diff --git a/rinex/examples/python/observation.py b/rinex/examples/python/observation.py new file mode 100644 index 000000000..de179f4ec --- /dev/null +++ b/rinex/examples/python/observation.py @@ -0,0 +1,11 @@ +from rinex import * + +if __name__ == "__main__": + crinex = Crinex() + assert(crinex.version.major == 3) + assert(crinex.version.minor == 0) + + observation = ObservationData(10.0) + assert(observation.obs == 10.0) + assert(observation.ssi == None) + assert(observation.lli == None) diff --git a/rinex/src/observation/mod.rs b/rinex/src/observation/mod.rs index 1faee3030..aff8cd9cd 100644 --- a/rinex/src/observation/mod.rs +++ b/rinex/src/observation/mod.rs @@ -11,6 +11,9 @@ pub use record::{ use std::collections::HashMap; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + macro_rules! fmt_month { ($m: expr) => { match $m { @@ -36,6 +39,7 @@ use serde::Serialize; /// Describes `Compact RINEX` specific information #[derive(Clone, Debug)] #[derive(PartialEq, Eq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Crinex { /// Compression program version @@ -46,6 +50,7 @@ pub struct Crinex { pub date: Epoch, } +#[cfg_attr(feature = "pyo3", pymethods)] impl Crinex { /// Sets compression algorithm revision pub fn with_version(&self, version: Version) -> Self { @@ -65,6 +70,26 @@ impl Crinex { s.date = e; s } + #[cfg(feature = "pyo3")] + #[new] + fn new_py() -> Self { + Self::default() + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_version(&self) -> Version { + self.version + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_prog(&self) -> &str { + &self.prog + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_date(&self) -> Epoch { + self.date + } } impl Default for Crinex { diff --git a/rinex/src/observation/record.rs b/rinex/src/observation/record.rs index ab4202707..1e4b1b92f 100644 --- a/rinex/src/observation/record.rs +++ b/rinex/src/observation/record.rs @@ -1,6 +1,5 @@ use thiserror::Error; use std::str::FromStr; -//use chrono::Timelike; use bitflags::bitflags; use std::collections::{BTreeMap, HashMap}; @@ -18,6 +17,9 @@ use crate::{ }; use hifitime::Duration; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[derive(Error, Debug)] pub enum Error { #[error("failed to parse epoch")] @@ -42,6 +44,7 @@ use serde::Serialize; /// `Ssi` describes signals strength #[repr(u8)] #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone, Debug)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum Ssi { /// Ssi ~= 0 dB/Hz @@ -106,29 +109,31 @@ impl FromStr for Ssi { } } +#[cfg_attr(feature = "pyo3", pymethods)] impl Ssi { /// Returns true if `self` is a bad signal level, very poor quality, /// measurements should be discarded - pub fn is_bad (self) -> bool { - self <= Ssi::DbHz18_23 + pub fn is_bad (&self) -> bool { + *self <= Ssi::DbHz18_23 } /// Returns true if `self` is a weak signal level, poor quality - pub fn is_weak (self) -> bool { - self < Ssi::DbHz30_35 + pub fn is_weak (&self) -> bool { + *self < Ssi::DbHz30_35 } /// Returns true if `self` is a strong signal level, good quality as defined by standard - pub fn is_strong (self) -> bool { - self >= Ssi::DbHz30_35 + pub fn is_strong (&self) -> bool { + *self >= Ssi::DbHz30_35 } /// Returns true if `self` is a very strong signal level, very high quality - pub fn is_excellent (self) -> bool { - self > Ssi::DbHz42_47 + pub fn is_excellent (&self) -> bool { + *self > Ssi::DbHz42_47 } /// Returns true if `self` matches a strong signal level (defined by standard) - pub fn is_ok (self) -> bool { self.is_strong() } + pub fn is_ok (&self) -> bool { self.is_strong() } } bitflags! { + #[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct LliFlags: u8 { /// Current epoch is marked Ok or Unknown status @@ -146,6 +151,7 @@ bitflags! { #[derive(Copy, Clone, Debug)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct ObservationData { /// physical measurement @@ -156,8 +162,11 @@ pub struct ObservationData { pub ssi: Option, } +#[cfg_attr(feature = "pyo3", pymethods)] impl ObservationData { /// Builds new ObservationData structure from given predicates + #[cfg(feature = "pyo3")] + #[new] pub fn new (obs: f64, lli: Option, ssi: Option) -> ObservationData { ObservationData { obs, @@ -172,7 +181,7 @@ impl ObservationData { /// + LLI must match the LliFlags::OkOrUnknown flag (strictly) /// if SSI exists: /// + SSI must match the .is_ok() criteria, refer to API - pub fn is_ok (self) -> bool { + pub fn is_ok (&self) -> bool { let lli_ok = self.lli.unwrap_or(LliFlags::OK_OR_UNKNOWN) == LliFlags::OK_OR_UNKNOWN; let ssi_ok = self.ssi.unwrap_or(Ssi::default()).is_ok(); lli_ok && ssi_ok @@ -188,6 +197,22 @@ impl ObservationData { pub fn pr_real_distance (&self, rcvr_offset: f64, sv_offset: f64, biases: f64) -> f64 { self.obs + 299_792_458.0_f64 * (rcvr_offset - sv_offset) + biases } + + #[cfg(feature = "pyo3")] + #[getter] + fn get_obs(&self) -> f64 { + self.obs + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_lli(&self) -> Option { + self.lli + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_ssi(&self) -> Option { + self.ssi + } } /// Observation Record content. diff --git a/rinex/src/python.rs b/rinex/src/python.rs index f2d6486b7..3826146ce 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -1,9 +1,26 @@ use pyo3::{exceptions::PyException, prelude::*}; -use crate::prelude::*; +use crate::{ + prelude::*, + observation::{ + Crinex, + record::*, + }, +}; #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { + /* + * prelude module + */ + m.add_class::()?; m.add_class::()?; + /* + * Observation module + */ + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; Ok(()) } diff --git a/rinex/src/version.rs b/rinex/src/version.rs index 91573e2e3..34b66e171 100644 --- a/rinex/src/version.rs +++ b/rinex/src/version.rs @@ -1,5 +1,8 @@ //! `RINEX` revision description +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// Current `RINEX` version supported to this day pub const SUPPORTED_VERSION: Version = Version { major: 4, @@ -8,6 +11,7 @@ pub const SUPPORTED_VERSION: Version = Version { #[derive(Copy, Clone, Debug)] #[derive(PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Version { pub major: u8, @@ -21,6 +25,20 @@ impl Default for Version { } } +#[cfg_attr(feature = "pyo3", pymethods)] +impl Version { + #[cfg(feature = "pyo3")] + #[getter] + fn get_major(&self) -> u8 { + self.major + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_minor(&self) -> u8 { + self.minor + } +} + impl std::fmt::Display for Version { fn fmt (&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}.{}", self.major, self.minor) From 2e231ccda1ffaa3357db85ca2d332dd5ce149bd1 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Wed, 30 Nov 2022 23:41:26 +0100 Subject: [PATCH 18/46] python.yml: fix windows-x86 build --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index ee1e4b70d..ccbaf0660 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -48,7 +48,7 @@ jobs: - name: Build uses: PyO3/maturin-action@v1 with: - target: ${{ matrix.platform.target }} + target: ${{ matrix.target }} args: --release --all-features -m rinex/Cargo.toml --out dist - name: Install run: | From 6690dfe8c25a13e3bff6be51f4401a69968af42f Mon Sep 17 00:00:00 2001 From: gwbres Date: Thu, 1 Dec 2022 13:12:34 +0100 Subject: [PATCH 19/46] python.yml supply more than one python version --- .github/workflows/python.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index ccbaf0660..f50a6230b 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -9,12 +9,15 @@ on: jobs: macos: runs-on: macos-latest + strategy: + matrix: + py_ver: [3.8, 3.9, 3.10] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: ${{ matrix.py_ver }} - uses: dtolnay/rust-toolchain@stable with: targets: aarch64-apple-darwin @@ -38,12 +41,13 @@ jobs: strategy: matrix: target: [x64, x86] + py_ver: [3.8, 3.9, 3.10] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: ${{ matrix.py_ver }} architecture: ${{ matrix.target }} - name: Build uses: PyO3/maturin-action@v1 @@ -62,19 +66,23 @@ jobs: linux: runs-on: ubuntu-latest + strategy: + matrix: + target: [x64] + py_ver: [3.8, 3.9, 3.10] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: - python-version: 3.9 - architecture: x64 + python-version: ${{ matrix.py_ver }} + architecture: ${{ matrix.target }} - name: Build uses: PyO3/maturin-action@main with: target: x64 manylinux: auto - args: --release -i python3.9 --all-features -m rinex/Cargo.toml -o dist + args: --release -i python${{ matrix.py_ver }} --all-features -m rinex/Cargo.toml -o dist - name: Build run: | pip3 install rinex --no-index --find-links dist --force-reinstall From 60b0489adf985b12b42818a9a040adef770d082e Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Thu, 1 Dec 2022 13:35:31 +0100 Subject: [PATCH 20/46] python: support and supply py3.8 Signed-off-by: Guillaume W. Bres --- rinex/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rinex/pyproject.toml b/rinex/pyproject.toml index f3776ad89..d3658530c 100644 --- a/rinex/pyproject.toml +++ b/rinex/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "rinex" -requires-python = ">=3.9" +requires-python = ">=3.8" classifiers = [ "Development Status :: 4 - Beta", "Topic :: Scientific/Engineering :: Physics", From 105c5ac7d1b12a40aa0cbc685b15e8f1a16c4a69 Mon Sep 17 00:00:00 2001 From: gwbres Date: Thu, 1 Dec 2022 14:02:46 +0100 Subject: [PATCH 21/46] python.yml fix python3.10 auto generation --- .github/workflows/python.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index f50a6230b..3f3c690f0 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -11,7 +11,7 @@ jobs: runs-on: macos-latest strategy: matrix: - py_ver: [3.8, 3.9, 3.10] + py_ver: ['3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable @@ -41,7 +41,7 @@ jobs: strategy: matrix: target: [x64, x86] - py_ver: [3.8, 3.9, 3.10] + py_ver: ['3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable @@ -69,7 +69,7 @@ jobs: strategy: matrix: target: [x64] - py_ver: [3.8, 3.9, 3.10] + py_ver: ['3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable From a43ff4b74af0dca2688c64a3a84c5faca320adc4 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Thu, 1 Dec 2022 08:42:24 +0100 Subject: [PATCH 22/46] rust.yml * no need to run tests with default features * we should run coverage and benchmark on tags \ PR events Signed-off-by: Guillaume W. Bres --- .github/workflows/rust.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1902d3eee..9dd0c4e0d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,8 +21,6 @@ jobs: cargo install cargo-tarpaulin - name: Build run: cargo build - - name: Test - run: cargo test -- --nocapture - name: Build (all features) run: cargo build --all-features - name: Test (all features) From d68d797a98c1e2400f66dd0ea4f0d64c9bd734a5 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Thu, 1 Dec 2022 09:33:39 +0100 Subject: [PATCH 23/46] fix testbench: crinex day is 2 digit number Signed-off-by: Guillaume W. Bres --- rinex/src/observation/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rinex/src/observation/mod.rs b/rinex/src/observation/mod.rs index aff8cd9cd..e956ed339 100644 --- a/rinex/src/observation/mod.rs +++ b/rinex/src/observation/mod.rs @@ -218,7 +218,7 @@ mod crinex { let now = Epoch::now().unwrap(); let (y, m, d, hh, mm, _, _) = now.to_gregorian_utc(); let expected = format!("3.0 COMPACT RINEX FORMAT CRINEX VERS / TYPE -rust-rinex-{} {}-{}-{} {:02}:{:02} CRINEX PROG / DATE", env!("CARGO_PKG_VERSION"), d, fmt_month!(m), y-2000, hh, mm); +rust-rinex-{} {:02}-{}-{} {:02}:{:02} CRINEX PROG / DATE", env!("CARGO_PKG_VERSION"), d, fmt_month!(m), y-2000, hh, mm); assert_eq!(crinex.to_string(), expected); } } From f7935b17322a08a29a16f44d9bf991ce44203b76 Mon Sep 17 00:00:00 2001 From: gwbres Date: Thu, 1 Dec 2022 14:21:00 +0100 Subject: [PATCH 24/46] release.yml: cli binary packaging (#61) * rinex-cli binary generated renamed * linux and macos jobs need build-rs action improvement * linux needs build-rs action improvement regarding python-pkg --- .github/workflows/release.yml | 63 ++++++----------------------------- 1 file changed, 10 insertions(+), 53 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b588c576..a86efae16 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,71 +2,28 @@ name: release on: release: types: [created] + pull_request: + +env: + PKG_CONFIG_PATH: "/usr/lib/pkgconfig" jobs: - release-pc-windows-gnu: - name: release pc-windows-gnu - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - target: x86_64-pc-windows-gnu - archive: zip - steps: - - uses: actions/checkout@master - - name: Compile and release - uses: rust-build/rust-build.action@v1.4.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - RUSTTARGET: ${{ matrix.target }} - EXTRA_FILES: "rinex-cli/README.md LICENSE-APACHE LICENSE-MIT" - SRC_DIR: "rinex-cli" - ARCHIVE_TYPES: ${{ matrix.archive }} - TOOLCHAIN_VERSION: stable - release-apple-darwin: - name: release apple-darwin + release: runs-on: ubuntu-latest continue-on-error: true strategy: - fail-fast: false matrix: - include: - - target: x86_64-apple-darwin - archive: zip + target: [x86_64-unknown-linux-musl, x86_64-apple-darwin, x86_64-pc-windows-gnu] steps: - uses: actions/checkout@master - - name: Compile and release + - name: Release ${{ matrix.target }} uses: rust-build/rust-build.action@v1.4.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ matrix.target }} with: - RUSTTARGET: ${{ matrix.target }} - EXTRA_FILES: "rinex-cli/README.md LICENSE-APACHE LICENSE-MIT" SRC_DIR: "rinex-cli" - ARCHIVE_TYPES: ${{ matrix.archive }} - TOOLCHAIN_VERSION: stable - release-linux-musl: - name: release linux-musl - runs-on: ubuntu-latest - continue-on-error: true - strategy: - fail-fast: false - matrix: - include: - - target: x86_64-unknown-linux-musl - archive: tar.gz - steps: - - uses: actions/checkout@master - - name: Compile and release - uses: rust-build/rust-build.action@v1.4.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PKG_CONFIG_PATH: "/usr/lib/x86-64_linux-gnu/pkgconfig/fontconfig.pc" - with: RUSTTARGET: ${{ matrix.target }} EXTRA_FILES: "rinex-cli/README.md LICENSE-APACHE LICENSE-MIT" - SRC_DIR: "rinex-cli" - ARCHIVE_TYPES: ${{ matrix.archive }} TOOLCHAIN_VERSION: stable + ARCHIVE_TYPES: zip + ARCHIVE_NAME: rinex-cli-${{ matrix.target }}.zip From 931bfe8471cfa4c134fdc5f625f0308b8c7753f8 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Fri, 2 Dec 2022 21:43:51 +0100 Subject: [PATCH 25/46] python.rs: add a few more bindings Signed-off-by: Guillaume W. Bres --- rinex/src/hardware.rs | 6 ++++++ rinex/src/navigation/eopmessage.rs | 4 ++++ rinex/src/navigation/ephemeris.rs | 34 ++++++++++++++++++------------ rinex/src/navigation/health.rs | 8 +++++++ rinex/src/navigation/ionmessage.rs | 8 +++++++ rinex/src/navigation/record.rs | 5 +++++ rinex/src/navigation/stomessage.rs | 4 ++++ rinex/src/python.rs | 20 ++++++++++++++++-- 8 files changed, 74 insertions(+), 15 deletions(-) diff --git a/rinex/src/hardware.rs b/rinex/src/hardware.rs index fa7430ce2..2c6fa157b 100644 --- a/rinex/src/hardware.rs +++ b/rinex/src/hardware.rs @@ -2,6 +2,9 @@ use super::Sv; use rust_3d::Point3D; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[cfg(feature = "serde")] use crate::formatter::opt_point3d; @@ -11,6 +14,7 @@ use serde::{Serialize, Deserialize}; /// GNSS receiver description #[derive(Clone, Debug)] #[derive(PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Rcvr { /// Receiver (hardware) model @@ -49,6 +53,7 @@ impl std::str::FromStr for Rcvr { /// Antenna description #[derive(Debug, Clone, Default)] #[derive(PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Antenna { /// Hardware model / make descriptor @@ -69,6 +74,7 @@ pub struct Antenna { pub northern: Option, } +#[cfg_attr(feature = "pyo3", pymethods)] impl Antenna { /// Sets desired model pub fn with_model (&self, m: &str) -> Self { diff --git a/rinex/src/navigation/eopmessage.rs b/rinex/src/navigation/eopmessage.rs index 7c1e2fcf9..608237460 100644 --- a/rinex/src/navigation/eopmessage.rs +++ b/rinex/src/navigation/eopmessage.rs @@ -4,6 +4,9 @@ use thiserror::Error; use std::str::FromStr; use crate::prelude::*; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// EopMessage Parsing error #[derive(Debug, Error)] pub enum Error { @@ -44,6 +47,7 @@ pub enum Error { #[derive(Debug, Clone)] #[derive(Default)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct EopMessage { /// ([arc-sec], [arc-sec.day⁻¹], [arc-sec.day⁻²]) diff --git a/rinex/src/navigation/ephemeris.rs b/rinex/src/navigation/ephemeris.rs index 8198dfaed..d7d075841 100644 --- a/rinex/src/navigation/ephemeris.rs +++ b/rinex/src/navigation/ephemeris.rs @@ -17,6 +17,9 @@ use thiserror::Error; use std::str::FromStr; use std::collections::HashMap; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// Parsing errors #[derive(Debug, Error)] pub enum Error { @@ -93,6 +96,7 @@ pub enum Error { /// ``` #[derive(Clone, Debug)] #[derive(PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Ephemeris { /// Clock bias [s] @@ -117,12 +121,29 @@ impl Default for Ephemeris { } } +#[cfg_attr(feature = "pyo3", pymethods)] impl Ephemeris { /// Retrieve all clock fields (bias, drift, drift_rate) at once pub fn clock_data(&self) -> (f64,f64,f64) { (self.clock_bias, self.clock_drift, self.clock_drift_rate) } + + /// Computes elevation angle. Useful macro so the user + /// does not have to either care for the Orbit field identification, + /// or involved computations + pub fn elevation_angle(&self) -> Option { + if let Some(e) = self.orbits.get("e") { + e.as_f64() + } else { + // Orbit field was either missing + // but what about glonass ?? + None + } + } +} + +impl Ephemeris { /// Parses ephemeris from given line iterator pub fn parse_v2v3(version: Version, constellation: Constellation, mut lines: std::str::Lines<'_>) -> Result<(Epoch, Sv, Self), Error> { let line = match lines.next() { @@ -208,19 +229,6 @@ impl Ephemeris { }, )) } - - /// Computes elevation angle. Useful macro so the user - /// does not have to either care for the Orbit field identification, - /// or involved computations - pub fn elevation_angle(&self) -> Option { - if let Some(e) = self.orbits.get("e") { - e.as_f64() - } else { - // Orbit field was either missing - // but what about glonass ?? - None - } - } } /// Parses constellation + revision dependent orbits data diff --git a/rinex/src/navigation/health.rs b/rinex/src/navigation/health.rs index 33d23c2ba..a74c8a09b 100644 --- a/rinex/src/navigation/health.rs +++ b/rinex/src/navigation/health.rs @@ -1,9 +1,13 @@ use bitflags::bitflags; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// GNSS / GPS orbit health indication #[derive(Debug, Clone)] #[derive(FromPrimitive)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum Health { Unhealthy = 0, @@ -41,6 +45,7 @@ impl std::fmt::UpperExp for Health { #[derive(Debug, Clone)] #[derive(FromPrimitive)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum IrnssHealth { Healthy = 0, @@ -66,6 +71,7 @@ impl std::fmt::UpperExp for IrnssHealth { #[derive(Debug, Clone)] #[derive(FromPrimitive)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum GeoHealth { Unknown = 0, @@ -91,6 +97,7 @@ impl std::fmt::UpperExp for GeoHealth { #[derive(Debug, Clone)] #[derive(FromPrimitive)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum GloHealth { Healthy = 0, @@ -115,6 +122,7 @@ impl std::fmt::UpperExp for GloHealth { bitflags! { /// GAL orbit health indication #[derive(Default)] + #[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct GalHealth: u8 { const E1B_DVS = 0x01; diff --git a/rinex/src/navigation/ionmessage.rs b/rinex/src/navigation/ionmessage.rs index 14f391162..e7a8ca89c 100644 --- a/rinex/src/navigation/ionmessage.rs +++ b/rinex/src/navigation/ionmessage.rs @@ -6,6 +6,9 @@ use thiserror::Error; use std::str::FromStr; use bitflags::bitflags; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// Model parsing error #[derive(Debug, Error)] pub enum Error { @@ -36,6 +39,7 @@ pub enum Error { /// Klobuchar Parameters region #[derive(Debug, Copy, Clone)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum KbRegionCode { /// Coefficients apply to wide area @@ -55,6 +59,7 @@ impl Default for KbRegionCode { #[derive(Default)] #[derive(Debug, Copy, Clone)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct KbModel { /// Alpha coefficients @@ -131,6 +136,7 @@ impl KbModel { bitflags! { #[derive(Default)] + #[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct NgRegionFlags: u16 { const REGION5 = 0x01; @@ -145,6 +151,7 @@ bitflags! { #[derive(Debug, Clone)] #[derive(Default, Copy)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct NgModel { /// a_i coefficients @@ -187,6 +194,7 @@ impl NgModel { #[derive(Debug, Copy, Clone)] #[derive(Default)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct BdModel { /// Alpha coefficients [TECu] diff --git a/rinex/src/navigation/record.rs b/rinex/src/navigation/record.rs index 38225ac9b..9dc36273b 100644 --- a/rinex/src/navigation/record.rs +++ b/rinex/src/navigation/record.rs @@ -5,6 +5,9 @@ use strum_macros::EnumString; use regex::{Regex, Captures}; use std::collections::BTreeMap; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + use crate::{ sv, epoch, @@ -37,6 +40,7 @@ use hifitime::Duration; #[derive(PartialEq, PartialOrd)] #[derive(Eq, Ord)] #[derive(EnumString)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum FrameClass { #[strum(serialize = "EPH")] @@ -71,6 +75,7 @@ impl std::fmt::Display for FrameClass { #[derive(PartialEq, PartialOrd)] #[derive(Eq, Ord)] #[derive(EnumString)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MsgType { /// Legacy NAV diff --git a/rinex/src/navigation/stomessage.rs b/rinex/src/navigation/stomessage.rs index d5189fc43..ffd2db79e 100644 --- a/rinex/src/navigation/stomessage.rs +++ b/rinex/src/navigation/stomessage.rs @@ -5,6 +5,9 @@ use crate::{ }; use hifitime::Epoch; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// Parsing error #[derive(Debug, Error)] pub enum Error { @@ -43,6 +46,7 @@ pub enum Error { #[derive(Debug, Clone)] #[derive(Default)] #[derive(PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct StoMessage { /// Time System diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 3826146ce..5f9e03c39 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -6,21 +6,37 @@ use crate::{ Crinex, record::*, }, + hardware::*, + navigation::*, }; +impl std::convert::From for PyErr { + fn from(err: Error) -> PyErr { + PyException::new_err(err.to_string()) + } +} + #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { /* - * prelude module + * TODO: prelude module */ m.add_class::()?; m.add_class::()?; + m.add_class::()?; + m.add_class::()?; /* - * Observation module + * TODO: Observation module */ m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; + /* + * TODO: Navigation module + */ + m.add_class::()?; + m.add_class::()?; + m.add_class::()?; Ok(()) } From 98d926cb3423835d7c9213f9ba1f10b448359912 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Fri, 2 Dec 2022 21:58:13 +0100 Subject: [PATCH 26/46] python.rs: add a few more bindings Signed-off-by: Guillaume W. Bres --- rinex/src/constellation/augmentation.rs | 8 ++++++-- rinex/src/constellation/mod.rs | 3 +++ rinex/src/header.rs | 4 ++++ rinex/src/python.rs | 6 ++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/rinex/src/constellation/augmentation.rs b/rinex/src/constellation/augmentation.rs index 9f35acba3..8d711fe5e 100644 --- a/rinex/src/constellation/augmentation.rs +++ b/rinex/src/constellation/augmentation.rs @@ -2,14 +2,18 @@ //! mainly used for high precision positioning use strum_macros::EnumString; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[cfg(feature = "serde")] use serde::{Serialize, Deserialize}; +/// GNSS Augmentation systems, +/// must be used based on current location #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[derive(EnumString)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -/// GNSS Augmentation systems, -/// must be used based on current location pub enum Augmentation { /// Augmentation Unknown Unknown, diff --git a/rinex/src/constellation/mod.rs b/rinex/src/constellation/mod.rs index fdc2b96bc..0b165298a 100644 --- a/rinex/src/constellation/mod.rs +++ b/rinex/src/constellation/mod.rs @@ -6,6 +6,9 @@ pub use augmentation::{ Augmentation, }; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[cfg(feature = "sbas")] pub use augmentation::selection_helper; diff --git a/rinex/src/header.rs b/rinex/src/header.rs index 862803148..6129f118c 100644 --- a/rinex/src/header.rs +++ b/rinex/src/header.rs @@ -53,9 +53,13 @@ use serde::{Serialize, Deserialize}; #[cfg(feature = "serde")] use crate::formatter::opt_point3d; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[derive(Clone, Debug)] #[derive(PartialEq, Eq)] #[derive(EnumString)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum MarkerType { /// Earth fixed & high precision diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 5f9e03c39..3536855a2 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -6,6 +6,9 @@ use crate::{ Crinex, record::*, }, + header::{ + MarkerType, + }, hardware::*, navigation::*, }; @@ -25,6 +28,9 @@ fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; + // header + m.add_class::()?; /* * TODO: Observation module */ From 834518f7f7e8eda14bc33924b575168766cc2fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 2 Dec 2022 21:59:36 +0100 Subject: [PATCH 27/46] add topic --- rinex/pyproject.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rinex/pyproject.toml b/rinex/pyproject.toml index d3658530c..48a3f2fc3 100644 --- a/rinex/pyproject.toml +++ b/rinex/pyproject.toml @@ -7,9 +7,10 @@ name = "rinex" requires-python = ">=3.8" classifiers = [ "Development Status :: 4 - Beta", - "Topic :: Scientific/Engineering :: Physics", "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", "License :: OSI Approved :: MIT License", - "License :: OSI Approved :: Apache Software License" + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering :: Atmospheric Science", ] From 9de6af20d1adbff3a97cdc9b62be1796e3f67ba7 Mon Sep 17 00:00:00 2001 From: gwbres Date: Fri, 2 Dec 2022 22:31:26 +0100 Subject: [PATCH 28/46] python.yml: archive, github release --- .github/workflows/python.yml | 42 ++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 3f3c690f0..6d35194df 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -14,7 +14,6 @@ jobs: py_ver: ['3.8', '3.9', '3.10'] steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@stable - uses: actions/setup-python@v4 with: python-version: ${{ matrix.py_ver }} @@ -35,6 +34,19 @@ jobs: with: name: wheels path: dist + - name: Archive + run: | + cd target/release + tar czvf rinex.tar.gz rinex + cd - + - name: Gthub Release + uses: svenstaro/upload-release-action@v2 + if: "startsWith(github_ref, 'refs/tags/')" + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + asset_name: rinex-python${{ matrix.pyver }}-apple-darwin.tar.gz + file: target/release/rinex.tar.gz + tag: ${{ github.ref }} windows: runs-on: windows-latest @@ -63,7 +75,20 @@ jobs: with: name: wheels path: dist - + - name: Archive + run: | + cd target/release + zip rinex.zip rinex + cd - + - name: Gthub Release + uses: svenstaro/upload-release-action@v2 + if: "startsWith(github_ref, 'refs/tags/')" + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + asset_name: rinex-python${{ matrix.pyver }}-windows-${{ matrix.target }}.zip + file: target/release/rinex.zip + tag: ${{ github.ref }} + linux: runs-on: ubuntu-latest strategy: @@ -92,3 +117,16 @@ jobs: with: name: wheels path: dist + - name: Archive + run: | + cd target/release + tar czvf rinex.tar.gz rinex + cd - + - name: Gthub Release + uses: svenstaro/upload-release-action@v2 + if: "startsWith(github_ref, 'refs/tags/')" + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + asset_name: rinex-python${{ matrix.pyver }}-linux-${{ matrix.target }}.tar.gz + file: target/release/rinex.tar.gz + tag: ${{ github.ref }} From 1e3f528de2aea551082a44771c657a80811e8eca Mon Sep 17 00:00:00 2001 From: gwbres Date: Fri, 2 Dec 2022 22:34:34 +0100 Subject: [PATCH 29/46] python.yml: archive, github release --- .github/workflows/python.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 6d35194df..9b48ebe76 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -41,7 +41,7 @@ jobs: cd - - name: Gthub Release uses: svenstaro/upload-release-action@v2 - if: "startsWith(github_ref, 'refs/tags/')" + if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-apple-darwin.tar.gz @@ -82,7 +82,7 @@ jobs: cd - - name: Gthub Release uses: svenstaro/upload-release-action@v2 - if: "startsWith(github_ref, 'refs/tags/')" + if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-windows-${{ matrix.target }}.zip @@ -124,7 +124,7 @@ jobs: cd - - name: Gthub Release uses: svenstaro/upload-release-action@v2 - if: "startsWith(github_ref, 'refs/tags/')" + if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-linux-${{ matrix.target }}.tar.gz From 2aab1c7bea7e874aa64ccd07ccedbee21b14736c Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 3 Dec 2022 14:33:18 +0100 Subject: [PATCH 30/46] python.yml * try to build all wheels on ubuntu-latest docker * get rid of unnecessary artifact --- .github/workflows/python.yml | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9b48ebe76..2a3ff2843 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -8,7 +8,7 @@ on: jobs: macos: - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: py_ver: ['3.8', '3.9', '3.10'] @@ -29,17 +29,12 @@ jobs: run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Upload - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - name: Archive + - name: Prepare for upload run: | cd target/release tar czvf rinex.tar.gz rinex cd - - - name: Gthub Release + - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: @@ -49,7 +44,7 @@ jobs: tag: ${{ github.ref }} windows: - runs-on: windows-latest + runs-on: ubuntu-latest strategy: matrix: target: [x64, x86] @@ -70,17 +65,12 @@ jobs: run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Upload - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - name: Archive + - name: Prepare for upload run: | cd target/release zip rinex.zip rinex cd - - - name: Gthub Release + - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: @@ -112,17 +102,12 @@ jobs: run: | pip3 install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Upload - uses: actions/upload-artifact@v3 - with: - name: wheels - path: dist - - name: Archive + - name: Prepare for upload run: | cd target/release tar czvf rinex.tar.gz rinex cd - - - name: Gthub Release + - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: From 0e1376b9b5a2c32b08297e64fca504971433c5ba Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 3 Dec 2022 14:43:58 +0100 Subject: [PATCH 31/46] python.yml * windows docker * -i Python for macos generated on ubuntu docker * linux: test wheel prior anythint --- .github/workflows/python.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 2a3ff2843..e2da18dc7 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -24,8 +24,8 @@ jobs: uses: PyO3/maturin-action@v1 with: target: x86_64-apple-darwin - args: --release --all-features -m rinex/Cargo.toml --out dist --sdist - - name: Install + args: --release -i python${{ matrix.py_ver }} --all-features -m rinex/Cargo.toml --out dist --sdist + - name: Test wheel run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" @@ -44,7 +44,7 @@ jobs: tag: ${{ github.ref }} windows: - runs-on: ubuntu-latest + runs-on: windows-latest strategy: matrix: target: [x64, x86] @@ -61,7 +61,7 @@ jobs: with: target: ${{ matrix.target }} args: --release --all-features -m rinex/Cargo.toml --out dist - - name: Install + - name: Test wheel run: | pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" @@ -102,11 +102,12 @@ jobs: run: | pip3 install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - - name: Prepare for upload + - name: Test wheel run: | - cd target/release - tar czvf rinex.tar.gz rinex - cd - + pip install rinex --no-index --find-links dist --force-reinstall + python -c "import rinex" + - name: Prepare for upload + run: tar czvf rinex.tar.gz rinex - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" From 97e11040c5e10c4a1bf090294636b8ea87ee8677 Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 3 Dec 2022 15:05:56 +0100 Subject: [PATCH 32/46] python.yml * macos docker image * windows docker image * fix upload preparation --- .github/workflows/python.yml | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e2da18dc7..d3b37c4cd 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -8,7 +8,7 @@ on: jobs: macos: - runs-on: ubuntu-latest + runs-on: macos-latest strategy: matrix: py_ver: ['3.8', '3.9', '3.10'] @@ -30,17 +30,14 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: | - cd target/release - tar czvf rinex.tar.gz rinex - cd - + run: tar czvf dist/*.whl rinex.tar.gz - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-apple-darwin.tar.gz - file: target/release/rinex.tar.gz + file: rinex.tar.gz tag: ${{ github.ref }} windows: @@ -66,17 +63,14 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: | - cd target/release - zip rinex.zip rinex - cd - + run: Compress-Archive dist/*.whl rinex.zip - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-windows-${{ matrix.target }}.zip - file: target/release/rinex.zip + file: rinex.zip tag: ${{ github.ref }} linux: @@ -107,12 +101,12 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: tar czvf rinex.tar.gz rinex + run: tar czvf dist/*.whl rinex.tar.gz - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" with: repo_token: ${{ secrets.GITHUB_TOKEN }} asset_name: rinex-python${{ matrix.pyver }}-linux-${{ matrix.target }}.tar.gz - file: target/release/rinex.tar.gz + file: rinex.tar.gz tag: ${{ github.ref }} From 061cff46dfa7e76cfc5e39a4ca17b8afc0552839 Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 3 Dec 2022 16:58:46 +0100 Subject: [PATCH 33/46] python.yml: package and release --- .github/workflows/python.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index d3b37c4cd..62c0b2c90 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -30,7 +30,7 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: tar czvf dist/*.whl rinex.tar.gz + run: tar czvf rinex.tar.gz dist/*.whl - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" @@ -63,7 +63,9 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: Compress-Archive dist/*.whl rinex.zip + run: | + Compress-Archive dist/*.whl rinex.zip + dir - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" @@ -101,7 +103,10 @@ jobs: pip install rinex --no-index --find-links dist --force-reinstall python -c "import rinex" - name: Prepare for upload - run: tar czvf dist/*.whl rinex.tar.gz + run: | + ls dist/*.whl + ls dist/ + tar czvf rinex.tar.gz dist/*.whl - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" From 3504e3ebb68bb1e6585a5c87f205dbd5d9b51e6e Mon Sep 17 00:00:00 2001 From: gwbres Date: Sat, 3 Dec 2022 17:17:00 +0100 Subject: [PATCH 34/46] python.yml - continue on wheel upload/build failures --- .github/workflows/python.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 62c0b2c90..b76a01e04 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -9,6 +9,7 @@ on: jobs: macos: runs-on: macos-latest + continue-on-error: true strategy: matrix: py_ver: ['3.8', '3.9', '3.10'] @@ -42,6 +43,7 @@ jobs: windows: runs-on: windows-latest + continue-on-error: true strategy: matrix: target: [x64, x86] @@ -77,6 +79,7 @@ jobs: linux: runs-on: ubuntu-latest + continue-on-error: true strategy: matrix: target: [x64] @@ -104,9 +107,8 @@ jobs: python -c "import rinex" - name: Prepare for upload run: | - ls dist/*.whl - ls dist/ tar czvf rinex.tar.gz dist/*.whl + ls -lah rinex.tar.gz - name: Github Release uses: svenstaro/upload-release-action@v2 if: "startsWith(github.ref, 'refs/tags/')" From b25150acd96dccdf3daa6b732eb9dbb07549a89c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Dec 2022 02:33:01 +0100 Subject: [PATCH 35/46] simplify and align a bit with upstream --- README.md | 2 +- rinex/src/constellation/mod.rs | 4 +--- rinex/src/lib.rs | 3 +-- rinex/src/version.rs | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2daa222c5..66a4c3580 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ allow native parsing of .gz compressed RINEX files. Otherwise, user must uncompr * `--pyo3` (experimental) Add Python bindings via `PyO3`. To build the Python package, you must first install maturin and then build it with the pyo3 feature flag. For example, `maturin build -F pyo3`. Maturin will then build and place the resulting .whl file in `/target/wheels/`, after which you can install the package with `pip install rinex`. -## Performances +## `rinex-cli` benchmark Parsing and `--sv` enumeration requested with `rinex-cli` diff --git a/rinex/src/constellation/mod.rs b/rinex/src/constellation/mod.rs index 0b165298a..7430cb538 100644 --- a/rinex/src/constellation/mod.rs +++ b/rinex/src/constellation/mod.rs @@ -2,12 +2,10 @@ use thiserror::Error; mod augmentation; -pub use augmentation::{ - Augmentation, -}; #[cfg(feature = "pyo3")] use pyo3::prelude::*; +pub use augmentation::Augmentation; #[cfg(feature = "sbas")] pub use augmentation::selection_helper; diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 54b8e2315..0bc77c89d 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -151,8 +151,7 @@ macro_rules! hourly_session { } } -#[derive(Clone, Debug)] -#[derive(PartialEq)] +#[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "pyo3", pyclass)] /// `Rinex` describes a `RINEX` file. /// ``` diff --git a/rinex/src/version.rs b/rinex/src/version.rs index 34b66e171..c1aa85794 100644 --- a/rinex/src/version.rs +++ b/rinex/src/version.rs @@ -9,8 +9,7 @@ pub const SUPPORTED_VERSION: Version = Version { minor: 0 }; -#[derive(Copy, Clone, Debug)] -#[derive(PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Version { From 7a420fb4ed3ceea9b00acb18598d6c8d5a44f231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Dec 2022 17:51:59 +0100 Subject: [PATCH 36/46] fix a few compile errors due to wrong merge --- rinex/src/epoch/flag.rs | 4 ++-- rinex/src/header.rs | 3 --- rinex/src/observation/record.rs | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/rinex/src/epoch/flag.rs b/rinex/src/epoch/flag.rs index f97977779..c83542597 100644 --- a/rinex/src/epoch/flag.rs +++ b/rinex/src/epoch/flag.rs @@ -44,8 +44,8 @@ impl Default for EpochFlag { #[cfg_attr(feature = "pyo3", pymethods)] impl EpochFlag { /// Returns True if self is a valid epoch - pub fn is_ok(self) -> bool { - self == Self::Ok + pub fn is_ok(&self) -> bool { + *self == Self::Ok } } diff --git a/rinex/src/header.rs b/rinex/src/header.rs index b825547c6..90528dd1a 100644 --- a/rinex/src/header.rs +++ b/rinex/src/header.rs @@ -39,9 +39,6 @@ macro_rules! from_b_fmt_month { #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -#[cfg(feature = "serde")] -use crate::formatter::opt_point3d; - #[cfg(feature = "pyo3")] use pyo3::prelude::*; diff --git a/rinex/src/observation/record.rs b/rinex/src/observation/record.rs index 44eae1dfb..d4f1b761a 100644 --- a/rinex/src/observation/record.rs +++ b/rinex/src/observation/record.rs @@ -110,23 +110,23 @@ impl FromStr for Ssi { impl Ssi { /// Returns true if `self` is a bad signal level, very poor quality, /// measurements should be discarded - pub fn is_bad(self) -> bool { - self <= Ssi::DbHz18_23 + pub fn is_bad(&self) -> bool { + self <= &Ssi::DbHz18_23 } /// Returns true if `self` is a weak signal level, poor quality - pub fn is_weak(self) -> bool { - self < Ssi::DbHz30_35 + pub fn is_weak(&self) -> bool { + self < &Ssi::DbHz30_35 } /// Returns true if `self` is a strong signal level, good quality as defined by standard - pub fn is_strong(self) -> bool { - self >= Ssi::DbHz30_35 + pub fn is_strong(&self) -> bool { + self >= &Ssi::DbHz30_35 } /// Returns true if `self` is a very strong signal level, very high quality - pub fn is_excellent(self) -> bool { - self > Ssi::DbHz42_47 + pub fn is_excellent(&self) -> bool { + self > &Ssi::DbHz42_47 } /// Returns true if `self` matches a strong signal level (defined by standard) - pub fn is_ok(self) -> bool { + pub fn is_ok(&self) -> bool { self.is_strong() } } @@ -175,7 +175,7 @@ impl ObservationData { /// + LLI must match the LliFlags::OkOrUnknown flag (strictly) /// if SSI exists: /// + SSI must match the .is_ok() criteria, refer to API - pub fn is_ok(self) -> bool { + pub fn is_ok(&self) -> bool { let lli_ok = self.lli.unwrap_or(LliFlags::OK_OR_UNKNOWN) == LliFlags::OK_OR_UNKNOWN; let ssi_ok = self.ssi.unwrap_or(Ssi::default()).is_ok(); lli_ok && ssi_ok From 4a69bb7d4e7e4a959de51e886088f9647198f969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Dec 2022 18:49:40 +0100 Subject: [PATCH 37/46] formatting --- rinex/src/constellation/mod.rs | 2 +- rinex/src/epoch/flag.rs | 2 +- rinex/src/hardware.rs | 2 +- rinex/src/navigation/ephemeris.rs | 3 +-- rinex/src/navigation/record.rs | 2 +- rinex/src/observation/record.rs | 6 +++--- rinex/src/python.rs | 11 +++-------- 7 files changed, 11 insertions(+), 17 deletions(-) diff --git a/rinex/src/constellation/mod.rs b/rinex/src/constellation/mod.rs index f8dbc1977..201a1fedb 100644 --- a/rinex/src/constellation/mod.rs +++ b/rinex/src/constellation/mod.rs @@ -2,9 +2,9 @@ use thiserror::Error; mod augmentation; +pub use augmentation::Augmentation; #[cfg(feature = "pyo3")] use pyo3::prelude::*; -pub use augmentation::Augmentation; #[cfg(feature = "sbas")] pub use augmentation::selection_helper; diff --git a/rinex/src/epoch/flag.rs b/rinex/src/epoch/flag.rs index c83542597..ee5f408b0 100644 --- a/rinex/src/epoch/flag.rs +++ b/rinex/src/epoch/flag.rs @@ -13,7 +13,7 @@ pub enum Error { #[cfg(feature = "pyo3")] use pyo3::prelude::*; -/// `EpochFlag` validates an epoch, +/// `EpochFlag` validates an epoch, /// or describes possible events that occurred #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/rinex/src/hardware.rs b/rinex/src/hardware.rs index 17f6fffcc..84eb8cddd 100644 --- a/rinex/src/hardware.rs +++ b/rinex/src/hardware.rs @@ -45,7 +45,7 @@ impl std::str::FromStr for Rcvr { } } -/// Antenna description +/// Antenna description #[cfg_attr(feature = "pyo3", pyclass)] #[derive(Debug, Clone, Default, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/rinex/src/navigation/ephemeris.rs b/rinex/src/navigation/ephemeris.rs index 0040ef735..d73d54965 100644 --- a/rinex/src/navigation/ephemeris.rs +++ b/rinex/src/navigation/ephemeris.rs @@ -421,8 +421,7 @@ impl Ephemeris { Some((map_3d::rad2deg(elev), azim)) } - -/// Parses ephemeris from given line iterator + /// Parses ephemeris from given line iterator pub fn parse_v2v3( version: Version, constellation: Constellation, diff --git a/rinex/src/navigation/record.rs b/rinex/src/navigation/record.rs index 65fd42986..25e9aa53e 100644 --- a/rinex/src/navigation/record.rs +++ b/rinex/src/navigation/record.rs @@ -67,7 +67,7 @@ impl std::fmt::Display for FrameClass { } } -/// Navigation Message Types +/// Navigation Message Types #[cfg_attr(feature = "pyo3", pyclass)] #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, EnumString)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/rinex/src/observation/record.rs b/rinex/src/observation/record.rs index d4f1b761a..1d7eff44a 100644 --- a/rinex/src/observation/record.rs +++ b/rinex/src/observation/record.rs @@ -1,7 +1,7 @@ -use std::str::FromStr; -use thiserror::Error; use bitflags::bitflags; use std::collections::{BTreeMap, HashMap}; +use std::str::FromStr; +use thiserror::Error; use crate::{ constellation, epoch, gnss_time::TimeScaling, merge, merge::Merge, prelude::*, @@ -191,7 +191,7 @@ impl ObservationData { pub fn pr_real_distance(&self, rcvr_offset: f64, sv_offset: f64, biases: f64) -> f64 { self.obs + 299_792_458.0_f64 * (rcvr_offset - sv_offset) + biases } - + #[cfg(feature = "pyo3")] #[getter] fn get_obs(&self) -> f64 { diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 3536855a2..a627740e3 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -1,16 +1,11 @@ use pyo3::{exceptions::PyException, prelude::*}; use crate::{ - prelude::*, - observation::{ - Crinex, - record::*, - }, - header::{ - MarkerType, - }, hardware::*, + header::MarkerType, navigation::*, + observation::{record::*, Crinex}, + prelude::*, }; impl std::convert::From for PyErr { From 31a43d5cdc69da0948527e87e7caed92f8623fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 18 Dec 2022 20:20:25 +0100 Subject: [PATCH 38/46] add the new classes --- rinex/src/navigation/ephemeris.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rinex/src/navigation/ephemeris.rs b/rinex/src/navigation/ephemeris.rs index d73d54965..889c22b05 100644 --- a/rinex/src/navigation/ephemeris.rs +++ b/rinex/src/navigation/ephemeris.rs @@ -120,6 +120,7 @@ impl Default for Ephemeris { /// Kepler parameters #[derive(Clone, Debug)] +#[cfg_attr(feature = "pyo3", pyclass)] pub struct Kepler { /// sqrt(semi major axis) [sqrt(m)] pub a: f64, @@ -146,6 +147,7 @@ impl Kepler { /// Perturbation parameters #[derive(Clone, Debug)] +#[cfg_attr(feature = "pyo3", pyclass)] pub struct Perturbations { /// Mean motion difference from computed value [semicircles.s-1] pub dn: f64, From fb52f5d7b5e5cf95d77f0b27bc4ccd6bd1b64df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sun, 28 May 2023 17:11:01 +0200 Subject: [PATCH 39/46] update PyO3 to match HiFiTime --- rinex/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 8a4904c70..1e4e2e534 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -36,7 +36,7 @@ geo = { version = "0.24.1", optional = true } wkt = { version = "0.10.0", default-features = false, optional = true } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } flate2 = { version = "1.0.24", optional = true, default-features = false, features = ["zlib"] } -pyo3 = { version = "0.17.3", default-features = false, features = ["extension-module"], optional = true} +pyo3 = { version = "0.18.3", default-features = false, features = ["extension-module"], optional = true} hifitime = { version = "3.8", features = ["serde", "std"] } horrorshow = { version = "0.8" } From bb5329bf0458af4305145a0ab3abd60a5d7b8203 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 15:51:52 +0200 Subject: [PATCH 40/46] declare ephemeris type Signed-off-by: Guillaume W. Bres --- rinex/src/navigation/ephemeris.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rinex/src/navigation/ephemeris.rs b/rinex/src/navigation/ephemeris.rs index 2bb0901f3..e5a6fbccd 100644 --- a/rinex/src/navigation/ephemeris.rs +++ b/rinex/src/navigation/ephemeris.rs @@ -94,8 +94,8 @@ pub enum Error { /// } /// } /// ``` -#[cfg_attr(feature = "pyo3", pyclass)] #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize))] pub struct Ephemeris { /// Clock bias [s] @@ -421,6 +421,9 @@ impl Ephemeris { } Some((map_3d::rad2deg(elev), azim)) } +} + +impl Ephemeris { /* * Parses ephemeris from given line iterator */ From 665c38b80e1c24aa6b7dc0baa685d7dafd563470 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 15:52:05 +0200 Subject: [PATCH 41/46] use latest pyo3 Signed-off-by: Guillaume W. Bres --- rinex/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rinex/Cargo.toml b/rinex/Cargo.toml index 0ea062fcb..95ba85f29 100644 --- a/rinex/Cargo.toml +++ b/rinex/Cargo.toml @@ -37,8 +37,8 @@ geo = { version = "0.25", optional = true } wkt = { version = "0.10.0", default-features = false, optional = true } serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } flate2 = { version = "1.0.24", optional = true, default-features = false, features = ["zlib"] } -pyo3 = { version = "0.18.3", default-features = false, features = ["extension-module"], optional = true} -hifitime = { version = "3.8", features = ["serde", "std"] } +pyo3 = { version = "0.19", default-features = false, features = ["extension-module"], optional = true} +hifitime = { git = "https://github.com/nyx-space/hifitime", branch = "master", features = ["serde", "std"] } horrorshow = { version = "0.8" } statrs = "0.16" From 381f5844afd668222f6afe0d27226905d8bacf27 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 15:52:27 +0200 Subject: [PATCH 42/46] version bindings Signed-off-by: Guillaume W. Bres --- rinex/src/version.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/rinex/src/version.rs b/rinex/src/version.rs index 99828f414..0f6f6430d 100644 --- a/rinex/src/version.rs +++ b/rinex/src/version.rs @@ -21,20 +21,6 @@ impl Default for Version { } } -#[cfg_attr(feature = "pyo3", pymethods)] -impl Version { - #[cfg(feature = "pyo3")] - #[getter] - fn get_major(&self) -> u8 { - self.major - } - #[cfg(feature = "pyo3")] - #[getter] - fn get_minor(&self) -> u8 { - self.minor - } -} - impl std::fmt::Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}.{}", self.major, self.minor) @@ -60,11 +46,23 @@ impl std::str::FromStr for Version { } } +#[cfg_attr(feature = "pyo3", pymethods)] impl Version { /// Builds a new `Version` object + #[new] pub fn new(major: u8, minor: u8) -> Self { Self { major, minor } } + #[cfg(feature = "pyo3")] + #[getter] + fn get_major(&self) -> u8 { + self.major + } + #[cfg(feature = "pyo3")] + #[getter] + fn get_minor(&self) -> u8 { + self.minor + } /// Returns true if this version is supported pub fn is_supported(&self) -> bool { if self.major < SUPPORTED_VERSION.major { From 9951a82b59921bbdcd43bcc825e253c34e3fe268 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 17:02:54 +0200 Subject: [PATCH 43/46] python: correct build instructions Signed-off-by: Guillaume W. Bres --- doc/python.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/python.md b/doc/python.md index edc336467..f7b2e504c 100644 --- a/doc/python.md +++ b/doc/python.md @@ -3,18 +3,19 @@ Python3 wheel Install requirements: python3 (>3.8) and maturin -Then build the python bindings with `maturin` +Then build the python bindings: use `maturin` to build the crate +with the `pyo3` feature ```bash cd rinex/ -maturin -F pyo3 +maturin build -F pyo3 ``` -A pip3 wheel is generated for your architecture. Use pip3 once again, -to install the library +A pip3 wheel is generated for your architecture. +Use pip3 to install the library ```bash -pip3 --force-reinstall install rinex/target/wheels/rinex-xxx.whl +pip3 install --force-reinstall rinex/target/wheels/rinex-xxx.whl ``` Now move on to the provided examples, run the basics with From a86f83688b40a526c6b7cce36c7f1f2a6b122f43 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 17:04:05 +0200 Subject: [PATCH 44/46] add more bindings Signed-off-by: Guillaume W. Bres --- rinex/examples/python/basic.py | 26 +++++++++++++++++++++++++- rinex/src/constellation/mod.rs | 14 ++++++++++++-- rinex/src/ground_position.rs | 4 ++++ rinex/src/header.rs | 20 +++++++++++++------- rinex/src/lib.rs | 4 ++-- rinex/src/navigation/orbits.rs | 3 +++ rinex/src/observable.rs | 3 +++ rinex/src/observation/mod.rs | 3 ++- rinex/src/python.rs | 9 ++++++++- rinex/src/sv.rs | 5 +++++ rinex/src/types.rs | 4 ++++ 11 files changed, 81 insertions(+), 14 deletions(-) diff --git a/rinex/examples/python/basic.py b/rinex/examples/python/basic.py index 9e57ef5ad..6b22bc12d 100644 --- a/rinex/examples/python/basic.py +++ b/rinex/examples/python/basic.py @@ -1,4 +1,28 @@ from rinex import * +# rinex::prelude basic examples, +# This example program depicts how you can interact with +# all the basic structures from the rust crate + +def parser_example(fp): + pass + +def rinex_manual_constructor(): + pass + +def sv_example(): + pass + +def constellation_example(): + pass + + +def epoch_example(): + print("Epoch.system_now(): ", Epoch.system_now()) + if __name__ == "__main__": - print(Epoch.system_now()) + parser_example("test") + epoch_example() + rinex_manual_constructor() + sv_example() + constellation_example() diff --git a/rinex/src/constellation/mod.rs b/rinex/src/constellation/mod.rs index ac1d1214f..2a19aef98 100644 --- a/rinex/src/constellation/mod.rs +++ b/rinex/src/constellation/mod.rs @@ -3,8 +3,6 @@ use thiserror::Error; mod augmentation; pub use augmentation::Augmentation; -#[cfg(feature = "pyo3")] -use pyo3::prelude::*; #[cfg(feature = "sbas")] pub use augmentation::selection_helper; @@ -12,6 +10,9 @@ pub use augmentation::selection_helper; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[derive(Error, Clone, Debug, PartialEq)] /// Constellation parsing & identification related errors pub enum Error { @@ -49,6 +50,15 @@ pub enum Constellation { Mixed, } +#[cfg(feature = "pyo3")] +use crate::prelude::Sv; +#[cfg(feature = "pyo3")] +impl From<&PyCell> for Constellation { + fn from(cell: &PyCell) -> Self { + Self::default() + } +} + impl std::fmt::Display for Constellation { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str(self.to_3_letter_code()) diff --git a/rinex/src/ground_position.rs b/rinex/src/ground_position.rs index 772a9a6e2..87c873100 100644 --- a/rinex/src/ground_position.rs +++ b/rinex/src/ground_position.rs @@ -3,7 +3,11 @@ use map_3d::{deg2rad, ecef2geodetic, geodetic2ecef, rad2deg, Ellipsoid}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[derive(Copy, Debug, Clone, PartialEq, PartialOrd)] +#[cfg_attr(feature = "pyo3", derive(FromPyObject))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GroundPosition(f64, f64, f64); diff --git a/rinex/src/header.rs b/rinex/src/header.rs index c44f6f500..da641c441 100644 --- a/rinex/src/header.rs +++ b/rinex/src/header.rs @@ -100,6 +100,7 @@ impl Default for MarkerType { /// Describes `RINEX` file header #[derive(Clone, Debug, PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Header { /// revision for this `RINEX` @@ -1269,7 +1270,16 @@ impl Header { }, }) } + /// Adds desired constellation to Self + pub fn with_constellation(&self, c: Constellation) -> Self { + let mut s = self.clone(); + s.constellation = Some(c); + s + } +} +#[cfg_attr(feature = "pyo3", pymethods)] +impl Header { /// Returns true if self is a `Compressed RINEX` pub fn is_crinex(&self) -> bool { if let Some(obs) = &self.obs { @@ -1281,6 +1291,7 @@ impl Header { /// Creates a Basic Header structure /// for Mixed Constellation Navigation RINEX + #[staticmethod] pub fn basic_nav() -> Self { Self::default() .with_type(Type::NavigationData) @@ -1289,6 +1300,7 @@ impl Header { /// Creates a Basic Header structure /// for Mixed Constellation Observation RINEX + #[staticmethod] pub fn basic_obs() -> Self { Self::default() .with_type(Type::ObservationData) @@ -1297,6 +1309,7 @@ impl Header { /// Creates Basic Header structure /// for Compact RINEX with Mixed Constellation context + #[staticmethod] pub fn basic_crinex() -> Self { Self::default() .with_type(Type::ObservationData) @@ -1351,13 +1364,6 @@ impl Header { s } - /// Adds desired constellation to Self - pub fn with_constellation(&self, c: Constellation) -> Self { - let mut s = self.clone(); - s.constellation = Some(c); - s - } - /// adds comments to Self pub fn with_comments(&self, c: Vec) -> Self { let mut s = self.clone(); diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 117703fc8..26324e675 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -42,10 +42,9 @@ use reader::BufferedReader; use std::io::Write; //, Read}; pub mod writer; -use writer::BufferedWriter; - use std::collections::{BTreeMap, HashMap}; use thiserror::Error; +use writer::BufferedWriter; use hifitime::Duration; use navigation::OrbitItem; @@ -257,6 +256,7 @@ pub enum Error { IoError(#[from] std::io::Error), } +//#[cfg_attr(feature = "pyo3", pymethods)] impl Rinex { /// Builds a new `RINEX` struct from given header & body sections pub fn new(header: Header, record: record::Record) -> Rinex { diff --git a/rinex/src/navigation/orbits.rs b/rinex/src/navigation/orbits.rs index 22602767f..b50f307cd 100644 --- a/rinex/src/navigation/orbits.rs +++ b/rinex/src/navigation/orbits.rs @@ -11,6 +11,9 @@ use thiserror::Error; include!(concat!(env!("OUT_DIR"), "/nav_orbits.rs")); +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + bitflags! { #[derive(Default, Debug, Clone)] #[derive(PartialEq, PartialOrd)] diff --git a/rinex/src/observable.rs b/rinex/src/observable.rs index 682237bf8..71f83dcce 100644 --- a/rinex/src/observable.rs +++ b/rinex/src/observable.rs @@ -1,6 +1,9 @@ use crate::{carrier, Carrier, Constellation}; use thiserror::Error; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + #[derive(Error, Debug, Clone, PartialEq)] pub enum Error { #[error("unknown observable")] diff --git a/rinex/src/observation/mod.rs b/rinex/src/observation/mod.rs index 3e1336139..cd944027b 100644 --- a/rinex/src/observation/mod.rs +++ b/rinex/src/observation/mod.rs @@ -33,8 +33,8 @@ macro_rules! fmt_month { use serde::Serialize; /// Describes `Compact RINEX` specific information -#[cfg_attr(feature = "pyo3", pyclass)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Crinex { /// Compression program version @@ -121,6 +121,7 @@ impl std::fmt::Display for Crinex { /// Describes known marker types /// Observation Record specific header fields #[derive(Debug, Clone, Default, PartialEq)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct HeaderFields { /// Optional CRINEX information diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 775f2f56c..01d2ba724 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -17,15 +17,21 @@ impl std::convert::From for PyErr { #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { /* - * TODO: prelude module + * TODO: follow the crate module names + * this should be the wrapped in the "prelude" module */ m.add_class::()?; m.add_class::()?; m.add_class::()?; m.add_class::()?; + //m.add_class::()?; m.add_class::()?; + m.add_class::()?; // header m.add_class::()?; + // rinex + m.add_class::
()?; + m.add_class::()?; /* * TODO: Observation module */ @@ -39,5 +45,6 @@ fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + Ok(()) } diff --git a/rinex/src/sv.rs b/rinex/src/sv.rs index 061c13497..fa7b0d6e1 100644 --- a/rinex/src/sv.rs +++ b/rinex/src/sv.rs @@ -5,8 +5,12 @@ use thiserror::Error; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// ̀`Sv` describes a Satellite Vehicle #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Sv { /// PRN identification # for this vehicle @@ -24,6 +28,7 @@ pub enum Error { ParseIntError(#[from] std::num::ParseIntError), } +#[cfg_attr(feature = "pyo3", pymethods)] impl Sv { /// Creates a new `Sv` pub fn new(constellation: Constellation, prn: u8) -> Self { diff --git a/rinex/src/types.rs b/rinex/src/types.rs index 4c5d5d6a1..bd8a43b0a 100644 --- a/rinex/src/types.rs +++ b/rinex/src/types.rs @@ -2,8 +2,12 @@ use super::Constellation; use thiserror::Error; +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + /// Describes all known `RINEX` file types #[derive(Copy, Clone, PartialEq, Debug)] +#[cfg_attr(feature = "pyo3", pyclass)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Type { /// Describes Observation Data (OBS), From 71826d9032cea13ec31d9d96d08fc87d25d82d0e Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 17:31:06 +0200 Subject: [PATCH 45/46] add more bindings Signed-off-by: Guillaume W. Bres --- rinex/examples/python/basic.py | 15 ++++++++++----- rinex/src/lib.rs | 23 +++++++++++++++-------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/rinex/examples/python/basic.py b/rinex/examples/python/basic.py index 6b22bc12d..3515aa6e5 100644 --- a/rinex/examples/python/basic.py +++ b/rinex/examples/python/basic.py @@ -5,10 +5,16 @@ # all the basic structures from the rust crate def parser_example(fp): - pass + # parse a RINEX file + rinex = Rinex(fp) + # use header section + print(rinex.header.is_crinex()) def rinex_manual_constructor(): - pass + # Manual construction example. + # This is handy in data production contexts + header = Header.basic_obs() + print(header.is_crinex()) def sv_example(): pass @@ -16,13 +22,12 @@ def sv_example(): def constellation_example(): pass - def epoch_example(): print("Epoch.system_now(): ", Epoch.system_now()) if __name__ == "__main__": - parser_example("test") + parser_example("../test_resources/OBS/V3/DUTH0630.22O") epoch_example() - rinex_manual_constructor() sv_example() + rinex_manual_constructor() constellation_example() diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index 26324e675..acd5bfb81 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -256,17 +256,24 @@ pub enum Error { IoError(#[from] std::io::Error), } -//#[cfg_attr(feature = "pyo3", pymethods)] +#[cfg_attr(feature = "pyo3", pymethods)] impl Rinex { - /// Builds a new `RINEX` struct from given header & body sections - pub fn new(header: Header, record: record::Record) -> Rinex { - Rinex { - header, - record, - comments: record::Comments::new(), - } + #[new] + fn new(path: String) -> Self { + Self::from_file(&path).unwrap() } + #[getter] + fn get_header(&self) -> PyResult
{ + Ok(self.header.clone()) + } + #[setter] + fn set_header(&mut self, header: Header) -> PyResult<()> { + self.replace_header(header); + Ok(()) + } +} +impl Rinex { /// Returns a copy of self with given header attributes pub fn with_header(&self, header: Header) -> Self { Rinex { From 5e0ba377e55dd538c7ecd1992e161c0270b3fab1 Mon Sep 17 00:00:00 2001 From: "Guillaume W. Bres" Date: Sun, 11 Jun 2023 22:35:26 +0200 Subject: [PATCH 46/46] working on complex cast Signed-off-by: Guillaume W. Bres --- rinex/examples/python/basic.py | 5 +- rinex/examples/python/observation.py | 2 +- rinex/src/constellation/mod.rs | 56 +++++++++++++++++--- rinex/src/header.rs | 4 ++ rinex/src/lib.rs | 15 +++++- rinex/src/observable.rs | 10 ++++ rinex/src/observation/mod.rs | 3 ++ rinex/src/observation/record.rs | 30 +++++++++++ rinex/src/python.rs | 79 +++++++++++++++++++++++++++- rinex/src/record.rs | 13 +++++ rinex/src/sv.rs | 15 +++++- 11 files changed, 221 insertions(+), 11 deletions(-) diff --git a/rinex/examples/python/basic.py b/rinex/examples/python/basic.py index 3515aa6e5..51bf0f329 100644 --- a/rinex/examples/python/basic.py +++ b/rinex/examples/python/basic.py @@ -8,7 +8,10 @@ def parser_example(fp): # parse a RINEX file rinex = Rinex(fp) # use header section - print(rinex.header.is_crinex()) + print("is_crinex: ", rinex.header.is_crinex()) + print("header : \n{:s}".format(str(rinex.header))) + # use record section + print(rinex.record) def rinex_manual_constructor(): # Manual construction example. diff --git a/rinex/examples/python/observation.py b/rinex/examples/python/observation.py index de179f4ec..f3b1fe090 100644 --- a/rinex/examples/python/observation.py +++ b/rinex/examples/python/observation.py @@ -7,5 +7,5 @@ observation = ObservationData(10.0) assert(observation.obs == 10.0) - assert(observation.ssi == None) + assert(observation.snr == None) assert(observation.lli == None) diff --git a/rinex/src/constellation/mod.rs b/rinex/src/constellation/mod.rs index 2a19aef98..8d6b7e58b 100644 --- a/rinex/src/constellation/mod.rs +++ b/rinex/src/constellation/mod.rs @@ -51,12 +51,12 @@ pub enum Constellation { } #[cfg(feature = "pyo3")] -use crate::prelude::Sv; -#[cfg(feature = "pyo3")] -impl From<&PyCell> for Constellation { - fn from(cell: &PyCell) -> Self { - Self::default() - } +#[cfg_attr(feature = "pyo3", pyclass)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PyConstellation { + GPS, + Glonass, + Geo, } impl std::fmt::Display for Constellation { @@ -197,6 +197,50 @@ impl std::str::FromStr for Constellation { } } +#[cfg(feature = "pyo3")] +impl IntoPy for Constellation { + fn into_py(self, py: Python<'_>) -> PyObject { + let pyc: PyConstellation = self.into(); + pyc.into_py(py) + } +} + +#[cfg(feature = "pyo3")] +impl From for PyConstellation { + fn from(c: Constellation) -> Self { + match c { + Constellation::SBAS(_) => Self::Geo, + Constellation::GPS => Self::GPS, + Constellation::Glonass => Self::Glonass, + _ => panic!("gnss not supported yet"), + } + } +} + +#[cfg(feature = "pyo3")] +impl From for Constellation { + fn from(c: PyConstellation) -> Self { + match c { + PyConstellation::GPS => Self::GPS, + PyConstellation::Glonass => Self::Glonass, + PyConstellation::Geo => Self::Geo, + } + } +} + +/*#[cfg(feature = "pyo3")] +use crate::prelude::Sv; +#[cfg(feature = "pyo3")] +impl From<&PyCell> for Constellation { + fn from(cell: &PyCell) -> Self { + if let Ok(sv) = cell.extract::() { + sv.constellation + } else { + Self::default() + } + } +}*/ + #[cfg(test)] mod tests { use super::*; diff --git a/rinex/src/header.rs b/rinex/src/header.rs index da641c441..0f565705b 100644 --- a/rinex/src/header.rs +++ b/rinex/src/header.rs @@ -1289,6 +1289,10 @@ impl Header { } } + fn __str__(&self) -> String { + self.to_string() + } + /// Creates a Basic Header structure /// for Mixed Constellation Navigation RINEX #[staticmethod] diff --git a/rinex/src/lib.rs b/rinex/src/lib.rs index acd5bfb81..8c5f29cf9 100644 --- a/rinex/src/lib.rs +++ b/rinex/src/lib.rs @@ -55,6 +55,9 @@ use version::Version; #[cfg(feature = "pyo3")] pub mod python; +#[cfg(feature = "pyo3")] +use python::PyMap; + // Convenient package to import, that // comprises all basic and major structures pub mod prelude { @@ -104,7 +107,7 @@ use algorithm::{Combination, Combine, Dcb, IonoDelayDetector, Mp, Smooth}; extern crate horrorshow; #[cfg(feature = "pyo3")] -use pyo3::prelude::*; +use pyo3::{exceptions::PyTypeError, prelude::*, types::PyDict}; #[cfg(feature = "serde")] #[macro_use] @@ -271,6 +274,16 @@ impl Rinex { self.replace_header(header); Ok(()) } + #[getter] + fn get_record(&self) -> PyResult { + if let Some(rec) = self.record.as_obs() { + Ok(observation::PyRecord::new()) + } else { + Err(PyTypeError::new_err( + "python binding not available for this type", + )) + } + } } impl Rinex { diff --git a/rinex/src/observable.rs b/rinex/src/observable.rs index 71f83dcce..1fe0508d6 100644 --- a/rinex/src/observable.rs +++ b/rinex/src/observable.rs @@ -55,6 +55,16 @@ impl Default for Observable { } } +#[cfg(feature = "pyo3")] +impl IntoPy for Observable { + fn into_py(self, py: Python<'_>) -> PyObject { + match self { + Self::Phase(code) => code.into_py(py), + _ => panic!("not yet"), + } + } +} + impl Observable { pub fn is_phase_observable(&self) -> bool { match self { diff --git a/rinex/src/observation/mod.rs b/rinex/src/observation/mod.rs index cd944027b..d57b60a9a 100644 --- a/rinex/src/observation/mod.rs +++ b/rinex/src/observation/mod.rs @@ -10,6 +10,9 @@ mod snr; pub use record::{LliFlags, ObservationData, Record}; pub use snr::Snr; +#[cfg(feature = "pyo3")] +pub use record::PyRecord; + macro_rules! fmt_month { ($m: expr) => { match $m { diff --git a/rinex/src/observation/record.rs b/rinex/src/observation/record.rs index 79de77994..e723a23dd 100644 --- a/rinex/src/observation/record.rs +++ b/rinex/src/observation/record.rs @@ -208,6 +208,36 @@ pub type Record = BTreeMap< ), >; +#[cfg(feature = "pyo3")] +use crate::python::{PyBMap, PyMap}; + +/* + * Type used by python binding, + * since we can't directly derive on external types + */ +#[cfg(feature = "pyo3")] +pub type PyRecord = + PyBMap<(Epoch, EpochFlag), (Option, PyBMap>)>; + +/* + * Implement the casts when binding to python + */ +#[cfg(feature = "pyo3")] +impl IntoPy for PyRecord { + fn into_py(self, py: Python<'_>) -> PyObject { + self.into_py(py) + //self.extract().into_py(py) + //PyBMap::<(Epoch, EpochFlag), (Option, PyBMap>)>::extract() + } +} + +#[cfg(feature = "pyo3")] +impl From<&Record> for PyRecord { + fn from(rec: &Record) -> Self { + Self::new() + } +} + /// Returns true if given content matches a new OBSERVATION data epoch pub(crate) fn is_new_epoch(line: &str, v: Version) -> bool { if v.major < 3 { diff --git a/rinex/src/python.rs b/rinex/src/python.rs index 01d2ba724..a370c3437 100644 --- a/rinex/src/python.rs +++ b/rinex/src/python.rs @@ -1,4 +1,11 @@ -use pyo3::{exceptions::PyException, prelude::*}; +use pyo3::{ + exceptions::{PyException, PyTypeError}, + prelude::*, + types::PyDict, +}; + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; use crate::{ hardware::*, @@ -14,6 +21,76 @@ impl std::convert::From for PyErr { } } +/* + * Type used when casting HashMaps to Python compatible + * dictionnary + */ +pub struct PyMap(HashMap); + +impl PyMap { + pub(crate) fn new() -> Self { + Self(HashMap::::new()) + } +} + +impl From> for PyMap { + fn from(map: HashMap) -> Self { + Self(map) + } +} + +impl<'a, K, V> FromPyObject<'a> for PyMap +where + K: FromPyObject<'a> + Hash + Eq, + V: FromPyObject<'a> + Hash + Eq, +{ + fn extract(ob: &'a PyAny) -> PyResult { + if let Ok(dict) = ob.downcast::() { + Ok(PyMap( + dict.items() + .extract::>()? + .into_iter() + .collect::>(), + )) + } else { + Err(PyTypeError::new_err("dictionnary like type expected")) + } + } +} + +pub struct PyBMap(BTreeMap); + +impl PyBMap { + pub(crate) fn new() -> Self { + Self(BTreeMap::::new()) + } +} + +impl From> for PyBMap { + fn from(bmap: BTreeMap) -> Self { + Self(bmap) + } +} + +impl<'a, K, V> FromPyObject<'a> for PyBMap +where + K: FromPyObject<'a> + Hash + Ord + Eq, + V: FromPyObject<'a> + Hash + Ord + Eq, +{ + fn extract(ob: &'a PyAny) -> PyResult { + if let Ok(dict) = ob.downcast::() { + Ok(PyBMap( + dict.items() + .extract::>()? + .into_iter() + .collect::>(), + )) + } else { + Err(PyTypeError::new_err("dictionnary like type expected")) + } + } +} + #[pymodule] fn rinex(_py: Python, m: &PyModule) -> PyResult<()> { /* diff --git a/rinex/src/record.rs b/rinex/src/record.rs index 5cc96c2df..743505131 100644 --- a/rinex/src/record.rs +++ b/rinex/src/record.rs @@ -232,6 +232,19 @@ impl Default for Record { } } +#[cfg(feature = "pyo3")] +use pyo3::prelude::*; + +#[cfg(feature = "pyo3")] +impl IntoPy for Record { + fn into_py(self, py: Python<'_>) -> PyObject { + match self { + Self::ObsRecord(r) => py.None(), //r.into_py(py), + _ => panic!("into_py() not available for this type"), + } + } +} + #[derive(Error, Debug)] pub enum Error { #[error("record parsing not supported for type \"{0}\"")] diff --git a/rinex/src/sv.rs b/rinex/src/sv.rs index fa7b0d6e1..8d00609a2 100644 --- a/rinex/src/sv.rs +++ b/rinex/src/sv.rs @@ -8,6 +8,9 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "pyo3")] use pyo3::prelude::*; +#[cfg(feature = "pyo3")] +use super::constellation::PyConstellation; + /// ̀`Sv` describes a Satellite Vehicle #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "pyo3", pyclass)] @@ -28,7 +31,6 @@ pub enum Error { ParseIntError(#[from] std::num::ParseIntError), } -#[cfg_attr(feature = "pyo3", pymethods)] impl Sv { /// Creates a new `Sv` pub fn new(constellation: Constellation, prn: u8) -> Self { @@ -36,6 +38,17 @@ impl Sv { } } +#[cfg_attr(feature = "pyo3", pymethods)] +impl Sv { + #[new] + pub fn new_py(constellation: PyConstellation, prn: u8) -> Self { + Self { + prn, + constellation: constellation.into(), + } + } +} + impl std::str::FromStr for Sv { type Err = Error; /// Builds an `Sv` from XYY identification code.