This commit is contained in:
Tsuki 2024-11-11 15:47:07 +08:00
parent b5c617c9bb
commit 61930aeb02
39 changed files with 8456 additions and 14 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target
.idea
*/target/*

1331
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
[workspace]
members = ["mp"]
members = ["mp", "mp_core", "radarg_plugin_interface"]

263
config.toml Normal file
View File

@ -0,0 +1,263 @@
[common]
background = "Terrain"
path = "resources/alts.png"
plugins = "loaders"
[[cmap]]
type = "DBZ"
levels = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
colors = [
"#aaaaaa",
"#0022ff",
"#01a0f6",
"#00ecec",
"#00d800",
"#019000",
"#ffff00",
"#e7c000",
"#ff9000",
"#ff0000",
"#d60000",
"#c00000",
"#ff00f0",
"#9600b4",
"#ad90f0",
]
[[cmap]]
type = "VEL"
levels = [
-90,
-45,
-35,
-27,
-20,
-15,
-10,
-5,
-1,
0,
1,
5,
10,
15,
20,
27,
1000,
]
colors = [
"#9fffff",
"#00e0ff",
"#0080ff",
"#320096",
"#00fb90",
"#00bb90",
"#008f00",
"#cdc09f",
"#000000",
"#f88700",
"#ffcf00",
"#ffff00",
"#ae0000",
"#d07000",
"#dd0000",
"#ff0000",
]
[[cmap]]
type = "SW"
colors = [
"#E0E0E0",
"#7CE0E0",
"#00E0E0",
"#00B0B0",
"#00FEFE",
"#00C400",
"#008000",
"#FEFE00",
"#FED200",
"#FE7C00",
"#FEB0B0",
"#FE5858",
"#FE0000",
"#FEFEFE",
]
levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[[cmap]]
type = "CC"
colors = [
"#003CFF",
"#00EFEF",
"#00BABF",
"#00837D",
"#008938",
"#00B729",
"#00DA0D",
"#00FF00",
"#FFFF3B",
"#FFF000",
"#FFC600",
"#FFA500",
"#FF7200",
"#FF1F00",
"#C10000",
"#D400AA",
]
levels = [
0,
0.1,
0.3,
0.5,
0.6,
0.7,
0.8,
0.85,
0.9,
0.92,
0.94,
0.95,
0.96,
0.97,
0.98,
0.99,
]
[[cmap]]
type = "KDP"
colors = [
"#00FFFF",
"#00EFEF",
"#00A8AC",
"#B4B4B4",
"#B4B4B4",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-0.8,
-0.4,
-0.2,
-0.1,
0.1,
0.15,
0.22,
0.33,
0.5,
0.75,
1.1,
1.7,
2.4,
3.1,
7,
20,
]
[[cmap]]
type = "ZDR"
colors = [
"#464646",
"#505050",
"#5A5A5A",
"#646464",
"#6E6E6E",
"#787878",
"#828282",
"#8C8C8C",
"#969696",
"#AFAFAF",
"#C8C8C8",
"#DCF0DC",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFF20F",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#FFFF00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-5,
-4.5,
-4,
-3.5,
-3,
-2.5,
-2,
-1.5,
-1,
-0.5,
0,
0.5,
1,
1.5,
2,
2.5,
3,
3.5,
4,
4.5,
5,
5.5,
6,
6.5,
7,
]
[[cmap]]
type = "VIL"
levels = [
0.1,
1,
2.5,
5,
7.5,
10,
10.25,
15,
20,
25,
30,
35,
40,
45,
50,
55,
104,
]
colors = [
'#484892',
'#01a0f6',
'#00ecec',
'#01ff00',
'#00c800',
'#019000',
'#ffff00',
'#e7c000',
'#ff9000',
'#ff0000',
'#d60000',
'#c00000',
'#ff00f0',
'#ad90f0',
'#780084',
'#d8af97',
]

1788
loaders/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

2
loaders/Cargo.toml Normal file
View File

@ -0,0 +1,2 @@
[workspace]
members = ["etws_loader"]

974
loaders/etws_loader/Cargo.lock generated Normal file
View File

@ -0,0 +1,974 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "abi_stable"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d6512d3eb05ffe5004c59c206de7f99c34951504056ce23fc953842f12c445"
dependencies = [
"abi_stable_derive",
"abi_stable_shared",
"const_panic",
"core_extensions",
"crossbeam-channel",
"generational-arena",
"libloading",
"lock_api",
"parking_lot",
"paste",
"repr_offset",
"rustc_version",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "abi_stable_derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7178468b407a4ee10e881bc7a328a65e739f0863615cca4429d43916b05e898"
dependencies = [
"abi_stable_shared",
"as_derive_utils",
"core_extensions",
"proc-macro2",
"quote",
"rustc_version",
"syn 1.0.109",
"typed-arena",
]
[[package]]
name = "abi_stable_shared"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63"
dependencies = [
"core_extensions",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "as_derive_utils"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4"
dependencies = [
"core_extensions",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.0",
]
[[package]]
name = "const_panic"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
[[package]]
name = "core-foundation-sys"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "core_extensions"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee"
dependencies = [
"core_extensions_proc_macros",
]
[[package]]
name = "core_extensions_proc_macros"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "earcutr"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01"
dependencies = [
"itertools",
"num-traits",
]
[[package]]
name = "either"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
[[package]]
name = "etws_loader"
version = "0.1.0"
dependencies = [
"abi_stable",
"anyhow",
"byteorder",
"chrono",
"flate2",
"geo",
"nom",
"nom-derive",
"num-traits",
"radarg_plugin_interface",
"serde",
"serde_json",
"thiserror",
]
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "float_next_after"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
[[package]]
name = "generational-arena"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7"
dependencies = [
"cfg-if",
]
[[package]]
name = "geo"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501"
dependencies = [
"earcutr",
"float_next_after",
"geo-types",
"geographiclib-rs",
"log",
"num-traits",
"robust",
"rstar",
"spade",
]
[[package]]
name = "geo-types"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
dependencies = [
"approx",
"num-traits",
"rstar",
"serde",
]
[[package]]
name = "geographiclib-rs"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960"
dependencies = [
"libm",
]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"stable_deref_trait",
]
[[package]]
name = "iana-time-zone"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "js-sys"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "nom-derive"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ff943d68b88d0b87a6e0d58615e8fa07f9fd5a1319fa0a72efc1f62275c79a7"
dependencies = [
"nom",
"nom-derive-impl",
"rustversion",
]
[[package]]
name = "nom-derive-impl"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0b9a93a84b0d3ec3e70e02d332dc33ac6dfac9cde63e17fcb77172dededa62"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets 0.48.5",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radarg_plugin_interface"
version = "0.1.0"
dependencies = [
"abi_stable",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags",
]
[[package]]
name = "repr_offset"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea"
dependencies = [
"tstr",
]
[[package]]
name = "robust"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
[[package]]
name = "rstar"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
dependencies = [
"heapless",
"num-traits",
"smallvec",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustversion"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d1bd37ce2324cf3bf85e5a25f96eb4baf0d5aa6eba43e7ae8958870c4ec48ed"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "spade"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61addf9117b11d1f5b4bf6fe94242ba25f59d2d4b2080544b771bd647024fd00"
dependencies = [
"hashbrown",
"num-traits",
"robust",
"smallvec",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "tstr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e"
dependencies = [
"tstr_proc_macros",
]
[[package]]
name = "tstr_proc_macros"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a"
[[package]]
name = "typed-arena"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-bindgen"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.48",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "zerocopy"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]

View File

@ -0,0 +1,30 @@
[package]
name = "etws_loader"
version = "0.1.0"
authors = ["tsuki <tsuki@keitsuki.top>"]
edition = "2021"
[dependencies]
abi_stable = "*"
anyhow = "1.0.79"
bytemuck = "1.17.1"
byteorder = "1.5.0"
chrono = { version = "0.4.33", features = ["serde"] }
flate2 = "1.0.28"
geo = "0.28.0"
nom = "7.1.3"
nom-derive = "0.10.1"
num-traits = "0.2.17"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.112"
thiserror = "1.0.56"
zarrs = "0.16.4"
zip = "2.2.0"
[dependencies.radarg_plugin_interface]
version = "0.1"
path = "../../radarg_plugin_interface"
[lib]
name = "etws_loader"
crate-type = ["cdylib", 'rlib']

480
loaders/etws_loader/raw.py Normal file
View File

@ -0,0 +1,480 @@
"""
大探标准格式解析代码
"""
import gc
import numpy as np
import struct
__all__ = ['get_radar_data']
def get_radar_data_new(f):
"""
:param filename:
:return: dbz, vel, w, zdr, cc, dp, kdp, el_n, azm, el
"""
f.seek(0, 2)
block_length_all = f.tell()
f.seek(0)
GENERIC_HEADER = { # noqa
# 共32bit保留字节16bit
"Magic_Number": struct.unpack('I', f.read(4)), # 魔术字 - --固定标志,用来指示雷达数据文件
# "Major_Version": struct.unpack('h', f.read(2)), # 主版本号
# "Minor_Version": struct.unpack('h', f.read(2)), # 次版本号
"Major_Version": struct.unpack('H', f.read(2)), # 主版本号
"Minor_Version": struct.unpack('H', f.read(2)), # 次版本号
"Generic_Type": struct.unpack('I', f.read(4)),
# 文件类型 - --1基数据文件2气象产品文件3 - --体扫配置文件4 - --保留5 - --体扫调度配置文件6 - --雷达状态数据文件
"Product_Type": struct.unpack('I', f.read(4)), # 产品类型 - --仅在文件类型为2时生效
# 保留字节16bit
"Reserved": f.read(16)
}
# f.seek(32)
SITE_CONFIG = { # 共128bit保留字节46bit
"Site_Code": struct.unpack("8s", f.read(8))[0], # 站号 - --站号具有唯一性用来区别不同的雷达站如Z9010
"Site_Name": struct.unpack("32s", f.read(32))[0], # 站点名称 - --站点名称如BeiJing
"Latitude": struct.unpack('f', f.read(4))[0], # 纬度 - --雷达站天线所在位置纬度
"Longitude": struct.unpack('f', f.read(4))[0], # 经度 - --雷达站天线所在位置经度
"Antenna_Height": struct.unpack('I', f.read(4))[0], # 天线高度 - --天线馈源水平时海拔高度
"Ground_Height": struct.unpack('I', f.read(4)), # 地面高度 - --雷达塔楼地面海拔高度
"Frequency": struct.unpack('f', f.read(4)), # 工作频率
# TODO gyj modify
# "Beam_Width_Hori": struct.unpack('f', f.read(4)), # 水平波束宽度
# "Beam_Width_Vert": struct.unpack('f', f.read(4)), # 垂直波束宽度
"Antenna_Type": struct.unpack('I', f.read(4)), # 天线类型
"TR_Number": struct.unpack('I', f.read(4)), # 收发单元数量
"RDA_Version": struct.unpack('I', f.read(4)), # RDA版本号 - --雷达数据采集软件版本号
# TODO gyj modify
# 't':struct.unpack('h', f.read(2))[0],
"Radar_Type": {1: "SA", 2: "SB", 3: "SC", 4: "SAD", 7:'SPAR',8:'SPARD',
33: "CA", 34: "CB", 35: "CC", 36: "CCJ", 37: "CD", 38: "CAD",
43:'CPAR',44:'CPARD',
65: "XA", 66: "XAD",69:'XPAR',70:'XPARD'}.get(struct.unpack('h', f.read(2))[0]),
# 雷达类型 - --1SA2SB3SC4SAD33CA34CB35CC36CCJ37CD38CAD65XA66XAD
# 7SPAR 8SPARD 43CPAR 44CPARD 69XPAR 70XPARD
# "Antenna_Gain": struct.unpack('h', f.read(2)), # 天线增益 - --编码为实际100倍
# "Transmitting_feeder_loss": struct.unpack('h', f.read(2)), # 发射馈线损耗 - --编码为实际损耗100倍
# "Receiving_feeder_loss": struct.unpack('h', f.read(2)), # 接收馈线损耗 - --编码为实际损耗100倍
# "Other_Loss": struct.unpack('h', f.read(2)), # 其他损耗 - --编码为实际损耗100倍
# 保留字节46bit
"Reserved": f.read(54)
}
# f.seek(160)
TASK_CONFIG = { # 共256bit保留字节40bit
"Task_Name": struct.unpack("32s", f.read(32)), # 任务名称 - --任务名称如VCP21
"Task_Description": struct.unpack("128s", f.read(128)), # 任务描述
"Polarization_Type": struct.unpack('I', f.read(4))[0], # 极化方式 - --1 水平极化2 垂直极化3 水平 / 垂直同时4 水平 / 垂直交替
"Scan_Type": struct.unpack('I', f.read(4)
),
# 扫描任务类型 - --0 体扫1单层PPI2 单层RHI3 单层扇扫4 扇体扫5 多层RHI6 手工扫描
# "Pulse_Width": struct.unpack('I', f.read(4)
# ), # 脉冲宽度 - --发射脉冲宽度
# "Scan_Start_Time": struct.unpack('I', f.read(4)
# ), # 扫描开始时间 - --扫描开始时间为UTC标准时间计数, 1970年1月1日0时为起始计数基准点
#TODO gyj modify
'Scan_Beam_Number':struct.unpack('I', f.read(4)), #扫描配置中的发射波束数量
"Cut_Number": struct.unpack('I', f.read(4)
), # 扫描层数 - --根据扫描任务类型确定的扫描层数,与基数据保存的层数一致
"Ray_Order": struct.unpack('I', f.read(4)), #径向数据排序,0 按径向采集时间排序1 按先方位后俯仰方式排序
"Scan_Start_Time": struct.unpack('q', f.read(8)
), # 扫描开始时间 s - --扫描开始时间为UTC标准时间计数, 1970年1月1日0时为起始计数基准点
# "Horizontal_Noise": struct.unpack('f', f.read(4)
# ), # 水平通道噪声 - --水平通道的噪声电平
# "Vertical_Noise": struct.unpack('f', f.read(4)
# ), # 垂直通道噪声 - --垂直通道的噪声电平
# "Horizontal_Calibration": struct.unpack('f', f.read(4)
# ), # 水平通道标定值 - --水平通道的反射率标定常数
# "Vertical_Calibration": struct.unpack('f', f.read(4)
# ), # 水平通道噪声温度 - --垂直通道的反射率标定常数
# "Horizontal_Noise_Temperature": struct.unpack('f', f.read(4)
# ), # 水平通道噪声温度
# "Vertical_Noise_Temperature": struct.unpack('f', f.read(4)
# ), # 垂直通道噪声温度
# "ZDR_Calibration": struct.unpack('f', f.read(4)
# ), # ZDR标定偏差
# "PHIDP_Calibration": struct.unpack('f', f.read(4)
# ), # 差分相移标定偏差
# "LDR_Calibration": struct.unpack('f', f.read(4)
# ), # 系统LDR标定偏差
"Reserved": f.read(68)
# 保留字节68bit
}
# CUT_CONFIG = { # 每个仰角共256bit保留72bit
# }
# print(np.frombuffer(f.read(128),'S128')[0].decode('Windows-1252').encode("utf-8"))
cut_number = TASK_CONFIG.get("Cut_Number")[0] # 扫描层数
Scan_Beam_Number = TASK_CONFIG.get("Scan_Beam_Number")[0] #扫描配置中的发射波束数量
# print(cut_number)
SCAN_CONFIG = {}
for i in range(1, Scan_Beam_Number + 1):
# f.seek(416)
# 扫描波束配置块(一个)
SCAN_CONFIG[i] = {
'BEAM_INDEX': struct.unpack('I', f.read(4)), # 按扫描波束依次编号,一个体扫可由多个扫描波束组成,一个扫描波束可输出多个扫描仰角数据
'BEAM_TYPE': struct.unpack('I', f.read(4)), # 扫描波束类型 1 宽波束 2 窄波束
'SubPulse_Number': struct.unpack('I', f.read(4)), # 子脉冲数量,主工作脉冲和补盲子脉冲数量总和以下按作用距离由远及近排列14
'Tx_Beam_Direction': struct.unpack('f', f.read(4)), # 发射波束中心指向,发射波束中心的俯仰角指向-2.0090.00
'Tx_Beam_Width-H': struct.unpack('f', f.read(4)), # 发射波束水平宽度
'Tx_Beam_Width-V': struct.unpack('f', f.read(4)), # 发射波束垂直宽度
'Tx_Beam_Gain': struct.unpack('f', f.read(4)), # 发射波束中心增益
'Reserved': f.read(100) # 保留字节100bit
}
# cut_number = SCAN_CONFIG['0'].get("SubPulse_Number")[0]
# cut_number = 4
# print('cut_number: ',cut_number)
# 子脉冲参数块4个
for j in range(1, 5):
SCAN_CONFIG[i]['%d' % j] = { # 共128bit保留70bit
'SubPulse_Strategy': struct.unpack('I', f.read(4)), # 子脉冲策略 0=频分 1=时分
'SubPulse_Modulation': struct.unpack('I', f.read(4)), # 子脉冲调制方式 0=单载频 1=线性正调频ULFM 2=线性负调频DLFM 3=非线性正调频UNLFM 4=非线性负调频DNLFM
'SubPulse_Frequency': struct.unpack('f', f.read(4)), # 子脉冲频率,点频脉冲指工作频率,调频脉冲指中心频率, 1.00999,000.00
'SubPulse_BandWidth': struct.unpack('f', f.read(4)), # 子脉冲带宽,对调频模式有效,表示双边带宽, MHz兆赫 1.0099.00
'SubPulse_Width': struct.unpack('I', f.read(4)), # 子脉冲宽度,单位为纳秒ns,信号脉冲宽度(时长)
'Horizontal_Noise': struct.unpack('f', f.read(4)), # 水平通道噪声,dBm分贝毫瓦,水平通道的噪声电平-100.000.00
'Vertical_Noise': struct.unpack('f', f.read(4)), # 垂直通道噪声,dBm分贝毫瓦,垂直通道的噪声电平-100.000.00
'Horizontal_Calibration': struct.unpack('f', f.read(4)), # 水平通道标定偏差,dB分贝,水平通道的标定偏差0.00200.00
'Vertical_Calibration': struct.unpack('f', f.read(4)), # 垂直通道标定偏差,dB分贝,垂直通道的标定偏差0.00200.00
'Horizontal_Noise_Temperature': struct.unpack('f', f.read(4)), # 水平通道噪声温度,单位为K开氏温标,水平通道的噪声温度0.00800.00
'Vertical_Noise_Temperature': struct.unpack('f', f.read(4)), # 垂直通道噪声温度,单位为K开氏温标,垂直通道的噪声温度0.00800.00
'ZDR_Calibration': struct.unpack('f', f.read(4)), # ZDR标定偏差,dB分贝,水平通道的标定偏差 -10.0010.00
'PHIDP_Calibration': struct.unpack('f', f.read(4)), # PHIDP标定偏差,度,水平通道的标定偏差 -180.00180.00
'LDR_Calibration': struct.unpack('f', f.read(4)), # LDR标定偏差,dB分贝,水平通道的标定偏差 -600
'Pulse_Points': struct.unpack('h', f.read(2)), # 脉冲距离库数, 0~10000,该子脉冲探测的距离库数
'Reserved': f.read(70), # 保留字节70bit
}
# # # ========== 接收仰角配置块 ========
BEAM_CONFIG = {}
for i in range(1, cut_number + 1):
BEAM_CONFIG['%d' %i] = {
'Cut_Index': struct.unpack('h', f.read(2)), #扫描层索引, 1256, 基数据仰角编号
'Tx_Beam_Index': struct.unpack('h', f.read(2)), # 发射波束索引, 1256, 对应的发射波束索引
'Rx_Beam_Elevation': struct.unpack('f', f.read(4)), #接收波束指向,度, -2.0090.00, PPI模式的俯仰角
'Tx_Beam_Gain': struct.unpack('f', f.read(4)), #发射波束增益,,dB分贝, 在本接收方向上,发射波束的增益
'Rx_Beam_Width_H': struct.unpack('f', f.read(4)), #接收波束水平宽度, 度
'Rx_Beam_Width_V': struct.unpack('f', f.read(4)), #接收波束垂直宽度, 度
'Rx_Beam_Gain': struct.unpack('f', f.read(4)), #接收波束垂直宽度, dB分贝,
"Process_Mode": {1: "PPP", 2: "FFT"}.get(struct.unpack('I', f.read(4))[0]), # 处理模式---1---PPP2---FFT
"Wave_Form": {0: "CS连续监测", 1: "CD连续多普勒", 2: "CDX多普勒扩展", 3: "Rx Test", 4: "BATCH批模式", 5: "Dual PRF双PRF",
6: "Staggered PRT 参差PRT"}.get(struct.unpack('I', f.read(4))[0]),
# 波形类别---0 CS连续监测1 CD连续多普勒2 CDX多普勒扩展3 Rx Test4 BATC H批模式5 Dual PRF双PRF 6---Staggered PRT 参差PRT
"N1PRF1": struct.unpack('f', f.read(4)
), # 第一组脉冲重复频率1---单位赫兹Hz---对于Batch、双PRF和参差PRT模式表示高PRF值。对于其它单PRF模式表示唯一的PRF值。
"N1PRF2": struct.unpack('f', f.read(4)
), # 第一组脉冲重复频率2---单位赫兹Hz---对Batch、双PRF和参差PRT模式表示低PRF值。对其它单PRF模式无效。
"N2PRF1": struct.unpack('f', f.read(4)
), # 第二组脉冲重复频率1---单位赫兹Hz---对于Batch、双PRF和参差PRT模式表示高PRF值。对于其它单PRF模式表示唯一的PRF值。
"N2PRF2": struct.unpack('f', f.read(4)
), # 第二组脉冲重复频率2---单位赫兹Hz---对Batch、双PRF和参差PRT模式表示低PRF值。对其它单PRF模式无效。
"Dealiasing_Mode": struct.unpack('I', f.read(4)), # 速度退模糊方法---1 单PRF2 双PRF3:2模式3 双PRF4:3模式4 双PRF 5:4模式
"Azimuth": struct.unpack('f', f.read(4)), # 方位角---RHI模式的方位角, 度0.00360.00, PPI模式的俯仰角同参数接收波
# "Elevation": struct.unpack('f', f.read(4)), # 俯仰角---PPI模式的俯仰角
"Start_Angle": struct.unpack('f', f.read(4)), # 起始角度----度,-10.00360.00---PPI扇扫的起始方位角或RHI模式的高限仰角
"End_Angle": struct.unpack('f', f.read(4)), # 结束角度---度,-10.00360.00---PPI扇扫的结束方位角或RHI模式的低限仰角
"Angular_Resolution": struct.unpack('f', f.read(4)), # 角度分辨率--度,---径向数据的角度分辨率仅用于PPI扫描模式
"Scan_Speed": struct.unpack('f', f.read(4)), # 扫描速度---度/秒---0.00100.00---PPI扫描的方位转速或RHI扫描的俯仰转速
"Log_Resolution": struct.unpack('f', f.read(4)), # 强度分辨率---米---15,000---强度数据的距离分辨率,支持浮点分辨率
"Doppler_Resolution": struct.unpack('f', f.read(4)), # 多普勒分辨率---米---15,000---多普勒数据的距离分辨率
"Maximum_Range1": struct.unpack('I', f.read(4)), # 最大距离1---米---1500,000---对应脉冲重复频率1的最大可探测距离
"Maximum_Range2": struct.unpack('I', f.read(4)), # 最大距离2---米---1500,000---对应脉冲重复频率2的最大可探测距离
"Start_Range": struct.unpack('I', f.read(4)), # 起始距离---米---1500,000---数据探测起始距离
"Sample1": struct.unpack('I', f.read(4)), # 采样个数1---2512---对应于脉冲重复频率1的采样个数
"Sample2": struct.unpack('I', f.read(4)), # 采样个数2---2512---对应于脉冲重复频率2的采样个数
"Phase_Mode": struct.unpack('I', f.read(4)), # 相位编码模式---1 固定相位;2 随机相位;3 SZ编码
"Atmospheric_Loss": struct.unpack('f', f.read(4)), # 大气衰减---分贝/千米--0.00000010.000000---双程大气衰减值精度为小数点后保留6位
"Nyquist_Speed": struct.unpack('f', f.read(4)), # 最大不模糊速度---米/秒---0100---理论最大不模糊速度
"Moments_Mask": struct.unpack("q", f.read(8)), # 数据类型掩码---00xFFFFFFFFFFFFFFFF---以掩码的形式表示当前允许获取的数据类型其中0不允许获取数据;1 –允许获取数据。(json1)
"Moments_Size_Mask": struct.unpack("q", f.read(8)),# 数据大小掩码---00xFFFFFFFFFFFFFFFF---以掩码形式表示每种数据类型字节数其中01个字节;1 2个字节(json1)
"Misc_Filter_Mask": struct.unpack('I', f.read(4)), # 滤波设置掩码--00xFFFFFFFF---0未应用;1应用(json2)
"SQI_Threshold": struct.unpack('f', f.read(4)), # SQI门限---0.001.00----
"SIG_Threshold": struct.unpack('f', f.read(4)), # SIG门限--dB分贝---0.0020.00
"CSR_Threshold": struct.unpack('f', f.read(4)), # CSR门限--dB分贝--0.00100.00
"LOG_Threshold": struct.unpack('f', f.read(4)), # LOG门限--dB分贝--0.0020.00
"CPA_Threshold": struct.unpack('f', f.read(4)), # CPA门限--0.00100.00--
"PMI_Threshold": struct.unpack('f', f.read(4)), # PMI门限--0.001.00
"DPLOG_Threshold": struct.unpack('f', f.read(4)), # DPLOG门限--0.00100.00
"Thresholds_r": struct.unpack('4s', f.read(4)), #阈值门限保留---水平通道的反射率标定常数 =======保留字段======
# 这个时保留字段 "Thresholds_r":data.slice(548,552),#阈值门限保留---水平通道的反射率标定常数
"dBT_Mask": struct.unpack('I', f.read(4)), # dBT质控掩码---00xFFFFFFFF---dBT数据使用的质控门限掩码其中0未应用,1应用(json3)
"dBZ_Mask": struct.unpack('I', f.read(4)), # dBZ质控掩码---dBZ数据使用的质控门限掩码, 其中0未应用,1应用(json3)
"Velocity_Mask": struct.unpack('I', f.read(4)), # 速度质控掩码----00xFFFFFFFF---速度数据使用的质控门限掩码, 其中0未应用,1应用(json3)
"Spectrum_Width_Mask": struct.unpack('I', f.read(4)), # 谱宽质控掩码----00xFFFFFFFF--谱宽数据使用的质控门限掩码,其中0未应用,1应用(json3)
"DP_Mask": struct.unpack('I', f.read(4)), # 偏振量质控掩码----00xFFFFFFFF--偏振量数据使用的质控门限掩码,其中0未应用,1应用(json3)
"Mask_Reserved": f.read(12), # 保留字节12bit
'Reserved': f.read(4), # 保留字节4bit
# 保留12bit
# "Scan_Sync": struct.unpack('I', f.read(4)), # 扫描同步标志---用于多部雷达同步扫描标识
"Direction": struct.unpack('I', f.read(4)), # 天线运行方向---仅对PPI模式有效1 顺时针;2 逆时针
"Ground_Clutter_Classifier_Type": struct.unpack('h', f.read(2)), # 地物杂波图类型---1 所有数据不滤波;2 全程滤波;3 使用实时动态滤波图;4 使用静态滤波图
"Ground_Clutter_Filter_Type": struct.unpack('h', f.read(2)),
# 地物滤波类型---0 –不滤波;1 频域自适应滤波;2 - 固定宽带频域滤波器;3 - 可变宽带频域滤波器;4 - 可变最小方差频域滤波器;5 IIR时域滤波
"Ground_Clutter_Filter_Notch_Width": struct.unpack('h', f.read(2)), # 地物滤波宽度--0.1 米/秒--0.110.0
"Ground_Clutter_Filter_Window": struct.unpack('h', f.read(2)),
# 滤波窗口类型---滤波算法FFT窗口类型;0 矩形窗;1 汉明窗;2 Blackman窗;3 自适应窗口;4
"Reserved": f.read(44), # 保留字节44bit
}
#TODO
# radar_range = np.arange(BEAM_CONFIG['1']['Sample1'][0]) * BEAM_CONFIG['1']['Log_Resolution'][0]
data_block = f.tell()
# print(data_block)
# return
data_shape = {}
# for J in range(radial_number):
RadialData = {
# 径向头共128字节 保留13 字节
"Radial_State": struct.unpack('I', f.read(4)),
# 径向数据状态 - --0仰角开始1中间数据2仰角结束3体扫开始4体扫结束5RHI开始6RHI结束
"Spot_Blank": struct.unpack('I', f.read(4)), # 消隐标志 - --0正常1消隐
"Sequence_Number": struct.unpack('I', f.read(4)), # 序号 - 165536--每个体扫径向从1计数
"Radial_Number": struct.unpack('I', f.read(4)), # 径向数 - 11000--每个扫描从1计数
"Elevation_Number": struct.unpack('I', f.read(4)), # 仰角编号 - --仰角编号每个体扫从1计数
"Azimuth": struct.unpack('f', f.read(4)), # 方位角 ---度 0.00360.00 --扫描的方位角度
"Elevation": struct.unpack('f', f.read(4)), # 仰角 ---度 -2.0090.00--扫描的俯仰角度
"Seconds": struct.unpack('q', f.read(8)), # 秒 - --径向数据采集的时间UTC计数的秒数, 从1970年1月1日0时开始计数
"Microseconds": struct.unpack('I', f.read(4)), # 微秒 - --径向数据采集的时间除去UTC秒数后留下的微秒数
"Length_of_data": struct.unpack('I', f.read(4)), # 数据长度 --1100000 --仅本径向数据块所占用的长度如有压缩,长度为压缩后数据长度
"Moment_Number": struct.unpack('I', f.read(4)), # 数据类别数量 ---164 --径向数据类别如ZVW等各占一种的数量
"Scan Beam Index": struct.unpack('h', f.read(2)), # 波束编号 ---1-32 --波束编号
"Horizontal_Estimated_Noise": struct.unpack('h', f.read(2)), # 径向的水平估计噪声 --dB分贝 --编码为实际噪声的 - 100倍
"Vertical_Estimated_Noise": struct.unpack('h', f.read(2)), # 径向的垂直估计噪 - --编码为实际噪声的 - 100 倍
"PRF_FLAG": struct.unpack('I', f.read(4)), #重频标志, 11 N1 PRF #1 ; 12 N1 PRF #2; 21 N2 PRF #1; 22 N2 PRF #2; 排列方式11128888表示使用N1 PRF #1和PRF #2 #######################uint
# 88882122表示使用N2 PRF #1和PRF #2 11122122表示使用N1和N2的PRF #1和PRF #2 对应表2-7中09-13的描述8表示无效填充数据弥补用0表示会出现的问题
# "Zip_Type": struct.unpack('h', f.read(2)), # 压缩类型 - --0 - 不压缩1 - LZO压缩
"Reversed": f.read(70) # 保留70bit
}
radial_number = RadialData.get("Radial_Number")[0] # 径向数 J
type_number = RadialData.get("Moment_Number")[0] # 数据类别数量 K
typeHeader = {}
# for J in range(radial_number):
# for K in range(type_number):
for i in range(type_number):
# typeHeader[J][K] = {
typeHeader = {
# 共32字节
"Data_Type": struct.unpack('I', f.read(4)), # 数据类型 ---164 --具体径向数据类型见表2 - 6
"Scale": struct.unpack('I', f.read(4)), # 比例 --032768 --数据编码的比例
"Offset": struct.unpack('I', f.read(4)), # 偏移 --032768 --数据编码的偏移
"Bin_Length": struct.unpack('h', f.read(2)), # 库字节长度 - --保存一个距离库值用的字节数
"Flags": struct.unpack('h', f.read(2)), # 标志 - --数据标志位,暂不使用
"Length": struct.unpack('I', f.read(4)), # 长度 --132768 --距离库数据的长度,不包括当前的径向数据头大小
"Reserved": f.read(12)
}
block_length = typeHeader.get("Length")[0]
bin_length = typeHeader.get("Bin_Length")[0]
offset = typeHeader.get("Offset")[0]
scale = typeHeader.get("Scale")[0]
data_body = f.read(block_length)
raw = np.frombuffer(data_body, 'u' + str(bin_length)).astype(np.float64)
#TODO
if i ==0:
radar_range = np.arange(len(raw))*30
raw[raw <= 5.] = np.nan
# 对于保存的编码值来说5以下的值表示特殊意义不应该被解码。
value = (raw - offset) / scale
data_type = typeHeader.get("Data_Type")[0]
data_shape[data_type] = value.shape[0]
block_index = f.tell()
roll_num = int((block_length_all - data_block) / (block_index - data_block))
azm_num = int(roll_num / cut_number)
gc.collect()
# print("=========",data_shape)
# return get_radial_data(f, cut_number, azm_num, data_shape, data_block, SITE_CONFIG, radar_range, TASK_CONFIG['Polarization_Type'])
return get_radial_data(f, cut_number, azm_num, data_shape, data_block, SITE_CONFIG, radar_range, TASK_CONFIG['Polarization_Type'], BEAM_CONFIG)
def get_radial_data(f, cut_number, azm_num, data_shape, data_block, site_config, radar_range, polar_type, BEAM_CONFIG):
"""
:param f:
:param cut_number: cut number
:param azm_num: number of azm
:param data_shape: length of block
:return: dbz, vel, w, zdr, cc, dp, kdp, el_n, azm, el
dbz:滤波后反射率(Reflectivity) dBZ
vel:径向速度(Doppler Velocity) V
w:谱宽Spectrum Width W
zdr_:差分反射率Differential Reflectivity ZDR
cc_:协相关系数Cross Correlation Coefficient CC
dp_:差分相移Differential Phase DP
kdp_差分相移率Specific Differential Phase KDp
azm:方位角 维度1
el:仰角 维度 1
"""
_data_dict = {}
for k, v in data_shape.items():
if k == 2:
dbz = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['reflectData'] = dbz
elif k == 3:
vel = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['velData'] = vel
elif k == 4:
w = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['widData'] = w
elif k == 7:
zdr = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['zdrData'] = zdr
elif k == 9:
cc = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['ccData'] = cc
elif k == 10:
dp = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['dpData'] = dp
elif k == 11:
kdp = np.empty((cut_number, azm_num, v), dtype=np.float64)
_data_dict['kdpData'] = kdp
el_n = np.empty((cut_number, azm_num), np.int32)
azm = np.empty((cut_number, azm_num), np.float32)
el = np.empty((cut_number, azm_num), np.float32)
f.seek(data_block)
for j in range(cut_number):
for k in range(azm_num):
RadialData = {
# 共64字节 保留13 字节
"Radial_State": struct.unpack('I', f.read(4)),
# 径向数据状态 - --0仰角开始1中间数据2仰角结束3体扫开始4体扫结束5RHI开始6RHI结束
"Spot_Blank": struct.unpack('I', f.read(4)
), # 消隐标志 - --0正常1消隐
"Sequence_Number": struct.unpack('I', f.read(4)
), # 序号 - --每个体扫径向从1计数
"Radial_Number": struct.unpack('I', f.read(4)
), # 径向数 - --每个扫描从1计数
"Elevation_Number": struct.unpack('I', f.read(4)
), # 仰角编号 - --仰角编号每个体扫从1计数
"Azimuth": struct.unpack('f', f.read(4)
), # 方位角 - --扫描的方位角度
"Elevation": struct.unpack('f', f.read(4)
), # 仰角 - --扫描的俯仰角度
"Seconds": struct.unpack('q', f.read(8)
), # 秒 - --径向数据采集的时间UTC计数的秒数, 从1970年1月1日0时开始计数
"Microseconds": struct.unpack('I', f.read(4)
), # 微秒 - --径向数据采集的时间除去UTC秒数后留下的微秒数
"Length_of_data": struct.unpack('I', f.read(4)
), # 数据长度 - --仅本径向数据块所占用的长度如有压缩,长度为压缩后数据长度
"Moment_Number": struct.unpack('I', f.read(4)
), # 数据类别数量 - --径向数据类别如ZVW等各占一种的数量
"Scan Beam Index": struct.unpack('h', f.read(2)
), # 波束编号 ---1-32 --波束编号
"Horizontal_Estimated_Noise": struct.unpack('h', f.read(2)
), # 径向的水平估计噪声 - --编码为实际噪声的 - 100倍
"Vertical_Estimated_Noise": struct.unpack('h', f.read(2)
), # 径向的垂直估计噪 - --编码为实际噪声的 - 100 倍
"PRF_FLAG": struct.unpack('I', f.read(4)), #重频标志, 11 N1 PRF #1 ; 12 N1 PRF #2; 21 N2 PRF #1; 22 N2 PRF #2; 排列方式11128888表示使用N1 PRF #1和PRF #2 #######################uint
# 88882122表示使用N2 PRF #1和PRF #2 11122122表示使用N1和N2的PRF #1和PRF #2 对应表2-7中09-13的描述8表示无效填充数据弥补用0表示会出现的问题
# "Zip_Type": struct.unpack('h', f.read(2)
# ), # 压缩类型 - --0 - 不压缩1 - LZO压缩
"Reversed": f.read(70) # 保留13bit
}
# print(f.tell())
# f.seek(8672)
el_n_ = RadialData.get("Elevation_Number")[0]
azm_ = RadialData.get("Azimuth")[0]
# el_ = RadialData.get("Elevation")[0]
el_ = BEAM_CONFIG['%d' %(j+1)]["Rx_Beam_Elevation"][0]
type_number = RadialData.get("Moment_Number")[0]
for i in range(type_number):
typeHeader = {
# 共32字节
"Data_Type": struct.unpack('I', f.read(4)), # 数据类型 - --具体径向数据类型见表2 - 6
"Scale": struct.unpack('I', f.read(4)
), # 比例 - --数据编码的比例
"Offset": struct.unpack('I', f.read(4)
), # 偏移 - --数据编码的偏移
"Bin_Length": struct.unpack('h', f.read(2)
), # 库字节长度 - --保存一个距离库值用的字节数
"Flags": struct.unpack('h', f.read(2)
), # 标志 - --数据标志位,暂不使用
"Length": struct.unpack('I', f.read(4)
), # 长度 - --距离库数据的长度,不包括当前的径向数据头大小
"Reserved": f.read(12)
}
# print(typeHeader)
data_type = typeHeader.get("Data_Type")[0]
scale = typeHeader.get("Scale")[0]
offset = typeHeader.get("Offset")[0]
bin_length = typeHeader.get("Bin_Length")[0]
block_length = typeHeader.get("Length")[0]
data_body = f.read(block_length)
dtype = "u%d" % bin_length
# print(body)
raw = np.frombuffer(data_body, dtype).astype(np.float64)
# raw = np.asarray(struct.unpack(str(block_length) + "B", data_body)).astype(np.float64)
raw[raw <= 5.] = np.nan
value = (raw - offset) / scale
# print(np.nanmin(value))
# print(np.nanmax(value))
if data_type == 2:
# dbz[el_n_ - 1, k, :] = value
dbz[el_n_ - 1, k, :len(value)] = value
elif data_type == 3:
# vel[el_n_ - 1, k, :] = value
vel[el_n_ - 1, k, :len(value)] = value
elif data_type == 4:
# w[el_n_ - 1, k, :] = value
w[el_n_ - 1, k, :len(value)] = value
elif data_type == 7:
# zdr[el_n_ - 1, k, :] = value
zdr[el_n_ - 1, k, :len(value)] = value
elif data_type == 9:
# cc[el_n_ - 1, k, :] = value
cc[el_n_ - 1, k, :len(value)] = value
elif data_type == 10:
# dp[el_n_ - 1, k, :] = value
dp[el_n_ - 1, k, :len(value)] = value
elif data_type == 11:
# kdp[el_n_ - 1, k, :] = value
kdp[el_n_ - 1, k, :len(value)] = value
el_n[el_n_ - 1, k] = el_n_
azm[el_n_ - 1, k] = azm_
el[el_n_ - 1, k] = el_
# print(f"the No.{el_n_} elevation is {el_}")
gc.collect()
result = {
'radarNumber': site_config['Site_Code'],
'longitude': site_config['Longitude'],
'latitude': site_config['Latitude'],
'altitude': site_config['Antenna_Height'],
'radarRange': radar_range,
'azimuthData': azm[1, :],
'elevationData': el[:, 1],
'polar_type': polar_type
}
result.update(_data_dict)
f.close()
return result

View File

@ -0,0 +1,23 @@
use nom::error::Error;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ETWSError {
#[error("IO Error")]
IOError {
#[from]
source: std::io::Error,
},
#[error("Unsupported Format")]
FormatError {
#[from]
source: anyhow::Error,
},
#[error("Binary Error")]
NomError,
}
impl<'a> From<nom::Err<nom::error::Error<&'a [u8]>>> for ETWSError {
fn from(value: nom::Err<nom::error::Error<&'a [u8]>>) -> Self {
ETWSError::NomError
}
}

View File

@ -0,0 +1,252 @@
mod error;
mod raw;
use raw::parse_raw_data;
mod parser;
use abi_stable::{
export_root_module,
prefix_type::PrefixTypeTrait,
rvec, sabi_extern_fn,
sabi_trait::prelude::TD_Opaque,
std_types::{
RNone, ROk,
ROption::RSome,
RResult::{self, RErr},
RStr, RString, RVec,
},
};
use parser::{Record, ValueResult};
use radarg_plugin_interface::{
DataLoaderPlugin, DataLoaderPlugin_TO, Error, GridDataInfo, LoadedData, PluginId, PluginMod,
PluginMod_Ref, PluginType, ProbeDataType, RadarGridData, VecResult,
};
macro_rules! data_rvec {
($data:ident, $({$parsed_vec:tt => $abi_stable_rvec:tt}),+) => {
match $data {
$(
ValueResult::$parsed_vec(data) => VecResult::$abi_stable_rvec(RVec::from(data)),
)+
}
};
}
macro_rules! data_type {
($datetype:ident, $({ $($name:literal)|+ => $type:tt }),+) => {
match $datetype {
$(
$($name)|+ => ProbeDataType::$type,
)+
_ => ProbeDataType::Unknown
}
};
}
#[export_root_module]
fn instantiate_root_module() -> PluginMod_Ref {
PluginMod { new }.leak_into_prefix()
}
struct ETWSLoader {
id: PluginId,
}
impl DataLoaderPlugin for ETWSLoader {
fn plugin_id(&self) -> &PluginId {
&self.id
}
fn load(&self, path: RStr<'_>) -> RResult<RVec<LoadedData>, Error> where {
let path = path.as_str();
let c = Record::parse_from_path(path.to_string())
.map(|record| {
let result = record
.blocks
.into_iter()
.map(|b| {
let data = b.data;
let converted_data = data_rvec!(
data,
{I16 => I16},
{F32 => F32},
{F64 => F64},
{I32 => I32},
{U32 => U32},
{I64 => I64},
{U64 => U64},
{I8 => I8},
{U8 => U8}
);
let dimension_values = convert_nested_vec_to_rvec(b.info.dimension_values);
let dimension_size = convert_vec_to_rvec(
b.info
.dimension_size
.into_iter()
.map(|p| p as usize)
.collect::<Vec<_>>(),
);
let dimension_names = convert_vec_to_rvec(
b.info
.dimension_des
.into_iter()
.map(RString::from)
.collect::<Vec<_>>(),
);
let fill_value = b.info.fill_value;
let value_name = RString::from(b.info.value_name);
let value_description = RString::from(b.info.value_des);
let datetime = record.filetime.timestamp();
let value_key = value_name.as_str();
let data_type = data_type!(
value_key,
{ "ET" => ET },
{ "DBZ" | "CR" | "FR" | "R" | "CR0" | "CR1" | "CR2" => DBZ},
{ "VIL" => VIL},
{ "EB" => EB},
{ "V" => V},
{ "ZDR" => ZDR},
{ "PHIDP" => PHIDP},
{ "KDP" => KDP},
{ "CC" =>CC},
{ "HCA" => HCA},
{ "QPE" => QPE},
{ "QPF" => QPF}
);
let grid_info = GridDataInfo {
shape: dimension_size,
dimensions: dimension_values,
dimension_names,
fill_value,
datetime: RSome(datetime),
value_description: RSome(value_description),
maybe_probe_data_type: RSome(data_type),
value_name,
dimension_description: "Wrong".into(),
};
LoadedData::RadarGridData(RadarGridData {
data: converted_data,
info: grid_info,
})
})
.collect::<RVec<_>>();
result
})
.or_else(|_| {
let result = parse_raw_data(path)
.map(|result| {
let data = result.datas;
let shape = rvec![
result.els.len() as usize,
result.azs.len() as usize,
result.ranges.len() as usize
];
let dimensions = rvec![
RVec::from(result.els),
RVec::from(result.azs),
RVec::from(result.ranges)
];
let dimension_names = rvec!["el".into(), "az".into(), "range".into()];
let datetime = result.datetime;
let loaded = data
.into_iter()
.map(move |(k, v)| {
let data_type = data_type!(
k,
{ "ET" => ET },
{ "DBZ" | "CR" | "FR" | "R" | "CR0" | "CR1" | "CR2" => DBZ},
{ "VIL" => VIL},
{ "EB" => EB},
{ "V" | "VEL" => V},
{ "ZDR" => ZDR},
{ "PHIDP" => PHIDP},
{ "KDP" => KDP},
{ "CC" =>CC},
{ "HCA" => HCA},
{ "QPE" => QPE},
{ "QPF" => QPF}
);
let radar_data = RadarGridData {
data: VecResult::F64(RVec::from(v)),
info: GridDataInfo {
shape: shape.clone(),
dimensions: dimensions.clone(),
dimension_names: dimension_names.clone(),
fill_value: 0.0,
datetime: RSome(datetime),
value_description: RNone,
maybe_probe_data_type: RSome(data_type),
value_name: RString::from(k),
dimension_description: "Wrong".into(),
},
};
LoadedData::RadarGridData(radar_data)
})
.collect::<RVec<_>>();
loaded
})
.map_err(|_| Error::UnsupportedFormat)?;
Ok(result)
});
RResult::from(c)
}
fn plugin_info(&self) -> radarg_plugin_interface::PluginInfo where {
radarg_plugin_interface::PluginInfo {
plugin_type: "DataLoader".into(),
name: "ETWS_Loader".into(),
version: "0.1.0".into(),
author: "Tsuki".into(),
description: RSome("ETWS Loader".into()),
url: RSome("https://keitsuki.club".into()),
}
}
}
fn convert_iter_to_rvec<T>(raw: impl Iterator<Item = T>) -> RVec<T> {
RVec::from(raw.collect::<Vec<_>>())
}
fn convert_vec_to_rvec<T>(raw: Vec<T>) -> RVec<T> {
RVec::from(raw)
}
fn convert_nested_vec_to_rvec<T>(raw: Vec<Vec<T>>) -> RVec<RVec<T>> {
RVec::from(raw.into_iter().map(RVec::from).collect::<Vec<_>>())
}
#[sabi_extern_fn]
pub fn new(plugin_id: PluginId) -> RResult<PluginType, Error> {
let this = ETWSLoader { id: plugin_id };
ROk(DataLoaderPlugin_TO::from_value(this, TD_Opaque))
}
// 测试模块
#[cfg(test)]
mod tests {
// 导入外部作用域中的所有项
use super::*;
// 一个简单的测试
#[test]
fn test() {
let result =
Record::parse_from_path("/Volumes/data2/RadarArray/ShaoXing/radarData/OutputProducts/RadarProducts/BasicProductsX/20230627/20230627163400/ZJSXAA_20230627163400_VIL.dat.gz")
.unwrap();
}
}

View File

@ -0,0 +1,376 @@
use crate::error::ETWSError;
use byteorder::{BigEndian, ByteOrder, LittleEndian};
use chrono::{DateTime, NaiveDateTime, Utc};
use flate2::read::GzDecoder;
use nom::{
bytes::complete::{tag, take},
multi::count,
IResult,
};
use nom_derive::*;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::Read;
use std::path::Path;
pub enum ValueResult {
I64(Vec<i64>),
F64(Vec<f64>),
I32(Vec<i32>),
F32(Vec<f32>),
I16(Vec<i16>),
U64(Vec<u64>),
U32(Vec<u32>),
I8(Vec<i8>),
U8(Vec<u8>),
}
enum ValueTypes {
I64,
F64,
I32,
F32,
U64,
U32,
I16,
I8,
U8,
}
#[derive(Clone, Copy)]
enum Order {
BigEndian,
LittleEndian,
}
pub struct Record {
pub filetime: DateTime<Utc>,
pub blocks: Vec<ParsedBlock>, // Fill in generic types appropriately
}
macro_rules! match_in_macro {
($block:ident,$len:ident,$input:ident,$offset:ident,$scale:ident,$fill_value:ident,$(($branch:path, $t:ty, $bigger:ty,$raw_result:path, $bigger_result:path)),+) => {
{
use std::mem;
let need_trans = $offset != 0.0 || $scale != 1.0;
let trans_to_bigger = $offset.trunc() != $offset || $scale != 1.0;
match $block {
$(
$branch => {
let ratio = mem::size_of::<$t>() / mem::size_of::<u8>();
let (input, result) = take($len * ratio)($input)?;
let result = unsafe {
let ptr = result.as_ptr() as *const $t;
let slice = std::slice::from_raw_parts(ptr, $len);
let slice = slice.to_vec();
if trans_to_bigger {
let offset = $offset as $bigger;
let scale = $scale as $bigger;
$bigger_result(
slice
.into_iter()
.map(|p| if (p as f64 - $fill_value).abs() < f64::EPSILON {p as $bigger} else {(p as $bigger - offset) / scale} )
.collect::<Vec<$bigger>>(),
)
} else {
$raw_result(if need_trans {
let offset = $offset as $t;
slice.into_iter().map(|p| if (p as f64 - $fill_value).abs() < f64::EPSILON {p} else {p - offset}).collect::<Vec<$t>>()
} else {
slice
})
}
};
Ok((input, result))
},
)+
}
}
};
}
impl Record {
pub fn parse_from_path(path: impl AsRef<Path>) -> Result<Self, ETWSError> {
let path = path.as_ref();
if !(path.ends_with(".dat") || !path.ends_with(".dat.gz")) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid file extension",
)
.into());
}
if !path.exists() {
return Err(std::io::Error::new(std::io::ErrorKind::NotFound, "File not found").into());
}
let mut file = File::open(path)?;
let binary_data = if path.extension().unwrap() == "gz" {
let mut result: Vec<u8> = Vec::new();
let mut decoder = GzDecoder::new(file);
decoder.read_to_end(&mut result)?;
result
} else {
let mut result = Vec::new();
file.read_to_end(&mut result)?;
result
};
let (_, parsed) =
Self::_parse(binary_data.as_slice()).map_err(|_| anyhow::Error::msg("Parse error"))?;
Ok(parsed)
}
fn _parse(binary_data: &[u8]) -> IResult<&[u8], Record> {
let start_tag = b"UNI_DATA";
let (input, _) = tag(start_tag)(binary_data)?;
let (input, order) = Self::_parse_split_fn(input, 8, 8, Self::_parse_order)?;
let (input, hlen) = take(4usize)(input)?;
let hlen = match order {
Order::BigEndian => BigEndian::read_u32(hlen),
_ => LittleEndian::read_u32(hlen),
};
let (input, record_info) = Self::_parse_split_fn(input, 0, 8, |input| {
let (input, p) = take(hlen)(input)?;
let p: RecordInfo = serde_json::from_slice(p).unwrap();
Ok((input, p))
})?;
let (input, blocks) =
count(Self::_parse_block_fn(order), record_info.block_num as usize)(input)?;
let data_time =
NaiveDateTime::parse_from_str(&record_info.file_time, r"%Y%m%d%H%M%S").unwrap();
let filetime = data_time.and_utc();
Ok((input, Record { filetime, blocks }))
}
fn _parse_order(input: &[u8]) -> IResult<&[u8], Order> {
let (input, order) = take(4usize)(input)?;
let result = if order == b"LEND" {
Order::LittleEndian
} else {
Order::BigEndian
};
let (input, _) = take(4usize)(input)?;
Ok((input, result))
}
fn _parse_split(input: &[u8], s: usize, fore: usize, after: usize) -> IResult<&[u8], &[u8]> {
let (input, _) = take(fore)(input)?;
let (input, result) = take(s)(input)?;
let (input, _) = take(after)(input)?;
Ok((input, result))
}
fn _parse_split_fn<E, F: Fn(&[u8]) -> IResult<&[u8], E>>(
input: &[u8],
fore: usize,
after: usize,
f: F,
) -> IResult<&[u8], E> {
let (input, _) = take(fore)(input)?;
let (input, result) = (f)(input)?;
let (input, _) = take(after)(input)?;
Ok((input, result))
}
fn _parse_block_fn(order: Order) -> impl FnMut(&[u8]) -> IResult<&[u8], ParsedBlock> {
move |input| Self::_parse_split_fn(input, 0, 0, |input| Self::_parse_block(input, order))
}
fn _parse_block(input: &[u8], order: Order) -> IResult<&[u8], ParsedBlock> {
let (input, _) = take(8usize)(input)?;
let (input, hlen1) = Self::_parse_u32(input, order)?;
let (input, block_info) = Self::_parse_split_fn(input, 0, 8, |input| {
let (input, p) = take(hlen1)(input)?;
let p: BlockJsonInfo = serde_json::from_slice(p).unwrap();
Ok((input, p))
})?;
let (input, _) = take(8usize)(input)?; // skip 8 bytes
let (input, hlen2) = Self::_parse_u32(input, order)?;
let dimension_size = block_info.dimension_size.clone();
let size = dimension_size.iter().fold(1, |acc, x| acc * x) as usize;
let value_type = block_info.value_type.clone();
let value_type = Self::_parse_type(&value_type);
let (input, data) = Self::_parse_matrix(
input,
value_type,
order,
size,
block_info.value_offset,
block_info.value_scale,
block_info.fill_value,
)?;
Ok((
input,
ParsedBlock {
info: block_info,
data,
},
))
}
fn _parse_i32(input: &[u8], order: Order) -> IResult<&[u8], i32> {
let (input, hlen) = take(4usize)(input)?;
let hlen = match order {
Order::BigEndian => BigEndian::read_i32(hlen),
_ => LittleEndian::read_i32(hlen),
};
Ok((input, hlen))
}
fn _parse_u32(input: &[u8], order: Order) -> IResult<&[u8], u32> {
let (input, hlen) = take(4usize)(input)?;
let hlen = match order {
Order::BigEndian => BigEndian::read_u32(hlen),
_ => LittleEndian::read_u32(hlen),
};
Ok((input, hlen))
}
fn _parse_i64(input: &[u8], order: Order) -> IResult<&[u8], i64> {
let (input, hlen) = take(8usize)(input)?;
let hlen = match order {
Order::BigEndian => BigEndian::read_i64(hlen),
_ => LittleEndian::read_i64(hlen),
};
Ok((input, hlen))
}
fn _parse_u64(input: &[u8], order: Order) -> IResult<&[u8], u64> {
let (input, hlen) = take(8usize)(input)?;
let hlen = match order {
Order::BigEndian => BigEndian::read_u64(hlen),
_ => LittleEndian::read_u64(hlen),
};
Ok((input, hlen))
}
fn _parse_type<'a>(type_str: &'a str) -> ValueTypes {
match type_str {
"b" => ValueTypes::I8,
"B" => ValueTypes::U8,
"i" => ValueTypes::I32,
"I" => ValueTypes::U32,
"f" => ValueTypes::F32,
"d" => ValueTypes::F64,
"h" => ValueTypes::I16,
_ => panic!("Invalid type"),
}
}
fn _parse_matrix(
input: &[u8],
type_: ValueTypes,
order: Order,
len: usize,
offset: f32,
scale: f32,
fill_value: f64,
) -> IResult<&[u8], ValueResult> {
match_in_macro!(
type_,
len,
input,
offset,
scale,
fill_value,
(
ValueTypes::I64,
i64,
f64,
ValueResult::I64,
ValueResult::F64
),
(
ValueTypes::F64,
f64,
f64,
ValueResult::F64,
ValueResult::F64
),
(
ValueTypes::U64,
u64,
f64,
ValueResult::U64,
ValueResult::F64
),
(
ValueTypes::I32,
i32,
f32,
ValueResult::I32,
ValueResult::F32
),
(
ValueTypes::F32,
f32,
f32,
ValueResult::F32,
ValueResult::F32
),
(
ValueTypes::U32,
u32,
f32,
ValueResult::U32,
ValueResult::F32
),
(
ValueTypes::I16,
i16,
f32,
ValueResult::I16,
ValueResult::F32
),
(ValueTypes::I8, i8, f32, ValueResult::I8, ValueResult::F32),
(ValueTypes::U8, u8, f32, ValueResult::U8, ValueResult::F32)
)
}
}
#[derive(Serialize, Deserialize, Debug)]
struct RecordInfo {
file_time: String,
block_num: i32,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct BlockJsonInfo {
pub value_name: String,
pub value_des: String,
pub value_type: String,
pub dimension_size: Vec<u64>,
pub dimension: usize,
pub dimension_des: Vec<String>,
pub dimension_start: Vec<f64>,
pub dimension_end: Vec<f64>,
pub dimension_values: Vec<Vec<f64>>,
pub fill_value: f64,
pub value_scale: f32,
pub value_offset: f32,
pub radar_alt: Option<f64>,
pub radar_lat: Option<f64>,
pub radar_lon: Option<f64>,
pub radar_name: Option<String>,
}
pub struct ParsedBlock {
pub info: BlockJsonInfo,
pub data: ValueResult,
}

View File

@ -0,0 +1,808 @@
use crate::error::ETWSError;
use abi_stable::std_types::vec;
use bytemuck::cast_slice;
use nom::{
bytes::complete::take,
combinator::map,
number::complete::{le_f32, le_i16, le_i64, le_u16, le_u32},
sequence::tuple,
IResult,
};
use zip::read::ZipArchive;
use std::{
collections::HashMap,
io::{self, Cursor, Read, Seek, SeekFrom},
process::id,
};
use std::{fs::File, path::Path};
const SUPPORTED_TYPE: [u32; 7] = [2, 3, 4, 7, 9, 10, 11];
#[derive(Debug)]
pub struct RadarData {
pub datetime: i64,
pub els: Vec<f64>,
pub azs: Vec<f64>,
pub ranges: Vec<f64>,
pub datas: HashMap<&'static str, Vec<f64>>,
}
#[derive(Debug)]
struct SiteConfig {
site_code: String,
site_name: String,
latitude: f32,
longitude: f32,
antenna_height: u32,
}
#[derive(Debug)]
struct TaskConfig {
task_name: String,
task_description: String,
polarization_type: u32,
scan_type: u32,
scan_beam_number: u32,
cut_number: u32,
ray_order: u32,
scan_start_time: i64,
}
#[derive(Debug)]
struct BeamConfig {
cut_index: i16,
tx_beam_index: i16,
rx_beam_elevation: f32,
tx_beam_gain: f32,
rx_beam_width_h: f32,
rx_beam_width_v: f32,
rx_beam_gain: f32,
process_mode: String,
wave_form: String,
n1_prf1: f32,
n1_prf2: f32,
n2_prf1: f32,
n2_prf2: f32,
dealiasing_mode: u32,
azimuth: f32,
start_angle: f32,
end_angle: f32,
angular_resolution: f32,
scan_speed: f32,
log_resolution: f32,
doppler_resolution: f32,
maximum_range1: u32,
maximum_range2: u32,
start_range: u32,
sample1: u32,
sample2: u32,
phase_mode: u32,
atmospheric_loss: f32,
nyquist_speed: f32,
moments_mask: i64,
moments_size_mask: i64,
misc_filter_mask: u32,
sqi_threshold: f32,
sig_threshold: f32,
csr_threshold: f32,
log_threshold: f32,
cpa_threshold: f32,
pmi_threshold: f32,
dplog_threshold: f32,
thresholds_r: [u8; 4],
dbt_mask: u32,
dbz_mask: u32,
velocity_mask: u32,
spectrum_width_mask: u32,
dp_mask: u32,
direction: u32,
ground_clutter_classifier_type: i16,
ground_clutter_filter_type: i16,
ground_clutter_filter_notch_width: i16,
ground_clutter_filter_window: i16,
}
#[derive(Debug)]
struct TypeHeader {
data_type: u32,
scale: u32,
offset: u32,
bin_length: u16,
flags: u16,
length: u32,
}
#[derive(Debug)]
struct RadialData {
radial_state: u32,
spot_blank: u32,
sequence_number: u32,
radial_number: u32,
elevation_number: u32,
azimuth: f32,
elevation: f32,
seconds: i64,
microseconds: u32,
length_of_data: u32,
moment_number: u32,
scan_beam_index: i16,
horizontal_estimated_noise: i16,
vertical_estimated_noise: i16,
prf_flag: u32,
}
#[derive(Debug)]
struct GenericHeader {
magic_number: u32,
major_version: u16,
minor_version: u16,
generic_type: u32,
product_type: u32,
reserved: [u8; 16],
}
#[derive(Debug)]
struct SubPulseConfig {
strategy: u32,
modulation: u32,
frequency: f32,
bandwidth: f32,
width: u32,
horizontal_noise: f32,
vertical_noise: f32,
horizontal_calibration: f32,
vertical_calibration: f32,
horizontal_noise_temperature: f32,
vertical_noise_temperature: f32,
zdr_calibration: f32,
phidp_calibration: f32,
ldr_calibration: f32,
pulse_points: i16,
}
#[derive(Debug)]
struct ScanConfig {
beam_index: u32,
beam_type: u32,
sub_pulse_number: u32,
tx_beam_direction: f32,
tx_beam_width_h: f32,
tx_beam_width_v: f32,
tx_beam_gain: f32,
sub_pulse_configs: Vec<SubPulseConfig>,
}
fn parse_scan_config(input: &[u8]) -> IResult<&[u8], ScanConfig> {
let (input, beam_index) = le_u32(input)?;
let (input, beam_type) = le_u32(input)?;
let (input, sub_pulse_number) = le_u32(input)?;
let (input, tx_beam_direction) = le_f32(input)?;
let (input, tx_beam_width_h) = le_f32(input)?;
let (input, tx_beam_width_v) = le_f32(input)?;
let (input, tx_beam_gain) = le_f32(input)?;
let (input, _) = take(100usize)(input)?; // Skip reserved bytes
// Parse the SubPulseConfigs
let mut sub_pulse_configs = Vec::new();
let mut input = input;
for _ in 0..4 {
let (new_input, sub_pulse_config) = parse_sub_pulse_config(input)?;
input = new_input;
sub_pulse_configs.push(sub_pulse_config);
}
let scan_config = ScanConfig {
beam_index,
beam_type,
sub_pulse_number,
tx_beam_direction,
tx_beam_width_h,
tx_beam_width_v,
tx_beam_gain,
sub_pulse_configs,
};
Ok((input, scan_config))
}
fn parse_sub_pulse_config(input: &[u8]) -> IResult<&[u8], SubPulseConfig> {
let (input, strategy) = le_u32(input)?;
let (input, modulation) = le_u32(input)?;
let (input, frequency) = le_f32(input)?;
let (input, bandwidth) = le_f32(input)?;
let (input, width) = le_u32(input)?;
let (input, horizontal_noise) = le_f32(input)?;
let (input, vertical_noise) = le_f32(input)?;
let (input, horizontal_calibration) = le_f32(input)?;
let (input, vertical_calibration) = le_f32(input)?;
let (input, horizontal_noise_temperature) = le_f32(input)?;
let (input, vertical_noise_temperature) = le_f32(input)?;
let (input, zdr_calibration) = le_f32(input)?;
let (input, phidp_calibration) = le_f32(input)?;
let (input, ldr_calibration) = le_f32(input)?;
let (input, pulse_points) = nom::number::complete::le_i16(input)?;
let (input, _) = take(70usize)(input)?; // Skip reserved bytes
let sub_pulse_config = SubPulseConfig {
strategy,
modulation,
frequency,
bandwidth,
width,
horizontal_noise,
vertical_noise,
horizontal_calibration,
vertical_calibration,
horizontal_noise_temperature,
vertical_noise_temperature,
zdr_calibration,
phidp_calibration,
ldr_calibration,
pulse_points,
};
Ok((input, sub_pulse_config))
}
pub fn parse_raw_data<P: AsRef<Path>>(path: P) -> Result<RadarData, ETWSError> {
let path = path.as_ref();
if path.extension().is_none() {
if path.is_file() {
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf).unwrap();
if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" {
return get_radar_data(buf);
} else {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
} else {
return Err(ETWSError::IOError {
source: io::Error::new(io::ErrorKind::NotFound, "File not found"),
});
}
}
if path.extension().unwrap() == "zip" {
let file = File::open(path).unwrap();
let mut archive = ZipArchive::new(file).unwrap();
let mut file = archive.by_index(0).unwrap();
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
if buf.len() < 4 {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" {
return get_radar_data(buf);
} else {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
} else if path.extension().unwrap() == ".gz" {
let file = File::open(path)?;
let mut archive = flate2::read::GzDecoder::new(file);
let mut buf = Vec::new();
archive.read_to_end(&mut buf)?;
if buf.len() < 4 {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" {
return get_radar_data(buf);
} else {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
} else {
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
if buf.len() < 4 {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
if &buf[0..4] == b"SSTM" || &buf[16..20] == b"STDM" || &buf[0..4] == b"RSTM" {
return get_radar_data(buf);
} else {
return Err(ETWSError::FormatError {
source: anyhow::anyhow!("Invalid file format"),
});
}
}
}
fn get_radar_data(data: Vec<u8>) -> Result<RadarData, ETWSError> {
let total_length = data.len() as u64;
// 使用 Cursor 来处理 Vec<u8>
let mut cursor = Cursor::new(data);
// 读取 GENERIC_HEADER
let mut buffer = vec![0u8; 32];
cursor.read_exact(&mut buffer)?;
let (_, generic_header) = parse_generic_header(&buffer)?;
// 读取 SITE_CONFIG
let mut buffer = vec![0u8; 128];
cursor.read_exact(&mut buffer)?;
let (_, site_config) = parse_site_config(&buffer)?;
// 读取 TASK_CONFIG
let mut buffer = vec![0u8; 256];
cursor.read_exact(&mut buffer)?;
let (_, task_config) = parse_task_config(&buffer)?;
// 读取 SCAN_CONFIG
let mut scan_configs = Vec::new();
for _ in 0..task_config.scan_beam_number {
let mut buffer = vec![0u8; 640];
cursor.read_exact(&mut buffer)?;
let (_, scan_config) = parse_scan_config(&buffer)?;
scan_configs.push(scan_config);
}
// 读取 BEAM_CONFIG
let mut beam_configs = Vec::new();
for _ in 0..task_config.cut_number {
let mut buffer = vec![0u8; 256];
cursor.read_exact(&mut buffer)?;
let (_, beam_config) = parse_beam_config(&buffer)?;
beam_configs.push(beam_config);
}
// 读取 RADIAL_DATA
let (datas, els, azs, ranges) =
parse_final_data(total_length, task_config.cut_number as usize, cursor)?;
// 构建并返回 RadarData 结构体
let radar_data = RadarData {
datetime: task_config.scan_start_time,
els,
azs,
ranges,
datas,
};
Ok(radar_data)
}
fn parse_generic_header(input: &[u8]) -> IResult<&[u8], GenericHeader> {
let (input, magic_number) = le_u32(input)?;
let (input, major_version) = le_u16(input)?;
let (input, minor_version) = le_u16(input)?;
let (input, generic_type) = le_u32(input)?;
let (input, product_type) = le_u32(input)?;
let (input, reserved) = take(16usize)(input)?;
let generic_header = GenericHeader {
magic_number,
major_version,
minor_version,
generic_type,
product_type,
reserved: reserved.try_into().unwrap(),
};
Ok((input, generic_header))
}
fn parse_site_config(input: &[u8]) -> IResult<&[u8], SiteConfig> {
let (input, site_code) = take(8usize)(input)?;
let (input, site_name) = take(32usize)(input)?;
let (input, latitude) = le_f32(input)?;
let (input, longitude) = le_f32(input)?;
let (input, antenna_height) = le_u32(input)?;
let (input, _) = take(4usize)(input)?; // Skip reserved bytes
let site_config = SiteConfig {
site_code: String::from_utf8(site_code.to_vec())
.unwrap()
.trim_end_matches('\0')
.to_string(),
site_name: String::from_utf8(site_name.to_vec())
.unwrap()
.trim_end_matches('\0')
.to_string(),
latitude,
longitude,
antenna_height,
};
Ok((input, site_config))
}
fn parse_task_config(input: &[u8]) -> IResult<&[u8], TaskConfig> {
let (input, task_name) = take(32usize)(input)?;
let (input, task_description) = take(128usize)(input)?;
let (input, polarization_type) = le_u32(input)?;
let (input, scan_type) = le_u32(input)?;
let (input, scan_beam_number) = le_u32(input)?;
let (input, cut_number) = le_u32(input)?;
let (input, ray_order) = le_u32(input)?;
let (input, scan_start_time) = le_i64(input)?;
let (input, _) = take(68usize)(input)?; // Skip reserved bytes
let task_config = TaskConfig {
task_name: String::from_utf8_lossy(task_name).to_string(),
task_description: String::from_utf8_lossy(task_description).to_string(),
polarization_type,
scan_type,
scan_beam_number,
cut_number,
ray_order,
scan_start_time,
};
Ok((input, task_config))
}
fn single_beam_block(raw: &[u8], bin_length: usize) -> io::Result<Vec<f64>> {
let raw = match bin_length {
1 => {
let new_raw: Vec<f64> = raw.into_iter().map(|v| *v as f64).collect();
new_raw
}
2 => {
let new_raw: Vec<f64> = cast_slice(&raw)
.into_iter()
.map(|v: &u16| *v as f64)
.collect();
new_raw
}
4 => {
let new_raw: Vec<f64> = cast_slice(&raw)
.into_iter()
.map(|v: &u32| *v as f64)
.collect();
new_raw
}
8 => {
let new_raw: Vec<f64> = cast_slice(&raw)
.into_iter()
.map(|v: &u64| *v as f64)
.collect();
new_raw
}
_ => {
let new_raw: Vec<f64> = raw.into_iter().map(|v| *v as f64).collect();
new_raw
}
};
Ok(raw)
}
fn beam_block_exact(cursor: &mut Cursor<Vec<u8>>, header: &TypeHeader) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; header.length as usize];
cursor.read_exact(&mut buf)?;
Ok(buf)
}
fn data_type(typ: u32) -> &'static str {
match typ {
2 => "DBZ",
3 => "VEL",
4 => "SW",
7 => "ZDR",
9 => "CC",
10 => "PHIDP",
11 => "KDP",
_ => "Unknown",
}
}
fn parse_final_data(
all_length: u64,
cut_number: usize,
mut cursor: Cursor<Vec<u8>>,
) -> Result<
(
HashMap<&'static str, Vec<f64>>,
Vec<f64>,
Vec<f64>,
Vec<f64>,
),
ETWSError,
> {
let position = cursor.position();
let mut radial_buffer = vec![0u8; 128];
let mut header_buffer = vec![0u8; 32];
cursor.read_exact(&mut radial_buffer)?;
// First Block
let (_, radial_data) = parse_radial_data(&radial_buffer)?;
// Radial_number: I don't know why not use it.
// let radial_number = radial_data.radial_number as usize;
// 变量类型
let type_number = radial_data.moment_number as usize;
let mut gate_len = vec![0; type_number];
let mut types = vec!["Unknown"; type_number];
// final datas
let mut datas = HashMap::new();
for idx in 0..type_number {
cursor.read_exact(&mut header_buffer)?;
let (_, header) = parse_type_header(&header_buffer)?;
let first_beam_block = beam_block_exact(&mut cursor, &header)?;
let first_beam = single_beam_block(&first_beam_block, header.bin_length as usize)?;
gate_len[idx] = first_beam.len();
let data_type = data_type(header.data_type);
if data_type == "Unknown" {
continue;
}
types[idx] = data_type;
}
let first_block_position = cursor.position();
let roll_num = (all_length - position) / (first_block_position - position);
let azm_number = (roll_num / cut_number as u64) as usize;
for idx in 0..type_number {
if types[idx] == "Unknown" {
continue;
}
datas.insert(
types[idx],
vec![0f64; cut_number * azm_number * gate_len[idx]],
);
}
// Reset Cursor
cursor.set_position(position);
let mut els = vec![0f64; cut_number];
let mut azs = vec![0f64; azm_number];
for e in 0..cut_number {
for a in 0..azm_number {
cursor.read_exact(&mut radial_buffer)?;
let (_, radial_data) = parse_radial_data(&radial_buffer)?;
els[e] = radial_data.elevation as f64;
azs[a] = radial_data.azimuth as f64;
let type_number = radial_data.moment_number as usize;
for typ in 0..type_number {
cursor.read_exact(&mut header_buffer)?;
let (_, header) = parse_type_header(&header_buffer)?;
let beam_block = beam_block_exact(&mut cursor, &header)?;
let mut beam = single_beam_block(&beam_block, header.bin_length as usize)?;
beam.iter_mut()
.for_each(|v| *v = (*v - header.offset as f64) / header.scale as f64);
if let Some(typ_name) = types.get(typ) {
if typ_name == &"Unknown" {
continue;
}
let e = radial_data.elevation_number as usize - 1;
datas.get_mut(typ_name).unwrap()[e * (azm_number * gate_len[typ])
+ a * gate_len[typ]
..e * (azm_number * gate_len[typ]) + (a + 1) * gate_len[typ]]
.copy_from_slice(&beam);
} else {
continue;
}
}
}
}
let mut ranges = vec![0f64; gate_len[0]];
ranges
.iter_mut()
.enumerate()
.for_each(|(idx, r)| *r = idx as f64 * 30.0);
Ok((datas, els, azs, ranges))
}
fn parse_beam_config(input: &[u8]) -> IResult<&[u8], BeamConfig> {
let (input, cut_index) = le_i16(input)?;
let (input, tx_beam_index) = le_i16(input)?;
let (input, rx_beam_elevation) = le_f32(input)?;
let (input, tx_beam_gain) = le_f32(input)?;
let (input, rx_beam_width_h) = le_f32(input)?;
let (input, rx_beam_width_v) = le_f32(input)?;
let (input, rx_beam_gain) = le_f32(input)?;
let (input, process_mode) = map(le_u32, |v| match v {
1 => "PPP".to_string(),
2 => "FFT".to_string(),
_ => "Unknown".to_string(),
})(input)?;
let (input, wave_form) = map(le_u32, |v| match v {
0 => "CS连续监测".to_string(),
1 => "CD连续多普勒".to_string(),
2 => "CDX多普勒扩展".to_string(),
3 => "Rx Test".to_string(),
4 => "BATCH批模式".to_string(),
5 => "Dual PRF双PRF".to_string(),
6 => "Staggered PRT 参差PRT".to_string(),
_ => "Unknown".to_string(),
})(input)?;
let (input, n1_prf1) = le_f32(input)?;
let (input, n1_prf2) = le_f32(input)?;
let (input, n2_prf1) = le_f32(input)?;
let (input, n2_prf2) = le_f32(input)?;
let (input, dealiasing_mode) = le_u32(input)?;
let (input, azimuth) = le_f32(input)?;
let (input, start_angle) = le_f32(input)?;
let (input, end_angle) = le_f32(input)?;
let (input, angular_resolution) = le_f32(input)?;
let (input, scan_speed) = le_f32(input)?;
let (input, log_resolution) = le_f32(input)?;
let (input, doppler_resolution) = le_f32(input)?;
let (input, maximum_range1) = le_u32(input)?;
let (input, maximum_range2) = le_u32(input)?;
let (input, start_range) = le_u32(input)?;
let (input, sample1) = le_u32(input)?;
let (input, sample2) = le_u32(input)?;
let (input, phase_mode) = le_u32(input)?;
let (input, atmospheric_loss) = le_f32(input)?;
let (input, nyquist_speed) = le_f32(input)?;
let (input, moments_mask) = le_i64(input)?;
let (input, moments_size_mask) = le_i64(input)?;
let (input, misc_filter_mask) = le_u32(input)?;
let (input, sqi_threshold) = le_f32(input)?;
let (input, sig_threshold) = le_f32(input)?;
let (input, csr_threshold) = le_f32(input)?;
let (input, log_threshold) = le_f32(input)?;
let (input, cpa_threshold) = le_f32(input)?;
let (input, pmi_threshold) = le_f32(input)?;
let (input, dplog_threshold) = le_f32(input)?;
let (input, thresholds_r) = take(4usize)(input)?;
let (input, dbt_mask) = le_u32(input)?;
let (input, dbz_mask) = le_u32(input)?;
let (input, velocity_mask) = le_u32(input)?;
let (input, spectrum_width_mask) = le_u32(input)?;
let (input, dp_mask) = le_u32(input)?;
let (input, direction) = le_u32(input)?;
let (input, ground_clutter_classifier_type) = le_i16(input)?;
let (input, ground_clutter_filter_type) = le_i16(input)?;
let (input, ground_clutter_filter_notch_width) = le_i16(input)?;
let (input, ground_clutter_filter_window) = le_i16(input)?;
let (input, _) = take(44usize)(input)?;
let beam_config = BeamConfig {
cut_index,
tx_beam_index,
rx_beam_elevation,
tx_beam_gain,
rx_beam_width_h,
rx_beam_width_v,
rx_beam_gain,
process_mode,
wave_form,
n1_prf1,
n1_prf2,
n2_prf1,
n2_prf2,
dealiasing_mode,
azimuth,
start_angle,
end_angle,
angular_resolution,
scan_speed,
log_resolution,
doppler_resolution,
maximum_range1,
maximum_range2,
start_range,
sample1,
sample2,
phase_mode,
atmospheric_loss,
nyquist_speed,
moments_mask,
moments_size_mask,
misc_filter_mask,
sqi_threshold,
sig_threshold,
csr_threshold,
log_threshold,
cpa_threshold,
pmi_threshold,
dplog_threshold,
thresholds_r: thresholds_r.try_into().unwrap(),
dbt_mask,
dbz_mask,
velocity_mask,
spectrum_width_mask,
dp_mask,
direction,
ground_clutter_classifier_type,
ground_clutter_filter_type,
ground_clutter_filter_notch_width,
ground_clutter_filter_window,
};
Ok((input, beam_config))
}
fn parse_radial_data(input: &[u8]) -> IResult<&[u8], RadialData> {
let (input, radial_state) = le_u32(input)?;
let (input, spot_blank) = le_u32(input)?;
let (input, sequence_number) = le_u32(input)?;
let (input, radial_number) = le_u32(input)?;
let (input, elevation_number) = le_u32(input)?;
let (input, azimuth) = le_f32(input)?;
let (input, elevation) = le_f32(input)?;
let (input, seconds) = le_i64(input)?;
let (input, microseconds) = le_u32(input)?;
let (input, length_of_data) = le_u32(input)?;
let (input, moment_number) = le_u32(input)?;
let (input, scan_beam_index) = le_i16(input)?;
let (input, horizontal_estimated_noise) = le_i16(input)?;
let (input, vertical_estimated_noise) = le_i16(input)?;
let (input, prf_flag) = le_u32(input)?;
let (input, _) = take(70usize)(input)?; // Skip reserved bytes
let radial_data = RadialData {
radial_state,
spot_blank,
sequence_number,
radial_number,
elevation_number,
azimuth,
elevation,
seconds,
microseconds,
length_of_data,
moment_number,
scan_beam_index,
horizontal_estimated_noise,
vertical_estimated_noise,
prf_flag,
};
Ok((input, radial_data))
}
fn parse_type_header(input: &[u8]) -> IResult<&[u8], TypeHeader> {
let (input, data_type) = le_u32(input)?;
let (input, scale) = le_u32(input)?;
let (input, offset) = le_u32(input)?;
let (input, bin_length) = le_u16(input)?;
let (input, flags) = le_u16(input)?;
let (input, length) = le_u32(input)?;
let (input, _) = take(12usize)(input)?; // Skip reserved bytes
let type_header = TypeHeader {
data_type,
scale,
offset,
bin_length,
flags,
length,
};
Ok((input, type_header))
}
mod test {
use crate::raw::parse_raw_data;
use super::get_radar_data;
#[test]
fn test() {
use std::fs::File;
let radar_data = parse_raw_data(
"/Users/tsuki/Desktop/Z_RADR_I_X5775_20230726180000_O_DOR-XPD-CAP-FMT.BIN.zip",
)
.unwrap();
}
}

View File

@ -5,5 +5,10 @@ edition = "2021"
[dependencies]
glam = "0.29.2"
log = "0.4.22"
makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik", version = "0.6.0" }
# makepad-widgets = { path = "/Users/tsuki/projects/makepad/widgets", version = "0.6.0" }
mp_core = { path = "../mp_core", version = "*" }
once_cell = "1.20.2"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"

263
mp/config.toml Normal file
View File

@ -0,0 +1,263 @@
[common]
background = "Terrain"
path = "resources/alts.png"
plugins = "loaders"
[[cmap]]
type = "DBZ"
levels = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
colors = [
"#aaaaaa",
"#0022ff",
"#01a0f6",
"#00ecec",
"#00d800",
"#019000",
"#ffff00",
"#e7c000",
"#ff9000",
"#ff0000",
"#d60000",
"#c00000",
"#ff00f0",
"#9600b4",
"#ad90f0",
]
[[cmap]]
type = "VEL"
levels = [
-90,
-45,
-35,
-27,
-20,
-15,
-10,
-5,
-1,
0,
1,
5,
10,
15,
20,
27,
1000,
]
colors = [
"#9fffff",
"#00e0ff",
"#0080ff",
"#320096",
"#00fb90",
"#00bb90",
"#008f00",
"#cdc09f",
"#000000",
"#f88700",
"#ffcf00",
"#ffff00",
"#ae0000",
"#d07000",
"#dd0000",
"#ff0000",
]
[[cmap]]
type = "SW"
colors = [
"#E0E0E0",
"#7CE0E0",
"#00E0E0",
"#00B0B0",
"#00FEFE",
"#00C400",
"#008000",
"#FEFE00",
"#FED200",
"#FE7C00",
"#FEB0B0",
"#FE5858",
"#FE0000",
"#FEFEFE",
]
levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[[cmap]]
type = "CC"
colors = [
"#003CFF",
"#00EFEF",
"#00BABF",
"#00837D",
"#008938",
"#00B729",
"#00DA0D",
"#00FF00",
"#FFFF3B",
"#FFF000",
"#FFC600",
"#FFA500",
"#FF7200",
"#FF1F00",
"#C10000",
"#D400AA",
]
levels = [
0,
0.1,
0.3,
0.5,
0.6,
0.7,
0.8,
0.85,
0.9,
0.92,
0.94,
0.95,
0.96,
0.97,
0.98,
0.99,
]
[[cmap]]
type = "KDP"
colors = [
"#00FFFF",
"#00EFEF",
"#00A8AC",
"#B4B4B4",
"#B4B4B4",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-0.8,
-0.4,
-0.2,
-0.1,
0.1,
0.15,
0.22,
0.33,
0.5,
0.75,
1.1,
1.7,
2.4,
3.1,
7,
20,
]
[[cmap]]
type = "ZDR"
colors = [
"#464646",
"#505050",
"#5A5A5A",
"#646464",
"#6E6E6E",
"#787878",
"#828282",
"#8C8C8C",
"#969696",
"#AFAFAF",
"#C8C8C8",
"#DCF0DC",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFF20F",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#FFFF00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-5,
-4.5,
-4,
-3.5,
-3,
-2.5,
-2,
-1.5,
-1,
-0.5,
0,
0.5,
1,
1.5,
2,
2.5,
3,
3.5,
4,
4.5,
5,
5.5,
6,
6.5,
7,
]
[[cmap]]
type = "VIL"
levels = [
0.1,
1,
2.5,
5,
7.5,
10,
10.25,
15,
20,
25,
30,
35,
40,
45,
50,
55,
104,
]
colors = [
'#484892',
'#01a0f6',
'#00ecec',
'#01ff00',
'#00c800',
'#019000',
'#ffff00',
'#e7c000',
'#ff9000',
'#ff0000',
'#d60000',
'#c00000',
'#ff00f0',
'#ad90f0',
'#780084',
'#d8af97',
]

View File

@ -12,6 +12,8 @@ live_design! {
flow: Down
spacing: 10.
padding: 15.
height: Fit
width: Fill
draw_bg: {
instance border_width: 1.0
instance border_color: (THEME_COLOR_U_2)
@ -88,6 +90,8 @@ live_design! {
flow: Down,
spacing: 15.,
scroll_bars: <ScrollBars> {show_scroll_x: false, show_scroll_y: true}
<H4>{ text: "Controller"}
<Group> {

View File

@ -5,3 +5,5 @@ pub use makepad_widgets::makepad_platform;
pub mod app_ui;
pub mod shaders;
pub mod widgets;
pub mod render;

View File

@ -1,3 +1,48 @@
use log::*;
use mp_core::{config::Setting, datapool::DataPool, plugin_system::PluginManager};
use once_cell::sync::Lazy;
use std::env;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
static CONFIG: Lazy<Setting> = Lazy::new(|| Setting::new());
static PLUGIN_MANAGER: Lazy<PluginManager> = Lazy::new(|| {
PluginManager::new(
env::current_dir().unwrap().join(
(CONFIG.common.plugins)
.as_ref()
.unwrap_or(&"loaders".into()),
),
)
.unwrap()
});
static DATAPOOL: Lazy<DataPool> = Lazy::new(|| DataPool::new(&PLUGIN_MANAGER, 10));
// static DATA_POOL: Lazy<DataPool> = Lazy::new(|| Data);
fn main() {
// Init logger
// Logger Filter
let filter = tracing_subscriber::filter::targets::Targets::new()
.with_targets(vec![
("radarg_plugin_interface", tracing::Level::INFO),
("mp_core", tracing::Level::INFO),
])
.with_target("mp", tracing::level_filters::LevelFilter::DEBUG);
// Logger Registry
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.with(filter)
.init();
// Default Plugin Dir
let plugin_dir = env::current_dir().unwrap().join("loaders");
info!("Plugin Dir: {:?}", plugin_dir);
// Init data pool
info!("Init DataPool.... DataPool Length: {}", DATAPOOL.len());
// Init Setting
info!("Init Settings");
info!("Settings: {:?}", CONFIG.common);
mp::app::app_main();
}

1
mp/src/render/camera.rs Normal file
View File

@ -0,0 +1 @@
pub struct Camera {}

View File

@ -0,0 +1,33 @@
use makepad_widgets::{Cx, Texture, TextureFormat};
use mp_core::data::RadarGridData;
pub struct GridData {
texture: Texture,
clear_buffer: bool,
shape: Option<Vec<u32>>,
}
impl GridData {
pub fn new(cx: &mut Cx) -> Self {
let texture = Texture::new_with_format(cx, TextureFormat::Unknown);
GridData {
texture,
clear_buffer: true,
shape: None,
}
}
pub fn update(&mut self, cx: &mut Cx, data: &RadarGridData) {
let data = data.data.cast_to::<f32>();
self.clear_buffer = false;
// self.shape = Some(data.dim().into());
// self.texture.put_back_vec_f32(cx, data.into(), None);
}
pub fn clear(&mut self) {
self.clear_buffer = true;
}
pub fn texture(&self) -> &Texture {
&self.texture
}
}

2
mp/src/render/mod.rs Normal file
View File

@ -0,0 +1,2 @@
pub mod camera;
pub mod grid_data;

View File

@ -1,11 +1,27 @@
use makepad_platform::*;
use crate::render::grid_data::GridData;
use makepad_widgets::*;
live_design! {
PPI = {{PPI}} {
uniform three_d: int
//
uniform conf: vec4
varing pos: vec4
// Data
texture data: texture3d
// ColorMAPPER
textrue color_map: texture1d
fn vertex(self) -> vec4 {
let pos = geometry * vec2(1., 1.);
return vec4(pos, 0., 1.);
if (three_d == 1) {
return vec4(self.position,)
} else {
return vec4(self.position, 1.0);
}
}
fn pixel(self) -> vec4 {
@ -22,6 +38,10 @@ pub struct PPI {
draw_vars: DrawVars,
#[live]
geometry: GeometryQuad2D,
#[calc]
pub position: Vec3,
#[calc]
pub value: f32,
}
impl LiveHook for PPI {
@ -37,5 +57,7 @@ impl LiveHook for PPI {
}
impl PPI {
pub fn update_draw_call_vars(&mut self) {}
pub fn update_draw_call_vars(&mut self, data: &GridData) {
self.draw_vars.texture_slots[0] = Some(data.texture().clone());
}
}

23
mp_core/Cargo.toml Normal file
View File

@ -0,0 +1,23 @@
[package]
name = "mp_core"
version = "0.1.0"
edition = "2021"
[dependencies]
abi_stable = "0.11.3"
chrono = "0.4.38"
core_extensions = "1.5.3"
dirs = "5.0.1"
futures = "0.3.31"
ndarray = { version = "0.16.1", features = ["rayon"] }
num-traits = "0.2.19"
once_cell = "1.20.2"
quick_cache = "0.6.9"
rust-embed = "8.5.0"
serde = { version = "1.0.214", features = ["derive"] }
thiserror = "1.0.65"
tokio = { version = "1.41.0", features = ["full"] }
toml = "0.8.19"
[dependencies.radarg_plugin_interface]
path = "../radarg_plugin_interface"

142
mp_core/src/config.rs Normal file
View File

@ -0,0 +1,142 @@
use crate::data::ProbeDataType;
use crate::errors::ConfigError;
use crate::utils::color_tools::hex_to_rgba_u8;
use crate::Asset;
use serde::{Deserialize, Serialize};
use std::{env, path::PathBuf};
use toml;
// 定义一个宏 find_cmap用于在给定的集合中查找特定类型的元素
macro_rules! find_cmap {
// 宏的参数:$c 是要匹配的值,$find_on 是要查找的集合,$b 和 $name 是匹配模式和类型名称
($c:ident,$find_on:ident,$({$b:tt => $name:literal}),*) => {
{
// 初始化 cmap 为 None
let mut cmap = None;
// 根据 $c 的值进行匹配
match $c {
$(
// 如果 $c 匹配到 $b则在 $find_on 集合中查找类型名称为 $name 的元素
$b => {
let find_v = $find_on.iter().find(|cb| cb.type_name == $name).map(|cb| cb);
// 将找到的元素赋值给 cmap
cmap = find_v;
}
)*
// 如果没有匹配到任何模式,则保持 cmap 为 None
_ => {}
}
// 返回 cmap
cmap
}
};
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Setting {
pub common: Common,
pub cmap: Vec<CB>,
}
impl Setting {
pub fn new() -> Self {
use std::fs::read_to_string;
use std::io::*;
let current_dir = env::current_dir().unwrap();
if !current_dir.join("config.toml").exists() {
let default_config = Asset::get("config.toml").unwrap();
let folder_path = current_dir.clone();
let conf = folder_path.join("config.toml");
let mut file = std::fs::File::create_new(&conf).unwrap();
file.write_all(&default_config.data).unwrap();
}
let file = read_to_string(current_dir.join("config.toml")).unwrap();
let setting: Setting = toml::from_str(&file).unwrap();
setting
}
pub fn find(&self, name: &ProbeDataType) -> Option<&CB> {
let cmap = &self.cmap;
use ProbeDataType::*;
find_cmap!(
name, cmap,
{DBZ => "DBZ"},
{V => "VEL"},
{VIL => "VIL"},
{CC => "CC"},
{KDP => "KDP"},
{ZDR => "ZDR"},
{PHIDP => "PHIDP"}
)
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct CB {
#[serde(rename = "type")]
pub type_name: String,
pub colors: Vec<String>,
pub levels: Vec<f32>,
}
impl CB {
pub fn value_range(&self) -> [f32; 2] {
let mut range = [0.0, 0.0];
let levels = &self.levels;
range[0] = levels[0];
range[1] = levels[levels.len() - 1];
range
}
pub fn color(&self) -> Result<Vec<[u8; 4]>, ConfigError> {
if self.colors.len() != self.levels.len() - 1 {
return Err(ConfigError::FormatError(
"The number of colors and levels are not matched".to_string(),
));
}
let mut result = self
.colors
.iter()
.map(|v| hex_to_rgba_u8(v))
.collect::<std::result::Result<Vec<_>, String>>()
.map_err(|v| ConfigError::FormatError(v.to_string()))?;
let mut span = Vec::with_capacity(self.levels.len() - 1);
for idx in 0..self.levels.len() - 1 {
let start = self.levels[idx];
let end = self.levels[idx + 1];
let range = end - start;
span.push(range);
}
let range = self.value_range();
let all_range = range[1] - range[0];
for (level, r) in span.iter().zip(result.iter_mut()) {
r[3] = ((*level / all_range) * 255.0) as u8;
}
Ok(result)
}
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Common {
pub background: BackgroundType,
pub path: PathBuf,
pub plugins: Option<PathBuf>,
}
#[derive(Deserialize, Serialize, Debug)]
pub enum BackgroundType {
Terrain,
Earth,
}

401
mp_core/src/data/mod.rs Normal file
View File

@ -0,0 +1,401 @@
use chrono::Utc;
use ndarray::ArrayD;
use num_traits::FromPrimitive;
use radarg_plugin_interface::{
LoadedData, ProbeDataType as RPT, RadarGridData as RawRadarGridData, VecResult::*,
};
use std::{
fmt::Display,
hash::Hash,
sync::{atomic::AtomicUsize, Arc},
};
// Data's unique identifier
static DATA_ID: AtomicUsize = AtomicUsize::new(0);
// Data Struct
#[derive(Clone)]
pub struct Data {
pub id: usize,
pub description: String,
_data: _Data,
}
impl std::fmt::Debug for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Data {{ id: {}, description: {}",
self.id, self.description
)
}
}
impl PartialEq for Data {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Hash for Data {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Eq for Data {}
impl Display for Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self._data)
}
}
#[derive(Clone)]
pub(crate) enum _Data {
RadarGridData(Arc<RadarGridData>),
JsonData,
PlainText(String),
Binary(Vec<u8>),
}
impl Display for _Data {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
_Data::RadarGridData(info) => write!(f, "RadarGridData: {}", info),
_Data::JsonData => write!(f, "JsonData"),
_Data::PlainText(_) => write!(f, "PlainText"),
_Data::Binary(_) => write!(f, "Binary"),
}
}
}
#[derive(Clone)]
pub struct RadarGridData {
pub data: ArrayData,
pub info: GridDataInfo,
}
impl RadarGridData {
pub fn get_data(&self) -> &ArrayData {
&self.data
}
pub fn coord_type<'a>(&'a self) -> Option<CoordType<'a>> {
let names = &self.info.dimension_names;
let dimension_keys = names.iter().map(|v| v.as_str()).collect::<Vec<_>>();
if dimension_keys.contains(&"lon") {
let shape_len = self.data.shape().len();
if shape_len == 2 {
Some(CoordType::Cartesian {
hgt: None,
lat: &self.info.dimensions[0],
lon: &self.info.dimensions[1],
})
} else if shape_len == 3 {
Some(CoordType::Cartesian {
hgt: Some(&self.info.dimensions[0]),
lat: &self.info.dimensions[1],
lon: &self.info.dimensions[2],
})
} else {
None
}
} else if dimension_keys.contains(&"el") {
let shape_len = self.data.shape().len();
if shape_len == 2 {
Some(CoordType::Polar {
elevation: None,
azimuth: &self.info.dimensions[0],
range: &self.info.dimensions[1],
})
} else if shape_len == 3 {
Some(CoordType::Polar {
elevation: Some(&self.info.dimensions[0]),
azimuth: &self.info.dimensions[1],
range: &self.info.dimensions[2],
})
} else {
None
}
} else {
None
}
}
pub fn invalid_value(&self) -> f64 {
self.info.fill_value
}
pub fn datetime(&self) -> Option<chrono::DateTime<Utc>> {
self.info.datetime
}
pub fn data_type(&self) -> ProbeDataType {
self.info
.maybe_probe_data_type
.unwrap_or(ProbeDataType::Unknown)
}
}
pub enum CoordType<'a> {
Cartesian {
hgt: Option<&'a Vec<f64>>,
lat: &'a Vec<f64>,
lon: &'a Vec<f64>,
},
Polar {
range: &'a Vec<f64>,
azimuth: &'a Vec<f64>,
elevation: Option<&'a Vec<f64>>,
},
Other,
}
impl Display for CoordType<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CoordType::Cartesian { .. } => write!(f, "Cartesian"),
CoordType::Polar { .. } => write!(f, "Polar"),
CoordType::Other => write!(f, "Other"),
}
}
}
impl Display for RadarGridData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "shape: {:?}, dimension_names: {:?}, fill_value: {}, datetime: {:?}, value_description: {:?}, value_name: {}, maybe_probe_data_type: {:?}, dimension_description: {}",
self.info.shape, self.info.dimension_names, self.info.fill_value, self.info.datetime, self.info.value_description, self.info.value_name, self.info.maybe_probe_data_type, self.info.dimension_description)
}
}
#[derive(Debug, Clone, Default)]
pub struct GridDataInfo {
pub shape: Vec<usize>,
pub dimensions: Vec<Vec<f64>>,
pub dimension_names: Vec<String>,
pub fill_value: f64,
pub datetime: Option<chrono::DateTime<Utc>>,
pub value_description: Option<String>,
pub value_name: String,
pub maybe_probe_data_type: Option<ProbeDataType>,
pub dimension_description: String,
}
#[derive(Debug, Clone, Copy)]
pub enum ProbeDataType {
// Single
R,
V,
SW,
CC,
ZDR,
PHIDP,
KDP,
HCA,
DBZ,
QPE,
QPF,
VIL,
OHP,
THP,
ET,
EB,
// Unknown
Unknown,
}
macro_rules! raw2new {
($({$raw_bc:tt | $typ:ty | $from:ident}),+, ($({$raw_rdt:tt}),+)) => {
#[derive(Debug, Clone)]
pub enum ArrayData {
$(
$raw_bc(ArrayD<$typ>),
)+
}
impl ArrayData {
pub fn shape(&self) -> &[usize] {
match self {
$(
ArrayData::$raw_bc(v) => v.shape(),
)+
}
}
pub fn cast_to<T: FromPrimitive>(&self) -> ArrayD<T> {
match self {
$(
ArrayData::$raw_bc(v) => v.map(|v| T::$from(*v).unwrap()),
)+
}
}
pub fn scale_offset(&self, scale: f64, offset: f64) -> ArrayD<f32>{
match self {
$(
ArrayData::$raw_bc(v) => v.mapv(|v| v as f32 * scale as f32 + offset as f32),
)+
}
}
pub fn scale_offset_to_pixel(&self, fill_value:f32, min:f32, max:f32) -> ArrayD<u8>{
match self {
$(
ArrayData::$raw_bc(v) => v.mapv(|v| {
if (v as f32 - fill_value).abs() < 1e-6 {
0
} else {
let v = (v as f32 - min) / (max - min) * 254.0;
v as u8 + 1
}
}),
)+
}
}
$(
pub fn $raw_bc(&self) -> &ArrayD<$typ> {
match self {
ArrayData::$raw_bc(v) => v,
_ => panic!("Unsupported data type"),
}
}
)+
}
impl From<RawRadarGridData> for RadarGridData {
fn from(raw: RawRadarGridData) -> Self {
let RawRadarGridData { data, info } = raw;
let shape = info.shape.into_vec();
// println!("{:?}", shape);
let data = match data {
$(
$raw_bc(v) => {
ArrayData::$raw_bc(ArrayD::from_shape_vec(shape.clone(), v.to_vec()).unwrap())
}
)+
_ => panic!("Unsupported data type"),
};
let dimensions = info.dimensions.into_iter().map(|v| v.into()).collect();
let dimension_names = info.dimension_names.into_iter().map(|v| v.into()).collect();
let value_description = info.value_description.into_option().map(|v| v.into());
let maybe_probe_data_type = info.maybe_probe_data_type.map(|v| match v {
$(
RPT::$raw_rdt => ProbeDataType::$raw_rdt,
)+
RPT::Unknown => ProbeDataType::Unknown,
}).into_option();
let datetime = info.datetime.into_option().map(|v| chrono::DateTime::from_timestamp(v,0).unwrap());
let info = GridDataInfo {
shape,
dimensions: dimensions,
dimension_names,
fill_value: info.fill_value,
datetime,
value_description,
value_name: info.value_name.into_string(),
maybe_probe_data_type,
dimension_description: info.dimension_description.into_string(),
};
Self { data, info }
}
}
};
}
raw2new!(
{ F32 | f32 | from_f32 },
{ F64 | f64 | from_f64 },
{ I32 | i32 | from_i32 },
{ I16 | i16 | from_i16 },
{ U64 | u64 | from_u64 },
{ U32 | u32 | from_u32 },
{ I8 | i8 | from_i8 },
{ U8 | u8 | from_u8 },
{ I64 | i64 | from_i64 },
(
{ R },
{ V },
{ SW },
{ CC },
{ ZDR },
{ PHIDP },
{ KDP },
{ HCA },
{ DBZ },
{ QPE },
{ QPF },
{ VIL },
{ OHP },
{ THP },
{ ET },
{ EB }
)
);
impl From<LoadedData> for _Data {
fn from(value: LoadedData) -> Self {
match value {
LoadedData::Binary(v) => _Data::Binary(v.into_vec()),
LoadedData::JsonData => _Data::JsonData,
LoadedData::PlainText(v) => _Data::PlainText(v.into_string()),
LoadedData::RadarGridData(v) => _Data::RadarGridData(Arc::new(v.into())),
}
}
}
impl From<LoadedData> for Data {
fn from(value: LoadedData) -> Self {
let description = match &value {
LoadedData::Binary(_) => "Binary".into(),
LoadedData::JsonData => "JsonData".into(),
LoadedData::PlainText(_) => "PlainText".into(),
LoadedData::RadarGridData(v) => format!("{}", v.info.value_name),
};
Data {
id: DATA_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
description: description,
_data: value.into(),
}
}
}
impl<'a> TryFrom<&'a Data> for &'a () {
type Error = crate::errors::DataError;
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
Ok(&())
}
}
impl<'a> TryFrom<&'a Data> for &'a Arc<RadarGridData> {
type Error = crate::errors::DataError;
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
match &value._data {
_Data::RadarGridData(v) => Ok(&v),
_ => Err(crate::errors::DataError::FormatError),
}
}
}
impl<'a> TryFrom<&'a Data> for &'a RadarGridData {
type Error = crate::errors::DataError;
fn try_from(value: &'a Data) -> Result<Self, Self::Error> {
match &value._data {
_Data::RadarGridData(v) => Ok(&*v),
_ => Err(crate::errors::DataError::FormatError),
}
}
}

109
mp_core/src/datapool/mod.rs Normal file
View File

@ -0,0 +1,109 @@
use super::data::Data;
use crate::errors::DataError;
use crate::plugin_system::PluginManager;
use once_cell::sync::Lazy;
use quick_cache::sync::Cache;
use std::collections::HashMap;
use std::fmt::Display;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
static RUNTIME: Lazy<tokio::runtime::Runtime> =
Lazy::new(|| tokio::runtime::Runtime::new().unwrap());
pub type DataValue = Value<Data>;
#[derive(Clone, Debug)]
pub struct Value<T: Display>(Arc<HashMap<usize, Arc<T>>>);
impl Value<Data> {
pub fn new(data: Vec<Data>) -> Self {
let mut hashmap = HashMap::new();
for data in data {
hashmap.insert(data.id, Arc::new(data));
}
Self(Arc::new(hashmap))
}
pub fn get(&self, key: usize) -> Option<&Arc<Data>> {
self.0.get(&key)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, usize, Arc<Data>> {
self.0.iter()
}
}
impl<T: Display> Value<T> {
pub fn len(&self) -> usize {
self.0.len()
}
}
impl<T: Display> Deref for Value<T> {
type Target = HashMap<usize, Arc<T>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct DataPool {
plugin_manager: &'static PluginManager,
pool: Cache<PathBuf, Value<Data>>,
}
impl DataPool {
pub fn new(plugin_manager: &'static PluginManager, cap: usize) -> Self {
Self {
plugin_manager,
pool: Cache::new(cap),
}
}
pub fn get_or_load(&self, path: impl Into<PathBuf>) -> Result<Value<Data>, DataError> {
let path = path.into();
self.pool.get_or_insert_with(&path, || {
self.plugin_manager.try_load_data(&path).map(Value::new)
})
}
pub fn len(&self) -> usize {
self.pool.len()
}
pub async fn get_or_load_async(
&self,
path: impl Into<PathBuf>,
) -> Result<Value<Data>, DataError> {
let path = path.into();
let plugin_manager = self.plugin_manager;
self.pool
.get_or_insert_async(&path.clone(), async move {
RUNTIME
.spawn_blocking(move || plugin_manager.try_load_data(&path))
.await
.unwrap()
.map(Value::new)
})
.await
}
pub async fn get_or_insert_batch_async<A: Into<PathBuf>, P: IntoIterator<Item = A>>(
&self,
paths: P,
) -> Vec<Result<Value<Data>, DataError>> {
use futures::future::join_all;
let tasks = paths
.into_iter()
.map(|path| self.get_or_load_async(path.into()));
let results = join_all(tasks).await;
results
}
}

42
mp_core/src/errors.rs Normal file
View File

@ -0,0 +1,42 @@
use radarg_plugin_interface::Error as PluginError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataError {
#[error("value")]
FormatError,
#[error("")]
IOError(#[from] std::io::Error),
#[error("")]
Plugin(#[from] PluginError),
}
#[derive(Debug, Error)]
pub enum PipelineError {
#[error("data error")]
DataError(String),
}
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("")]
IOError(#[from] std::io::Error),
#[error("")]
TomlError(#[from] toml::de::Error),
#[error("")]
VarError(#[from] std::env::VarError),
#[error("Can't find and create default Config")]
DefaultConfigError,
#[error("Format Error, {0}")]
FormatError(String),
}
#[derive(Debug, Error)]
pub enum PoolError {
#[error("")]
TimestampError,
#[error("Data Pool is full")]
PoolFull,
#[error("Data Pool is not initialized")]
PoolInitialized(&'static str),
}

16
mp_core/src/lib.rs Normal file
View File

@ -0,0 +1,16 @@
pub mod config;
pub mod data;
pub mod datapool;
pub mod errors;
pub mod plugin_system;
mod utils;
use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "statics/"]
pub struct Asset;
pub use config::Setting;
pub use data::{Data, RadarGridData};
pub use datapool::{DataPool, DataValue};
pub use plugin_system::PluginManager;

View File

@ -0,0 +1,111 @@
use super::data::Data;
use crate::errors::DataError;
use abi_stable::{
library::{lib_header_from_path, LibrarySuffix, RawLibrary},
std_types::{RBox, ROk, RStr},
};
use core_extensions::*;
use radarg_plugin_interface::{DataLoaderPlugin_TO, PluginId, PluginMod_Ref};
use std::{
collections::HashMap,
io,
path::{Path, PathBuf},
};
fn compute_plugin_path(base_dir: &Path, base_name: &str) -> io::Result<PathBuf> {
let debug_dir = base_dir.join("target/debug");
let release_dir = base_dir.join("target/release");
// let debug_dir = "./loaders/target/debug"
// .as_ref_::<Path>()
// .into_::<PathBuf>();
// let release_dir = "./loaders/target/release"
// .as_ref_::<Path>()
// .into_::<PathBuf>();
let debug_path = RawLibrary::path_in_directory(&debug_dir, base_name, LibrarySuffix::NoSuffix);
let release_path =
RawLibrary::path_in_directory(&release_dir, base_name, LibrarySuffix::NoSuffix);
match (debug_path.exists(), release_path.exists()) {
(false, false) => debug_path,
(true, false) => debug_path,
(false, true) => release_path,
(true, true) => {
if debug_path.metadata()?.modified()? < release_path.metadata()?.modified()? {
release_path
} else {
debug_path
}
}
}
.piped(Ok)
}
pub fn init_plugin(
base_dir: &Path,
base_name: impl AsRef<str>,
) -> Result<(PluginId, DataLoaderPlugin_TO<'static, RBox<()>>), DataError> {
let library_path: PathBuf = compute_plugin_path(base_dir, base_name.as_ref())?;
let res = (|| {
let header = lib_header_from_path(&library_path)?;
header.init_root_module::<PluginMod_Ref>()
})()
.unwrap();
let plugin_constructor = res.new();
let new_id = PluginId {
named: base_name.as_ref().to_owned().into(),
instance: 0,
};
let plugin = plugin_constructor(new_id.clone()).unwrap();
Ok((new_id, plugin))
}
pub struct PluginManager {
registered_plugins: HashMap<PluginId, DataLoaderPlugin_TO<'static, RBox<()>>>,
}
impl PluginManager {
pub fn new<P: AsRef<Path>>(plugin_dir: P) -> Result<Self, DataError> {
let mut this = Self {
registered_plugins: HashMap::new(),
};
let (id, plugin) = init_plugin(plugin_dir.as_ref(), "etws_loader")?;
this.registered_plugins.insert(id, plugin);
Ok(this)
}
pub fn get_plugin(&self, id: &PluginId) -> Option<&DataLoaderPlugin_TO<'static, RBox<()>>> {
self.registered_plugins.get(id)
}
pub fn get_plugin_by_name(
&self,
name: &str,
) -> Option<&DataLoaderPlugin_TO<'static, RBox<()>>> {
self.registered_plugins
.iter()
.find(|(id, _)| id.named == name)
.map(|(_, p)| p)
}
pub fn try_load_data<P: AsRef<Path>>(&self, path: P) -> Result<Vec<Data>, DataError> {
let path: &Path = path.as_ref();
let path_rstr = RStr::from(path.to_str().unwrap());
for plugin in self.registered_plugins.values() {
if let ROk(result) = plugin.load(path_rstr) {
return Ok(result.into_iter().map(|v| v.into()).collect());
}
}
Err(DataError::FormatError)
}
}

View File

@ -0,0 +1,18 @@
pub fn hex_to_rgba_u8(hex: &str) -> Result<[u8; 4], String> {
let hex = hex.trim_start_matches('#');
if hex.len() != 6 && hex.len() != 8 {
return Err("Hex color should be in #RRGGBB or #RRGGBBAA format".to_string());
}
let r = u8::from_str_radix(&hex[0..2], 16).map_err(|e| e.to_string())?;
let g = u8::from_str_radix(&hex[2..4], 16).map_err(|e| e.to_string())?;
let b = u8::from_str_radix(&hex[4..6], 16).map_err(|e| e.to_string())?;
let a = if hex.len() == 8 {
u8::from_str_radix(&hex[6..8], 16).map_err(|e| e.to_string())?
} else {
255 // 默认不透明
};
Ok([r, g, b, a])
}

1
mp_core/src/utils/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod color_tools;

263
mp_core/statics/config.toml Normal file
View File

@ -0,0 +1,263 @@
[common]
background = "Terrain"
path = "resources/alts.png"
plugins = "loaders"
[[cmap]]
type = "DBZ"
levels = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
colors = [
"#aaaaaa",
"#0022ff",
"#01a0f6",
"#00ecec",
"#00d800",
"#019000",
"#ffff00",
"#e7c000",
"#ff9000",
"#ff0000",
"#d60000",
"#c00000",
"#ff00f0",
"#9600b4",
"#ad90f0",
]
[[cmap]]
type = "VEL"
levels = [
-90,
-45,
-35,
-27,
-20,
-15,
-10,
-5,
-1,
0,
1,
5,
10,
15,
20,
27,
1000,
]
colors = [
"#9fffff",
"#00e0ff",
"#0080ff",
"#320096",
"#00fb90",
"#00bb90",
"#008f00",
"#cdc09f",
"#000000",
"#f88700",
"#ffcf00",
"#ffff00",
"#ae0000",
"#d07000",
"#dd0000",
"#ff0000",
]
[[cmap]]
type = "SW"
colors = [
"#E0E0E0",
"#7CE0E0",
"#00E0E0",
"#00B0B0",
"#00FEFE",
"#00C400",
"#008000",
"#FEFE00",
"#FED200",
"#FE7C00",
"#FEB0B0",
"#FE5858",
"#FE0000",
"#FEFEFE",
]
levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[[cmap]]
type = "CC"
colors = [
"#003CFF",
"#00EFEF",
"#00BABF",
"#00837D",
"#008938",
"#00B729",
"#00DA0D",
"#00FF00",
"#FFFF3B",
"#FFF000",
"#FFC600",
"#FFA500",
"#FF7200",
"#FF1F00",
"#C10000",
"#D400AA",
]
levels = [
0,
0.1,
0.3,
0.5,
0.6,
0.7,
0.8,
0.85,
0.9,
0.92,
0.94,
0.95,
0.96,
0.97,
0.98,
0.99,
]
[[cmap]]
type = "KDP"
colors = [
"#00FFFF",
"#00EFEF",
"#00A8AC",
"#B4B4B4",
"#B4B4B4",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-0.8,
-0.4,
-0.2,
-0.1,
0.1,
0.15,
0.22,
0.33,
0.5,
0.75,
1.1,
1.7,
2.4,
3.1,
7,
20,
]
[[cmap]]
type = "ZDR"
colors = [
"#464646",
"#505050",
"#5A5A5A",
"#646464",
"#6E6E6E",
"#787878",
"#828282",
"#8C8C8C",
"#969696",
"#AFAFAF",
"#C8C8C8",
"#DCF0DC",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFF20F",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#FFFF00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-5,
-4.5,
-4,
-3.5,
-3,
-2.5,
-2,
-1.5,
-1,
-0.5,
0,
0.5,
1,
1.5,
2,
2.5,
3,
3.5,
4,
4.5,
5,
5.5,
6,
6.5,
7,
]
[[cmap]]
type = "VIL"
levels = [
0.1,
1,
2.5,
5,
7.5,
10,
10.25,
15,
20,
25,
30,
35,
40,
45,
50,
55,
104,
]
colors = [
'#484892',
'#01a0f6',
'#00ecec',
'#01ff00',
'#00c800',
'#019000',
'#ffff00',
'#e7c000',
'#ff9000',
'#ff0000',
'#d60000',
'#c00000',
'#ff00f0',
'#ad90f0',
'#780084',
'#d8af97',
]

BIN
radarg_plugin_interface/.DS_Store vendored Normal file

Binary file not shown.

422
radarg_plugin_interface/Cargo.lock generated Normal file
View File

@ -0,0 +1,422 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "abi_stable"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d6512d3eb05ffe5004c59c206de7f99c34951504056ce23fc953842f12c445"
dependencies = [
"abi_stable_derive",
"abi_stable_shared",
"const_panic",
"core_extensions",
"crossbeam-channel",
"generational-arena",
"libloading",
"lock_api",
"parking_lot",
"paste",
"repr_offset",
"rustc_version",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "abi_stable_derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7178468b407a4ee10e881bc7a328a65e739f0863615cca4429d43916b05e898"
dependencies = [
"abi_stable_shared",
"as_derive_utils",
"core_extensions",
"proc-macro2",
"quote",
"rustc_version",
"syn 1.0.109",
"typed-arena",
]
[[package]]
name = "abi_stable_shared"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63"
dependencies = [
"core_extensions",
]
[[package]]
name = "as_derive_utils"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4"
dependencies = [
"core_extensions",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "const_panic"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
[[package]]
name = "core_extensions"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee"
dependencies = [
"core_extensions_proc_macros",
]
[[package]]
name = "core_extensions_proc_macros"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6"
[[package]]
name = "crossbeam-channel"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "generational-arena"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e94aff08e743b651baaea359664321055749b398adff8740a7399af7796e7"
dependencies = [
"cfg-if",
]
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "libc"
version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "proc-macro2"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "radarg_plugin_interface"
version = "0.1.0"
dependencies = [
"abi_stable",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags",
]
[[package]]
name = "repr_offset"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb1070755bd29dffc19d0971cab794e607839ba2ef4b69a9e6fbc8733c1b72ea"
dependencies = [
"tstr",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "semver"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "serde"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d1bd37ce2324cf3bf85e5a25f96eb4baf0d5aa6eba43e7ae8958870c4ec48ed"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tstr"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e"
dependencies = [
"tstr_proc_macros",
]
[[package]]
name = "tstr_proc_macros"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a"
[[package]]
name = "typed-arena"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View File

@ -0,0 +1,11 @@
[package]
name = "radarg_plugin_interface"
version = "0.1.0"
authors = ["tsuki <tsuki@keitsuki.top>"]
edition = "2021"
[dependencies]
abi_stable = "0.11.3"
[features]
impls = []

BIN
radarg_plugin_interface/src/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,48 @@
use abi_stable::{
std_types::{RBoxError, RVec},
StableAbi,
};
use std::{
error::Error as ErrorTrait,
fmt::{self, Display},
};
#[repr(u8)]
#[derive(Debug, StableAbi)]
pub enum Error {
UnsupportedFormat,
FileError(RBoxError),
Custom(RBoxError),
/// A list of errors.
Many(RVec<Error>),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::UnsupportedFormat => {
writeln!(
f,
"Plugin ooes not support this command:\n\
\t\n\
Because of this error:\n\n\
Supported commands:\
"
)?;
Ok(())
}
Error::Custom(e) => Display::fmt(e, f),
Error::Many(list) => {
for e in list {
writeln!(f, "{}", e)?;
}
Ok(())
}
_ => Ok(()),
}
}
}
impl ErrorTrait for Error {}

View File

@ -0,0 +1,143 @@
use abi_stable::{
declare_root_module_statics,
library::RootModule,
package_version_strings, sabi_trait,
sabi_types::VersionStrings,
std_types::{RBox, RCowStr, ROption, RResult, RStr, RString, RVec},
StableAbi,
};
mod error;
pub use self::error::Error;
pub mod utils;
/// The identifier for a plugin.
#[repr(C)]
#[derive(Debug, Clone, PartialEq, Eq, StableAbi, Hash)]
pub struct PluginId {
pub named: RCowStr<'static>,
pub instance: u64,
}
// Plugin Can be loaded from a file path
#[repr(C)]
#[derive(Clone, Debug, StableAbi)]
pub enum LoadedData {
RadarGridData(RadarGridData),
JsonData,
PlainText(RString),
Binary(RVec<u8>),
}
#[repr(C)]
#[derive(StableAbi, Clone, Debug)]
pub struct RadarGridData {
pub data: VecResult,
pub info: GridDataInfo,
}
#[repr(C)]
#[derive(StableAbi, Clone, Debug)]
pub enum VecResult {
I64(RVec<i64>),
F64(RVec<f64>),
I32(RVec<i32>),
I16(RVec<i16>),
F32(RVec<f32>),
U64(RVec<u64>),
U32(RVec<u32>),
Bool(RVec<bool>),
I8(RVec<i8>),
U8(RVec<u8>),
}
#[repr(C)]
#[derive(StableAbi, Clone, Debug)]
pub struct GridDataInfo {
pub shape: RVec<usize>,
pub dimensions: RVec<RVec<f64>>,
pub dimension_names: RVec<RString>,
pub fill_value: f64,
pub datetime: ROption<i64>,
pub value_description: ROption<RString>,
pub value_name: RString,
pub maybe_probe_data_type: ROption<ProbeDataType>,
pub dimension_description: RString,
}
#[repr(C)]
#[derive(StableAbi, Clone, Copy, Debug)]
pub enum ProbeDataType {
// Single
R,
V,
SW,
CC,
ZDR,
PHIDP,
KDP,
HCA,
DBZ,
QPE,
QPF,
VIL,
OHP,
THP,
ET,
EB,
// Unknown
Unknown,
}
#[repr(C)]
#[derive(Debug, StableAbi)]
#[sabi(impl_InterfaceType(Sync, Send, Debug))]
pub struct PluginInfo {
pub plugin_type: RStr<'static>,
pub name: RStr<'static>,
pub version: RStr<'static>,
pub author: RStr<'static>,
pub description: ROption<RStr<'static>>,
pub url: ROption<RStr<'static>>,
}
pub type PluginType = DataLoaderPlugin_TO<'static, RBox<()>>;
#[sabi_trait]
pub trait DataLoaderPlugin: Send + Sync {
fn load(&self, path: RStr<'_>) -> RResult<RVec<LoadedData>, Error>;
fn plugin_id(&self) -> &PluginId;
fn plugin_info(&self) -> PluginInfo;
}
/// The root module of a`plugin` dynamic library.
///
/// To load this module,
/// call <PluginMod as RootModule>::load_from_directory(some_directory_path)
#[repr(C)]
#[derive(StableAbi)]
#[sabi(kind(Prefix(prefix_ref = PluginMod_Ref)))]
#[sabi(missing_field(panic))]
pub struct PluginMod {
/// Constructs the plugin.
///
///
/// The `#[sabi(last_prefix_field)]` attribute here means that this is the last field in this struct
/// that was defined in the first compatible version of the library
/// (0.1.0, 0.2.0, 0.3.0, 1.0.0, 2.0.0 ,etc),
/// requiring new fields to always be added below preexisting ones.
///
/// The `#[sabi(last_prefix_field)]` attribute would stay on this field until the library
/// bumps its "major" version,
/// at which point it would be moved to the last field at the time.
///
#[sabi(last_prefix_field)]
pub new: extern "C" fn(PluginId) -> RResult<PluginType, Error>,
}
impl RootModule for PluginMod_Ref {
declare_root_module_statics! {PluginMod_Ref}
const BASE_NAME: &'static str = "data_loader_plugin";
const NAME: &'static str = "data_loader_plugin";
const VERSION_STRINGS: VersionStrings = package_version_strings!();
}

View File

@ -0,0 +1 @@