doppy 0.5.2__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.

Files changed (49) hide show
  1. {doppy-0.5.2 → doppy-0.5.4}/Cargo.lock +55 -29
  2. {doppy-0.5.2 → doppy-0.5.4}/Cargo.toml +6 -1
  3. {doppy-0.5.2 → doppy-0.5.4}/PKG-INFO +2 -1
  4. {doppy-0.5.2 → doppy-0.5.4}/crates/doppy_rs/Cargo.toml +2 -1
  5. doppy-0.5.4/crates/doppy_rs/src/raw/wls77.rs +90 -0
  6. {doppy-0.5.2 → doppy-0.5.4}/crates/doppy_rs/src/raw.rs +2 -0
  7. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/Cargo.toml +1 -0
  8. doppy-0.5.4/crates/doprs/src/bin/parse_wls70.rs +8 -0
  9. doppy-0.5.4/crates/doprs/src/bin/parse_wls77.rs +7 -0
  10. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/src/raw/halo_hpl.rs +30 -4
  11. doppy-0.5.4/crates/doprs/src/raw/wls77.rs +290 -0
  12. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/src/raw.rs +1 -0
  13. {doppy-0.5.2 → doppy-0.5.4}/pyproject.toml +1 -0
  14. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/__init__.py +10 -1
  15. doppy-0.5.4/src/doppy/raw/wls77.py +163 -0
  16. {doppy-0.5.2 → doppy-0.5.4}/LICENSE +0 -0
  17. {doppy-0.5.2 → doppy-0.5.4}/README.md +0 -0
  18. {doppy-0.5.2 → doppy-0.5.4}/crates/doppy_rs/src/lib.rs +0 -0
  19. {doppy-0.5.2 → doppy-0.5.4}/crates/doppy_rs/src/raw/halo_hpl.rs +0 -0
  20. {doppy-0.5.2 → doppy-0.5.4}/crates/doppy_rs/src/raw/wls70.rs +0 -0
  21. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/.gitignore +0 -0
  22. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/src/lib.rs +0 -0
  23. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/src/raw/error.rs +0 -0
  24. {doppy-0.5.2 → doppy-0.5.4}/crates/doprs/src/raw/wls70.rs +0 -0
  25. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/__init__.py +0 -0
  26. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/__main__.py +0 -0
  27. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/bench.py +0 -0
  28. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/data/__init__.py +0 -0
  29. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/data/api.py +0 -0
  30. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/data/cache.py +0 -0
  31. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/data/exceptions.py +0 -0
  32. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/defaults.py +0 -0
  33. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/exceptions.py +0 -0
  34. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/netcdf.py +0 -0
  35. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/options.py +0 -0
  36. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/__init__.py +0 -0
  37. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/noise_utils.py +0 -0
  38. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/stare.py +0 -0
  39. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/stare_depol.py +0 -0
  40. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/turbulence.py +0 -0
  41. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/product/wind.py +0 -0
  42. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/py.typed +0 -0
  43. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/halo_bg.py +0 -0
  44. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/halo_hpl.py +0 -0
  45. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/halo_sys_params.py +0 -0
  46. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/utils.py +0 -0
  47. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/windcube.py +0 -0
  48. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/raw/wls70.py +0 -0
  49. {doppy-0.5.2 → doppy-0.5.4}/src/doppy/utils.py +0 -0
@@ -34,9 +34,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
34
34
 
35
35
  [[package]]
36
36
  name = "bitflags"
37
- version = "2.8.0"
37
+ version = "2.9.0"
38
38
  source = "registry+https://github.com/rust-lang/crates.io-index"
39
- checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
39
+ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
40
40
 
41
41
  [[package]]
42
42
  name = "bumpalo"
@@ -46,9 +46,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
46
46
 
47
47
  [[package]]
48
48
  name = "cc"
49
- version = "1.2.15"
49
+ version = "1.2.16"
50
50
  source = "registry+https://github.com/rust-lang/crates.io-index"
51
- checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af"
51
+ checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
52
52
  dependencies = [
53
53
  "shlex",
54
54
  ]
@@ -106,27 +106,29 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
106
106
 
107
107
  [[package]]
108
108
  name = "doppy_rs"
109
- version = "0.5.2"
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.2"
119
+ version = "0.5.4"
119
120
  dependencies = [
120
121
  "chrono",
122
+ "ndarray 0.16.1",
121
123
  "rayon",
122
124
  "regex",
123
125
  ]
124
126
 
125
127
  [[package]]
126
128
  name = "either"
127
- version = "1.14.0"
129
+ version = "1.15.0"
128
130
  source = "registry+https://github.com/rust-lang/crates.io-index"
129
- checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d"
131
+ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
130
132
 
131
133
  [[package]]
132
134
  name = "heck"
