doppy 0.5.3__tar.gz → 0.5.4__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.
Potentially problematic release.
This version of doppy might be problematic. Click here for more details.
- {doppy-0.5.3 → doppy-0.5.4}/Cargo.lock +29 -3
- {doppy-0.5.3 → doppy-0.5.4}/Cargo.toml +6 -1
- {doppy-0.5.3 → doppy-0.5.4}/PKG-INFO +2 -1
- {doppy-0.5.3 → doppy-0.5.4}/crates/doppy_rs/Cargo.toml +2 -1
- doppy-0.5.4/crates/doppy_rs/src/raw/wls77.rs +90 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doppy_rs/src/raw.rs +2 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/Cargo.toml +1 -0
- doppy-0.5.4/crates/doprs/src/bin/parse_wls70.rs +8 -0
- doppy-0.5.4/crates/doprs/src/bin/parse_wls77.rs +7 -0
- doppy-0.5.4/crates/doprs/src/raw/wls77.rs +290 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/src/raw.rs +1 -0
- {doppy-0.5.3 → doppy-0.5.4}/pyproject.toml +1 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/__init__.py +10 -1
- doppy-0.5.4/src/doppy/raw/wls77.py +163 -0
- {doppy-0.5.3 → doppy-0.5.4}/LICENSE +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/README.md +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doppy_rs/src/lib.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doppy_rs/src/raw/halo_hpl.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doppy_rs/src/raw/wls70.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/.gitignore +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/src/lib.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/src/raw/error.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/src/raw/halo_hpl.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/crates/doprs/src/raw/wls70.rs +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/__init__.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/__main__.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/bench.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/data/__init__.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/data/api.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/data/cache.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/data/exceptions.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/defaults.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/exceptions.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/netcdf.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/options.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/__init__.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/noise_utils.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/stare.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/stare_depol.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/turbulence.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/product/wind.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/py.typed +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/halo_bg.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/halo_hpl.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/halo_sys_params.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/utils.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/windcube.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/raw/wls70.py +0 -0
- {doppy-0.5.3 → doppy-0.5.4}/src/doppy/utils.py +0 -0
|
@@ -106,18 +106,20 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
|
106
106
|
|
|
107
107
|
[[package]]
|
|
108
108
|
name = "doppy_rs"
|
|
109
|
-
version = "0.5.
|
|
109
|
+
version = "0.5.4"
|
|
110
110
|
dependencies = [
|
|
111
111
|
"doprs",
|
|
112
|
+
"ndarray 0.16.1",
|
|
112
113
|
"numpy",
|
|
113
114
|
"pyo3",
|
|
114
115
|
]
|
|
115
116
|
|
|
116
117
|
[[package]]
|
|
117
118
|
name = "doprs"
|
|
118
|
-
version = "0.5.
|
|
119
|
+
version = "0.5.4"
|
|
119
120
|
dependencies = [
|
|
120
121
|
"chrono",
|
|
122
|
+
"ndarray 0.16.1",
|
|
121
123
|
"rayon",
|
|
122
124
|
"regex",
|
|
123
125
|
]
|
|
@@ -233,6 +235,21 @@ dependencies = [
|
|
|
233
235
|
"rawpointer",
|
|
234
236
|
]
|
|
235
237
|
|
|
238
|
+
[[package]]
|
|
239
|
+
name = "ndarray"
|
|
240
|
+
version = "0.16.1"
|
|
241
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
242
|
+
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
|
|
243
|
+
dependencies = [
|
|
244
|
+
"matrixmultiply",
|
|
245
|
+
"num-complex",
|
|
246
|
+
"num-integer",
|
|
247
|
+
"num-traits",
|
|
248
|
+
"portable-atomic",
|
|
249
|
+
"portable-atomic-util",
|
|
250
|
+
"rawpointer",
|
|
251
|
+
]
|
|
252
|
+
|
|
236
253
|
[[package]]
|
|
237
254
|
name = "num-complex"
|
|
238
255
|
version = "0.4.6"
|
|
@@ -267,7 +284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
|
267
284
|
checksum = "bef41cbb417ea83b30525259e30ccef6af39b31c240bda578889494c5392d331"
|
|
268
285
|
dependencies = [
|
|
269
286
|
"libc",
|
|
270
|
-
"ndarray",
|
|
287
|
+
"ndarray 0.15.6",
|
|
271
288
|
"num-complex",
|
|
272
289
|
"num-integer",
|
|
273
290
|
"num-traits",
|
|
@@ -310,6 +327,15 @@ version = "1.11.0"
|
|
|
310
327
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
311
328
|
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
|
312
329
|
|
|
330
|
+
[[package]]
|
|
331
|
+
name = "portable-atomic-util"
|
|
332
|
+
version = "0.2.4"
|
|
333
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
334
|
+
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
|
335
|
+
dependencies = [
|
|
336
|
+
"portable-atomic",
|
|
337
|
+
]
|
|
338
|
+
|
|
313
339
|
[[package]]
|
|
314
340
|
name = "proc-macro2"
|
|
315
341
|
version = "1.0.94"
|
|
@@ -4,6 +4,11 @@ resolver = "2"
|
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
6
|
edition = "2021"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.4"
|
|
8
8
|
authors = ["Niko Leskinen <niko.leskinen@fmi.fi>"]
|
|
9
9
|
license-file = "LICENSE"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[workspace.dependencies]
|
|
13
|
+
ndarray = "0.16"
|
|
14
|
+
numpy = {version = "0.20", fatures = ["ndarray"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: doppy
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -28,6 +28,7 @@ Requires-Dist: maturin==1.8 ; extra == 'dev'
|
|
|
28
28
|
Requires-Dist: release-version ; extra == 'dev'
|
|
29
29
|
Requires-Dist: pre-commit ; extra == 'dev'
|
|
30
30
|
Requires-Dist: xarray[io] ; extra == 'dev'
|
|
31
|
+
Requires-Dist: seaborn ; extra == 'dev'
|
|
31
32
|
Provides-Extra: dev
|
|
32
33
|
License-File: LICENSE
|
|
33
34
|
License-File: LICENSE
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
use numpy::PyArray1;
|
|
2
|
+
use pyo3::exceptions::PyRuntimeError;
|
|
3
|
+
use pyo3::prelude::*;
|
|
4
|
+
use pyo3::types::PyDict;
|
|
5
|
+
|
|
6
|
+
#[pymodule]
|
|
7
|
+
pub fn wls77(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
8
|
+
m.add_function(wrap_pyfunction!(from_bytes_src, m)?)?;
|
|
9
|
+
Ok(())
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[pyfunction]
|
|
13
|
+
pub fn from_bytes_srcs<'a>(py: Python<'a>, contents: Vec<&'a [u8]>) -> PyResult<Vec<&'a PyDict>> {
|
|
14
|
+
let raws = doprs::raw::wls77::from_bytes_srcs(contents);
|
|
15
|
+
let mut result = Vec::new();
|
|
16
|
+
for raw in raws {
|
|
17
|
+
result.push(convert_to_python(py, raw)?);
|
|
18
|
+
}
|
|
19
|
+
Ok(result)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[pyfunction]
|
|
23
|
+
pub fn from_bytes_src<'a>(py: Python<'a>, content: &'a [u8]) -> PyResult<&'a PyDict> {
|
|
24
|
+
let raw = match doprs::raw::wls77::from_bytes_src(content) {
|
|
25
|
+
Ok(raw) => raw,
|
|
26
|
+
Err(e) => {
|
|
27
|
+
return Err(PyRuntimeError::new_err(format!(
|
|
28
|
+
"Failed to read files: {}",
|
|
29
|
+
e
|
|
30
|
+
)))
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
convert_to_python(py, raw)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[pyfunction]
|
|
37
|
+
pub fn from_filename_srcs(py: Python, filenames: Vec<String>) -> PyResult<Vec<&PyDict>> {
|
|
38
|
+
let raws = doprs::raw::wls77::from_filename_srcs(filenames);
|
|
39
|
+
let mut result = Vec::new();
|
|
40
|
+
for raw in raws {
|
|
41
|
+
result.push(convert_to_python(py, raw)?);
|
|
42
|
+
}
|
|
43
|
+
Ok(result)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[pyfunction]
|
|
47
|
+
pub fn from_filename_src(py: Python, filename: String) -> PyResult<&PyDict> {
|
|
48
|
+
let raw = match doprs::raw::wls77::from_filename_src(filename) {
|
|
49
|
+
Ok(raw) => raw,
|
|
50
|
+
Err(e) => {
|
|
51
|
+
return Err(PyRuntimeError::new_err(format!(
|
|
52
|
+
"Failed to read files: {}",
|
|
53
|
+
e
|
|
54
|
+
)))
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
convert_to_python(py, raw)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fn convert_to_python(py: Python, raw: doprs::raw::wls77::Wls77) -> PyResult<&PyDict> {
|
|
61
|
+
let d = PyDict::new(py);
|
|
62
|
+
|
|
63
|
+
let fields = [
|
|
64
|
+
("time", raw.time.as_slice()),
|
|
65
|
+
("altitude", raw.altitude.as_slice()),
|
|
66
|
+
("position", raw.position.as_slice()),
|
|
67
|
+
("temperature", raw.temperature.as_slice()),
|
|
68
|
+
("wiper_count", raw.wiper_count.as_slice()),
|
|
69
|
+
("cnr", raw.cnr.as_slice()),
|
|
70
|
+
("radial_velocity", raw.radial_velocity.as_slice()),
|
|
71
|
+
(
|
|
72
|
+
"radial_velocity_deviation",
|
|
73
|
+
raw.radial_velocity_deviation.as_slice(),
|
|
74
|
+
),
|
|
75
|
+
("wind_speed", raw.wind_speed.as_slice()),
|
|
76
|
+
("wind_direction", raw.wind_direction.as_slice()),
|
|
77
|
+
("zonal_wind", raw.zonal_wind.as_slice()),
|
|
78
|
+
("meridional_wind", raw.meridional_wind.as_slice()),
|
|
79
|
+
("vertical_wind", raw.vertical_wind.as_slice()),
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
for (key, value) in fields {
|
|
83
|
+
d.set_item(key, PyArray1::from_slice(py, value.unwrap()))?;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
d.set_item("cnr_threshold", raw.cnr_threshold)?;
|
|
87
|
+
d.set_item("system_id", raw.system_id)?;
|
|
88
|
+
|
|
89
|
+
Ok(d)
|
|
90
|
+
}
|
|
@@ -3,10 +3,12 @@ use pyo3::wrap_pymodule;
|
|
|
3
3
|
|
|
4
4
|
pub mod halo_hpl;
|
|
5
5
|
pub mod wls70;
|
|
6
|
+
pub mod wls77;
|
|
6
7
|
|
|
7
8
|
#[pymodule]
|
|
8
9
|
pub fn raw(_py: Python, m: &PyModule) -> PyResult<()> {
|
|
9
10
|
m.add_wrapped(wrap_pymodule!(halo_hpl::halo_hpl))?;
|
|
10
11
|
m.add_wrapped(wrap_pymodule!(wls70::wls70))?;
|
|
12
|
+
m.add_wrapped(wrap_pymodule!(wls77::wls77))?;
|
|
11
13
|
Ok(())
|
|
12
14
|
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
use crate::raw::error::RawParseError;
|
|
2
|
+
use chrono::{DateTime, NaiveDateTime, ParseError, Utc};
|
|
3
|
+
use ndarray::{s, Array, Array1, Array2};
|
|
4
|
+
use rayon::prelude::*;
|
|
5
|
+
use std::fs::File;
|
|
6
|
+
use std::io::{BufRead, Cursor, Read};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Default, Clone)]
|
|
9
|
+
pub struct Wls77Old {
|
|
10
|
+
pub info: Info,
|
|
11
|
+
pub data_columns: Vec<String>,
|
|
12
|
+
pub data: Vec<f64>,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
#[derive(Debug, Default, Clone)]
|
|
16
|
+
pub struct Wls77 {
|
|
17
|
+
pub time: Array1<f64>,
|
|
18
|
+
pub altitude: Array1<f64>,
|
|
19
|
+
pub position: Array1<f64>,
|
|
20
|
+
pub temperature: Array1<f64>,
|
|
21
|
+
pub wiper_count: Array1<f64>,
|
|
22
|
+
pub cnr: Array2<f64>,
|
|
23
|
+
pub radial_velocity: Array2<f64>,
|
|
24
|
+
pub radial_velocity_deviation: Array2<f64>,
|
|
25
|
+
pub wind_speed: Array2<f64>,
|
|
26
|
+
pub wind_direction: Array2<f64>,
|
|
27
|
+
pub zonal_wind: Array2<f64>,
|
|
28
|
+
pub meridional_wind: Array2<f64>,
|
|
29
|
+
pub vertical_wind: Array2<f64>,
|
|
30
|
+
pub cnr_threshold: f64,
|
|
31
|
+
pub system_id: String,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#[derive(Debug, Default, Clone)]
|
|
35
|
+
pub struct Info {
|
|
36
|
+
pub altitude: Vec<f64>,
|
|
37
|
+
pub system_id: String,
|
|
38
|
+
pub cnr_threshold: f64,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
pub fn from_file_src(mut file: &File) -> Result<Wls77, RawParseError> {
|
|
42
|
+
let mut content = vec![];
|
|
43
|
+
file.read_to_end(&mut content)?;
|
|
44
|
+
from_bytes_src(&content)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
pub fn from_filename_src(filename: String) -> Result<Wls77, RawParseError> {
|
|
48
|
+
let file = File::open(filename)?;
|
|
49
|
+
from_file_src(&file)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
pub fn from_filename_srcs(filenames: Vec<String>) -> Vec<Wls77> {
|
|
53
|
+
let results = filenames
|
|
54
|
+
.par_iter()
|
|
55
|
+
.filter_map(|filename| from_filename_src(filename.to_string()).ok())
|
|
56
|
+
.collect();
|
|
57
|
+
results
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pub fn from_file_srcs(files: Vec<&File>) -> Vec<Wls77> {
|
|
61
|
+
let results = files
|
|
62
|
+
.par_iter()
|
|
63
|
+
.filter_map(|file| from_file_src(file).ok())
|
|
64
|
+
.collect();
|
|
65
|
+
results
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
pub fn from_bytes_srcs(contents: Vec<&[u8]>) -> Vec<Wls77> {
|
|
69
|
+
let results = contents
|
|
70
|
+
.par_iter()
|
|
71
|
+
.filter_map(|content| from_bytes_src(content).ok())
|
|
72
|
+
.collect();
|
|
73
|
+
results
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
enum Phase {
|
|
77
|
+
Info,
|
|
78
|
+
Data,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub fn from_bytes_src(content: &[u8]) -> Result<Wls77, RawParseError> {
|
|
82
|
+
let cur = Cursor::new(content);
|
|
83
|
+
let mut info_str = Vec::new();
|
|
84
|
+
let mut header = Vec::new();
|
|
85
|
+
let mut data_str = Vec::new();
|
|
86
|
+
|
|
87
|
+
let mut phase = Phase::Info;
|
|
88
|
+
|
|
89
|
+
for line in cur.split(b'\n') {
|
|
90
|
+
let line = line.unwrap();
|
|
91
|
+
match phase {
|
|
92
|
+
Phase::Info => {
|
|
93
|
+
if line.starts_with(b"Timestamp\tPosition\tTemperature")
|
|
94
|
+
|| line.starts_with(b"Date\tPosition\tTemperature")
|
|
95
|
+
{
|
|
96
|
+
header.extend_from_slice(&line);
|
|
97
|
+
header.push(b'\n');
|
|
98
|
+
phase = Phase::Data;
|
|
99
|
+
} else {
|
|
100
|
+
info_str.extend_from_slice(&line);
|
|
101
|
+
info_str.push(b'\n');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
Phase::Data => {
|
|
105
|
+
data_str.extend_from_slice(&line);
|
|
106
|
+
data_str.push(b'\n');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
let info = parse_info(&info_str)?;
|
|
111
|
+
|
|
112
|
+
match parse_data(&data_str) {
|
|
113
|
+
Ok((data, ncols)) => {
|
|
114
|
+
let header_str: String = header.iter().map(|&c| c as char).collect();
|
|
115
|
+
let cols: Vec<_> = header_str
|
|
116
|
+
.split('\t')
|
|
117
|
+
.map(|s| s.trim().to_string())
|
|
118
|
+
.filter(|s| !s.is_empty())
|
|
119
|
+
.collect();
|
|
120
|
+
if ncols != (cols.len() as i64) {
|
|
121
|
+
return Err(RawParseError {
|
|
122
|
+
message: "Number of columns on header and number of columns in data mismatch"
|
|
123
|
+
.to_string(),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let data: Array1<_> = Array::from_vec(data.clone());
|
|
128
|
+
let n = (data.len() as i64 / ncols)
|
|
129
|
+
.try_into()
|
|
130
|
+
.map_err(|e| RawParseError {
|
|
131
|
+
message: format!("Failed to convert rows count: {}", e),
|
|
132
|
+
})?;
|
|
133
|
+
let m = ncols.try_into().map_err(|e| RawParseError {
|
|
134
|
+
message: format!("Failed to convert columns count: {}", e),
|
|
135
|
+
})?;
|
|
136
|
+
let shape: [usize; 2] = [n, m];
|
|
137
|
+
|
|
138
|
+
let data = data
|
|
139
|
+
.into_shape_with_order(shape)
|
|
140
|
+
.map_err(|e| RawParseError {
|
|
141
|
+
message: format!("Cannot reshape data array: {}", e),
|
|
142
|
+
})?;
|
|
143
|
+
let altitude = Array::from_vec(info.altitude);
|
|
144
|
+
|
|
145
|
+
let time = data.slice(s![.., 0]).to_owned();
|
|
146
|
+
let position = data.slice(s![.., 1]).to_owned();
|
|
147
|
+
let temperature = data.slice(s![.., 2]).to_owned();
|
|
148
|
+
let wiper_count = data.slice(s![.., 2]).to_owned();
|
|
149
|
+
let cnr = data.slice(s![..,4..;8]).to_owned();
|
|
150
|
+
let radial_velocity = data.slice(s![..,5..;8]).to_owned();
|
|
151
|
+
let radial_velocity_deviation = data.slice(s![..,6..;8]).to_owned();
|
|
152
|
+
let wind_speed = data.slice(s![..,7..;8]).to_owned();
|
|
153
|
+
let wind_direction = data.slice(s![..,8..;8]).to_owned();
|
|
154
|
+
let zonal_wind = data.slice(s![..,9..;8]).to_owned();
|
|
155
|
+
let meridional_wind = data.slice(s![..,10..;8]).to_owned();
|
|
156
|
+
let vertical_wind = data.slice(s![..,11..;8]).to_owned();
|
|
157
|
+
|
|
158
|
+
Ok(Wls77 {
|
|
159
|
+
time,
|
|
160
|
+
altitude,
|
|
161
|
+
position,
|
|
162
|
+
temperature,
|
|
163
|
+
wiper_count,
|
|
164
|
+
cnr,
|
|
165
|
+
radial_velocity,
|
|
166
|
+
radial_velocity_deviation,
|
|
167
|
+
wind_speed,
|
|
168
|
+
wind_direction,
|
|
169
|
+
zonal_wind,
|
|
170
|
+
meridional_wind,
|
|
171
|
+
vertical_wind,
|
|
172
|
+
system_id: info.system_id,
|
|
173
|
+
cnr_threshold: info.cnr_threshold,
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
Err(e) => Err(e),
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fn parse_info(info_str: &[u8]) -> Result<Info, RawParseError> {
|
|
181
|
+
let mut info = Info::default();
|
|
182
|
+
for line in info_str.split(|&b| b == b'\n') {
|
|
183
|
+
match line {
|
|
184
|
+
b if b.starts_with(b"Altitudes AGL (m)=") => {
|
|
185
|
+
info.altitude = line
|
|
186
|
+
.split(|&b| b == b'\t')
|
|
187
|
+
.skip(1)
|
|
188
|
+
.map(|part| {
|
|
189
|
+
String::from_utf8(part.to_vec())
|
|
190
|
+
.map_err(|_| RawParseError {
|
|
191
|
+
message: "UTF-8 conversion error".into(),
|
|
192
|
+
})
|
|
193
|
+
.and_then(|s| {
|
|
194
|
+
s.trim().parse::<f64>().map_err(|_| RawParseError {
|
|
195
|
+
message: "Parse float error".into(),
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
.collect::<Result<Vec<f64>, _>>()?;
|
|
200
|
+
}
|
|
201
|
+
b if b.starts_with(b"ID System=") => {
|
|
202
|
+
info.system_id = std::str::from_utf8(&line[10..])
|
|
203
|
+
.map(|s| s.trim())
|
|
204
|
+
.map_err(|_| RawParseError {
|
|
205
|
+
message: "UTF-8 conversion error".into(),
|
|
206
|
+
})?
|
|
207
|
+
.to_string();
|
|
208
|
+
}
|
|
209
|
+
b if b.starts_with(b"CNRThreshold=") => {
|
|
210
|
+
info.cnr_threshold = std::str::from_utf8(&line[13..])
|
|
211
|
+
.map_err(|_| RawParseError {
|
|
212
|
+
message: "UTF-8 conversion error".into(),
|
|
213
|
+
})?
|
|
214
|
+
.trim()
|
|
215
|
+
.parse::<f64>()
|
|
216
|
+
.map_err(|_| RawParseError {
|
|
217
|
+
message: "Parse float error".into(),
|
|
218
|
+
})?;
|
|
219
|
+
}
|
|
220
|
+
_ => (),
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
Ok(info)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
pub fn parse_data(data: &[u8]) -> Result<(Vec<f64>, i64), RawParseError> {
|
|
228
|
+
let mut ncols: i64 = -1;
|
|
229
|
+
let mut data_flat = vec![];
|
|
230
|
+
for line in data.split(|&b| b == b'\n') {
|
|
231
|
+
let parts: Vec<_> = line
|
|
232
|
+
.split(|&b| b == b'\t')
|
|
233
|
+
.filter(|part| !(part.is_empty() || part == b"\r"))
|
|
234
|
+
.collect();
|
|
235
|
+
if parts.is_empty() {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if ncols < 0 {
|
|
239
|
+
ncols = parts.len() as i64;
|
|
240
|
+
}
|
|
241
|
+
if ncols != parts.len() as i64 {
|
|
242
|
+
return Err(RawParseError {
|
|
243
|
+
message: "Unexpected number of columns".to_string(),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
for (i, part) in parts.iter().enumerate() {
|
|
247
|
+
match i {
|
|
248
|
+
0 => {
|
|
249
|
+
let date = String::from_utf8_lossy(part).trim().to_string();
|
|
250
|
+
match datetime_to_timestamp(&date) {
|
|
251
|
+
Ok(d) => {
|
|
252
|
+
data_flat.push(d);
|
|
253
|
+
}
|
|
254
|
+
Err(_) => println!("Error with datetime"),
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
1 => {
|
|
258
|
+
let s = String::from_utf8_lossy(part).trim().to_string();
|
|
259
|
+
if let Ok(val) = s.parse::<f64>() {
|
|
260
|
+
data_flat.push(val);
|
|
261
|
+
} else {
|
|
262
|
+
match s.as_ref() {
|
|
263
|
+
"V" => data_flat.push(-1.0),
|
|
264
|
+
_ => data_flat.push(-2.0),
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_ => match String::from_utf8_lossy(part).trim().parse::<f64>() {
|
|
270
|
+
Ok(x) => {
|
|
271
|
+
data_flat.push(x);
|
|
272
|
+
}
|
|
273
|
+
Err(_) => println!("Cannot parse float"),
|
|
274
|
+
},
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if ncols < 1 || (data_flat.len() as i64) % ncols != 0 {
|
|
279
|
+
return Err(RawParseError {
|
|
280
|
+
message: "Unexpected number of columns".to_string(),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
Ok((data_flat, ncols))
|
|
284
|
+
}
|
|
285
|
+
fn datetime_to_timestamp(s: &str) -> Result<f64, ParseError> {
|
|
286
|
+
let format = "%Y/%m/%d %H:%M:%S%.f";
|
|
287
|
+
let ndt = NaiveDateTime::parse_from_str(s, format)?;
|
|
288
|
+
let dt = DateTime::<Utc>::from_naive_utc_and_offset(ndt, Utc);
|
|
289
|
+
Ok(dt.timestamp() as f64 + dt.timestamp_subsec_millis() as f64 / 1000.0)
|
|
290
|
+
}
|
|
@@ -3,5 +3,14 @@ from .halo_hpl import HaloHpl
|
|
|
3
3
|
from .halo_sys_params import HaloSysParams
|
|
4
4
|
from .windcube import WindCube, WindCubeFixed
|
|
5
5
|
from .wls70 import Wls70
|
|
6
|
+
from .wls77 import Wls77
|
|
6
7
|
|
|
7
|
-
__all__ = [
|
|
8
|
+
__all__ = [
|
|
9
|
+
"HaloHpl",
|
|
10
|
+
"HaloBg",
|
|
11
|
+
"HaloSysParams",
|
|
12
|
+
"WindCube",
|
|
13
|
+
"WindCubeFixed",
|
|
14
|
+
"Wls70",
|
|
15
|
+
"Wls77",
|
|
16
|
+
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
from io import BufferedIOBase
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Sequence
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import numpy.typing as npt
|
|
11
|
+
from numpy import datetime64
|
|
12
|
+
|
|
13
|
+
import doppy
|
|
14
|
+
from doppy import exceptions
|
|
15
|
+
from doppy.raw.utils import bytes_from_src
|
|
16
|
+
from doppy.utils import merge_all_equal
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class Wls77:
|
|
21
|
+
time: npt.NDArray[datetime64] # dim: (time, )
|
|
22
|
+
altitude: npt.NDArray[np.float64] # dim: (altitude, )
|
|
23
|
+
position: npt.NDArray[np.float64] # dim: (time, )
|
|
24
|
+
temperature: npt.NDArray[np.float64] # dim: (time, )
|
|
25
|
+
wiper_count: npt.NDArray[np.float64] # dim: (time, )
|
|
26
|
+
cnr: npt.NDArray[np.float64] # dim: (time, altitude)
|
|
27
|
+
radial_velocity: npt.NDArray[np.float64] # dim: (time, altitude)
|
|
28
|
+
radial_velocity_deviation: npt.NDArray[np.float64] # dim: (time, altitude)
|
|
29
|
+
wind_speed: npt.NDArray[np.float64] # dim: (time, altitude)
|
|
30
|
+
wind_direction: npt.NDArray[np.float64] # dim: (time, altitude)
|
|
31
|
+
zonal_wind: npt.NDArray[np.float64] # u := zonal wind?, dim: (time, altitude)
|
|
32
|
+
meridional_wind: npt.NDArray[
|
|
33
|
+
np.float64
|
|
34
|
+
] # v := meridional wind?, dim: (time, altitude)
|
|
35
|
+
vertical_wind: npt.NDArray[np.float64] # w := vertical wind?, dim: (time, altitude)
|
|
36
|
+
cnr_threshold: float
|
|
37
|
+
system_id: str
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_srcs(
|
|
41
|
+
cls, data: Sequence[str | bytes | Path | BufferedIOBase]
|
|
42
|
+
) -> list[Wls77]:
|
|
43
|
+
data_bytes = [bytes_from_src(src) for src in data]
|
|
44
|
+
raws = doppy.rs.raw.wls77.from_bytes_srcs(data_bytes)
|
|
45
|
+
try:
|
|
46
|
+
return [_raw_rs_to_wls77(r) for r in raws]
|
|
47
|
+
except RuntimeError as err:
|
|
48
|
+
raise exceptions.RawParsingError(err) from err
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def from_src(cls, data: str | Path | bytes | BufferedIOBase) -> Wls77:
|
|
52
|
+
data_bytes = bytes_from_src(data)
|
|
53
|
+
try:
|
|
54
|
+
return _raw_rs_to_wls77(doppy.rs.raw.wls77.from_bytes_src(data_bytes))
|
|
55
|
+
except RuntimeError as err:
|
|
56
|
+
raise exceptions.RawParsingError(err) from err
|
|
57
|
+
|
|
58
|
+
def __getitem__(
|
|
59
|
+
self,
|
|
60
|
+
index: int
|
|
61
|
+
| slice
|
|
62
|
+
| list[int]
|
|
63
|
+
| npt.NDArray[np.int64]
|
|
64
|
+
| npt.NDArray[np.bool_]
|
|
65
|
+
| tuple[slice, slice],
|
|
66
|
+
) -> Wls77:
|
|
67
|
+
if isinstance(index, (int, slice, list, np.ndarray)):
|
|
68
|
+
return Wls77(
|
|
69
|
+
time=self.time[index],
|
|
70
|
+
altitude=self.altitude,
|
|
71
|
+
position=self.position[index],
|
|
72
|
+
temperature=self.temperature[index],
|
|
73
|
+
wiper_count=self.wiper_count[index],
|
|
74
|
+
cnr=self.cnr[index],
|
|
75
|
+
radial_velocity=self.radial_velocity[index],
|
|
76
|
+
radial_velocity_deviation=self.radial_velocity_deviation[index],
|
|
77
|
+
wind_speed=self.wind_speed[index],
|
|
78
|
+
wind_direction=self.wind_direction[index],
|
|
79
|
+
zonal_wind=self.zonal_wind[index],
|
|
80
|
+
meridional_wind=self.meridional_wind[index],
|
|
81
|
+
vertical_wind=self.vertical_wind[index],
|
|
82
|
+
system_id=self.system_id,
|
|
83
|
+
cnr_threshold=self.cnr_threshold,
|
|
84
|
+
)
|
|
85
|
+
raise TypeError
|
|
86
|
+
|
|
87
|
+
def sorted_by_time(self) -> Wls77:
|
|
88
|
+
sort_indices = np.argsort(self.time)
|
|
89
|
+
return self[sort_indices]
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def merge(cls, raws: Sequence[Wls77]) -> Wls77:
|
|
93
|
+
return cls(
|
|
94
|
+
time=np.concatenate(tuple(r.time for r in raws)),
|
|
95
|
+
altitude=raws[0].altitude,
|
|
96
|
+
position=np.concatenate(tuple(r.position for r in raws)),
|
|
97
|
+
temperature=np.concatenate(tuple(r.temperature for r in raws)),
|
|
98
|
+
wiper_count=np.concatenate(tuple(r.wiper_count for r in raws)),
|
|
99
|
+
cnr=np.concatenate(tuple(r.cnr for r in raws)),
|
|
100
|
+
radial_velocity=np.concatenate(tuple(r.radial_velocity for r in raws)),
|
|
101
|
+
radial_velocity_deviation=np.concatenate(
|
|
102
|
+
tuple(r.radial_velocity_deviation for r in raws)
|
|
103
|
+
),
|
|
104
|
+
wind_speed=np.concatenate(tuple(r.wind_speed for r in raws)),
|
|
105
|
+
wind_direction=np.concatenate(tuple(r.wind_direction for r in raws)),
|
|
106
|
+
zonal_wind=np.concatenate(tuple(r.zonal_wind for r in raws)),
|
|
107
|
+
meridional_wind=np.concatenate(tuple(r.meridional_wind for r in raws)),
|
|
108
|
+
vertical_wind=np.concatenate(tuple(r.vertical_wind for r in raws)),
|
|
109
|
+
system_id=merge_all_equal("system_id", [r.system_id for r in raws]),
|
|
110
|
+
cnr_threshold=merge_all_equal(
|
|
111
|
+
"cnr_threshold", [r.cnr_threshold for r in raws]
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def non_strictly_increasing_timesteps_removed(self) -> Wls77:
|
|
116
|
+
if len(self.time) == 0:
|
|
117
|
+
return self
|
|
118
|
+
mask = np.ones_like(self.time, dtype=np.bool_)
|
|
119
|
+
latest_time = self.time[0]
|
|
120
|
+
for i, t in enumerate(self.time[1:], start=1):
|
|
121
|
+
if t <= latest_time:
|
|
122
|
+
mask[i] = False
|
|
123
|
+
else:
|
|
124
|
+
latest_time = t
|
|
125
|
+
return self[mask]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _raw_rs_to_wls77(
|
|
129
|
+
raw: dict[str, Any],
|
|
130
|
+
) -> Wls77:
|
|
131
|
+
time_ts = raw["time"]
|
|
132
|
+
time = np.array(
|
|
133
|
+
[
|
|
134
|
+
datetime64(datetime.fromtimestamp(ts, timezone.utc).replace(tzinfo=None))
|
|
135
|
+
for ts in time_ts
|
|
136
|
+
]
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
n = time.size
|
|
140
|
+
|
|
141
|
+
return Wls77(
|
|
142
|
+
time=time,
|
|
143
|
+
altitude=np.array(raw["altitude"], dtype=np.float64),
|
|
144
|
+
position=np.array(raw["position"], dtype=np.float64),
|
|
145
|
+
temperature=np.array(raw["temperature"], dtype=np.float64),
|
|
146
|
+
wiper_count=np.array(raw["wiper_count"], dtype=np.float64),
|
|
147
|
+
cnr=np.array(raw["cnr"], dtype=np.float64).reshape(n, -1),
|
|
148
|
+
radial_velocity=np.array(raw["radial_velocity"], dtype=np.float64).reshape(
|
|
149
|
+
n, -1
|
|
150
|
+
),
|
|
151
|
+
radial_velocity_deviation=np.array(
|
|
152
|
+
raw["radial_velocity_deviation"], dtype=np.float64
|
|
153
|
+
).reshape(n, -1),
|
|
154
|
+
wind_speed=np.array(raw["wind_speed"], dtype=np.float64).reshape(n, -1),
|
|
155
|
+
wind_direction=np.array(raw["wind_direction"], dtype=np.float64).reshape(n, -1),
|
|
156
|
+
zonal_wind=np.array(raw["zonal_wind"], dtype=np.float64).reshape(n, -1),
|
|
157
|
+
meridional_wind=np.array(raw["meridional_wind"], dtype=np.float64).reshape(
|
|
158
|
+
n, -1
|
|
159
|
+
),
|
|
160
|
+
vertical_wind=np.array(raw["vertical_wind"], dtype=np.float64).reshape(n, -1),
|
|
161
|
+
cnr_threshold=raw["cnr_threshold"],
|
|
162
|
+
system_id=raw["system_id"],
|
|
163
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|