sentinel1decoder 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sentinel1decoder-1.0.0/PKG-INFO +111 -0
- sentinel1decoder-1.0.0/README.md +77 -0
- sentinel1decoder-1.0.0/pyproject.toml +74 -0
- sentinel1decoder-1.0.0/rust/Cargo.lock +164 -0
- sentinel1decoder-1.0.0/rust/Cargo.toml +15 -0
- sentinel1decoder-1.0.0/rust/src/lib.rs +14 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/__init__.py +14 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_bypass_decoder.py +100 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_fdbaq_decoder.py +161 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_headers.py +245 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_lookup_tables.py +311 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_sample_code.py +25 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_sample_value_reconstruction.py +88 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/_user_data_decoder.py +84 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/constants.py +73 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/l0decoder.py +157 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/l0file.py +140 -0
- sentinel1decoder-1.0.0/src/sentinel1decoder/utilities.py +139 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sentinel1decoder
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Dist: numpy
|
|
10
|
+
Requires-Dist: pandas
|
|
11
|
+
Requires-Dist: maturin>=1.4.0 ; extra == 'dev'
|
|
12
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
14
|
+
Requires-Dist: pre-commit ; extra == 'dev'
|
|
15
|
+
Requires-Dist: black==24.2 ; extra == 'dev'
|
|
16
|
+
Requires-Dist: flake8==7.0 ; extra == 'dev'
|
|
17
|
+
Requires-Dist: autoflake ; extra == 'dev'
|
|
18
|
+
Requires-Dist: isort==5.13.2 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: mypy==1.8.0 ; extra == 'dev'
|
|
20
|
+
Requires-Dist: jupyter ; extra == 'dev'
|
|
21
|
+
Requires-Dist: jupyterlab ; extra == 'dev'
|
|
22
|
+
Requires-Dist: ipywidgets ; extra == 'dev'
|
|
23
|
+
Requires-Dist: scipy ; extra == 'dev'
|
|
24
|
+
Requires-Dist: matplotlib ; extra == 'dev'
|
|
25
|
+
Requires-Dist: pandas-stubs ; extra == 'dev'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Summary: A python decoder for ESA Sentinel-1 Level0 files
|
|
29
|
+
License: GPL-3.0
|
|
30
|
+
Requires-Python: >=3.8
|
|
31
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
32
|
+
Project-URL: Homepage, https://github.com/Rich-Hall/sentinel1decoder
|
|
33
|
+
|
|
34
|
+
# sentinel1decoder
|
|
35
|
+
Python decoder for Sentinel-1 level0 files. The level0 format consists of the raw space packets downlinked from the Sentinel-1 spacecraft. This package decodes these and produces the raw I/Q sensor output from the SAR instrument, which can then be further processed to focus a SAR image. An example Jupyter notebook which runs through the process of decoding Level 0 data and forming an image is available on github [here](https://github.com/Rich-Hall/sentinel1Level0DecodingDemo) or nbviewer.org [here](https://nbviewer.org/github/Rich-Hall/sentinel1Level0DecodingDemo/blob/main/sentinel1Level0DecodingDemo.ipynb).
|
|
36
|
+
|
|
37
|
+
This code is heavily based on an implementation in C by jmfriedt which can be found [here](https://github.com/jmfriedt/sentinel1_level0).
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
In a terminal window:
|
|
42
|
+
```
|
|
43
|
+
pip install sentinel1decoder
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This package requires python 3.8 or higher. [Numpy](https://numpy.org/) and [Pandas](https://pandas.pydata.org/) are also required.
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
Import the package:
|
|
51
|
+
```
|
|
52
|
+
import sentinel1decoder
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The Level0File class wraps most of the below functionality, while also breaking the file into bursts of constant swath number/number of quads, to allow for easy handling.
|
|
56
|
+
|
|
57
|
+
Initialize a Level0File object:
|
|
58
|
+
```
|
|
59
|
+
l0file = sentinel1decoder.Level0File( filename )
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
This class contains: a dataframe containing the packet metadata:
|
|
63
|
+
```
|
|
64
|
+
l0file.packet_metadata
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
A dataframe containing the ephemeris:
|
|
68
|
+
```
|
|
69
|
+
l0file.ephemeris
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The metadata is indexed by burst as well as packet number. Metadata on individual bursts can be accessed via:
|
|
73
|
+
```
|
|
74
|
+
l0file.get_burst_metadata( burst )
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The I/Q array for each burst can be generated via:
|
|
78
|
+
```
|
|
79
|
+
l0file.get_burst_data( burst )
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Importantly, this data can now be cached in an `.npy` file using:
|
|
83
|
+
```
|
|
84
|
+
l0file.save_burst_data( burst )
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
--------------------------------------------
|
|
88
|
+
|
|
89
|
+
The individual decoding functions can still be used:
|
|
90
|
+
|
|
91
|
+
Initialize a Level0Decoder object:
|
|
92
|
+
```
|
|
93
|
+
decoder = sentinel1decoder.Level0Decoder( filename )
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Generate a Pandas dataframe containing the header information associated with the Sentinel-1 downlink packets contained in the file:
|
|
97
|
+
```
|
|
98
|
+
df = decoder.decode_metadata()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Further decode the satellite ephemeris data from the information in the packet headers:
|
|
102
|
+
```
|
|
103
|
+
ephemeris = sentinel1decoder.utilities.read_subcommed_data(df)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Extract the data payload from the data packets in the file. Takes a Pandas dataframe as an input, and only decodes packets whose header is present in the input dataframe. The intended usage of this is to allow the user to select which packets to decode, rather than having to always decode the full file. For example, to decode the first 100 packets only:
|
|
107
|
+
```
|
|
108
|
+
selection = df.iloc[0:100]
|
|
109
|
+
iq_array = decoder.decode_packets(selection)
|
|
110
|
+
```
|
|
111
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# sentinel1decoder
|
|
2
|
+
Python decoder for Sentinel-1 level0 files. The level0 format consists of the raw space packets downlinked from the Sentinel-1 spacecraft. This package decodes these and produces the raw I/Q sensor output from the SAR instrument, which can then be further processed to focus a SAR image. An example Jupyter notebook which runs through the process of decoding Level 0 data and forming an image is available on github [here](https://github.com/Rich-Hall/sentinel1Level0DecodingDemo) or nbviewer.org [here](https://nbviewer.org/github/Rich-Hall/sentinel1Level0DecodingDemo/blob/main/sentinel1Level0DecodingDemo.ipynb).
|
|
3
|
+
|
|
4
|
+
This code is heavily based on an implementation in C by jmfriedt which can be found [here](https://github.com/jmfriedt/sentinel1_level0).
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
In a terminal window:
|
|
9
|
+
```
|
|
10
|
+
pip install sentinel1decoder
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This package requires python 3.8 or higher. [Numpy](https://numpy.org/) and [Pandas](https://pandas.pydata.org/) are also required.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Import the package:
|
|
18
|
+
```
|
|
19
|
+
import sentinel1decoder
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The Level0File class wraps most of the below functionality, while also breaking the file into bursts of constant swath number/number of quads, to allow for easy handling.
|
|
23
|
+
|
|
24
|
+
Initialize a Level0File object:
|
|
25
|
+
```
|
|
26
|
+
l0file = sentinel1decoder.Level0File( filename )
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This class contains: a dataframe containing the packet metadata:
|
|
30
|
+
```
|
|
31
|
+
l0file.packet_metadata
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A dataframe containing the ephemeris:
|
|
35
|
+
```
|
|
36
|
+
l0file.ephemeris
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The metadata is indexed by burst as well as packet number. Metadata on individual bursts can be accessed via:
|
|
40
|
+
```
|
|
41
|
+
l0file.get_burst_metadata( burst )
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The I/Q array for each burst can be generated via:
|
|
45
|
+
```
|
|
46
|
+
l0file.get_burst_data( burst )
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Importantly, this data can now be cached in an `.npy` file using:
|
|
50
|
+
```
|
|
51
|
+
l0file.save_burst_data( burst )
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
--------------------------------------------
|
|
55
|
+
|
|
56
|
+
The individual decoding functions can still be used:
|
|
57
|
+
|
|
58
|
+
Initialize a Level0Decoder object:
|
|
59
|
+
```
|
|
60
|
+
decoder = sentinel1decoder.Level0Decoder( filename )
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Generate a Pandas dataframe containing the header information associated with the Sentinel-1 downlink packets contained in the file:
|
|
64
|
+
```
|
|
65
|
+
df = decoder.decode_metadata()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Further decode the satellite ephemeris data from the information in the packet headers:
|
|
69
|
+
```
|
|
70
|
+
ephemeris = sentinel1decoder.utilities.read_subcommed_data(df)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Extract the data payload from the data packets in the file. Takes a Pandas dataframe as an input, and only decodes packets whose header is present in the input dataframe. The intended usage of this is to allow the user to select which packets to decode, rather than having to always decode the full file. For example, to decode the first 100 packets only:
|
|
74
|
+
```
|
|
75
|
+
selection = df.iloc[0:100]
|
|
76
|
+
iq_array = decoder.decode_packets(selection)
|
|
77
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["maturin>=1,<2"]
|
|
3
|
+
build-backend = "maturin"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sentinel1decoder"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "A python decoder for ESA Sentinel-1 Level0 files"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "GPL-3.0"}
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Rust",
|
|
13
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
14
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
15
|
+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
16
|
+
"Operating System :: OS Independent",
|
|
17
|
+
]
|
|
18
|
+
requires-python = ">=3.8"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"numpy",
|
|
21
|
+
"pandas",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/Rich-Hall/sentinel1decoder"
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
# Build system
|
|
30
|
+
"maturin>=1.4.0",
|
|
31
|
+
# Testing
|
|
32
|
+
"pytest",
|
|
33
|
+
"pytest-cov",
|
|
34
|
+
"pre-commit",
|
|
35
|
+
"black==24.2",
|
|
36
|
+
"flake8==7.0",
|
|
37
|
+
"autoflake",
|
|
38
|
+
"isort==5.13.2",
|
|
39
|
+
"mypy==1.8.0",
|
|
40
|
+
# Jupyter
|
|
41
|
+
"jupyter",
|
|
42
|
+
"jupyterlab",
|
|
43
|
+
"ipywidgets",
|
|
44
|
+
# Related packages useful in jupyter notebooks
|
|
45
|
+
"scipy",
|
|
46
|
+
"matplotlib",
|
|
47
|
+
# Type checking
|
|
48
|
+
"pandas-stubs",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.maturin]
|
|
52
|
+
python-source = "src"
|
|
53
|
+
module-name = "sentinel1decoder"
|
|
54
|
+
manifest-path = "rust/Cargo.toml"
|
|
55
|
+
manylinux = "2_34"
|
|
56
|
+
interpreter = ["python3.8", "python3.9", "python3.10", "python3.11"]
|
|
57
|
+
|
|
58
|
+
[tool.black]
|
|
59
|
+
line-length = 120
|
|
60
|
+
|
|
61
|
+
[tool.isort]
|
|
62
|
+
profile = "black"
|
|
63
|
+
multi_line_output = 3
|
|
64
|
+
|
|
65
|
+
[tool.mypy]
|
|
66
|
+
disallow_untyped_defs = true
|
|
67
|
+
disallow_incomplete_defs = true
|
|
68
|
+
check_untyped_defs = true
|
|
69
|
+
disallow_untyped_decorators = true
|
|
70
|
+
no_implicit_optional = true
|
|
71
|
+
warn_redundant_casts = true
|
|
72
|
+
warn_unused_ignores = true
|
|
73
|
+
warn_return_any = true
|
|
74
|
+
warn_unreachable = true
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "autocfg"
|
|
7
|
+
version = "1.5.0"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "heck"
|
|
13
|
+
version = "0.5.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "indoc"
|
|
19
|
+
version = "2.0.6"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "libc"
|
|
25
|
+
version = "0.2.174"
|
|
26
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
27
|
+
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "memoffset"
|
|
31
|
+
version = "0.9.1"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
|
34
|
+
dependencies = [
|
|
35
|
+
"autocfg",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[[package]]
|
|
39
|
+
name = "once_cell"
|
|
40
|
+
version = "1.21.3"
|
|
41
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
42
|
+
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "portable-atomic"
|
|
46
|
+
version = "1.11.1"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
|
49
|
+
|
|
50
|
+
[[package]]
|
|
51
|
+
name = "proc-macro2"
|
|
52
|
+
version = "1.0.95"
|
|
53
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
54
|
+
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
|
55
|
+
dependencies = [
|
|
56
|
+
"unicode-ident",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[[package]]
|
|
60
|
+
name = "pyo3"
|
|
61
|
+
version = "0.25.1"
|
|
62
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
63
|
+
checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
|
|
64
|
+
dependencies = [
|
|
65
|
+
"indoc",
|
|
66
|
+
"libc",
|
|
67
|
+
"memoffset",
|
|
68
|
+
"once_cell",
|
|
69
|
+
"portable-atomic",
|
|
70
|
+
"pyo3-build-config",
|
|
71
|
+
"pyo3-ffi",
|
|
72
|
+
"pyo3-macros",
|
|
73
|
+
"unindent",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[[package]]
|
|
77
|
+
name = "pyo3-build-config"
|
|
78
|
+
version = "0.25.1"
|
|
79
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
80
|
+
checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
|
|
81
|
+
dependencies = [
|
|
82
|
+
"once_cell",
|
|
83
|
+
"target-lexicon",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[[package]]
|
|
87
|
+
name = "pyo3-ffi"
|
|
88
|
+
version = "0.25.1"
|
|
89
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
90
|
+
checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
|
|
91
|
+
dependencies = [
|
|
92
|
+
"libc",
|
|
93
|
+
"pyo3-build-config",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[[package]]
|
|
97
|
+
name = "pyo3-macros"
|
|
98
|
+
version = "0.25.1"
|
|
99
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
100
|
+
checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
|
|
101
|
+
dependencies = [
|
|
102
|
+
"proc-macro2",
|
|
103
|
+
"pyo3-macros-backend",
|
|
104
|
+
"quote",
|
|
105
|
+
"syn",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
[[package]]
|
|
109
|
+
name = "pyo3-macros-backend"
|
|
110
|
+
version = "0.25.1"
|
|
111
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
112
|
+
checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
|
|
113
|
+
dependencies = [
|
|
114
|
+
"heck",
|
|
115
|
+
"proc-macro2",
|
|
116
|
+
"pyo3-build-config",
|
|
117
|
+
"quote",
|
|
118
|
+
"syn",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "quote"
|
|
123
|
+
version = "1.0.40"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
126
|
+
dependencies = [
|
|
127
|
+
"proc-macro2",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[[package]]
|
|
131
|
+
name = "sentinel1decoder"
|
|
132
|
+
version = "1.0.0"
|
|
133
|
+
dependencies = [
|
|
134
|
+
"pyo3",
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
[[package]]
|
|
138
|
+
name = "syn"
|
|
139
|
+
version = "2.0.104"
|
|
140
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
141
|
+
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
|
142
|
+
dependencies = [
|
|
143
|
+
"proc-macro2",
|
|
144
|
+
"quote",
|
|
145
|
+
"unicode-ident",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
[[package]]
|
|
149
|
+
name = "target-lexicon"
|
|
150
|
+
version = "0.13.2"
|
|
151
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
152
|
+
checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
|
|
153
|
+
|
|
154
|
+
[[package]]
|
|
155
|
+
name = "unicode-ident"
|
|
156
|
+
version = "1.0.18"
|
|
157
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
158
|
+
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
159
|
+
|
|
160
|
+
[[package]]
|
|
161
|
+
name = "unindent"
|
|
162
|
+
version = "0.2.4"
|
|
163
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
164
|
+
checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "sentinel1decoder"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
name = "sentinel1decoder"
|
|
8
|
+
crate-type = ["cdylib"]
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
pyo3 = { version = "0.25.1", features = ["extension-module"] }
|
|
12
|
+
|
|
13
|
+
[package.metadata.maturin]
|
|
14
|
+
# Optional: set the Python package name if different from the Rust crate name
|
|
15
|
+
# name = "sentinel1decoder"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
use pyo3::prelude::*;
|
|
2
|
+
|
|
3
|
+
/// Formats the sum of two numbers as string.
|
|
4
|
+
#[pyfunction]
|
|
5
|
+
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
|
|
6
|
+
Ok((a + b).to_string())
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/// A Python module implemented in Rust.
|
|
10
|
+
#[pymodule]
|
|
11
|
+
fn sentinel1decoder(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
12
|
+
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
|
|
13
|
+
Ok(())
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Sentinel-1 decoder package."""
|
|
2
|
+
|
|
3
|
+
from . import constants, utilities
|
|
4
|
+
from .l0decoder import Level0Decoder
|
|
5
|
+
from .l0file import Level0File
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"Level0Decoder",
|
|
11
|
+
"Level0File",
|
|
12
|
+
"utilities",
|
|
13
|
+
"constants",
|
|
14
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _ten_bit_unsigned_to_signed_int(ten_bit: int) -> int:
|
|
7
|
+
"""
|
|
8
|
+
Convert a ten-bit unsigned int to a standard signed int.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
ten_bit: Raw ten-bit int extracted from packet.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
A standard signed integer
|
|
15
|
+
"""
|
|
16
|
+
# First bit is the sign, remaining 9 encode the number
|
|
17
|
+
sign = int((-1) ** ((ten_bit >> 9) & 0x1))
|
|
18
|
+
return sign * (ten_bit & 0x1FF)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BypassDecoder:
|
|
22
|
+
"""Decode user data format type A and B (“Bypass” or “Decimation Only”)."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, data: bytes, num_quads: int) -> None:
|
|
25
|
+
self._data = data
|
|
26
|
+
self._num_quads = num_quads
|
|
27
|
+
|
|
28
|
+
_num_words = math.ceil((10 / 16) * num_quads) # No. of 16-bit words per channel
|
|
29
|
+
self._num_bytes = 2 * _num_words # No. of 8-bit bytes per channel
|
|
30
|
+
|
|
31
|
+
self._i_evens = self._process_channel(0)
|
|
32
|
+
self._i_odds = self._process_channel(self._num_bytes)
|
|
33
|
+
self._q_evens = self._process_channel(2 * self._num_bytes)
|
|
34
|
+
self._q_odds = self._process_channel(3 * self._num_bytes)
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def i_evens(self) -> np.ndarray:
|
|
38
|
+
return self._i_evens
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def i_odds(self) -> np.ndarray:
|
|
42
|
+
return self._i_odds
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def q_evens(self) -> np.ndarray:
|
|
46
|
+
return self._q_evens
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def q_odds(self) -> np.ndarray:
|
|
50
|
+
return self._q_odds
|
|
51
|
+
|
|
52
|
+
def _process_channel(self, start_8bit_index: int) -> np.ndarray:
|
|
53
|
+
"""Process a single channel's data.
|
|
54
|
+
|
|
55
|
+
Python doesn't have an easy way of extracting 10-bit integers.
|
|
56
|
+
We're going to read in sets of five normal 8-bit bytes, and extract four
|
|
57
|
+
10-bit words per set. We'll need to track the indexing separately and
|
|
58
|
+
check for the end of the file each time.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
channel_name: The name of the channel to process.
|
|
62
|
+
output_array: The array to store the processed data.
|
|
63
|
+
start_8bit_index: The starting index of the 8-bit bytes to process.
|
|
64
|
+
"""
|
|
65
|
+
index_8bit = start_8bit_index
|
|
66
|
+
index_10bit = 0
|
|
67
|
+
output_array = np.zeros(self._num_quads, dtype=int)
|
|
68
|
+
|
|
69
|
+
while index_10bit < self._num_quads:
|
|
70
|
+
if index_10bit < self._num_quads:
|
|
71
|
+
s_code = (self._data[index_8bit] << 2 | self._data[index_8bit + 1] >> 6) & 1023
|
|
72
|
+
output_array[index_10bit] = _ten_bit_unsigned_to_signed_int(s_code)
|
|
73
|
+
index_10bit += 1
|
|
74
|
+
else:
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
if index_10bit < self._num_quads:
|
|
78
|
+
s_code = (self._data[index_8bit + 1] << 4 | self._data[index_8bit + 2] >> 4) & 1023
|
|
79
|
+
output_array[index_10bit] = _ten_bit_unsigned_to_signed_int(s_code)
|
|
80
|
+
index_10bit += 1
|
|
81
|
+
else:
|
|
82
|
+
break
|
|
83
|
+
|
|
84
|
+
if index_10bit < self._num_quads:
|
|
85
|
+
s_code = (self._data[index_8bit + 2] << 6 | self._data[index_8bit + 3] >> 2) & 1023
|
|
86
|
+
output_array[index_10bit] = _ten_bit_unsigned_to_signed_int(s_code)
|
|
87
|
+
index_10bit += 1
|
|
88
|
+
else:
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
if index_10bit < self._num_quads:
|
|
92
|
+
s_code = (self._data[index_8bit + 3] << 8 | self._data[index_8bit + 4] >> 0) & 1023
|
|
93
|
+
output_array[index_10bit] = _ten_bit_unsigned_to_signed_int(s_code)
|
|
94
|
+
index_10bit += 1
|
|
95
|
+
else:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
index_8bit += 5
|
|
99
|
+
|
|
100
|
+
return output_array
|