@@ -159,9 +161,9 @@ dependencies = [
159
161
 
160
162
  [[package]]
161
163
  name = "indoc"
162
- version = "2.0.5"
164
+ version = "2.0.6"
163
165
  source = "registry+https://github.com/rust-lang/crates.io-index"
164
- checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
166
+ checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd"
165
167
 
166
168
  [[package]]
167
169
  name = "js-sys"
@@ -175,9 +177,9 @@ dependencies = [
175
177
 
176
178
  [[package]]
177
179
  name = "libc"
178
- version = "0.2.170"
180
+ version = "0.2.171"
179
181
  source = "registry+https://github.com/rust-lang/crates.io-index"
180
- checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
182
+ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
181
183
 
182
184
  [[package]]
183
185
  name = "lock_api"
@@ -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",
@@ -277,9 +294,9 @@ dependencies = [
277
294
 
278
295
  [[package]]
279
296
  name = "once_cell"
280
- version = "1.20.3"
297
+ version = "1.21.0"
281
298
  source = "registry+https://github.com/rust-lang/crates.io-index"
282
- checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
299
+ checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
283
300
 
284
301
  [[package]]
285
302
  name = "parking_lot"
@@ -310,11 +327,20 @@ 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
- version = "1.0.93"
341
+ version = "1.0.94"
316
342
  source = "registry+https://github.com/rust-lang/crates.io-index"
317
- checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
343
+ checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
318
344
  dependencies = [
319
345
  "unicode-ident",
320
346
  ]
@@ -384,9 +410,9 @@ dependencies = [
384
410
 
385
411
  [[package]]
386
412
  name = "quote"
387
- version = "1.0.38"
413
+ version = "1.0.40"
388
414
  source = "registry+https://github.com/rust-lang/crates.io-index"
389
- checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
415
+ checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
390
416
  dependencies = [
391
417
  "proc-macro2",
392
418
  ]
@@ -419,9 +445,9 @@ dependencies = [
419
445
 
420
446
  [[package]]
421
447
  name = "redox_syscall"
422
- version = "0.5.9"
448
+ version = "0.5.10"
423
449
  source = "registry+https://github.com/rust-lang/crates.io-index"
424
- checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f"
450
+ checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
425
451
  dependencies = [
426
452
  "bitflags",
427
453
  ]
@@ -463,9 +489,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
463
489
 
464
490
  [[package]]
465
491
  name = "rustversion"
466
- version = "1.0.19"
492
+ version = "1.0.20"
467
493
  source = "registry+https://github.com/rust-lang/crates.io-index"
468
- checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
494
+ checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
469
495
 
470
496
  [[package]]
471
497
  name = "scopeguard"
@@ -487,9 +513,9 @@ checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
487
513
 
488
514
  [[package]]
489
515
  name = "syn"
490
- version = "2.0.98"
516
+ version = "2.0.100"
491
517
  source = "registry+https://github.com/rust-lang/crates.io-index"
492
- checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
518
+ checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
493
519
  dependencies = [
494
520
  "proc-macro2",
495
521
  "quote",
@@ -504,15 +530,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
504
530
 
505
531
  [[package]]
506
532
  name = "unicode-ident"
507
- version = "1.0.17"
533
+ version = "1.0.18"
508
534
  source = "registry+https://github.com/rust-lang/crates.io-index"
509
- checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe"
535
+ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
510
536
 
511
537
  [[package]]
512
538
  name = "unindent"
513
- version = "0.2.3"
539
+ version = "0.2.4"
514
540
  source = "registry+https://github.com/rust-lang/crates.io-index"
515
- checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
541
+ checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
516
542
 
517
543
  [[package]]
518
544
  name = "wasm-bindgen"
@@ -4,6 +4,11 @@ resolver = "2"
4
4
 
5
5
  [workspace.package]
6
6
  edition = "2021"
7
- version = "0.5.2"
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.2
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
@@ -1,6 +1,7 @@
1
1
  [dependencies]
2
- numpy = "0.20"
3
2
  doprs = {path = "../doprs"}
3
+ ndarray = {workspace=true}
4
+ numpy = {workspace=true}
4
5
 
5
6
  [dependencies.pyo3]
6
7
  version = "0.20.0"
@@ -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
  }
@@ -2,6 +2,7 @@
2
2
  chrono = "0.4"
3
3
  regex = "1.10"
4
4
  rayon = "1.8"
5
+ ndarray = {workspace=true}
5
6
 
6
7
  [package]
7
8
  name = "doprs"
@@ -0,0 +1,8 @@
1
+ use doprs::raw::wls70;
2
+
3
+ fn main() {
4
+ let path = std::env::args().nth(1).unwrap();
5
+ let file = std::fs::File::open(path).unwrap();
6
+ let a = wls70::from_file_src(&file).unwrap();
7
+ dbg!(a);
8
+ }
@@ -0,0 +1,7 @@
1
+ use doprs::raw::wls77;
2
+
3
+ fn main() {
4
+ let path = std::env::args().nth(1).unwrap();
5
+ let file = std::fs::File::open(path).unwrap();
6
+ let _a = wls77::from_file_src(&file).unwrap();
7
+ }
@@ -29,6 +29,22 @@ pub struct Info {
29
29
  pub start_time: i64, // Unix-timestamp
30
30
  pub system_id: String,
31
31
  pub instrument_spectral_width: Option<f64>,
32
+ range_formula: Option<RangeFormula>,
33
+ }
34
+
35
+ #[derive(Debug, Clone)]
36
+ enum RangeFormula {
37
+ Common, //(range gate + 0.5) * Gate length
38
+ Overlapping, //Gate length / 2 + (range gate x 3)
39
+ }
40
+
41
+ impl RangeFormula {
42
+ fn compute_distance(&self, gate_index: f64, range_gate_length: f64) -> f64 {
43
+ match self {
44
+ RangeFormula::Common => (gate_index + 0.5) * range_gate_length,
45
+ RangeFormula::Overlapping => range_gate_length / 2.0 + gate_index * 3.0,
46
+ }
47
+ }
32
48
  }
33
49
 
34
50
  #[derive(Debug, Default, Clone)]
@@ -97,7 +113,11 @@ pub fn from_bytes_src(content: &[u8]) -> Result<HaloHpl, RawParseError> {
97
113
  }
98
114
  }
99
115
  let info = parse_header(&buf_header)?;
100
- let data = parse_data(&mut cur, info.ngates, info.range_gate_length)?;
116
+ let range_formula = info
117
+ .range_formula
118
+ .as_ref()
119
+ .ok_or("Cannot find range formula")?;
120
+ let data = parse_data(&mut cur, info.ngates, info.range_gate_length, range_formula)?;
101
121
  Ok(HaloHpl { info, data })
102
122
  }
103
123
 
@@ -105,6 +125,7 @@ fn parse_data(
105
125
  cur: &mut Cursor<&[u8]>,
106
126
  ngates: u64,
107
127
  range_gate_length: f64,
128
+ range_formula: &RangeFormula,
108
129
  ) -> Result<Data, RawParseError> {
109
130
  let (n1d, n2d) = infer_data_shape(cur)?;
110
131
  if ngates < 1 || n1d < 3 || n2d < 4 {
@@ -161,7 +182,7 @@ fn parse_data(
161
182
  time: data_1d[0].clone(),
162
183
  radial_distance: gate
163
184
  .iter()
164
- .map(|&x| (x + 0.5) * range_gate_length)
185
+ .map(|&x| range_formula.compute_distance(x, range_gate_length))
165
186
  .collect(),
166
187
  azimuth: data_1d[1].clone(),
167
188
  elevation: data_1d[2].clone(),
@@ -249,8 +270,13 @@ fn parse_header(header_bytes: &[u8]) -> Result<Info, RawParseError> {
249
270
  info.instrument_spectral_width = Some(captures[1].parse()?);
250
271
  } else {
251
272
  match line.as_str() {
252
- "Altitude of measurement (center of gate) = (range gate + 0.5) * Gate length" => (),
253
- "Range of measurement (center of gate) = (range gate + 0.5) * Gate length" => (),
273
+ "Altitude of measurement (center of gate) = (range gate + 0.5) * Gate length" |
274
+ "Range of measurement (center of gate) = (range gate + 0.5) * Gate length" => {
275
+ info.range_formula = Some(RangeFormula::Common)
276
+ },
277
+ "Range of measurement (center of gate) = Gate length / 2 + (range gate x 3)" => {
278
+ info.range_formula = Some(RangeFormula::Overlapping)
279
+ },
254
280
  "Data line 1: Decimal time (hours) Azimuth (degrees) Elevation (degrees) Pitch (degrees) Roll (degrees)" => (),
255
281
  "Data line 1: Decimal time (hours) Azimuth (degrees) Elevation (degrees)" => (),
256
282
  "f9.6,1x,f6.2,1x,f6.2" => (),
@@ -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
+ }
@@ -1,3 +1,4 @@
1
1
  pub mod error;
2
2
  pub mod halo_hpl;
3
3
  pub mod wls70;
4
+ pub mod wls77;
@@ -44,6 +44,7 @@ dev = [
44
44
  "release-version",
45
45
  "pre-commit",
46
46
  "xarray[io]",
47
+ "seaborn",
47
48
  ]
48
49
 
49
50
  [project.scripts]
@@ -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__ = ["HaloHpl", "HaloBg", "HaloSysParams", "WindCube", "WindCubeFixed", "Wls70"]
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